Pristine Ack-5.5
[Ack-5.5.git] / lang / fortran / comp / parse_args.c
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