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;
134 u_char flag
, argument
;
135 const char *found
, *string
, *s
;
138 return (args_create());
140 args
= args_create();
141 for (i
= 1; i
< count
; /* nothing */) {
143 if (value
->type
!= ARGS_STRING
)
146 string
= value
->string
;
147 if (*string
++ != '-' || *string
== '\0')
150 if (string
[0] == '-' && string
[1] == '\0')
161 if (!isalnum(flag
)) {
162 xasprintf(cause
, "invalid flag -%c", flag
);
166 found
= strchr(parse
->template, flag
);
168 xasprintf(cause
, "unknown flag -%c", flag
);
173 if (argument
!= ':') {
174 log_debug("%s: -%c", __func__
, flag
);
175 args_set(args
, flag
, NULL
);
178 new = xcalloc(1, sizeof *new);
179 if (*string
!= '\0') {
180 new->type
= ARGS_STRING
;
181 new->string
= xstrdup(string
);
185 "-%c expects an argument",
190 if (values
[i
].type
!= ARGS_STRING
) {
192 "-%c argument must be a string",
197 args_copy_value(new, &values
[i
++]);
199 s
= args_value_as_string(new);
200 log_debug("%s: -%c = %s", __func__
, flag
, s
);
201 args_set(args
, flag
, new);
205 log_debug("%s: flags end at %u of %u", __func__
, i
, count
);
207 for (/* nothing */; i
< count
; i
++) {
210 s
= args_value_as_string(value
);
211 log_debug("%s: %u = %s (type %d)", __func__
, i
, s
,
214 if (parse
->cb
!= NULL
) {
215 type
= parse
->cb(args
, args
->count
, cause
);
216 if (type
== ARGS_PARSE_INVALID
) {
221 type
= ARGS_PARSE_STRING
;
223 args
->values
= xrecallocarray(args
->values
,
224 args
->count
, args
->count
+ 1, sizeof *args
->values
);
225 new = &args
->values
[args
->count
++];
228 case ARGS_PARSE_INVALID
:
229 fatalx("unexpected argument type");
230 case ARGS_PARSE_STRING
:
231 if (value
->type
!= ARGS_STRING
) {
233 "argument %u must be \"string\"",
238 args_copy_value(new, value
);
240 case ARGS_PARSE_COMMANDS_OR_STRING
:
241 args_copy_value(new, value
);
243 case ARGS_PARSE_COMMANDS
:
244 if (value
->type
!= ARGS_COMMANDS
) {
246 "argument %u must be { commands }",
251 args_copy_value(new, value
);
257 if (parse
->lower
!= -1 && args
->count
< (u_int
)parse
->lower
) {
259 "too few arguments (need at least %u)",
264 if (parse
->upper
!= -1 && args
->count
> (u_int
)parse
->upper
) {
266 "too many arguments (need at most %u)",
274 /* Copy and expand a value. */
276 args_copy_copy_value(struct args_value
*to
, struct args_value
*from
, int argc
,
282 to
->type
= from
->type
;
283 switch (from
->type
) {
287 expanded
= xstrdup(from
->string
);
288 for (i
= 0; i
< argc
; i
++) {
289 s
= cmd_template_replace(expanded
, argv
[i
], i
+ 1);
293 to
->string
= expanded
;
296 to
->cmdlist
= cmd_list_copy(from
->cmdlist
, argc
, argv
);
301 /* Copy an arguments set. */
303 args_copy(struct args
*args
, int argc
, char **argv
)
305 struct args
*new_args
;
306 struct args_entry
*entry
;
307 struct args_value
*value
, *new_value
;
310 cmd_log_argv(argc
, argv
, "%s", __func__
);
312 new_args
= args_create();
313 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
314 if (TAILQ_EMPTY(&entry
->values
)) {
315 for (i
= 0; i
< entry
->count
; i
++)
316 args_set(new_args
, entry
->flag
, NULL
);
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
);
325 if (args
->count
== 0)
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
);
338 args_free_value(struct args_value
*value
)
340 switch (value
->type
) {
347 cmd_list_free(value
->cmdlist
);
355 args_free_values(struct args_value
*values
, u_int count
)
359 for (i
= 0; i
< count
; i
++)
360 args_free_value(&values
[i
]);
363 /* Free an arguments set. */
365 args_free(struct args
*args
)
367 struct args_entry
*entry
;
368 struct args_entry
*entry1
;
369 struct args_value
*value
;
370 struct args_value
*value1
;
372 args_free_values(args
->values
, args
->count
);
375 RB_FOREACH_SAFE(entry
, args_tree
, &args
->tree
, entry1
) {
376 RB_REMOVE(args_tree
, &args
->tree
, entry
);
377 TAILQ_FOREACH_SAFE(value
, &entry
->values
, entry
, value1
) {
378 TAILQ_REMOVE(&entry
->values
, value
, entry
);
379 args_free_value(value
);
388 /* Convert arguments to vector. */
390 args_to_vector(struct args
*args
, int *argc
, char ***argv
)
398 for (i
= 0; i
< args
->count
; i
++) {
399 switch (args
->values
[i
].type
) {
403 cmd_append_argv(argc
, argv
, args
->values
[i
].string
);
406 s
= cmd_list_print(args
->values
[i
].cmdlist
, 0);
407 cmd_append_argv(argc
, argv
, s
);
414 /* Convert arguments from vector. */
416 args_from_vector(int argc
, char **argv
)
418 struct args_value
*values
;
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
]);
430 static void printflike(3, 4)
431 args_print_add(char **buf
, size_t *len
, const char *fmt
, ...)
438 slen
= xvasprintf(&s
, fmt
, ap
);
442 *buf
= xrealloc(*buf
, *len
);
444 strlcat(*buf
, s
, *len
);
448 /* Add value to string. */
450 args_print_add_value(char **buf
, size_t *len
, struct args_value
*value
)
452 char *expanded
= NULL
;
455 args_print_add(buf
, len
, " ");
457 switch (value
->type
) {
461 expanded
= cmd_list_print(value
->cmdlist
, 0);
462 args_print_add(buf
, len
, "{ %s }", expanded
);
465 expanded
= args_escape(value
->string
);
466 args_print_add(buf
, len
, "%s", expanded
);
472 /* Print a set of arguments. */
474 args_print(struct args
*args
)
479 struct args_entry
*entry
;
480 struct args_value
*value
;
483 buf
= xcalloc(1, len
);
485 /* Process the flags first. */
486 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
487 if (!TAILQ_EMPTY(&entry
->values
))
491 args_print_add(&buf
, &len
, "-");
492 for (j
= 0; j
< entry
->count
; j
++)
493 args_print_add(&buf
, &len
, "%c", entry
->flag
);
496 /* Then the flags with arguments. */
497 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
498 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
500 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
502 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
503 args_print_add_value(&buf
, &len
, value
);
507 /* And finally the argument vector. */
508 for (i
= 0; i
< args
->count
; i
++)
509 args_print_add_value(&buf
, &len
, &args
->values
[i
]);
514 /* Escape an argument. */
516 args_escape(const char *s
)
518 static const char dquoted
[] = " #';${}%";
519 static const char squoted
[] = " \"";
520 char *escaped
, *result
;
521 int flags
, quotes
= 0;
524 xasprintf(&result
, "''");
527 if (s
[strcspn(s
, dquoted
)] != '\0')
529 else if (s
[strcspn(s
, squoted
)] != '\0')
534 (quotes
!= 0 || s
[0] == '~')) {
535 xasprintf(&escaped
, "\\%c", s
[0]);
539 flags
= VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
|VIS_NL
;
542 utf8_stravis(&escaped
, s
, flags
);
545 xasprintf(&result
, "'%s'", escaped
);
546 else if (quotes
== '"') {
548 xasprintf(&result
, "\"\\%s\"", escaped
);
550 xasprintf(&result
, "\"%s\"", escaped
);
553 xasprintf(&result
, "\\%s", escaped
);
555 result
= xstrdup(escaped
);
561 /* Return if an argument is present. */
563 args_has(struct args
*args
, u_char flag
)
565 struct args_entry
*entry
;
567 entry
= args_find(args
, flag
);
570 return (entry
->count
);
573 /* Set argument value in the arguments tree. */
575 args_set(struct args
*args
, u_char flag
, struct args_value
*value
)
577 struct args_entry
*entry
;
579 entry
= args_find(args
, flag
);
581 entry
= xcalloc(1, sizeof *entry
);
584 TAILQ_INIT(&entry
->values
);
585 RB_INSERT(args_tree
, &args
->tree
, entry
);
588 if (value
!= NULL
&& value
->type
!= ARGS_NONE
)
589 TAILQ_INSERT_TAIL(&entry
->values
, value
, entry
);
592 /* Get argument value. Will be NULL if it isn't present. */
594 args_get(struct args
*args
, u_char flag
)
596 struct args_entry
*entry
;
598 if ((entry
= args_find(args
, flag
)) == NULL
)
600 if (TAILQ_EMPTY(&entry
->values
))
602 return (TAILQ_LAST(&entry
->values
, args_values
)->string
);
605 /* Get first argument. */
607 args_first(struct args
*args
, struct args_entry
**entry
)
609 *entry
= RB_MIN(args_tree
, &args
->tree
);
612 return ((*entry
)->flag
);
615 /* Get next argument. */
617 args_next(struct args_entry
**entry
)
619 *entry
= RB_NEXT(args_tree
, &args
->tree
, *entry
);
622 return ((*entry
)->flag
);
625 /* Get argument count. */
627 args_count(struct args
*args
)
629 return (args
->count
);
632 /* Get argument values. */
634 args_values(struct args
*args
)
636 return (args
->values
);
639 /* Get argument value. */
641 args_value(struct args
*args
, u_int idx
)
643 if (idx
>= args
->count
)
645 return (&args
->values
[idx
]);
648 /* Return argument as string. */
650 args_string(struct args
*args
, u_int idx
)
652 if (idx
>= args
->count
)
654 return (args_value_as_string(&args
->values
[idx
]));
657 /* Make a command now. */
659 args_make_commands_now(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
662 struct args_command_state
*state
;
664 struct cmd_list
*cmdlist
;
666 state
= args_make_commands_prepare(self
, item
, idx
, NULL
, 0, expand
);
667 cmdlist
= args_make_commands(state
, 0, NULL
, &error
);
668 if (cmdlist
== NULL
) {
669 cmdq_error(item
, "%s", error
);
673 cmdlist
->references
++;
674 args_make_commands_free(state
);
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
)
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
;
690 state
= xcalloc(1, sizeof *state
);
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
++;
701 if (default_command
== NULL
)
702 fatalx("argument out of range");
703 cmd
= default_command
;
708 state
->cmd
= format_single_from_target(item
, cmd
);
710 state
->cmd
= xstrdup(cmd
);
711 log_debug("%s: %s", __func__
, state
->cmd
);
714 state
->pi
.item
= item
;
715 cmd_get_source(self
, &state
->pi
.file
, &state
->pi
.line
);
717 if (state
->pi
.c
!= NULL
)
718 state
->pi
.c
->references
++;
719 cmd_find_copy_state(&state
->pi
.fs
, target
);
724 /* Return argument as command. */
726 args_make_commands(struct args_command_state
*state
, int argc
, char **argv
,
729 struct cmd_parse_result
*pr
;
733 if (state
->cmdlist
!= NULL
) {
735 return (state
->cmdlist
);
736 return (cmd_list_copy(state
->cmdlist
, argc
, argv
));
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
);
746 log_debug("%s: %s", __func__
, cmd
);
748 pr
= cmd_parse_from_string(cmd
, &state
->pi
);
750 switch (pr
->status
) {
751 case CMD_PARSE_ERROR
:
754 case CMD_PARSE_SUCCESS
:
755 return (pr
->cmdlist
);
757 fatalx("invalid parse return state");
760 /* Free commands state. */
762 args_make_commands_free(struct args_command_state
*state
)
764 if (state
->cmdlist
!= NULL
)
765 cmd_list_free(state
->cmdlist
);
766 if (state
->pi
.c
!= NULL
)
767 server_client_unref(state
->pi
.c
);
772 /* Get prepared command. */
774 args_make_commands_get_command(struct args_command_state
*state
)
780 if (state
->cmdlist
!= NULL
) {
781 first
= cmd_list_first(state
->cmdlist
);
783 return (xstrdup(""));
784 return (xstrdup(cmd_get_entry(first
)->name
));
786 n
= strcspn(state
->cmd
, " ,");
787 xasprintf(&s
, "%.*s", n
, state
->cmd
);
791 /* Get first value in argument. */
793 args_first_value(struct args
*args
, u_char flag
)
795 struct args_entry
*entry
;
797 if ((entry
= args_find(args
, flag
)) == NULL
)
799 return (TAILQ_FIRST(&entry
->values
));
802 /* Get next value in argument. */
804 args_next_value(struct args_value
*value
)
806 return (TAILQ_NEXT(value
, entry
));
809 /* Convert an argument value to a number. */
811 args_strtonum(struct args
*args
, u_char flag
, long long minval
,
812 long long maxval
, char **cause
)
816 struct args_entry
*entry
;
817 struct args_value
*value
;
819 if ((entry
= args_find(args
, flag
)) == NULL
) {
820 *cause
= xstrdup("missing");
823 value
= TAILQ_LAST(&entry
->values
, args_values
);
825 ll
= strtonum(value
->string
, minval
, maxval
, &errstr
);
826 if (errstr
!= NULL
) {
827 *cause
= xstrdup(errstr
);
835 /* Convert an argument to a number which may be a percentage. */
837 args_percentage(struct args
*args
, u_char flag
, long long minval
,
838 long long maxval
, long long curval
, char **cause
)
841 struct args_entry
*entry
;
843 if ((entry
= args_find(args
, flag
)) == NULL
) {
844 *cause
= xstrdup("missing");
847 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
848 return (args_string_percentage(value
, minval
, maxval
, curval
, cause
));
851 /* Convert a string to a number which may be a percentage. */
853 args_string_percentage(const char *value
, long long minval
, long long maxval
,
854 long long curval
, char **cause
)
858 size_t valuelen
= strlen(value
);
861 if (value
[valuelen
- 1] == '%') {
862 copy
= xstrdup(value
);
863 copy
[valuelen
- 1] = '\0';
865 ll
= strtonum(copy
, 0, 100, &errstr
);
867 if (errstr
!= NULL
) {
868 *cause
= xstrdup(errstr
);
871 ll
= (curval
* ll
) / 100;
873 *cause
= xstrdup("too small");
877 *cause
= xstrdup("too large");
881 ll
= strtonum(value
, minval
, maxval
, &errstr
);
882 if (errstr
!= NULL
) {
883 *cause
= xstrdup(errstr
);