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

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