|
|
1.1 root 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: extern long atol();
90: static int arg_parse (/* char *, arg_info * */);
91:
92:
93: boolean parse_args (argc, argv, table, entries, others, other_count)
94: int argc;
95: char *argv[];
96: arg_info table[];
97: int entries;
98: char *others[];
99: int other_count;
100: {
101: boolean arg_verify (/* argv, table, entries */);
102: void init_store (/* table, entries */);
103:
104: boolean result;
105:
106: if (argv)
107: this_program = argv[0];
108:
109: /* Check the validity of the table and its parameters */
110:
111: result = arg_verify (argv, table, entries);
112:
113: /* Initialize the storage values */
114:
115: init_store (table, entries);
116:
117: if (result) {
118: boolean use_prefix = TRUE;
119: char *argv0;
120:
121: argc--;
122: argv0 = *++argv;
123: while (argc) {
124: int index, length;
125:
126: index = match_table (*argv, table, entries, use_prefix, &length);
127: if (index < 0) {
128:
129: /* The argument doesn't match anything in the table */
130:
131: if (others) {
132:
133: if (*argv > argv0)
134: *--*argv = '-'; /* complain at invalid flag */
135:
136: if (other_count > 0) {
137: *others++ = *argv;
138: other_count--;
139: } else {
140: fprintf (stderr, "%s: too many parameters: ",
141: this_program);
142: fprintf (stderr, "'%s' ignored\n", *argv);
143: } /* else */
144: } /* if (others) */
145: argv0 = *++argv;
146: argc--;
147: } else {
148:
149: /* A match was found */
150:
151: if (length >= strlen (*argv)) {
152: argc--;
153: argv0 = *++argv;
154: use_prefix = TRUE;
155: } else {
156: (*argv) += length;
157: use_prefix = FALSE;
158: } /* else */
159:
160: /* Parse any necessary arguments */
161:
162: if (arg_count (table[index]) != P_NO_ARGS) {
163:
164: /* Now length will be used to store the number of parsed characters */
165:
166: length = arg_parse(*argv, &table[index]);
167: if (*argv == NULL)
168: argc = 0;
169: else if (length >= strlen (*argv)) {
170: argc--;
171: argv0 = *++argv;
172: use_prefix = TRUE;
173: } else {
174: (*argv) += length;
175: use_prefix = FALSE;
176: } /* else */
177: } /* if (argv_count != P_NO_ARGS) */
178: else
179: *arg_result_ptr(table[index]) =
180: arg_table_size(table[index]);
181: } /* else */
182: } /* while (argc) */
183: } /* if (result) */
184:
185: return result;
186: } /* parse_args */
187:
188:
189: boolean arg_verify (argv, table, entries)
190: char *argv[];
191: arg_info table[];
192: int entries;
193: {
194: int i;
195: char *this_program = "";
196:
197: if (argv)
198: this_program = argv[0];
199:
200: for (i = 0; i < entries; i++) {
201: arg_info *arg = &table[i];
202:
203: /* Check the argument flags */
204:
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,
208: arg_flags (*arg));
209: } /* if */
210:
211: /* Check the argument count */
212:
213: { int count = arg_count (*arg);
214:
215: if (count != P_NO_ARGS && count != P_ONE_ARG && count !=
216: P_INFINITE_ARGS) {
217: fprintf (stderr, "%s [arg_verify]: invalid ", this_program);
218: fprintf (stderr, "argument count in entry %d: '%d'\n", i,
219: count);
220: } /* if count != P_NO_ARGS ... */
221:
222: /* Check the result field; want to be able to store results */
223:
224: else
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 */
230: }
231:
232: /* Check the argument type */
233:
234: { int type = arg_result_type (*arg);
235:
236: if (type < P_STRING || type > P_DOUBLE)
237: fprintf(stderr,
238: "%s [arg_verify]: bad arg type in entry %d: '%d'\n",
239: this_program, i, type);
240: }
241:
242: /* Check table size */
243:
244: { int size = arg_table_size (*arg);
245:
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,
249: size);
250: } /* if (arg_count == P_INFINITE_ARGS && size < 1) */
251: }
252:
253: } /* for i = 0 */
254:
255: return TRUE;
256: } /* arg_verify */
257:
258:
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. */
263:
264: int match_table (norm_input, table, entries, use_prefix, length)
265: register char *norm_input;
266: arg_info table[];
267: int entries;
268: boolean use_prefix;
269: int *length;
270: {
271: extern int match (/* char *, char *, arg_info *, boolean */);
272:
273: char low_input[MAX_INPUT_SIZE];
274: register int i;
275: int best_index = -1, best_length = 0;
276:
277: /* FUNCTION BODY */
278:
279: (void) lower_string (low_input, norm_input);
280:
281: for (i = 0; i < entries; i++) {
282: int this_length = match (norm_input, low_input, &table[i], use_prefix);
283:
284: if (this_length > best_length) {
285: best_index = i;
286: best_length = this_length;
287: } /* if (this_length > best_length) */
288: } /* for (i = 0) */
289:
290: if (best_index > -1 && length != (int *) NULL)
291: *length = best_length;
292:
293: return best_index;
294: } /* match_table */
295:
296:
297: /* match -- takes an input string and table entry, and returns the length
298: of the longer match.
299:
300: 0 ==> input doesn't match
301:
302: For example:
303:
304: INPUT PREFIX STRING RESULT
305: ----------------------------------------------------------------------
306: "abcd" "-" "d" 0
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
311: */
312:
313: int match (norm_input, low_input, entry, use_prefix)
314: char *norm_input, *low_input;
315: arg_info *entry;
316: boolean use_prefix;
317: {
318: char *norm_prefix = arg_prefix (*entry);
319: char *norm_string = arg_string (*entry);
320: boolean prefix_match = FALSE, string_match = FALSE;
321: int result = 0;
322:
323: /* Buffers for the lowercased versions of the strings being compared.
324: These are used when the switch is to be case insensitive */
325:
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);
330:
331: /* Pointers for the required strings (lowered or nonlowered) */
332:
333: register char *input, *prefix, *string;
334:
335: /* FUNCTION BODY */
336:
337: /* Use the appropriate strings to handle case sensitivity */
338:
339: if (arg_flags (*entry) & P_CASE_INSENSITIVE) {
340: input = low_input;
341: prefix = lower_string (low_prefix, norm_prefix);
342: string = lower_string (low_string, norm_string);
343: } else {
344: input = norm_input;
345: prefix = norm_prefix;
346: string = norm_string;
347: } /* else */
348:
349: /* First, check the string formed by concatenating the prefix onto the
350: switch string, but only when the prefix is not being ignored */
351:
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);
355:
356: /* Next, check just the switch string, if that's allowed */
357:
358: if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0)
359: string_match = strncmp (input, string, string_length) == 0;
360:
361: if (prefix_match)
362: result = prefix_length + string_length;
363: else if (string_match)
364: result = string_length;
365:
366: return result;
367: } /* match */
368:
369:
370: char *lower_string (dest, src)
371: char *dest, *src;
372: {
373: char *result = dest;
374: register int c;
375:
376: if (dest == NULL || src == NULL)
377: result = NULL;
378: else
379: while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c);
380:
381: return result;
382: } /* lower_string */
383:
384:
385: /* arg_parse -- returns the number of characters parsed for this entry */
386:
387: static int arg_parse (str, entry)
388: char *str;
389: arg_info *entry;
390: {
391: int length = 0;
392:
393: if (arg_count (*entry) == P_ONE_ARG) {
394: char **store = (char **) arg_result_ptr (*entry);
395:
396: length = put_one_arg (arg_result_type (*entry), str, store,
397: arg_prefix (*entry), arg_string (*entry));
398:
399: } /* if (arg_count == P_ONE_ARG) */
400: else { /* Must be a table of arguments */
401: char **store = (char **) arg_result_ptr (*entry);
402:
403: if (store) {
404: while (*store)
405: store++;
406:
407: length = put_one_arg (arg_result_type (*entry), str, store++,
408: arg_prefix (*entry), arg_string (*entry));
409:
410: *store = (char *) NULL;
411: } /* if (store) */
412: } /* else */
413:
414: return length;
415: } /* arg_parse */
416:
417:
418: int put_one_arg (type, str, store, prefix, string)
419: int type;
420: char *str;
421: char **store;
422: char *prefix, *string;
423: {
424: int length = 0;
425: long L;
426:
427: if (store) {
428: switch (type) {
429: case P_STRING:
430: case P_FILE:
431: case P_OLD_FILE:
432: case P_NEW_FILE:
433: *store = str;
434: if (str == NULL)
435: fprintf (stderr, "%s: Missing argument after '%s%s'\n",
436: this_program, prefix, string);
437: length = str ? strlen (str) : 0;
438: break;
439: case P_CHAR:
440: *((char *) store) = *str;
441: length = 1;
442: break;
443: case P_SHORT:
444: L = atol(str);
445: *(short *)store = (short) L;
446: if (L != *(short *)store)
447: fprintf(stderr,
448: "%s%s parameter '%ld' is not a SHORT INT (truncating to %d)\n",
449: prefix, string, L, *(short *)store);
450: length = strlen (str);
451: break;
452: case P_INT:
453: L = atol(str);
454: *(int *)store = (int)L;
455: if (L != *(int *)store)
456: fprintf(stderr,
457: "%s%s parameter '%ld' is not an INT (truncating to %d)\n",
458: prefix, string, L, *(int *)store);
459: length = strlen (str);
460: break;
461: case P_LONG:
462: *(long *)store = atol(str);
463: length = strlen (str);
464: break;
465: case P_FLOAT:
466: *((float *) store) = (float) atof (str);
467: length = strlen (str);
468: break;
469: case P_DOUBLE:
470: *((double *) store) = (double) atof (str);
471: length = strlen (str);
472: break;
473: default:
474: fprintf (stderr, "put_one_arg: bad type '%d'\n",
475: type);
476: break;
477: } /* switch */
478: } /* if (store) */
479:
480: return length;
481: } /* put_one_arg */
482:
483:
484: void init_store (table, entries)
485: arg_info *table;
486: int entries;
487: {
488: int index;
489:
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]);
493:
494: if (place)
495: *place = (char *) NULL;
496: } /* if arg_count == P_INFINITE_ARGS */
497:
498: } /* init_store */
499:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.