Mercurial > hg > aim92
view glib/options.c @ 0:5242703e91d3 tip
Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author | tomwalters |
---|---|
date | Fri, 20 May 2011 15:19:45 +0100 |
parents | |
children |
line wrap: on
line source
/* Copyright (c) Applied Psychology Unit, Medical Research Council. 1988, 1989 =========================================================================== Permission to use, copy, modify, and distribute this software without fee is hereby granted for research purposes, provided that this copyright notice appears in all copies and in all supporting documentation, and that the software is not redistributed for any fee (except for a nominal shipping charge). Anyone wanting to incorporate all or part of this software in a commercial product must obtain a license from the Medical Research Council. The MRC makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE MRC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE A.P.U. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ------------------------------------------------------------------------------- The Leanest Resource Manager in the West. Options may be supplied as defaults (actually in the code), as command line arguments, and in a option file. The default arguments may be overridden by the option-file contents, which may be overridden from the command line. Copyright (c), 1989 The Medical Research Council of the United Kingdom. Author : Paul Manson Written : March 1st 1989 Edited : 2nd March 1989 (Paul Manson) Added the facility of searching the HOME directory for a option file if one is not found in the working directory. 8th March 1989 (Paul Manson) Enhanced the above by providing a OPTIONPATH search strategy for Unix. Currently MSDOS only searches for a option file in the directory where the program was found. 8th March 1989 (Paul Manson) Added the ability to refer to options via abbreviations of their names. At present, an abbrev. will match if it is exactly the same as precisely one option, or if it is the same as the first <n> chars of that option (where <n> is the length of the abbrev) and it matches no other option name. 9th March 1989 (Paul Manson) Added the readopts and writeopts calls, to implement option headers for files. Also altered the form of command line options accepted: See getopts.h for details. 10th March 1989 (Paul Manson) Added the "help" facility to provide a simple self-documentation facility. Also altered the Option type to put defaults and comments in the code. 16th March 1989 (Paul Manson) Altered the final argc/argv shuffling in getopts so that argv simply points at the first argument after the options. Also added fileopts. 29th March 1989 (Paul Manson) Altered ".res" to ".opt" to coincide with the new naming r'egime. Also added the spitOptionsFile call to automatically produce an options file with comments. To enable this facility, compile getopts with -DSPITHELP; to use it, type <prog> -spit. Do NOT distribute a spitting version of getopts! Altered Options-file scanning to permit trailing comments. 30th March 1989 (Paul Manson) Completely eradicated the blockWriteToFile used by writenopts. This is now done on a non-aligned basis, as this is likely to be quicker and less complex. 03rd April 1989 (Paul Manson) Added the calll to outputopt. 06th April 1989 (Paul Manson) Oh Joy! Oh Bliss! The once-much-maligned SPIT option is recieved into decent society, albeit in the guise of an options "editor" called 'update'. 10th April 1989 (Paul Manson) The 'update' option no longer exit(1)s, but carries on to execute the program. Also changed OptionFileName so that it correctly gets the cwd on the PC. 11th April 1989 (Paul Manson) Altered the OptionFileName routines to be EITHER for the PC or for Unix. 26th April 1989 (Paul Manson) Put in a temporary delta to the PC version so that it only looks for option files (and hence only creates option files) in the current directory. This will be abandoned when we determine a better search strategy. 15th June 1989 (Paul Manson) Altered SilentOption so that it is alterable, but is NOT visible in any way (ie. not printed by help or spit, and not output to headers). 11th July 1989 (Paul Manson) Altered so that "help" (and "update") only display (or output) the options which were either read in from the options file, or have been subsequently altered on the command line. The current implementation simply checks to see whether the default and current values for a particular option differ (either in value OR address!). 27th July 1989 (Paul Manson) Altered so that isON == !isOFF, and not the other way around -- this allows "2", "3", etc to masquerade as "1" while only "0" is "off". Also changed so that no quotes are output to file headers. July 1994 (MAA) Allowed spaces in strings, by adding double-quotes to the arguments. Bit of a hack, though. ------------------------------------------------------------------------------- */ #include "options.h" #include <stdio.h> #include <string.h> #if defined( NeXT) #include <stdlib.h> #else #include <malloc.h> #endif #include <ctype.h> #if !defined( lint ) static char *sccs_id = "@(#)options.c 1.14 Paul Manson (MRC-APU) 5/2/89"; #endif char defversion[64] = "not set yet" , *versionstr = defversion ; #if defined( PC ) #include <direct.h> #define DOT '.' #define BACKSLASH_CHAR '\\' #define SLASH_CHARACTER '/' #define OPTION_FILE_PREFIX "" #define OPTION_FILE_SUFFIX ".opt" #else #ifndef THINK_C #include <sys/types.h> #include <sys/stat.h> #endif #define OPTION_FILE_PREFIX "." #define OPTION_FILE_SUFFIX "rc" #define OPTION_PATH "OPTIONSPATH" /* Environment variable for path */ #define DEFAULT_OPTION_PATH "." /* Path is first . then ~ (ie home) */ #endif #define MAX_LINE_LENGTH (256) #define MAX_NAME_LENGTH (128) #define FALSE (0) #define TRUE (1) #define HYPHEN_CHARACTER '-' #define BLANK_CHARACTER ' ' #define TAB_CHARACTER '\t' #define NEWLINE_CHARACTER '\n' #define DOUBLE_QUOTE_CHAR '\"' #define ERROR_RESULT (1234) #define EQUALS_CHARACTER '=' #define COMMENT_CHARACTER '#' #define NULL_CHARACTER '\000' #define CARRIAGE_RETURN '\r' /* Options-specific definitions for "ON", "OFF" and "NULL" */ #define ON_STRING (ON_OPTION) #define ON_ALIAS "1" #define OFF_STRING (OFF_OPTION) #define OFF_ALIAS "0" #define NULL_STRING (NULL_OPTION) #define NULL_ALIAS "null" #define NOT_USED "Not_used" /* For the search_path and Ambiguity procedures */ #define LINE_CHARS 200 #define AMBIGUOUS (-2) #define NO_MATCH (-1) #define DEFAULT_TOGGLE_VALUE (ON_STRING) /* For the readopts and writeopts procedures */ #define OPTION_HEADER_SIZE ( sizeof ( head_string_start ) - 1 + size_digits + 1 ) #define OPTION_HEADER_HEADER "header_bytes=" #define OPTION_HEADER_DIGITS (7) #define min(X,Y) (((X) < (Y)) ? (X) : (Y)) static char OptionHeaderHeader[] = OPTION_HEADER_HEADER; static char LineFeedArray[] = "\n"; static char EqualsArray[] = "="; /* Was " = " */ static char QuotesArray[] = "\""; /* For the OnLine Help Facility */ #define HELP_STRING "help" #define UPDATE_STRING "update" #define ALL_STRING "all" static int helpOnAllRequired = FALSE; static int helpOnOneRequired = FALSE; static int helpOnOneLocation = 0; static int updateAllRequired = FALSE; extern void exit(); static int thereWasAnOptionsFile = FALSE; static char *theOptionFileName = NULL; #ifndef PC /* ----------------------------------------------------------------------- search_path( file, path ) ========================= jwh - 4th jan 1987 searches through string path for file and returns full file name if it finds it. Returns NULL if file is not found. Path is specified in normal path format i.e. "dir1:dir2:dir3...". Works for "~" and "~/dir.." - substitutes HOME for "~". ~ only works for current user!! ----------------------------------------------------------------------- */ static char *search_path( file, path ) char *file, *path ; { #ifdef THINK_C return ( file ) ; #else char file_name[ LINE_CHARS ] ; int len_next ; struct stat buf ; extern getenv(); char *home_directory = (char *)getenv("HOME"); if( file[ 0 ] == '/' || file[ 0 ] == '.' && file[ 1 ] == '/' ) return ( strcpy( malloc((unsigned)(strlen(file) + 1)), file) ); do { len_next = ( strchr( path, ':' ) == NULL ) ? strlen( path ) : strchr( path, ':' ) - path ; if( *path == '~' ) { (void) strcpy( file_name, home_directory ) ; (void) strncpy( file_name + strlen( home_directory ), path+1, len_next-1 ) ; (void) sprintf( file_name + strlen( home_directory ) + len_next - 1 , "/%s", file ) ; } else { (void) strncpy( file_name, path, len_next ) ; (void) sprintf( file_name + len_next , "/%s", file ) ; } if( stat( file_name, &buf ) >= 0 && ( buf.st_mode & S_IFDIR ) == 0 ) return ( strcpy( malloc((unsigned)(strlen(file_name) + 1)), file_name) ); path = path + len_next + 1 ; } while( path[ -1 ] != '\0' ) ; /* return( NULL ) ; */ return( file ); /* If PATH is NULL, return FILE */ #endif } /* --------------------------------------------------------------------------------- Return the Option File Name, given the Name of the Application (Unix Version) Edited: --------------------------------------------------------------------------------- */ char *UnixOptionFileName(appn) char *appn ; { char *temp; temp = malloc((unsigned) (strlen(appn) + strlen(OPTION_FILE_SUFFIX) + strlen(OPTION_FILE_PREFIX) + 1)); temp = strcpy(temp,OPTION_FILE_PREFIX); temp = strcat(temp,appn); temp = strcat(temp,OPTION_FILE_SUFFIX); return (temp); } #endif /* Unix Option File Stuff */ #if defined( PC ) /* --------------------------------------------------------------------------------- Return the Option File Name, given the Name of the Application (PC Version) Edited: --------------------------------------------------------------------------------- */ char *PCOptionFileName(appn, onlyLocal) char *appn ; int onlyLocal; { char *temp; int i ; char *temp2, *lastPart; temp = malloc((unsigned) (strlen(appn) + strlen(OPTION_FILE_SUFFIX) + strlen(OPTION_FILE_PREFIX) + 1)); temp = strcpy(temp, appn); for (i = 0; temp[i] != NULL_CHARACTER; i++) if (temp[i] == BACKSLASH_CHAR) temp[i] = SLASH_CHARACTER; /* Strip Back to the Application Name */ if ((lastPart = strrchr(temp, SLASH_CHARACTER)) == NULL) lastPart = temp; else lastPart++; /* Skip over the actual "/" */ /* lastPart points to the tail name of the path */ if ((temp2 = strchr(lastPart,DOT)) == NULL) temp = strcat(temp,OPTION_FILE_SUFFIX); else temp2 = strcpy(temp2, OPTION_FILE_SUFFIX); if (onlyLocal) /* Remove the full path to give only a local name */ return( lastPart); else return (temp); } #endif /* PC Version of OptionFileName */ /* --------------------------------------------------------------------------------- Check that there is no ambiguity in the (possibly abbreviated) <name>. If there is no match whatever, return NO_MATCH. If there is a single exact match, or a single abbreviating match, return the index of the matched option; otherwise return AMBIGUOUS. --------------------------------------------------------------------------------- */ int Ambiguity(options, numOptions, name) Option options[] ; int numOptions; char *name ; { int i, matched, matchedExactly, matchedWhere, moreThanOneInexactMatch; matched = FALSE; matchedExactly = FALSE; moreThanOneInexactMatch = FALSE; for (i = 0; i < numOptions; i++) if (options[i].classification != OutputOption && strncmp(name, options[i].name, strlen(name)) == 0) { if (matched) { if (matchedExactly) { if (strcmp(name, options[i].name) == 0) { /* TWO Exact Matches */ return AMBIGUOUS; } } else { if (strcmp(name, options[i].name) == 0) { /* THIS is an exact match */ matchedExactly = TRUE; matchedWhere = i; } else { /* There are two inexact matches ... if there are no exact ones we have AMBIGUITY */ moreThanOneInexactMatch = TRUE; } } } else { if (strcmp(name, options[i].name) == 0) { /* This is an exact match */ matchedExactly = TRUE; } matched = TRUE; matchedWhere = i ; } } if (moreThanOneInexactMatch) return (AMBIGUOUS); else { if (matched) return (matchedWhere); else return (NO_MATCH); } } /* --------------------------------------------------------------------------------- Search for the named option in the list of options. If it is found, return its location (index) in the options array. Otherwise, return NO_MATCH. --------------------------------------------------------------------------------- */ int LookUp(options, numOptions, name) Option options[] ; int numOptions; char *name ; { int i; for (i = 0; i < numOptions; i++) if (options[i].classification != OutputOption && strcmp(name, options[i].name) == 0) { return (i); } return (NO_MATCH); } extern helpRoutine *onLineHelpHandler; extern void updateOptionsFile(); /* --------------------------------------------------------------------------------- Search for the named option in the list of options. If it is found, set its new value as indicated and return TRUE. If not, return either FALSE or AMBIGUOUS. Edited: 8th March 1989 (Paul Manson) Changed the call to LookUp to call Ambiguity instead, thus achieving the matching of abbreviated options. 15th March 1989 (Paul Manson) Reinstated the call to LookUp in the case that abbreviations (and hence ambiguity) are/is not permitted (ie. in option Header Files). --------------------------------------------------------------------------------- */ int LookUpAndStore(options, numOptions, name, value, permitAbbrevs, programName) Option options[] ; int numOptions ; char *name, *value; int permitAbbrevs; char *programName ; { int loc ; char *temp; /* Account for specific references to the "help" option, with different treatment of the "help=all" command */ if (strcmp(name, HELP_STRING) == 0) { /* Check whether it is asking for ALL help */ if (strcmp(value, ALL_STRING) == 0) { helpOnAllRequired = TRUE; helpOnOneRequired = FALSE; (*onLineHelpHandler)(options, numOptions, programName); exit(0); } /* Check for other specific kinds of help request */ /* else if (strcmp(value, _STRING) == 0) { */ else { /* Expand the (possibly abbreviated) option name */ if ((permitAbbrevs && ((loc = Ambiguity(options, numOptions, value)) >= 0)) || (!permitAbbrevs && ((loc = LookUp(options, numOptions, value)) >= 0))) { helpOnOneRequired = TRUE; helpOnAllRequired = FALSE; helpOnOneLocation = loc ; (*onLineHelpHandler)(options, numOptions, programName); exit(0); } else { if (loc == AMBIGUOUS) { (void) fprintf(stderr, "WARNING: The option name %s is AMBIGUOUS; please be more specific.\n", value); exit(1); } else { (void) fprintf(stderr, "WARNING: The %s program does not have an option named %s.\n", programName, value); exit(1); } } } } else if (strcmp(name, UPDATE_STRING) == 0) { /* Check whether it is asking for ALL updated */ if (strcmp(value, ALL_STRING) == 0) { updateAllRequired = TRUE ; updateOptionsFile(options, numOptions, programName, theOptionFileName); updateAllRequired = FALSE; helpOnAllRequired = TRUE ; helpOnOneRequired = FALSE; return (TRUE); } else { /* At present there is no use for a specific update call */ (void) fprintf(stderr, "options: The update option may not be altered!\n"); exit(1); } } else if ((permitAbbrevs && ((loc = Ambiguity(options, numOptions, name)) >= 0)) || (!permitAbbrevs && ((loc = LookUp(options, numOptions, name)) >= 0))) { /* It is a conventional "LookUpAndStore" operation */ temp = malloc((unsigned)(strlen(value) + 1)); temp = strcpy(temp, value); *(options[loc].value) = temp; return (TRUE); } else { if (loc == AMBIGUOUS) { #if defined( PC ) (void) fprintf(stderr, "ERROR: The name %s is AMBIGUOUS, and the value %s has not been assigned to any option\n", name, value); exit(1); #else (void) fprintf(stderr, "WARNING: The name %s is AMBIGUOUS, and the value %s has not been assigned to any option\n", name, value); return (AMBIGUOUS); #endif } else return (FALSE); } } /* --------------------------------------------------------------------------------- Skip Blanks and Tabs. Stop at a printable character or a newline or null. --------------------------------------------------------------------------------- */ char *skipBlanks (ptr) char *ptr; { while ((*ptr == BLANK_CHARACTER) || (*ptr == TAB_CHARACTER)) ptr++; return (ptr); } /* --------------------------------------------------------------------------------- Ensure that the next character is an '='. If it is, return a pointer to the first character after the '='; otherwise, return NULL. --------------------------------------------------------------------------------- */ char *skipEquals (ptr) char *ptr; { if (ptr[0] == EQUALS_CHARACTER) return (++ptr); else return (NULL); } /* --------------------------------------------------------------------------------- Read a name (ie. a token, a contiguous sequence of nonblank characters, or a quoted string) from the string pointed to by ptr, and copy it the buffer pointed to by <name>. If successful, return a pointer to the character after the token; otherwise, return NULL. --------------------------------------------------------------------------------- */ char *getName (ptr, name) char *ptr, *name; { char *temp, *i, *namePtr; char stopChar; if (ptr[0] == DOUBLE_QUOTE_CHAR) { ptr++; stopChar = DOUBLE_QUOTE_CHAR; } else stopChar = BLANK_CHARACTER; temp = ptr; while (*temp != NULL_CHARACTER && *temp != NEWLINE_CHARACTER && *temp != CARRIAGE_RETURN && ((stopChar == DOUBLE_QUOTE_CHAR && *temp != stopChar) || (stopChar == BLANK_CHARACTER && (*temp != stopChar && *temp != TAB_CHARACTER && *temp != EQUALS_CHARACTER)))) temp++; if (stopChar == DOUBLE_QUOTE_CHAR && *temp != DOUBLE_QUOTE_CHAR) { (void) fprintf(stderr, "options: Improperly closed quoted string %s\n", --ptr); return (NULL); } namePtr = name; for (i = ptr; i < temp; ) *namePtr++ = *i++; *namePtr = NULL_CHARACTER; if (stopChar == DOUBLE_QUOTE_CHAR) temp++; return (temp); } /* ------------------------------------------------------------------------------ Read lines from the specified file, updating option settings as appropriate. Edits: 29th March 1989 (Paul Manson) Altered so that trailing comments allowed. ------------------------------------------------------------------------------ */ void processOptionFile (options, numOptions, OptionFile, theOptionFileName, limitedChars, checkOptions, programName) Option options[] ; int numOptions ; FILE *OptionFile ; char *theOptionFileName; int limitedChars ; int checkOptions ; char *programName ; { int result ; char name[MAX_NAME_LENGTH] ; char value[MAX_NAME_LENGTH] ; char *line, *linePtr; int maxCharsToRead ; int totalCharsRead ; result = EOF; totalCharsRead = 0 ; line = malloc((unsigned) MAX_LINE_LENGTH + 1); maxCharsToRead = ((limitedChars) ? (min(MAX_LINE_LENGTH, limitedChars)) : MAX_LINE_LENGTH); while (maxCharsToRead > 0 && ((line = fgets(line, maxCharsToRead + 1, OptionFile)) != NULL)) { if (line[0] == NULL_CHARACTER) break; /* In the case of a Option Header */ else if (limitedChars) { totalCharsRead += strlen(line); maxCharsToRead = min(MAX_LINE_LENGTH, limitedChars - totalCharsRead); } linePtr = skipBlanks(line); /* Check for Comments and Blank Lines */ if ((linePtr[0] == COMMENT_CHARACTER) || (linePtr[0] == NEWLINE_CHARACTER) || (linePtr[0] == CARRIAGE_RETURN ) || (linePtr[0] == NULL_CHARACTER)) continue; if ((linePtr = getName(linePtr, name)) == NULL) { (void) fprintf(stderr, "options: Missing or Invalid name\n"); result = ERROR_RESULT; break; } linePtr = skipBlanks(linePtr); if ((linePtr = skipEquals(linePtr)) == NULL) { (void) fprintf(stderr, "options: Expected an '=' after the name %s\n", name); result = ERROR_RESULT; break; } linePtr = skipBlanks(linePtr); if ((linePtr = getName(linePtr, value)) == NULL) { (void) fprintf(stderr, "options: Missing or Invalid value; name is %s\n", name); result = ERROR_RESULT; break; } linePtr = skipBlanks(linePtr); if ((*linePtr != NEWLINE_CHARACTER) && (*linePtr != NULL_CHARACTER) && (*linePtr != COMMENT_CHARACTER) && (*linePtr != CARRIAGE_RETURN)) { (void) fprintf(stderr, "options: Detected Trailing Garbage in the line %s = %s\n", name, value); result = ERROR_RESULT; break; } if (!LookUpAndStore(options, numOptions, name, value, checkOptions, programName) && checkOptions) { (void) fprintf(stderr, "options: Could not find Option Called %s\n", name); result = ERROR_RESULT; break; } } if (result != EOF) { (void) fprintf(stderr,"options: Error reading the Option File %s\n", theOptionFileName); (void) fprintf(stderr,"options: Remainder of File is as follows;\n"); (void) fputs(line, stderr); while ((line = fgets(line, MAX_LINE_LENGTH, OptionFile)) != NULL) (void) fputs(line, stderr); exit(1); } } /* ------------------------------------------------------------------------------ OnLine Help Facility. This is simply the default handler which is to be called if the user specifies no replacement handler. ------------------------------------------------------------------------------ */ static void defaultHelpHandler(options, numOptions, progName) Option options[] ; int numOptions; char *progName ; { register int help, res; if (helpOnOneRequired) { res = helpOnOneLocation; (void) fprintf(stdout, "%-13s %-9s %-9s %s\n", options[res].name, ((*(options[res].value) == NULL) ? ("") : (*(options[res].value))), ((options[res].defaultValue == NULL) ? ("") : (options[res].defaultValue)), ((options[res].comment == NULL) ? ("") : (options[res].comment))); return; } help = -1; for (res = 0; res < numOptions; res++) if (strcmp(options[res].name, HELP_STRING) == 0) { help = res; break ; }; /************* Print head of help menu *****************/ if (help < 0) { (void) fprintf(stdout, "The Rotter who wrote the `%s' program hasn't provided any \n", progName); (void) fprintf(stdout, "form of on-line 'help' documentation, tsk tsk!\n\n"); (void) fprintf(stdout, "We can, however, provide you with the list of available options:\n\n"); } else { (void)fprintf(stdout,"%s\n", versionstr); (void) fprintf(stdout, "Usage: %s [options] %s\n\n", progName, options[help].comment); } (void) fprintf(stdout, "Option Name Current Default Comment\n"); (void) fprintf(stdout, "----------- ------- ------- -------\n"); for (res = numOptions-1 ; res >= 0 ; res-- ) if (options[res].classification < OutputOption && (!thereWasAnOptionsFile || helpOnAllRequired || strcmp(*(options[res].value), options[res].defaultValue) != 0 || *(options[res].value) != options[res].defaultValue)) (void) fprintf(stdout, "%-13s %-9s %-9s %s\n", options[res].name, ((*(options[res].value) == NULL) ? ("") : (*(options[res].value))), ((options[res].defaultValue == NULL) ? ("") : (options[res].defaultValue)), ((options[res].comment == NULL) ? ("") : (options[res].comment))); (void) fprintf(stdout, "\n"); return; } /* ----- Declare the handlerHolder variable ----- */ static helpRoutine *onLineHelpHandler = defaultHelpHandler; /* ------------------------------------------------------------------------------ Spit out the options to a file (with the appropriate option file name). Edits: 29th March 1989 (Paul Manson). For the sake of IBM/PC users, this routine will now spit lines with CR/LF pairs. 06th April 1989 (Paul Manson). This routine is no-longer conditionally compiled, but is now more widely accepted. ------------------------------------------------------------------------------ */ void updateOptionsFile(options, numOptions, programName, resFileName) Option options[] ; int numOptions ; char *programName; char *resFileName; { int res ; FILE *fptr; if ((fptr = fopen(resFileName, "w")) == NULL) { (void) fprintf(stderr, "Unable to Update Options File called %s.\n", resFileName); exit(1); } #if defined( PC ) || defined( THINK_C ) (void) fprintf(fptr ,"# %s\n", versionstr); (void) fprintf(fptr, "#\r\n# Options file for the %s program.\r\n#\r\n\r\n", programName); (void) fprintf(fptr, "#Option Name Current Default Comment\r\n"); (void) fprintf(fptr, "#----------- ------- ------- -------\r\n\r\n"); #else (void) fprintf(fptr ,"# %s\n", versionstr); (void) fprintf(fptr, "#\n# Options file for the %s program.\n#\n\n", programName); (void) fprintf(fptr, "#Option Name Current Default Comment\n"); (void) fprintf(fptr, "#----------- ------- ------- -------\n\n"); #endif for (res = numOptions-1; res >=0 ; res-- ) if (options[res].classification < OutputOption && *(options[res].value) != NULL && (updateAllRequired || strcmp(*(options[res].value), options[res].defaultValue) != 0 || *(options[res].value) != options[res].defaultValue)) { #if defined( PC ) (void) fprintf(fptr, "%-13s = %-9s # %-9s %s\r\n", options[res].name, ((*(options[res].value) == NULL) ? ("") : (*(options[res].value))), ((options[res].defaultValue == NULL) ? ("") : (options[res].defaultValue)), ((options[res].comment == NULL) ? ("") : (options[res].comment))); #else (void) fprintf(fptr, "%-13s = %-9s # %-9s %s\n", options[res].name, ((*(options[res].value) == NULL) ? ("") : (*(options[res].value))), ((options[res].defaultValue == NULL) ? ("") : (options[res].defaultValue)), ((options[res].comment == NULL) ? ("") : (options[res].comment))); #endif } if (fclose(fptr) == EOF) { (void) fprintf(stderr, "options: fclose failed on the options file %s.\n", resFileName); exit(1); } thereWasAnOptionsFile = TRUE; /* Cos there is one now! */ return; } /* ------------------------------------------------------------------------------ Process the option file and then the command line options supplied. Edited: 2nd March 1989 (Paul Manson). Changed the calls to OptionFileName (ie. added a <prefix> argument) to facilitate searching of HOME (etc) directories. Note that this is IGNORED for IBM/PCs. 8th March 1989 (Paul Manson). Ignore the above, as OPTIONSPATH is now used (in conjunction with search_path) to enable proper paths for Unix. 10th March 1989 (Paul Manson). Added the "help" facility and the default and comment stuff. 16th March 1989 (Paul Manson). Altered the final argc/argv shuffling so that argv simply points at the first argument after the options. July 1994 (MAA). Mended to allow spaces in command-line strings. ------------------------------------------------------------------------------ */ /* Assign the default options */ void default_nopts( options, numOptions ) Option options[] ; int numOptions ; { int i ; for (i = 0; i < numOptions; i++) *(options[i].value) = options[i].defaultValue ; } /**************************************************************************** * Assign options from the rc file. * * The option file is called ".genxxxrc", where the "xxx" is determined * by the name of the application, (which is in argv[0]). * Here "rc" stands for "resource control". * The file-name is assigned to a string ptr "theOptionFileName". * On Unix, the path for the file-name is determined as follows: * if environment variable OPTION_PATH is set, then use this path. * else the default path is: * `.' (the current directory) * `~' (the users home directory) * The DEFAULT_OPTION_PATH for the option file is defined "." (see above). * This means look first in the current directory, and failing that in the * users home directory. * But these defaults can be overridden by setting an environment variable, * which is defined as OPTIONSPATH (see above). Thus the user could set a * new default path for option files by, eg: * setenv OPTIONSPATH ".:~/rcbin" * This would look first in the current directory, and failing that in a * directory ~/rcbin *****************************************************************************/ void rcfile_nopts( options, numOptions, file ) Option options[] ; int numOptions ; char *file ; { FILE *OptionFile ; char *OptionPathPtr ; #if defined( PC ) if (((OptionFile = fopen(theOptionFileName = PCOptionFileName( file, TRUE ), "r")) != NULL)) { /* ------------------------------------------------------------------------------------- Removed temporarily so the PC version ONLY examines and creates option files in the cwd. This will be changed when we determine a better strategy for PC directory PATH searches. ((OptionFile = fopen(theOptionFileName = PCOptionFileName( file, FALSE), "r")) != NULL)) { ---------------------------------------------------------------------------------------*/ #else if ((OptionPathPtr = (char *)getenv(OPTION_PATH)) == NULL) OptionPathPtr = DEFAULT_OPTION_PATH; if ((OptionFile = fopen(theOptionFileName = search_path(UnixOptionFileName( file ), OptionPathPtr), "r")) != NULL) { #endif /* Process the Option File */ (void) processOptionFile(options, numOptions, OptionFile, theOptionFileName, FALSE, TRUE, file ) ; (void) fclose(OptionFile); thereWasAnOptionsFile = TRUE; } } /* Assign options from the command line */ void cmd_line_nopts( options, numOptions, argc, argv ) Option options[] ; int numOptions ; int *argc ; char ***argv ; { int i, currentArg ; int finalArg, wasAHyphen ; char name[MAX_NAME_LENGTH] ; char value[MAX_NAME_LENGTH] ; char *line, *linePtr; char *tempstring; /* MAA 23-4-1994 */ char *tempstring2; /* MAA 23-4-1994 */ int count=0; /* MAA: next 3 lines */ linePtr = (char *) calloc((size_t) MAX_NAME_LENGTH, (size_t) sizeof(char)); tempstring = (char *) calloc((size_t) MAX_NAME_LENGTH, (size_t) sizeof(char)); tempstring2 = (char *) calloc((size_t) MAX_NAME_LENGTH, (size_t) sizeof(char)); finalArg = currentArg = 1; /* Ignore the Program Name */ line = malloc((unsigned) MAX_LINE_LENGTH); while (currentArg < *argc) { wasAHyphen = FALSE; linePtr = strcpy(line, (*argv)[currentArg]); if (linePtr[0] == HYPHEN_CHARACTER) { wasAHyphen = TRUE; linePtr++; /* Skip any leading hyphen */ } /* MAA: force doublequotes onto beg & end . A rather big hack ...*/ if (strchr(linePtr, BLANK_CHARACTER) != NULL){ strncpy(tempstring, linePtr, (int) strcspn(linePtr, "=")); strcat(tempstring, "=\""); tempstring2 = (char *) strrchr(linePtr, EQUALS_CHARACTER); tempstring2++; strcat(tempstring, tempstring2); strcat(tempstring, "\""); linePtr=tempstring;} if ((linePtr = getName(linePtr, name)) == NULL) { break; } if ((linePtr = skipEquals(linePtr)) == NULL) { if (!wasAHyphen) { break; /* Don't interpret <name> <value> as being options */ } if (strcmp(name, HELP_STRING) == 0) { #ifdef PC if (strncmp((*argv)[0]+strlen((*argv)[0])-strlen("gen.exe"), "gen", strlen("gen")) == 0) #else if (strcmp((*argv)[0], "gen") == 0) #endif /* Special case of gen -help */ usageHelp(); else /* Case of gen??? -help */ (*onLineHelpHandler)(options, numOptions, (*argv)[0]); exit(0); } if (strcmp(name, UPDATE_STRING) == 0) { /* Do the Update and Then Goto the Top of the Loop Again */ updateOptionsFile(options, numOptions, (*argv)[0], theOptionFileName); finalArg = ++currentArg; continue; } (void) strcpy(value, DEFAULT_TOGGLE_VALUE); } else { if ((linePtr = getName(linePtr, value)) == NULL) { break; } } if(!LookUpAndStore(options, numOptions, name, value, TRUE, (*argv)[0])) { (void) fprintf(stderr,"options: Could not find an Option called %s\n", name); exit(1); } finalArg = ++currentArg; } /* Reset the Command Line argc and argv */ /* The Original Version actually cut the options out of the argv array (by shifting the remaining arguments left), but the new version simply lets argv point to the next argument to be processed. if (finalArg > 1) { for (i = finalArg; i < *argc; i++) (*argv)[i - finalArg + 1] = (*argv)[i]; *argc = *argc - finalArg + 1; } */ *argv+= finalArg; *argc = *argc - finalArg; return; } /* Assign options using defaults, then the rc file, then the command line */ void getnopts (options, numOptions, argc, argv) Option options[] ; int numOptions ; int *argc ; char ***argv ; { default_nopts( options, numOptions ) ; rcfile_nopts( options, numOptions, (*argv)[0] ) ; cmd_line_nopts( options, numOptions, argc, argv ) ; } /* ------------------------------------------------------------------------------- Read an option header from the specified file into the given options array. ------------------------------------------------------------------------------- */ void readnopts(options, numOptions, filePointer) Option options[] ; int numOptions; FILE *filePointer ; { long filePosition ; char first[200] ; int headerLength ; filePosition = ftell(filePointer); /* Save the Original Position */ /* Read the first line of the first block, or as much of it as possible */ if ((!fread(first, 1, strlen(OptionHeaderHeader) + OPTION_HEADER_DIGITS + strlen(LineFeedArray), filePointer)) || (strncmp(first, OptionHeaderHeader, strlen(OptionHeaderHeader)) != 0)) { /* Assume that this is NOT a option header */ if (fseek(filePointer, filePosition, 0)) { (void) fprintf(stderr, "options: Error fseeking Option Header File\n"); exit(1); } return; } /* Extract the Header Length in Bytes */ headerLength = atoi(first + strlen(OptionHeaderHeader)); /* Now, process the stuff. Need to "seek" past the first line before attempting to read anything, and shorten the headerLength accordingly */ (void) processOptionFile(options, numOptions, filePointer, "Unknown Option File", (headerLength - strlen(OptionHeaderHeader) - OPTION_HEADER_DIGITS - strlen(LineFeedArray)), FALSE, "???"); /* Make sure that the file pointer is set to the data area */ if (fseek(filePointer, filePosition + headerLength, 0)) { (void) fprintf(stderr, "options: Error fseeking Option Header File\n"); exit(1); } } /* ------------------------------------------------------------------------------- Count the number of bytes which will be written by writenopts, given the options array. ------------------------------------------------------------------------------- */ static int countBytesToWrite( options, numOptions ) Option options[] ; int numOptions; { int sum, i; sum = 0; for (i = 0; i < numOptions; i++) if((options[i].classification == OutputOption || options[i].classification == InOutOption) && *(options[i].value) != NULL && strcmp(*(options[i].value), NULL_STRING) != 0) sum += strlen(options[i].name) + strlen(EqualsArray) + strlen(*(options[i].value)) + strlen(LineFeedArray); /* + (2 * strlen(QuotesArray)); */ return (sum); } /* ------------------------------------------------------------------------------- The descendent of the lost and lamented blockCopyStrToFile... writes a string to the indicated file. ------------------------------------------------------------------------------- */ static void WriteStringToFile(str, filePtr) char *str; FILE *filePtr; { if (!fwrite(str, 1, strlen(str), filePtr)) { (void) fprintf(stderr, "WriteStringToFile: Error printing %s to file.\n", str); exit(1); } return; } /* ------------------------------------------------------------------------------- Write an option header to the specified file from the information contained in the given options array. Remove the comments to get debugging info. ------------------------------------------------------------------------------- */ void writenopts(options, numOptions, filePointer) Option options[] ; int numOptions; FILE *filePointer ; { /*int bytesWritten ;*/ int i ; long filePosition ; int bytesToWrite ; int oddHeaderLength ; char DigitString[OPTION_HEADER_DIGITS + 2]; filePosition = ftell(filePointer); bytesToWrite = countBytesToWrite(options, numOptions) + strlen(OptionHeaderHeader) + OPTION_HEADER_DIGITS + strlen(LineFeedArray) + 1; if (oddHeaderLength = (bytesToWrite % 2)) bytesToWrite++; (void) WriteStringToFile(OptionHeaderHeader, filePointer); (void) sprintf(DigitString, "%0*d", OPTION_HEADER_DIGITS, bytesToWrite); /*(void) printf("bytesToWrite is %i.\n", bytesToWrite);*/ (void) WriteStringToFile(DigitString, filePointer); (void) WriteStringToFile(LineFeedArray, filePointer); /* Copy all of the Options */ for (i = 0; i < numOptions; i++) { if((options[i].classification == OutputOption || options[i].classification == InOutOption) && *(options[i].value) != NULL && strcmp(*(options[i].value), NULL_STRING) != 0) { (void) WriteStringToFile(options[i].name, filePointer); (void) WriteStringToFile(EqualsArray, filePointer); /* (void) WriteStringToFile(QuotesArray, filePointer); */ (void) WriteStringToFile(*(options[i].value), filePointer); /* (void) WriteStringToFile(QuotesArray, filePointer); */ (void) WriteStringToFile(LineFeedArray, filePointer); } } if (fputc(NULL_CHARACTER, filePointer) == EOF) { (void) fprintf(stderr, "writenopts: Error doing fputc of the last NULL character.\n"); exit(1); } if (oddHeaderLength && fputc(NULL_CHARACTER, filePointer) == EOF) { (void) fprintf(stderr, "writenopts: Error doing fputc of the odd-pad NULL character.\n"); exit(1); } if (fflush(filePointer)) { (void) fprintf(stderr, "options: Error while flushing the option header\n"); exit(1); } /* seek the fileptr to after the last byte of the header */ if (filePointer != stdout) { if (fseek(filePointer, filePosition + (long)bytesToWrite, 0)) { (void) fprintf(stderr, "options: Error fseeking Option Header File\n"); exit(1); } } } /* ---------------------------------------------------------------------------- A Little Routine to Count the Options in a Options Array .... This MUST Be NULL-Terminated ...... OR ELSE! ---------------------------------------------------------------------------- */ static int countOptions(options) Option options[]; { int numoptions; for (numoptions = 0; options[numoptions].name != NULL; /* DEATH TO ALL WHO TREAD HERE! */ numoptions++); return (numoptions); } /* ------------------------------------------------------------------------------- The Counting version of getnopts -- you MUST null-terminate the options array. ------------------------------------------------------------------------------- */ void getopts (options, argc, argv) Option options[] ; int *argc ; char ***argv ; { (void) getnopts(options, countOptions(options), argc, argv); } void cmd_line_opts(options, argc, argv) Option options[] ; int *argc ; char ***argv ; { (void) cmd_line_nopts( options, countOptions(options), argc, argv ) ; } /* ------------------------------------------------------------------------------- The Counting version of readnopts -- you MUST null-terminate the options array. ------------------------------------------------------------------------------- */ void readopts(options, filePointer) Option options[] ; FILE *filePointer ; { (void) readnopts(options, countOptions(options), filePointer); } /* ------------------------------------------------------------------------------- The Counting version of writenopts -- you MUST null-terminate the options array. ------------------------------------------------------------------------------- */ void writeopts(options, filePointer) Option options[] ; FILE *filePointer ; { (void) writenopts(options, countOptions(options), filePointer); } /* ------------------------------------------------------------------------------- Packaging for convenience -- Guaranteed to be Listeria-free! ------------------------------------------------------------------------------- */ FILE *fileopts( res, argc, argv ) Option *res ; int *argc ; char **argv[]; { FILE *ifp ; int numOptions; #if defined( PC ) char *progName ; progName = (*argv)[0]; #endif numOptions = countOptions(res); getnopts( res, numOptions, argc, argv ) ; if( *argc > 0 ) { if((ifp = fopen( **argv, "r" )) == NULL) { (void) fprintf(stderr, "Unable to open the file %s.\n", **argv); exit(1); } } else #if defined( PC ) { /* PCs cannot take binmode files through standard input */ (void) (*onLineHelpHandler)( res, numOptions, progName ); exit(1); } #else ifp = stdin ; #endif (*argv)++ ; (*argc)-- ; readopts( res, ifp ) ; return( ifp ) ; } /* ------------------------------------------------------------------------------- Packaging for convenience -- Guaranteed to be Listeria-free! ------------------------------------------------------------------------------- */ FILE *optoutput(str) char *str; { FILE *f; if (str == NULL) return (NULL); else if (str == DEFAULT_TOGGLE_VALUE) return (stdout); if ((f = fopen(str, "w")) == NULL) { (void) fprintf(stderr, "options: Could not open the file %s for output.\n", str); exit(1); } return (f); } /* ------------------------------------------------------------------------------- OnLine Help from the user program. ------------------------------------------------------------------------------- */ void helpnopts(res, numRes, name) Option res[]; int numRes; char *name ; { (void) (*onLineHelpHandler)(res, numRes, name); } /* ------------------------------------------------------------------------------- OnLine Help from the user program. (counting version) ------------------------------------------------------------------------------- */ void helpopts(res, name) Option res[]; char *name ; { #ifdef PC if (strncmp(name+strlen(name)-strlen("gen.exe"), "gen", strlen("gen")) == 0) #else if (strcmp(name, "gen") == 0) #endif /* Special case of gen */ usageHelp(); else /* Case of gen??? */ (void) (*onLineHelpHandler)(res, countOptions(res), name); } /* --------------------------------------------------------------------------------- Allows an application program to install its very own helpHandler routine to override the default help-message handler. The behaviour of such a routine is implicitly trusted by <options>, so you should only install your own handler when you are quite familiar with the Options structure format. This routine returns the address of the previously installed handler (which is initially the default handler) so that this may be reinstated at a later point. ---------------------------------------------------------------------------------- */ helpRoutine *helpOptsHandler(aHelpRoutine) helpRoutine *aHelpRoutine; { helpRoutine *temp; temp = onLineHelpHandler; onLineHelpHandler = aHelpRoutine; return (temp); } /* --------------------------------------------------------------------------------- A convenience call which returns TRUE (1) if the optionStr is what options thinks of as "ON", "YES", "TRUE", or "1".... This merely allows us to hide the actual implementation of "ON" from the application programs. --------------------------------------------------------------------------------- */ int isOFF(optionStr) char *optionStr; { return (strcmp(OFF_STRING, optionStr) == 0 || strcmp(OFF_ALIAS , optionStr) == 0 || strcmp(NOT_USED , optionStr) == 0); } /* --------------------------------------------------------------------------------- A convenience call which returns TRUE (1) if the optionStr is what options thinks of as "NULL", "NONE", nothing, zip, etc... This merely allows us to hide the actual implementation of "NULL" from the application programs. --------------------------------------------------------------------------------- */ int isNULL(optionStr) char *optionStr; { return (optionStr == NULL || strcmp(NULL_STRING, optionStr) == 0 || strcmp(NULL_ALIAS , optionStr) == 0); } /* --------------------------------------------------------------------------------- A routine to reduce the load on the application programmer by providing a simple boolean check for string equality WITHOUT CASE SIGNIFICANCE. This allows applications to leave case and typing problems within "options" reliably. --------------------------------------------------------------------------------- */ static char *stringToLowerCase(str) char *str; { register int i; if (str == NULL) return (str); else { for (i = 0; str[i] != NULL_CHARACTER; i++) if (isupper((char) str[i])) str[i] = (char) tolower((char) str[i]); return (str); } } extern int OptionStringsEqual(str1, str2) char *str1, *str2; { /* This routine copies the strings into two local buffers, converts them to lower case, and then compares them for equality. This local copy ensures that they are automatically deallocated on function return */ #define MAX_COPY_LENGTH (256) char lc1[MAX_COPY_LENGTH], lc2[MAX_COPY_LENGTH]; if (strlen(str1) < MAX_COPY_LENGTH && strlen(str2) < MAX_COPY_LENGTH) return (strcmp(stringToLowerCase(strcpy(lc1, str1)), stringToLowerCase(strcpy(lc2, str2))) == 0); else { (void) fprintf(stderr, "options: OptionStringsEqual() could not compare the strings %s and %s becaues one was more that %i characters long.\n", str1, str2, MAX_COPY_LENGTH); exit(1); } } /* Print general usage information in response to: gen -help */ usageHelp() { (void)fprintf(stdout,"%s\n\n", versionstr); (void) fprintf(stdout, "Usage: genXXX [options] [file_name]\n"); (void) fprintf(stdout, " where XXX is one of the following abbreviations:\n\n\ wav waveform \n\ bmm basilar membrane motion \n\ nap neural activity pattern \n\ sai stabilized auditory image \n\ spl spiral version of auditory image \n\ sgm spectrogram \n\ cgm cochleogram " ) ; (void) fprintf(stdout, "\n\ asa auditory spectral analysis \n\ epn excitation pattern \n\n"); (void) fprintf(stdout, " [file_name] is a headerless wave file (2-byte binary integers).\n"); (void) fprintf(stdout, " [options] are parameters, options and swtiches that control\n"); (void) fprintf(stdout, " the AIM instructions and the AIM tools.\n\n"); (void) fprintf(stdout, " Help with options: genXXX [-help | -help=all | -help=option]\n"); #if !defined( PC ) (void) fprintf(stdout, " Path for options file, (.genXXXrc) = . (or setenv OPTIONSPATH)\n\n"); #endif (void) fprintf(stdout, "Processes Applied by AIM and Routes Through the Model: \n\n\ Process Auditory Speech Spectra \n\ -------------------------------- -------- ------ ------- \n\ Display input wave genwav genwav genwav \n\ Auditory filtering (gtf/tlf) genbmm \n\ Compression and rectification gensgm genasa " ) ; (void) fprintf(stdout, "\n\ Neural encoding (2D-AT/haircell) gennap \n\ Temporal integration (LP filter) gencgm genepn \n\ Strobed temporal integration gensai \n\ Spiral mapping of auditory image genspl \n\n"); (void) fprintf(stdout, "Output: genXXX output=on file_name \n"); (void) fprintf(stdout, " output is written to file: file_name.XXX \n"); (void) fprintf(stdout, "\n\ The format for 2-dimensional output format is by columns, with the \n\ lowest channel first in each column (bmm, nap, sgm, cgm, asa, epn).\n\ The format for auditory image output is by rows, for each image frame \n\ in succession, with the row of the lowest channel first (sai, spl).\n\n"); (void) fprintf(stdout, "The Auditory Image Model was developed at the Applied Psychology Unit\n"); (void) fprintf(stdout, "of the Medical Research Council, 15 Chaucer Road, Cambridge, U.K.\n\n"); (void) fprintf(stdout, "Copyright(c) Applied Psychology Unit, Medical Research Council, 1988-1995.\n"); }