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

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