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
);
113 /* Create an empty arguments set. */
119 args
= xcalloc(1, sizeof *args
);
120 RB_INIT(&args
->tree
);
124 /* Parse arguments into a new argument set. */
126 args_parse(const struct args_parse
*parse
, struct args_value
*values
,
127 u_int count
, char **cause
)
131 enum args_parse_type type
;
132 struct args_value
*value
, *new;
133 u_char flag
, argument
;
134 const char *found
, *string
, *s
;
137 return (args_create());
139 args
= args_create();
140 for (i
= 1; i
< count
; /* nothing */) {
142 if (value
->type
!= ARGS_STRING
)
145 string
= value
->string
;
146 if (*string
++ != '-' || *string
== '\0')
149 if (string
[0] == '-' && string
[1] == '\0')
160 if (!isalnum(flag
)) {
161 xasprintf(cause
, "invalid flag -%c", flag
);
165 found
= strchr(parse
->template, flag
);
167 xasprintf(cause
, "unknown flag -%c", flag
);
172 if (argument
!= ':') {
173 log_debug("%s: -%c", __func__
, flag
);
174 args_set(args
, flag
, NULL
);
177 new = xcalloc(1, sizeof *new);
178 if (*string
!= '\0') {
179 new->type
= ARGS_STRING
;
180 new->string
= xstrdup(string
);
184 "-%c expects an argument",
189 if (values
[i
].type
!= ARGS_STRING
) {
191 "-%c argument must be a string",
196 args_copy_value(new, &values
[i
++]);
198 s
= args_value_as_string(new);
199 log_debug("%s: -%c = %s", __func__
, flag
, s
);
200 args_set(args
, flag
, new);
204 log_debug("%s: flags end at %u of %u", __func__
, i
, count
);
206 for (/* nothing */; i
< count
; i
++) {
209 s
= args_value_as_string(value
);
210 log_debug("%s: %u = %s (type %d)", __func__
, i
, s
,
213 if (parse
->cb
!= NULL
) {
214 type
= parse
->cb(args
, args
->count
, cause
);
215 if (type
== ARGS_PARSE_INVALID
) {
220 type
= ARGS_PARSE_STRING
;
222 args
->values
= xrecallocarray(args
->values
,
223 args
->count
, args
->count
+ 1, sizeof *args
->values
);
224 new = &args
->values
[args
->count
++];
227 case ARGS_PARSE_INVALID
:
228 fatalx("unexpected argument type");
229 case ARGS_PARSE_STRING
:
230 if (value
->type
!= ARGS_STRING
) {
232 "argument %u must be \"string\"",
237 args_copy_value(new, value
);
239 case ARGS_PARSE_COMMANDS_OR_STRING
:
240 args_copy_value(new, value
);
242 case ARGS_PARSE_COMMANDS
:
243 if (value
->type
!= ARGS_COMMANDS
) {
245 "argument %u must be { commands }",
250 args_copy_value(new, value
);
256 if (parse
->lower
!= -1 && args
->count
< (u_int
)parse
->lower
) {
258 "too few arguments (need at least %u)",
263 if (parse
->upper
!= -1 && args
->count
> (u_int
)parse
->upper
) {
265 "too many arguments (need at most %u)",
273 /* Copy and expand a value. */
275 args_copy_copy_value(struct args_value
*to
, struct args_value
*from
, int argc
,
281 to
->type
= from
->type
;
282 switch (from
->type
) {
286 expanded
= xstrdup(from
->string
);
287 for (i
= 0; i
< argc
; i
++) {
288 s
= cmd_template_replace(expanded
, argv
[i
], i
+ 1);
292 to
->string
= expanded
;
295 to
->cmdlist
= cmd_list_copy(from
->cmdlist
, argc
, argv
);
300 /* Copy an arguments set. */
302 args_copy(struct args
*args
, int argc
, char **argv
)
304 struct args
*new_args
;
305 struct args_entry
*entry
;
306 struct args_value
*value
, *new_value
;
309 cmd_log_argv(argc
, argv
, "%s", __func__
);
311 new_args
= args_create();
312 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
313 if (TAILQ_EMPTY(&entry
->values
)) {
314 for (i
= 0; i
< entry
->count
; i
++)
315 args_set(new_args
, entry
->flag
, NULL
);
318 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
319 new_value
= xcalloc(1, sizeof *new_value
);
320 args_copy_copy_value(new_value
, value
, argc
, argv
);
321 args_set(new_args
, entry
->flag
, new_value
);
324 if (args
->count
== 0)
326 new_args
->count
= args
->count
;
327 new_args
->values
= xcalloc(args
->count
, sizeof *new_args
->values
);
328 for (i
= 0; i
< args
->count
; i
++) {
329 new_value
= &new_args
->values
[i
];
330 args_copy_copy_value(new_value
, &args
->values
[i
], argc
, argv
);
337 args_free_value(struct args_value
*value
)
339 switch (value
->type
) {
346 cmd_list_free(value
->cmdlist
);
354 args_free_values(struct args_value
*values
, u_int count
)
358 for (i
= 0; i
< count
; i
++)
359 args_free_value(&values
[i
]);
362 /* Free an arguments set. */
364 args_free(struct args
*args
)
366 struct args_entry
*entry
;
367 struct args_entry
*entry1
;
368 struct args_value
*value
;
369 struct args_value
*value1
;
371 args_free_values(args
->values
, args
->count
);
374 RB_FOREACH_SAFE(entry
, args_tree
, &args
->tree
, entry1
) {
375 RB_REMOVE(args_tree
, &args
->tree
, entry
);
376 TAILQ_FOREACH_SAFE(value
, &entry
->values
, entry
, value1
) {
377 TAILQ_REMOVE(&entry
->values
, value
, entry
);
378 args_free_value(value
);
387 /* Convert arguments to vector. */
389 args_to_vector(struct args
*args
, int *argc
, char ***argv
)
397 for (i
= 0; i
< args
->count
; i
++) {
398 switch (args
->values
[i
].type
) {
402 cmd_append_argv(argc
, argv
, args
->values
[i
].string
);
405 s
= cmd_list_print(args
->values
[i
].cmdlist
, 0);
406 cmd_append_argv(argc
, argv
, s
);
413 /* Convert arguments from vector. */
415 args_from_vector(int argc
, char **argv
)
417 struct args_value
*values
;
420 values
= xcalloc(argc
, sizeof *values
);
421 for (i
= 0; i
< argc
; i
++) {
422 values
[i
].type
= ARGS_STRING
;
423 values
[i
].string
= xstrdup(argv
[i
]);
429 static void printflike(3, 4)
430 args_print_add(char **buf
, size_t *len
, const char *fmt
, ...)
437 slen
= xvasprintf(&s
, fmt
, ap
);
441 *buf
= xrealloc(*buf
, *len
);
443 strlcat(*buf
, s
, *len
);
447 /* Add value to string. */
449 args_print_add_value(char **buf
, size_t *len
, struct args_value
*value
)
451 char *expanded
= NULL
;
454 args_print_add(buf
, len
, " ");
456 switch (value
->type
) {
460 expanded
= cmd_list_print(value
->cmdlist
, 0);
461 args_print_add(buf
, len
, "{ %s }", expanded
);
464 expanded
= args_escape(value
->string
);
465 args_print_add(buf
, len
, "%s", expanded
);
471 /* Print a set of arguments. */
473 args_print(struct args
*args
)
478 struct args_entry
*entry
;
479 struct args_value
*value
;
482 buf
= xcalloc(1, len
);
484 /* Process the flags first. */
485 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
486 if (!TAILQ_EMPTY(&entry
->values
))
490 args_print_add(&buf
, &len
, "-");
491 for (j
= 0; j
< entry
->count
; j
++)
492 args_print_add(&buf
, &len
, "%c", entry
->flag
);
495 /* Then the flags with arguments. */
496 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
497 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
499 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
501 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
502 args_print_add_value(&buf
, &len
, value
);
506 /* And finally the argument vector. */
507 for (i
= 0; i
< args
->count
; i
++)
508 args_print_add_value(&buf
, &len
, &args
->values
[i
]);
513 /* Escape an argument. */
515 args_escape(const char *s
)
517 static const char dquoted
[] = " #';${}%";
518 static const char squoted
[] = " \"";
519 char *escaped
, *result
;
520 int flags
, quotes
= 0;
523 xasprintf(&result
, "''");
526 if (s
[strcspn(s
, dquoted
)] != '\0')
528 else if (s
[strcspn(s
, squoted
)] != '\0')
533 (quotes
!= 0 || s
[0] == '~')) {
534 xasprintf(&escaped
, "\\%c", s
[0]);
538 flags
= VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
|VIS_NL
;
541 utf8_stravis(&escaped
, s
, flags
);
544 xasprintf(&result
, "'%s'", escaped
);
545 else if (quotes
== '"') {
547 xasprintf(&result
, "\"\\%s\"", escaped
);
549 xasprintf(&result
, "\"%s\"", escaped
);
552 xasprintf(&result
, "\\%s", escaped
);
554 result
= xstrdup(escaped
);
560 /* Return if an argument is present. */
562 args_has(struct args
*args
, u_char flag
)
564 struct args_entry
*entry
;
566 entry
= args_find(args
, flag
);
569 return (entry
->count
);
572 /* Set argument value in the arguments tree. */
574 args_set(struct args
*args
, u_char flag
, struct args_value
*value
)
576 struct args_entry
*entry
;
578 entry
= args_find(args
, flag
);
580 entry
= xcalloc(1, sizeof *entry
);
583 TAILQ_INIT(&entry
->values
);
584 RB_INSERT(args_tree
, &args
->tree
, entry
);
587 if (value
!= NULL
&& value
->type
!= ARGS_NONE
)
588 TAILQ_INSERT_TAIL(&entry
->values
, value
, entry
);
591 /* Get argument value. Will be NULL if it isn't present. */
593 args_get(struct args
*args
, u_char flag
)
595 struct args_entry
*entry
;
597 if ((entry
= args_find(args
, flag
)) == NULL
)
599 if (TAILQ_EMPTY(&entry
->values
))
601 return (TAILQ_LAST(&entry
->values
, args_values
)->string
);
604 /* Get first argument. */
606 args_first(struct args
*args
, struct args_entry
**entry
)
608 *entry
= RB_MIN(args_tree
, &args
->tree
);
611 return ((*entry
)->flag
);
614 /* Get next argument. */
616 args_next(struct args_entry
**entry
)
618 *entry
= RB_NEXT(args_tree
, &args
->tree
, *entry
);
621 return ((*entry
)->flag
);
624 /* Get argument count. */
626 args_count(struct args
*args
)
628 return (args
->count
);
631 /* Get argument values. */
633 args_values(struct args
*args
)
635 return (args
->values
);
638 /* Get argument value. */
640 args_value(struct args
*args
, u_int idx
)
642 if (idx
>= args
->count
)
644 return (&args
->values
[idx
]);
647 /* Return argument as string. */
649 args_string(struct args
*args
, u_int idx
)
651 if (idx
>= args
->count
)
653 return (args_value_as_string(&args
->values
[idx
]));
656 /* Make a command now. */
658 args_make_commands_now(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
661 struct args_command_state
*state
;
663 struct cmd_list
*cmdlist
;
665 state
= args_make_commands_prepare(self
, item
, idx
, NULL
, 0, expand
);
666 cmdlist
= args_make_commands(state
, 0, NULL
, &error
);
667 if (cmdlist
== NULL
) {
668 cmdq_error(item
, "%s", error
);
672 cmdlist
->references
++;
673 args_make_commands_free(state
);
677 /* Save bits to make a command later. */
678 struct args_command_state
*
679 args_make_commands_prepare(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
680 const char *default_command
, int wait
, int expand
)
682 struct args
*args
= cmd_get_args(self
);
683 struct cmd_find_state
*target
= cmdq_get_target(item
);
684 struct client
*tc
= cmdq_get_target_client(item
);
685 struct args_value
*value
;
686 struct args_command_state
*state
;
689 state
= xcalloc(1, sizeof *state
);
691 if (idx
< args
->count
) {
692 value
= &args
->values
[idx
];
693 if (value
->type
== ARGS_COMMANDS
) {
694 state
->cmdlist
= value
->cmdlist
;
695 state
->cmdlist
->references
++;
700 if (default_command
== NULL
)
701 fatalx("argument out of range");
702 cmd
= default_command
;
707 state
->cmd
= format_single_from_target(item
, cmd
);
709 state
->cmd
= xstrdup(cmd
);
710 log_debug("%s: %s", __func__
, state
->cmd
);
713 state
->pi
.item
= item
;
714 cmd_get_source(self
, &state
->pi
.file
, &state
->pi
.line
);
716 if (state
->pi
.c
!= NULL
)
717 state
->pi
.c
->references
++;
718 cmd_find_copy_state(&state
->pi
.fs
, target
);
723 /* Return argument as command. */
725 args_make_commands(struct args_command_state
*state
, int argc
, char **argv
,
728 struct cmd_parse_result
*pr
;
732 if (state
->cmdlist
!= NULL
) {
734 return (state
->cmdlist
);
735 return (cmd_list_copy(state
->cmdlist
, argc
, argv
));
738 cmd
= xstrdup(state
->cmd
);
739 for (i
= 0; i
< argc
; i
++) {
740 new_cmd
= cmd_template_replace(cmd
, argv
[i
], i
+ 1);
741 log_debug("%s: %%%u %s: %s", __func__
, i
+ 1, argv
[i
], new_cmd
);
745 log_debug("%s: %s", __func__
, cmd
);
747 pr
= cmd_parse_from_string(cmd
, &state
->pi
);
749 switch (pr
->status
) {
750 case CMD_PARSE_ERROR
:
753 case CMD_PARSE_SUCCESS
:
754 return (pr
->cmdlist
);
758 /* Free commands state. */
760 args_make_commands_free(struct args_command_state
*state
)
762 if (state
->cmdlist
!= NULL
)
763 cmd_list_free(state
->cmdlist
);
764 if (state
->pi
.c
!= NULL
)
765 server_client_unref(state
->pi
.c
);
770 /* Get prepared command. */
772 args_make_commands_get_command(struct args_command_state
*state
)
778 if (state
->cmdlist
!= NULL
) {
779 first
= cmd_list_first(state
->cmdlist
);
781 return (xstrdup(""));
782 return (xstrdup(cmd_get_entry(first
)->name
));
784 n
= strcspn(state
->cmd
, " ,");
785 xasprintf(&s
, "%.*s", n
, state
->cmd
);
789 /* Get first value in argument. */
791 args_first_value(struct args
*args
, u_char flag
)
793 struct args_entry
*entry
;
795 if ((entry
= args_find(args
, flag
)) == NULL
)
797 return (TAILQ_FIRST(&entry
->values
));
800 /* Get next value in argument. */
802 args_next_value(struct args_value
*value
)
804 return (TAILQ_NEXT(value
, entry
));
807 /* Convert an argument value to a number. */
809 args_strtonum(struct args
*args
, u_char flag
, long long minval
,
810 long long maxval
, char **cause
)
814 struct args_entry
*entry
;
815 struct args_value
*value
;
817 if ((entry
= args_find(args
, flag
)) == NULL
) {
818 *cause
= xstrdup("missing");
821 value
= TAILQ_LAST(&entry
->values
, args_values
);
823 ll
= strtonum(value
->string
, minval
, maxval
, &errstr
);
824 if (errstr
!= NULL
) {
825 *cause
= xstrdup(errstr
);
833 /* Convert an argument to a number which may be a percentage. */
835 args_percentage(struct args
*args
, u_char flag
, long long minval
,
836 long long maxval
, long long curval
, char **cause
)
839 struct args_entry
*entry
;
841 if ((entry
= args_find(args
, flag
)) == NULL
) {
842 *cause
= xstrdup("missing");
845 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
846 return (args_string_percentage(value
, minval
, maxval
, curval
, cause
));
849 /* Convert a string to a number which may be a percentage. */
851 args_string_percentage(const char *value
, long long minval
, long long maxval
,
852 long long curval
, char **cause
)
856 size_t valuelen
= strlen(value
);
859 if (value
[valuelen
- 1] == '%') {
860 copy
= xstrdup(value
);
861 copy
[valuelen
- 1] = '\0';
863 ll
= strtonum(copy
, 0, 100, &errstr
);
865 if (errstr
!= NULL
) {
866 *cause
= xstrdup(errstr
);
869 ll
= (curval
* ll
) / 100;
871 *cause
= xstrdup("too small");
875 *cause
= xstrdup("too large");
879 ll
= strtonum(value
, minval
, maxval
, &errstr
);
880 if (errstr
!= NULL
) {
881 *cause
= xstrdup(errstr
);