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')
156 if (!isalnum(flag
)) {
157 xasprintf(cause
, "invalid flag -%c", flag
);
161 found
= strchr(parse
->template, flag
);
163 xasprintf(cause
, "unknown flag -%c", flag
);
168 if (argument
!= ':') {
169 log_debug("%s: -%c", __func__
, flag
);
170 args_set(args
, flag
, NULL
);
173 new = xcalloc(1, sizeof *new);
174 if (*string
!= '\0') {
175 new->type
= ARGS_STRING
;
176 new->string
= xstrdup(string
);
180 "-%c expects an argument",
185 if (values
[i
].type
!= ARGS_STRING
) {
187 "-%c argument must be a string",
192 args_copy_value(new, &values
[i
++]);
194 s
= args_value_as_string(new);
195 log_debug("%s: -%c = %s", __func__
, flag
, s
);
196 args_set(args
, flag
, new);
200 log_debug("%s: flags end at %u of %u", __func__
, i
, count
);
202 for (/* nothing */; i
< count
; i
++) {
205 s
= args_value_as_string(value
);
206 log_debug("%s: %u = %s (type %d)", __func__
, i
, s
,
209 if (parse
->cb
!= NULL
) {
210 type
= parse
->cb(args
, args
->count
, cause
);
211 if (type
== ARGS_PARSE_INVALID
) {
216 type
= ARGS_PARSE_STRING
;
218 args
->values
= xrecallocarray(args
->values
,
219 args
->count
, args
->count
+ 1, sizeof *args
->values
);
220 new = &args
->values
[args
->count
++];
223 case ARGS_PARSE_INVALID
:
224 fatalx("unexpected argument type");
225 case ARGS_PARSE_STRING
:
226 if (value
->type
!= ARGS_STRING
) {
228 "argument %u must be \"string\"",
233 args_copy_value(new, value
);
235 case ARGS_PARSE_COMMANDS_OR_STRING
:
236 args_copy_value(new, value
);
238 case ARGS_PARSE_COMMANDS
:
239 if (value
->type
!= ARGS_COMMANDS
) {
241 "argument %u must be { commands }",
246 args_copy_value(new, value
);
252 if (parse
->lower
!= -1 && args
->count
< (u_int
)parse
->lower
) {
254 "too few arguments (need at least %u)",
259 if (parse
->upper
!= -1 && args
->count
> (u_int
)parse
->upper
) {
261 "too many arguments (need at most %u)",
269 /* Copy and expand a value. */
271 args_copy_copy_value(struct args_value
*to
, struct args_value
*from
, int argc
,
277 to
->type
= from
->type
;
278 switch (from
->type
) {
282 expanded
= xstrdup(from
->string
);
283 for (i
= 0; i
< argc
; i
++) {
284 s
= cmd_template_replace(expanded
, argv
[i
], i
+ 1);
288 to
->string
= expanded
;
291 to
->cmdlist
= cmd_list_copy(from
->cmdlist
, argc
, argv
);
296 /* Copy an arguments set. */
298 args_copy(struct args
*args
, int argc
, char **argv
)
300 struct args
*new_args
;
301 struct args_entry
*entry
;
302 struct args_value
*value
, *new_value
;
305 cmd_log_argv(argc
, argv
, "%s", __func__
);
307 new_args
= args_create();
308 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
309 if (TAILQ_EMPTY(&entry
->values
)) {
310 for (i
= 0; i
< entry
->count
; i
++)
311 args_set(new_args
, entry
->flag
, NULL
);
314 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
315 new_value
= xcalloc(1, sizeof *new_value
);
316 args_copy_copy_value(new_value
, value
, argc
, argv
);
317 args_set(new_args
, entry
->flag
, new_value
);
320 if (args
->count
== 0)
322 new_args
->count
= args
->count
;
323 new_args
->values
= xcalloc(args
->count
, sizeof *new_args
->values
);
324 for (i
= 0; i
< args
->count
; i
++) {
325 new_value
= &new_args
->values
[i
];
326 args_copy_copy_value(new_value
, &args
->values
[i
], argc
, argv
);
333 args_free_value(struct args_value
*value
)
335 switch (value
->type
) {
342 cmd_list_free(value
->cmdlist
);
350 args_free_values(struct args_value
*values
, u_int count
)
354 for (i
= 0; i
< count
; i
++)
355 args_free_value(&values
[i
]);
358 /* Free an arguments set. */
360 args_free(struct args
*args
)
362 struct args_entry
*entry
;
363 struct args_entry
*entry1
;
364 struct args_value
*value
;
365 struct args_value
*value1
;
367 args_free_values(args
->values
, args
->count
);
370 RB_FOREACH_SAFE(entry
, args_tree
, &args
->tree
, entry1
) {
371 RB_REMOVE(args_tree
, &args
->tree
, entry
);
372 TAILQ_FOREACH_SAFE(value
, &entry
->values
, entry
, value1
) {
373 TAILQ_REMOVE(&entry
->values
, value
, entry
);
374 args_free_value(value
);
383 /* Convert arguments to vector. */
385 args_to_vector(struct args
*args
, int *argc
, char ***argv
)
393 for (i
= 0; i
< args
->count
; i
++) {
394 switch (args
->values
[i
].type
) {
398 cmd_append_argv(argc
, argv
, args
->values
[i
].string
);
401 s
= cmd_list_print(args
->values
[i
].cmdlist
, 0);
402 cmd_append_argv(argc
, argv
, s
);
409 /* Convert arguments from vector. */
411 args_from_vector(int argc
, char **argv
)
413 struct args_value
*values
;
416 values
= xcalloc(argc
, sizeof *values
);
417 for (i
= 0; i
< argc
; i
++) {
418 values
[i
].type
= ARGS_STRING
;
419 values
[i
].string
= xstrdup(argv
[i
]);
425 static void printflike(3, 4)
426 args_print_add(char **buf
, size_t *len
, const char *fmt
, ...)
433 slen
= xvasprintf(&s
, fmt
, ap
);
437 *buf
= xrealloc(*buf
, *len
);
439 strlcat(*buf
, s
, *len
);
443 /* Add value to string. */
445 args_print_add_value(char **buf
, size_t *len
, struct args_value
*value
)
447 char *expanded
= NULL
;
450 args_print_add(buf
, len
, " ");
452 switch (value
->type
) {
456 expanded
= cmd_list_print(value
->cmdlist
, 0);
457 args_print_add(buf
, len
, "{ %s }", expanded
);
460 expanded
= args_escape(value
->string
);
461 args_print_add(buf
, len
, "%s", expanded
);
467 /* Print a set of arguments. */
469 args_print(struct args
*args
)
474 struct args_entry
*entry
;
475 struct args_value
*value
;
478 buf
= xcalloc(1, len
);
480 /* Process the flags first. */
481 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
482 if (!TAILQ_EMPTY(&entry
->values
))
486 args_print_add(&buf
, &len
, "-");
487 for (j
= 0; j
< entry
->count
; j
++)
488 args_print_add(&buf
, &len
, "%c", entry
->flag
);
491 /* Then the flags with arguments. */
492 RB_FOREACH(entry
, args_tree
, &args
->tree
) {
493 TAILQ_FOREACH(value
, &entry
->values
, entry
) {
495 args_print_add(&buf
, &len
, " -%c", entry
->flag
);
497 args_print_add(&buf
, &len
, "-%c", entry
->flag
);
498 args_print_add_value(&buf
, &len
, value
);
502 /* And finally the argument vector. */
503 for (i
= 0; i
< args
->count
; i
++)
504 args_print_add_value(&buf
, &len
, &args
->values
[i
]);
509 /* Escape an argument. */
511 args_escape(const char *s
)
513 static const char dquoted
[] = " #';${}%";
514 static const char squoted
[] = " \"";
515 char *escaped
, *result
;
516 int flags
, quotes
= 0;
519 xasprintf(&result
, "''");
522 if (s
[strcspn(s
, dquoted
)] != '\0')
524 else if (s
[strcspn(s
, squoted
)] != '\0')
529 (quotes
!= 0 || s
[0] == '~')) {
530 xasprintf(&escaped
, "\\%c", s
[0]);
534 flags
= VIS_OCTAL
|VIS_CSTYLE
|VIS_TAB
|VIS_NL
;
537 utf8_stravis(&escaped
, s
, flags
);
540 xasprintf(&result
, "'%s'", escaped
);
541 else if (quotes
== '"') {
543 xasprintf(&result
, "\"\\%s\"", escaped
);
545 xasprintf(&result
, "\"%s\"", escaped
);
548 xasprintf(&result
, "\\%s", escaped
);
550 result
= xstrdup(escaped
);
556 /* Return if an argument is present. */
558 args_has(struct args
*args
, u_char flag
)
560 struct args_entry
*entry
;
562 entry
= args_find(args
, flag
);
565 return (entry
->count
);
568 /* Set argument value in the arguments tree. */
570 args_set(struct args
*args
, u_char flag
, struct args_value
*value
)
572 struct args_entry
*entry
;
574 entry
= args_find(args
, flag
);
576 entry
= xcalloc(1, sizeof *entry
);
579 TAILQ_INIT(&entry
->values
);
580 RB_INSERT(args_tree
, &args
->tree
, entry
);
583 if (value
!= NULL
&& value
->type
!= ARGS_NONE
)
584 TAILQ_INSERT_TAIL(&entry
->values
, value
, entry
);
587 /* Get argument value. Will be NULL if it isn't present. */
589 args_get(struct args
*args
, u_char flag
)
591 struct args_entry
*entry
;
593 if ((entry
= args_find(args
, flag
)) == NULL
)
595 if (TAILQ_EMPTY(&entry
->values
))
597 return (TAILQ_LAST(&entry
->values
, args_values
)->string
);
600 /* Get first argument. */
602 args_first(struct args
*args
, struct args_entry
**entry
)
604 *entry
= RB_MIN(args_tree
, &args
->tree
);
607 return ((*entry
)->flag
);
610 /* Get next argument. */
612 args_next(struct args_entry
**entry
)
614 *entry
= RB_NEXT(args_tree
, &args
->tree
, *entry
);
617 return ((*entry
)->flag
);
620 /* Get argument count. */
622 args_count(struct args
*args
)
624 return (args
->count
);
627 /* Get argument values. */
629 args_values(struct args
*args
)
631 return (args
->values
);
634 /* Get argument value. */
636 args_value(struct args
*args
, u_int idx
)
638 if (idx
>= args
->count
)
640 return (&args
->values
[idx
]);
643 /* Return argument as string. */
645 args_string(struct args
*args
, u_int idx
)
647 if (idx
>= args
->count
)
649 return (args_value_as_string(&args
->values
[idx
]));
652 /* Make a command now. */
654 args_make_commands_now(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
657 struct args_command_state
*state
;
659 struct cmd_list
*cmdlist
;
661 state
= args_make_commands_prepare(self
, item
, idx
, NULL
, 0, expand
);
662 cmdlist
= args_make_commands(state
, 0, NULL
, &error
);
663 if (cmdlist
== NULL
) {
664 cmdq_error(item
, "%s", error
);
668 cmdlist
->references
++;
669 args_make_commands_free(state
);
673 /* Save bits to make a command later. */
674 struct args_command_state
*
675 args_make_commands_prepare(struct cmd
*self
, struct cmdq_item
*item
, u_int idx
,
676 const char *default_command
, int wait
, int expand
)
678 struct args
*args
= cmd_get_args(self
);
679 struct cmd_find_state
*target
= cmdq_get_target(item
);
680 struct client
*tc
= cmdq_get_target_client(item
);
681 struct args_value
*value
;
682 struct args_command_state
*state
;
685 state
= xcalloc(1, sizeof *state
);
687 if (idx
< args
->count
) {
688 value
= &args
->values
[idx
];
689 if (value
->type
== ARGS_COMMANDS
) {
690 state
->cmdlist
= value
->cmdlist
;
691 state
->cmdlist
->references
++;
696 if (default_command
== NULL
)
697 fatalx("argument out of range");
698 cmd
= default_command
;
703 state
->cmd
= format_single_from_target(item
, cmd
);
705 state
->cmd
= xstrdup(cmd
);
706 log_debug("%s: %s", __func__
, state
->cmd
);
709 state
->pi
.item
= item
;
710 cmd_get_source(self
, &state
->pi
.file
, &state
->pi
.line
);
712 if (state
->pi
.c
!= NULL
)
713 state
->pi
.c
->references
++;
714 cmd_find_copy_state(&state
->pi
.fs
, target
);
719 /* Return argument as command. */
721 args_make_commands(struct args_command_state
*state
, int argc
, char **argv
,
724 struct cmd_parse_result
*pr
;
728 if (state
->cmdlist
!= NULL
) {
730 return (state
->cmdlist
);
731 return (cmd_list_copy(state
->cmdlist
, argc
, argv
));
734 cmd
= xstrdup(state
->cmd
);
735 for (i
= 0; i
< argc
; i
++) {
736 new_cmd
= cmd_template_replace(cmd
, argv
[i
], i
+ 1);
737 log_debug("%s: %%%u %s: %s", __func__
, i
+ 1, argv
[i
], new_cmd
);
741 log_debug("%s: %s", __func__
, cmd
);
743 pr
= cmd_parse_from_string(cmd
, &state
->pi
);
745 switch (pr
->status
) {
746 case CMD_PARSE_ERROR
:
749 case CMD_PARSE_SUCCESS
:
750 return (pr
->cmdlist
);
754 /* Free commands state. */
756 args_make_commands_free(struct args_command_state
*state
)
758 if (state
->cmdlist
!= NULL
)
759 cmd_list_free(state
->cmdlist
);
760 if (state
->pi
.c
!= NULL
)
761 server_client_unref(state
->pi
.c
);
766 /* Get prepared command. */
768 args_make_commands_get_command(struct args_command_state
*state
)
774 if (state
->cmdlist
!= NULL
) {
775 first
= cmd_list_first(state
->cmdlist
);
777 return (xstrdup(""));
778 return (xstrdup(cmd_get_entry(first
)->name
));
780 n
= strcspn(state
->cmd
, " ,");
781 xasprintf(&s
, "%.*s", n
, state
->cmd
);
785 /* Get first value in argument. */
787 args_first_value(struct args
*args
, u_char flag
)
789 struct args_entry
*entry
;
791 if ((entry
= args_find(args
, flag
)) == NULL
)
793 return (TAILQ_FIRST(&entry
->values
));
796 /* Get next value in argument. */
798 args_next_value(struct args_value
*value
)
800 return (TAILQ_NEXT(value
, entry
));
803 /* Convert an argument value to a number. */
805 args_strtonum(struct args
*args
, u_char flag
, long long minval
,
806 long long maxval
, char **cause
)
810 struct args_entry
*entry
;
811 struct args_value
*value
;
813 if ((entry
= args_find(args
, flag
)) == NULL
) {
814 *cause
= xstrdup("missing");
817 value
= TAILQ_LAST(&entry
->values
, args_values
);
819 ll
= strtonum(value
->string
, minval
, maxval
, &errstr
);
820 if (errstr
!= NULL
) {
821 *cause
= xstrdup(errstr
);
829 /* Convert an argument to a number which may be a percentage. */
831 args_percentage(struct args
*args
, u_char flag
, long long minval
,
832 long long maxval
, long long curval
, char **cause
)
835 struct args_entry
*entry
;
837 if ((entry
= args_find(args
, flag
)) == NULL
) {
838 *cause
= xstrdup("missing");
841 value
= TAILQ_LAST(&entry
->values
, args_values
)->string
;
842 return (args_string_percentage(value
, minval
, maxval
, curval
, cause
));
845 /* Convert a string to a number which may be a percentage. */
847 args_string_percentage(const char *value
, long long minval
, long long maxval
,
848 long long curval
, char **cause
)
852 size_t valuelen
= strlen(value
);
855 if (value
[valuelen
- 1] == '%') {
856 copy
= xstrdup(value
);
857 copy
[valuelen
- 1] = '\0';
859 ll
= strtonum(copy
, 0, 100, &errstr
);
861 if (errstr
!= NULL
) {
862 *cause
= xstrdup(errstr
);
865 ll
= (curval
* ll
) / 100;
867 *cause
= xstrdup("too small");
871 *cause
= xstrdup("too large");
875 ll
= strtonum(value
, minval
, maxval
, &errstr
);
876 if (errstr
!= NULL
) {
877 *cause
= xstrdup(errstr
);