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");

}