=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/format.c,v retrieving revision 1.225 retrieving revision 1.226 diff -u -r1.225 -r1.226 --- src/usr.bin/tmux/format.c 2020/02/14 13:57:58 1.225 +++ src/usr.bin/tmux/format.c 2020/03/11 14:17:55 1.226 @@ -1,4 +1,4 @@ -/* $OpenBSD: format.c,v 1.225 2020/02/14 13:57:58 nicm Exp $ */ +/* $OpenBSD: format.c,v 1.226 2020/03/11 14:17:55 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,6 @@ struct timeval *); static int format_replace(struct format_tree *, const char *, size_t, char **, size_t *, size_t *); - static void format_defaults_session(struct format_tree *, struct session *); static void format_defaults_client(struct format_tree *, struct client *); @@ -1528,7 +1528,7 @@ } /* Now try single character with arguments. */ - if (strchr("mCs=p", cp[0]) == NULL) + if (strchr("mCs=pe", cp[0]) == NULL) break; c = cp[0]; @@ -1784,6 +1784,108 @@ return (value); } +static char * +format_replace_expression(struct format_modifier *mexp, struct format_tree *ft, + const char *copy) +{ + int argc = mexp->argc; + const char *errstr; + char *endch, *value, *left = NULL, *right = NULL; + int use_fp = 0; + u_int prec = 0; + double mleft, mright, result; + enum { ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULUS } operator; + + if (strcmp(mexp->argv[0], "+") == 0) + operator = ADD; + else if (strcmp(mexp->argv[0], "-") == 0) + operator = SUBTRACT; + else if (strcmp(mexp->argv[0], "*") == 0) + operator = MULTIPLY; + else if (strcmp(mexp->argv[0], "/") == 0) + operator = DIVIDE; + else if (strcmp(mexp->argv[0], "%") == 0 || + strcmp(mexp->argv[0], "m") == 0) + operator = MODULUS; + else { + format_log(ft, "expression has no valid operator: '%s'", + mexp->argv[0]); + goto fail; + } + + /* The second argument may be flags. */ + if (argc >= 2 && strchr(mexp->argv[1], 'f') != NULL) { + use_fp = 1; + prec = 2; + } + + /* The third argument may be precision. */ + if (argc >= 3) { + prec = strtonum(mexp->argv[2], INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + format_log (ft, "expression precision %s: %s", errstr, + mexp->argv[2]); + goto fail; + } + } + + if (format_choose(ft, copy, &left, &right, 1) != 0) { + format_log(ft, "expression syntax error"); + goto fail; + } + + mleft = strtod(left, &endch); + if (*endch != '\0') { + format_log(ft, "expression left side is invalid: %s", left); + goto fail; + } + + mright = strtod(right, &endch); + if (*endch != '\0') { + format_log(ft, "expression right side is invalid: %s", right); + goto fail; + } + + if (!use_fp) { + mleft = (long long)mleft; + mright = (long long)mright; + } + format_log(ft, "expression left side is: %.*f", prec, mleft); + format_log(ft, "expression right side is: %.*f", prec, mright); + + switch (operator) { + case ADD: + result = mleft + mright; + break; + case SUBTRACT: + result = mleft - mright; + break; + case MULTIPLY: + result = mleft * mright; + break; + case DIVIDE: + result = mleft / mright; + break; + case MODULUS: + result = fmod(mleft, mright); + break; + } + if (use_fp) + xasprintf(&value, "%.*f", prec, result); + else + xasprintf(&value, "%.*f", prec, (double)(long long)result); + format_log(ft, "expression result is %s", value); + + free(right); + free(left); + return value; + +fail: + free(right); + free(left); + return (NULL); +} + /* Replace a key. */ static int format_replace(struct format_tree *ft, const char *key, size_t keylen, @@ -1796,7 +1898,7 @@ size_t valuelen; int modifiers = 0, limit = 0, width = 0, j; struct format_modifier *list, *fm, *cmp = NULL, *search = NULL; - struct format_modifier **sub = NULL; + struct format_modifier **sub = NULL, *mexp = NULL; u_int i, count, nsub = 0; /* Make a copy of the key. */ @@ -1848,6 +1950,11 @@ if (errptr != NULL) width = 0; break; + case 'e': + if (fm->argc < 1 || fm->argc > 3) + break; + mexp = fm; + break; case 'l': modifiers |= FORMAT_LITERAL; break; @@ -2024,6 +2131,10 @@ free(condition); free(found); + } else if (mexp != NULL) { + value = format_replace_expression(mexp, ft, copy); + if (value == NULL) + value = xstrdup(""); } else { /* Neither: look up directly. */ value = format_find(ft, copy, modifiers);