1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
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 
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Manipulate command arguments.
30  */
31 
32 /* List of argument values. */
33 TAILQ_HEAD(args_values, args_value);
34 
35 /* Single arguments flag. */
36 struct args_entry {
37           u_char                         flag;
38           struct args_values   values;
39           u_int                          count;
40 
41           int                            flags;
42 #define ARGS_ENTRY_OPTIONAL_VALUE 0x1
43 
44           RB_ENTRY(args_entry)           entry;
45 };
46 
47 /* Parsed argument flags and values. */
48 struct args {
49           struct args_tree     tree;
50           u_int                          count;
51           struct args_value   *values;
52 };
53 
54 /* Prepared command state. */
55 struct args_command_state {
56           struct cmd_list               *cmdlist;
57           char                          *cmd;
58           struct cmd_parse_input         pi;
59 };
60 
61 static struct args_entry      *args_find(struct args *, u_char);
62 
63 static int          args_cmp(struct args_entry *, struct args_entry *);
64 RB_GENERATE_STATIC(args_tree, args_entry, entry, args_cmp);
65 
66 /* Arguments tree comparison function. */
67 static int
args_cmp(struct args_entry * a1,struct args_entry * a2)68 args_cmp(struct args_entry *a1, struct args_entry *a2)
69 {
70           return (a1->flag - a2->flag);
71 }
72 
73 /* Find a flag in the arguments tree. */
74 static struct args_entry *
args_find(struct args * args,u_char flag)75 args_find(struct args *args, u_char flag)
76 {
77           struct args_entry   entry;
78 
79           entry.flag = flag;
80           return (RB_FIND(args_tree, &args->tree, &entry));
81 }
82 
83 /* Copy value. */
84 static void
args_copy_value(struct args_value * to,struct args_value * from)85 args_copy_value(struct args_value *to, struct args_value *from)
86 {
87           to->type = from->type;
88           switch (from->type) {
89           case ARGS_NONE:
90                     break;
91           case ARGS_COMMANDS:
92                     to->cmdlist = from->cmdlist;
93                     to->cmdlist->references++;
94                     break;
95           case ARGS_STRING:
96                     to->string = xstrdup(from->string);
97                     break;
98           }
99 }
100 
101 /* Type to string. */
102 static const char *
args_type_to_string(enum args_type type)103 args_type_to_string (enum args_type type)
104 {
105           switch (type)
106           {
107           case ARGS_NONE:
108                     return "NONE";
109           case ARGS_STRING:
110                     return "STRING";
111           case ARGS_COMMANDS:
112                     return "COMMANDS";
113           }
114           return "INVALID";
115 }
116 
117 /* Get value as string. */
118 static const char *
args_value_as_string(struct args_value * value)119 args_value_as_string(struct args_value *value)
120 {
121           switch (value->type) {
122           case ARGS_NONE:
123                     return ("");
124           case ARGS_COMMANDS:
125                     if (value->cached == NULL)
126                               value->cached = cmd_list_print(value->cmdlist, 0);
127                     return (value->cached);
128           case ARGS_STRING:
129                     return (value->string);
130           }
131           fatalx("unexpected argument type");
132 }
133 
134 /* Create an empty arguments set. */
135 struct args *
args_create(void)136 args_create(void)
137 {
138           struct args          *args;
139 
140           args = xcalloc(1, sizeof *args);
141           RB_INIT(&args->tree);
142           return (args);
143 }
144 
145 /* Parse a single flag. */
146 static int
args_parse_flag_argument(struct args_value * values,u_int count,char ** cause,struct args * args,u_int * i,const char * string,int flag,int optional_argument)147 args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
148     struct args *args, u_int *i, const char *string, int flag,
149     int optional_argument)
150 {
151           struct args_value   *argument, *new;
152           const char                    *s;
153 
154           new = xcalloc(1, sizeof *new);
155           if (*string != '\0') {
156                     new->type = ARGS_STRING;
157                     new->string = xstrdup(string);
158                     goto out;
159           }
160 
161           if (*i == count)
162                     argument = NULL;
163           else {
164                     argument = &values[*i];
165                     if (argument->type != ARGS_STRING) {
166                               xasprintf(cause, "-%c argument must be a string", flag);
167                               args_free_value(new);
168                               free(new);
169                               return (-1);
170                     }
171           }
172           if (argument == NULL) {
173                     args_free_value(new);
174                     free(new);
175                     if (optional_argument) {
176                               log_debug("%s: -%c (optional)", __func__, flag);
177                               args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
178                               return (0); /* either - or end */
179                     }
180                     xasprintf(cause, "-%c expects an argument", flag);
181                     return (-1);
182           }
183           args_copy_value(new, argument);
184           (*i)++;
185 
186 out:
187           s = args_value_as_string(new);
188           log_debug("%s: -%c = %s", __func__, flag, s);
189           args_set(args, flag, new, 0);
190           return (0);
191 }
192 
193 /* Parse flags argument. */
194 static int
args_parse_flags(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause,struct args * args,u_int * i)195 args_parse_flags(const struct args_parse *parse, struct args_value *values,
196     u_int count, char **cause, struct args *args, u_int *i)
197 {
198           struct args_value   *value;
199           u_char                         flag;
200           const char                    *found, *string;
201           int                            optional_argument;
202 
203           value = &values[*i];
204           if (value->type != ARGS_STRING)
205                     return (1);
206 
207           string = value->string;
208           log_debug("%s: next %s", __func__, string);
209           if (*string++ != '-' || *string == '\0')
210                     return (1);
211           (*i)++;
212           if (string[0] == '-' && string[1] == '\0')
213                     return (1);
214 
215           for (;;) {
216                     flag = *string++;
217                     if (flag == '\0')
218                               return (0);
219                     if (flag == '?')
220                               return (-1);
221                     if (!isalnum(flag)) {
222                               xasprintf(cause, "invalid flag -%c", flag);
223                               return (-1);
224                     }
225 
226                     found = strchr(parse->template, flag);
227                     if (found == NULL) {
228                               xasprintf(cause, "unknown flag -%c", flag);
229                               return (-1);
230                     }
231                     if (found[1] != ':') {
232                               log_debug("%s: -%c", __func__, flag);
233                               args_set(args, flag, NULL, 0);
234                               continue;
235                     }
236                     optional_argument = (found[2] == ':');
237                     return (args_parse_flag_argument(values, count, cause, args, i,
238                         string, flag, optional_argument));
239           }
240 }
241 
242 /* Parse arguments into a new argument set. */
243 struct args *
args_parse(const struct args_parse * parse,struct args_value * values,u_int count,char ** cause)244 args_parse(const struct args_parse *parse, struct args_value *values,
245     u_int count, char **cause)
246 {
247           struct args                   *args;
248           u_int                          i;
249           enum args_parse_type           type;
250           struct args_value   *value, *new;
251           const char                    *s;
252           int                            stop;
253 
254           if (count == 0)
255                     return (args_create());
256 
257           args = args_create();
258           for (i = 1; i < count; /* nothing */) {
259                     stop = args_parse_flags(parse, values, count, cause, args, &i);
260                     if (stop == -1) {
261                               args_free(args);
262                               return (NULL);
263                     }
264                     if (stop == 1)
265                               break;
266           }
267           log_debug("%s: flags end at %u of %u", __func__, i, count);
268           if (i != count) {
269                     for (/* nothing */; i < count; i++) {
270                               value = &values[i];
271 
272                               s = args_value_as_string(value);
273                               log_debug("%s: %u = %s (type %s)", __func__, i, s,
274                                   args_type_to_string (value->type));
275 
276                               if (parse->cb != NULL) {
277                                         type = parse->cb(args, args->count, cause);
278                                         if (type == ARGS_PARSE_INVALID) {
279                                                   args_free(args);
280                                                   return (NULL);
281                                         }
282                               } else
283                                         type = ARGS_PARSE_STRING;
284 
285                               args->values = xrecallocarray(args->values,
286                                   args->count, args->count + 1, sizeof *args->values);
287                               new = &args->values[args->count++];
288 
289                               switch (type) {
290                               case ARGS_PARSE_INVALID:
291                                         fatalx("unexpected argument type");
292                               case ARGS_PARSE_STRING:
293                                         if (value->type != ARGS_STRING) {
294                                                   xasprintf(cause,
295                                                       "argument %u must be \"string\"",
296                                                       args->count);
297                                                   args_free(args);
298                                                   return (NULL);
299                                         }
300                                         args_copy_value(new, value);
301                                         break;
302                               case ARGS_PARSE_COMMANDS_OR_STRING:
303                                         args_copy_value(new, value);
304                                         break;
305                               case ARGS_PARSE_COMMANDS:
306                                         if (value->type != ARGS_COMMANDS) {
307                                                   xasprintf(cause,
308                                                       "argument %u must be { commands }",
309                                                       args->count);
310                                                   args_free(args);
311                                                   return (NULL);
312                                         }
313                                         args_copy_value(new, value);
314                                         break;
315                               }
316                     }
317           }
318 
319           if (parse->lower != -1 && args->count < (u_int)parse->lower) {
320                     xasprintf(cause,
321                         "too few arguments (need at least %u)",
322                         parse->lower);
323                     args_free(args);
324                     return (NULL);
325           }
326           if (parse->upper != -1 && args->count > (u_int)parse->upper) {
327                     xasprintf(cause,
328                         "too many arguments (need at most %u)",
329                         parse->upper);
330                     args_free(args);
331                     return (NULL);
332           }
333           return (args);
334 }
335 
336 /* Copy and expand a value. */
337 static void
args_copy_copy_value(struct args_value * to,struct args_value * from,int argc,char ** argv)338 args_copy_copy_value(struct args_value *to, struct args_value *from, int argc,
339     char **argv)
340 {
341           char      *s, *expanded;
342           int        i;
343 
344           to->type = from->type;
345           switch (from->type) {
346           case ARGS_NONE:
347                     break;
348           case ARGS_STRING:
349                     expanded = xstrdup(from->string);
350                     for (i = 0; i < argc; i++) {
351                               s = cmd_template_replace(expanded, argv[i], i + 1);
352                               free(expanded);
353                               expanded = s;
354                     }
355                     to->string = expanded;
356                     break;
357           case ARGS_COMMANDS:
358                     to->cmdlist = cmd_list_copy(from->cmdlist, argc, argv);
359                     break;
360           }
361 }
362 
363 /* Copy an arguments set. */
364 struct args *
args_copy(struct args * args,int argc,char ** argv)365 args_copy(struct args *args, int argc, char **argv)
366 {
367           struct args                   *new_args;
368           struct args_entry   *entry;
369           struct args_value   *value, *new_value;
370           u_int                          i;
371 
372           cmd_log_argv(argc, argv, "%s", __func__);
373 
374           new_args = args_create();
375           RB_FOREACH(entry, args_tree, &args->tree) {
376                     if (TAILQ_EMPTY(&entry->values)) {
377                               for (i = 0; i < entry->count; i++)
378                                         args_set(new_args, entry->flag, NULL, 0);
379                               continue;
380                     }
381                     TAILQ_FOREACH(value, &entry->values, entry) {
382                               new_value = xcalloc(1, sizeof *new_value);
383                               args_copy_copy_value(new_value, value, argc, argv);
384                               args_set(new_args, entry->flag, new_value, 0);
385                     }
386           }
387           if (args->count == 0)
388                     return (new_args);
389           new_args->count = args->count;
390           new_args->values = xcalloc(args->count, sizeof *new_args->values);
391           for (i = 0; i < args->count; i++) {
392                     new_value = &new_args->values[i];
393                     args_copy_copy_value(new_value, &args->values[i], argc, argv);
394           }
395           return (new_args);
396 }
397 
398 /* Free a value. */
399 void
args_free_value(struct args_value * value)400 args_free_value(struct args_value *value)
401 {
402           switch (value->type) {
403           case ARGS_NONE:
404                     break;
405           case ARGS_STRING:
406                     free(value->string);
407                     break;
408           case ARGS_COMMANDS:
409                     cmd_list_free(value->cmdlist);
410                     break;
411           }
412           free(value->cached);
413 }
414 
415 /* Free values. */
416 void
args_free_values(struct args_value * values,u_int count)417 args_free_values(struct args_value *values, u_int count)
418 {
419           u_int     i;
420 
421           for (i = 0; i < count; i++)
422                     args_free_value(&values[i]);
423 }
424 
425 /* Free an arguments set. */
426 void
args_free(struct args * args)427 args_free(struct args *args)
428 {
429           struct args_entry   *entry;
430           struct args_entry   *entry1;
431           struct args_value   *value;
432           struct args_value   *value1;
433 
434           args_free_values(args->values, args->count);
435           free(args->values);
436 
437           RB_FOREACH_SAFE(entry, args_tree, &args->tree, entry1) {
438                     RB_REMOVE(args_tree, &args->tree, entry);
439                     TAILQ_FOREACH_SAFE(value, &entry->values, entry, value1) {
440                               TAILQ_REMOVE(&entry->values, value, entry);
441                               args_free_value(value);
442                               free(value);
443                     }
444                     free(entry);
445           }
446 
447           free(args);
448 }
449 
450 /* Convert arguments to vector. */
451 void
args_to_vector(struct args * args,int * argc,char *** argv)452 args_to_vector(struct args *args, int *argc, char ***argv)
453 {
454           char      *s;
455           u_int      i;
456 
457           *argc = 0;
458           *argv = NULL;
459 
460           for (i = 0; i < args->count; i++) {
461                     switch (args->values[i].type) {
462                     case ARGS_NONE:
463                               break;
464                     case ARGS_STRING:
465                               cmd_append_argv(argc, argv, args->values[i].string);
466                               break;
467                     case ARGS_COMMANDS:
468                               s = cmd_list_print(args->values[i].cmdlist, 0);
469                               cmd_append_argv(argc, argv, s);
470                               free(s);
471                               break;
472                     }
473           }
474 }
475 
476 /* Convert arguments from vector. */
477 struct args_value *
args_from_vector(int argc,char ** argv)478 args_from_vector(int argc, char **argv)
479 {
480           struct args_value   *values;
481           int                            i;
482 
483           values = xcalloc(argc, sizeof *values);
484           for (i = 0; i < argc; i++) {
485                     values[i].type = ARGS_STRING;
486                     values[i].string = xstrdup(argv[i]);
487           }
488           return (values);
489 }
490 
491 /* Add to string. */
492 static void printflike(3, 4)
args_print_add(char ** buf,size_t * len,const char * fmt,...)493 args_print_add(char **buf, size_t *len, const char *fmt, ...)
494 {
495           va_list    ap;
496           char      *s;
497           size_t     slen;
498 
499           va_start(ap, fmt);
500           slen = xvasprintf(&s, fmt, ap);
501           va_end(ap);
502 
503           *len += slen;
504           *buf = xrealloc(*buf, *len);
505 
506           strlcat(*buf, s, *len);
507           free(s);
508 }
509 
510 /* Add value to string. */
511 static void
args_print_add_value(char ** buf,size_t * len,struct args_value * value)512 args_print_add_value(char **buf, size_t *len, struct args_value *value)
513 {
514           char      *expanded = NULL;
515 
516           if (**buf != '\0')
517                     args_print_add(buf, len, " ");
518 
519           switch (value->type) {
520           case ARGS_NONE:
521                     break;
522           case ARGS_COMMANDS:
523                     expanded = cmd_list_print(value->cmdlist, 0);
524                     args_print_add(buf, len, "{ %s }", expanded);
525                     break;
526           case ARGS_STRING:
527                     expanded = args_escape(value->string);
528                     args_print_add(buf, len, "%s", expanded);
529                     break;
530           }
531           free(expanded);
532 }
533 
534 /* Print a set of arguments. */
535 char *
args_print(struct args * args)536 args_print(struct args *args)
537 {
538           size_t                         len;
539           char                          *buf;
540           u_int                          i, j;
541           struct args_entry   *entry;
542           struct args_entry   *last = NULL;
543           struct args_value   *value;
544 
545           len = 1;
546           buf = xcalloc(1, len);
547 
548           /* Process the flags first. */
549           RB_FOREACH(entry, args_tree, &args->tree) {
550                     if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
551                               continue;
552                     if (!TAILQ_EMPTY(&entry->values))
553                               continue;
554 
555                     if (*buf == '\0')
556                               args_print_add(&buf, &len, "-");
557                     for (j = 0; j < entry->count; j++)
558                               args_print_add(&buf, &len, "%c", entry->flag);
559           }
560 
561           /* Then the flags with arguments. */
562           RB_FOREACH(entry, args_tree, &args->tree) {
563                     if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
564                               if (*buf != '\0')
565                                         args_print_add(&buf, &len, " -%c", entry->flag);
566                               else
567                                         args_print_add(&buf, &len, "-%c", entry->flag);
568                               last = entry;
569                               continue;
570                     }
571                     if (TAILQ_EMPTY(&entry->values))
572                               continue;
573                     TAILQ_FOREACH(value, &entry->values, entry) {
574                               if (*buf != '\0')
575                                         args_print_add(&buf, &len, " -%c", entry->flag);
576                               else
577                                         args_print_add(&buf, &len, "-%c", entry->flag);
578                               args_print_add_value(&buf, &len, value);
579                     }
580                     last = entry;
581           }
582           if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
583                     args_print_add(&buf, &len, " --");
584 
585           /* And finally the argument vector. */
586           for (i = 0; i < args->count; i++)
587                     args_print_add_value(&buf, &len, &args->values[i]);
588 
589           return (buf);
590 }
591 
592 /* Escape an argument. */
593 char *
args_escape(const char * s)594 args_escape(const char *s)
595 {
596           static const char    dquoted[] = " #';${}%";
597           static const char    squoted[] = " \"";
598           char                          *escaped, *result;
599           int                            flags, quotes = 0;
600 
601           if (*s == '\0') {
602                     xasprintf(&result, "''");
603                     return (result);
604           }
605           if (s[strcspn(s, dquoted)] != '\0')
606                     quotes = '"';
607           else if (s[strcspn(s, squoted)] != '\0')
608                     quotes = '\'';
609 
610           if (s[0] != ' ' &&
611               s[1] == '\0' &&
612               (quotes != 0 || s[0] == '~')) {
613                     xasprintf(&escaped, "\\%c", s[0]);
614                     return (escaped);
615           }
616 
617           flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
618           if (quotes == '"')
619                     flags |= VIS_DQ;
620           utf8_stravis(&escaped, s, flags);
621 
622           if (quotes == '\'')
623                     xasprintf(&result, "'%s'", escaped);
624           else if (quotes == '"') {
625                     if (*escaped == '~')
626                               xasprintf(&result, "\"\\%s\"", escaped);
627                     else
628                               xasprintf(&result, "\"%s\"", escaped);
629           } else {
630                     if (*escaped == '~')
631                               xasprintf(&result, "\\%s", escaped);
632                     else
633                               result = xstrdup(escaped);
634           }
635           free(escaped);
636           return (result);
637 }
638 
639 /* Return if an argument is present. */
640 int
args_has(struct args * args,u_char flag)641 args_has(struct args *args, u_char flag)
642 {
643           struct args_entry   *entry;
644 
645           entry = args_find(args, flag);
646           if (entry == NULL)
647                     return (0);
648           return (entry->count);
649 }
650 
651 /* Set argument value in the arguments tree. */
652 void
args_set(struct args * args,u_char flag,struct args_value * value,int flags)653 args_set(struct args *args, u_char flag, struct args_value *value, int flags)
654 {
655           struct args_entry   *entry;
656 
657           entry = args_find(args, flag);
658           if (entry == NULL) {
659                     entry = xcalloc(1, sizeof *entry);
660                     entry->flag = flag;
661                     entry->count = 1;
662                     entry->flags = flags;
663                     TAILQ_INIT(&entry->values);
664                     RB_INSERT(args_tree, &args->tree, entry);
665           } else
666                     entry->count++;
667           if (value != NULL && value->type != ARGS_NONE)
668                     TAILQ_INSERT_TAIL(&entry->values, value, entry);
669           else
670                     free(value);
671 }
672 
673 /* Get argument value. Will be NULL if it isn't present. */
674 const char *
args_get(struct args * args,u_char flag)675 args_get(struct args *args, u_char flag)
676 {
677           struct args_entry   *entry;
678 
679           if ((entry = args_find(args, flag)) == NULL)
680                     return (NULL);
681           if (TAILQ_EMPTY(&entry->values))
682                     return (NULL);
683           return (TAILQ_LAST(&entry->values, args_values)->string);
684 }
685 
686 /* Get first argument. */
687 u_char
args_first(struct args * args,struct args_entry ** entry)688 args_first(struct args *args, struct args_entry **entry)
689 {
690           *entry = RB_MIN(args_tree, &args->tree);
691           if (*entry == NULL)
692                     return (0);
693           return ((*entry)->flag);
694 }
695 
696 /* Get next argument. */
697 u_char
args_next(struct args_entry ** entry)698 args_next(struct args_entry **entry)
699 {
700           *entry = RB_NEXT(args_tree, &args->tree, *entry);
701           if (*entry == NULL)
702                     return (0);
703           return ((*entry)->flag);
704 }
705 
706 /* Get argument count. */
707 u_int
args_count(struct args * args)708 args_count(struct args *args)
709 {
710           return (args->count);
711 }
712 
713 /* Get argument values. */
714 struct args_value *
args_values(struct args * args)715 args_values(struct args *args)
716 {
717           return (args->values);
718 }
719 
720 /* Get argument value. */
721 struct args_value *
args_value(struct args * args,u_int idx)722 args_value(struct args *args, u_int idx)
723 {
724           if (idx >= args->count)
725                     return (NULL);
726           return (&args->values[idx]);
727 }
728 
729 /* Return argument as string. */
730 const char *
args_string(struct args * args,u_int idx)731 args_string(struct args *args, u_int idx)
732 {
733           if (idx >= args->count)
734                     return (NULL);
735           return (args_value_as_string(&args->values[idx]));
736 }
737 
738 /* Make a command now. */
739 struct cmd_list *
args_make_commands_now(struct cmd * self,struct cmdq_item * item,u_int idx,int expand)740 args_make_commands_now(struct cmd *self, struct cmdq_item *item, u_int idx,
741     int expand)
742 {
743           struct args_command_state     *state;
744           char                                    *error;
745           struct cmd_list                         *cmdlist;
746 
747           state = args_make_commands_prepare(self, item, idx, NULL, 0, expand);
748           cmdlist = args_make_commands(state, 0, NULL, &error);
749           if (cmdlist == NULL) {
750                     cmdq_error(item, "%s", error);
751                     free(error);
752           }
753           else
754                     cmdlist->references++;
755           args_make_commands_free(state);
756           return (cmdlist);
757 }
758 
759 /* Save bits to make a command later. */
760 struct args_command_state *
args_make_commands_prepare(struct cmd * self,struct cmdq_item * item,u_int idx,const char * default_command,int wait,int expand)761 args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
762     const char *default_command, int wait, int expand)
763 {
764           struct args                             *args = cmd_get_args(self);
765           struct cmd_find_state                   *target = cmdq_get_target(item);
766           struct client                           *tc = cmdq_get_target_client(item);
767           struct args_value             *value;
768           struct args_command_state     *state;
769           const char                              *cmd;
770           const char                              *file;
771 
772           state = xcalloc(1, sizeof *state);
773 
774           if (idx < args->count) {
775                     value = &args->values[idx];
776                     if (value->type == ARGS_COMMANDS) {
777                               state->cmdlist = value->cmdlist;
778                               state->cmdlist->references++;
779                               return (state);
780                     }
781                     cmd = value->string;
782           } else {
783                     if (default_command == NULL)
784                               fatalx("argument out of range");
785                     cmd = default_command;
786           }
787 
788 
789           if (expand)
790                     state->cmd = format_single_from_target(item, cmd);
791           else
792                     state->cmd = xstrdup(cmd);
793           log_debug("%s: %s", __func__, state->cmd);
794 
795           if (wait)
796                     state->pi.item = item;
797           cmd_get_source(self, &file, &state->pi.line);
798           if (file != NULL)
799                     state->pi.file = xstrdup(file);
800           state->pi.c = tc;
801           if (state->pi.c != NULL)
802                     state->pi.c->references++;
803           cmd_find_copy_state(&state->pi.fs, target);
804 
805           return (state);
806 }
807 
808 /* Return argument as command. */
809 struct cmd_list *
args_make_commands(struct args_command_state * state,int argc,char ** argv,char ** error)810 args_make_commands(struct args_command_state *state, int argc, char **argv,
811     char **error)
812 {
813           struct cmd_parse_result       *pr;
814           char                          *cmd, *new_cmd;
815           int                            i;
816 
817           if (state->cmdlist != NULL) {
818                     if (argc == 0)
819                               return (state->cmdlist);
820                     return (cmd_list_copy(state->cmdlist, argc, argv));
821           }
822 
823           cmd = xstrdup(state->cmd);
824           log_debug("%s: %s", __func__, cmd);
825           cmd_log_argv(argc, argv, __func__);
826           for (i = 0; i < argc; i++) {
827                     new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
828                     log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
829                     free(cmd);
830                     cmd = new_cmd;
831           }
832           log_debug("%s: %s", __func__, cmd);
833 
834           pr = cmd_parse_from_string(cmd, &state->pi);
835           free(cmd);
836           switch (pr->status) {
837           case CMD_PARSE_ERROR:
838                     *error = pr->error;
839                     return (NULL);
840           case CMD_PARSE_SUCCESS:
841                     return (pr->cmdlist);
842           }
843           fatalx("invalid parse return state");
844 }
845 
846 /* Free commands state. */
847 void
args_make_commands_free(struct args_command_state * state)848 args_make_commands_free(struct args_command_state *state)
849 {
850           if (state->cmdlist != NULL)
851                     cmd_list_free(state->cmdlist);
852           if (state->pi.c != NULL)
853                     server_client_unref(state->pi.c);
854           free(__UNCONST(state->pi.file));
855           free(state->cmd);
856           free(state);
857 }
858 
859 /* Get prepared command. */
860 char *
args_make_commands_get_command(struct args_command_state * state)861 args_make_commands_get_command(struct args_command_state *state)
862 {
863           struct cmd          *first;
864           int                  n;
865           char                *s;
866 
867           if (state->cmdlist != NULL) {
868                     first = cmd_list_first(state->cmdlist);
869                     if (first == NULL)
870                               return (xstrdup(""));
871                     return (xstrdup(cmd_get_entry(first)->name));
872           }
873           n = strcspn(state->cmd, " ,");
874           xasprintf(&s, "%.*s", n, state->cmd);
875           return (s);
876 }
877 
878 /* Get first value in argument. */
879 struct args_value *
args_first_value(struct args * args,u_char flag)880 args_first_value(struct args *args, u_char flag)
881 {
882           struct args_entry   *entry;
883 
884           if ((entry = args_find(args, flag)) == NULL)
885                     return (NULL);
886           return (TAILQ_FIRST(&entry->values));
887 }
888 
889 /* Get next value in argument. */
890 struct args_value *
args_next_value(struct args_value * value)891 args_next_value(struct args_value *value)
892 {
893           return (TAILQ_NEXT(value, entry));
894 }
895 
896 /* Convert an argument value to a number. */
897 long long
args_strtonum(struct args * args,u_char flag,long long minval,long long maxval,char ** cause)898 args_strtonum(struct args *args, u_char flag, long long minval,
899     long long maxval, char **cause)
900 {
901           const char                    *errstr;
902           long long            ll;
903           struct args_entry   *entry;
904           struct args_value   *value;
905 
906           if ((entry = args_find(args, flag)) == NULL) {
907                     *cause = xstrdup("missing");
908                     return (0);
909           }
910           value = TAILQ_LAST(&entry->values, args_values);
911           if (value == NULL ||
912               value->type != ARGS_STRING ||
913               value->string == NULL) {
914                     *cause = xstrdup("missing");
915                     return (0);
916           }
917 
918           ll = strtonum(value->string, minval, maxval, &errstr);
919           if (errstr != NULL) {
920                     *cause = xstrdup(errstr);
921                     return (0);
922           }
923 
924           *cause = NULL;
925           return (ll);
926 }
927 
928 /* Convert an argument value to a number, and expand formats. */
929 long long
args_strtonum_and_expand(struct args * args,u_char flag,long long minval,long long maxval,struct cmdq_item * item,char ** cause)930 args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
931     long long maxval, struct cmdq_item *item, char **cause)
932 {
933           const char                    *errstr;
934           char                          *formatted;
935           long long            ll;
936           struct args_entry   *entry;
937           struct args_value   *value;
938 
939           if ((entry = args_find(args, flag)) == NULL) {
940                     *cause = xstrdup("missing");
941                     return (0);
942           }
943           value = TAILQ_LAST(&entry->values, args_values);
944           if (value == NULL ||
945               value->type != ARGS_STRING ||
946               value->string == NULL) {
947                     *cause = xstrdup("missing");
948                     return (0);
949           }
950 
951           formatted = format_single_from_target(item, value->string);
952           ll = strtonum(formatted, minval, maxval, &errstr);
953           free(formatted);
954           if (errstr != NULL) {
955                     *cause = xstrdup(errstr);
956                     return (0);
957           }
958 
959           *cause = NULL;
960           return (ll);
961 }
962 
963 /* Convert an argument to a number which may be a percentage. */
964 long long
args_percentage(struct args * args,u_char flag,long long minval,long long maxval,long long curval,char ** cause)965 args_percentage(struct args *args, u_char flag, long long minval,
966     long long maxval, long long curval, char **cause)
967 {
968           const char                    *value;
969           struct args_entry   *entry;
970 
971           if ((entry = args_find(args, flag)) == NULL) {
972                     *cause = xstrdup("missing");
973                     return (0);
974           }
975           if (TAILQ_EMPTY(&entry->values)) {
976                     *cause = xstrdup("empty");
977                     return (0);
978           }
979           value = TAILQ_LAST(&entry->values, args_values)->string;
980           return (args_string_percentage(value, minval, maxval, curval, cause));
981 }
982 
983 /* Convert a string to a number which may be a percentage. */
984 long long
args_string_percentage(const char * value,long long minval,long long maxval,long long curval,char ** cause)985 args_string_percentage(const char *value, long long minval, long long maxval,
986     long long curval, char **cause)
987 {
988           const char          *errstr;
989           long long  ll;
990           size_t               valuelen = strlen(value);
991           char                *copy;
992 
993           if (valuelen == 0) {
994                     *cause = xstrdup("empty");
995                     return (0);
996           }
997           if (value[valuelen - 1] == '%') {
998                     copy = xstrdup(value);
999                     copy[valuelen - 1] = '\0';
1000 
1001                     ll = strtonum(copy, 0, 100, &errstr);
1002                     free(copy);
1003                     if (errstr != NULL) {
1004                               *cause = xstrdup(errstr);
1005                               return (0);
1006                     }
1007                     ll = (curval * ll) / 100;
1008                     if (ll < minval) {
1009                               *cause = xstrdup("too small");
1010                               return (0);
1011                     }
1012                     if (ll > maxval) {
1013                               *cause = xstrdup("too large");
1014                               return (0);
1015                     }
1016           } else {
1017                     ll = strtonum(value, minval, maxval, &errstr);
1018                     if (errstr != NULL) {
1019                               *cause = xstrdup(errstr);
1020                               return (0);
1021                     }
1022           }
1023 
1024           *cause = NULL;
1025           return (ll);
1026 }
1027 
1028 /*
1029  * Convert an argument to a number which may be a percentage, and expand
1030  * formats.
1031  */
1032 long long
args_percentage_and_expand(struct args * args,u_char flag,long long minval,long long maxval,long long curval,struct cmdq_item * item,char ** cause)1033 args_percentage_and_expand(struct args *args, u_char flag, long long minval,
1034     long long maxval, long long curval, struct cmdq_item *item, char **cause)
1035 {
1036           const char                    *value;
1037           struct args_entry   *entry;
1038 
1039           if ((entry = args_find(args, flag)) == NULL) {
1040                     *cause = xstrdup("missing");
1041                     return (0);
1042           }
1043           if (TAILQ_EMPTY(&entry->values)) {
1044                     *cause = xstrdup("empty");
1045                     return (0);
1046           }
1047           value = TAILQ_LAST(&entry->values, args_values)->string;
1048           return (args_string_percentage_and_expand(value, minval, maxval, curval,
1049                         item, cause));
1050 }
1051 
1052 /*
1053  * Convert a string to a number which may be a percentage, and expand formats.
1054  */
1055 long long
args_string_percentage_and_expand(const char * value,long long minval,long long maxval,long long curval,struct cmdq_item * item,char ** cause)1056 args_string_percentage_and_expand(const char *value, long long minval,
1057     long long maxval, long long curval, struct cmdq_item *item, char **cause)
1058 {
1059           const char          *errstr;
1060           long long  ll;
1061           size_t               valuelen = strlen(value);
1062           char                *copy, *f;
1063 
1064           if (value[valuelen - 1] == '%') {
1065                     copy = xstrdup(value);
1066                     copy[valuelen - 1] = '\0';
1067 
1068                     f = format_single_from_target(item, copy);
1069                     ll = strtonum(f, 0, 100, &errstr);
1070                     free(f);
1071                     free(copy);
1072                     if (errstr != NULL) {
1073                               *cause = xstrdup(errstr);
1074                               return (0);
1075                     }
1076                     ll = (curval * ll) / 100;
1077                     if (ll < minval) {
1078                               *cause = xstrdup("too small");
1079                               return (0);
1080                     }
1081                     if (ll > maxval) {
1082                               *cause = xstrdup("too large");
1083                               return (0);
1084                     }
1085           } else {
1086                     f = format_single_from_target(item, value);
1087                     ll = strtonum(f, minval, maxval, &errstr);
1088                     free(f);
1089                     if (errstr != NULL) {
1090                               *cause = xstrdup(errstr);
1091                               return (0);
1092                     }
1093           }
1094 
1095           *cause = NULL;
1096           return (ll);
1097 }
1098