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

1.56    ! nicm        1: /* $OpenBSD: arguments.c,v 1.55 2022/06/07 10:02:19 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.45      nicm       32: /* List of argument values. */
1.21      nicm       33: TAILQ_HEAD(args_values, args_value);
                     34:
1.45      nicm       35: /* Single arguments flag. */
1.11      nicm       36: struct args_entry {
                     37:        u_char                   flag;
1.21      nicm       38:        struct args_values       values;
1.27      nicm       39:        u_int                    count;
1.11      nicm       40:        RB_ENTRY(args_entry)     entry;
                     41: };
1.5       nicm       42:
1.45      nicm       43: /* Parsed argument flags and values. */
1.38      nicm       44: struct args {
1.44      nicm       45:        struct args_tree         tree;
1.43      nicm       46:        u_int                    count;
                     47:        struct args_value       *values;
1.38      nicm       48: };
                     49:
1.45      nicm       50: /* Prepared command state. */
                     51: struct args_command_state {
                     52:        struct cmd_list         *cmdlist;
                     53:        char                    *cmd;
                     54:        struct cmd_parse_input   pi;
                     55: };
                     56:
1.14      nicm       57: static struct args_entry       *args_find(struct args *, u_char);
1.5       nicm       58:
1.14      nicm       59: static int     args_cmp(struct args_entry *, struct args_entry *);
                     60: RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
1.5       nicm       61:
                     62: /* Arguments tree comparison function. */
1.14      nicm       63: static int
1.5       nicm       64: args_cmp(struct args_entry *a1, struct args_entry *a2)
                     65: {
                     66:        return (a1->flag - a2->flag);
                     67: }
                     68:
                     69: /* Find a flag in the arguments tree. */
1.14      nicm       70: static struct args_entry *
1.32      nicm       71: args_find(struct args *args, u_char flag)
1.5       nicm       72: {
                     73:        struct args_entry       entry;
                     74:
1.32      nicm       75:        entry.flag = flag;
1.5       nicm       76:        return (RB_FIND(args_tree, &args->tree, &entry));
                     77: }
                     78:
1.43      nicm       79: /* Copy value. */
                     80: static void
                     81: args_copy_value(struct args_value *to, struct args_value *from)
                     82: {
                     83:        to->type = from->type;
                     84:        switch (from->type) {
                     85:        case ARGS_NONE:
                     86:                break;
                     87:        case ARGS_COMMANDS:
                     88:                to->cmdlist = from->cmdlist;
                     89:                to->cmdlist->references++;
                     90:                break;
                     91:        case ARGS_STRING:
                     92:                to->string = xstrdup(from->string);
                     93:                break;
                     94:        }
                     95: }
                     96:
1.42      nicm       97: /* Get value as string. */
1.43      nicm       98: static const char *
1.42      nicm       99: args_value_as_string(struct args_value *value)
                    100: {
                    101:        switch (value->type) {
                    102:        case ARGS_NONE:
1.43      nicm      103:                return ("");
1.42      nicm      104:        case ARGS_COMMANDS:
1.43      nicm      105:                if (value->cached == NULL)
                    106:                        value->cached = cmd_list_print(value->cmdlist, 0);
                    107:                return (value->cached);
1.42      nicm      108:        case ARGS_STRING:
1.43      nicm      109:                return (value->string);
1.42      nicm      110:        }
1.52      nicm      111:        fatalx("unexpected argument type");
1.42      nicm      112: }
                    113:
1.36      nicm      114: /* Create an empty arguments set. */
                    115: struct args *
                    116: args_create(void)
                    117: {
                    118:        struct args      *args;
                    119:
                    120:        args = xcalloc(1, sizeof *args);
                    121:        RB_INIT(&args->tree);
                    122:        return (args);
                    123: }
                    124:
1.42      nicm      125: /* Parse arguments into a new argument set. */
1.1       nicm      126: struct args *
1.42      nicm      127: args_parse(const struct args_parse *parse, struct args_value *values,
1.47      nicm      128:     u_int count, char **cause)
1.1       nicm      129: {
1.42      nicm      130:        struct args             *args;
1.46      nicm      131:        u_int                    i;
1.47      nicm      132:        enum args_parse_type     type;
1.43      nicm      133:        struct args_value       *value, *new;
1.53      nicm      134:        u_char                   flag;
1.43      nicm      135:        const char              *found, *string, *s;
1.53      nicm      136:        int                      optional_argument;
1.1       nicm      137:
1.42      nicm      138:        if (count == 0)
                    139:                return (args_create());
1.1       nicm      140:
1.36      nicm      141:        args = args_create();
1.42      nicm      142:        for (i = 1; i < count; /* nothing */) {
                    143:                value = &values[i];
                    144:                if (value->type != ARGS_STRING)
                    145:                        break;
                    146:
                    147:                string = value->string;
                    148:                if (*string++ != '-' || *string == '\0')
                    149:                        break;
                    150:                i++;
                    151:                if (string[0] == '-' && string[1] == '\0')
                    152:                        break;
                    153:
                    154:                for (;;) {
                    155:                        flag = *string++;
                    156:                        if (flag == '\0')
                    157:                                break;
1.51      nicm      158:                        if (flag == '?') {
                    159:                                args_free(args);
                    160:                                return (NULL);
                    161:                        }
1.42      nicm      162:                        if (!isalnum(flag)) {
1.47      nicm      163:                                xasprintf(cause, "invalid flag -%c", flag);
1.42      nicm      164:                                args_free(args);
                    165:                                return (NULL);
                    166:                        }
                    167:                        found = strchr(parse->template, flag);
                    168:                        if (found == NULL) {
1.47      nicm      169:                                xasprintf(cause, "unknown flag -%c", flag);
1.42      nicm      170:                                args_free(args);
                    171:                                return (NULL);
                    172:                        }
1.53      nicm      173:                        if (*++found != ':') {
1.48      nicm      174:                                log_debug("%s: -%c", __func__, flag);
1.42      nicm      175:                                args_set(args, flag, NULL);
                    176:                                continue;
                    177:                        }
1.53      nicm      178:                        if (*found == ':') {
                    179:                                optional_argument = 1;
                    180:                                found++;
                    181:                        }
1.47      nicm      182:                        new = xcalloc(1, sizeof *new);
1.43      nicm      183:                        if (*string != '\0') {
                    184:                                new->type = ARGS_STRING;
                    185:                                new->string = xstrdup(string);
                    186:                        } else {
1.42      nicm      187:                                if (i == count) {
1.53      nicm      188:                                        if (optional_argument) {
                    189:                                                log_debug("%s: -%c", __func__,
                    190:                                                    flag);
                    191:                                                args_set(args, flag, NULL);
                    192:                                                continue;
                    193:                                        }
1.47      nicm      194:                                        xasprintf(cause,
                    195:                                            "-%c expects an argument",
                    196:                                            flag);
                    197:                                        args_free(args);
                    198:                                        return (NULL);
                    199:                                }
                    200:                                if (values[i].type != ARGS_STRING) {
                    201:                                        xasprintf(cause,
                    202:                                            "-%c argument must be a string",
                    203:                                            flag);
1.42      nicm      204:                                        args_free(args);
                    205:                                        return (NULL);
                    206:                                }
1.43      nicm      207:                                args_copy_value(new, &values[i++]);
1.42      nicm      208:                        }
1.43      nicm      209:                        s = args_value_as_string(new);
1.48      nicm      210:                        log_debug("%s: -%c = %s", __func__, flag, s);
1.43      nicm      211:                        args_set(args, flag, new);
1.42      nicm      212:                        break;
                    213:                }
                    214:        }
                    215:        log_debug("%s: flags end at %u of %u", __func__, i, count);
                    216:        if (i != count) {
                    217:                for (/* nothing */; i < count; i++) {
                    218:                        value = &values[i];
                    219:
                    220:                        s = args_value_as_string(value);
1.48      nicm      221:                        log_debug("%s: %u = %s (type %d)", __func__, i, s,
                    222:                            value->type);
1.43      nicm      223:
1.47      nicm      224:                        if (parse->cb != NULL) {
                    225:                                type = parse->cb(args, args->count, cause);
                    226:                                if (type == ARGS_PARSE_INVALID) {
                    227:                                        args_free(args);
                    228:                                        return (NULL);
                    229:                                }
                    230:                        } else
                    231:                                type = ARGS_PARSE_STRING;
                    232:
1.43      nicm      233:                        args->values = xrecallocarray(args->values,
                    234:                            args->count, args->count + 1, sizeof *args->values);
1.47      nicm      235:                        new = &args->values[args->count++];
                    236:
                    237:                        switch (type) {
                    238:                        case ARGS_PARSE_INVALID:
                    239:                                fatalx("unexpected argument type");
                    240:                        case ARGS_PARSE_STRING:
                    241:                                if (value->type != ARGS_STRING) {
                    242:                                        xasprintf(cause,
                    243:                                            "argument %u must be \"string\"",
                    244:                                            args->count);
                    245:                                        args_free(args);
                    246:                                        return (NULL);
                    247:                                }
                    248:                                args_copy_value(new, value);
                    249:                                break;
                    250:                        case ARGS_PARSE_COMMANDS_OR_STRING:
                    251:                                args_copy_value(new, value);
                    252:                                break;
                    253:                        case ARGS_PARSE_COMMANDS:
                    254:                                if (value->type != ARGS_COMMANDS) {
                    255:                                        xasprintf(cause,
                    256:                                            "argument %u must be { commands }",
                    257:                                            args->count);
                    258:                                        args_free(args);
                    259:                                        return (NULL);
                    260:                                }
                    261:                                args_copy_value(new, value);
                    262:                                break;
                    263:                        }
1.1       nicm      264:                }
                    265:        }
                    266:
1.47      nicm      267:        if (parse->lower != -1 && args->count < (u_int)parse->lower) {
                    268:                xasprintf(cause,
                    269:                    "too few arguments (need at least %u)",
                    270:                    parse->lower);
                    271:                args_free(args);
                    272:                return (NULL);
                    273:        }
                    274:        if (parse->upper != -1 && args->count > (u_int)parse->upper) {
                    275:                xasprintf(cause,
                    276:                    "too many arguments (need at most %u)",
                    277:                    parse->upper);
1.38      nicm      278:                args_free(args);
                    279:                return (NULL);
                    280:        }
1.1       nicm      281:        return (args);
1.42      nicm      282: }
                    283:
1.48      nicm      284: /* Copy and expand a value. */
                    285: static void
                    286: args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
                    287:     char **argv)
                    288: {
                    289:        char    *s, *expanded;
                    290:        int      i;
                    291:
                    292:        to->type = from->type;
                    293:        switch (from->type) {
                    294:        case ARGS_NONE:
                    295:                break;
                    296:        case ARGS_STRING:
                    297:                expanded = xstrdup(from->string);
                    298:                for (i = 0; i < argc; i++) {
                    299:                        s = cmd_template_replace(expanded, argv[i], i + 1);
                    300:                        free(expanded);
                    301:                        expanded = s;
                    302:                }
                    303:                to->string = expanded;
                    304:                break;
                    305:        case ARGS_COMMANDS:
                    306:                to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
                    307:                break;
                    308:        }
                    309: }
                    310:
                    311: /* Copy an arguments set. */
                    312: struct args *
                    313: args_copy(struct args *args, int argc, char **argv)
                    314: {
                    315:        struct args             *new_args;
                    316:        struct args_entry       *entry;
                    317:        struct args_value       *value, *new_value;
                    318:        u_int                    i;
                    319:
1.49      nicm      320:        cmd_log_argv(argc, argv, "%s", __func__);
                    321:
1.48      nicm      322:        new_args = args_create();
                    323:        RB_FOREACH(entry, args_tree, &args->tree) {
1.49      nicm      324:                if (TAILQ_EMPTY(&entry->values)) {
                    325:                        for (i = 0; i < entry->count; i++)
                    326:                                args_set(new_args, entry->flag, NULL);
1.48      nicm      327:                        continue;
                    328:                }
                    329:                TAILQ_FOREACH(value, &entry->values, entry) {
                    330:                        new_value = xcalloc(1, sizeof *new_value);
                    331:                        args_copy_copy_value(new_value, value, argc, argv);
                    332:                        args_set(new_args, entry->flag, new_value);
                    333:                }
                    334:        }
1.49      nicm      335:        if (args->count == 0)
                    336:                return (new_args);
1.48      nicm      337:        new_args->count = args->count;
                    338:        new_args->values = xcalloc(args->count, sizeof *new_args->values);
                    339:        for (i = 0; i < args->count; i++) {
                    340:                new_value = &new_args->values[i];
                    341:                args_copy_copy_value(new_value, &args->values[i], argc, argv);
                    342:        }
                    343:        return (new_args);
                    344: }
                    345:
1.42      nicm      346: /* Free a value. */
                    347: void
                    348: args_free_value(struct args_value *value)
                    349: {
                    350:        switch (value->type) {
                    351:        case ARGS_NONE:
                    352:                break;
                    353:        case ARGS_STRING:
                    354:                free(value->string);
                    355:                break;
                    356:        case ARGS_COMMANDS:
                    357:                cmd_list_free(value->cmdlist);
                    358:                break;
                    359:        }
1.43      nicm      360:        free(value->cached);
1.1       nicm      361: }
                    362:
1.48      nicm      363: /* Free values. */
                    364: void
                    365: args_free_values(struct args_value *values, u_int count)
                    366: {
                    367:        u_int   i;
                    368:
                    369:        for (i = 0; i < count; i++)
                    370:                args_free_value(&values[i]);
                    371: }
                    372:
1.1       nicm      373: /* Free an arguments set. */
                    374: void
                    375: args_free(struct args *args)
                    376: {
1.5       nicm      377:        struct args_entry       *entry;
                    378:        struct args_entry       *entry1;
1.21      nicm      379:        struct args_value       *value;
                    380:        struct args_value       *value1;
1.1       nicm      381:
1.48      nicm      382:        args_free_values(args->values, args->count);
1.43      nicm      383:        free(args->values);
1.1       nicm      384:
1.5       nicm      385:        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                    386:                RB_REMOVE(args_tree, &args->tree, entry);
1.21      nicm      387:                TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
                    388:                        TAILQ_REMOVE(&entry->values, value, entry);
1.44      nicm      389:                        args_free_value(value);
1.21      nicm      390:                        free(value);
                    391:                }
1.5       nicm      392:                free(entry);
                    393:        }
1.1       nicm      394:
1.4       nicm      395:        free(args);
1.1       nicm      396: }
                    397:
1.38      nicm      398: /* Convert arguments to vector. */
                    399: void
1.48      nicm      400: args_to_vector(struct args *args, int *argc, char ***argv)
1.38      nicm      401: {
1.47      nicm      402:        char    *s;
                    403:        u_int    i;
1.43      nicm      404:
                    405:        *argc = 0;
                    406:        *argv = NULL;
                    407:
                    408:        for (i = 0; i < args->count; i++) {
1.47      nicm      409:                switch (args->values[i].type) {
                    410:                case ARGS_NONE:
                    411:                        break;
                    412:                case ARGS_STRING:
                    413:                        cmd_append_argv(argc, argv, args->values[i].string);
                    414:                        break;
                    415:                case ARGS_COMMANDS:
                    416:                        s = cmd_list_print(args->values[i].cmdlist, 0);
                    417:                        cmd_append_argv(argc, argv, s);
                    418:                        free(s);
                    419:                        break;
                    420:                }
1.43      nicm      421:        }
1.38      nicm      422: }
                    423:
1.48      nicm      424: /* Convert arguments from vector. */
                    425: struct args_value *
                    426: args_from_vector(int argc, char **argv)
                    427: {
                    428:        struct args_value       *values;
                    429:        int                      i;
                    430:
                    431:        values = xcalloc(argc, sizeof *values);
                    432:        for (i = 0; i < argc; i++) {
                    433:                values[i].type = ARGS_STRING;
                    434:                values[i].string = xstrdup(argv[i]);
                    435:        }
                    436:        return (values);
                    437: }
                    438:
1.12      nicm      439: /* Add to string. */
                    440: static void printflike(3, 4)
                    441: args_print_add(char **buf, size_t *len, const char *fmt, ...)
                    442: {
1.39      nicm      443:        va_list  ap;
1.12      nicm      444:        char    *s;
                    445:        size_t   slen;
                    446:
                    447:        va_start(ap, fmt);
                    448:        slen = xvasprintf(&s, fmt, ap);
                    449:        va_end(ap);
                    450:
                    451:        *len += slen;
                    452:        *buf = xrealloc(*buf, *len);
                    453:
                    454:        strlcat(*buf, s, *len);
                    455:        free(s);
                    456: }
                    457:
1.43      nicm      458: /* Add value to string. */
1.21      nicm      459: static void
1.43      nicm      460: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21      nicm      461: {
1.43      nicm      462:        char    *expanded = NULL;
1.21      nicm      463:
                    464:        if (**buf != '\0')
                    465:                args_print_add(buf, len, " ");
                    466:
1.43      nicm      467:        switch (value->type) {
                    468:        case ARGS_NONE:
                    469:                break;
                    470:        case ARGS_COMMANDS:
                    471:                expanded = cmd_list_print(value->cmdlist, 0);
                    472:                args_print_add(buf, len, "{ %s }", expanded);
                    473:                break;
                    474:        case ARGS_STRING:
                    475:                expanded = args_escape(value->string);
                    476:                args_print_add(buf, len, "%s", expanded);
                    477:                break;
                    478:        }
                    479:        free(expanded);
1.21      nicm      480: }
                    481:
1.1       nicm      482: /* Print a set of arguments. */
1.12      nicm      483: char *
                    484: args_print(struct args *args)
1.1       nicm      485: {
1.39      nicm      486:        size_t                   len;
1.21      nicm      487:        char                    *buf;
1.43      nicm      488:        u_int                    i, j;
1.5       nicm      489:        struct args_entry       *entry;
1.21      nicm      490:        struct args_value       *value;
1.1       nicm      491:
1.12      nicm      492:        len = 1;
                    493:        buf = xcalloc(1, len);
1.1       nicm      494:
                    495:        /* Process the flags first. */
1.5       nicm      496:        RB_FOREACH(entry, args_tree, &args->tree) {
1.21      nicm      497:                if (!TAILQ_EMPTY(&entry->values))
1.1       nicm      498:                        continue;
                    499:
1.12      nicm      500:                if (*buf == '\0')
                    501:                        args_print_add(&buf, &len, "-");
1.27      nicm      502:                for (j = 0; j < entry->count; j++)
                    503:                        args_print_add(&buf, &len, "%c", entry->flag);
1.1       nicm      504:        }
                    505:
                    506:        /* Then the flags with arguments. */
1.5       nicm      507:        RB_FOREACH(entry, args_tree, &args->tree) {
1.38      nicm      508:                TAILQ_FOREACH(value, &entry->values, entry) {
                    509:                        if (*buf != '\0')
                    510:                                args_print_add(&buf, &len, " -%c", entry->flag);
                    511:                        else
                    512:                                args_print_add(&buf, &len, "-%c", entry->flag);
1.43      nicm      513:                        args_print_add_value(&buf, &len, value);
1.38      nicm      514:                }
1.1       nicm      515:        }
                    516:
                    517:        /* And finally the argument vector. */
1.43      nicm      518:        for (i = 0; i < args->count; i++)
                    519:                args_print_add_value(&buf, &len, &args->values[i]);
1.1       nicm      520:
1.12      nicm      521:        return (buf);
1.22      nicm      522: }
                    523:
                    524: /* Escape an argument. */
                    525: char *
                    526: args_escape(const char *s)
                    527: {
1.48      nicm      528:        static const char        dquoted[] = " #';${}%";
1.35      nicm      529:        static const char        squoted[] = " \"";
1.22      nicm      530:        char                    *escaped, *result;
1.35      nicm      531:        int                      flags, quotes = 0;
1.22      nicm      532:
1.30      nicm      533:        if (*s == '\0') {
                    534:                xasprintf(&result, "''");
                    535:                return (result);
                    536:        }
1.35      nicm      537:        if (s[strcspn(s, dquoted)] != '\0')
                    538:                quotes = '"';
                    539:        else if (s[strcspn(s, squoted)] != '\0')
                    540:                quotes = '\'';
                    541:
1.26      nicm      542:        if (s[0] != ' ' &&
1.35      nicm      543:            s[1] == '\0' &&
                    544:            (quotes != 0 || s[0] == '~')) {
1.22      nicm      545:                xasprintf(&escaped, "\\%c", s[0]);
1.34      nicm      546:                return (escaped);
                    547:        }
                    548:
1.25      nicm      549:        flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35      nicm      550:        if (quotes == '"')
1.22      nicm      551:                flags |= VIS_DQ;
                    552:        utf8_stravis(&escaped, s, flags);
                    553:
1.35      nicm      554:        if (quotes == '\'')
                    555:                xasprintf(&result, "'%s'", escaped);
                    556:        else if (quotes == '"') {
1.22      nicm      557:                if (*escaped == '~')
                    558:                        xasprintf(&result, "\"\\%s\"", escaped);
                    559:                else
                    560:                        xasprintf(&result, "\"%s\"", escaped);
                    561:        } else {
                    562:                if (*escaped == '~')
                    563:                        xasprintf(&result, "\\%s", escaped);
                    564:                else
                    565:                        result = xstrdup(escaped);
                    566:        }
                    567:        free(escaped);
                    568:        return (result);
1.1       nicm      569: }
                    570:
                    571: /* Return if an argument is present. */
                    572: int
1.32      nicm      573: args_has(struct args *args, u_char flag)
1.1       nicm      574: {
1.27      nicm      575:        struct args_entry       *entry;
                    576:
1.32      nicm      577:        entry = args_find(args, flag);
1.27      nicm      578:        if (entry == NULL)
                    579:                return (0);
                    580:        return (entry->count);
1.1       nicm      581: }
                    582:
1.5       nicm      583: /* Set argument value in the arguments tree. */
1.19      nicm      584: void
1.43      nicm      585: args_set(struct args *args, u_char flag, struct args_value *value)
1.1       nicm      586: {
1.5       nicm      587:        struct args_entry       *entry;
                    588:
1.32      nicm      589:        entry = args_find(args, flag);
1.21      nicm      590:        if (entry == NULL) {
1.7       nicm      591:                entry = xcalloc(1, sizeof *entry);
1.32      nicm      592:                entry->flag = flag;
1.27      nicm      593:                entry->count = 1;
1.21      nicm      594:                TAILQ_INIT(&entry->values);
1.7       nicm      595:                RB_INSERT(args_tree, &args->tree, entry);
1.27      nicm      596:        } else
                    597:                entry->count++;
1.43      nicm      598:        if (value != NULL && value->type != ARGS_NONE)
1.21      nicm      599:                TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1       nicm      600: }
                    601:
                    602: /* Get argument value. Will be NULL if it isn't present. */
                    603: const char *
1.32      nicm      604: args_get(struct args *args, u_char flag)
1.1       nicm      605: {
1.5       nicm      606:        struct args_entry       *entry;
                    607:
1.32      nicm      608:        if ((entry = args_find(args, flag)) == NULL)
                    609:                return (NULL);
                    610:        if (TAILQ_EMPTY(&entry->values))
1.5       nicm      611:                return (NULL);
1.41      nicm      612:        return (TAILQ_LAST(&entry->values, args_values)->string);
1.21      nicm      613: }
                    614:
1.32      nicm      615: /* Get first argument. */
                    616: u_char
                    617: args_first(struct args *args, struct args_entry **entry)
                    618: {
                    619:        *entry = RB_MIN(args_tree, &args->tree);
                    620:        if (*entry == NULL)
                    621:                return (0);
                    622:        return ((*entry)->flag);
                    623: }
                    624:
                    625: /* Get next argument. */
                    626: u_char
                    627: args_next(struct args_entry **entry)
                    628: {
                    629:        *entry = RB_NEXT(args_tree, &args->tree, *entry);
                    630:        if (*entry == NULL)
                    631:                return (0);
                    632:        return ((*entry)->flag);
1.38      nicm      633: }
                    634:
                    635: /* Get argument count. */
                    636: u_int
                    637: args_count(struct args *args)
                    638: {
1.43      nicm      639:        return (args->count);
                    640: }
                    641:
1.48      nicm      642: /* Get argument values. */
                    643: struct args_value *
                    644: args_values(struct args *args)
                    645: {
                    646:        return (args->values);
                    647: }
                    648:
1.43      nicm      649: /* Get argument value. */
                    650: struct args_value *
                    651: args_value(struct args *args, u_int idx)
                    652: {
                    653:        if (idx >= args->count)
                    654:                return (NULL);
                    655:        return (&args->values[idx]);
1.38      nicm      656: }
                    657:
                    658: /* Return argument as string. */
                    659: const char *
                    660: args_string(struct args *args, u_int idx)
                    661: {
1.44      nicm      662:        if (idx >= args->count)
1.38      nicm      663:                return (NULL);
1.43      nicm      664:        return (args_value_as_string(&args->values[idx]));
1.45      nicm      665: }
                    666:
                    667: /* Make a command now. */
                    668: struct cmd_list *
1.50      nicm      669: args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
                    670:     int expand)
1.45      nicm      671: {
                    672:        struct args_command_state       *state;
                    673:        char                            *error;
                    674:        struct cmd_list                 *cmdlist;
                    675:
1.50      nicm      676:        state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
1.45      nicm      677:        cmdlist = args_make_commands(state, 0, NULL, &error);
                    678:        if (cmdlist == NULL) {
                    679:                cmdq_error(item, "%s", error);
                    680:                free(error);
                    681:        }
1.48      nicm      682:        else
                    683:                cmdlist->references++;
1.46      nicm      684:        args_make_commands_free(state);
1.45      nicm      685:        return (cmdlist);
                    686: }
                    687:
                    688: /* Save bits to make a command later. */
                    689: struct args_command_state *
                    690: args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
                    691:     const char *default_command, int wait, int expand)
                    692: {
                    693:        struct args                     *args = cmd_get_args(self);
                    694:        struct cmd_find_state           *target = cmdq_get_target(item);
                    695:        struct client                   *tc = cmdq_get_target_client(item);
                    696:        struct args_value               *value;
                    697:        struct args_command_state       *state;
                    698:        const char                      *cmd;
                    699:
                    700:        state = xcalloc(1, sizeof *state);
                    701:
                    702:        if (idx < args->count) {
                    703:                value = &args->values[idx];
                    704:                if (value->type == ARGS_COMMANDS) {
                    705:                        state->cmdlist = value->cmdlist;
                    706:                        state->cmdlist->references++;
                    707:                        return (state);
                    708:                }
                    709:                cmd = value->string;
                    710:        } else {
                    711:                if (default_command == NULL)
                    712:                        fatalx("argument out of range");
                    713:                cmd = default_command;
                    714:        }
                    715:
                    716:
                    717:        if (expand)
                    718:                state->cmd = format_single_from_target(item, cmd);
                    719:        else
                    720:                state->cmd = xstrdup(cmd);
                    721:        log_debug("%s: %s", __func__, state->cmd);
                    722:
                    723:        if (wait)
                    724:                state->pi.item = item;
                    725:        cmd_get_source(self, &state->pi.file, &state->pi.line);
                    726:        state->pi.c = tc;
                    727:        if (state->pi.c != NULL)
                    728:                state->pi.c->references++;
                    729:        cmd_find_copy_state(&state->pi.fs, target);
                    730:
                    731:        return (state);
                    732: }
                    733:
                    734: /* Return argument as command. */
                    735: struct cmd_list *
                    736: args_make_commands(struct args_command_state *state, int argc, char **argv,
                    737:     char **error)
                    738: {
                    739:        struct cmd_parse_result *pr;
                    740:        char                    *cmd, *new_cmd;
                    741:        int                      i;
                    742:
1.48      nicm      743:        if (state->cmdlist != NULL) {
                    744:                if (argc == 0)
                    745:                        return (state->cmdlist);
                    746:                return (cmd_list_copy(state->cmdlist, argc, argv));
                    747:        }
1.45      nicm      748:
                    749:        cmd = xstrdup(state->cmd);
                    750:        for (i = 0; i < argc; i++) {
                    751:                new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
                    752:                log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
                    753:                free(cmd);
                    754:                cmd = new_cmd;
                    755:        }
                    756:        log_debug("%s: %s", __func__, cmd);
                    757:
                    758:        pr = cmd_parse_from_string(cmd, &state->pi);
                    759:        free(cmd);
                    760:        switch (pr->status) {
                    761:        case CMD_PARSE_ERROR:
                    762:                *error = pr->error;
                    763:                return (NULL);
                    764:        case CMD_PARSE_SUCCESS:
                    765:                return (pr->cmdlist);
                    766:        }
1.52      nicm      767:        fatalx("invalid parse return state");
1.45      nicm      768: }
                    769:
                    770: /* Free commands state. */
                    771: void
                    772: args_make_commands_free(struct args_command_state *state)
                    773: {
                    774:        if (state->cmdlist != NULL)
                    775:                cmd_list_free(state->cmdlist);
                    776:        if (state->pi.c != NULL)
                    777:                server_client_unref(state->pi.c);
                    778:        free(state->cmd);
                    779:        free(state);
                    780: }
                    781:
                    782: /* Get prepared command. */
                    783: char *
                    784: args_make_commands_get_command(struct args_command_state *state)
                    785: {
                    786:        struct cmd      *first;
                    787:        int              n;
                    788:        char            *s;
                    789:
                    790:        if (state->cmdlist != NULL) {
                    791:                first = cmd_list_first(state->cmdlist);
                    792:                if (first == NULL)
                    793:                        return (xstrdup(""));
                    794:                return (xstrdup(cmd_get_entry(first)->name));
                    795:        }
                    796:        n = strcspn(state->cmd, " ,");
                    797:        xasprintf(&s, "%.*s", n, state->cmd);
                    798:        return (s);
1.32      nicm      799: }
                    800:
1.21      nicm      801: /* Get first value in argument. */
1.37      nicm      802: struct args_value *
                    803: args_first_value(struct args *args, u_char flag)
1.21      nicm      804: {
                    805:        struct args_entry       *entry;
                    806:
1.32      nicm      807:        if ((entry = args_find(args, flag)) == NULL)
1.21      nicm      808:                return (NULL);
1.37      nicm      809:        return (TAILQ_FIRST(&entry->values));
1.21      nicm      810: }
                    811:
                    812: /* Get next value in argument. */
1.37      nicm      813: struct args_value *
                    814: args_next_value(struct args_value *value)
1.21      nicm      815: {
1.37      nicm      816:        return (TAILQ_NEXT(value, entry));
1.1       nicm      817: }
                    818:
                    819: /* Convert an argument value to a number. */
                    820: long long
1.32      nicm      821: args_strtonum(struct args *args, u_char flag, long long minval,
                    822:     long long maxval, char **cause)
1.1       nicm      823: {
1.5       nicm      824:        const char              *errstr;
1.39      nicm      825:        long long                ll;
1.5       nicm      826:        struct args_entry       *entry;
1.21      nicm      827:        struct args_value       *value;
1.1       nicm      828:
1.32      nicm      829:        if ((entry = args_find(args, flag)) == NULL) {
1.1       nicm      830:                *cause = xstrdup("missing");
                    831:                return (0);
                    832:        }
1.21      nicm      833:        value = TAILQ_LAST(&entry->values, args_values);
1.54      nicm      834:        if (value == NULL ||
                    835:            value->type != ARGS_STRING ||
                    836:            value->string == NULL) {
                    837:                *cause = xstrdup("missing");
                    838:                return (0);
                    839:        }
1.1       nicm      840:
1.41      nicm      841:        ll = strtonum(value->string, minval, maxval, &errstr);
1.1       nicm      842:        if (errstr != NULL) {
                    843:                *cause = xstrdup(errstr);
                    844:                return (0);
1.29      nicm      845:        }
                    846:
                    847:        *cause = NULL;
                    848:        return (ll);
                    849: }
                    850:
1.55      nicm      851: /* Convert an argument value to a number, and expand formats. */
                    852: long long
                    853: args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
                    854:     long long maxval, struct cmdq_item *item, char **cause)
                    855: {
                    856:        const char              *errstr;
                    857:        char                    *formatted;
                    858:        long long                ll;
                    859:        struct args_entry       *entry;
                    860:        struct args_value       *value;
                    861:
                    862:        if ((entry = args_find(args, flag)) == NULL) {
                    863:                *cause = xstrdup("missing");
                    864:                return (0);
                    865:        }
                    866:        value = TAILQ_LAST(&entry->values, args_values);
                    867:        if (value == NULL ||
                    868:            value->type != ARGS_STRING ||
                    869:            value->string == NULL) {
                    870:                *cause = xstrdup("missing");
                    871:                return (0);
                    872:        }
                    873:
                    874:        formatted = format_single_from_target(item, value->string);
                    875:        ll = strtonum(formatted, minval, maxval, &errstr);
                    876:        free(formatted);
                    877:        if (errstr != NULL) {
                    878:                *cause = xstrdup(errstr);
                    879:                return (0);
                    880:        }
                    881:
                    882:        *cause = NULL;
                    883:        return (ll);
                    884: }
                    885:
1.29      nicm      886: /* Convert an argument to a number which may be a percentage. */
                    887: long long
1.32      nicm      888: args_percentage(struct args *args, u_char flag, long long minval,
1.29      nicm      889:     long long maxval, long long curval, char **cause)
                    890: {
1.31      nicm      891:        const char              *value;
1.29      nicm      892:        struct args_entry       *entry;
                    893:
1.32      nicm      894:        if ((entry = args_find(args, flag)) == NULL) {
1.29      nicm      895:                *cause = xstrdup("missing");
                    896:                return (0);
                    897:        }
1.56    ! nicm      898:        if (TAILQ_EMPTY(&entry->values)) {
        !           899:                *cause = xstrdup("empty");
        !           900:                return (0);
        !           901:        }
1.41      nicm      902:        value = TAILQ_LAST(&entry->values, args_values)->string;
1.31      nicm      903:        return (args_string_percentage(value, minval, maxval, curval, cause));
                    904: }
                    905:
                    906: /* Convert a string to a number which may be a percentage. */
                    907: long long
                    908: args_string_percentage(const char *value, long long minval, long long maxval,
                    909:     long long curval, char **cause)
                    910: {
                    911:        const char      *errstr;
1.39      nicm      912:        long long        ll;
1.31      nicm      913:        size_t           valuelen = strlen(value);
                    914:        char            *copy;
1.29      nicm      915:
1.56    ! nicm      916:        if (valuelen == 0) {
        !           917:                *cause = xstrdup("empty");
        !           918:                return (0);
        !           919:        }
1.31      nicm      920:        if (value[valuelen - 1] == '%') {
                    921:                copy = xstrdup(value);
1.29      nicm      922:                copy[valuelen - 1] = '\0';
                    923:
                    924:                ll = strtonum(copy, 0, 100, &errstr);
                    925:                free(copy);
                    926:                if (errstr != NULL) {
                    927:                        *cause = xstrdup(errstr);
                    928:                        return (0);
                    929:                }
                    930:                ll = (curval * ll) / 100;
                    931:                if (ll < minval) {
1.33      nicm      932:                        *cause = xstrdup("too small");
1.29      nicm      933:                        return (0);
                    934:                }
                    935:                if (ll > maxval) {
1.33      nicm      936:                        *cause = xstrdup("too large");
1.29      nicm      937:                        return (0);
                    938:                }
                    939:        } else {
1.31      nicm      940:                ll = strtonum(value, minval, maxval, &errstr);
1.55      nicm      941:                if (errstr != NULL) {
                    942:                        *cause = xstrdup(errstr);
                    943:                        return (0);
                    944:                }
                    945:        }
                    946:
                    947:        *cause = NULL;
                    948:        return (ll);
                    949: }
                    950:
                    951: /*
                    952:  * Convert an argument to a number which may be a percentage, and expand
                    953:  * formats.
                    954:  */
                    955: long long
                    956: args_percentage_and_expand(struct args *args, u_char flag, long long minval,
                    957:     long long maxval, long long curval, struct cmdq_item *item, char **cause)
                    958: {
                    959:        const char              *value;
                    960:        struct args_entry       *entry;
                    961:
                    962:        if ((entry = args_find(args, flag)) == NULL) {
                    963:                *cause = xstrdup("missing");
1.56    ! nicm      964:                return (0);
        !           965:        }
        !           966:        if (TAILQ_EMPTY(&entry->values)) {
        !           967:                *cause = xstrdup("empty");
1.55      nicm      968:                return (0);
                    969:        }
                    970:        value = TAILQ_LAST(&entry->values, args_values)->string;
                    971:        return (args_string_percentage_and_expand(value, minval, maxval, curval,
                    972:                    item, cause));
                    973: }
                    974:
                    975: /*
                    976:  * Convert a string to a number which may be a percentage, and expand formats.
                    977:  */
                    978: long long
                    979: args_string_percentage_and_expand(const char *value, long long minval,
                    980:     long long maxval, long long curval, struct cmdq_item *item, char **cause)
                    981: {
                    982:        const char      *errstr;
                    983:        long long        ll;
                    984:        size_t           valuelen = strlen(value);
                    985:        char            *copy, *f;
                    986:
                    987:        if (value[valuelen - 1] == '%') {
                    988:                copy = xstrdup(value);
                    989:                copy[valuelen - 1] = '\0';
                    990:
                    991:                f = format_single_from_target(item, copy);
                    992:                ll = strtonum(f, 0, 100, &errstr);
                    993:                free(f);
                    994:                free(copy);
                    995:                if (errstr != NULL) {
                    996:                        *cause = xstrdup(errstr);
                    997:                        return (0);
                    998:                }
                    999:                ll = (curval * ll) / 100;
                   1000:                if (ll < minval) {
                   1001:                        *cause = xstrdup("too small");
                   1002:                        return (0);
                   1003:                }
                   1004:                if (ll > maxval) {
                   1005:                        *cause = xstrdup("too large");
                   1006:                        return (0);
                   1007:                }
                   1008:        } else {
                   1009:                f = format_single_from_target(item, value);
                   1010:                ll = strtonum(f, minval, maxval, &errstr);
                   1011:                free(f);
1.29      nicm     1012:                if (errstr != NULL) {
                   1013:                        *cause = xstrdup(errstr);
                   1014:                        return (0);
                   1015:                }
1.1       nicm     1016:        }
                   1017:
                   1018:        *cause = NULL;
                   1019:        return (ll);
                   1020: }