 /*
  * Khoros: $Id: db_util.c,v 1.2 1992/03/20 22:47:47 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: db_util.c,v 1.2 1992/03/20 22:47:47 dkhoros Exp $";
#endif

 /*
  * $Log: db_util.c,v $
 * Revision 1.2  1992/03/20  22:47:47  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * 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 "forms.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>             file name: db_util.c                      <<<<
   >>>>                                                       <<<<
   >>>>		    Routines to Read the UIS file             <<<<
   >>>>                                                       <<<<
   >>>>			xvf_read_database()		      <<<<
   >>>>			xvf_read_subform()		      <<<<
   >>>>			xvf_read_pane()		              <<<<
   >>>>		        xvf_copy_database()		      <<<<
   >>>>			xvf_free_database()	              <<<<
   >>>>			xvf_realloc_database()	              <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  */


/************************************************************
*
*  Routine Name: xvf_read_database
*
*      Purpose:  Reads in a database file and fills in a local
*		 database structure with the contents
*
*        Input:  fid  - file id of UIS
*		 num  - number of lines in the UIS
*		 size - allocated size of the UIS (>= num)
*
*       Output:  returns database - array of strings which the internal
*			            UIS is stored
*
*   Written By: Danielle Argiro, Stephanie Hallett & Mark Young
*
*************************************************************/


char **xvf_read_database(file, num, size)

FILE	*file;
int	*num, *size;
{
	Line_Info line_info;
        char      temp[MaxLength*20], temp2[MaxLength*20];
	int	  i, j, db_size, done, length, count, flag; 
	char    **database, **xvf_read_subform(), **xvf_read_pane();

	xvf_clear_line_info(&line_info);

	/* premature end of file */
	if (feof(file))
	{
	   *num = 0;
	   return(NULL);
	}

	/* allocate internal database */
	db_size = MaxDBSize;
	database = (caddr_t *) calloc(1,sizeof(char *)*MaxDBSize);
	if (database == NULL)
	{
	   xvf_error_wait("Out of memory! Cannot allocate room for internal user interface specification structure",
			  "xvf_read_database", NULL);
	   *num = 0;
	   return(NULL);
	}

	done = false;
	i = count = 0;
	while (!done)
 	{
           j = 0;

	   if (fgets(temp, MaxLength*20, file) == NULL) 
	   {
	      xvf_error_wait("Error!  End of file encountered before reading \
entire user interface specification", "xvf_read_database", NULL);

	      *num = 0;
	      xvf_free_database(database);
	      return(NULL);
	   }

	   while(temp[j] == ' ' || temp[j] == '\t') j++;

           if ((temp[j] != '\n') && (temp[j] != '#'))
	   {
	      flag = xvf_get_line_type(&temp[j]);
	      switch (flag)
	      {
		 case IncludePane:
		      xvf_parse_includepane_line(&temp[j], &line_info);
		      database = xvf_read_pane(line_info.filename, database,
					&i, &db_size);
		      if (database == NULL)
		      {
		         *num = 0;
		         xvf_free_database(database);
			 sprintf(temp2, "Error found in included file specified by line '%s'", &temp[j]);
			
	                 xvf_error_wait(temp2,  "xvf_read_database", NULL);
		         return(NULL);
		      }
		      break;

		 case IncludeSubForm:
		      xvf_parse_includesubform_line(&temp[j], &line_info);
		      database = xvf_read_subform(line_info.filename, database,
					&i, &db_size);
		      if (database == NULL)
		      {
		         *num = 0;
		         xvf_free_database(database);
			 sprintf(temp2, "Error found in included file specified by line '%s'", &temp[j]);
			
	                 xvf_error_wait(temp2,  "xvf_read_database", NULL);
		         return(NULL);
		      }
		      break;

		 case StartForm:
		 case StartMaster:
		 case StartSubMenu:
		 case StartSubForm:
		 case StartGuide:
		 case StartPane:
		 case Toggle:
		 case MutExcl:
		      count++;
		      break;

		 case End:
		      count--;
		      break;

		 case -1:
		      *num = 0;
		      xvf_free_database(database);
		      fprintf(stderr, "xvf_read_database:\n");
		      fprintf(stderr, "    ERROR: unrecognisable line in specified UIS file.\n"); 
		      fprintf(stderr, "    Please check to make sure that the file in question\n"); 
		      fprintf(stderr, "    is a syntactically correct UIS file\n"); 
		      return(NULL); 
		      break;
	      }

	      if (flag != IncludePane && flag != IncludeSubForm)
	      {
		 if (i == (db_size-1))
		 {
		    database=xvf_realloc_database(database,&db_size,MaxDBSize);
		    if (database == NULL)
		       return(NULL);
		 }
	         length = xvf_strlen(&temp[j]);
	         database[i] = (char *) calloc(1, sizeof(char) * length);
	         (void) strncpy(database[i],&temp[j], length);
	         database[i][length-1] = '\0';
	         i++;
	      }

	      if (count == 0)
		 done = true;
           }
	}
	*num  = i;
	*size = db_size;
        return(database);
}



/************************************************************
*
*  Routine Name: xvf_read_subform
*
*      Purpose:  Reads in a database subform from the specified file
*		 into the supplied database.  This routine is called
*		 when xvf_read_database finds a IncludeSubForm user
*		 interface specification.  '-k filename'
*
*		 xvf_read_subform includes the subform specification into
*		 the existing database file.  If the subform file has
*		 a StartSubForm specification then the subform will be read
*		 in and the StartForm will be discarded; otherwise the entire
*		 subform description is read.
*
*
*        Input:  filename - name of the file in which to get the
*			    subform definition.
*		 num      - number of current lines in the database
*		 db_size  - size of current database
*
*       Output:  returns:  returns true on success, otherwise false
*			   is returned.
*
*		 database - adds onto the array of strings the pane
*			    database definition.
*
*		 num - returns the current number of lines to the
*		       database
*		 db_size  - returns the size of current database
*
*   Written By: Mark Young & Danielle Argiro
*
*
*************************************************************/


char **xvf_read_subform(filename, database, num, db_size)

char	*filename;
char    **database;
int	*num, *db_size;
{
	FILE		*file;
	int 	        i, j, count, size, offset = 0; 
	char		*fullpath, **db, temp[MaxLength];
	Line_Info  	line_info;


	/*
	 *  Open the database file that contains the subform specification.
	 */
	fullpath = vfullpath(filename, NULL, NULL); 
	if (!(file = fopen(fullpath, "r")))
        {
           sprintf(temp, "Unable to open '%s' to read pane", filename);
           xvf_error_wait(temp, "xvf_read_subform", NULL);
           return(NULL);
        }

	/*
	 *  Read in the pane specifcation
	 */
	db = xvf_read_database(file, &count, &size);
	if (db == NULL)
	{
	   fclose(file);
	   return(NULL);
	}

	/*
	 *  Check to see if the pane specification starts with
	 *  a StartForm specification.
	 */
	xvf_clear_line_info(&line_info);
	if (xvf_get_line_type(db[offset]) == StartForm)
	{
	   if (!(xvf_parse_startform_line(db[offset], &line_info))) 
		return(NULL);
	   count--; offset++;
	}

	/*
	 *  Copy the rest of the database, which should be the subform,
	 *  into old database.
	 */
	for (j = *num, i = offset; i < count; i++, j++)
	{
	    if (j == (*db_size-1))
	    {
	       database = xvf_realloc_database(database, db_size, MaxDBSize);
	       if (database == NULL)
	       {
		  fclose(file);
	          return(NULL);
	       }
	    }
	    database[j] = db[i];
	    db[i] = NULL;
	}

	/*
	 *  Free the temporary database.
	 */
	xvf_free_database(db);

	*num = j;
        fclose(file);

	return(database);
}



/************************************************************
*
*  Routine Name: xvf_read_pane
*
*      Purpose:  Reads in a database pane from the specified file
*		 into the supplied database.  This routine is called
*		 when xvf_read_database finds a IncludePane user
*		 interface specification.  '-p filename'
*
*		 xvf_read_pane includes the pane specification into
*		 the existing database file.  If the pane file has
*		 a StartForm and a StartSubForm specification then the
*		 pane will be read in and the StartForm/StartSubForm
*		 will be discarded; otherwise the entire pane description
*		 is read.
*
*
*        Input:  filename - name of the file in which to get the
*			    database pane.
*
*		 num - Number of current lines in the database
*		 db_size  - size of current database
*
*       Output:  returns:  returns true on success, otherwise false
*			   is returned.
*
*		 database - adds onto the array of strings the pane
*			    database definition.
*
*		 num - returns the current number of lines to the
*		       database
*		 db_size  - returns the size of current database
*
*   Written By: Mark Young & Danielle Argiro
*
*
*************************************************************/


char **xvf_read_pane(filename, database, num, db_size)

char	*filename, **database;
int	*num, *db_size;
{
	FILE		*file;
	int 	        i, j, count, size, offset = 0; 
	char		*fullpath, **db, temp[MaxLength];
	Line_Info  	line_info;


	/*
	 *  Open the database file that contains the pane specification.
	 */
	fullpath = vfullpath(filename, NULL, NULL); 
	if (!(file = fopen(fullpath, "r")))
        {
           sprintf(temp, "Unable to open '%s' to read pane", filename);
           xvf_error_wait(temp, "xvf_read_pane", NULL);
           return(NULL);
        }

	/*
	 *  Read in the pane specifcation
	 */
	db = xvf_read_database(file, &count, &size);
	if (db == NULL)
	{
	   fclose(file);
	   return(NULL);
	}

	/*
	 *  Check to see if the pane specification starts with
	 *  a StartForm specification.
	 */
	xvf_clear_line_info(&line_info);
	if (xvf_get_line_type(db[offset]) == StartForm)
	{
	   if (!(xvf_parse_startform_line(db[offset], &line_info))) 
		return(NULL);
	   count--; offset++;
	}

	/*
	 *  Check to see if the pane specification starts with
	 *  a StartSubForm specification.
	 */
	if (xvf_get_line_type(db[offset]) == StartSubForm)
	{
	   if (!(xvf_parse_startsubform_line(db[offset], &line_info)))
		return(NULL);
	   count--; offset++;
	}

	/*
	 *  Copy the rest of the database, which should be the pane,
	 *  into old database.
	 */
	for (j = *num, i = offset; i < count; i++, j++)
	{
	    if (j == (*db_size-1))
	    {
	       database = xvf_realloc_database(database,db_size,MaxDBSize);
	       if (database == NULL)
	       {
		  fclose(file);
	          return(NULL);
	       }
	    }
	    database[j] = db[i];
	    db[i] = NULL;
	}

	/*
	 *  Free the temporary database.
	 */
	xvf_free_database(db);

	*num = j;
        fclose(file);

	return(database);

}



/******************************************************************
*
*  Routine Name:  xvf_copy_database
*
*	Purpose:  copies a database, returns a pointer to the new database.
*
*	  Input:  old_database - pointer to the database to be copied.
*
*        Output:  pointer to the new database is returned
*
*    Written By: Danielle Argiro and Mark Young
*
*******************************************************************/

char **xvf_copy_database(old_database)

char **old_database;
{
	char **database;
	int  db_size, balance, flag, sub_type, index = 0,
	     subform_index = -1, pseudo_subform = False;


	/* allocate new internal database */
	db_size = MaxDBSize;
        database = (caddr_t *) calloc(1,sizeof(char *)*MaxDBSize);
        if (database == NULL)
        {
           fprintf(stderr, "xvf_copy_database:\n");
	   fprintf(stderr, "Out of memory! Cannot calloc database");
           return(NULL);
        }
 
        while (old_database[index] != NULL)
        {
	   if (index == (db_size-1))
	   {
	      database = xvf_realloc_database(database, &db_size, MaxDBSize);
	      if (database == NULL)
		 return(NULL);
	   }

	   flag = xvf_get_line_type(old_database[index]);
	   if (flag  == StartSubForm)
	   {
	      /*
	       *  Find out if the subform is either a PsuedoSubForm or
	       *  a SubFormButton.  PsuedoSubForm can be shared among other
	       *  other databases.
	       */
	      do
	      {
		 subform_index++;
		 if (database[subform_index] == NULL)
		 {
		    sub_type = SubFormButton;
		    break;
		 }
		 else
		    sub_type = xvf_get_line_type(database[subform_index]);
	      } while (sub_type != PsuedoSubForm && sub_type != SubFormButton);

	      if (sub_type == PsuedoSubForm)
	      {
	         pseudo_subform = True;
	         balance = 1;
	      }
	   }
	   else if (pseudo_subform)
	   {
	      switch (flag)
	      {
		 case StartPane:	balance++; break;
		 case StartGuide:	balance++; break;
		 case Toggle:		balance++; break;
		 case MutExcl:		balance++; break;

		 case End:		balance--; break;
	      }
	   }

	   if (pseudo_subform)
	      database[index] = old_database[index];
	   else
 	      database[index] = xvf_strcpy(old_database[index]);

	   index++;

	   if (pseudo_subform && balance == 0)
	      pseudo_subform = False;
	}
	return(database);
}



/************************************************************
*
*  Routine Name: xvf_free_database(database)
*
*      Purpose:  Free the form database.  Runs thru the database
*		 freeing each database line until the first NULL
*		 line is encountered or MaxDBSize lines are freed.
*		 Then free the database pointer.
*
*        Input:  database  - the form database to be freed.
*
*       Output:  None
*
*   Written By:  Danielle Argiro & Mark Young
*
*************************************************************/

xvf_free_database(database)

char **database;
{
	int  balance, flag, sub_type, save_index, index = 0,
	     subform_index = -1, pseudo_subform = False;

	if (database == NULL) return;

	while (database[index] != NULL)
	{
	   flag = xvf_get_line_type(database[index]);
	   if (flag == StartSubForm)
	      break;
	   else
	      index++;
	}
	save_index = index;

        while (database[index] != NULL)
        {
	   flag = xvf_get_line_type(database[index]);
	   if (flag  == StartSubForm)
	   {
	      /*
	       *  Find out if the subform is either a PsuedoSubForm or
	       *  a SubFormButton.  PsuedoSubForm can be shared among other
	       *  other databases.
	       */
	      do
	      {
		 subform_index++;
		 if (subform_index >= save_index)
		 {
		    sub_type = SubFormButton;
		    break;
		 }
		 else
		    sub_type = xvf_get_line_type(database[subform_index]);
	      } while (sub_type != PsuedoSubForm && sub_type != SubFormButton);

	      if (sub_type == PsuedoSubForm)
	      {
	         pseudo_subform = True;
	         balance = 1;
	      }
	   }
	   else if (pseudo_subform)
	   {
	      switch (flag)
	      {
		 case StartPane:	balance++; break;
		 case StartGuide:	balance++; break;
		 case Toggle:		balance++; break;
		 case MutExcl:		balance++; break;

		 case End:		balance--; break;
	      }
	   }

	   if (pseudo_subform == False)
 	      free(database[index]);
	   else if (balance == 0)
	      pseudo_subform = False;

	   database[index] = NULL;
	   index++;
	}

	for (index = 0; index < save_index; index++)
	   free(database[index]);

	free(database);
}



/************************************************************
*
*  Routine Name: xvf_realloc_database(database, old_size, new_size)
*
*      Purpose:  Free the form database.  Runs thru the database
*		 freeing each database line until the first NULL
*		 line is encountered or MaxDBSize lines are freed.
*		 Then free the database pointer.
*
*        Input:  database  - the form database to be freed.
*
*       Output:  None
*
*   Written By:  Danielle Argiro & Mark Young
*
*************************************************************/

char **xvf_realloc_database(database, size, request)

char **database;
int  *size, request;
{
	int num_bytes;


	if (database == NULL)
	{
	   num_bytes = request * sizeof(char *);
	   database = (caddr_t *) calloc(1, num_bytes);
	   if (database == NULL)
	   {
	      fprintf(stderr, "\nxvf_realloc_database: \n");
	      fprintf(stderr, "\nOut of memory! Cannot allocate room for \
internal user interface specification structure.\n");
	      return(NULL);
	   }
	   *size = request;
	}
	else
	{
	   num_bytes = (*size + request) * sizeof(char *);
	   database = (caddr_t *) realloc(database, num_bytes);
	   if (database == NULL)
	   {
	      fprintf(stderr, "\nxvf_realloc_database: \n");
	      fprintf(stderr, "\nOut of memory! Cannot allocate room for \
internal user interface specification structure.\n");
	      return(NULL);
	   }

	   /*
	    *  Need to put nulls in the new part of the allocated array.
	    */
	   num_bytes = request * sizeof(char *);
	   bzero(&database[*size], num_bytes);
	   *size += request;
	}
	return(database);
}
