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

1.50    ! nicm        1: /* $OpenBSD: arguments.c,v 1.49 2021/09/02 07:11:03 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,
1.47      nicm      127:     u_int count, char **cause)
1.1       nicm      128: {
1.42      nicm      129:        struct args             *args;
1.46      nicm      130:        u_int                    i;
1.47      nicm      131:        enum args_parse_type     type;
1.43      nicm      132:        struct args_value       *value, *new;
1.42      nicm      133:        u_char                   flag, argument;
1.43      nicm      134:        const char              *found, *string, *s;
1.1       nicm      135:
1.42      nicm      136:        if (count == 0)
                    137:                return (args_create());
1.1       nicm      138:
1.36      nicm      139:        args = args_create();
1.42      nicm      140:        for (i = 1; i < count; /* nothing */) {
                    141:                value = &values[i];
                    142:                if (value->type != ARGS_STRING)
                    143:                        break;
                    144:
                    145:                string = value->string;
                    146:                if (*string++ != '-' || *string == '\0')
                    147:                        break;
                    148:                i++;
                    149:                if (string[0] == '-' && string[1] == '\0')
                    150:                        break;
                    151:
                    152:                for (;;) {
                    153:                        flag = *string++;
                    154:                        if (flag == '\0')
                    155:                                break;
                    156:                        if (!isalnum(flag)) {
1.47      nicm      157:                                xasprintf(cause, "invalid flag -%c", flag);
1.42      nicm      158:                                args_free(args);
                    159:                                return (NULL);
                    160:                        }
                    161:                        found = strchr(parse->template, flag);
                    162:                        if (found == NULL) {
1.47      nicm      163:                                xasprintf(cause, "unknown flag -%c", flag);
1.42      nicm      164:                                args_free(args);
                    165:                                return (NULL);
                    166:                        }
                    167:                        argument = *++found;
                    168:                        if (argument != ':') {
1.48      nicm      169:                                log_debug("%s: -%c", __func__, flag);
1.42      nicm      170:                                args_set(args, flag, NULL);
                    171:                                continue;
                    172:                        }
1.47      nicm      173:                        new = xcalloc(1, sizeof *new);
1.43      nicm      174:                        if (*string != '\0') {
                    175:                                new->type = ARGS_STRING;
                    176:                                new->string = xstrdup(string);
                    177:                        } else {
1.42      nicm      178:                                if (i == count) {
1.47      nicm      179:                                        xasprintf(cause,
                    180:                                            "-%c expects an argument",
                    181:                                            flag);
                    182:                                        args_free(args);
                    183:                                        return (NULL);
                    184:                                }
                    185:                                if (values[i].type != ARGS_STRING) {
                    186:                                        xasprintf(cause,
                    187:                                            "-%c argument must be a string",
                    188:                                            flag);
1.42      nicm      189:                                        args_free(args);
                    190:                                        return (NULL);
                    191:                                }
1.43      nicm      192:                                args_copy_value(new, &values[i++]);
1.42      nicm      193:                        }
1.43      nicm      194:                        s = args_value_as_string(new);
1.48      nicm      195:                        log_debug("%s: -%c = %s", __func__, flag, s);
1.43      nicm      196:                        args_set(args, flag, new);
1.42      nicm      197:                        break;
                    198:                }
                    199:        }
                    200:        log_debug("%s: flags end at %u of %u", __func__, i, count);
                    201:        if (i != count) {
                    202:                for (/* nothing */; i < count; i++) {
                    203:                        value = &values[i];
                    204:
                    205:                        s = args_value_as_string(value);
1.48      nicm      206:                        log_debug("%s: %u = %s (type %d)", __func__, i, s,
                    207:                            value->type);
1.43      nicm      208:
1.47      nicm      209:                        if (parse->cb != NULL) {
                    210:                                type = parse->cb(args, args->count, cause);
                    211:                                if (type == ARGS_PARSE_INVALID) {
                    212:                                        args_free(args);
                    213:                                        return (NULL);
                    214:                                }
                    215:                        } else
                    216:                                type = ARGS_PARSE_STRING;
                    217:
1.43      nicm      218:                        args->values = xrecallocarray(args->values,
                    219:                            args->count, args->count + 1, sizeof *args->values);
1.47      nicm      220:                        new = &args->values[args->count++];
                    221:
                    222:                        switch (type) {
                    223:                        case ARGS_PARSE_INVALID:
                    224:                                fatalx("unexpected argument type");
                    225:                        case ARGS_PARSE_STRING:
                    226:                                if (value->type != ARGS_STRING) {
                    227:                                        xasprintf(cause,
                    228:                                            "argument %u must be \"string\"",
                    229:                                            args->count);
                    230:                                        args_free(args);
                    231:                                        return (NULL);
                    232:                                }
                    233:                                args_copy_value(new, value);
                    234:                                break;
                    235:                        case ARGS_PARSE_COMMANDS_OR_STRING:
                    236:                                args_copy_value(new, value);
                    237:                                break;
                    238:                        case ARGS_PARSE_COMMANDS:
                    239:                                if (value->type != ARGS_COMMANDS) {
                    240:                                        xasprintf(cause,
                    241:                                            "argument %u must be { commands }",
                    242:                                            args->count);
                    243:                                        args_free(args);
                    244:                                        return (NULL);
                    245:                                }
                    246:                                args_copy_value(new, value);
                    247:                                break;
                    248:                        }
1.1       nicm      249:                }
                    250:        }
                    251:
1.47      nicm      252:        if (parse->lower != -1 && args->count < (u_int)parse->lower) {
                    253:                xasprintf(cause,
                    254:                    "too few arguments (need at least %u)",
                    255:                    parse->lower);
                    256:                args_free(args);
                    257:                return (NULL);
                    258:        }
                    259:        if (parse->upper != -1 && args->count > (u_int)parse->upper) {
                    260:                xasprintf(cause,
                    261:                    "too many arguments (need at most %u)",
                    262:                    parse->upper);
1.38      nicm      263:                args_free(args);
                    264:                return (NULL);
                    265:        }
1.1       nicm      266:        return (args);
1.42      nicm      267: }
                    268:
1.48      nicm      269: /* Copy and expand a value. */
                    270: static void
                    271: args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
                    272:     char **argv)
                    273: {
                    274:        char    *s, *expanded;
                    275:        int      i;
                    276:
                    277:        to->type = from->type;
                    278:        switch (from->type) {
                    279:        case ARGS_NONE:
                    280:                break;
                    281:        case ARGS_STRING:
                    282:                expanded = xstrdup(from->string);
                    283:                for (i = 0; i < argc; i++) {
                    284:                        s = cmd_template_replace(expanded, argv[i], i + 1);
                    285:                        free(expanded);
                    286:                        expanded = s;
                    287:                }
                    288:                to->string = expanded;
                    289:                break;
                    290:        case ARGS_COMMANDS:
                    291:                to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
                    292:                break;
                    293:        }
                    294: }
                    295:
                    296: /* Copy an arguments set. */
                    297: struct args *
                    298: args_copy(struct args *args, int argc, char **argv)
                    299: {
                    300:        struct args             *new_args;
                    301:        struct args_entry       *entry;
                    302:        struct args_value       *value, *new_value;
                    303:        u_int                    i;
                    304:
1.49      nicm      305:        cmd_log_argv(argc, argv, "%s", __func__);
                    306:
1.48      nicm      307:        new_args = args_create();
                    308:        RB_FOREACH(entry, args_tree, &args->tree) {
1.49      nicm      309:                if (TAILQ_EMPTY(&entry->values)) {
                    310:                        for (i = 0; i < entry->count; i++)
                    311:                                args_set(new_args, entry->flag, NULL);
1.48      nicm      312:                        continue;
                    313:                }
                    314:                TAILQ_FOREACH(value, &entry->values, entry) {
                    315:                        new_value = xcalloc(1, sizeof *new_value);
                    316:                        args_copy_copy_value(new_value, value, argc, argv);
                    317:                        args_set(new_args, entry->flag, new_value);
                    318:                }
                    319:        }
1.49      nicm      320:        if (args->count == 0)
                    321:                return (new_args);
1.48      nicm      322:        new_args->count = args->count;
                    323:        new_args->values = xcalloc(args->count, sizeof *new_args->values);
                    324:        for (i = 0; i < args->count; i++) {
                    325:                new_value = &new_args->values[i];
                    326:                args_copy_copy_value(new_value, &args->values[i], argc, argv);
                    327:        }
                    328:        return (new_args);
                    329: }
                    330:
1.42      nicm      331: /* Free a value. */
                    332: void
                    333: args_free_value(struct args_value *value)
                    334: {
                    335:        switch (value->type) {
                    336:        case ARGS_NONE:
                    337:                break;
                    338:        case ARGS_STRING:
                    339:                free(value->string);
                    340:                break;
                    341:        case ARGS_COMMANDS:
                    342:                cmd_list_free(value->cmdlist);
                    343:                break;
                    344:        }
1.43      nicm      345:        free(value->cached);
1.1       nicm      346: }
                    347:
1.48      nicm      348: /* Free values. */
                    349: void
                    350: args_free_values(struct args_value *values, u_int count)
                    351: {
                    352:        u_int   i;
                    353:
                    354:        for (i = 0; i < count; i++)
                    355:                args_free_value(&values[i]);
                    356: }
                    357:
1.1       nicm      358: /* Free an arguments set. */
                    359: void
                    360: args_free(struct args *args)
                    361: {
1.5       nicm      362:        struct args_entry       *entry;
                    363:        struct args_entry       *entry1;
1.21      nicm      364:        struct args_value       *value;
                    365:        struct args_value       *value1;
1.1       nicm      366:
1.48      nicm      367:        args_free_values(args->values, args->count);
1.43      nicm      368:        free(args->values);
1.1       nicm      369:
1.5       nicm      370:        RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
                    371:                RB_REMOVE(args_tree, &args->tree, entry);
1.21      nicm      372:                TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
                    373:                        TAILQ_REMOVE(&entry->values, value, entry);
1.44      nicm      374:                        args_free_value(value);
1.21      nicm      375:                        free(value);
                    376:                }
1.5       nicm      377:                free(entry);
                    378:        }
1.1       nicm      379:
1.4       nicm      380:        free(args);
1.1       nicm      381: }
                    382:
1.38      nicm      383: /* Convert arguments to vector. */
                    384: void
1.48      nicm      385: args_to_vector(struct args *args, int *argc, char ***argv)
1.38      nicm      386: {
1.47      nicm      387:        char    *s;
                    388:        u_int    i;
1.43      nicm      389:
                    390:        *argc = 0;
                    391:        *argv = NULL;
                    392:
                    393:        for (i = 0; i < args->count; i++) {
1.47      nicm      394:                switch (args->values[i].type) {
                    395:                case ARGS_NONE:
                    396:                        break;
                    397:                case ARGS_STRING:
                    398:                        cmd_append_argv(argc, argv, args->values[i].string);
                    399:                        break;
                    400:                case ARGS_COMMANDS:
                    401:                        s = cmd_list_print(args->values[i].cmdlist, 0);
                    402:                        cmd_append_argv(argc, argv, s);
                    403:                        free(s);
                    404:                        break;
                    405:                }
1.43      nicm      406:        }
1.38      nicm      407: }
                    408:
1.48      nicm      409: /* Convert arguments from vector. */
                    410: struct args_value *
                    411: args_from_vector(int argc, char **argv)
                    412: {
                    413:        struct args_value       *values;
                    414:        int                      i;
                    415:
                    416:        values = xcalloc(argc, sizeof *values);
                    417:        for (i = 0; i < argc; i++) {
                    418:                values[i].type = ARGS_STRING;
                    419:                values[i].string = xstrdup(argv[i]);
                    420:        }
                    421:        return (values);
                    422: }
                    423:
1.12      nicm      424: /* Add to string. */
                    425: static void printflike(3, 4)
                    426: args_print_add(char **buf, size_t *len, const char *fmt, ...)
                    427: {
1.39      nicm      428:        va_list  ap;
1.12      nicm      429:        char    *s;
                    430:        size_t   slen;
                    431:
                    432:        va_start(ap, fmt);
                    433:        slen = xvasprintf(&s, fmt, ap);
                    434:        va_end(ap);
                    435:
                    436:        *len += slen;
                    437:        *buf = xrealloc(*buf, *len);
                    438:
                    439:        strlcat(*buf, s, *len);
                    440:        free(s);
                    441: }
                    442:
1.43      nicm      443: /* Add value to string. */
1.21      nicm      444: static void
1.43      nicm      445: args_print_add_value(char **buf, size_t *len, struct args_value *value)
1.21      nicm      446: {
1.43      nicm      447:        char    *expanded = NULL;
1.21      nicm      448:
                    449:        if (**buf != '\0')
                    450:                args_print_add(buf, len, " ");
                    451:
1.43      nicm      452:        switch (value->type) {
                    453:        case ARGS_NONE:
                    454:                break;
                    455:        case ARGS_COMMANDS:
                    456:                expanded = cmd_list_print(value->cmdlist, 0);
                    457:                args_print_add(buf, len, "{ %s }", expanded);
                    458:                break;
                    459:        case ARGS_STRING:
                    460:                expanded = args_escape(value->string);
                    461:                args_print_add(buf, len, "%s", expanded);
                    462:                break;
                    463:        }
                    464:        free(expanded);
1.21      nicm      465: }
                    466:
1.1       nicm      467: /* Print a set of arguments. */
1.12      nicm      468: char *
                    469: args_print(struct args *args)
1.1       nicm      470: {
1.39      nicm      471:        size_t                   len;
1.21      nicm      472:        char                    *buf;
1.43      nicm      473:        u_int                    i, j;
1.5       nicm      474:        struct args_entry       *entry;
1.21      nicm      475:        struct args_value       *value;
1.1       nicm      476:
1.12      nicm      477:        len = 1;
                    478:        buf = xcalloc(1, len);
1.1       nicm      479:
                    480:        /* Process the flags first. */
1.5       nicm      481:        RB_FOREACH(entry, args_tree, &args->tree) {
1.21      nicm      482:                if (!TAILQ_EMPTY(&entry->values))
1.1       nicm      483:                        continue;
                    484:
1.12      nicm      485:                if (*buf == '\0')
                    486:                        args_print_add(&buf, &len, "-");
1.27      nicm      487:                for (j = 0; j < entry->count; j++)
                    488:                        args_print_add(&buf, &len, "%c", entry->flag);
1.1       nicm      489:        }
                    490:
                    491:        /* Then the flags with arguments. */
1.5       nicm      492:        RB_FOREACH(entry, args_tree, &args->tree) {
1.38      nicm      493:                TAILQ_FOREACH(value, &entry->values, entry) {
                    494:                        if (*buf != '\0')
                    495:                                args_print_add(&buf, &len, " -%c", entry->flag);
                    496:                        else
                    497:                                args_print_add(&buf, &len, "-%c", entry->flag);
1.43      nicm      498:                        args_print_add_value(&buf, &len, value);
1.38      nicm      499:                }
1.1       nicm      500:        }
                    501:
                    502:        /* And finally the argument vector. */
1.43      nicm      503:        for (i = 0; i < args->count; i++)
                    504:                args_print_add_value(&buf, &len, &args->values[i]);
1.1       nicm      505:
1.12      nicm      506:        return (buf);
1.22      nicm      507: }
                    508:
                    509: /* Escape an argument. */
                    510: char *
                    511: args_escape(const char *s)
                    512: {
1.48      nicm      513:        static const char        dquoted[] = " #';${}%";
1.35      nicm      514:        static const char        squoted[] = " \"";
1.22      nicm      515:        char                    *escaped, *result;
1.35      nicm      516:        int                      flags, quotes = 0;
1.22      nicm      517:
1.30      nicm      518:        if (*s == '\0') {
                    519:                xasprintf(&result, "''");
                    520:                return (result);
                    521:        }
1.35      nicm      522:        if (s[strcspn(s, dquoted)] != '\0')
                    523:                quotes = '"';
                    524:        else if (s[strcspn(s, squoted)] != '\0')
                    525:                quotes = '\'';
                    526:
1.26      nicm      527:        if (s[0] != ' ' &&
1.35      nicm      528:            s[1] == '\0' &&
                    529:            (quotes != 0 || s[0] == '~')) {
1.22      nicm      530:                xasprintf(&escaped, "\\%c", s[0]);
1.34      nicm      531:                return (escaped);
                    532:        }
                    533:
1.25      nicm      534:        flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
1.35      nicm      535:        if (quotes == '"')
1.22      nicm      536:                flags |= VIS_DQ;
                    537:        utf8_stravis(&escaped, s, flags);
                    538:
1.35      nicm      539:        if (quotes == '\'')
                    540:                xasprintf(&result, "'%s'", escaped);
                    541:        else if (quotes == '"') {
1.22      nicm      542:                if (*escaped == '~')
                    543:                        xasprintf(&result, "\"\\%s\"", escaped);
                    544:                else
                    545:                        xasprintf(&result, "\"%s\"", escaped);
                    546:        } else {
                    547:                if (*escaped == '~')
                    548:                        xasprintf(&result, "\\%s", escaped);
                    549:                else
                    550:                        result = xstrdup(escaped);
                    551:        }
                    552:        free(escaped);
                    553:        return (result);
1.1       nicm      554: }
                    555:
                    556: /* Return if an argument is present. */
                    557: int
1.32      nicm      558: args_has(struct args *args, u_char flag)
1.1       nicm      559: {
1.27      nicm      560:        struct args_entry       *entry;
                    561:
1.32      nicm      562:        entry = args_find(args, flag);
1.27      nicm      563:        if (entry == NULL)
                    564:                return (0);
                    565:        return (entry->count);
1.1       nicm      566: }
                    567:
1.5       nicm      568: /* Set argument value in the arguments tree. */
1.19      nicm      569: void
1.43      nicm      570: args_set(struct args *args, u_char flag, struct args_value *value)
1.1       nicm      571: {
1.5       nicm      572:        struct args_entry       *entry;
                    573:
1.32      nicm      574:        entry = args_find(args, flag);
1.21      nicm      575:        if (entry == NULL) {
1.7       nicm      576:                entry = xcalloc(1, sizeof *entry);
1.32      nicm      577:                entry->flag = flag;
1.27      nicm      578:                entry->count = 1;
1.21      nicm      579:                TAILQ_INIT(&entry->values);
1.7       nicm      580:                RB_INSERT(args_tree, &args->tree, entry);
1.27      nicm      581:        } else
                    582:                entry->count++;
1.43      nicm      583:        if (value != NULL && value->type != ARGS_NONE)
1.21      nicm      584:                TAILQ_INSERT_TAIL(&entry->values, value, entry);
1.1       nicm      585: }
                    586:
                    587: /* Get argument value. Will be NULL if it isn't present. */
                    588: const char *
1.32      nicm      589: args_get(struct args *args, u_char flag)
1.1       nicm      590: {
1.5       nicm      591:        struct args_entry       *entry;
                    592:
1.32      nicm      593:        if ((entry = args_find(args, flag)) == NULL)
                    594:                return (NULL);
                    595:        if (TAILQ_EMPTY(&entry->values))
1.5       nicm      596:                return (NULL);
1.41      nicm      597:        return (TAILQ_LAST(&entry->values, args_values)->string);
1.21      nicm      598: }
                    599:
1.32      nicm      600: /* Get first argument. */
                    601: u_char
                    602: args_first(struct args *args, struct args_entry **entry)
                    603: {
                    604:        *entry = RB_MIN(args_tree, &args->tree);
                    605:        if (*entry == NULL)
                    606:                return (0);
                    607:        return ((*entry)->flag);
                    608: }
                    609:
                    610: /* Get next argument. */
                    611: u_char
                    612: args_next(struct args_entry **entry)
                    613: {
                    614:        *entry = RB_NEXT(args_tree, &args->tree, *entry);
                    615:        if (*entry == NULL)
                    616:                return (0);
                    617:        return ((*entry)->flag);
1.38      nicm      618: }
                    619:
                    620: /* Get argument count. */
                    621: u_int
                    622: args_count(struct args *args)
                    623: {
1.43      nicm      624:        return (args->count);
                    625: }
                    626:
1.48      nicm      627: /* Get argument values. */
                    628: struct args_value *
                    629: args_values(struct args *args)
                    630: {
                    631:        return (args->values);
                    632: }
                    633:
1.43      nicm      634: /* Get argument value. */
                    635: struct args_value *
                    636: args_value(struct args *args, u_int idx)
                    637: {
                    638:        if (idx >= args->count)
                    639:                return (NULL);
                    640:        return (&args->values[idx]);
1.38      nicm      641: }
                    642:
                    643: /* Return argument as string. */
                    644: const char *
                    645: args_string(struct args *args, u_int idx)
                    646: {
1.44      nicm      647:        if (idx >= args->count)
1.38      nicm      648:                return (NULL);
1.43      nicm      649:        return (args_value_as_string(&args->values[idx]));
1.45      nicm      650: }
                    651:
                    652: /* Make a command now. */
                    653: struct cmd_list *
1.50    ! nicm      654: args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
        !           655:     int expand)
1.45      nicm      656: {
                    657:        struct args_command_state       *state;
                    658:        char                            *error;
                    659:        struct cmd_list                 *cmdlist;
                    660:
1.50    ! nicm      661:        state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
1.45      nicm      662:        cmdlist = args_make_commands(state, 0, NULL, &error);
                    663:        if (cmdlist == NULL) {
                    664:                cmdq_error(item, "%s", error);
                    665:                free(error);
                    666:        }
1.48      nicm      667:        else
                    668:                cmdlist->references++;
1.46      nicm      669:        args_make_commands_free(state);
1.45      nicm      670:        return (cmdlist);
                    671: }
                    672:
                    673: /* Save bits to make a command later. */
                    674: struct args_command_state *
                    675: args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
                    676:     const char *default_command, int wait, int expand)
                    677: {
                    678:        struct args                     *args = cmd_get_args(self);
                    679:        struct cmd_find_state           *target = cmdq_get_target(item);
                    680:        struct client                   *tc = cmdq_get_target_client(item);
                    681:        struct args_value               *value;
                    682:        struct args_command_state       *state;
                    683:        const char                      *cmd;
                    684:
                    685:        state = xcalloc(1, sizeof *state);
                    686:
                    687:        if (idx < args->count) {
                    688:                value = &args->values[idx];
                    689:                if (value->type == ARGS_COMMANDS) {
                    690:                        state->cmdlist = value->cmdlist;
                    691:                        state->cmdlist->references++;
                    692:                        return (state);
                    693:                }
                    694:                cmd = value->string;
                    695:        } else {
                    696:                if (default_command == NULL)
                    697:                        fatalx("argument out of range");
                    698:                cmd = default_command;
                    699:        }
                    700:
                    701:
                    702:        if (expand)
                    703:                state->cmd = format_single_from_target(item, cmd);
                    704:        else
                    705:                state->cmd = xstrdup(cmd);
                    706:        log_debug("%s: %s", __func__, state->cmd);
                    707:
                    708:        if (wait)
                    709:                state->pi.item = item;
                    710:        cmd_get_source(self, &state->pi.file, &state->pi.line);
                    711:        state->pi.c = tc;
                    712:        if (state->pi.c != NULL)
                    713:                state->pi.c->references++;
                    714:        cmd_find_copy_state(&state->pi.fs, target);
                    715:
                    716:        return (state);
                    717: }
                    718:
                    719: /* Return argument as command. */
                    720: struct cmd_list *
                    721: args_make_commands(struct args_command_state *state, int argc, char **argv,
                    722:     char **error)
                    723: {
                    724:        struct cmd_parse_result *pr;
                    725:        char                    *cmd, *new_cmd;
                    726:        int                      i;
                    727:
1.48      nicm      728:        if (state->cmdlist != NULL) {
                    729:                if (argc == 0)
                    730:                        return (state->cmdlist);
                    731:                return (cmd_list_copy(state->cmdlist, argc, argv));
                    732:        }
1.45      nicm      733:
                    734:        cmd = xstrdup(state->cmd);
                    735:        for (i = 0; i < argc; i++) {
                    736:                new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
                    737:                log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
                    738:                free(cmd);
                    739:                cmd = new_cmd;
                    740:        }
                    741:        log_debug("%s: %s", __func__, cmd);
                    742:
                    743:        pr = cmd_parse_from_string(cmd, &state->pi);
                    744:        free(cmd);
                    745:        switch (pr->status) {
                    746:        case CMD_PARSE_ERROR:
                    747:                *error = pr->error;
                    748:                return (NULL);
                    749:        case CMD_PARSE_SUCCESS:
                    750:                return (pr->cmdlist);
                    751:        }
                    752: }
                    753:
                    754: /* Free commands state. */
                    755: void
                    756: args_make_commands_free(struct args_command_state *state)
                    757: {
                    758:        if (state->cmdlist != NULL)
                    759:                cmd_list_free(state->cmdlist);
                    760:        if (state->pi.c != NULL)
                    761:                server_client_unref(state->pi.c);
                    762:        free(state->cmd);
                    763:        free(state);
                    764: }
                    765:
                    766: /* Get prepared command. */
                    767: char *
                    768: args_make_commands_get_command(struct args_command_state *state)
                    769: {
                    770:        struct cmd      *first;
                    771:        int              n;
                    772:        char            *s;
                    773:
                    774:        if (state->cmdlist != NULL) {
                    775:                first = cmd_list_first(state->cmdlist);
                    776:                if (first == NULL)
                    777:                        return (xstrdup(""));
                    778:                return (xstrdup(cmd_get_entry(first)->name));
                    779:        }
                    780:        n = strcspn(state->cmd, " ,");
                    781:        xasprintf(&s, "%.*s", n, state->cmd);
                    782:        return (s);
1.32      nicm      783: }
                    784:
1.21      nicm      785: /* Get first value in argument. */
1.37      nicm      786: struct args_value *
                    787: args_first_value(struct args *args, u_char flag)
1.21      nicm      788: {
                    789:        struct args_entry       *entry;
                    790:
1.32      nicm      791:        if ((entry = args_find(args, flag)) == NULL)
1.21      nicm      792:                return (NULL);
1.37      nicm      793:        return (TAILQ_FIRST(&entry->values));
1.21      nicm      794: }
                    795:
                    796: /* Get next value in argument. */
1.37      nicm      797: struct args_value *
                    798: args_next_value(struct args_value *value)
1.21      nicm      799: {
1.37      nicm      800:        return (TAILQ_NEXT(value, entry));
1.1       nicm      801: }
                    802:
                    803: /* Convert an argument value to a number. */
                    804: long long
1.32      nicm      805: args_strtonum(struct args *args, u_char flag, long long minval,
                    806:     long long maxval, char **cause)
1.1       nicm      807: {
1.5       nicm      808:        const char              *errstr;
1.39      nicm      809:        long long                ll;
1.5       nicm      810:        struct args_entry       *entry;
1.21      nicm      811:        struct args_value       *value;
1.1       nicm      812:
1.32      nicm      813:        if ((entry = args_find(args, flag)) == NULL) {
1.1       nicm      814:                *cause = xstrdup("missing");
                    815:                return (0);
                    816:        }
1.21      nicm      817:        value = TAILQ_LAST(&entry->values, args_values);
1.1       nicm      818:
1.41      nicm      819:        ll = strtonum(value->string, minval, maxval, &errstr);
1.1       nicm      820:        if (errstr != NULL) {
                    821:                *cause = xstrdup(errstr);
                    822:                return (0);
1.29      nicm      823:        }
                    824:
                    825:        *cause = NULL;
                    826:        return (ll);
                    827: }
                    828:
                    829: /* Convert an argument to a number which may be a percentage. */
                    830: long long
1.32      nicm      831: args_percentage(struct args *args, u_char flag, long long minval,
1.29      nicm      832:     long long maxval, long long curval, char **cause)
                    833: {
1.31      nicm      834:        const char              *value;
1.29      nicm      835:        struct args_entry       *entry;
                    836:
1.32      nicm      837:        if ((entry = args_find(args, flag)) == NULL) {
1.29      nicm      838:                *cause = xstrdup("missing");
                    839:                return (0);
                    840:        }
1.41      nicm      841:        value = TAILQ_LAST(&entry->values, args_values)->string;
1.31      nicm      842:        return (args_string_percentage(value, minval, maxval, curval, cause));
                    843: }
                    844:
                    845: /* Convert a string to a number which may be a percentage. */
                    846: long long
                    847: args_string_percentage(const char *value, long long minval, long long maxval,
                    848:     long long curval, char **cause)
                    849: {
                    850:        const char      *errstr;
1.39      nicm      851:        long long        ll;
1.31      nicm      852:        size_t           valuelen = strlen(value);
                    853:        char            *copy;
1.29      nicm      854:
1.31      nicm      855:        if (value[valuelen - 1] == '%') {
                    856:                copy = xstrdup(value);
1.29      nicm      857:                copy[valuelen - 1] = '\0';
                    858:
                    859:                ll = strtonum(copy, 0, 100, &errstr);
                    860:                free(copy);
                    861:                if (errstr != NULL) {
                    862:                        *cause = xstrdup(errstr);
                    863:                        return (0);
                    864:                }
                    865:                ll = (curval * ll) / 100;
                    866:                if (ll < minval) {
1.33      nicm      867:                        *cause = xstrdup("too small");
1.29      nicm      868:                        return (0);
                    869:                }
                    870:                if (ll > maxval) {
1.33      nicm      871:                        *cause = xstrdup("too large");
1.29      nicm      872:                        return (0);
                    873:                }
                    874:        } else {
1.31      nicm      875:                ll = strtonum(value, minval, maxval, &errstr);
1.29      nicm      876:                if (errstr != NULL) {
                    877:                        *cause = xstrdup(errstr);
                    878:                        return (0);
                    879:                }
1.1       nicm      880:        }
                    881:
                    882:        *cause = NULL;
                    883:        return (ll);
                    884: }