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

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