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