comparison lib/args.c @ 261:ae693c7bf2f7

Add enable/disable/exclude logic, update docs.
author Rob Landley <rob@landley.net>
date Mon, 18 Feb 2008 03:32:17 -0600
parents 3fe66e630944
children 56a29fb3c9f6
comparison
equal deleted inserted replaced
260:10d6a8148339 261:ae693c7bf2f7
14 // Note that pointer and long are always the same size, even on 64 bit. 14 // Note that pointer and long are always the same size, even on 64 bit.
15 // : plus a string argument, keep most recent if more than one 15 // : plus a string argument, keep most recent if more than one
16 // * plus a string argument, appended to a list 16 // * plus a string argument, appended to a list
17 // # plus a signed long argument (TODO: Bounds checking?) 17 // # plus a signed long argument (TODO: Bounds checking?)
18 // @ plus an occurrence counter (which is a long) 18 // @ plus an occurrence counter (which is a long)
19 // (longopt)
19 // | this is required. If more than one marked, only one required. 20 // | this is required. If more than one marked, only one required.
20 // (longopt) 21 //
21 // +X enabling this enables X (switch on) 22 // These modify other option letters (previously seen in string):
22 // ~X enabling this disables X (switch off) 23 // +X enabling this enables X (switch on)
23 // x~x means toggle x, I.E. specifying it again switches it off. 24 // ~X enabling this disables X (switch off)
24 // !X die with error if X already set (x!x die if x supplied twice) 25 // !X die with error if X already set (x!x die if x supplied twice)
25 // [yz] needs at least one of y or z. 26 // [yz] needs at least one of y or z.
26 // at the beginning: 27 // at the beginning:
27 // + stop at first nonoption argument 28 // + stop at first nonoption argument
28 // <0 at least # leftover arguments needed (default 0) 29 // <0 at least # leftover arguments needed (default 0)
29 // >9 at most # leftover arguments needed (default MAX_INT) 30 // >9 at most # leftover arguments needed (default MAX_INT)
30 // ? don't show_usage() on unknown argument. 31 // ? don't show_usage() on unknown argument.
35 // - and -- cannot be arguments. 36 // - and -- cannot be arguments.
36 // -- force end of arguments 37 // -- force end of arguments
37 // - is a synonym for stdin in file arguments 38 // - is a synonym for stdin in file arguments
38 // -abc means -a -b -c 39 // -abc means -a -b -c
39 40
40 /* This uses a getopt-like option string, but not getopt() itself. 41 /* This uses a getopt-like option string, but not getopt() itself. We call
41 * 42 * it the get_opt string.
42 * Each option in options corresponds to a bit position in the return 43 *
43 * value (last argument is (1<<0), the next to last is (1<<1) and so on). 44 * Each option in the get_opt string corresponds to a bit position in the
44 * If the option isn't seen in argv[] its bit is 0. 45 * return value. The rightmost argument is (1<<0), the next to last is (1<<1)
46 * and so on. If the option isn't seen in argv[], its bit remains 0.
45 * 47 *
46 * Options which have an argument fill in the corresponding slot in the global 48 * Options which have an argument fill in the corresponding slot in the global
47 * toys.command struct, which it treats as an array of longs (note that 49 * union "this" (see generated/globals.h), which it treats as an array of longs
48 * sizeof(long)==sizeof(pointer) is guaranteed by LP64). 50 * (note that sizeof(long)==sizeof(pointer) is guaranteed by LP64).
49 * 51 *
50 * You don't have to free the option strings, which point into the environment 52 * You don't have to free the option strings, which point into the environment
51 * space. List list objects should be freed by main() when command_main() 53 * space. List objects should be freed by main() when command_main() returns.
52 * returns.
53 * 54 *
54 * Example: 55 * Example:
55 * get_optflags() when toys.which->options="ab:c:d" 56 * Calling get_optflags() when toys.which->options="ab:c:d" and
56 * argv = ["command", "-b", "fruit", "-d"] 57 * argv = ["command", "-b", "fruit", "-d", "walrus"] results in:
57 * flags = 5, toy[0]=NULL, toy[1]="fruit"; 58 *
59 * Changes to struct toys:
60 * toys.optflags = 5 (-b=4 | -d=1)
61 * toys.optargs[0]="walrus" (leftover argument)
62 * toys.optargs[1]=NULL (end of list)
63 * toys.optc=1 (there was 1 leftover argument)
64 *
65 * Changes to union this:
66 * this[0]=NULL (because -c didn't get an argument this time)
67 * this[1]="fruit" (argument to -b)
58 */ 68 */
59 69
60 // 70 // Linked list of all known options (get_opt string is parsed into this).
61 struct opts { 71 struct opts {
62 struct opts *next; 72 struct opts *next;
63 char c; 73 uint32_t edx[3]; // Flag mask to enable/disable/exclude.
64 int type; 74 long *arg; // Pointer into union this to store arguments at.
65 int shift; 75 int c; // Short argument character
66 long *arg; 76 char type; // Type of arguments to store
67 }; 77 };
68 78
69 static struct getoptflagstate 79 // State during argument parsing.
80 struct getoptflagstate
70 { 81 {
71 int argc; 82 int argc;
72 char *arg; 83 char *arg;
73 struct opts *opts, *this; 84 struct opts *opts, *this;
74 int noerror, nodash_now; 85 int noerror, nodash_now;
75 } gof; 86 uint32_t excludes;
76 87 };
77 static void gotflag(void) 88
89 // Parse one command line option.
90
91 static void gotflag(struct getoptflagstate *gof)
78 { 92 {
79 int type; 93 int type;
94 struct opts *opt = gof->this;
80 95
81 // Did we recognize this option? 96 // Did we recognize this option?
82 if (!gof.this && !gof.noerror) error_exit("Unknown option %s", gof.arg); 97 if (!opt) {
83 else toys.optflags |= 1 << gof.this->shift; 98 if (gof->noerror) return;
99 error_exit("Unknown option %s", gof->arg);
100 }
101 toys.optflags |= opt->edx[0];
102 toys.optflags &= ~opt->edx[1];
103 gof->excludes = opt->edx[2];
84 104
85 // Does this option take an argument? 105 // Does this option take an argument?
86 gof.arg++; 106 gof->arg++;
87 type = gof.this->type & 255; 107 type = opt->type;
88 if (type) { 108 if (type) {
89 109
90 // Handle "-xblah" and "-x blah", but also a third case: "abxc blah" 110 // Handle "-xblah" and "-x blah", but also a third case: "abxc blah"
91 // to make "tar xCjfv blah1 blah2 thingy" work like 111 // to make "tar xCjfv blah1 blah2 thingy" work like
92 // "tar -x -C blah1 -j -f blah2 -v thingy" 112 // "tar -x -C blah1 -j -f blah2 -v thingy"
93 if (!gof.nodash_now && !*gof.arg) { 113 if (!gof->nodash_now && !gof->arg[0]) {
94 gof.arg = toys.argv[++gof.argc]; 114 gof->arg = toys.argv[++gof->argc];
95 if (!gof.arg) error_exit("Missing argument"); 115 // TODO: The following line doesn't display --longopt correctly
116 if (!gof->arg) error_exit("Missing argument to -%c",opt->c);
96 } 117 }
97 118
98 // Grab argument. 119 // Grab argument.
99 if (!gof.arg && !(gof.arg = toys.argv[++gof.argc])) 120 if (!gof->arg && !(gof->arg = toys.argv[++(gof->argc)]))
100 error_exit("Missing argument"); 121 error_exit("Missing argument");
101 if (type == ':') *(gof.this->arg) = (long)gof.arg; 122 if (type == ':') *(opt->arg) = (long)gof->arg;
102 else if (type == '*') { 123 else if (type == '*') {
103 struct arg_list *temp, **list; 124 struct arg_list *temp, **list;
104 list = (struct arg_list **)gof.this->arg; 125 list = (struct arg_list **)opt->arg;
105 temp = xmalloc(sizeof(struct arg_list)); 126 temp = xmalloc(sizeof(struct arg_list));
106 temp->arg = gof.arg; 127 temp->arg = gof->arg;
107 temp->next = *list; 128 temp->next = *list;
108 *list = temp; 129 *list = temp;
109 } else if (type == '#') *(gof.this->arg) = atolx((char *)gof.arg); 130 } else if (type == '#') *(opt->arg) = atolx((char *)gof->arg);
110 else if (type == '@') { 131 else if (type == '@') {
111 } 132 }
112 133
113 gof.arg = ""; 134 gof->arg = "";
114 } 135 }
115 136
116 gof.this = NULL; 137 gof->this = NULL;
117 } 138 }
118 139
119 // Fill out toys.optflags and toys.optargs. This isn't reentrant because 140 // Fill out toys.optflags and toys.optargs.
120 // we don't bzero(&gof, sizeof(gof)); 141
121 142 static char *plustildenot = "+~!";
122 void get_optflags(void) 143 void get_optflags(void)
123 { 144 {
124 int stopearly = 0, nodash = 0, minargs = 0, maxargs; 145 int stopearly = 0, nodash = 0, minargs = 0, maxargs;
125 struct longopts { 146 struct longopts {
126 struct longopts *next; 147 struct longopts *next;
127 struct opts *opt; 148 struct opts *opt;
128 char *str; 149 char *str;
129 int len; 150 int len;
130 } *longopts = NULL; 151 } *longopts = NULL;
152 struct getoptflagstate gof;
131 long *nextarg = (long *)&this; 153 long *nextarg = (long *)&this;
132 char *options = toys.which->options; 154 char *options = toys.which->options;
133 155
134 if (CFG_HELP) toys.exithelp++; 156 if (CFG_HELP) toys.exithelp++;
135 // Allocate memory for optargs 157 // Allocate memory for optargs
136 maxargs = 0; 158 maxargs = 0;
137 while (toys.argv[maxargs++]); 159 while (toys.argv[maxargs++]);
138 toys.optargs = xzalloc(sizeof(char *)*maxargs); 160 toys.optargs = xzalloc(sizeof(char *)*maxargs);
139 maxargs = INT_MAX; 161 maxargs = INT_MAX;
162 bzero(&gof, sizeof(struct getoptflagstate));
140 163
141 // Parse option format 164 // Parse option format
142 if (options) { 165 if (options) {
166
143 // Parse leading special behavior indicators 167 // Parse leading special behavior indicators
144 for (;;) { 168 for (;;) {
145 if (*options == '+') stopearly++; 169 if (*options == '+') stopearly++;
146 else if (*options == '<') minargs=*(++options)-'0'; 170 else if (*options == '<') minargs=*(++options)-'0';
147 else if (*options == '>') maxargs=*(++options)-'0'; 171 else if (*options == '>') maxargs=*(++options)-'0';
151 options++; 175 options++;
152 } 176 }
153 177
154 // Parse rest of opts into array 178 // Parse rest of opts into array
155 while (*options) { 179 while (*options) {
180 char *temp = "+~!";
156 181
157 // Allocate a new option entry when necessary 182 // Allocate a new option entry when necessary
158 if (!gof.this) { 183 if (!gof.this) {
159 gof.this = xzalloc(sizeof(struct opts)); 184 gof.this = xzalloc(sizeof(struct opts));
160 gof.this->next = gof.opts; 185 gof.this->next = gof.opts;
161 gof.opts = gof.this; 186 gof.opts = gof.this;
187 ++*(gof.this->edx);
162 } 188 }
163 // Each option must start with (or an option character. (Bare 189 // Each option must start with "(" or an option character. (Bare
164 // longopts only come at the start of the string.) 190 // longopts only come at the start of the string.)
165 if (*options == '(') { 191 if (*options == '(') {
166 char *end; 192 char *end;
167 struct longopts *lo = xmalloc(sizeof(struct longopts)); 193 struct longopts *lo = xmalloc(sizeof(struct longopts));
168 194
169 // Find the end of the longopt 195 // Find the end of the longopt
170 for (end = ++options; *end && *end != ')'; end++); 196 for (end = ++options; *end && *end != ')'; end++);
171 if (CFG_TOYBOX_DEBUG && !*end) 197 if (CFG_TOYBOX_DEBUG && !*end)
172 error_exit("Unterminated optstring"); 198 error_exit("Bug1 in get_opt");
173 199
174 // Allocate and init a new struct longopts 200 // Allocate and init a new struct longopts
175 lo = xmalloc(sizeof(struct longopts)); 201 lo = xmalloc(sizeof(struct longopts));
176 lo->next = longopts; 202 lo->next = longopts;
177 lo->opt = gof.this; 203 lo->opt = gof.this;
178 lo->str = options; 204 lo->str = options;
179 lo->len = end-options; 205 lo->len = end-options;
180 longopts = lo; 206 longopts = lo;
181 options = end; 207 options = end;
182 208
183 // For leading longopts (with no corresponding short opt), note 209 // Mark this as struct opt as used, even when no short opt.
184 // that this option struct has been used. 210 if (!gof.this->c) gof.this->c = -1;
185 gof.this->shift++;
186 211
187 // If this is the start of a new option that wasn't a longopt, 212 // If this is the start of a new option that wasn't a longopt,
188 213
189 } else if (index(":*#@", *options)) { 214 } else if (strchr(":*#@", *options)) {
190 gof.this->type |= *options; 215 gof.this->type = *options;
216 } else if (0 != (temp = strchr(plustildenot, *options))) {
217 int i=0, idx = temp - plustildenot;
218 struct opts *opt;
219
220 if (CFG_TOYBOX_DEBUG && !*++options)
221 error_exit("Bug2 in get_opt");
222 // Find this option flag (in previously parsed struct opt)
223 for (opt = gof.this; ; opt = opt->next) {
224 if (CFG_TOYBOX_DEBUG && !opt) error_exit("Bug3 in get_opt");
225 if (opt->c == *options) break;
226 i++;
227 }
228 gof.this->edx[idx] |= 1<<i;
229
230 } else if (*options == '[') {
191 } else if (*options == '|') { 231 } else if (*options == '|') {
192 } else if (*options == '+') {
193 } else if (*options == '~') {
194 } else if (*options == '!') {
195 } else if (*options == '[') {
196 232
197 // At this point, we've hit the end of the previous option. The 233 // At this point, we've hit the end of the previous option. The
198 // current character is the start of a new option. If we've already 234 // current character is the start of a new option. If we've already
199 // assigned an option to this struct, loop to allocate a new one. 235 // assigned an option to this struct, loop to allocate a new one.
200 // (It'll get back here afterwards.) 236 // (It'll get back here afterwards and fall through to next else.)
201 } else if(gof.this->shift || gof.this->c) { 237 } else if(gof.this->c) {
202 gof.this = NULL; 238 gof.this = NULL;
203 continue; 239 continue;
204 240
205 // Claim this option, loop to see what's after it. 241 // Claim this option, loop to see what's after it.
206 } else gof.this->c = *options; 242 } else gof.this->c = *options;
207 243
208 options++; 244 options++;
209 } 245 }
210 } 246 }
211 247
212 // Initialize shift bits and pointers to store arguments. (We have to 248 // Initialize enable/disable/exclude masks and pointers to store arguments.
213 // calculate this ahead of time because longopts jump into the middle of 249 // (We have to calculate all this ahead of time because longopts jump into
214 // the list.) 250 // the middle of the list.)
215 gof.argc = 0; 251 gof.argc = 0;
216 for (gof.this = gof.opts; gof.this; gof.this = gof.this->next) { 252 for (gof.this = gof.opts; gof.this; gof.this = gof.this->next) {
217 gof.this->shift = gof.argc++; 253 int i;
218 if (gof.this->type & 255) { 254
255 for (i=0;i<3;i++) gof.this->edx[i] <<= gof.argc;
256 gof.argc++;
257 if (gof.this->type) {
219 gof.this->arg = (void *)nextarg; 258 gof.this->arg = (void *)nextarg;
220 *(nextarg++) = 0; 259 *(nextarg++) = 0;
221 } 260 }
222 } 261 }
223 262
249 // Handle --longopt 288 // Handle --longopt
250 289
251 for (lo = longopts; lo; lo = lo->next) { 290 for (lo = longopts; lo; lo = lo->next) {
252 if (!strncmp(gof.arg, lo->str, lo->len)) { 291 if (!strncmp(gof.arg, lo->str, lo->len)) {
253 if (gof.arg[lo->len]) { 292 if (gof.arg[lo->len]) {
254 if (gof.arg[lo->len]=='=' 293 if (gof.arg[lo->len]=='=' && lo->opt->type)
255 && (lo->opt->type & 255))
256 {
257 gof.arg += lo->len; 294 gof.arg += lo->len;
258 } else continue; 295 else continue;
259 } 296 }
260 // It's a match. 297 // It's a match.
261 gof.arg = ""; 298 gof.arg = "";
262 gof.this = lo->opt; 299 gof.this = lo->opt;
263 break; 300 break;
264 } 301 }
265 } 302 }
266 303
267 // Long option parsed, handle option. 304 // Long option parsed, handle option.
268 gotflag(); 305 gotflag(&gof);
269 continue; 306 continue;
270 } 307 }
271 308
272 // Handle things that don't start with a dash. 309 // Handle things that don't start with a dash.
273 } else { 310 } else {
282 // Identify next option char. 319 // Identify next option char.
283 for (gof.this = gof.opts; gof.this; gof.this = gof.this->next) 320 for (gof.this = gof.opts; gof.this; gof.this = gof.this->next)
284 if (*gof.arg == gof.this->c) break; 321 if (*gof.arg == gof.this->c) break;
285 322
286 // Handle option char (advancing past what was used) 323 // Handle option char (advancing past what was used)
287 gotflag(); 324 gotflag(&gof);
288 } 325 }
289 continue; 326 continue;
290 327
291 // Not a flag, save value in toys.optargs[] 328 // Not a flag, save value in toys.optargs[]
292 notflag: 329 notflag:
299 error_exit("Need %d argument%s", minargs, minargs ? "s" : ""); 336 error_exit("Need %d argument%s", minargs, minargs ? "s" : "");
300 if (toys.optc>maxargs) 337 if (toys.optc>maxargs)
301 error_exit("Max %d argument%s", maxargs, maxargs ? "s" : ""); 338 error_exit("Max %d argument%s", maxargs, maxargs ? "s" : "");
302 if (CFG_HELP) toys.exithelp = 0; 339 if (CFG_HELP) toys.exithelp = 0;
303 } 340 }
304
305 // Loop through files listed on the command line
306
307 static int dofileargs(char ***files, int fd, int iswrite)
308 {
309 char *filename = *((*files)++);
310 static int flags[] = {O_RDONLY, O_CREAT|O_TRUNC, O_RDWR};
311
312 if (fd != -1) close(fd);
313
314 for (;;) {
315
316 // Are there no more files?
317 if (!*filename)
318 return (fd == -1) ? iswrite : -1;
319
320 // A filename of "-" means stdin.
321 if (*filename == '-' && !filename[1]) return 0;
322
323 fd = xcreate(filename, flags[iswrite], 0777);
324 }
325 }
326
327 int readfileargs(char ***files, int fd)
328 {
329 return dofileargs(files, fd, 0);
330 }
331
332 int writefileargs(char ***files, int fd)
333 {
334 return dofileargs(files, fd, 1);
335 }