Commit | Line | Data |
---|---|---|
f1525c23 WH |
1 | /**************************************************************** |
2 | Copyright 1990 by AT&T Bell Laboratories and Bellcore. | |
3 | ||
4 | Permission to use, copy, modify, and distribute this software | |
5 | and its documentation for any purpose and without fee is hereby | |
6 | granted, provided that the above copyright notice appear in all | |
7 | copies and that both that the copyright notice and this | |
8 | permission notice and warranty disclaimer appear in supporting | |
9 | documentation, and that the names of AT&T Bell Laboratories or | |
10 | Bellcore or any of their entities not be used in advertising or | |
11 | publicity pertaining to distribution of the software without | |
12 | specific, written prior permission. | |
13 | ||
14 | AT&T and Bellcore disclaim all warranties with regard to this | |
15 | software, including all implied warranties of merchantability | |
16 | and fitness. In no event shall AT&T or Bellcore be liable for | |
17 | any special, indirect or consequential damages or any damages | |
18 | whatsoever resulting from loss of use, data or profits, whether | |
19 | in an action of contract, negligence or other tortious action, | |
20 | arising out of or in connection with the use or performance of | |
21 | this software. | |
22 | ****************************************************************/ | |
23 | ||
24 | /* parse_args | |
25 | ||
26 | This function will parse command line input into appropriate data | |
27 | structures, output error messages when appropriate and provide some | |
28 | minimal type conversion. | |
29 | ||
30 | Input to the function consists of the standard argc,argv | |
31 | values, and a table which directs the parser. Each table entry has the | |
32 | following components: | |
33 | ||
34 | prefix -- the (optional) switch character string, e.g. "-" "/" "=" | |
35 | switch -- the command string, e.g. "o" "data" "file" "F" | |
36 | flags -- control flags, e.g. CASE_INSENSITIVE, REQUIRED_PREFIX | |
37 | arg_count -- number of arguments this command requires, e.g. 0 for | |
38 | booleans, 1 for filenames, INFINITY for input files | |
39 | result_type -- how to interpret the switch arguments, e.g. STRING, | |
40 | CHAR, FILE, OLD_FILE, NEW_FILE | |
41 | result_ptr -- pointer to storage for the result, be it a table or | |
42 | a string or whatever | |
43 | table_size -- if the arguments fill a table, the maximum number of | |
44 | entries; if there are no arguments, the value to | |
45 | load into the result storage | |
46 | ||
47 | Although the table can be used to hold a list of filenames, only | |
48 | scalar values (e.g. pointers) can be stored in the table. No vector | |
49 | processing will be done, only pointers to string storage will be moved. | |
50 | ||
51 | An example entry, which could be used to parse input filenames, is: | |
52 | ||
53 | "-", "o", 0, oo, OLD_FILE, infilenames, INFILE_TABLE_SIZE | |
54 | ||
55 | */ | |
56 | ||
57 | #include <stdio.h> | |
58 | #ifndef NULL | |
59 | /* ANSI C */ | |
60 | #include <stddef.h> | |
61 | #endif | |
62 | #include "parse.h" | |
63 | #include <math.h> /* For atof */ | |
64 | #include <ctype.h> | |
65 | ||
66 | #define MAX_INPUT_SIZE 1000 | |
67 | ||
68 | #define arg_prefix(x) ((x).prefix) | |
69 | #define arg_string(x) ((x).string) | |
70 | #define arg_flags(x) ((x).flags) | |
71 | #define arg_count(x) ((x).count) | |
72 | #define arg_result_type(x) ((x).result_type) | |
73 | #define arg_result_ptr(x) ((x).result_ptr) | |
74 | #define arg_table_size(x) ((x).table_size) | |
75 | ||
76 | #ifndef TRUE | |
77 | #define TRUE 1 | |
78 | #endif | |
79 | #ifndef FALSE | |
80 | #define FALSE 0 | |
81 | #endif | |
82 | typedef int boolean; | |
83 | ||
84 | ||
85 | char *lower_string (/* char [], char * */); | |
86 | ||
87 | static char *this_program = ""; | |
88 | ||
89 | #ifndef atol | |
90 | extern long atol(); | |
91 | #endif | |
92 | static int arg_parse (/* char *, arg_info * */); | |
93 | ||
94 | ||
95 | boolean parse_args (argc, argv, table, entries, others, other_count) | |
96 | int argc; | |
97 | char *argv[]; | |
98 | arg_info table[]; | |
99 | int entries; | |
100 | char *others[]; | |
101 | int other_count; | |
102 | { | |
103 | boolean arg_verify (/* argv, table, entries */); | |
104 | void init_store (/* table, entries */); | |
105 | ||
106 | boolean result; | |
107 | ||
108 | if (argv) | |
109 | this_program = argv[0]; | |
110 | ||
111 | /* Check the validity of the table and its parameters */ | |
112 | ||
113 | result = arg_verify (argv, table, entries); | |
114 | ||
115 | /* Initialize the storage values */ | |
116 | ||
117 | init_store (table, entries); | |
118 | ||
119 | if (result) { | |
120 | boolean use_prefix = TRUE; | |
121 | char *argv0; | |
122 | ||
123 | argc--; | |
124 | argv0 = *++argv; | |
125 | while (argc) { | |
126 | int index, length; | |
127 | ||
128 | index = match_table (*argv, table, entries, use_prefix, &length); | |
129 | if (index < 0) { | |
130 | ||
131 | /* The argument doesn't match anything in the table */ | |
132 | ||
133 | if (others) { | |
134 | ||
135 | if (*argv > argv0) | |
136 | *--*argv = '-'; /* complain at invalid flag */ | |
137 | ||
138 | if (other_count > 0) { | |
139 | *others++ = *argv; | |
140 | other_count--; | |
141 | } else { | |
142 | fprintf (stderr, "%s: too many parameters: ", | |
143 | this_program); | |
144 | fprintf (stderr, "'%s' ignored\n", *argv); | |
145 | } /* else */ | |
146 | } /* if (others) */ | |
147 | argv0 = *++argv; | |
148 | argc--; | |
149 | } else { | |
150 | ||
151 | /* A match was found */ | |
152 | ||
153 | if (length >= strlen (*argv)) { | |
154 | argc--; | |
155 | argv0 = *++argv; | |
156 | use_prefix = TRUE; | |
157 | } else { | |
158 | (*argv) += length; | |
159 | use_prefix = FALSE; | |
160 | } /* else */ | |
161 | ||
162 | /* Parse any necessary arguments */ | |
163 | ||
164 | if (arg_count (table[index]) != P_NO_ARGS) { | |
165 | ||
166 | /* Now length will be used to store the number of parsed characters */ | |
167 | ||
168 | length = arg_parse(*argv, &table[index]); | |
169 | if (*argv == NULL) | |
170 | argc = 0; | |
171 | else if (length >= strlen (*argv)) { | |
172 | argc--; | |
173 | argv0 = *++argv; | |
174 | use_prefix = TRUE; | |
175 | } else { | |
176 | (*argv) += length; | |
177 | use_prefix = FALSE; | |
178 | } /* else */ | |
179 | } /* if (argv_count != P_NO_ARGS) */ | |
180 | else | |
181 | *arg_result_ptr(table[index]) = | |
182 | arg_table_size(table[index]); | |
183 | } /* else */ | |
184 | } /* while (argc) */ | |
185 | } /* if (result) */ | |
186 | ||
187 | return result; | |
188 | } /* parse_args */ | |
189 | ||
190 | ||
191 | boolean arg_verify (argv, table, entries) | |
192 | char *argv[]; | |
193 | arg_info table[]; | |
194 | int entries; | |
195 | { | |
196 | int i; | |
197 | char *this_program = ""; | |
198 | ||
199 | if (argv) | |
200 | this_program = argv[0]; | |
201 | ||
202 | for (i = 0; i < entries; i++) { | |
203 | arg_info *arg = &table[i]; | |
204 | ||
205 | /* Check the argument flags */ | |
206 | ||
207 | if (arg_flags (*arg) & ~(P_CASE_INSENSITIVE | P_REQUIRED_PREFIX)) { | |
208 | fprintf (stderr, "%s [arg_verify]: too many ", this_program); | |
209 | fprintf (stderr, "flags in entry %d: '%x' (hex)\n", i, | |
210 | arg_flags (*arg)); | |
211 | } /* if */ | |
212 | ||
213 | /* Check the argument count */ | |
214 | ||
215 | { int count = arg_count (*arg); | |
216 | ||
217 | if (count != P_NO_ARGS && count != P_ONE_ARG && count != | |
218 | P_INFINITE_ARGS) { | |
219 | fprintf (stderr, "%s [arg_verify]: invalid ", this_program); | |
220 | fprintf (stderr, "argument count in entry %d: '%d'\n", i, | |
221 | count); | |
222 | } /* if count != P_NO_ARGS ... */ | |
223 | ||
224 | /* Check the result field; want to be able to store results */ | |
225 | ||
226 | else | |
227 | if (arg_result_ptr (*arg) == (int *) NULL) { | |
228 | fprintf (stderr, "%s [arg_verify]: ", this_program); | |
229 | fprintf (stderr, "no argument storage given for "); | |
230 | fprintf (stderr, "entry %d\n", i); | |
231 | } /* if arg_result_ptr */ | |
232 | } | |
233 | ||
234 | /* Check the argument type */ | |
235 | ||
236 | { int type = arg_result_type (*arg); | |
237 | ||
238 | if (type < P_STRING || type > P_DOUBLE) | |
239 | fprintf(stderr, | |
240 | "%s [arg_verify]: bad arg type in entry %d: '%d'\n", | |
241 | this_program, i, type); | |
242 | } | |
243 | ||
244 | /* Check table size */ | |
245 | ||
246 | { int size = arg_table_size (*arg); | |
247 | ||
248 | if (arg_count (*arg) == P_INFINITE_ARGS && size < 1) { | |
249 | fprintf (stderr, "%s [arg_verify]: bad ", this_program); | |
250 | fprintf (stderr, "table size in entry %d: '%d'\n", i, | |
251 | size); | |
252 | } /* if (arg_count == P_INFINITE_ARGS && size < 1) */ | |
253 | } | |
254 | ||
255 | } /* for i = 0 */ | |
256 | ||
257 | return TRUE; | |
258 | } /* arg_verify */ | |
259 | ||
260 | ||
261 | /* match_table -- returns the index of the best entry matching the input, | |
262 | -1 if no match. The best match is the one of longest length which | |
263 | appears lowest in the table. The length of the match will be returned | |
264 | in length ONLY IF a match was found. */ | |
265 | ||
266 | int match_table (norm_input, table, entries, use_prefix, length) | |
267 | register char *norm_input; | |
268 | arg_info table[]; | |
269 | int entries; | |
270 | boolean use_prefix; | |
271 | int *length; | |
272 | { | |
273 | extern int match (/* char *, char *, arg_info *, boolean */); | |
274 | ||
275 | char low_input[MAX_INPUT_SIZE]; | |
276 | register int i; | |
277 | int best_index = -1, best_length = 0; | |
278 | ||
279 | /* FUNCTION BODY */ | |
280 | ||
281 | (void) lower_string (low_input, norm_input); | |
282 | ||
283 | for (i = 0; i < entries; i++) { | |
284 | int this_length = match (norm_input, low_input, &table[i], use_prefix); | |
285 | ||
286 | if (this_length > best_length) { | |
287 | best_index = i; | |
288 | best_length = this_length; | |
289 | } /* if (this_length > best_length) */ | |
290 | } /* for (i = 0) */ | |
291 | ||
292 | if (best_index > -1 && length != (int *) NULL) | |
293 | *length = best_length; | |
294 | ||
295 | return best_index; | |
296 | } /* match_table */ | |
297 | ||
298 | ||
299 | /* match -- takes an input string and table entry, and returns the length | |
300 | of the longer match. | |
301 | ||
302 | 0 ==> input doesn't match | |
303 | ||
304 | For example: | |
305 | ||
306 | INPUT PREFIX STRING RESULT | |
307 | ---------------------------------------------------------------------- | |
308 | "abcd" "-" "d" 0 | |
309 | "-d" "-" "d" 2 (i.e. "-d") | |
310 | "dout" "-" "d" 1 (i.e. "d") | |
311 | "-d" "" "-d" 2 (i.e. "-d") | |
312 | "dd" "d" "d" 2 <= here's the weird one | |
313 | */ | |
314 | ||
315 | int match (norm_input, low_input, entry, use_prefix) | |
316 | char *norm_input, *low_input; | |
317 | arg_info *entry; | |
318 | boolean use_prefix; | |
319 | { | |
320 | char *norm_prefix = arg_prefix (*entry); | |
321 | char *norm_string = arg_string (*entry); | |
322 | boolean prefix_match = FALSE, string_match = FALSE; | |
323 | int result = 0; | |
324 | ||
325 | /* Buffers for the lowercased versions of the strings being compared. | |
326 | These are used when the switch is to be case insensitive */ | |
327 | ||
328 | static char low_prefix[MAX_INPUT_SIZE]; | |
329 | static char low_string[MAX_INPUT_SIZE]; | |
330 | int prefix_length = strlen (norm_prefix); | |
331 | int string_length = strlen (norm_string); | |
332 | ||
333 | /* Pointers for the required strings (lowered or nonlowered) */ | |
334 | ||
335 | register char *input, *prefix, *string; | |
336 | ||
337 | /* FUNCTION BODY */ | |
338 | ||
339 | /* Use the appropriate strings to handle case sensitivity */ | |
340 | ||
341 | if (arg_flags (*entry) & P_CASE_INSENSITIVE) { | |
342 | input = low_input; | |
343 | prefix = lower_string (low_prefix, norm_prefix); | |
344 | string = lower_string (low_string, norm_string); | |
345 | } else { | |
346 | input = norm_input; | |
347 | prefix = norm_prefix; | |
348 | string = norm_string; | |
349 | } /* else */ | |
350 | ||
351 | /* First, check the string formed by concatenating the prefix onto the | |
352 | switch string, but only when the prefix is not being ignored */ | |
353 | ||
354 | if (use_prefix && prefix != NULL && *prefix != '\0') | |
355 | prefix_match = (strncmp (input, prefix, prefix_length) == 0) && | |
356 | (strncmp (input + prefix_length, string, string_length) == 0); | |
357 | ||
358 | /* Next, check just the switch string, if that's allowed */ | |
359 | ||
360 | if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0) | |
361 | string_match = strncmp (input, string, string_length) == 0; | |
362 | ||
363 | if (prefix_match) | |
364 | result = prefix_length + string_length; | |
365 | else if (string_match) | |
366 | result = string_length; | |
367 | ||
368 | return result; | |
369 | } /* match */ | |
370 | ||
371 | ||
372 | char *lower_string (dest, src) | |
373 | char *dest, *src; | |
374 | { | |
375 | char *result = dest; | |
376 | register int c; | |
377 | ||
378 | if (dest == NULL || src == NULL) | |
379 | result = NULL; | |
380 | else | |
381 | while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c); | |
382 | ||
383 | return result; | |
384 | } /* lower_string */ | |
385 | ||
386 | ||
387 | /* arg_parse -- returns the number of characters parsed for this entry */ | |
388 | ||
389 | static int arg_parse (str, entry) | |
390 | char *str; | |
391 | arg_info *entry; | |
392 | { | |
393 | int length = 0; | |
394 | ||
395 | if (arg_count (*entry) == P_ONE_ARG) { | |
396 | char **store = (char **) arg_result_ptr (*entry); | |
397 | ||
398 | length = put_one_arg (arg_result_type (*entry), str, store, | |
399 | arg_prefix (*entry), arg_string (*entry)); | |
400 | ||
401 | } /* if (arg_count == P_ONE_ARG) */ | |
402 | else { /* Must be a table of arguments */ | |
403 | char **store = (char **) arg_result_ptr (*entry); | |
404 | ||
405 | if (store) { | |
406 | while (*store) | |
407 | store++; | |
408 | ||
409 | length = put_one_arg (arg_result_type (*entry), str, store++, | |
410 | arg_prefix (*entry), arg_string (*entry)); | |
411 | ||
412 | *store = (char *) NULL; | |
413 | } /* if (store) */ | |
414 | } /* else */ | |
415 | ||
416 | return length; | |
417 | } /* arg_parse */ | |
418 | ||
419 | ||
420 | int put_one_arg (type, str, store, prefix, string) | |
421 | int type; | |
422 | char *str; | |
423 | char **store; | |
424 | char *prefix, *string; | |
425 | { | |
426 | int length = 0; | |
427 | long L; | |
428 | ||
429 | if (store) { | |
430 | switch (type) { | |
431 | case P_STRING: | |
432 | case P_FILE: | |
433 | case P_OLD_FILE: | |
434 | case P_NEW_FILE: | |
435 | *store = str; | |
436 | if (str == NULL) | |
437 | fprintf (stderr, "%s: Missing argument after '%s%s'\n", | |
438 | this_program, prefix, string); | |
439 | length = str ? strlen (str) : 0; | |
440 | break; | |
441 | case P_CHAR: | |
442 | *((char *) store) = *str; | |
443 | length = 1; | |
444 | break; | |
445 | case P_SHORT: | |
446 | L = atol(str); | |
447 | *(short *)store = (short) L; | |
448 | if (L != *(short *)store) | |
449 | fprintf(stderr, | |
450 | "%s%s parameter '%ld' is not a SHORT INT (truncating to %d)\n", | |
451 | prefix, string, L, *(short *)store); | |
452 | length = strlen (str); | |
453 | break; | |
454 | case P_INT: | |
455 | L = atol(str); | |
456 | *(int *)store = (int)L; | |
457 | if (L != *(int *)store) | |
458 | fprintf(stderr, | |
459 | "%s%s parameter '%ld' is not an INT (truncating to %d)\n", | |
460 | prefix, string, L, *(int *)store); | |
461 | length = strlen (str); | |
462 | break; | |
463 | case P_LONG: | |
464 | *(long *)store = atol(str); | |
465 | length = strlen (str); | |
466 | break; | |
467 | case P_FLOAT: | |
468 | *((float *) store) = (float) atof (str); | |
469 | length = strlen (str); | |
470 | break; | |
471 | case P_DOUBLE: | |
472 | *((double *) store) = (double) atof (str); | |
473 | length = strlen (str); | |
474 | break; | |
475 | default: | |
476 | fprintf (stderr, "put_one_arg: bad type '%d'\n", | |
477 | type); | |
478 | break; | |
479 | } /* switch */ | |
480 | } /* if (store) */ | |
481 | ||
482 | return length; | |
483 | } /* put_one_arg */ | |
484 | ||
485 | ||
486 | void init_store (table, entries) | |
487 | arg_info *table; | |
488 | int entries; | |
489 | { | |
490 | int index; | |
491 | ||
492 | for (index = 0; index < entries; index++) | |
493 | if (arg_count (table[index]) == P_INFINITE_ARGS) { | |
494 | char **place = (char **) arg_result_ptr (table[index]); | |
495 | ||
496 | if (place) | |
497 | *place = (char *) NULL; | |
498 | } /* if arg_count == P_INFINITE_ARGS */ | |
499 | ||
500 | } /* init_store */ | |
501 |