Added upstream from http://ftp.icm.edu.pl/pub/loglan/
[loglan.git] / sources / f2c / parsearg.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
120         argc--;
121         argv++;
122         while (argc) {
123             int index, length;
124
125             index = match_table (*argv, table, entries, use_prefix, &length);
126             if (index < 0) {
127
128 /* The argument doesn't match anything in the table */
129
130                 if (others) {
131
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 */
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                 argv++;
146                 argc--;
147             } else {
148
149 /* A match was found */
150
151                 if (length >= strlen (*argv)) {
152                     argc--;
153                     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                         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 &&
237                 type != P_CHAR &&
238                 type != P_SHORT &&
239                 type != P_LONG &&
240                 type != P_INT &&
241                 type != P_FILE &&
242                 type != P_OLD_FILE &&
243                 type != P_NEW_FILE &&
244                 type != P_FLOAT &&
245                 type != P_DOUBLE) {
246                     fprintf (stderr, "%s [arg_verify]:  bad ", this_program);
247                     fprintf (stderr, "arg type in entry %d:  '%d'\n", i,
248                             type);
249             } /* if type != .... */
250         }
251
252 /* Check table size */
253
254         { int size = arg_table_size (*arg);
255
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,
259                         size);
260             } /* if (arg_count == P_INFINITE_ARGS && size < 1) */
261         }
262
263     } /* for i = 0 */
264
265     return TRUE;
266 } /* arg_verify */
267
268
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.   */
273
274 int match_table (norm_input, table, entries, use_prefix, length)
275 register char *norm_input;
276 arg_info table[];
277 int entries;
278 boolean use_prefix;
279 int *length;
280 {
281     extern int match (/* char *, char *, arg_info *, boolean */);
282
283     char low_input[MAX_INPUT_SIZE];
284     register int i;
285     int best_index = -1, best_length = 0;
286
287 /* FUNCTION BODY */
288
289     (void) lower_string (low_input, norm_input);
290
291     for (i = 0; i < entries; i++) {
292         int this_length = match (norm_input, low_input, &table[i], use_prefix);
293
294         if (this_length > best_length) {
295             best_index = i;
296             best_length = this_length;
297         } /* if (this_length > best_length) */
298     } /* for (i = 0) */
299
300     if (best_index > -1 && length != (int *) NULL)
301         *length = best_length;
302
303     return best_index;
304 } /* match_table */
305
306
307 /* match -- takes an input string and table entry, and returns the length
308    of the longer match.
309
310         0 ==> input doesn't match
311
312    For example:
313
314         INPUT   PREFIX  STRING  RESULT
315 ----------------------------------------------------------------------
316         "abcd"  "-"     "d"     0
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
321 */
322
323 int match (norm_input, low_input, entry, use_prefix)
324 char *norm_input, *low_input;
325 arg_info *entry;
326 boolean use_prefix;
327 {
328     char *norm_prefix = arg_prefix (*entry);
329     char *norm_string = arg_string (*entry);
330     boolean prefix_match = FALSE, string_match = FALSE;
331     int result = 0;
332
333 /* Buffers for the lowercased versions of the strings being compared.
334    These are used when the switch is to be case insensitive */
335
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);
340
341 /* Pointers for the required strings (lowered or nonlowered) */
342
343     register char *input, *prefix, *string;
344
345 /* FUNCTION BODY */
346
347 /* Use the appropriate strings to handle case sensitivity */
348
349     if (arg_flags (*entry) & P_CASE_INSENSITIVE) {
350         input = low_input;
351         prefix = lower_string (low_prefix, norm_prefix);
352         string = lower_string (low_string, norm_string);
353     } else {
354         input = norm_input;
355         prefix = norm_prefix;
356         string = norm_string;
357     } /* else */
358
359 /* First, check the string formed by concatenating the prefix onto the
360    switch string, but only when the prefix is not being ignored */
361
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);
365
366 /* Next, check just the switch string, if that's allowed */
367
368     if (!use_prefix && (arg_flags (*entry) & P_REQUIRED_PREFIX) == 0)
369         string_match = strncmp (input, string, string_length) == 0;
370
371     if (prefix_match)
372         result = prefix_length + string_length;
373     else if (string_match)
374         result = string_length;
375
376     return result;
377 } /* match */
378
379
380 char *lower_string (dest, src)
381 char *dest, *src;
382 {
383     char *result = dest;
384     register int c;
385
386     if (dest == NULL || src == NULL)
387         result = NULL;
388     else
389         while (*dest++ = (c = *src++) >= 'A' && c <= 'Z' ? tolower(c) : c);
390
391     return result;
392 } /* lower_string */
393
394
395 /* arg_parse -- returns the number of characters parsed for this entry */
396
397 static int arg_parse (str, entry)
398 char *str;
399 arg_info *entry;
400 {
401     int length = 0;
402
403     if (arg_count (*entry) == P_ONE_ARG) {
404         char **store = (char **) arg_result_ptr (*entry);
405
406         length = put_one_arg (arg_result_type (*entry), str, store,
407                 arg_prefix (*entry), arg_string (*entry));
408
409     } /* if (arg_count == P_ONE_ARG) */
410       else { /* Must be a table of arguments */
411         char **store = (char **) arg_result_ptr (*entry);
412
413         if (store) {
414             while (*store)
415                 store++;
416
417             length = put_one_arg (arg_result_type (*entry), str, store++,
418                     arg_prefix (*entry), arg_string (*entry));
419
420             *store = (char *) NULL;
421         } /* if (store) */
422     } /* else */
423
424     return length;
425 } /* arg_parse */
426
427
428 int put_one_arg (type, str, store, prefix, string)
429 int type;
430 char *str;
431 char **store;
432 char *prefix, *string;
433 {
434     int length = 0;
435
436     if (store) {
437         switch (type) {
438             case P_STRING:
439             case P_FILE:
440             case P_OLD_FILE:
441             case P_NEW_FILE:
442                 *store = str;
443                 if (str == NULL)
444                     fprintf (stderr, "%s: Missing argument after '%s%s'\n",
445                             this_program, prefix, string);
446                 length = str ? strlen (str) : 0;
447                 break;
448             case P_CHAR:
449                 *((char *) store) = *str;
450                 length = 1;
451                 break;
452             case P_SHORT: {
453                 long int i = atol(str);
454
455                 *((short *) store) = (short) i;
456                 if (i > 32767 || i < -32768) {
457                     fprintf (stderr, "%s%s parameter '%ld' is not a ",
458                             prefix, string, i);
459                     fprintf (stderr, "SHORT INT (truncating to %d)\n",
460                             (short) (*store));
461                 } /* if i > 32767 || i < -32768 */
462
463 /* This is pessimistic, and should change */
464
465                 length = strlen (str);
466                 break; }
467             case P_LONG:
468                 *((int *) store) = atol(str);
469
470 /* This is pessimistic too */
471
472                 length = strlen (str);
473                 break;
474             case P_FLOAT:
475                 *((float *) store) = (float) atof (str);
476                 length = strlen (str);
477                 break;
478             case P_DOUBLE:
479                 *((double *) store) = (double) atof (str);
480                 length = strlen (str);
481                 break;
482             default:
483                 fprintf (stderr, "put_one_arg:  bad type '%d'\n",
484                         type);
485                 break;
486         } /* switch */
487     } /* if (store) */
488
489     return length;
490 } /* put_one_arg */
491
492
493 void init_store (table, entries)
494 arg_info *table;
495 int entries;
496 {
497     int index;
498
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]);
502
503             if (place)
504                 *place = (char *) NULL;
505         } /* if arg_count == P_INFINITE_ARGS */
506
507 } /* init_store */
508