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