/* National Institute of Standards and Technology (NIST)
/* National Computer System Laboratory (NCSL)
/* Office Systems Engineering (OSE) Group
/* ********************************************************************
/*                            D I S C L A I M E R
/*                              (March 8, 1989)
/*  
/* There is no warranty for the NIST NCSL OSE SGML parser and/or the NIST
/* NCSL OSE SGML parser validation suite.  If the SGML parser and/or
/* validation suite is modified by someone else and passed on, NIST wants
/* the parser's recipients to know that what they have is not what NIST
/* distributed, so that any problems introduced by others will not
/* reflect on our reputation.
/* 
/* Policies
/* 
/* 1. Anyone may copy and distribute verbatim copies of the SGML source
/* code as received in any medium.
/* 
/* 2. Anyone may modify your copy or copies of SGML parser source code or
/* any portion of it, and copy and distribute such modifications provided
/* that all modifications are clearly associated with the entity that
/* performs the modifications.
/* 
/* NO WARRANTY
/* ===========
/* 
/* NIST PROVIDES ABSOLUTELY NO WARRANTY.  THE SGML PARSER AND VALIDATION
/* SUITE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
/* EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
/* THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
/* WITH YOU.  SHOULD THE SGML PARSER OR VALIDATION SUITE PROVE DEFECTIVE,
/* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
/* 
/* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL NIST BE LIABLE FOR
/* DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
/* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
/* INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
/* BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
/* FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY
/* NIST) THE PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF
/* SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
*/

/************************************************************************/
/*   TITLE:          SGML PARSER                                        */
/*   SYSTEM:         DTD PROCESSOR                                      */
/*   SUBSYSTEM:                                                         */
/*   SOURCE FILE:    DTDSUTIL.C                                         */
/*   AUTHOR:         Michael Garris                                     */
/*                                                                      */
/*   DATE CREATED:                                                      */
/*   LAST MODIFIED:                                                     */
/*                                                                      */
/*                  REVISIONS                                           */
/*   WHEN      WHO            WHY                                       */
/************************************************************************/
#include <stdio.h>
#include <setjmp.h>
#include "qntyset.h"
#include "dtd.h"
#include "dtdfncs.h"
#include "dtddefs.h"
#include "dtdglbl.h"
/************************************************************************/
/* globals for linked list used for resolving entity references         */
/************************************************************************/
static SYMBOL   Nsymbol; /* structure containing: 
                           name, syntactic literal, parameter literal */


static NODE   *Phead = NULL;/* pointer to first NODE in parameter entity 
                               linked list, initialized to NULL */

static NODE   *Ghead = NULL;/* pointer to first NODE in general entity
                               linked list, initialized to NULL */

/************************************************************************/
/* doentity() is the main module for processing any entity declaration. */
/* It calls routines to input PS's, get the entity name, and get the    */
/* entity text building 2 pairs of tables, 1 (parameter entity, general */
/* entity) pair as linked lists, and the second (parameter entity,      */
/* general entity) pair as output to file.                              */
/************************************************************************/
void doentity()
{
   char *nameptr, *litptr;
   int synkey;
   int pscount;
   int enttype;
   int c;

   pscount = INPPS();
   nameptr = getentname(pscount, &enttype);
   pscount = INPPS();
   synkey = getenttext(pscount, &litptr, enttype);
   pscount = INPPS();
   ADDCHAR(SPACE);
   if((c = jgetc()) == EOF)
      terminate(1, "End of File found while expecting MDO");
   if(c == MDC) {
      buildtbl(enttype, nameptr, synkey, litptr);
      ADDCHAR(MDC);
   }
   else{
      ADDCHAR(c);
      jungetc(c);
      syntxerr("MDC not found in entity declaration");
   }
}

/************************************************************************/
/* getentname() gets a valid entity name determining if the name is a   */
/* valid general entity name or a valid parameter entity name.          */
/************************************************************************/
char *getentname(pscount, typeptr)
int pscount;
int *typeptr;
{
   static char namearray[NAMELEN + 1], *nptr;
   int c, j;

   nptr = namearray;
   if((c = jgetc()) == EOF)
      terminate(1, "EOF found");
   if(c != PERO){
      /* then general entity declaration */
      *typeptr = GEN_ENT_NAME;
      if(pscount == 0)
         syntxerr("Required PS not found");
      jungetc(c);
      j = INPRNINAME( &nptr, NAMELEN, noxlat);
      if (namearray[0] == RNI)
         if(strcmp(namearray, "#DEFAULT") != 0){
            ADDCHAR(SPACE);
            ADDSTRING(namearray);
            syntxerr("Illegal use of RNI in entity name");
         }
   }
   else{
      /* else parameter entity declaration */
      *typeptr = PARM_ENT_NAME;
      ADDCHAR(SPACE);
      ADDCHAR(PERO);
      if(INPPS() >= 1)
         j = INPNAME( &nptr, NAMELEN - 1, noxlat);
      else
         syntxerr("Required PS not found in parameter entity name");
   }
   ADDCHAR(SPACE);
   ADDSTRING(namearray);
   if(j >= GOOD)
      return(namearray);
   else
      syntxerr("Entity name not found");
   /* should not be reached, only for lint */
   return(namearray);
}

/************************************************************************/
/* getenttext() gets the entity text for any entity declaration.  It    */
/* inputs a syntactic literal which may be NULL, PS's, and a parameter  */
/* literal.                                                             */
/************************************************************************/
int getenttext(pscount, litptr, enttype)
int pscount;
char **litptr;
int enttype;
{
   char namearray[NAMELEN + 1], *nameptr = namearray;
   static char litarray[LITLEN + 1];
   char *lptr;
   int j, jj, synlitsize;

   lptr = litarray;
   memset(litarray, '\0', LITLEN);
   if((j = INPNAME(&nameptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF found in entity declaration");
   switch (j){

   case KW_SYSTEM:
   case KW_PUBLIC:
      if(pscount < 1)
         syntxerr("PS not found before syntactic literal in data text");
      ADDCHAR(SPACE);
      ADDSTRING(synliteral(j));
      ADDCHAR(SPACE);
      /* call procedure to input the parameter literal */
      if (j == KW_SYSTEM)
         dosystem(YES);
      else dopublic(YES);
      if (INPPS() == EOF)
         terminate(1, "EOF found in entity declaration");
      ADDCHAR(SPACE);
      nameptr = namearray;
      if((jj = INPNAME(&nameptr, NAMELEN, TOUPPER)) == EOF)
         terminate(1, "EOF found in entity declaration");
      if (jj < GOOD)
         return(j);
      ADDSTRING(namearray);
      if (jj != KW_NDATA)
         syntxerr("illegal syntactic keyword");
      else if (enttype != GEN_ENT_NAME)
         syntxerr("General entity name required when using entity type");
      if (INPPS() == EOF)
         terminate(1, "EOF found in entity declaration");
      nameptr = litarray;
      if((jj = INPNAME(&nameptr, NAMELEN, TOUPPER)) == EOF)
         terminate(1, "EOF found in entity declaration");
      if (jj < GOOD)
         syntxerr("expected NOTATION NAME");
      *litptr = litarray;
      return(j);
      /* cases for DATA TEXT */
   case KW_CDATA:
   case KW_SDATA:
      /* CDATA and SDATA may only be used with in a general entity decl */
      if(enttype == PARM_ENT_NAME){
         ADDCHAR(SPACE);
         ADDSTRING(synliteral(j));
         syntxerr("Illegal use of data text while processing entity text");
      }
   case KW_PI:
      if(pscount < 1)
         syntxerr("PS not found before syntactic literal in data text");
      ADDCHAR(SPACE);
      ADDSTRING(synliteral(j));
      if (INPPS() == EOF)
         terminate(1, "EOF found in entity declaration");
      ADDCHAR(SPACE);
      /* call procedure to input the parameter literal */
      inpparmlit(&lptr);
      *litptr = litarray;
      /* return the syntactic literal */
      return(j);

      /* cases for BRAKETED TEXT */
   case KW_STARTTAG:
      synlitsize = STARTTAGSIZE;
      goto SHARE;
   case KW_ENDTAG:
      synlitsize = ENDTAGSIZE;
      goto SHARE;
   case KW_MS:
      synlitsize = MSSIZE;
      goto SHARE;
   case KW_MD:
      synlitsize = MDSIZE;
SHARE:
      if(pscount < 1)
         syntxerr("PS not found before syntactic literal in braketed text");
      ADDCHAR(SPACE);
      ADDSTRING(synliteral(j));
      INPPS();
      ADDCHAR(SPACE);
      /* call procedure to input parameter literal */
      inpparmlit(&lptr);
      *litptr = litarray;
      /* if the length of the parameter literal plus the length of */
      /* the corresponding bracketed text delimeters exceeds LITLEN */
      /* then ERROR */
      if((synlitsize + strlen(litarray)) > LITLEN)
         syntxerr("LITLEN exceeded in interpreted bracketed text.");
      /* return the syntactic literal */
      return(j);
      /* If BAD name, ie. NULL syntactice literal, then check if */
      /* there exists a parameter literal */
   case BAD:
      if((j = jgetc()) == EOF)
         terminate(1, "End of File found while processing entity text");
      /* if a parameter literal does not exist then ERROR */
      if((j != LITA) && (j != LIT)){
         ADDCHAR(j);
         jungetc(j);
         syntxerr("Entity text not found");
      }
      jungetc(j);
      ADDCHAR(SPACE);
      /* else call procedure to input parameter literal */
      inpparmlit(&lptr);
      *litptr = litarray;
      return(NULL);
   default:
      /* syntactic literal found out of context */
      ADDCHAR(SPACE);
      ADDSTRING(synliteral(j));
      syntxerr("Entity text not found");
   }
   /*should never be reached, for lint only*/
   return(BAD);
}

/************************************************************************/
/* search() determines the type of a name passed (parameter, general)   */
/* and searches through the appropriate linked list for a match on the  */
/* name. If the name is found, it returns the syntactic literal and     */
/* parameter literal text. Otherwise, if the name is not found or the   */
/* list is empty, the procedure returns an error flag value.            */
/************************************************************************/
int search(enttype, nptr, litptr)
int enttype;
char *nptr, **litptr;
{
   NODE *Ncurrptr;
   NODE *head;
   char *lptr = *litptr;

   mprintf("Entered [search].\n");
   /* if entity name being searched for is a parameter entity name ... */
   if(enttype == PARM_ENT_NAME)
      /* search the parameter entity linked list */
      head = Phead;
   else
      /* else the name is a general entity name, so search the general */
      /* entity linked list. */
      head = Ghead;
   /* if linked list is not empty ... */
   if(head != NULL){
      Ncurrptr = head;
      /* while no match on name and not at end of linked list ... */
      while((strcmp(nptr, Ncurrptr -> Nsymbol.SSname) != 0)
          && (Ncurrptr -> Nnextptr != NULL))
         Ncurrptr = Ncurrptr -> Nnextptr;
      /* if match on name ... */
      if(strcmp(nptr, Ncurrptr -> Nsymbol.SSname) == 0){
         lptr = Ncurrptr -> Nsymbol.Sdef;
         *litptr = lptr;
         return(Ncurrptr -> Nsymbol.Ssynkey);
      }
   }
   /* else either linked list empty or name not found in list */
   *litptr = lptr;
   /* so return error flag */
   return(ILLCHAR);
}

/************************************************************************/
/* buildtbl() determines what type of entity name is passed and adds a  */
/* new node to the appropriate linked list and calls a procedure which  */
/* adds a record of info to the appropriate file table.                 */
/************************************************************************/
void buildtbl(enttype, nameptr, synkey, litptr)
int enttype, synkey;
char *nameptr, *litptr;
{
   NODE *Nnode, *Ncurrptr;
   char *malloc();
   NODE **headptr;

   /* if parameter entity name, then use parameter entity linked list */
   if(enttype == PARM_ENT_NAME)
      headptr = &Phead;
   else
      /* else use the general entity linked list */
      headptr = &Ghead;
   mprintf("Entered [buildtbl].\n");
   /* allocate memory for a new NODE */
   Nnode = (NODE *)(malloc(sizeof(NODE)));
   if(Nnode == 0)
      terminate(1, "Unable to allocate memory using malloc");
   /* set the node pointer in Nnode to NULL because it will added to the */
   /* end of the existing linked list */
   Nnode -> Nnextptr = NULL;
   mprintf("Calling [buildsymbol] from [buildtbl].\n");
   /* call procedure which adds to the appropriate file table and */
   /* sets up Nnode with valid data */
   buildandwritesymbol(enttype, &(Nnode -> Nsymbol), nameptr, synkey, litptr);
   /* if the list is empty, then simply make */
   /* Nnode the first node in the list */
   if(*headptr == NULL)
      *headptr = Nnode;
   else{
      /* else search the appropriate linked list to the end of the list */
      Ncurrptr = *headptr;
      while(Ncurrptr -> Nnextptr != NULL)
         Ncurrptr = Ncurrptr -> Nnextptr;
      /* and add Nnode to the end of the list */
      Ncurrptr -> Nnextptr = Nnode;
   }
   mprintf("Exiting [buildtbl].\n");
}

/************************************************************************/
/* buildandwritesymbol() is a procedure which updates the appropriate   */
/* file table according to whether the entity name is a parameter or    */
/* general entity name. It also fills the node which is to be added to  */
/* the appropriate linked list with proper data.                        */
/************************************************************************/
void buildandwritesymbol(enttype, symbol, nameptr, synkey, litptr)
int enttype;
SYMBOL *symbol;
char *nameptr;
int synkey;
char *litptr;
{
   int litlen,i;
   char namebuff[NAMELEN];
   char *nptr,*namebuffptr;

   mprintf("Entered [buildsymbol].\n");
   nptr = nameptr;
   namebuffptr = namebuff;
   /* put the name passed into a buffer which is right padded with spaces */
   for(i = 0; i < NAMELEN; ++i)
      *namebuffptr++ = ' ';
   namebuffptr = namebuff;
   while(*nptr != '\0')
      *namebuffptr++ = *nptr++;

   litlen = strlen(litptr) + 1;
   /* call procedure which actually writes the data to the appropriate file */
   writerecord(enttype, namebuff, synkey, litptr, litlen);
   /* copy the entity name into a node to be added to the linked list */
   strcpy (symbol->SSname, nameptr);
   /* copy the syntactic literal into the node to be added */
   symbol -> Ssynkey = synkey;
   /* copy the parameter literal ext into the node */
   strcpy (symbol -> Sdef, litptr);
   mprintf("Exiting [buildsymbol].\n");
}

/********************************************************************/
/* writerecord() writes a record of data into the appropriate file  */
/* table using a procedure called safewrite.                        */
/********************************************************************/
void writerecord(enttype, nameptr, synkey, litptr, litlen)
int enttype;
char *nameptr, *litptr;
int synkey, litlen;
{
   int file;

   /* if entity a parameter entity then use parameter entity file table */
   if(enttype == PARM_ENT_NAME)
      file = preffile;
   else
      /* else use the general entity file table */
      file = greffile;
   /* first field = length of parameter literal text */
   safewrite(file,(char *)&litlen,sizeof(litlen));
   /* second field = entity name */
   safewrite(file, nameptr, (unsigned)(NAMELEN));
   /* third field = syntactic literal */
   safewrite(file,(char *)&synkey, sizeof(synkey));
   /* fourth field = parameter literal text */
   safewrite(file, litptr, (unsigned)litlen);
}
/* ================================================================ */
