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

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