1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-04 17:49:45 +02:00
2010-09-13 23:42:21 +00:00

480 lines
16 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* cmdline.c -- command line parsing routines */
/* Copyright 1989 Carnegie Mellon University */
/*
* 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
* 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.
*
* Long names are good for readability, but single character abbreviations
* are nice for experienced users. cmdline.c allows single character
* abbreviations provided that they are unambiguous. These are
* recognized with no extra work by the programmer. If an
* isolated '?' is encountered in the command line, then all of
* the options and switches are printed as help and for debugging.
*
* Given that we must tell this module about option names and switch
* names, how should we do it? We can't wait until modules are
* initialized, since often modules want to read the command line
* at initialization time. In the original implementation, the
* main program was supposed to provide names for the whole program,
* but this violates modularity: when an option is added to a module,
* the main program has to be modified too. This is a real pain when
* different machines support different options and you want to have
* a single machine-independent main program. The solution is to
* have the main program import strings describing the options and
* switches used by each module. These are passed into cmdline.c
* before initialization of other modules is begun.
*
* A main program that uses cmdline.c should do the following:
* call cl_syntax(s) for each module's option/switch string.
* The string s should have the following format:
* "opt1<o>description;opt2<o>description;...;switch1<s>description;..."
* where opt1 and opt2 are option names (without the preceding "-"), and
* switch1 is a switch name. The <o> and <s> indicate whether the
* name is an option or a switch. The descriptions are arbitrary strings
* (without semicolons) that are printed out for the user when "?"
* is typed on the command line.
*
* After calling cl_syntax, main() should call
* cl_init(argv, argc)
* cl_init will report an error (to STDERR) if it finds any illegal
* switch or option names in argv, and help will be printed if "?"
* is found in argv. If cl_init returns false, then the user has been
* given an error message or help, and main should probably exit.
*
* 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.
*
*/
/*****************************************************************************
* Change Log
* Date | Change
*-----------+-----------------------------------------------------------------
* 13-Jun-86 | Created Change Log
* 6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines
* 20-Sep-89 | Redesigned the interface, adding cl_syntax call.
* 2-Apr-91 | JDW : further changes
* 27-Dec-93 | "@file" as first arg reads command line args from file
* 11-Mar-94 | PLu: Add private to cl_search() definition.
* 28-Apr-03 | DM: true->TRUE, false->FALSE
*****************************************************************************/
/* stdlib.h not on PMAX */
#ifndef mips
#include "stdlib.h"
#endif
#include "stdio.h"
#include "cext.h"
#include "userio.h"
#include "cmdline.h"
#include "ctype.h"
#include "string.h"
#define syntax_max 10 /* allow for 10 syntax strings */
private char *syntax[syntax_max];
private int n_syntax = 0; /* number of strings so far */
private char **argv; /* command line argument vector */
private int argc; /* length of argv */
private boolean cl_rdy = FALSE; /* set to TRUE when initialized */
#define cl_OPT 1
#define cl_SW 2
#define cl_INIT 3
#define cl_ARG 4
/*****************************************************************************
* Routines local to this module
*****************************************************************************/
private char *cl_search();
private int find_string();
private void indirect_command(char *filename, char *oldarg0);
private void ready_check();
/****************************************************************
* 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;
{
return (n <= 0 ? argv[0] :
cl_search((char *)NULL, cl_ARG, n));
}
/* cl_help -- print help from syntax strings */
/**/
void cl_help()
{
register int i, j;
int count = 0; /* see if there are any switches or flags */
for (i = 0; i < n_syntax; i++) {
register char *ptr = syntax[i];
register char c = *ptr++;
while (c != EOS) {
while (c != EOS && !(isalnum(c))) c = *ptr++;
if (c != EOS) {
count++;
gprintf(TRANS, "-");
j = 1;
while (c != EOS && c != '<') {
gprintf(TRANS, "%c", c);
c = *ptr++;
j++;
}
if (c != EOS) {
c = *ptr++;
if (c == 'o') {
gprintf(TRANS, " xxx");
j += 4;
}
}
/* attempt to tab */
do {
gprintf(TRANS, " ");
} while (j++ < 16);
while (c != EOS && c != '>') c = *ptr++;
if (c != EOS) c = *ptr++;
while (c != EOS && c != ';') {
gprintf(TRANS, "%c", c);
c = *ptr++;
}
gprintf(TRANS, "\n");
}
}
}
if (!count) gprintf(TRANS, "No switches or options exist.\n");
}
/*****************************************************************************
* 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(av, ac)
char *av[];
int ac;
{
argv = av;
argc = ac;
/* check for help request */
if (argc == 2 && strcmp(argv[1], "?") == 0) {
cl_help();
return FALSE; /* avoid cl_search which would complain about "?" */
}
/* check for indirection */
if (argc == 2 && *(argv[1]) == '@') {
/* read new args from file */
indirect_command(av[1] + 1, av[0]);
}
/* check command line syntax: */
cl_rdy = TRUE;
return (cl_rdy = (cl_search("true", cl_INIT, 0) != NULL));
}
/****************************************************************
* cl_int_option
* Inputs:
* char *name: name of option
* long default: default value for option
* Result:
* returns long encoding of the option, deflt if none
* Implementation:
* call cl_option and sscanf result
*****************************************************************/
long cl_int_option(name, deflt)
char *name;
long deflt;
{
char *opt = cl_option(name);
if (opt) {
if (sscanf(opt, "%ld", &deflt) != 1) {
gprintf(TRANS, "Warning: option %s %s not an integer, ignored\n",
name, opt);
}
}
return deflt;
}
/****************************************************************
* cl_search
* Inputs:
* char *name: name of field, must be non-null if opt_sw == cl_INIT
* int opt_sw: option, switch, init, or arg
* int n: argument number (if opt_sw is cl_ARG)
* Result:
* returns pointer to option value/switch if one exists, otherwise null
* Implementation:
* parse the command line until name or arg is found
* see if the option is followed by a string that does
* not start with "-"
*****************************************************************/
private char *cl_search(name, opt_sw, n)
char *name;
int opt_sw;
int n; /* if opt_sw is cl_ARG, n > 0 tells which one */
{
register int i = 1; /* index into command line */
boolean abbr;
boolean result = TRUE;
ready_check();
/* parse command line: */
while (i < argc) {
register char *arg = argv[i];
/* arguments that start with '-' should be quoted and quotes must
be removed by the application
*/
if (*arg == '-') {
int arg_type = find_string(arg + 1, &abbr);
if (arg_type == cl_OPT) {
i += 1; /* skip name and option */
/* don't look for '-' because the option might be a
* negative number
*/
if (i >= argc /* || *arg == '-' */) {
if (opt_sw == cl_INIT) {
gprintf(ERROR, "missing argument after %s\n", arg);
result = FALSE;
}
} else if (opt_sw == cl_OPT &&
(strcmp(arg + 1, name) == 0 ||
(abbr && *(arg + 1) == name[0]))) {
return argv[i];
}
} else if (arg_type == cl_SW) {
if (opt_sw == cl_SW &&
(strcmp(arg + 1, name) == 0 ||
(abbr && *(arg + 1) == name[0])))
return arg;
} else if (opt_sw == cl_INIT) {
gprintf(ERROR, "invalid switch: %s\n", arg);
result = FALSE;
}
} else if (opt_sw == cl_ARG) {
if (n == 1) return arg;
n--;
}
i++; /* skip to next field */
}
if (opt_sw == cl_INIT) {
/* return name or NULL to represent TRUE or FALSE */
return (result ? name : NULL);
}
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;
{
return cl_search(name, cl_OPT, 0);
}
/****************************************************************
* cl_switch
* Inputs:
* char *name: switch name
* Outputs:
* boolean: TRUE if switch found
****************************************************************/
boolean cl_switch(name)
char *name;
{
return (boolean)(cl_search(name, cl_SW, 0) != NULL);
}
/* cl_syntax -- install a string specifying options and switches */
/**/
boolean cl_syntax(char *s)
{
if (n_syntax < syntax_max) {
syntax[n_syntax++] = s;
return TRUE;
} else {
gprintf(ERROR, "cl_syntax: out of room\n");
return FALSE;
}
}
/****************************************************************
* find_string
* Inputs:
* char *s: string to find, terminated by any non-alphanumeric
* boolean *abbr: set TRUE if s is an abbreviation, otherwise false
* Effect:
* Looks for s in syntax strings
* Returns:
* 0 = FALSE = not found, 1 = cl_OPT = option, 2 = cl_SW = switch
*****************************************************************/
private int find_string(s, abbr)
char *s;
boolean *abbr;
{
int found_it = FALSE;
int i;
*abbr = FALSE;
for (i = 0; i < n_syntax; i++) { /* loop through strings */
register char *syntax_ptr = syntax[i];
while (*syntax_ptr != EOS) {
register char *s_ptr = s;
while (*syntax_ptr != EOS &&
!(isalnum(*syntax_ptr))) syntax_ptr++;
while (*s_ptr != EOS && (*s_ptr++ == *syntax_ptr))
syntax_ptr++; /* only increment if there's a match */
if (!(isalnum(*s_ptr)) && *syntax_ptr == '<') {
syntax_ptr++; /* advance to the type field */
if (*syntax_ptr == 's') return cl_SW;
if (*syntax_ptr != 'o')
gprintf(ERROR,
"(internal error) bad cl_syntax string: %s\n",
syntax[i]);
return cl_OPT;
}
/* no match, so go to next */
while (*syntax_ptr != ';' && *syntax_ptr != EOS) syntax_ptr++;
if (*syntax_ptr == ';') syntax_ptr++;
}
}
/* no match, maybe there is a single character match */
if (s[0] == EOS || s[1] != EOS) return FALSE;
for (i = 0; i < n_syntax; i++) { /* loop through strings */
char *syntax_ptr = syntax[i];
while (*syntax_ptr != EOS) {
while (*syntax_ptr != EOS &&
!(isalnum(*syntax_ptr))) syntax_ptr++;
if (s[0] == *syntax_ptr) {
if (found_it) return FALSE; /* ambiguous */
/* else, find the type */
while (*syntax_ptr != '<' && *syntax_ptr != EOS)
syntax_ptr++;
syntax_ptr++;
if (*syntax_ptr == 's') found_it = cl_SW;
else if (*syntax_ptr == 'o') found_it = cl_OPT;
else return FALSE; /* error in string syntax */
}
/* no match, so go to next */
while (*syntax_ptr != ';' && *syntax_ptr != EOS) syntax_ptr++;
if (*syntax_ptr == ';') syntax_ptr++;
}
}
if (found_it) *abbr = TRUE;
return found_it;
}
/* get_arg -- get an argument from a file */
/**/
boolean get_arg(file, arg)
FILE *file;
char *arg;
{
int c;
while ((c = getc(file)) != EOF && isspace(c)) ;
if (c == EOF) return FALSE;
ungetc(c, file);
while ((c = getc(file)) != EOF && !isspace(c)) {
*arg++ = c;
}
*arg = 0;
return TRUE;
}
/* indirect_command -- get argv, argc from a file */
/**/
private void indirect_command(filename, oldarg0)
char *filename;
char *oldarg0;
{
FILE *argfile = fopen(filename, "r");
if (!argfile) {
argv = (char **) malloc(sizeof(char *));
argv[0] = oldarg0;
argc = 1;
} else {
int i = 1;
char arg[100];
while (get_arg(argfile, arg)) i++;
fclose(argfile);
argfile = fopen(filename, "r");
argv = (char **) malloc(sizeof(char *) * i);
argv[0] = oldarg0;
argc = i;
i = 1;
while (get_arg(argfile, arg)) {
argv[i] = (char *) malloc(strlen(arg) + 1);
strcpy(argv[i], arg);
i++;
}
fclose(argfile);
}
}
/****************************************************************
* ready_check
* Effect:
* Halt program if cl_rdy is not true.
*****************************************************************/
private void ready_check()
{
if (!cl_rdy) {
gprintf(ERROR,
"Internal error: cl_init was not called, see cmdline.c\n");
EXIT(1);
}
}