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;
126 index = match_table (*argv, table, entries, use_prefix, &length);
129 /* The argument doesn't match anything in the table */
134 *--*argv = '-'; /* complain at invalid flag */
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 || type > P_DOUBLE)
238 "%s [arg_verify]: bad arg type in entry %d: '%d'\n",
239 this_program, i, type);
242 /* Check table size */
244 { int size = arg_table_size (*arg);
246 if (arg_count (*arg) == P_INFINITE_ARGS && size < 1) {
247 fprintf (stderr, "%s [arg_verify]: bad ", this_program);
248 fprintf (stderr, "table size in entry %d: '%d'\n", i,
250 } /* if (arg_count == P_INFINITE_ARGS && size < 1) */
259 /* match_table -- returns the index of the best entry matching the input,
260 -1 if no match. The best match is the one of longest length which
261 appears lowest in the table. The length of the match will be returned
262 in length ONLY IF a match was found. */
264 int match_table (norm_input, table, entries, use_prefix, length)
265 register char *norm_input;
271 extern int match (/* char *, char *, arg_info *, boolean */);
273 char low_input[MAX_INPUT_SIZE];
275 int best_index = -1, best_length = 0;
279 (void) lower_string (low_input, norm_input);
281 for (i = 0; i < entries; i++) {
282 int this_length = match (norm_input, low_input, &table[i], use_prefix);
284 if (this_length > best_length) {
286 best_length = this_length;
287 } /* if (this_length > best_length) */
290 if (best_index > -1 && length != (int *) NULL)
291 *length = best_length;
297 /* match -- takes an input string and table entry, and returns the length
300 0 ==> input doesn't match
304 INPUT PREFIX STRING RESULT
305 ----------------------------------------------------------------------
307 "-d" "-" "d" 2 (i.e. "-d")
308 "dout" "-" "d" 1 (i.e. "d")
309 "-d" "" "-d" 2 (i.e. "-d")
310 "dd" "d" "d" 2 <= here's the weird one
313 int match (norm_input, low_input, entry, use_prefix)
314 char *norm_input, *low_input;
318 char *norm_prefix = arg_prefix (*entry);
319 char *norm_string = arg_string (*entry);
320 boolean prefix_match = FALSE, string_match = FALSE;
323 /* Buffers for the lowercased versions of the strings being compared.
324 These are used when the switch is to be case insensitive */
326 static char low_prefix[MAX_INPUT_SIZE];
327 static char low_string[MAX_INPUT_SIZE];
328 int prefix_length = strlen (norm_prefix);
329 int string_length = strlen (norm_string);
331 /* Pointers for the required strings (lowered or nonlowered) */
333 register char *input, *prefix, *string;
337 /* Use the appropriate strings to handle case sensitivity */
339 if (arg_flags (*entry) & P_CASE_INSENSITIVE) {
341 prefix = lower_string (low_prefix, norm_prefix);
342 string = lower_string (low_string, norm_string);
345 prefix = norm_prefix;
346 string = norm_string;
349 /* First, check the string formed by concatenating the prefix onto the
350 switch string, but only when the prefix is not being ignored */
352 if (use_prefix && prefix != NULL && *prefix != '\0')
353 prefix_match = (strncmp (input, prefix, prefix_length) == 0) &&
354 (strncmp (input + prefix_length, string, string_length) == 0);
356 /* Next, check just the switch string, if that's allowed */
358 if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0)
359 string_match = strncmp (input, string, string_length) == 0;
362 result = prefix_length + string_length;
363 else if (string_match)
364 result = string_length;
370 char *lower_string (dest, src)
376 if (dest == NULL || src == NULL)
379 while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c);
385 /* arg_parse -- returns the number of characters parsed for this entry */
387 static int arg_parse (str, entry)
393 if (arg_count (*entry) == P_ONE_ARG) {
394 char **store = (char **) arg_result_ptr (*entry);
396 length = put_one_arg (arg_result_type (*entry), str, store,
397 arg_prefix (*entry), arg_string (*entry));
399 } /* if (arg_count == P_ONE_ARG) */
400 else { /* Must be a table of arguments */
401 char **store = (char **) arg_result_ptr (*entry);
407 length = put_one_arg (arg_result_type (*entry), str, store++,
408 arg_prefix (*entry), arg_string (*entry));
410 *store = (char *) NULL;
418 int put_one_arg (type, str, store, prefix, string)
422 char *prefix, *string;
435 fprintf (stderr, "%s: Missing argument after '%s%s'\n",
436 this_program, prefix, string);
437 length = str ? strlen (str) : 0;
440 *((char *) store) = *str;
445 *(short *)store = (short) L;
446 if (L != *(short *)store)
448 "%s%s parameter '%ld' is not a SHORT INT (truncating to %d)\n",
449 prefix, string, L, *(short *)store);
450 length = strlen (str);
454 *(int *)store = (int)L;
455 if (L != *(int *)store)
457 "%s%s parameter '%ld' is not an INT (truncating to %d)\n",
458 prefix, string, L, *(int *)store);
459 length = strlen (str);
462 *(long *)store = atol(str);
463 length = strlen (str);
466 *((float *) store) = (float) atof (str);
467 length = strlen (str);
470 *((double *) store) = (double) atof (str);
471 length = strlen (str);
474 fprintf (stderr, "put_one_arg: bad type '%d'\n",
484 void init_store (table, entries)
490 for (index = 0; index < entries; index++)
491 if (arg_count (table[index]) == P_INFINITE_ARGS) {
492 char **place = (char **) arg_result_ptr (table[index]);
495 *place = (char *) NULL;
496 } /* if arg_count == P_INFINITE_ARGS */