4 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
29 * Manipulate command arguments.
32 /* List of argument values. */
33 TAILQ_HEAD(args_values
, args_value
);
35 /* Single arguments flag. */
38 struct args_values values
;
40 RB_ENTRY(args_entry
) entry
;
43 /* Parsed argument flags and values. */
45 struct args_tree tree
;
47 struct args_value
*values
;
50 /* Prepared command state. */
51 struct args_command_state
{
52 struct cmd_list
*cmdlist
;
54 struct cmd_parse_input pi
;
57 static struct args_entry
*args_find(struct args
*, u_char
);
59 static int args_cmp(struct args_entry
*, struct args_entry
*);
60 RB_GENERATE_STATIC(args_tree
, args_entry
, entry
, args_cmp
);
62 /* Arguments tree comparison function. */
64 args_cmp(struct args_entry
*a1
, struct args_entry
*a2
)
66 return (a1
->flag
- a2
->flag
);
69 /* Find a flag in the arguments tree. */
70 static struct args_entry
*
71 args_find(struct args
*args
, u_char flag
)
73 struct args_entry entry
;
76 return (RB_FIND(args_tree
, &args
->tree
, &entry
));
81 args_copy_value(struct args_value
*to
, struct args_value
*from
)
83 to
->type
= from
->type
;
88 to
->cmdlist
= from
->cmdlist
;
89 to
->cmdlist
->references
++;
92 to
->string
= xstrdup(from
->string
);
97 /* Get value as string. */
99 args_value_as_string(struct args_value
*value
)
101 switch (value
->type
) {
105 if (value
->cached
== NULL
)
106 value
->cached
= cmd_list_print(value
->cmdlist
, 0);
107 return (value
->cached
);
109 return (value
->string
);
111 fatalx("unexpected argument type");
114 /* Create an empty arguments set. */
120 args
= xcalloc(1, sizeof *args
);
121 RB_INIT(&args
->tree
);
125 /* Parse arguments into a new argument set. */
127 args_parse(const struct args_parse
*parse
, struct args_value
*values
,
128 u_int count
, char **cause
)
132 enum args_parse_type type
;
133 struct args_value
*value
, *new;
135 const char *found
, *string
, *s
;
136 int optional_argument
;
139 return (args_create());
141 args
= args_create();
142 for (i
= 1; i
< count
; /* nothing */) {
144 if (value
->type
!= ARGS_STRING
)
147 string
= value
->string
;
148 if (*string
++ != '-' || *string
== '\0')
151 if (string
[0] == '-' && string
[1] == '\0')
162 if (!isalnum(flag
)) {
163 xasprintf(cause
, "invalid flag -%c", flag
);
167 found
= strchr(parse
->template, flag
);
169 xasprintf(cause
, "unknown flag -%c", flag
);
173 if (*++found
!= ':') {
174 log_debug("%s: -%c", __func__
, flag
);
175 args_set(args
, flag
, NULL
);
179 optional_argument
= 1;
182 new = xcalloc(1, sizeof *new);
183 if (*string
!= '\0') {
184 new->type
= ARGS_STRING
;
185 new->string
= xstrdup(string
);
188 if (optional_argument
) {
189 log_debug("%s: -%c", __func__
,
191 args_set(args
, flag
, NULL
);
195 "-%c expects an argument",
200 if (values
[i
].type
!= ARGS_STRING
) {
202 "-%c argument must be a string",
207 args_copy_value(new, &values
[i
++]);
209 s
= args_value_as_string(new);
210 log_debug("%s: -%c = %s", __func__
, flag
, s
);
211 args_set(args
, flag
, new);
215 log_debug("%s: flags end at %u of %u", __func__
, i
, count
);
217 for (/* nothing */; i
< count
; i
++) {
220 s
= args_value_as_string(value
);
221 log_debug("%s: %u = %s (type %d)", __func__
, i
, s
,
224 if (parse
->cb
!= NULL
) {
225 type
= parse
->cb(args
, args
->count
, cause
);
226 if (type
== ARGS_PARSE_INVALID
) {
231 type
= ARGS_PARSE_STRING
;
233 args
->values
= xrecallocarray(args
->values
,
234 args
->count
, args
->count
+ 1, sizeof *args
->values
);
235 new = &args
->values
[args
->count
++];
238 case ARGS_PARSE_INVALID
:
239 fatalx("unexpected argument type");
240 case ARGS_PARSE_STRING
:
241 if (value
->type
!= ARGS_STRING
) {
243 "argument %u must be \"string\"",
248 args_copy_value(new, value
);
250 case ARGS_PARSE_COMMANDS_OR_STRING
:
251 args_copy_value(new, value
);
253 case ARGS_PARSE_COMMANDS
:
254 if (value
->type
!= ARGS_COMMANDS
) {
256 "argument %u must be { commands }",
261 args_copy_value(new, value
);
267 if (parse
->lower
!= -1 && args
->count
< (u_int
)parse
->lower
) {
269 "too few arguments (need at least %u)",
274 if (parse
->upper
!= -1 && args
->count
> (u_int
)parse
->upper
) {
276 "too many arguments (need at most %u)",
284 /* Copy and expand a value. */
286 args_copy_copy_value(struct args_value
*to
, struct args_value
*from
, int argc
,
292 to
->type
= from
->type
;
293 switch (from
->type
) {
297 expanded
= xstrdup(from
->string
);
298 for (i
= 0; i
< argc
; i
++) {
299 s
= cmd_template_replace(expanded
, argv
[i
], i
+ 1);
303 to
->string
= expanded
;
306 to
->cmdlist
= cmd_list_copy(from
->cmdlist
, argc
, argv
);
311 /* Copy an arguments set. */
313 args_copy(struct args
*args
, int argc
, char **argv
)
315 struct args
*new_args
;
316 struct args_entry
*entry
;
317 struct args_value
*value
, *new_value
;
320 cmd_log_argv(argc
, argv
, "%s", __func__
);
322 new_args
= args_create();
323 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
324 if (TAILQ_EMPTY(&entry
->values
)) {
325 for (i
= 0; i
< entry
->count
; i
++)
326 args_set(new_args
, entry
->flag
, NULL
);
329 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
330 new_value
= xcalloc(1, sizeof *new_value
);
331 args_copy_copy_value(new_value
, value
, argc
, argv
);
332 args_set(new_args
, entry
->flag
, new_value
);
335 if (args
->count
== 0)
337 new_args
->count
= args
->count
;
338 new_args
->values
= xcalloc(args
->count
, sizeof *new_args
->values
);
339 for (i
= 0; i
< args
->count
; i
++) {
340 new_value
= &new_args
->values
[i
];
341 args_copy_copy_value(new_value
, &args
->values
[i
], argc
, argv
);
348 args_free_value(struct args_value
*value
)
350 switch (value
->type
) {
357 cmd_list_free(value
->cmdlist
);
365 args_free_values(struct args_value
*values
, u_int count
)
369 for (i
= 0; i
< count
; i
++)
370 args_free_value(&values
[i
]);
373 /* Free an arguments set. */
375 args_free(struct args
*args
)
377 struct args_entry
*entry
;
378 struct args_entry
*entry1
;
379 struct args_value
*value
;
380 struct args_value
*value1
;
382 args_free_values(args
->values
, args
->count
);
385 RB_FOREACH_SAFE(entry
, args_tree
, &args
->tree
, entry1
) {
386 RB_REMOVE(args_tree
, &args
->tree
, entry
);
387 TAILQ_FOREACH_SAFE(value
, &entry
->values
, entry
, value1
) {
388 TAILQ_REMOVE(&entry
->values
, value
, entry
);
389 args_free_value(value
);
398 /* Convert arguments to vector. */
400 args_to_vector(struct args
*args
, int *argc
, char ***argv
)
408 for (i
= 0; i
< args
->count
; i
++) {
409 switch (args
->values
[i
].type
) {
413 cmd_append_argv(argc
, argv
, args
->values
[i
].string
);
416 s
= cmd_list_print(args
->values
[i
].cmdlist
, 0);
417 cmd_append_argv(argc
, argv
, s
);
424 /* Convert arguments from vector. */
426 args_from_vector(int argc
, char **argv
)
428 struct args_value
*values
;
431 values
= xcalloc(argc
, sizeof *values
);
432 for (i
= 0; i
< argc
; i
++) {
433 values
[i
].type
= ARGS_STRING
;
434 values
[i
].string
= xstrdup(argv
[i
]);
440 static void printflike(3, 4)
441 args_print_add(char **buf
, size_t *len
, const char *fmt
, ...)
448 slen
= xvasprintf(&s
, fmt
, ap
);
452 *buf
= xrealloc(*buf
, *len
);
454 strlcat(*buf
, s
, *len
);
458 /* Add value to string. */
460 args_print_add_value(char **buf
, size_t *len
, struct args_value
*value
)
462 char *expanded
= NULL
;
465 args_print_add(buf
, len
, " ");
467 switch (value
->type
) {
471 expanded
= cmd_list_print(value
->cmdlist
, 0);
472 args_print_add(buf
, len
, "{ %s }", expanded
);
475 expanded
= args_escape(value
->string
);
476 args_print_add(buf
, len
, "%s", expanded
);
482 /* Print a set of arguments. */
484 args_print(struct args
*args
)
489 struct args_entry
*entry
;
490 struct args_value
*value
;
493 buf
= xcalloc(1, len
);
495 /* Process the flags first. */
496 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
497 if (!TAILQ_EMPTY(&entry
->values
))
501 args_print_add(&buf
, &len
, "-");
502 for (j
= 0; j
< entry
->count
; j
++)
503 args_print_add(&buf
, &len
, "%c", entry
->flag
);
506 /* Then the flags with arguments. */
507 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
508 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
510 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
512 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
513 args_print_add_value(&buf
, &len
, value
);
517 /* And finally the argument vector. */
518 for (i
= 0; i
< args
->count
; i
++)
519 args_print_add_value(&buf
, &len
, &args
->values
[i
]);
524 /* Escape an argument. */
526 args_escape(const char *s
)
528 static const char dquoted
[] = " #';${}%";
529 static const char squoted
[] = " \"";
530 char *escaped
, *result
;
531 int flags
, quotes
= 0;
534 xasprintf(&result
, "''");
537 if (s
[strcspn(s
, dquoted
)] != '\0')
539 else if (s
[strcspn(s
, squoted
)] != '\0')
544 (quotes
!= 0 || s
[0] == '~')) {
545 xasprintf(&escaped
, "\\%c", s
[0]);
549 flags
= VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
|VIS_NL
;
552 utf8_stravis(&escaped
, s
, flags
);
555 xasprintf(&result
, "'%s'", escaped
);
556 else if (quotes
== '"') {
558 xasprintf(&result
, "\"\\%s\"", escaped
);
560 xasprintf(&result
, "\"%s\"", escaped
);
563 xasprintf(&result
, "\\%s", escaped
);
565 result
= xstrdup(escaped
);
571 /* Return if an argument is present. */
573 args_has(struct args
*args
, u_char flag
)
575 struct args_entry
*entry
;
577 entry
= args_find(args
, flag
);
580 return (entry
->count
);
583 /* Set argument value in the arguments tree. */
585 args_set(struct args
*args
, u_char flag
, struct args_value
*value
)
587 struct args_entry
*entry
;
589 entry
= args_find(args
, flag
);
591 entry
= xcalloc(1, sizeof *entry
);
594 TAILQ_INIT(&entry
->values
);
595 RB_INSERT(args_tree
, &args
->tree
, entry
);
598 if (value
!= NULL
&& value
->type
!= ARGS_NONE
)
599 TAILQ_INSERT_TAIL(&entry
->values
, value
, entry
);
602 /* Get argument value. Will be NULL if it isn't present. */
604 args_get(struct args
*args
, u_char flag
)
606 struct args_entry
*entry
;
608 if ((entry
= args_find(args
, flag
)) == NULL
)
610 if (TAILQ_EMPTY(&entry
->values
))
612 return (TAILQ_LAST(&entry
->values
, args_values
)->string
);
615 /* Get first argument. */
617 args_first(struct args
*args
, struct args_entry
**entry
)
619 *entry
= RB_MIN(args_tree
, &args
->tree
);
622 return ((*entry
)->flag
);
625 /* Get next argument. */
627 args_next(struct args_entry
**entry
)
629 *entry
= RB_NEXT(args_tree
, &args
->tree
, *entry
);
632 return ((*entry
)->flag
);
635 /* Get argument count. */
637 args_count(struct args
*args
)
639 return (args
->count
);
642 /* Get argument values. */
644 args_values(struct args
*args
)
646 return (args
->values
);
649 /* Get argument value. */
651 args_value(struct args
*args
, u_int idx
)
653 if (idx
>= args
->count
)
655 return (&args
->values
[idx
]);
658 /* Return argument as string. */
660 args_string(struct args
*args
, u_int idx
)
662 if (idx
>= args
->count
)
664 return (args_value_as_string(&args
->values
[idx
]));
667 /* Make a command now. */
669 args_make_commands_now(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
672 struct args_command_state
*state
;
674 struct cmd_list
*cmdlist
;
676 state
= args_make_commands_prepare(self
, item
, idx
, NULL
, 0, expand
);
677 cmdlist
= args_make_commands(state
, 0, NULL
, &error
);
678 if (cmdlist
== NULL
) {
679 cmdq_error(item
, "%s", error
);
683 cmdlist
->references
++;
684 args_make_commands_free(state
);
688 /* Save bits to make a command later. */
689 struct args_command_state
*
690 args_make_commands_prepare(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
691 const char *default_command
, int wait
, int expand
)
693 struct args
*args
= cmd_get_args(self
);
694 struct cmd_find_state
*target
= cmdq_get_target(item
);
695 struct client
*tc
= cmdq_get_target_client(item
);
696 struct args_value
*value
;
697 struct args_command_state
*state
;
700 state
= xcalloc(1, sizeof *state
);
702 if (idx
< args
->count
) {
703 value
= &args
->values
[idx
];
704 if (value
->type
== ARGS_COMMANDS
) {
705 state
->cmdlist
= value
->cmdlist
;
706 state
->cmdlist
->references
++;
711 if (default_command
== NULL
)
712 fatalx("argument out of range");
713 cmd
= default_command
;
718 state
->cmd
= format_single_from_target(item
, cmd
);
720 state
->cmd
= xstrdup(cmd
);
721 log_debug("%s: %s", __func__
, state
->cmd
);
724 state
->pi
.item
= item
;
725 cmd_get_source(self
, &state
->pi
.file
, &state
->pi
.line
);
727 if (state
->pi
.c
!= NULL
)
728 state
->pi
.c
->references
++;
729 cmd_find_copy_state(&state
->pi
.fs
, target
);
734 /* Return argument as command. */
736 args_make_commands(struct args_command_state
*state
, int argc
, char **argv
,
739 struct cmd_parse_result
*pr
;
743 if (state
->cmdlist
!= NULL
) {
745 return (state
->cmdlist
);
746 return (cmd_list_copy(state
->cmdlist
, argc
, argv
));
749 cmd
= xstrdup(state
->cmd
);
750 for (i
= 0; i
< argc
; i
++) {
751 new_cmd
= cmd_template_replace(cmd
, argv
[i
], i
+ 1);
752 log_debug("%s: %%%u %s: %s", __func__
, i
+ 1, argv
[i
], new_cmd
);
756 log_debug("%s: %s", __func__
, cmd
);
758 pr
= cmd_parse_from_string(cmd
, &state
->pi
);
760 switch (pr
->status
) {
761 case CMD_PARSE_ERROR
:
764 case CMD_PARSE_SUCCESS
:
765 return (pr
->cmdlist
);
767 fatalx("invalid parse return state");
770 /* Free commands state. */
772 args_make_commands_free(struct args_command_state
*state
)
774 if (state
->cmdlist
!= NULL
)
775 cmd_list_free(state
->cmdlist
);
776 if (state
->pi
.c
!= NULL
)
777 server_client_unref(state
->pi
.c
);
782 /* Get prepared command. */
784 args_make_commands_get_command(struct args_command_state
*state
)
790 if (state
->cmdlist
!= NULL
) {
791 first
= cmd_list_first(state
->cmdlist
);
793 return (xstrdup(""));
794 return (xstrdup(cmd_get_entry(first
)->name
));
796 n
= strcspn(state
->cmd
, " ,");
797 xasprintf(&s
, "%.*s", n
, state
->cmd
);
801 /* Get first value in argument. */
803 args_first_value(struct args
*args
, u_char flag
)
805 struct args_entry
*entry
;
807 if ((entry
= args_find(args
, flag
)) == NULL
)
809 return (TAILQ_FIRST(&entry
->values
));
812 /* Get next value in argument. */
814 args_next_value(struct args_value
*value
)
816 return (TAILQ_NEXT(value
, entry
));
819 /* Convert an argument value to a number. */
821 args_strtonum(struct args
*args
, u_char flag
, long long minval
,
822 long long maxval
, char **cause
)
826 struct args_entry
*entry
;
827 struct args_value
*value
;
829 if ((entry
= args_find(args
, flag
)) == NULL
) {
830 *cause
= xstrdup("missing");
833 value
= TAILQ_LAST(&entry
->values
, args_values
);
835 ll
= strtonum(value
->string
, minval
, maxval
, &errstr
);
836 if (errstr
!= NULL
) {
837 *cause
= xstrdup(errstr
);
845 /* Convert an argument to a number which may be a percentage. */
847 args_percentage(struct args
*args
, u_char flag
, long long minval
,
848 long long maxval
, long long curval
, char **cause
)
851 struct args_entry
*entry
;
853 if ((entry
= args_find(args
, flag
)) == NULL
) {
854 *cause
= xstrdup("missing");
857 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
858 return (args_string_percentage(value
, minval
, maxval
, curval
, cause
));
861 /* Convert a string to a number which may be a percentage. */
863 args_string_percentage(const char *value
, long long minval
, long long maxval
,
864 long long curval
, char **cause
)
868 size_t valuelen
= strlen(value
);
871 if (value
[valuelen
- 1] == '%') {
872 copy
= xstrdup(value
);
873 copy
[valuelen
- 1] = '\0';
875 ll
= strtonum(copy
, 0, 100, &errstr
);
877 if (errstr
!= NULL
) {
878 *cause
= xstrdup(errstr
);
881 ll
= (curval
* ll
) / 100;
883 *cause
= xstrdup("too small");
887 *cause
= xstrdup("too large");
891 ll
= strtonum(value
, minval
, maxval
, &errstr
);
892 if (errstr
!= NULL
) {
893 *cause
= xstrdup(errstr
);