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

1.45    ! nicm        1: /* $OpenBSD: arguments.c,v 1.44 2021/08/21 20:57:52 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:        }
                    111: }
                    112:
1.36      nicm      113: /* Create an empty arguments set. */
                    114: struct args *
                    115: args_create(void)
                    116: {
                    117:        struct args      *args;
                    118:
                    119:        args = xcalloc(1, sizeof *args);
                    120:        RB_INIT(&args->tree);
                    121:        return (args);
                    122: }
                    123:
1.42      nicm      124: /* Parse arguments into a new argument set. */
1.1       nicm      125: struct args *
1.42      nicm      126: args_parse(const struct args_parse *parse, struct args_value *values,
                    127:     u_int count)
1.1       nicm      128: {
1.42      nicm      129:        struct args             *args;
                    130:        u_int                    i;
1.43      nicm      131:        struct args_value       *value, *new;
1.42      nicm      132:        u_char                   flag, argument;
1.43      nicm      133:        const char              *found, *string, *s;
1.1       nicm      134:
1.42      nicm      135:        if (count == 0)
                    136:                return (args_create());
1.1       nicm      137:
1.36      nicm      138:        args = args_create();
1.42      nicm      139:        for (i = 1; i < count; /* nothing */) {
                    140:                value = &values[i];
                    141:                if (value->type != ARGS_STRING)
                    142:                        break;
                    143:
                    144:                string = value->string;
                    145:                if (*string++ != '-' || *string == '\0')
                    146:                        break;
                    147:                i++;
                    148:                if (string[0] == '-' && string[1] == '\0')
                    149:                        break;
                    150:
                    151:                for (;;) {
                    152:                        flag = *string++;
                    153:                        if (flag == '\0')
                    154:                                break;
                    155:                        if (!isalnum(flag)) {
                    156:                                args_free(args);
                    157:                                return (NULL);
                    158:                        }
                    159:                        found = strchr(parse->template, flag);
                    160:                        if (found == NULL) {
                    161:                                args_free(args);
                    162:                                return (NULL);
                    163:                        }
                    164:                        argument = *++found;
                    165:                        if (argument != ':') {
                    166:                                log_debug("%s: add -%c", __func__, flag);
                    167:                                args_set(args, flag, NULL);
                    168:                                continue;
                    169:                        }
1.43      nicm      170:                        new = xcalloc(1, sizeof *value);
                    171:                        if (*string != '\0') {
                    172:                                new->type = ARGS_STRING;
                    173:                                new->string = xstrdup(string);
                    174:                        } else {
1.42      nicm      175:                                if (i == count) {
                    176:                                        args_free(args);
                    177:                                        return (NULL);
                    178:                                }
1.43      nicm      179:                                args_copy_value(new, &values[i++]);
1.42      nicm      180:                        }
1.43      nicm      181:                        s = args_value_as_string(new);
1.42      nicm      182:                        log_debug("%s: add -%c = %s", __func__, flag, s);
1.43      nicm      183:                        args_set(args, flag, new);
1.42      nicm      184:                        break;
                    185:                }
                    186:        }
                    187:        log_debug("%s: flags end at %u of %u", __func__, i, count);
                    188:        if (i != count) {
                    189:                for (/* nothing */; i < count; i++) {
                    190:                        value = &values[i];
                    191:
                    192:                        s = args_value_as_string(value);
                    193:                        log_debug("%s: %u = %s", __func__, i, s);
1.43      nicm      194:
                    195:                        args->values = xrecallocarray(args->values,
                    196:                            args->count, args->count + 1, sizeof *args->values);
                    197:                        args_copy_value(&args->values[args->count++], value);
1.1       nicm      198:                }
                    199:        }
                    200:
1.43      nicm      201:        if ((parse->lower != -1 && args->count < (u_int)parse->lower) ||
                    202:            (parse->upper != -1 && args->count > (u_int)parse->upper)) {
1.38      nicm      203:                args_free(args);
                    204:                return (NULL);
                    205:        }
1.1       nicm      206:        return (args);
1.42      nicm      207: }
                    208:
                    209: /* Free a value. */
                    210: void
                    211: args_free_value(struct args_value *value)
                    212: {
                    213:        switch (value->type) {
                    214:        case ARGS_NONE:
                    215:                break;
                    216:        case ARGS_STRING:
                    217:                free(value->string);
                    218:                break;
                    219:        case ARGS_COMMANDS:
                    220:                cmd_list_free(value->cmdlist);
                    221:                break;
                    222:        }
1.43      nicm      223:        free(value->cached);
1.1       nicm      224: }
                    225:
                    226: /* Free an arguments set. */
                    227: void
                    228: args_free(struct args *args)
                    229: {
1.5       nicm      230:        struct args_entry       *entry;
                    231:        struct args_entry       *entry1;
1.21      nicm      232:        struct args_value       *value;
                    233:        struct args_value       *value1;
1.43      nicm      234:        u_int                    i;
1.1       nicm      235:
1.43      nicm      236:        for (i = 0; i < args->count; i++)
                    237:                args_free_value(&args->values[i]);
                    238:        free(args->values);
1.1       nicm      239:
1.5       nicm      240:        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                    241:                RB_REMOVE(args_tree, &args->tree, entry);
1.21      nicm      242:                TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
                    243:                        TAILQ_REMOVE(&entry->values, value, entry);
1.44      nicm      244:                        args_free_value(value);
1.21      nicm      245:                        free(value);
                    246:                }
1.5       nicm      247:                free(entry);
                    248:        }
1.1       nicm      249:
1.4       nicm      250:        free(args);
1.1       nicm      251: }
                    252:
1.38      nicm      253: /* Convert arguments to vector. */
                    254: void
                    255: args_vector(struct args *args, int *argc, char ***argv)
                    256: {
1.43      nicm      257:        struct args_value       *value;
                    258:        u_int                    i;
                    259:
                    260:        *argc = 0;
                    261:        *argv = NULL;
                    262:
                    263:        for (i = 0; i < args->count; i++) {
                    264:                value = &args->values[i];
                    265:                cmd_append_argv(argc, argv, args_value_as_string(value));
                    266:        }
1.38      nicm      267: }
                    268:
1.12      nicm      269: /* Add to string. */
                    270: static void printflike(3, 4)
                    271: args_print_add(char **buf, size_t *len, const char *fmt, ...)
                    272: {
1.39      nicm      273:        va_list  ap;
1.12      nicm      274:        char    *s;
                    275:        size_t   slen;
                    276:
                    277:        va_start(ap, fmt);
                    278:        slen = xvasprintf(&s, fmt, ap);
                    279:        va_end(ap);
                    280:
                    281:        *len += slen;
                    282:        *buf = xrealloc(*buf, *len);
                    283:
                    284:        strlcat(*buf, s, *len);
                    285:        free(s);
                    286: }
                    287:
1.43      nicm      288: /* Add value to string. */
1.21      nicm      289: static void
1.43      nicm      290: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21      nicm      291: {
1.43      nicm      292:        char    *expanded = NULL;
1.21      nicm      293:
                    294:        if (**buf != '\0')
                    295:                args_print_add(buf, len, " ");
                    296:
1.43      nicm      297:        switch (value->type) {
                    298:        case ARGS_NONE:
                    299:                break;
                    300:        case ARGS_COMMANDS:
                    301:                expanded = cmd_list_print(value->cmdlist, 0);
                    302:                args_print_add(buf, len, "{ %s }", expanded);
                    303:                break;
                    304:        case ARGS_STRING:
                    305:                expanded = args_escape(value->string);
                    306:                args_print_add(buf, len, "%s", expanded);
                    307:                break;
                    308:        }
                    309:        free(expanded);
1.21      nicm      310: }
                    311:
1.1       nicm      312: /* Print a set of arguments. */
1.12      nicm      313: char *
                    314: args_print(struct args *args)
1.1       nicm      315: {
1.39      nicm      316:        size_t                   len;
1.21      nicm      317:        char                    *buf;
1.43      nicm      318:        u_int                    i, j;
1.5       nicm      319:        struct args_entry       *entry;
1.21      nicm      320:        struct args_value       *value;
1.1       nicm      321:
1.12      nicm      322:        len = 1;
                    323:        buf = xcalloc(1, len);
1.1       nicm      324:
                    325:        /* Process the flags first. */
1.5       nicm      326:        RB_FOREACH(entry, args_tree, &args->tree) {
1.21      nicm      327:                if (!TAILQ_EMPTY(&entry->values))
1.1       nicm      328:                        continue;
                    329:
1.12      nicm      330:                if (*buf == '\0')
                    331:                        args_print_add(&buf, &len, "-");
1.27      nicm      332:                for (j = 0; j < entry->count; j++)
                    333:                        args_print_add(&buf, &len, "%c", entry->flag);
1.1       nicm      334:        }
                    335:
                    336:        /* Then the flags with arguments. */
1.5       nicm      337:        RB_FOREACH(entry, args_tree, &args->tree) {
1.38      nicm      338:                TAILQ_FOREACH(value, &entry->values, entry) {
                    339:                        if (*buf != '\0')
                    340:                                args_print_add(&buf, &len, " -%c", entry->flag);
                    341:                        else
                    342:                                args_print_add(&buf, &len, "-%c", entry->flag);
1.43      nicm      343:                        args_print_add_value(&buf, &len, value);
1.38      nicm      344:                }
1.1       nicm      345:        }
                    346:
                    347:        /* And finally the argument vector. */
1.43      nicm      348:        for (i = 0; i < args->count; i++)
                    349:                args_print_add_value(&buf, &len, &args->values[i]);
1.1       nicm      350:
1.12      nicm      351:        return (buf);
1.22      nicm      352: }
                    353:
                    354: /* Escape an argument. */
                    355: char *
                    356: args_escape(const char *s)
                    357: {
1.35      nicm      358:        static const char        dquoted[] = " #';${}";
                    359:        static const char        squoted[] = " \"";
1.22      nicm      360:        char                    *escaped, *result;
1.35      nicm      361:        int                      flags, quotes = 0;
1.22      nicm      362:
1.30      nicm      363:        if (*s == '\0') {
                    364:                xasprintf(&result, "''");
                    365:                return (result);
                    366:        }
1.35      nicm      367:        if (s[strcspn(s, dquoted)] != '\0')
                    368:                quotes = '"';
                    369:        else if (s[strcspn(s, squoted)] != '\0')
                    370:                quotes = '\'';
                    371:
1.26      nicm      372:        if (s[0] != ' ' &&
1.35      nicm      373:            s[1] == '\0' &&
                    374:            (quotes != 0 || s[0] == '~')) {
1.22      nicm      375:                xasprintf(&escaped, "\\%c", s[0]);
1.34      nicm      376:                return (escaped);
                    377:        }
                    378:
1.25      nicm      379:        flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35      nicm      380:        if (quotes == '"')
1.22      nicm      381:                flags |= VIS_DQ;
                    382:        utf8_stravis(&escaped, s, flags);
                    383:
1.35      nicm      384:        if (quotes == '\'')
                    385:                xasprintf(&result, "'%s'", escaped);
                    386:        else if (quotes == '"') {
1.22      nicm      387:                if (*escaped == '~')
                    388:                        xasprintf(&result, "\"\\%s\"", escaped);
                    389:                else
                    390:                        xasprintf(&result, "\"%s\"", escaped);
                    391:        } else {
                    392:                if (*escaped == '~')
                    393:                        xasprintf(&result, "\\%s", escaped);
                    394:                else
                    395:                        result = xstrdup(escaped);
                    396:        }
                    397:        free(escaped);
                    398:        return (result);
1.1       nicm      399: }
                    400:
                    401: /* Return if an argument is present. */
                    402: int
1.32      nicm      403: args_has(struct args *args, u_char flag)
1.1       nicm      404: {
1.27      nicm      405:        struct args_entry       *entry;
                    406:
1.32      nicm      407:        entry = args_find(args, flag);
1.27      nicm      408:        if (entry == NULL)
                    409:                return (0);
                    410:        return (entry->count);
1.1       nicm      411: }
                    412:
1.5       nicm      413: /* Set argument value in the arguments tree. */
1.19      nicm      414: void
1.43      nicm      415: args_set(struct args *args, u_char flag, struct args_value *value)
1.1       nicm      416: {
1.5       nicm      417:        struct args_entry       *entry;
                    418:
1.32      nicm      419:        entry = args_find(args, flag);
1.21      nicm      420:        if (entry == NULL) {
1.7       nicm      421:                entry = xcalloc(1, sizeof *entry);
1.32      nicm      422:                entry->flag = flag;
1.27      nicm      423:                entry->count = 1;
1.21      nicm      424:                TAILQ_INIT(&entry->values);
1.7       nicm      425:                RB_INSERT(args_tree, &args->tree, entry);
1.27      nicm      426:        } else
                    427:                entry->count++;
1.43      nicm      428:        if (value != NULL && value->type != ARGS_NONE)
1.21      nicm      429:                TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1       nicm      430: }
                    431:
                    432: /* Get argument value. Will be NULL if it isn't present. */
                    433: const char *
1.32      nicm      434: args_get(struct args *args, u_char flag)
1.1       nicm      435: {
1.5       nicm      436:        struct args_entry       *entry;
                    437:
1.32      nicm      438:        if ((entry = args_find(args, flag)) == NULL)
                    439:                return (NULL);
                    440:        if (TAILQ_EMPTY(&entry->values))
1.5       nicm      441:                return (NULL);
1.41      nicm      442:        return (TAILQ_LAST(&entry->values, args_values)->string);
1.21      nicm      443: }
                    444:
1.32      nicm      445: /* Get first argument. */
                    446: u_char
                    447: args_first(struct args *args, struct args_entry **entry)
                    448: {
                    449:        *entry = RB_MIN(args_tree, &args->tree);
                    450:        if (*entry == NULL)
                    451:                return (0);
                    452:        return ((*entry)->flag);
                    453: }
                    454:
                    455: /* Get next argument. */
                    456: u_char
                    457: args_next(struct args_entry **entry)
                    458: {
                    459:        *entry = RB_NEXT(args_tree, &args->tree, *entry);
                    460:        if (*entry == NULL)
                    461:                return (0);
                    462:        return ((*entry)->flag);
1.38      nicm      463: }
                    464:
                    465: /* Get argument count. */
                    466: u_int
                    467: args_count(struct args *args)
                    468: {
1.43      nicm      469:        return (args->count);
                    470: }
                    471:
                    472: /* Get argument value. */
                    473: struct args_value *
                    474: args_value(struct args *args, u_int idx)
                    475: {
                    476:        if (idx >= args->count)
                    477:                return (NULL);
                    478:        return (&args->values[idx]);
1.38      nicm      479: }
                    480:
                    481: /* Return argument as string. */
                    482: const char *
                    483: args_string(struct args *args, u_int idx)
                    484: {
1.44      nicm      485:        if (idx >= args->count)
1.38      nicm      486:                return (NULL);
1.43      nicm      487:        return (args_value_as_string(&args->values[idx]));
1.45    ! nicm      488: }
        !           489:
        !           490: /* Make a command now. */
        !           491: struct cmd_list *
        !           492: args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx)
        !           493: {
        !           494:        struct args_command_state       *state;
        !           495:        char                            *error;
        !           496:        struct cmd_list                 *cmdlist;
        !           497:
        !           498:        state = args_make_commands_prepare(self, item, idx, NULL, 0, 0);
        !           499:        cmdlist = args_make_commands(state, 0, NULL, &error);
        !           500:        args_make_commands_free(state);
        !           501:        if (cmdlist == NULL) {
        !           502:                cmdq_error(item, "%s", error);
        !           503:                free(error);
        !           504:        }
        !           505:        return (cmdlist);
        !           506: }
        !           507:
        !           508: /* Save bits to make a command later. */
        !           509: struct args_command_state *
        !           510: args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
        !           511:     const char *default_command, int wait, int expand)
        !           512: {
        !           513:        struct args                     *args = cmd_get_args(self);
        !           514:        struct cmd_find_state           *target = cmdq_get_target(item);
        !           515:        struct client                   *tc = cmdq_get_target_client(item);
        !           516:        struct args_value               *value;
        !           517:        struct args_command_state       *state;
        !           518:        const char                      *cmd;
        !           519:
        !           520:        state = xcalloc(1, sizeof *state);
        !           521:
        !           522:        if (idx < args->count) {
        !           523:                value = &args->values[idx];
        !           524:                if (value->type == ARGS_COMMANDS) {
        !           525:                        state->cmdlist = value->cmdlist;
        !           526:                        state->cmdlist->references++;
        !           527:                        return (state);
        !           528:                }
        !           529:                cmd = value->string;
        !           530:        } else {
        !           531:                if (default_command == NULL)
        !           532:                        fatalx("argument out of range");
        !           533:                cmd = default_command;
        !           534:        }
        !           535:
        !           536:
        !           537:        if (expand)
        !           538:                state->cmd = format_single_from_target(item, cmd);
        !           539:        else
        !           540:                state->cmd = xstrdup(cmd);
        !           541:        log_debug("%s: %s", __func__, state->cmd);
        !           542:
        !           543:        if (wait)
        !           544:                state->pi.item = item;
        !           545:        cmd_get_source(self, &state->pi.file, &state->pi.line);
        !           546:        state->pi.c = tc;
        !           547:        if (state->pi.c != NULL)
        !           548:                state->pi.c->references++;
        !           549:        cmd_find_copy_state(&state->pi.fs, target);
        !           550:
        !           551:        return (state);
        !           552: }
        !           553:
        !           554: /* Return argument as command. */
        !           555: struct cmd_list *
        !           556: args_make_commands(struct args_command_state *state, int argc, char **argv,
        !           557:     char **error)
        !           558: {
        !           559:        struct cmd_parse_result *pr;
        !           560:        char                    *cmd, *new_cmd;
        !           561:        int                      i;
        !           562:
        !           563:        if (state->cmdlist != NULL)
        !           564:                return (state->cmdlist);
        !           565:
        !           566:        cmd = xstrdup(state->cmd);
        !           567:        for (i = 0; i < argc; i++) {
        !           568:                new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
        !           569:                log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
        !           570:                free(cmd);
        !           571:                cmd = new_cmd;
        !           572:        }
        !           573:        log_debug("%s: %s", __func__, cmd);
        !           574:
        !           575:        pr = cmd_parse_from_string(cmd, &state->pi);
        !           576:        free(cmd);
        !           577:        switch (pr->status) {
        !           578:        case CMD_PARSE_ERROR:
        !           579:                *error = pr->error;
        !           580:                return (NULL);
        !           581:        case CMD_PARSE_SUCCESS:
        !           582:                return (pr->cmdlist);
        !           583:        }
        !           584: }
        !           585:
        !           586: /* Free commands state. */
        !           587: void
        !           588: args_make_commands_free(struct args_command_state *state)
        !           589: {
        !           590:        if (state->cmdlist != NULL)
        !           591:                cmd_list_free(state->cmdlist);
        !           592:        if (state->pi.c != NULL)
        !           593:                server_client_unref(state->pi.c);
        !           594:        free(state->cmd);
        !           595:        free(state);
        !           596: }
        !           597:
        !           598: /* Get prepared command. */
        !           599: char *
        !           600: args_make_commands_get_command(struct args_command_state *state)
        !           601: {
        !           602:        struct cmd      *first;
        !           603:        int              n;
        !           604:        char            *s;
        !           605:
        !           606:        if (state->cmdlist != NULL) {
        !           607:                first = cmd_list_first(state->cmdlist);
        !           608:                if (first == NULL)
        !           609:                        return (xstrdup(""));
        !           610:                return (xstrdup(cmd_get_entry(first)->name));
        !           611:        }
        !           612:        n = strcspn(state->cmd, " ,");
        !           613:        xasprintf(&s, "%.*s", n, state->cmd);
        !           614:        return (s);
1.32      nicm      615: }
                    616:
1.21      nicm      617: /* Get first value in argument. */
1.37      nicm      618: struct args_value *
                    619: args_first_value(struct args *args, u_char flag)
1.21      nicm      620: {
                    621:        struct args_entry       *entry;
                    622:
1.32      nicm      623:        if ((entry = args_find(args, flag)) == NULL)
1.21      nicm      624:                return (NULL);
1.37      nicm      625:        return (TAILQ_FIRST(&entry->values));
1.21      nicm      626: }
                    627:
                    628: /* Get next value in argument. */
1.37      nicm      629: struct args_value *
                    630: args_next_value(struct args_value *value)
1.21      nicm      631: {
1.37      nicm      632:        return (TAILQ_NEXT(value, entry));
1.1       nicm      633: }
                    634:
                    635: /* Convert an argument value to a number. */
                    636: long long
1.32      nicm      637: args_strtonum(struct args *args, u_char flag, long long minval,
                    638:     long long maxval, char **cause)
1.1       nicm      639: {
1.5       nicm      640:        const char              *errstr;
1.39      nicm      641:        long long                ll;
1.5       nicm      642:        struct args_entry       *entry;
1.21      nicm      643:        struct args_value       *value;
1.1       nicm      644:
1.32      nicm      645:        if ((entry = args_find(args, flag)) == NULL) {
1.1       nicm      646:                *cause = xstrdup("missing");
                    647:                return (0);
                    648:        }
1.21      nicm      649:        value = TAILQ_LAST(&entry->values, args_values);
1.1       nicm      650:
1.41      nicm      651:        ll = strtonum(value->string, minval, maxval, &errstr);
1.1       nicm      652:        if (errstr != NULL) {
                    653:                *cause = xstrdup(errstr);
                    654:                return (0);
1.29      nicm      655:        }
                    656:
                    657:        *cause = NULL;
                    658:        return (ll);
                    659: }
                    660:
                    661: /* Convert an argument to a number which may be a percentage. */
                    662: long long
1.32      nicm      663: args_percentage(struct args *args, u_char flag, long long minval,
1.29      nicm      664:     long long maxval, long long curval, char **cause)
                    665: {
1.31      nicm      666:        const char              *value;
1.29      nicm      667:        struct args_entry       *entry;
                    668:
1.32      nicm      669:        if ((entry = args_find(args, flag)) == NULL) {
1.29      nicm      670:                *cause = xstrdup("missing");
                    671:                return (0);
                    672:        }
1.41      nicm      673:        value = TAILQ_LAST(&entry->values, args_values)->string;
1.31      nicm      674:        return (args_string_percentage(value, minval, maxval, curval, cause));
                    675: }
                    676:
                    677: /* Convert a string to a number which may be a percentage. */
                    678: long long
                    679: args_string_percentage(const char *value, long long minval, long long maxval,
                    680:     long long curval, char **cause)
                    681: {
                    682:        const char      *errstr;
1.39      nicm      683:        long long        ll;
1.31      nicm      684:        size_t           valuelen = strlen(value);
                    685:        char            *copy;
1.29      nicm      686:
1.31      nicm      687:        if (value[valuelen - 1] == '%') {
                    688:                copy = xstrdup(value);
1.29      nicm      689:                copy[valuelen - 1] = '\0';
                    690:
                    691:                ll = strtonum(copy, 0, 100, &errstr);
                    692:                free(copy);
                    693:                if (errstr != NULL) {
                    694:                        *cause = xstrdup(errstr);
                    695:                        return (0);
                    696:                }
                    697:                ll = (curval * ll) / 100;
                    698:                if (ll < minval) {
1.33      nicm      699:                        *cause = xstrdup("too small");
1.29      nicm      700:                        return (0);
                    701:                }
                    702:                if (ll > maxval) {
1.33      nicm      703:                        *cause = xstrdup("too large");
1.29      nicm      704:                        return (0);
                    705:                }
                    706:        } else {
1.31      nicm      707:                ll = strtonum(value, minval, maxval, &errstr);
1.29      nicm      708:                if (errstr != NULL) {
                    709:                        *cause = xstrdup(errstr);
                    710:                        return (0);
                    711:                }
1.1       nicm      712:        }
                    713:
                    714:        *cause = NULL;
                    715:        return (ll);
                    716: }