diff --git a/format.c b/format.c index 528c777c..cb8761e5 100644 --- a/format.c +++ b/format.c @@ -118,6 +118,9 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_REPEAT 0x200000 #define FORMAT_QUOTE_ARGUMENTS 0x400000 #define FORMAT_RELATIVE 0x800000 +#define FORMAT_CLIENT_TERMCAP 0x1000000 +#define FORMAT_CLIENT_TERMFEAT 0x2000000 +#define FORMAT_CLIENT_ENVIRON 0x4000000 /* Limit on recursion. */ #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: - * 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/ @@ -4463,7 +4466,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, } /* Now try single character with arguments. */ - if (strchr("mCLNPSst=pReqW", cp[0]) == NULL) + if (strchr("ImCLNPSst=pReqW", cp[0]) == NULL) break; 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; u_int i, count, nsub = 0, nrep; struct format_expand_state next; + struct environ_entry *envent; /* Set sorting defaults. */ sc->order = SORT_ORDER; @@ -5186,6 +5190,16 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'n': modifiers |= FORMAT_LENGTH; 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': modifiers |= FORMAT_TIMESTRING; 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? */ if (modifiers & FORMAT_LITERAL) { format_log(es, "literal string is '%s'", copy); diff --git a/tmux.1 b/tmux.1 index 8f69b6fb..e956955e 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6431,6 +6431,24 @@ see .Xr strftime 3 . .Pp 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:\& and .Ql d:\& diff --git a/tmux.h b/tmux.h index 6cc9c391..eb4f0fd8 100644 --- a/tmux.h +++ b/tmux.h @@ -2774,6 +2774,7 @@ int tty_term_read_list(const char *, int, char ***, u_int *, char **); void tty_term_free_list(char **, u_int); 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_i(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 */ void tty_add_features(int *, const char *, const char *); const char *tty_get_features(int); +int tty_feature_present(struct tty_term *, const char *); int tty_apply_features(struct tty_term *, int); void tty_default_features(int *, const char *, u_int); diff --git a/tty-features.c b/tty-features.c index b4340e2e..01b9bff3 100644 --- a/tty-features.c +++ b/tty-features.c @@ -442,6 +442,45 @@ tty_get_features(int feat) 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 tty_apply_features(struct tty_term *term, int feat) { diff --git a/tty-term.c b/tty-term.c index 39bfd9d6..c248aa84 100644 --- a/tty-term.c +++ b/tty-term.c @@ -776,6 +776,18 @@ tty_term_has(struct tty_term *term, enum tty_code_code code) 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 * tty_term_string(struct tty_term *term, enum tty_code_code code) {