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