 /*
  * Khoros: $Id: readprog.c,v 1.3 1991/12/18 09:58:20 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: readprog.c,v 1.3 1991/12/18 09:58:20 dkhoros Exp $";
#endif

 /*
  * $Log: readprog.c,v $
 * Revision 1.3  1991/12/18  09:58:20  dkhoros
 * HellPatch3
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER 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 PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "ghost.h"
#include "vinclude.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>      Ghostwriter Information Extracton Routines       <<<<
   >>>>                                                       <<<<
   >>>>  File Name: readprog.c				      <<<<
   >>>>                                                       <<<<
   >>>>		gw_read_progfile()			      <<<<
   >>>>         gw_copyright()                                <<<<
   >>>>		gw_get_prog_info()			      <<<<
   >>>>      	gw_prog()				      <<<<
   >>>>         gw_getstring()				      <<<<
   >>>>         gw_newlist()				      <<<<
   >>>>         gw_addtolist()				      <<<<
   >>>>         gw_length()				      <<<<
   >>>>         gw_freelist()				      <<<<
   >>>>         gw_copylist()				      <<<<
   >>>>                                                       <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


/************************************************************
*
*  Routine Name: gw_read_progfile
*
*      Purpose:  This routine reads the program specification 
*		 provided in the .prog file.
*		 It fills out the global **prog_spec array of character blocks,
*		 each of which is referenced by an index with a #define'd name
*		 that indicates the key from which it came.
*
*     Comments:  For instance, after gw_get_prog_info() is called successfully,
*		 prog_spec[Authors] will hold the block of text taken from
*	         the .prog file, between the begin key of "-authors" and the
*		 end key of "-authors_end";  prog_spec[Restrictions] will 
*		 have the text found between keys "-restrictions" and 
*		 "-restrictions_end", and so on.
*
*        Output:  prog_spec - array of strings, each of which holds a block
*		 	     of text pulled from the .prog file.
*
*    Called by:  ghost main()
*
*   Written By:  Danielle Argiro & Per Lysne
*
*************************************************************/

char **gw_read_progfile(file, keynames)
FILE *file;
char **keynames[2];
{
    int status = 0, check_status();
    char **prog_spec;
    char *new_key_begin, *new_key_end;

    /* 
     * check for premature end of file 
     */
    if (feof(file))
           return(NULL);

    /* 
     * allocate program specification array 
     */
    prog_spec = (caddr_t *) kcalloc(1,sizeof(char *)*MaxProgSpecSize);
    if (prog_spec == NULL)
    {
	   fprintf(stderr, "gw_read_progfile:\n");
           fprintf(stderr, "Out of memory! Cannot calloc prog_spec\n");
           return(NULL);
    }

    /* 
     * get text blocks for each key in the .prog file 
     * get new key begin and key end (which may have some
     * capital letters) and over-write the old keys with them
     */

    prog_spec[Authors]      = gw_get_prog_info(file, 
			  	keynames[0][Authors], 
			  	keynames[1][Authors], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Authors, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Authors]), &(keynames[1][Authors]),
                 new_key_begin, new_key_end);

    prog_spec[ShortProgDesc]= gw_get_prog_info(file, 
				keynames[0][ShortProgDesc],
			  	keynames[1][ShortProgDesc], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, ShortProgDesc, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][ShortProgDesc]), &(keynames[1][ShortProgDesc]),
                 new_key_begin, new_key_end);

    prog_spec[ShortLibDesc]= gw_get_prog_info(file, 
				keynames[0][ShortLibDesc], 
				keynames[1][ShortLibDesc], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, ShortLibDesc, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][ShortLibDesc]), &(keynames[1][ShortLibDesc]),
                 new_key_begin, new_key_end);

    prog_spec[Man1LongDesc] = gw_get_prog_info(file, 
				keynames[0][Man1LongDesc], 
			 	keynames[1][Man1LongDesc], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man1LongDesc, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man1LongDesc]), &(keynames[1][Man1LongDesc]),
                 new_key_begin, new_key_end);
    
    prog_spec[Man1Examples] = gw_get_prog_info(file, 
				keynames[0][Man1Examples], 
			  	keynames[1][Man1Examples], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man1Examples, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man1Examples]), &(keynames[1][Man1Examples]),
                 new_key_begin, new_key_end);
    
    prog_spec[Man1Restrictions] = gw_get_prog_info(file, 
				keynames[0][Man1Restrictions], 
				keynames[1][Man1Restrictions], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man1Restrictions, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man1Restrictions]), 
		 &(keynames[1][Man1Restrictions]),
                 new_key_begin, new_key_end);
    
    prog_spec[Man1SeeAlso]   = gw_get_prog_info(file, 
				keynames[0][Man1SeeAlso], 
				keynames[1][Man1SeeAlso], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man1SeeAlso, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man1SeeAlso]), 
		 &(keynames[1][Man1SeeAlso]),
                 new_key_begin, new_key_end);

    prog_spec[Man3LongDesc]  = gw_get_prog_info(file, 
				keynames[0][Man3LongDesc],
				keynames[1][Man3LongDesc], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man3LongDesc, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man3LongDesc]), 
		 &(keynames[1][Man3LongDesc]),
                 new_key_begin, new_key_end);
   
    prog_spec[Man3Restrictions] = gw_get_prog_info(file, 
				keynames[0][Man3Restrictions], 
				keynames[1][Man3Restrictions], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man3Restrictions, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man3Restrictions]), 
		 &(keynames[1][Man3Restrictions]),
                 new_key_begin, new_key_end);
   
    prog_spec[Man3SeeAlso]   = gw_get_prog_info(file, 
				keynames[0][Man3SeeAlso], 
				keynames[1][Man3SeeAlso], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, Man3SeeAlso, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][Man3SeeAlso]), 
		 &(keynames[1][Man3SeeAlso]),
                 new_key_begin, new_key_end);

    prog_spec[UsageAdd]      = gw_get_prog_info(file, 
				keynames[0][UsageAdd], 
				keynames[1][UsageAdd], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, UsageAdd, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][UsageAdd]), &(keynames[1][UsageAdd]),
                 new_key_begin, new_key_end);

    prog_spec[IncludeIncludes]= gw_get_prog_info(file, 
				keynames[0][IncludeIncludes], 
				keynames[1][IncludeIncludes], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, IncludeIncludes, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][IncludeIncludes]), 
 		 &(keynames[1][IncludeIncludes]),
                 new_key_begin, new_key_end);


    prog_spec[IncludeAdd]    = gw_get_prog_info(file, 
				keynames[0][IncludeAdd], 
				keynames[1][IncludeAdd], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, IncludeAdd, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][IncludeAdd]), 
 		 &(keynames[1][IncludeAdd]),
                 new_key_begin, new_key_end);

    prog_spec[IncludeMacros] = gw_get_prog_info(file, 
				keynames[0][IncludeMacros], 
				keynames[1][IncludeMacros], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, IncludeMacros, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][IncludeMacros]), 
 		 &(keynames[1][IncludeMacros]),
                 new_key_begin, new_key_end);


    prog_spec[MainVariables] = gw_get_prog_info(file, 
				keynames[0][MainVariables], 
				keynames[1][MainVariables], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, MainVariables, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][MainVariables]), 
 		 &(keynames[1][MainVariables]),
                 new_key_begin, new_key_end);

    prog_spec[MainBeforeLib] = gw_get_prog_info(file, 
				keynames[0][MainBeforeLib], 
				keynames[1][MainBeforeLib], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, MainBeforeLib, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][MainBeforeLib]), 
 		 &(keynames[1][MainBeforeLib]),
                 new_key_begin, new_key_end);

    prog_spec[MainLibCall]   = gw_get_prog_info(file, 
				keynames[0][MainLibCall], 
				keynames[1][MainLibCall], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, MainLibCall, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][MainLibCall]), 
 		 &(keynames[1][MainLibCall]),
                 new_key_begin, new_key_end);

    prog_spec[MainAfterLib]  = gw_get_prog_info(file, 
				keynames[0][MainAfterLib], 
				keynames[1][MainAfterLib], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, MainAfterLib, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][MainAfterLib]), 
 		 &(keynames[1][MainAfterLib]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryInput]  = gw_get_prog_info(file, 
				keynames[0][LibraryInput], 
				keynames[1][LibraryInput], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryInput, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryInput]), 
 		 &(keynames[1][LibraryInput]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryOutput] = gw_get_prog_info(file, 
				keynames[0][LibraryOutput], 
				keynames[1][LibraryOutput], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryOutput, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryOutput]), 
 		 &(keynames[1][LibraryOutput]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryDef]    = gw_get_prog_info(file, 
				keynames[0][LibraryDef], 
				keynames[1][LibraryDef], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryDef, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryDef]), 
 		 &(keynames[1][LibraryDef]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryCode]   = gw_get_prog_info(file, 
				keynames[0][LibraryCode], 
				keynames[1][LibraryCode], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryCode, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryCode]), 
 		 &(keynames[1][LibraryCode]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryIncludes]= gw_get_prog_info(file, 
				keynames[0][LibraryIncludes], 
				keynames[1][LibraryIncludes], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryIncludes, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryIncludes]), 
 		 &(keynames[1][LibraryIncludes]),
                 new_key_begin, new_key_end);

    prog_spec[LibraryMods]= gw_get_prog_info(file, 
				keynames[0][LibraryMods], 
				keynames[1][LibraryMods], 
				&status, &new_key_begin, &new_key_end);
    if (!(check_status(status, LibraryMods, keynames))) return(NULL);
    gw_copy_keys(&(keynames[0][LibraryMods]), 
 		 &(keynames[1][LibraryMods]),
                 new_key_begin, new_key_end);

    return(prog_spec);
}


gw_copy_keys(old_key_begin, old_key_end, new_key_begin, new_key_end)
char **old_key_begin;
char **old_key_end;
char *new_key_begin;
char *new_key_end;
{
    if (VStrcmp(new_key_begin, "") != 0)
    {
        free(*old_key_begin);
        *old_key_begin = VStrcpy(new_key_begin);
    }
    if (VStrcmp(new_key_end, "") != 0)
    {
        free(*old_key_end);
        *old_key_end = VStrcpy(new_key_end);
    }
}

/******************************************************************************
* Module Name: gw_copyright
*
*     Purpose: This function reads a block of text (the copyright) from
*              the specified copyright file returns a pointer to it.
*
*       Input: lors is a character string that should be "long" or
*              "short" depending on if you want the long or short
*              copyright to be returned.
*
*      Output: The return value of this function is a pointer to a block of
*              characters that is the copyright to put in the man pages.
*
*  Written By: Per Lysne & Danielle Argiro 
*
******************************************************************************/
char *gw_copyright(filepath, lors)
char *filepath;
char *lors;
{
	int status;
	char begin[30], end[30], *block;
	char *copyrightpath;
	char *dummy;
	FILE *copyrightfile;
	
	/*
	 * Find the full path to the file containing the copyright.
	 */
	copyrightpath = vfullpath (filepath, NULL, NULL);
	if (copyrightpath == NULL) 
	{
	    fprintf (stderr, "gw_copyright:\n");
	    fprintf (stderr, "Could not find file %s\n", filepath);
	}
	
	/*
	 * Open the file with the copyright.
	 */
	copyrightfile = fopen (copyrightpath, "r");
	if (copyrightfile == NULL) 
	{
	    fprintf (stderr, "gw_copyright:\n");
	    fprintf (stderr, "Could not open file \"%s\"\n", copyrightpath);
	    return (NULL);
	}
	
	/*
	 * Set the begin and end keys to search for the copyright with
	 * depending on whether we want the long or short copyright.
	 */
	if (VStrcmp (lors, "short") == 0){
	    strcpy (begin, "-copyright_short");
	    strcpy (end, "-copyright_short_end");
	}
	else {
	    strcpy (begin, "-copyright_long");
	    strcpy (end, "-copyright_long_end");
	}
	
	/*
	 * Read the copyright block from the file.
	 */
	block = gw_get_prog_info (copyrightfile, begin, end, &status, 
				  &dummy, &dummy);
	if (status == BEGIN_KEY_NOT_FOUND)
	{
	    fprintf(stderr, "ERROR in %s file:\n", filepath);
	    fprintf(stderr, "Begin key '%s' not found.\n", begin);
	    return(NULL);
	}
	else if (status == END_KEY_NOT_FOUND)
	{
	    fprintf(stderr, "ERROR in %s file:\n", filepath);
	    fprintf(stderr, "End key '%s' not found.\n", end);
	    return(NULL);
	}
	
	return (block);
}



/*
 * Linelength is the length of the strings to read from the .prog file,
 * stringlength is the size of the character string needed to hold a line,
 * i.e. line+'\0', and Maxlength is the maximum length of a begin or end key.
 */
#define linelength 81
#define stringlength 82
#define MaxLength 512

/*
 * These structures are used to implement the linked list that is used by
 * several functions in this file.  Structure _list contains a pointer to the
 * head of a list of structures type _element.  This is done so that the 
 * value of the head of the list may change without needing to pass the list
 * as a reference parameter.
 */
typedef struct E {
    char *string;
    struct E *next;
} _element, *element;

typedef struct {
    element head;
} _list, *list;


/******************************************************************************
*
* Module Name: gw_get_prog_info()
*
*     Purpose: This function reads a block of text from the .prog file and
*              returns it in a contiguous block of memory.
*
*    Comments: The purpose of the gw_get_prog_info is to get the blocks
*              of information, such as authors, from a .prog file.  The .prog
*              file contains blocks of information about the program skeleton
*              to be created.  For instance the authors are contained in a
*              block delimited by the strings "-authors" and "-authors_end".
*              If a .prog file contained the following entry for authors
*
*            -authors
*           
*            Batman, Superman, Chickenman
*
*            -authors_end
*
*            this function would return the following string.
*           "
*            Batman, Superman, Chickenman
*
*           "
*              The reason for doing this is so that the returned string may be
*              inserted into the various program skeleton files and will appear
*              exactly as it did in the .prog file.
*
*     Usage: 
*	     block = gw_get_key_info(file_pointer, key_begin, key_end, 
*				     status, new_key_begin, new_key_end)
*
*            The file pointer is a pointer pointing to the already open .prog
*            file to get the information from, and key is a character string
*            that is used to identify the information to get in the .prog file.
*            For instance, if you want the authors block then the key_begin 
*	     should be "authors", and the key_end should be "authors_end".  
*	     Block is a character pointer that will point to the
*            information that was returned from the .prog file and is now
*            stored in a contiguous block of memory.
*
*       Input: Parameter fpt is a file pointer to the .prog file to read from,
*              and key is a pointer to a string which is the key to search the
*              .prog file for.  For instance, if you want to seach the .prog
*              file for authors then key should be "authors".
*
*      Output: The return value of this function is a pointer to a block of
*              characters that was read from the .prog file.  In case of an 
*              error, which can be caused by a bad file pointer, running out
*              of memory, or not finding the key that was specified in the
*              .prog file, the return value will be NULL.
*
*	       status returns the status of the textblock search.
*	       it can be one of:  BEGIN_KEY_NOT_FOUND, END_KEY_NOT_FOUND,
*				  VALID_TEXT_BLOCK, OR EMPTY_TEXT_BLOCK.
*
*	       new_key_begin and new_key_end return the actual begin & end
*	       keys found.  for instance, "-AUTHORS" in the .prog file will
*	       be accepted for a begin key of "-authors".  However, "-AUTHORS"
*	       may be needed later to write out a new .prog file which has
*	       keys that are identical to the original ones.
*	       
*
* Called From: ghost
*
*       Calls: This function calls gw_newlist, gw_addtolist, 
*	       gw_copylist, and gw_freelist.
*
*  Written By: Per Lysne 3/9/90
*  Modifications: changed routine to ignore new_key_begin and new_key_end
*		  if they are set to NULL.  -- Steve J 12/4/91
*
******************************************************************************/

char *
gw_get_prog_info (fpt, key_begin, key_end, status, new_key_begin, new_key_end)
FILE *fpt;
char *key_begin, *key_end;
int  *status;
char **new_key_begin, **new_key_end;
{
    /*
     * Done is a boolean used to terminate a loop.  I is an index into the
     * string we read from the .prog file and is used to skip leading white
     * space.  Block points to the contiguous block of memory which holds
     * the information we will get from the .prog file.
     */
    int  i, done, key_begin_found; 
    list l;
    FILE *fpt2;
    char temp[stringlength];
    char *temp2, *temp3, *lowercase(), *fullpath, *include_file, *block;

    /*
     * These are the functions that are called by gw_prog.  For information
     * on each function see the individual function headers.  L is a list in
     * which the strings read from the .prog file are stored.  A dynamic
     * structure is needed so a list is used.
     */
    int gw_freelist();
    char *gw_getstring(), *gw_copylist();
    list gw_newlist(), gw_addtolist(); 

    key_begin_found = FALSE;

    /* If someone passed us a bad file pointer then return NULL. */
    if (fpt==NULL) {
        fprintf (stderr, "gw_get_prog_info:\n");
        fprintf (stderr, "Bad file pointer was input\n");
        return (NULL);
    }

    /* In case the file is not already rewound. */
    rewind (fpt);

    /*
     * Initialize the list that will be used to store the strings (lines) that
     * we read from the .prog file.  A list is needed because at this point
     * we don't know the size of the block we will get.
     */
    l = gw_newlist();
    if (l == NULL) {
        fprintf (stderr, "ghost: insufficient memory to run\n");
        return (NULL);
    }

    /*
     * Read lines from the file until the begin key is found or EOF is reached.
     * If EOF before the beginning key is found then return NULL.
     */
    done = FALSE;
    while (!done) {
        /* Get a line from .prog and check for EOF. */
        if (fgets(temp, linelength, fpt) == NULL) {
	    if (new_key_begin != NULL)
	      *new_key_begin = VStrcpy(key_begin);
	    *status = EMPTY_TEXT_BLOCK;
            return (NULL);
        }

        /*
         * Skip white space at the beginning of a string before comparing to
         * the begin_key.
         */
        i=0;
        while (temp[i]==' ' || temp[i]=='\t') i++ ;
        temp2 = lowercase(&temp[i]);
        if ((VStrncmp(temp2, key_begin, VStrlen(key_begin)) == 0) ||
	    (VStrncmp(&temp[i], key_begin, VStrlen(key_begin)) == 0)) {
            done = TRUE;
	    if ((VStrncmp(temp2, key_end, VStrlen(key_end)) != 0) &&
                (VStrncmp(&temp[i], key_end, VStrlen(key_end)) != 0))
	        key_begin_found = TRUE;
	    if (new_key_begin != NULL)
	      {
		*new_key_begin = VStrcpy(&temp[i]);
		temp3 = *new_key_begin;
		temp3[VStrlen(temp3)-1] = '\0';
	      }
        }
	free(temp2);

     }
    /*
     * This part actually goes with the loop below, it adds the remaining 
     * characters on the begin_key line to the head of the list so that they
     * will be included in the output block.
     */
    i = i + VStrlen(key_begin);
    while ((temp[i] == ' ') || (temp[i] == '\t')) i++;
    if ((temp[i] != '\0') && (temp[i] !='\n')) {
        if (gw_addtolist (l, &temp[i]) == NULL) {
            fprintf (stderr, "ghost: insufficient memory to run\n");
            return (NULL);
        }
    }
    /*
     * If we reach this point we must have found the begin key.  The file
     * pointer should now point to the next line following this key.  Read
     * lines from the file until we find the end key.  If EOF is reached
     * before the end key is found then return a NULL.
     */
    done = FALSE;
    while (!done) {
        /* If we got EOF before end_key return NULL. */
        if (gw_getstring(temp, linelength, fpt) == NULL) {
	    if (key_begin_found)
	       *status = END_KEY_NOT_FOUND;
	    else
	       *status = BEGIN_KEY_NOT_FOUND;
	    if (new_key_end != NULL)
	      *new_key_end = VStrcpy(key_end);
            return (NULL);
        }

        /*
         * Skip white space at the beginning of a line before checking for
         * the end key.  If the end key is found then set done to TRUE so
         * this loop will end.  
         */
        i=0;
        while (temp[i]==' ' || temp[i]=='\t') i++ ;
        temp2 = lowercase(&temp[i]);
        if ((VStrncmp(temp2, key_end, VStrlen(key_end)) == 0) ||
	    (VStrncmp(&temp[i], key_end, VStrlen(key_end)) == 0)) {
            done = TRUE;
	    if (new_key_end != NULL)
	      {
		*new_key_end = VStrcpy(&temp[i]);
		temp3 = *new_key_end;
		temp3[VStrlen(temp3)-1] = '\0';
	      }
        }

	/*
         * Special case: if a line occurs within the text block starting
	 * with "$include filename", that means that we have to open another
	 * file to get that segment of the text block.
	 */
	else if (VStrncmp(&temp[i],"@include ", 9) == 0)
	{
	    include_file = &temp[i+9];
	    include_file[VStrlen(include_file)-1] = '\0';
	    fullpath = vfullpath(include_file, NULL, NULL);
	    if (!(fpt2 = fopen(fullpath, "r")))
    	    {
        	fprintf(stderr, "\nghost: \n");
        	fprintf(stderr, "Error in .prog file, line '%s': \n", temp);
		fprintf(stderr, "unable to open '%s' to read ", fullpath);
		fprintf(stderr, "included text block. Continuing execution ");
		fprintf(stderr, "without it.\n");
            }
	    else
	    {
		/*
		 *  get strings out of the included file, and add them
		 *  to the list, until we get to end of file.
		 */
		while(!(feof(fpt2)))
		{
		    gw_getstring(temp, linelength, fpt2);
                    /* If we are out of memory for list, its an error. */
            	    if (gw_addtolist (l, temp) == NULL) {
                	fprintf (stderr, "ghost: insufficient memory to run\n");
                	return (NULL);
            	    }
		}
		fclose(fpt2);
	    }


	}
	/*
	 *  If the string is not the end key, and it's not the special
	 *  $include string, then it's just text within a text block -
	 *  add it to the list as part of the block.
	 */
        else {
            /* If we are out of memory for list then we have an error. */
            if (gw_addtolist (l, temp) == NULL) {
                fprintf (stderr, "ghost: insufficient memory to run\n");
                return (NULL);
            }
        }
	free(temp2);
    }

    /*
     * Now that we have the entire block from the .prog file copy the text
     * in the list to a contiguous block of memory.  If we don't have enough
     * memory to do this then return NULL.
     */
    block = gw_copylist(l);
    if (block == NULL) {
        fprintf (stderr, "ghost: insufficient memory to run\n");
        return (NULL);
    }

    /* Return the memory used by the list to the system. */
    gw_freelist (l);

    /*
     * If we make it to this point, all must we well so return a pointer to
     * the contiguous block of text from the .prog file.
     */
    if (VStrcmp(block, "") == 0)
        *status = EMPTY_TEXT_BLOCK;
    else
        *status = VALID_TEXT_BLOCK;

    return (block);
}



/******************************************************************************
* Module Name: gw_getstring
*
*     Purpose: This function is designed to work exactly like fgets from the
*              C library except that tabs are converted to eight spaces.
*
*       Input: Parameter string is a character pointer to a block of memory
*              to read a string into.  Number is the maximum number of chars
*              to read before returning, and fpt is a pointer to the file to
*              read from.
*
*      Output: On returning parameter string points to a character string.  If
*              the function executes successfully, the return value is the same
*              as parameter string.  If an error occurs, such as a bad file
*              pointer or a bad number of characters to read, the return value
*              will be NULL.
*
* Called From: This function is called from gw_prog.
*
*       Calls: This function calls
*
*  Written By: Per Lysne  3/9/90
******************************************************************************/
char *
gw_getstring (string, number, fpt)
char *string;
int number;
FILE *fpt;
{
    /*
     * Done is a boolean that tells us when to exit a loop, i is an index
     * into a character string that tells us where to put character c when
     * we read it from the input file.
     */
    int done, i;
    char c;

    /* If someone gave us a bad file pointer then return NULL. */
    if (fpt==NULL) {
        fprintf (stderr, "gw_fgets:\n");
        fprintf (stderr, "Bad file descriptor was input\n");
        return (NULL);
    }

    /* You can't read 0 or fewer characters. */
    if (number<=0) {
        fprintf (stderr, "gw_fgets:\n");
        fprintf (stderr, "Bad number of characters was input\n");
        return (NULL);
    }

    /*
     * Repeat this loop until the maximum number of charaters has been read,
     * the end of file is reached, a NULL is read, or an endline is found. 
     */
    i=0;
    done=FALSE;
    while (!done) {
        c = fgetc (fpt);

        /* These conditions terminate the loop. */
        if (c == EOF) {
            done = TRUE;
        }
        else if (c == '\0') {
            done = TRUE;
        }
        else if (c == '\n') {
            done = TRUE;
        }
        else if (i >= number-2) {
            done = TRUE;
        }

        /* This is what to do with the character we just read. */
        if (c == EOF) {
            string [i] = '\0';
        }
        else if (c == '\0') {
            string [i] = '\0';
        }
        else if (c == '\n') {
            string [i] = '\n';
            string [i+1] = '\0';
        }
        else if (i >= number-2) {
            /* No room left in string, so tab is cut short to one space. */
            if (c == '\t') {
                string [i] = ' ';
            }
            else {
                string [i] = c;
            }
            string [i+1] = '\0';
        }
        else {
            /* Tab equals eight spaces. */
            if (c == '\t') {
                string [i] = ' ';
                i = i+1;
                while ( ((i % 8)!=0) && (i <= number)) {
                    string [i] = ' ';
                    i = i+1;
                }
            }
            else {
                string [i] = c;
                i = i+1;
            }
        }
    }

    /*
     * If we are at EOF then return NULL, otherwise return the same string 
     * as was entered.
     */
    if (c == EOF) {
        return (NULL);
    }
    else {
        return (string);
    }
}



/******************************************************************************
* Module Name: gw_newlist
*
*     Purpose: This function initializes a new list.  The list created is a
*              dynammically allocated singly linked list that is used to hold
*              the strings that we read from the .prog file.  The list is made
*              up of two kinds of structures.  The first structure, _list which
*              is pointed to by a list holds only a pointer to the head of the
*              actual list.  This is done so that the head of the actual list
*              may change without passing the list as a reference parameter.
*              The list itself is made up of structures _element which are
*              pointed to by type element.  These structures contain a pointer
*              to a block of characters which is where the data is stored, and
*              a pointer to the next element in the list.  In this function
*              the _list structure is allocated and a pointer to it is returned.
*
*       Input: None
*
*      Output: The return value of this function is a list, or a pointer to
*              type _list.  If an error occurs allocating the _list structure
*              NULL is returned.  The new list is initialized to be empty.
*
* Called From: This function is called from gw_prog.
*
*       Calls: none
*
*  Written By: Per Lysne  3/9/90
******************************************************************************/
list
gw_newlist()
{
    /* L is the new list we will allocate and return. */
    list l;
 
    /* Allocate a new list structure. */
    l = (list) malloc (sizeof(_list));
    if (l == NULL) {
        fprintf (stderr, "gw_newlist:\n");
        fprintf (stderr, "Out of memory for a new list\n");
        return (NULL);
    }

    /* Make sure the new list is empty. */
    l->head = NULL;

    /* Return the new list. */
    return (l);
}



/******************************************************************************
* Module Name: gw_addtolist
*
*     Purpose: This function stores a string in a list.  This is done by
*              allocating a new list element and linking it to the end of
*              the list.  The data field in the new element is then set to
*              the string to store.
*
*       Input: Parameter l is a list and parameter s is a string to add to
*              that list.
*
*      Output: The return value of this function is the same as parameter
*              l, so the same list that was entered is returned.  The only
*              difference is that the list is now one element longer.  If
*              and error occurs (bad list was input or out of memory) the
*              return value will be NULL.
*
* Called From: This function is called from gw_prog.
*
*       Calls: None
*
*  Written By: Per Lysne  3/9/90
******************************************************************************/
list
gw_addtolist (l, s)
list l;
char *s;
{
    /*
     * Newstring is a copy of input paramter string.  This is the string that
     * will be added to the list.  Input string must be copied because we want
     * to reuse it in the main function.  New is a pointer to the new list
     * element we will create, and temp is a pointer to a list element that
     * we will use to traverse the list to the end.
     */
    char *newstring;
    element new, temp;

    /* If we got a bad list return NULL. */
    if (l == NULL) {
        return (NULL);
    }

    /* If we are out of memory for a new list element return NULL. */
    new = (element) malloc (sizeof(_element));
    if (new == NULL) {
        fprintf (stderr, "gw_addtolist:\n");
        fprintf (stderr, "Out of memory for a new list element\n");
        return (NULL);
    }

    /* Create a new string idenical to "string" to use in the list. */
    newstring = VStrcpy(s);

    /* Add the new element to the end of the list. */
    if (l->head == NULL) {
        l->head = new;
    }
    else {
        temp = l->head;
        while (temp->next != NULL) {
            temp = temp->next;
        }
        temp->next = new;
    }

    /* The last pointer in the list should be NULL. */
    new->next = NULL;

    /* Put the copy of the input string into the new list element. */
    new->string = newstring;

    /* Return the same value of the list as was entered. */
    return (l);  
}



/******************************************************************************
* Module Name: gw_length
*
*     Purpose: This function returns the total length of all of the strings
*              stored in a list.  Suppose that strings "hi" and "have a nice
*              day" where stored in the list.  These strings are actually
*              "hi\0" and "have a nice day\0".  The total length is 3+16=19,
*              but what is returned is 2+16=18 because only the '\0' on the
*              last string is counted.  This is done because the length we
*              really want is the number of characters it will take to store
*              all of the strings in the list concatenated into a single long
*              string.
*
*       Input: Parameter l is a list in which to determine the total length
*              of all the stored strings.
*
*      Output: The return value is the integer number of characters needed to
*              store all of the strings in the list concatenated into a 
*              single long string terminated with a '\0'.  If an error occurs
*              (bad list input) then the return value is 0.
*
* Called From: This function is called from gw_copylist.
*
*       Calls: None.
*
*  Written By: Per Lysne  3/9/90
******************************************************************************/
int
gw_length (l)
list l;
{
    int total, gw_length();
    element temp;

    /* If we got a bad list then return 0. */
    if (l == NULL) {
        fprintf (stderr, "gw_length:\n");
        fprintf (stderr, "Bad list was input\n");
        return (0);
    }

    /* This loop traverses the list and counts up all the characters. */
    total = 0;
    temp = l->head;
    while (temp != NULL) {
        total = total + VStrlen(temp->string);
        temp = temp->next;
    }

    /* Return the total number of characters stored in the list.  The extra
     * one is for the '\0' that must be placed at the end of the string.
     */
    return (total+1);
}



/******************************************************************************
* Module Name: gw_freelist
*
*     Purpose: This function returns to the system the memory that is used
*              by a list.  The list and all of the data in it are lost.  The
*              elements of the list are deallocated using free and so is the
*              _list structure itself.
*
*       Input: Parameter l is a list that is to be destroyed and its memory
*              returned to the system.
*
*      Output: The return value is an integer 0 if the list was successfully
*              deallocated, and a 1 if an error occured (bad list entered).
*
* Called From: This function is called by gw_prog.
*
*       Calls: None.
*
*  Written By: Per Lysne  3/9/90
******************************************************************************/
int
gw_freelist (l)
list l;
{
    /*
     * Temp and last are both pointers to list elements and are used to 
     * traverse the list.  Temp points to the current list element and last
     * points to the list element before temp.  This is needed because you
     * need a pointer to an element to deallocate it.
     */
    element temp, last;

    /* If we got a bad list then return NULL. */
    if (l == NULL) {
        fprintf (stderr, "gw_freelist:\n");
        fprintf (stderr, "Bad list was input\n");
        return (1);
    }

    /* Traverse the list and return each element as we visit it. */
    temp = l->head;
    last = l->head;
    while (temp != NULL) {
        temp = temp->next;
        free (last);
        last = temp;
    }

    /* Deallocate the _list structure itself. */
    free (l);

    /* Return 0 because all went well. */
    return (0);
}



/*****************************************************************************
* Module Name: gw_copylist
*
*     Purpose: This function takes all of the strings in a list, concatenates
*              them into a single long string, allocates memory to store this
*              string, and stores the long string in memory.  Since each
*              individual string in the list ends with a '\0', this character
*              is not copied.  Only at the end of the final concatenated
*              string is there a '\0'.  With this exception, the strings in the
*              list are copied directly into the new string.
*
*       Input: Paramter l is a list from which to get the strings to 
*              concatenate.
*
*      Output: The return value of this function is a pointer to the block
*              of memory where the concatenated string is stored.  If an
*              error occurs (bad list input, out of memory) the return value
*              will be NULL.
*
* Called From: This function is called from gw_prog.
*
*       Calls:
*
*  Written By: Per Lysne  3/9/90
*****************************************************************************/
char *
gw_copylist (l)
list l;
{
    /*
     * I is an integer that is used to tell us where in the long string to 
     * place the next individual string.  Block is a pointer to the contiguous
     * block of characters that will be returned.  Temp is a pointer to a list
     * element that is used to traverse the input list.
     */
    int i;
    char *block;
    element temp;

    /* If we got a bad list return NULL. */
    if (l == NULL) {
        fprintf (stderr, "gw_copylist:\n");
        fprintf (stderr, "Bad list was input\n");
        return (NULL);
    }

    /* Allocate a block of memory for the block of text. */
    block = (char *) kcalloc (gw_length(l), sizeof(char));
    if (block == NULL) {
        fprintf (stderr, "gw_copylist:\n");
        fprintf (stderr, "Not enough memory for block of characters\n");
        return (NULL);
    }

    /* Traverse the list and copy each string into the block. */
    i = 0;
    temp = l->head;
    while (temp != NULL) {
        strcpy (&block[i], temp->string);
        i = i + VStrlen(temp->string);
        temp = temp->next;
    }

    /* Make sure the block ends with a NULL character. */
    block[i] = '\0';

    /* Return a pointer to the new block of characters. */
    return (block);
}

char *lowercase(string)
char *string;
{
        int  i;
        char *lower;

        lower = VStrcpy(string);
        for (i = 0; i < VStrlen(string); i++)
        {
           if (isupper(lower[i]))
              lower[i] = tolower(lower[i]);
        }
        return(lower);
}


int check_status(status, key, keynames)
int status;
int key;
char **keynames[2];
{
    if (status == BEGIN_KEY_NOT_FOUND)
    {
	fprintf(stderr, "ERROR in *.prog file:\n");
	fprintf(stderr, "Begin key '%s' not found.\n", keynames[0][key]);
	return(FALSE);
    }
    else if (status == END_KEY_NOT_FOUND)
    {
        fprintf(stderr, "ERROR in *.prog file:\n");
        fprintf(stderr, "End key '%s' not found.\n", keynames[1][key]);
        return(FALSE);
    }
    return(TRUE);
}
