1 /****************************************************************
2 Copyright 1990 by AT&T Bell Laboratories and Bellcore.
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.
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
22 ****************************************************************/
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.
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
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
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
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.
51 An example entry, which could be used to parse input filenames, is:
53 "-", "o", 0, oo, OLD_FILE, infilenames, INFILE_TABLE_SIZE
63 #include <math.h> /* For atof */
66 #define MAX_INPUT_SIZE 1000
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)
85 char *lower_string (/* char [], char * */);
87 static char *this_program = "";
90 static int arg_parse (/* char *, arg_info * */);
93 boolean parse_args (argc, argv, table, entries, others, other_count)
101 boolean arg_verify (/* argv, table, entries */);
102 void init_store (/* table, entries */);
107 this_program = argv[0];
109 /* Check the validity of the table and its parameters */
111 result = arg_verify (argv, table, entries);
113 /* Initialize the storage values */
115 init_store (table, entries);
118 boolean use_prefix = TRUE;
125 index = match_table (*argv, table, entries, use_prefix, &length);
128 /* The argument doesn't match anything in the table */
132 /* Might want to filter out those strings which appear after earlier
133 switches in the current word. That is, only treat argv as valid if
134 use_prefix is true. Right now, any extra chars will be passed on */
136 if (other_count > 0) {
140 fprintf (stderr, "%s: too many parameters: ",
142 fprintf (stderr, "'%s' ignored\n", *argv);
149 /* A match was found */
151 if (length >= strlen (*argv)) {
160 /* Parse any necessary arguments */
162 if (arg_count (table[index]) != P_NO_ARGS) {
164 /* Now length will be used to store the number of parsed characters */
166 length = arg_parse(*argv, &table[index]);
169 else if (length >= strlen (*argv)) {
177 } /* if (argv_count != P_NO_ARGS) */
179 *arg_result_ptr(table[index]) =
180 arg_table_size(table[index]);
189 boolean arg_verify (argv, table, entries)
195 char *this_program = "";
198 this_program = argv[0];
200 for (i = 0; i < entries; i++) {
201 arg_info *arg = &table[i];
203 /* Check the argument flags */
205 if (arg_flags (*arg) & ~(P_CASE_INSENSITIVE | P_REQUIRED_PREFIX)) {
206 fprintf (stderr, "%s [arg_verify]: too many ", this_program);
207 fprintf (stderr, "flags in entry %d: '%x' (hex)\n", i,
211 /* Check the argument count */
213 { int count = arg_count (*arg);
215 if (count != P_NO_ARGS && count != P_ONE_ARG && count !=
217 fprintf (stderr, "%s [arg_verify]: invalid ", this_program);
218 fprintf (stderr, "argument count in entry %d: '%d'\n", i,
220 } /* if count != P_NO_ARGS ... */
222 /* Check the result field; want to be able to store results */
225 if (arg_result_ptr (*arg) == (int *) NULL) {
226 fprintf (stderr, "%s [arg_verify]: ", this_program);
227 fprintf (stderr, "no argument storage given for ");
228 fprintf (stderr, "entry %d\n", i);
229 } /* if arg_result_ptr */
232 /* Check the argument type */
234 { int type = arg_result_type (*arg);
236 if (type != P_STRING &&
242 type != P_OLD_FILE &&
243 type != P_NEW_FILE &&
246 fprintf (stderr, "%s [arg_verify]: bad ", this_program);
247 fprintf (stderr, "arg type in entry %d: '%d'\n", i,
249 } /* if type != .... */
252 /* Check table size */
254 { int size = arg_table_size (*arg);
256 if (arg_count (*arg) == P_INFINITE_ARGS && size < 1) {
257 fprintf (stderr, "%s [arg_verify]: bad ", this_program);
258 fprintf (stderr, "table size in entry %d: '%d'\n", i,
260 } /* if (arg_count == P_INFINITE_ARGS && size < 1) */
269 /* match_table -- returns the index of the best entry matching the input,
270 -1 if no match. The best match is the one of longest length which
271 appears lowest in the table. The length of the match will be returned
272 in length ONLY IF a match was found. */
274 int match_table (norm_input, table, entries, use_prefix, length)
275 register char *norm_input;
281 extern int match (/* char *, char *, arg_info *, boolean */);
283 char low_input[MAX_INPUT_SIZE];
285 int best_index = -1, best_length = 0;
289 (void) lower_string (low_input, norm_input);
291 for (i = 0; i < entries; i++) {
292 int this_length = match (norm_input, low_input, &table[i], use_prefix);
294 if (this_length > best_length) {
296 best_length = this_length;
297 } /* if (this_length > best_length) */
300 if (best_index > -1 && length != (int *) NULL)
301 *length = best_length;
307 /* match -- takes an input string and table entry, and returns the length
310 0 ==> input doesn't match
314 INPUT PREFIX STRING RESULT
315 ----------------------------------------------------------------------
317 "-d" "-" "d" 2 (i.e. "-d")
318 "dout" "-" "d" 1 (i.e. "d")
319 "-d" "" "-d" 2 (i.e. "-d")
320 "dd" "d" "d" 2 <= here's the weird one
323 int match (norm_input, low_input, entry, use_prefix)
324 char *norm_input, *low_input;
328 char *norm_prefix = arg_prefix (*entry);
329 char *norm_string = arg_string (*entry);
330 boolean prefix_match = FALSE, string_match = FALSE;
333 /* Buffers for the lowercased versions of the strings being compared.
334 These are used when the switch is to be case insensitive */
336 static char low_prefix[MAX_INPUT_SIZE];
337 static char low_string[MAX_INPUT_SIZE];
338 int prefix_length = strlen (norm_prefix);
339 int string_length = strlen (norm_string);
341 /* Pointers for the required strings (lowered or nonlowered) */
343 register char *input, *prefix, *string;
347 /* Use the appropriate strings to handle case sensitivity */
349 if (arg_flags (*entry) & P_CASE_INSENSITIVE) {
351 prefix = lower_string (low_prefix, norm_prefix);
352 string = lower_string (low_string, norm_string);
355 prefix = norm_prefix;
356 string = norm_string;
359 /* First, check the string formed by concatenating the prefix onto the
360 switch string, but only when the prefix is not being ignored */
362 if (use_prefix && prefix != NULL && *prefix != '\0')
363 prefix_match = (strncmp (input, prefix, prefix_length) == 0) &&
364 (strncmp (input + prefix_length, string, string_length) == 0);
366 /* Next, check just the switch string, if that's allowed */
368 if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0)
369 string_match = strncmp (input, string, string_length) == 0;
372 result = prefix_length + string_length;
373 else if (string_match)
374 result = string_length;
380 char *lower_string (dest, src)
386 if (dest == NULL || src == NULL)
389 while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c);
395 /* arg_parse -- returns the number of characters parsed for this entry */
397 static int arg_parse (str, entry)
403 if (arg_count (*entry) == P_ONE_ARG) {
404 char **store = (char **) arg_result_ptr (*entry);
406 length = put_one_arg (arg_result_type (*entry), str, store,
407 arg_prefix (*entry), arg_string (*entry));
409 } /* if (arg_count == P_ONE_ARG) */
410 else { /* Must be a table of arguments */
411 char **store = (char **) arg_result_ptr (*entry);
417 length = put_one_arg (arg_result_type (*entry), str, store++,
418 arg_prefix (*entry), arg_string (*entry));
420 *store = (char *) NULL;
428 int put_one_arg (type, str, store, prefix, string)
432 char *prefix, *string;
444 fprintf (stderr, "%s: Missing argument after '%s%s'\n",
445 this_program, prefix, string);
446 length = str ? strlen (str) : 0;
449 *((char *) store) = *str;
453 long int i = atol(str);
455 *((short *) store) = (short) i;
456 if (i > 32767 || i < -32768) {
457 fprintf (stderr, "%s%s parameter '%ld' is not a ",
459 fprintf (stderr, "SHORT INT (truncating to %d)\n",
461 } /* if i > 32767 || i < -32768 */
463 /* This is pessimistic, and should change */
465 length = strlen (str);
468 *((int *) store) = atol(str);
470 /* This is pessimistic too */
472 length = strlen (str);
475 *((float *) store) = (float) atof (str);
476 length = strlen (str);
479 *((double *) store) = (double) atof (str);
480 length = strlen (str);
483 fprintf (stderr, "put_one_arg: bad type '%d'\n",
493 void init_store (table, entries)
499 for (index = 0; index < entries; index++)
500 if (arg_count (table[index]) == P_INFINITE_ARGS) {
501 char **place = (char **) arg_result_ptr (table[index]);
504 *place = (char *) NULL;
505 } /* if arg_count == P_INFINITE_ARGS */