File: cmdline.c

package info (click to toggle)
audacity 1.2.4b-2.1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 24,136 kB
  • ctags: 20,445
  • sloc: ansic: 139,567; cpp: 55,998; sh: 24,963; lisp: 3,772; makefile: 1,683; python: 272
file content (406 lines) | stat: -rw-r--r-- 12,481 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/* cmdline.c -- command line parsing routines */
/*
 * This module is designed to allow various modules to scan (and rescan)
 * the command line for applicable arguments.  The goal is to hide as
 * much information about switches and their names as possible so that
 * switches become more consistent across applications and so that the
 * author of an application need not do a lot of work to provide numerous
 * options.  Instead, each module scans the command line for its own
 * arguments.
 *
 * Command lines are of the following form:
 *	command -s1 -s2 opt2 -s3 arg1 arg2 -s4 opt4 arg3
 *      command @filename
 * The @filename form reads commands of the first form from filename
 * Note that there are three kinds of command line parameters:
 * (1) A Switch is a "-" followed by a name, e.g. "-s1"
 * (2) An Option is a Switch followed by a space and name, e.g. "-s2 opt2"
 * (3) An Argument is a name by itself, e.g. "arg1"
 * Note also that a switch followed by an argument looks just like an
 * option, so a list of valid option names is necessary to disambiguate.
 *
 * A main program that uses cmdline.c should do the following:
 *	(1) create an array of pointers to strings (char *names[]) that
 *		contains every possible option name
 *	(2) create another array of pointers to strings that contains
 *		every possible switch name
 *	(2) call cl_init(switches, nsw, options, nopt, argv, argc)
 * cl_init will report an error (to stderr) if it finds any illegal
 * switch or option names.
 *
 * Afterward, switches, options, and arguments can be accessed by
 * calling cl_switch, cl_option, and cl_arg.  If cl_switch or cl_option
 * is called with a switch name that was not mentioned in the call to 
 * cl_init, an error will result.  This indicates that the application
 * author omitted a valid switch or option name when calling cl_init.
 * This is an error because the full set of names is needed for error
 * checking and to distinguish arguments from options.
 *
 * cl_nswitch and cl_noption are similar to cl_switch and cl_option,
 * except they each take a list of equivalent switch or option names.  
 * This makes it simple to allow both verbose (-debug) and terse (-d) names.
 */

/*****************************************************************************
*	    Change Log
*  Date	    | Change
*-----------+-----------------------------------------------------------------
* 13-Jun-86 | Created Change Log
*  6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines
* 27-Dec-93 | "@file" as first arg reads command line args from file
*****************************************************************************/

#include "stdlib.h"
#include "cext.h"
#include "stdio.h"
#include "ctype.h"
#include "cmdline.h"
#include "string.h"

private char **voptions;	/* valid options */
private int noptions;		/* number of options */
private char **vswitches;	/* valid switches */
private int nswitches;		/* number of switches */
private char **argv;		/* command line argument vector */
private int argc;		/* length of argv */

private int cl_rdy = false;	/* set to true when initialized */

/*****************************************************************************
*	Routines local to this module
*****************************************************************************/
private	void	check_names();
private int	find_match();
private int	find_string();
private	void	ready_check();
void indirect_command(char *filename, char ***argvp, int *argcp, char *oldarg0);


/****************************************************************
*			check_names
* Inputs:
*	char *names[]:	array of alternative switch or option names
*	int nnames:	number of alternative switch or option names
*	char *valid[]:	array of valid names
*	int nvalid:	number of valid names
* Effect:
*	Checks that all names are in validnames.  If not, print
*	an error message.
*****************************************************************/

private void check_names(names, nnames, valid, nvalid)
    char *names[];
    int nnames;
    char *valid[];
    int nvalid;
{
    int i;	/* loop counters */
    for (i = 0; i < nnames; i++) {
        if (find_string(names[i], valid, nvalid) >= nvalid) {
            fprintf(stderr, "internal error detected by cmdline module:\n");
            fprintf(stderr, "\t'%s' should be in valid lists\n", names[i]);
        }
    }
}

/****************************************************************
*			cl_arg
* Inputs:
*	n: the index of the arg needed
* Results:
*	pointer to the nth arg, or NULL if none exists
*	arg 0 is the command name
*****************************************************************/

char *cl_arg(n)
    int n;
{
    int i = 1;
    if (n <= 0) return argv[0];
    while (i < argc) {
        if (*argv[i] == '-') {
            if (find_string(argv[i], voptions, noptions) < noptions)
                i += 2; /* skip name and option */
            else i += 1; /* skip over switch name */
        } else if (n == 1) {
            return argv[i];
        } else { /* skip over argument */
            n--;
            i++;
        }
    }
    return NULL;
}

/*****************************************************************************
*			cl_init
* Inputs:
*	char *switches[]:	array of switch names
*	int nsw:		number of switch names
*	char *options[]:	array of option names
*	int nopt:		number of option names
*	char *av:		array of command line fields (argv)
*	int ac:			number of command line fields (argc)
* Effect:
*	Checks that all command line entries are valid.
*	Saves info for use by other routines.
* Returns:
*	True if syntax checks OK, otherwise false
*****************************************************************************/

boolean cl_init(switches, nsw, options, nopt, av, ac)
    char *switches[];
    int nsw;
    char *options[];
    int nopt;
    char *av[];
    int ac;
{
    int i;	/* index into argv */
    boolean result = true;

    vswitches = switches;	nswitches = nsw;
    voptions = options;		noptions = nopt;
    argv = av;			argc = ac;
    
    if (ac == 2 && *(av[1]) == '@') {
            /* read new args from file */
            indirect_command(av[1] + 1, &argv, &argc, av[0]);
    }

    for (i = 1; i < argc; i++) {  /* case fold lower */
        size_t j;
        for (j = 0; j < strlen(argv[i]); j++)
            if (isupper(argv[i][j])) 
                argv[i][j] = tolower(argv[i][j]);
    }

    /* check command line syntax: */
    i = 1;
    while (i < argc) {
        if (*argv[i] == '-') {
            if (find_string(argv[i], voptions, noptions) < noptions) {
                i += 1; /* skip name and option */
                if (i < argc && *argv[i] == '-') {
                    fprintf(stderr, "missing argument after %s\n", argv[i-1]);
                    result = false;
                    i += 1;
                }
            } else if (find_string(argv[i], vswitches, nswitches) < 
                       nswitches) {
                i += 1; /* skip over switch name */
            } else {
                fprintf(stderr, "invalid switch: %s\n", argv[i]);
                i += 1;
                result = false;
            }
        } else i++; /* skip over argument */
    }
    cl_rdy = true;
    return result;
}

/****************************************************************
*			cl_noption
* Inputs:
*	char *names[]:	array of alternative switch names
*	int nnames:	number of alternative switch names
* Result:
*	returns pointer to  if one exists, otherwise null
* Effect:
*	looks for pattern in command line of the form "-n s",
*	where n is a member of names.  Returns pointer to s.
* Implementation:
*	find the option name, then
*	see if the switch is followed by a string that does
*	not start with "-"
*****************************************************************/

char *cl_noption(names, nnames)
    char *names[];
    int nnames;
{
    int i;	/* index of switch */

    ready_check();
    check_names(names, nnames, voptions, noptions);
    i = find_match(names, nnames) + 1; /* point at the option */
    if (i < argc) { /* make sure option exists */
        if (*(argv[i]) != '-') return argv[i];
    }
    return NULL;
}

/*****************************************************************
*			cl_nswitch
* Inputs:
*	char *names[]:	array of alternative switch names
*	int nnames:	number of alternative switch names
* Effect:
*	Checks that names is valid.
*	Finds a pattern in command line of the form "-n", where
*	n is a member of names.
* Result:
*	returns pointer to command line switch if one exists,
*	otherwise null
*****************************************************************/

char *cl_nswitch(names, nnames)
    char *names[];
    int nnames;
{
    int i;	/* index of switch */

    ready_check();
    check_names(names, nnames, vswitches, nswitches);
    i = find_match(names, nnames);
    if (i < argc) return argv[i];
    /* else */ return NULL;
}

/****************************************************************
*			cl_option
* Inputs:
*	char *name:	option name
* Outputs:
*	returns char *: the option string if found, otherwise null
****************************************************************/

char *cl_option(name)
    char *name;
{
    char *names[1];	/* array to hold name */

    names[0] = name;
    return cl_noption(names, 1);
}

/****************************************************************
*			cl_switch
* Inputs:
*	char *name:	switch name
* Outputs:
*	boolean:	true if switch found
****************************************************************/

boolean cl_switch(name)
    char *name;
{
    char *names[1];	/* array to hold name */

    names[0] = name;
    return cl_nswitch(names, 1) != NULL;
}

/****************************************************************
*			find_match
* Inputs:
*	char *names[]:	array of alternative switch or option names
*	int nnames:	number of alternative switch or option names
* Effect:
*	Looks for command line switch that matches one of names.
* Returns:
*	Index of switch if found, argc if not found.
*****************************************************************/

private int find_match(names, nnames)
    char *names[];
    int nnames;
{
    int j;	/* loop counter */
    for (j = 0; j < argc; j++) {
        if (find_string(argv[j], names, nnames) < nnames) return j;
    }
    return argc;
}

/****************************************************************
*			find_string
* Inputs:
*	char *s:	string to find
*	char *names[]:	array of strings
*	int nnames:	number of strings
* Effect:
*	Looks for s in names
* Returns:
*	Index of s in names if found, nnames if not found
*****************************************************************/

private int find_string(s, names, nnames)
    char *s;
    char *names[];
    int nnames;
{
    int i; /* loop counter */
    for (i = 0; i < nnames; i++) {
        if (strcmp(s, names[i]) == 0) {
            return i;
        }
    }
    return nnames;
}

boolean is_whitespace(int c)
{
    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

boolean get_arg(file, arg)
  FILE *file;
  char *arg;
{
    int c;
    while ((c = getc(file)) != EOF && is_whitespace(c)) ;
    if (c == EOF) return false;
    ungetc(c, file);
    while ((c = getc(file)) != EOF && !is_whitespace(c)) {
            *arg++ = c;
    }
    *arg = 0;
    return true;
}


void indirect_command(filename, argvp, argcp, oldarg0)
        char *filename;
    char ***argvp;
    int *argcp;
    char *oldarg0;
{
    FILE *argfile = fopen(filename, "r");
    if (!argfile) {
            *argvp = (char **) malloc(sizeof(char *));
        (*argvp)[0] = oldarg0;
        *argcp = 1;
    } else {
            int i = 1;
        char arg[100];
        while (get_arg(argfile, arg)) i++;
        fclose(argfile);
        argfile = fopen(filename, "r");
        *argvp = (char **) malloc(sizeof(char *) * i);
        (*argvp)[0] = oldarg0;
        *argcp = i;
        i = 1;
        while (get_arg(argfile, arg)) {
            (*argvp)[i] = (char *) malloc(strlen(arg) + 1);
            strcpy((*argvp)[i], arg);
            i++;
        }
    }
}


/****************************************************************
*			ready_check
* Effect:
*	Halt program if cl_rdy is not true.
*****************************************************************/
private void ready_check()
{
    if (!cl_rdy) {
        fprintf(stderr,
            "Internal error: cl_init was not called, see cmdline.c\n");
        exit(1);
    }
}