Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-06-13 10:30:06 +01:00
5 changed files with 119 additions and 2 deletions

View File

@@ -118,6 +118,9 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_REPEAT 0x200000 #define FORMAT_REPEAT 0x200000
#define FORMAT_QUOTE_ARGUMENTS 0x400000 #define FORMAT_QUOTE_ARGUMENTS 0x400000
#define FORMAT_RELATIVE 0x800000 #define FORMAT_RELATIVE 0x800000
#define FORMAT_CLIENT_TERMCAP 0x1000000
#define FORMAT_CLIENT_TERMFEAT 0x2000000
#define FORMAT_CLIENT_ENVIRON 0x4000000
/* Limit on recursion. */ /* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100 #define FORMAT_LOOP_LIMIT 100
@@ -4422,7 +4425,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
/* /*
* Modifiers are a ; separated list of the forms: * Modifiers are a ; separated list of the forms:
* l,m,C,a,b,c,d,n,t,w,q,E,T,S,W,P,R,<,> * l,m,C,a,b,c,d,I,n,t,w,q,E,T,S,W,P,R,<,>
* =a * =a
* =/a * =/a
* =/a/ * =/a/
@@ -4463,7 +4466,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
} }
/* Now try single character with arguments. */ /* Now try single character with arguments. */
if (strchr("mCLNPSst=pReqW", cp[0]) == NULL) if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL)
break; break;
c = cp[0]; c = cp[0];
@@ -5104,6 +5107,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
struct format_modifier *bool_op_n = NULL; struct format_modifier *bool_op_n = NULL;
u_int i, count, nsub = 0, nrep; u_int i, count, nsub = 0, nrep;
struct format_expand_state next; struct format_expand_state next;
struct environ_entry *envent;
/* Set sorting defaults. */ /* Set sorting defaults. */
sc->order = SORT_ORDER; sc->order = SORT_ORDER;
@@ -5186,6 +5190,16 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'n': case 'n':
modifiers |= FORMAT_LENGTH; modifiers |= FORMAT_LENGTH;
break; break;
case 'I':
if (fm->argc < 1)
break;
if (strchr(fm->argv[0], 'f') != NULL)
modifiers |= FORMAT_CLIENT_TERMFEAT;
if (strchr(fm->argv[0], 'c') != NULL)
modifiers |= FORMAT_CLIENT_TERMCAP;
if (strchr(fm->argv[0], 'e') != NULL)
modifiers |= FORMAT_CLIENT_ENVIRON;
break;
case 't': case 't':
modifiers |= FORMAT_TIMESTRING; modifiers |= FORMAT_TIMESTRING;
if (fm->argc < 1) if (fm->argc < 1)
@@ -5313,6 +5327,38 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
} }
} }
/* Look up client capability, feature or environment. */
if ((modifiers & FORMAT_CLIENT_TERMCAP) ||
(modifiers & FORMAT_CLIENT_TERMFEAT) ||
(modifiers & FORMAT_CLIENT_ENVIRON)) {
if (ft->c == NULL ||
ft->c->tty.term == NULL ||
ft->c->flags & CLIENT_UNATTACHEDFLAGS) {
value = xstrdup("");
goto done;
}
if (modifiers & FORMAT_CLIENT_TERMCAP) {
if (tty_term_has_name(ft->c->tty.term, copy))
value = xstrdup("1");
else
value = xstrdup("0");
}
if (modifiers & FORMAT_CLIENT_TERMFEAT) {
if (tty_feature_present(ft->c->tty.term, copy))
value = xstrdup("1");
else
value = xstrdup("0");
}
if (modifiers & FORMAT_CLIENT_ENVIRON) {
envent = environ_find(ft->c->environ, copy);
if (envent != NULL && envent->value != NULL)
value = xstrdup(envent->value);
else
value = xstrdup("");
}
goto done;
}
/* Is this a literal string? */ /* Is this a literal string? */
if (modifiers & FORMAT_LITERAL) { if (modifiers & FORMAT_LITERAL) {
format_log(es, "literal string is '%s'", copy); format_log(es, "literal string is '%s'", copy);

18
tmux.1
View File

@@ -6431,6 +6431,24 @@ see
.Xr strftime 3 . .Xr strftime 3 .
.Pp .Pp
The The
.Ql I:\&
prefix will interrogate the client.
.Ql I/f
will be true if
.Nm
believes that the client supports the given terminal feature, for example
.Ql I/f:RGB ,
.Ql I/c
will be true if a client has the given
.Xr terminfo 3
capability, for example
.Ql I/c:smcup ,
and
.Ql I/e
gives the value of a client environment variable, for example
.Ql #{I/e:FOO} .
.Pp
The
.Ql b:\& .Ql b:\&
and and
.Ql d:\& .Ql d:\&

2
tmux.h
View File

@@ -2774,6 +2774,7 @@ int tty_term_read_list(const char *, int, char ***, u_int *,
char **); char **);
void tty_term_free_list(char **, u_int); void tty_term_free_list(char **, u_int);
int tty_term_has(struct tty_term *, enum tty_code_code); int tty_term_has(struct tty_term *, enum tty_code_code);
int tty_term_has_name(struct tty_term *, const char *);
const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code);
const char *tty_term_string_i(struct tty_term *, enum tty_code_code, int); const char *tty_term_string_i(struct tty_term *, enum tty_code_code, int);
const char *tty_term_string_ii(struct tty_term *, enum tty_code_code, int, const char *tty_term_string_ii(struct tty_term *, enum tty_code_code, int,
@@ -2791,6 +2792,7 @@ const char *tty_term_describe(struct tty_term *, enum tty_code_code);
/* tty-features.c */ /* tty-features.c */
void tty_add_features(int *, const char *, const char *); void tty_add_features(int *, const char *, const char *);
const char *tty_get_features(int); const char *tty_get_features(int);
int tty_feature_present(struct tty_term *, const char *);
int tty_apply_features(struct tty_term *, int); int tty_apply_features(struct tty_term *, int);
void tty_default_features(int *, const char *, u_int); void tty_default_features(int *, const char *, u_int);

View File

@@ -442,6 +442,45 @@ tty_get_features(int feat)
return (s); return (s);
} }
int
tty_feature_present(struct tty_term *term, const char *name)
{
const struct tty_feature *tf = NULL;
const char *const *capability;
u_int i;
char *copy;
for (i = 0; i < nitems(tty_features); i++) {
tf = tty_features[i];
if (strcmp(tf->name, name) == 0) {
if (term->features & (1 << i))
return (1);
break;
}
}
/*
* We don't just have the feature flag set. Check if the capabilities
* supported by the client are actual set instead.
*/
if (tf == NULL || strcmp(name, "ignorefkeys") == 0)
return (0);
if (tf->flags != 0 && (term->flags & tf->flags) != tf->flags)
return (0);
capability = tf->capabilities;
while (*capability != NULL) {
copy = xstrdup(*capability);
copy[strcspn(copy, "=")] = '\0';
if (!tty_term_has_name(term, copy)) {
free(copy);
return (0);
}
free(copy);
capability++;
}
return (1);
}
int int
tty_apply_features(struct tty_term *term, int feat) tty_apply_features(struct tty_term *term, int feat)
{ {

View File

@@ -776,6 +776,18 @@ tty_term_has(struct tty_term *term, enum tty_code_code code)
return (term->codes[code].type != TTYCODE_NONE); return (term->codes[code].type != TTYCODE_NONE);
} }
int
tty_term_has_name(struct tty_term *term, const char *name)
{
u_int i;
for (i = 0; i < tty_term_ncodes(); i++) {
if (strcmp(tty_term_codes[i].name, name) == 0)
return (tty_term_has(term, i));
}
return (0);
}
const char * const char *
tty_term_string(struct tty_term *term, enum tty_code_code code) tty_term_string(struct tty_term *term, enum tty_code_code code)
{ {