[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.42

1.42    ! nicm        1: /* $OpenBSD: arguments.c,v 1.41 2021/08/21 10:28:05 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;
                     43:        int                       argc;
                     44:        char                    **argv;
                     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.42    ! nicm       69: /* Get value as string. */
        !            70: static char *
        !            71: args_value_as_string(struct args_value *value)
        !            72: {
        !            73:        switch (value->type) {
        !            74:        case ARGS_NONE:
        !            75:                return (xstrdup(""));
        !            76:        case ARGS_COMMANDS:
        !            77:                return (cmd_list_print(value->cmdlist, 0));
        !            78:        case ARGS_STRING:
        !            79:                return (xstrdup(value->string));
        !            80:        }
        !            81: }
        !            82:
1.36      nicm       83: /* Create an empty arguments set. */
                     84: struct args *
                     85: args_create(void)
                     86: {
                     87:        struct args      *args;
                     88:
                     89:        args = xcalloc(1, sizeof *args);
                     90:        RB_INIT(&args->tree);
                     91:        return (args);
                     92: }
                     93:
1.42    ! nicm       94: /* Parse arguments into a new argument set. */
1.1       nicm       95: struct args *
1.42    ! nicm       96: args_parse(const struct args_parse *parse, struct args_value *values,
        !            97:     u_int count)
1.1       nicm       98: {
1.42    ! nicm       99:        struct args             *args;
        !           100:        u_int                    i;
        !           101:        struct args_value       *value;
        !           102:        u_char                   flag, argument;
        !           103:        const char              *found, *string;
        !           104:        char                    *s;
1.1       nicm      105:
1.42    ! nicm      106:        if (count == 0)
        !           107:                return (args_create());
1.1       nicm      108:
1.36      nicm      109:        args = args_create();
1.42    ! nicm      110:        for (i = 1; i < count; /* nothing */) {
        !           111:                value = &values[i];
        !           112:
        !           113:                s = args_value_as_string(value);
        !           114:                log_debug("%s: %u = %s", __func__, i, s);
        !           115:                free(s);
        !           116:
        !           117:                if (value->type != ARGS_STRING)
        !           118:                        break;
        !           119:
        !           120:                string = value->string;
        !           121:                if (*string++ != '-' || *string == '\0')
        !           122:                        break;
        !           123:                i++;
        !           124:                if (string[0] == '-' && string[1] == '\0')
        !           125:                        break;
        !           126:
        !           127:                for (;;) {
        !           128:                        flag = *string++;
        !           129:                        if (flag == '\0')
        !           130:                                break;
        !           131:                        if (!isalnum(flag)) {
        !           132:                                args_free(args);
        !           133:                                return (NULL);
        !           134:                        }
        !           135:                        found = strchr(parse->template, flag);
        !           136:                        if (found == NULL) {
        !           137:                                args_free(args);
        !           138:                                return (NULL);
        !           139:                        }
        !           140:                        argument = *++found;
        !           141:                        if (argument != ':') {
        !           142:                                log_debug("%s: add -%c", __func__, flag);
        !           143:                                args_set(args, flag, NULL);
        !           144:                                continue;
        !           145:                        }
        !           146:                        if (*string != '\0')
        !           147:                                s = xstrdup(string);
        !           148:                        else {
        !           149:                                if (i == count) {
        !           150:                                        args_free(args);
        !           151:                                        return (NULL);
        !           152:                                }
        !           153:                                s = args_value_as_string(&values[i++]);
        !           154:                        }
        !           155:                        log_debug("%s: add -%c = %s", __func__, flag, s);
        !           156:                        args_set(args, flag, s);
        !           157:                        free(s);
        !           158:                        break;
        !           159:                }
        !           160:        }
        !           161:        log_debug("%s: flags end at %u of %u", __func__, i, count);
        !           162:        if (i != count) {
        !           163:                for (/* nothing */; i < count; i++) {
        !           164:                        value = &values[i];
        !           165:
        !           166:                        s = args_value_as_string(value);
        !           167:                        log_debug("%s: %u = %s", __func__, i, s);
        !           168:                        cmd_append_argv(&args->argc, &args->argv, s);
        !           169:                        free(s);
1.1       nicm      170:                }
                    171:        }
                    172:
1.42    ! nicm      173:        if ((parse->lower != -1 && args->argc < parse->lower) ||
        !           174:            (parse->upper != -1 && args->argc > parse->upper)) {
1.38      nicm      175:                args_free(args);
                    176:                return (NULL);
                    177:        }
1.1       nicm      178:        return (args);
1.42    ! nicm      179: }
        !           180:
        !           181: /* Free a value. */
        !           182: void
        !           183: args_free_value(struct args_value *value)
        !           184: {
        !           185:        switch (value->type) {
        !           186:        case ARGS_NONE:
        !           187:                break;
        !           188:        case ARGS_STRING:
        !           189:                free(value->string);
        !           190:                break;
        !           191:        case ARGS_COMMANDS:
        !           192:                cmd_list_free(value->cmdlist);
        !           193:                break;
        !           194:        }
1.1       nicm      195: }
                    196:
                    197: /* Free an arguments set. */
                    198: void
                    199: args_free(struct args *args)
                    200: {
1.5       nicm      201:        struct args_entry       *entry;
                    202:        struct args_entry       *entry1;
1.21      nicm      203:        struct args_value       *value;
                    204:        struct args_value       *value1;
1.1       nicm      205:
                    206:        cmd_free_argv(args->argc, args->argv);
                    207:
1.5       nicm      208:        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                    209:                RB_REMOVE(args_tree, &args->tree, entry);
1.21      nicm      210:                TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
                    211:                        TAILQ_REMOVE(&entry->values, value, entry);
1.41      nicm      212:                        free(value->string);
1.21      nicm      213:                        free(value);
                    214:                }
1.5       nicm      215:                free(entry);
                    216:        }
1.1       nicm      217:
1.4       nicm      218:        free(args);
1.1       nicm      219: }
                    220:
1.38      nicm      221: /* Convert arguments to vector. */
                    222: void
                    223: args_vector(struct args *args, int *argc, char ***argv)
                    224: {
                    225:        *argc = args->argc;
                    226:        *argv = cmd_copy_argv(args->argc, args->argv);
                    227: }
                    228:
1.12      nicm      229: /* Add to string. */
                    230: static void printflike(3, 4)
                    231: args_print_add(char **buf, size_t *len, const char *fmt, ...)
                    232: {
1.39      nicm      233:        va_list  ap;
1.12      nicm      234:        char    *s;
                    235:        size_t   slen;
                    236:
                    237:        va_start(ap, fmt);
                    238:        slen = xvasprintf(&s, fmt, ap);
                    239:        va_end(ap);
                    240:
                    241:        *len += slen;
                    242:        *buf = xrealloc(*buf, *len);
                    243:
                    244:        strlcat(*buf, s, *len);
                    245:        free(s);
                    246: }
                    247:
1.21      nicm      248: /* Add argument to string. */
                    249: static void
                    250: args_print_add_argument(char **buf, size_t *len, const char *argument)
                    251: {
1.22      nicm      252:        char    *escaped;
1.21      nicm      253:
                    254:        if (**buf != '\0')
                    255:                args_print_add(buf, len, " ");
                    256:
1.22      nicm      257:        escaped = args_escape(argument);
                    258:        args_print_add(buf, len, "%s", escaped);
1.21      nicm      259:        free(escaped);
                    260: }
                    261:
1.1       nicm      262: /* Print a set of arguments. */
1.12      nicm      263: char *
                    264: args_print(struct args *args)
1.1       nicm      265: {
1.39      nicm      266:        size_t                   len;
1.21      nicm      267:        char                    *buf;
                    268:        int                      i;
1.27      nicm      269:        u_int                    j;
1.5       nicm      270:        struct args_entry       *entry;
1.21      nicm      271:        struct args_value       *value;
1.1       nicm      272:
1.12      nicm      273:        len = 1;
                    274:        buf = xcalloc(1, len);
1.1       nicm      275:
                    276:        /* Process the flags first. */
1.5       nicm      277:        RB_FOREACH(entry, args_tree, &args->tree) {
1.21      nicm      278:                if (!TAILQ_EMPTY(&entry->values))
1.1       nicm      279:                        continue;
                    280:
1.12      nicm      281:                if (*buf == '\0')
                    282:                        args_print_add(&buf, &len, "-");
1.27      nicm      283:                for (j = 0; j < entry->count; j++)
                    284:                        args_print_add(&buf, &len, "%c", entry->flag);
1.1       nicm      285:        }
                    286:
                    287:        /* Then the flags with arguments. */
1.5       nicm      288:        RB_FOREACH(entry, args_tree, &args->tree) {
1.38      nicm      289:                TAILQ_FOREACH(value, &entry->values, entry) {
                    290:                        if (*buf != '\0')
                    291:                                args_print_add(&buf, &len, " -%c", entry->flag);
                    292:                        else
                    293:                                args_print_add(&buf, &len, "-%c", entry->flag);
1.41      nicm      294:                        args_print_add_argument(&buf, &len, value->string);
1.38      nicm      295:                }
1.1       nicm      296:        }
                    297:
                    298:        /* And finally the argument vector. */
1.21      nicm      299:        for (i = 0; i < args->argc; i++)
                    300:                args_print_add_argument(&buf, &len, args->argv[i]);
1.1       nicm      301:
1.12      nicm      302:        return (buf);
1.22      nicm      303: }
                    304:
                    305: /* Escape an argument. */
                    306: char *
                    307: args_escape(const char *s)
                    308: {
1.35      nicm      309:        static const char        dquoted[] = " #';${}";
                    310:        static const char        squoted[] = " \"";
1.22      nicm      311:        char                    *escaped, *result;
1.35      nicm      312:        int                      flags, quotes = 0;
1.22      nicm      313:
1.30      nicm      314:        if (*s == '\0') {
                    315:                xasprintf(&result, "''");
                    316:                return (result);
                    317:        }
1.35      nicm      318:        if (s[strcspn(s, dquoted)] != '\0')
                    319:                quotes = '"';
                    320:        else if (s[strcspn(s, squoted)] != '\0')
                    321:                quotes = '\'';
                    322:
1.26      nicm      323:        if (s[0] != ' ' &&
1.35      nicm      324:            s[1] == '\0' &&
                    325:            (quotes != 0 || s[0] == '~')) {
1.22      nicm      326:                xasprintf(&escaped, "\\%c", s[0]);
1.34      nicm      327:                return (escaped);
                    328:        }
                    329:
1.25      nicm      330:        flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35      nicm      331:        if (quotes == '"')
1.22      nicm      332:                flags |= VIS_DQ;
                    333:        utf8_stravis(&escaped, s, flags);
                    334:
1.35      nicm      335:        if (quotes == '\'')
                    336:                xasprintf(&result, "'%s'", escaped);
                    337:        else if (quotes == '"') {
1.22      nicm      338:                if (*escaped == '~')
                    339:                        xasprintf(&result, "\"\\%s\"", escaped);
                    340:                else
                    341:                        xasprintf(&result, "\"%s\"", escaped);
                    342:        } else {
                    343:                if (*escaped == '~')
                    344:                        xasprintf(&result, "\\%s", escaped);
                    345:                else
                    346:                        result = xstrdup(escaped);
                    347:        }
                    348:        free(escaped);
                    349:        return (result);
1.1       nicm      350: }
                    351:
                    352: /* Return if an argument is present. */
                    353: int
1.32      nicm      354: args_has(struct args *args, u_char flag)
1.1       nicm      355: {
1.27      nicm      356:        struct args_entry       *entry;
                    357:
1.32      nicm      358:        entry = args_find(args, flag);
1.27      nicm      359:        if (entry == NULL)
                    360:                return (0);
                    361:        return (entry->count);
1.1       nicm      362: }
                    363:
1.5       nicm      364: /* Set argument value in the arguments tree. */
1.19      nicm      365: void
1.32      nicm      366: args_set(struct args *args, u_char flag, const char *s)
1.1       nicm      367: {
1.5       nicm      368:        struct args_entry       *entry;
1.21      nicm      369:        struct args_value       *value;
1.5       nicm      370:
1.32      nicm      371:        entry = args_find(args, flag);
1.21      nicm      372:        if (entry == NULL) {
1.7       nicm      373:                entry = xcalloc(1, sizeof *entry);
1.32      nicm      374:                entry->flag = flag;
1.27      nicm      375:                entry->count = 1;
1.21      nicm      376:                TAILQ_INIT(&entry->values);
1.7       nicm      377:                RB_INSERT(args_tree, &args->tree, entry);
1.27      nicm      378:        } else
                    379:                entry->count++;
1.5       nicm      380:
1.21      nicm      381:        if (s != NULL) {
                    382:                value = xcalloc(1, sizeof *value);
1.41      nicm      383:                value->string = xstrdup(s);
1.21      nicm      384:                TAILQ_INSERT_TAIL(&entry->values, value, entry);
                    385:        }
1.1       nicm      386: }
                    387:
                    388: /* Get argument value. Will be NULL if it isn't present. */
                    389: const char *
1.32      nicm      390: args_get(struct args *args, u_char flag)
1.1       nicm      391: {
1.5       nicm      392:        struct args_entry       *entry;
                    393:
1.32      nicm      394:        if ((entry = args_find(args, flag)) == NULL)
                    395:                return (NULL);
                    396:        if (TAILQ_EMPTY(&entry->values))
1.5       nicm      397:                return (NULL);
1.41      nicm      398:        return (TAILQ_LAST(&entry->values, args_values)->string);
1.21      nicm      399: }
                    400:
1.32      nicm      401: /* Get first argument. */
                    402: u_char
                    403: args_first(struct args *args, struct args_entry **entry)
                    404: {
                    405:        *entry = RB_MIN(args_tree, &args->tree);
                    406:        if (*entry == NULL)
                    407:                return (0);
                    408:        return ((*entry)->flag);
                    409: }
                    410:
                    411: /* Get next argument. */
                    412: u_char
                    413: args_next(struct args_entry **entry)
                    414: {
                    415:        *entry = RB_NEXT(args_tree, &args->tree, *entry);
                    416:        if (*entry == NULL)
                    417:                return (0);
                    418:        return ((*entry)->flag);
1.38      nicm      419: }
                    420:
                    421: /* Get argument count. */
                    422: u_int
                    423: args_count(struct args *args)
                    424: {
                    425:        return (args->argc);
                    426: }
                    427:
                    428: /* Return argument as string. */
                    429: const char *
                    430: args_string(struct args *args, u_int idx)
                    431: {
                    432:        if (idx >= (u_int)args->argc)
                    433:                return (NULL);
                    434:        return (args->argv[idx]);
1.32      nicm      435: }
                    436:
1.21      nicm      437: /* Get first value in argument. */
1.37      nicm      438: struct args_value *
                    439: args_first_value(struct args *args, u_char flag)
1.21      nicm      440: {
                    441:        struct args_entry       *entry;
                    442:
1.32      nicm      443:        if ((entry = args_find(args, flag)) == NULL)
1.21      nicm      444:                return (NULL);
1.37      nicm      445:        return (TAILQ_FIRST(&entry->values));
1.21      nicm      446: }
                    447:
                    448: /* Get next value in argument. */
1.37      nicm      449: struct args_value *
                    450: args_next_value(struct args_value *value)
1.21      nicm      451: {
1.37      nicm      452:        return (TAILQ_NEXT(value, entry));
1.1       nicm      453: }
                    454:
                    455: /* Convert an argument value to a number. */
                    456: long long
1.32      nicm      457: args_strtonum(struct args *args, u_char flag, long long minval,
                    458:     long long maxval, char **cause)
1.1       nicm      459: {
1.5       nicm      460:        const char              *errstr;
1.39      nicm      461:        long long                ll;
1.5       nicm      462:        struct args_entry       *entry;
1.21      nicm      463:        struct args_value       *value;
1.1       nicm      464:
1.32      nicm      465:        if ((entry = args_find(args, flag)) == NULL) {
1.1       nicm      466:                *cause = xstrdup("missing");
                    467:                return (0);
                    468:        }
1.21      nicm      469:        value = TAILQ_LAST(&entry->values, args_values);
1.1       nicm      470:
1.41      nicm      471:        ll = strtonum(value->string, minval, maxval, &errstr);
1.1       nicm      472:        if (errstr != NULL) {
                    473:                *cause = xstrdup(errstr);
                    474:                return (0);
1.29      nicm      475:        }
                    476:
                    477:        *cause = NULL;
                    478:        return (ll);
                    479: }
                    480:
                    481: /* Convert an argument to a number which may be a percentage. */
                    482: long long
1.32      nicm      483: args_percentage(struct args *args, u_char flag, long long minval,
1.29      nicm      484:     long long maxval, long long curval, char **cause)
                    485: {
1.31      nicm      486:        const char              *value;
1.29      nicm      487:        struct args_entry       *entry;
                    488:
1.32      nicm      489:        if ((entry = args_find(args, flag)) == NULL) {
1.29      nicm      490:                *cause = xstrdup("missing");
                    491:                return (0);
                    492:        }
1.41      nicm      493:        value = TAILQ_LAST(&entry->values, args_values)->string;
1.31      nicm      494:        return (args_string_percentage(value, minval, maxval, curval, cause));
                    495: }
                    496:
                    497: /* Convert a string to a number which may be a percentage. */
                    498: long long
                    499: args_string_percentage(const char *value, long long minval, long long maxval,
                    500:     long long curval, char **cause)
                    501: {
                    502:        const char      *errstr;
1.39      nicm      503:        long long        ll;
1.31      nicm      504:        size_t           valuelen = strlen(value);
                    505:        char            *copy;
1.29      nicm      506:
1.31      nicm      507:        if (value[valuelen - 1] == '%') {
                    508:                copy = xstrdup(value);
1.29      nicm      509:                copy[valuelen - 1] = '\0';
                    510:
                    511:                ll = strtonum(copy, 0, 100, &errstr);
                    512:                free(copy);
                    513:                if (errstr != NULL) {
                    514:                        *cause = xstrdup(errstr);
                    515:                        return (0);
                    516:                }
                    517:                ll = (curval * ll) / 100;
                    518:                if (ll < minval) {
1.33      nicm      519:                        *cause = xstrdup("too small");
1.29      nicm      520:                        return (0);
                    521:                }
                    522:                if (ll > maxval) {
1.33      nicm      523:                        *cause = xstrdup("too large");
1.29      nicm      524:                        return (0);
                    525:                }
                    526:        } else {
1.31      nicm      527:                ll = strtonum(value, minval, maxval, &errstr);
1.29      nicm      528:                if (errstr != NULL) {
                    529:                        *cause = xstrdup(errstr);
                    530:                        return (0);
                    531:                }
1.1       nicm      532:        }
                    533:
                    534:        *cause = NULL;
                    535:        return (ll);
                    536: }