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

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