[BACK]Return to arguments.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/arguments.c, Revision 1.43

1.43    ! nicm        1: /* $OpenBSD: arguments.c,v 1.42 2021/08/21 18:39:07 nicm Exp $ */
1.1       nicm        2:
                      3: /*
1.13      nicm        4:  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1       nicm        5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/types.h>
                     20:
1.42      nicm       21: #include <ctype.h>
1.1       nicm       22: #include <stdlib.h>
                     23: #include <string.h>
1.16      nicm       24: #include <vis.h>
1.1       nicm       25:
                     26: #include "tmux.h"
                     27:
1.5       nicm       28: /*
                     29:  * Manipulate command arguments.
                     30:  */
1.11      nicm       31:
1.21      nicm       32: TAILQ_HEAD(args_values, args_value);
                     33:
1.11      nicm       34: struct args_entry {
                     35:        u_char                   flag;
1.21      nicm       36:        struct args_values       values;
1.27      nicm       37:        u_int                    count;
1.11      nicm       38:        RB_ENTRY(args_entry)     entry;
                     39: };
1.5       nicm       40:
1.38      nicm       41: struct args {
                     42:        struct args_tree          tree;
1.43    ! nicm       43:        u_int                    count;
        !            44:        struct args_value       *values;
1.38      nicm       45: };
                     46:
1.14      nicm       47: static struct args_entry       *args_find(struct args *, u_char);
1.5       nicm       48:
1.14      nicm       49: static int     args_cmp(struct args_entry *, struct args_entry *);
                     50: RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
1.5       nicm       51:
                     52: /* Arguments tree comparison function. */
1.14      nicm       53: static int
1.5       nicm       54: args_cmp(struct args_entry *a1, struct args_entry *a2)
                     55: {
                     56:        return (a1->flag - a2->flag);
                     57: }
                     58:
                     59: /* Find a flag in the arguments tree. */
1.14      nicm       60: static struct args_entry *
1.32      nicm       61: args_find(struct args *args, u_char flag)
1.5       nicm       62: {
                     63:        struct args_entry       entry;
                     64:
1.32      nicm       65:        entry.flag = flag;
1.5       nicm       66:        return (RB_FIND(args_tree, &args->tree, &entry));
                     67: }
                     68:
1.43    ! nicm       69: /* Copy value. */
        !            70: static void
        !            71: args_copy_value(struct args_value *to, struct args_value *from)
        !            72: {
        !            73:        to->type = from->type;
        !            74:        switch (from->type) {
        !            75:        case ARGS_NONE:
        !            76:                break;
        !            77:        case ARGS_COMMANDS:
        !            78:                to->cmdlist = from->cmdlist;
        !            79:                to->cmdlist->references++;
        !            80:                break;
        !            81:        case ARGS_STRING:
        !            82:                to->string = xstrdup(from->string);
        !            83:                break;
        !            84:        }
        !            85: }
        !            86:
1.42      nicm       87: /* Get value as string. */
1.43    ! nicm       88: static const char *
1.42      nicm       89: args_value_as_string(struct args_value *value)
                     90: {
                     91:        switch (value->type) {
                     92:        case ARGS_NONE:
1.43    ! nicm       93:                return ("");
1.42      nicm       94:        case ARGS_COMMANDS:
1.43    ! nicm       95:                if (value->cached == NULL)
        !            96:                        value->cached = cmd_list_print(value->cmdlist, 0);
        !            97:                return (value->cached);
1.42      nicm       98:        case ARGS_STRING:
1.43    ! nicm       99:                return (value->string);
1.42      nicm      100:        }
                    101: }
                    102:
1.36      nicm      103: /* Create an empty arguments set. */
                    104: struct args *
                    105: args_create(void)
                    106: {
                    107:        struct args      *args;
                    108:
                    109:        args = xcalloc(1, sizeof *args);
                    110:        RB_INIT(&args->tree);
                    111:        return (args);
                    112: }
                    113:
1.42      nicm      114: /* Parse arguments into a new argument set. */
1.1       nicm      115: struct args *
1.42      nicm      116: args_parse(const struct args_parse *parse, struct args_value *values,
                    117:     u_int count)
1.1       nicm      118: {
1.42      nicm      119:        struct args             *args;
                    120:        u_int                    i;
1.43    ! nicm      121:        struct args_value       *value, *new;
1.42      nicm      122:        u_char                   flag, argument;
1.43    ! nicm      123:        const char              *found, *string, *s;
1.1       nicm      124:
1.42      nicm      125:        if (count == 0)
                    126:                return (args_create());
1.1       nicm      127:
1.36      nicm      128:        args = args_create();
1.42      nicm      129:        for (i = 1; i < count; /* nothing */) {
                    130:                value = &values[i];
                    131:                if (value->type != ARGS_STRING)
                    132:                        break;
                    133:
                    134:                string = value->string;
                    135:                if (*string++ != '-' || *string == '\0')
                    136:                        break;
                    137:                i++;
                    138:                if (string[0] == '-' && string[1] == '\0')
                    139:                        break;
                    140:
                    141:                for (;;) {
                    142:                        flag = *string++;
                    143:                        if (flag == '\0')
                    144:                                break;
                    145:                        if (!isalnum(flag)) {
                    146:                                args_free(args);
                    147:                                return (NULL);
                    148:                        }
                    149:                        found = strchr(parse->template, flag);
                    150:                        if (found == NULL) {
                    151:                                args_free(args);
                    152:                                return (NULL);
                    153:                        }
                    154:                        argument = *++found;
                    155:                        if (argument != ':') {
                    156:                                log_debug("%s: add -%c", __func__, flag);
                    157:                                args_set(args, flag, NULL);
                    158:                                continue;
                    159:                        }
1.43    ! nicm      160:                        new = xcalloc(1, sizeof *value);
        !           161:                        if (*string != '\0') {
        !           162:                                new->type = ARGS_STRING;
        !           163:                                new->string = xstrdup(string);
        !           164:                        } else {
1.42      nicm      165:                                if (i == count) {
                    166:                                        args_free(args);
                    167:                                        return (NULL);
                    168:                                }
1.43    ! nicm      169:                                args_copy_value(new, &values[i++]);
1.42      nicm      170:                        }
1.43    ! nicm      171:                        s = args_value_as_string(new);
1.42      nicm      172:                        log_debug("%s: add -%c = %s", __func__, flag, s);
1.43    ! nicm      173:                        args_set(args, flag, new);
1.42      nicm      174:                        break;
                    175:                }
                    176:        }
                    177:        log_debug("%s: flags end at %u of %u", __func__, i, count);
                    178:        if (i != count) {
                    179:                for (/* nothing */; i < count; i++) {
                    180:                        value = &values[i];
                    181:
                    182:                        s = args_value_as_string(value);
                    183:                        log_debug("%s: %u = %s", __func__, i, s);
1.43    ! nicm      184:
        !           185:                        args->values = xrecallocarray(args->values,
        !           186:                            args->count, args->count + 1, sizeof *args->values);
        !           187:                        args_copy_value(&args->values[args->count++], value);
1.1       nicm      188:                }
                    189:        }
                    190:
1.43    ! nicm      191:        if ((parse->lower != -1 && args->count < (u_int)parse->lower) ||
        !           192:            (parse->upper != -1 && args->count > (u_int)parse->upper)) {
1.38      nicm      193:                args_free(args);
                    194:                return (NULL);
                    195:        }
1.1       nicm      196:        return (args);
1.42      nicm      197: }
                    198:
                    199: /* Free a value. */
                    200: void
                    201: args_free_value(struct args_value *value)
                    202: {
                    203:        switch (value->type) {
                    204:        case ARGS_NONE:
                    205:                break;
                    206:        case ARGS_STRING:
                    207:                free(value->string);
                    208:                break;
                    209:        case ARGS_COMMANDS:
                    210:                cmd_list_free(value->cmdlist);
                    211:                break;
                    212:        }
1.43    ! nicm      213:        free(value->cached);
1.1       nicm      214: }
                    215:
                    216: /* Free an arguments set. */
                    217: void
                    218: args_free(struct args *args)
                    219: {
1.5       nicm      220:        struct args_entry       *entry;
                    221:        struct args_entry       *entry1;
1.21      nicm      222:        struct args_value       *value;
                    223:        struct args_value       *value1;
1.43    ! nicm      224:        u_int                    i;
1.1       nicm      225:
1.43    ! nicm      226:        for (i = 0; i < args->count; i++)
        !           227:                args_free_value(&args->values[i]);
        !           228:        free(args->values);
1.1       nicm      229:
1.5       nicm      230:        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                    231:                RB_REMOVE(args_tree, &args->tree, entry);
1.21      nicm      232:                TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
                    233:                        TAILQ_REMOVE(&entry->values, value, entry);
1.41      nicm      234:                        free(value->string);
1.21      nicm      235:                        free(value);
                    236:                }
1.5       nicm      237:                free(entry);
                    238:        }
1.1       nicm      239:
1.4       nicm      240:        free(args);
1.1       nicm      241: }
                    242:
1.38      nicm      243: /* Convert arguments to vector. */
                    244: void
                    245: args_vector(struct args *args, int *argc, char ***argv)
                    246: {
1.43    ! nicm      247:        struct args_value       *value;
        !           248:        u_int                    i;
        !           249:
        !           250:        *argc = 0;
        !           251:        *argv = NULL;
        !           252:
        !           253:        for (i = 0; i < args->count; i++) {
        !           254:                value = &args->values[i];
        !           255:                cmd_append_argv(argc, argv, args_value_as_string(value));
        !           256:        }
1.38      nicm      257: }
                    258:
1.12      nicm      259: /* Add to string. */
                    260: static void printflike(3, 4)
                    261: args_print_add(char **buf, size_t *len, const char *fmt, ...)
                    262: {
1.39      nicm      263:        va_list  ap;
1.12      nicm      264:        char    *s;
                    265:        size_t   slen;
                    266:
                    267:        va_start(ap, fmt);
                    268:        slen = xvasprintf(&s, fmt, ap);
                    269:        va_end(ap);
                    270:
                    271:        *len += slen;
                    272:        *buf = xrealloc(*buf, *len);
                    273:
                    274:        strlcat(*buf, s, *len);
                    275:        free(s);
                    276: }
                    277:
1.43    ! nicm      278: /* Add value to string. */
1.21      nicm      279: static void
1.43    ! nicm      280: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21      nicm      281: {
1.43    ! nicm      282:        char    *expanded = NULL;
1.21      nicm      283:
                    284:        if (**buf != '\0')
                    285:                args_print_add(buf, len, " ");
                    286:
1.43    ! nicm      287:        switch (value->type) {
        !           288:        case ARGS_NONE:
        !           289:                break;
        !           290:        case ARGS_COMMANDS:
        !           291:                expanded = cmd_list_print(value->cmdlist, 0);
        !           292:                args_print_add(buf, len, "{ %s }", expanded);
        !           293:                break;
        !           294:        case ARGS_STRING:
        !           295:                expanded = args_escape(value->string);
        !           296:                args_print_add(buf, len, "%s", expanded);
        !           297:                break;
        !           298:        }
        !           299:        free(expanded);
1.21      nicm      300: }
                    301:
1.1       nicm      302: /* Print a set of arguments. */
1.12      nicm      303: char *
                    304: args_print(struct args *args)
1.1       nicm      305: {
1.39      nicm      306:        size_t                   len;
1.21      nicm      307:        char                    *buf;
1.43    ! nicm      308:        u_int                    i, j;
1.5       nicm      309:        struct args_entry       *entry;
1.21      nicm      310:        struct args_value       *value;
1.1       nicm      311:
1.12      nicm      312:        len = 1;
                    313:        buf = xcalloc(1, len);
1.1       nicm      314:
                    315:        /* Process the flags first. */
1.5       nicm      316:        RB_FOREACH(entry, args_tree, &args->tree) {
1.21      nicm      317:                if (!TAILQ_EMPTY(&entry->values))
1.1       nicm      318:                        continue;
                    319:
1.12      nicm      320:                if (*buf == '\0')
                    321:                        args_print_add(&buf, &len, "-");
1.27      nicm      322:                for (j = 0; j < entry->count; j++)
                    323:                        args_print_add(&buf, &len, "%c", entry->flag);
1.1       nicm      324:        }
                    325:
                    326:        /* Then the flags with arguments. */
1.5       nicm      327:        RB_FOREACH(entry, args_tree, &args->tree) {
1.38      nicm      328:                TAILQ_FOREACH(value, &entry->values, entry) {
                    329:                        if (*buf != '\0')
                    330:                                args_print_add(&buf, &len, " -%c", entry->flag);
                    331:                        else
                    332:                                args_print_add(&buf, &len, "-%c", entry->flag);
1.43    ! nicm      333:                        args_print_add_value(&buf, &len, value);
1.38      nicm      334:                }
1.1       nicm      335:        }
                    336:
                    337:        /* And finally the argument vector. */
1.43    ! nicm      338:        for (i = 0; i < args->count; i++)
        !           339:                args_print_add_value(&buf, &len, &args->values[i]);
1.1       nicm      340:
1.12      nicm      341:        return (buf);
1.22      nicm      342: }
                    343:
                    344: /* Escape an argument. */
                    345: char *
                    346: args_escape(const char *s)
                    347: {
1.35      nicm      348:        static const char        dquoted[] = " #';${}";
                    349:        static const char        squoted[] = " \"";
1.22      nicm      350:        char                    *escaped, *result;
1.35      nicm      351:        int                      flags, quotes = 0;
1.22      nicm      352:
1.30      nicm      353:        if (*s == '\0') {
                    354:                xasprintf(&result, "''");
                    355:                return (result);
                    356:        }
1.35      nicm      357:        if (s[strcspn(s, dquoted)] != '\0')
                    358:                quotes = '"';
                    359:        else if (s[strcspn(s, squoted)] != '\0')
                    360:                quotes = '\'';
                    361:
1.26      nicm      362:        if (s[0] != ' ' &&
1.35      nicm      363:            s[1] == '\0' &&
                    364:            (quotes != 0 || s[0] == '~')) {
1.22      nicm      365:                xasprintf(&escaped, "\\%c", s[0]);
1.34      nicm      366:                return (escaped);
                    367:        }
                    368:
1.25      nicm      369:        flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35      nicm      370:        if (quotes == '"')
1.22      nicm      371:                flags |= VIS_DQ;
                    372:        utf8_stravis(&escaped, s, flags);
                    373:
1.35      nicm      374:        if (quotes == '\'')
                    375:                xasprintf(&result, "'%s'", escaped);
                    376:        else if (quotes == '"') {
1.22      nicm      377:                if (*escaped == '~')
                    378:                        xasprintf(&result, "\"\\%s\"", escaped);
                    379:                else
                    380:                        xasprintf(&result, "\"%s\"", escaped);
                    381:        } else {
                    382:                if (*escaped == '~')
                    383:                        xasprintf(&result, "\\%s", escaped);
                    384:                else
                    385:                        result = xstrdup(escaped);
                    386:        }
                    387:        free(escaped);
                    388:        return (result);
1.1       nicm      389: }
                    390:
                    391: /* Return if an argument is present. */
                    392: int
1.32      nicm      393: args_has(struct args *args, u_char flag)
1.1       nicm      394: {
1.27      nicm      395:        struct args_entry       *entry;
                    396:
1.32      nicm      397:        entry = args_find(args, flag);
1.27      nicm      398:        if (entry == NULL)
                    399:                return (0);
                    400:        return (entry->count);
1.1       nicm      401: }
                    402:
1.5       nicm      403: /* Set argument value in the arguments tree. */
1.19      nicm      404: void
1.43    ! nicm      405: args_set(struct args *args, u_char flag, struct args_value *value)
1.1       nicm      406: {
1.5       nicm      407:        struct args_entry       *entry;
                    408:
1.32      nicm      409:        entry = args_find(args, flag);
1.21      nicm      410:        if (entry == NULL) {
1.7       nicm      411:                entry = xcalloc(1, sizeof *entry);
1.32      nicm      412:                entry->flag = flag;
1.27      nicm      413:                entry->count = 1;
1.21      nicm      414:                TAILQ_INIT(&entry->values);
1.7       nicm      415:                RB_INSERT(args_tree, &args->tree, entry);
1.27      nicm      416:        } else
                    417:                entry->count++;
1.43    ! nicm      418:        if (value != NULL && value->type != ARGS_NONE)
1.21      nicm      419:                TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1       nicm      420: }
                    421:
                    422: /* Get argument value. Will be NULL if it isn't present. */
                    423: const char *
1.32      nicm      424: args_get(struct args *args, u_char flag)
1.1       nicm      425: {
1.5       nicm      426:        struct args_entry       *entry;
                    427:
1.32      nicm      428:        if ((entry = args_find(args, flag)) == NULL)
                    429:                return (NULL);
                    430:        if (TAILQ_EMPTY(&entry->values))
1.5       nicm      431:                return (NULL);
1.41      nicm      432:        return (TAILQ_LAST(&entry->values, args_values)->string);
1.21      nicm      433: }
                    434:
1.32      nicm      435: /* Get first argument. */
                    436: u_char
                    437: args_first(struct args *args, struct args_entry **entry)
                    438: {
                    439:        *entry = RB_MIN(args_tree, &args->tree);
                    440:        if (*entry == NULL)
                    441:                return (0);
                    442:        return ((*entry)->flag);
                    443: }
                    444:
                    445: /* Get next argument. */
                    446: u_char
                    447: args_next(struct args_entry **entry)
                    448: {
                    449:        *entry = RB_NEXT(args_tree, &args->tree, *entry);
                    450:        if (*entry == NULL)
                    451:                return (0);
                    452:        return ((*entry)->flag);
1.38      nicm      453: }
                    454:
                    455: /* Get argument count. */
                    456: u_int
                    457: args_count(struct args *args)
                    458: {
1.43    ! nicm      459:        return (args->count);
        !           460: }
        !           461:
        !           462: /* Get argument value. */
        !           463: struct args_value *
        !           464: args_value(struct args *args, u_int idx)
        !           465: {
        !           466:        if (idx >= args->count)
        !           467:                return (NULL);
        !           468:        return (&args->values[idx]);
1.38      nicm      469: }
                    470:
                    471: /* Return argument as string. */
                    472: const char *
                    473: args_string(struct args *args, u_int idx)
                    474: {
1.43    ! nicm      475:        if (idx >= (u_int)args->count)
1.38      nicm      476:                return (NULL);
1.43    ! nicm      477:        return (args_value_as_string(&args->values[idx]));
1.32      nicm      478: }
                    479:
1.21      nicm      480: /* Get first value in argument. */
1.37      nicm      481: struct args_value *
                    482: args_first_value(struct args *args, u_char flag)
1.21      nicm      483: {
                    484:        struct args_entry       *entry;
                    485:
1.32      nicm      486:        if ((entry = args_find(args, flag)) == NULL)
1.21      nicm      487:                return (NULL);
1.37      nicm      488:        return (TAILQ_FIRST(&entry->values));
1.21      nicm      489: }
                    490:
                    491: /* Get next value in argument. */
1.37      nicm      492: struct args_value *
                    493: args_next_value(struct args_value *value)
1.21      nicm      494: {
1.37      nicm      495:        return (TAILQ_NEXT(value, entry));
1.1       nicm      496: }
                    497:
                    498: /* Convert an argument value to a number. */
                    499: long long
1.32      nicm      500: args_strtonum(struct args *args, u_char flag, long long minval,
                    501:     long long maxval, char **cause)
1.1       nicm      502: {
1.5       nicm      503:        const char              *errstr;
1.39      nicm      504:        long long                ll;
1.5       nicm      505:        struct args_entry       *entry;
1.21      nicm      506:        struct args_value       *value;
1.1       nicm      507:
1.32      nicm      508:        if ((entry = args_find(args, flag)) == NULL) {
1.1       nicm      509:                *cause = xstrdup("missing");
                    510:                return (0);
                    511:        }
1.21      nicm      512:        value = TAILQ_LAST(&entry->values, args_values);
1.1       nicm      513:
1.41      nicm      514:        ll = strtonum(value->string, minval, maxval, &errstr);
1.1       nicm      515:        if (errstr != NULL) {
                    516:                *cause = xstrdup(errstr);
                    517:                return (0);
1.29      nicm      518:        }
                    519:
                    520:        *cause = NULL;
                    521:        return (ll);
                    522: }
                    523:
                    524: /* Convert an argument to a number which may be a percentage. */
                    525: long long
1.32      nicm      526: args_percentage(struct args *args, u_char flag, long long minval,
1.29      nicm      527:     long long maxval, long long curval, char **cause)
                    528: {
1.31      nicm      529:        const char              *value;
1.29      nicm      530:        struct args_entry       *entry;
                    531:
1.32      nicm      532:        if ((entry = args_find(args, flag)) == NULL) {
1.29      nicm      533:                *cause = xstrdup("missing");
                    534:                return (0);
                    535:        }
1.41      nicm      536:        value = TAILQ_LAST(&entry->values, args_values)->string;
1.31      nicm      537:        return (args_string_percentage(value, minval, maxval, curval, cause));
                    538: }
                    539:
                    540: /* Convert a string to a number which may be a percentage. */
                    541: long long
                    542: args_string_percentage(const char *value, long long minval, long long maxval,
                    543:     long long curval, char **cause)
                    544: {
                    545:        const char      *errstr;
1.39      nicm      546:        long long        ll;
1.31      nicm      547:        size_t           valuelen = strlen(value);
                    548:        char            *copy;
1.29      nicm      549:
1.31      nicm      550:        if (value[valuelen - 1] == '%') {
                    551:                copy = xstrdup(value);
1.29      nicm      552:                copy[valuelen - 1] = '\0';
                    553:
                    554:                ll = strtonum(copy, 0, 100, &errstr);
                    555:                free(copy);
                    556:                if (errstr != NULL) {
                    557:                        *cause = xstrdup(errstr);
                    558:                        return (0);
                    559:                }
                    560:                ll = (curval * ll) / 100;
                    561:                if (ll < minval) {
1.33      nicm      562:                        *cause = xstrdup("too small");
1.29      nicm      563:                        return (0);
                    564:                }
                    565:                if (ll > maxval) {
1.33      nicm      566:                        *cause = xstrdup("too large");
1.29      nicm      567:                        return (0);
                    568:                }
                    569:        } else {
1.31      nicm      570:                ll = strtonum(value, minval, maxval, &errstr);
1.29      nicm      571:                if (errstr != NULL) {
                    572:                        *cause = xstrdup(errstr);
                    573:                        return (0);
                    574:                }
1.1       nicm      575:        }
                    576:
                    577:        *cause = NULL;
                    578:        return (ll);
                    579: }