diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/glib/options.c	Fri May 20 15:19:45 2011 +0100
@@ -0,0 +1,1624 @@
+/*
+    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");
+
+}