/* 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:    DTDATTR.C                                          */
/*   AUTHOR:         Jim Heath                                          */
/*                                                                      */
/*   DATE CREATED:                                                      */
/*   LAST MODIFIED:                                                     */
/*                                                                      */
/*                  REVISIONS                                           */
/*   WHEN      WHO            WHY                                       */
/************************************************************************/
#include <stdio.h>
#include <setjmp.h>
#include <memory.h>
#include <unistd.h>

#include "qntyset.h"
#include "dtd.h"
#include "dtdfncs.h"
#include "dtdglbl.h"
#include "dtddefs.h"
long lseek();
static ATTRIBSTRUCT attrib;
static int decltokencount, totdfltcount;
/* ============================================================ */
int doattlist()
{
   REGISTER int j;
   int eltcount, namegrp = FALSE;
   char *nmptr, eltname[NAMELEN + 1], *eltnmptr;
   char tempnames[GRPCNT * (NAMELEN + 1)];

   totdfltcount = 0;
   memset(&attrib, '\0', sizeof(attrib));
   memset(eltname, '\0', sizeof(eltname));
#ifdef OLD
#else
	memset(tempnames, ' ', sizeof(tempnames));
#endif
   if ((j = INPPS()) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if (j < 1)
      syntxerr("Expected PS+ at this point");
   ADDCHAR(SPACE);
   nmptr = eltname;
   if((j = INPNAME(&nmptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if (j < GOOD) {
      namegrp = TRUE;
      eltcount = getnamegrp(tempnames, ISALPHA, isnmchar);
      eltnmptr = tempnames;
   }
   else {
      ADDSTRING(eltname);
      eltcount = 1;
   }

   if ((j = INPPS()) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if (j < 1)
      syntxerr("Expected PS+ at this point");
   ADDCHAR(SPACE);
   safewrite(attrtemp, (char *)&eltcount, sizeof(eltcount));
   if (namegrp == TRUE)
      for (j = 0; j < eltcount; j++, eltnmptr += NAMELEN + 1)
         safewrite(attrtemp, (char *)eltnmptr, NAMELEN + 1);
   else
      safewrite(attrtemp, (char *)eltname, sizeof(eltname));
   return(doattlist2());
}
/* ============================================================ */
int doattlist2()
{
   char *nmptr;
   REGISTER int j;
   int attindex = 0;
   char attnames[ATTCNT] [NAMELEN + 1];
   char decltokens[ATTCNT * (NAMELEN + 1)];
   int declvalcode, dfltvalcode, k,
   notatcount = 0, idcount = 0, attrcount = 0;

   memset (decltokens, '\0', sizeof(decltokens));
   decltokencount = 0;
   while(1) {
#ifdef OLD
#else
		memset(attrib.Agrpnames, '\0', sizeof(attrib.Agrpnames));
#endif
      ADDSTRING("\015\012          ");
      nmptr = attrib.Aname;

      if((j = INPNAME(&nmptr, NAMELEN, TOUPPER)) == EOF)
         terminate(1, "EOF found in ATTLIST declaration");
      if (j >= GOOD){
         ADDSTRING(attrib.Aname);
         for (k = 0; k < attindex; k++)
            if (strcmp(attnames[k], attrib.Aname) == 0)
               syntxerr("duplicate attribute name in ATTLIST");
         if (attindex > ATTCNT)
            syntxerr("ATTCNT exceeded in ATTLIST");
         strcpy(attnames[attindex++], attrib.Aname);
      }
      else
         syntxerr("expected valid attribute name");
      for (j = strlen(attrib.Aname); j < NAMELEN; j++)
         attrib.Aname[j] = ' ';

      declvalcode = getdeclvalcode(decltokens);
      if (declvalcode == KW_ID)
         if(idcount++ > 0)
            syntxerr("ID can be declared only once in an ATTLIST");
      if (declvalcode == KW_NOTATION) {
         if(notatcount++ > 0)
            syntxerr("NOTATION can be declared only once in an ATTLIST");
         if ((j = INPPS()) == EOF)
            terminate(1, "EOF found in ATTLIST declaration");
         ADDCHAR(SPACE);
         (void) getdeclgroup( decltokens);
      }
      /* convert declared value code */
      cvrtdeclcode (declvalcode);
      dfltvalcode = getdfltvalcode();

      /* convert default value code */
      cvrtdfltcode(dfltvalcode);

      analyzedflt(declvalcode, dfltvalcode, attrib.Adfltval);
      if (doattlist3(&attrcount))
         return(attrcount);
   }
}
/* ============================================================ */
int doattlist3(attrcount)
int *attrcount;
{
   static long position;
   long position2;
   int j;
   if (*attrcount == 0) {
      if((position = lseek(attrtemp, 0L, SEEK_CUR)) == -1L)
         terminate(1,"error on lseek");
      safewrite(attrtemp, attrcount, sizeof(int));
   }
   if ((*attrcount)++ >= ATTCNT)
      syntxerr("too many attributes in this ATTLIST");


   safewrite(attrtemp, attrib.Aname, sizeof(attrib.Aname));
   safewrite(attrtemp, &attrib.Adeclcode, sizeof(attrib.Adeclcode));
   safewrite(attrtemp, &attrib.Adfltcode, sizeof(attrib.Adfltcode));
   safewrite(attrtemp, attrib.Adfltval, sizeof(attrib.Adfltval));
   safewrite(attrtemp, &attrib.Agrpcount, sizeof(attrib.Agrpcount));
   for ( j = 0; j < attrib.Agrpcount; j++){
      safewrite(attrtemp, attrib.Agrpnames[j].lnames, NAMELEN + 1);
   }

   if ((j = INPPS()) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if ((j = jgetc()) == MDC){
      ADDCHAR(MDC);
      if((position2 = lseek(attrtemp, 0L, SEEK_CUR)) == -1L)
         terminate(1,"error on lseek");
      if ((position = lseek(attrtemp, position, SEEK_SET)) == -1L)
         terminate(1,"error on lseek");
      safewrite(attrtemp, attrcount, sizeof(int));
      if((position = lseek(attrtemp, position2, SEEK_SET)) == -1L)
         terminate(1,"error on lseek");
      if ((*attrcount + decltokencount) > ATTCNT)
         syntxerr("total number of names and name tokens exceeds ATTCNT");
      return(*attrcount);
   }
   ADDCHAR(' ');
   jungetc(j);
   return(0);
}
/* =========================================================== */
static int getdeclvalcode(decltokens)
char *decltokens;
{
   REGISTER int j;
   char temp[NAMELEN+1], *tmpptr = temp;

   if ((j = INPPS()) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if (j < 1)
      syntxerr("Expected PS+ at this point");
   ADDCHAR(SPACE);
   if ((j = INPNMTOKEN(&tmpptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF in ATTRIBUTE declaration");
   if (j > GOOD)
      ADDSTRING(temp);
   else
      j = getdeclgroup(decltokens);
   return(j);
}
/* =========================================================== */
static int getdeclgroup(decltokens)
char *decltokens;
{
   char tempnames[GRPCNT * (NAMELEN + 1)], *dgrpptr = tempnames, *dptr;
   REGISTER int j;

   attrib.Agrpcount = getnamegrp(tempnames, isnmchar, isnmchar);
   if (attrib.Agrpcount > GRPCNT)
      terminate(1, "too many declared values!");
   for (j = 0; j < attrib.Agrpcount; j++, dgrpptr += (NAMELEN + 1)) {
      strcpy(attrib.Agrpnames[j].lnames, dgrpptr);
      for (dptr = decltokens; strlen(dptr) != 0; dptr += (NAMELEN + 1)) {
         if(memcmp(dgrpptr, dptr, NAMELEN + 1) == 0)
            syntxerr("duplicate declared values in same ATTLIST");
      }
      strcpy(dptr, dgrpptr);
      if (++decltokencount >= ATTCNT)
         syntxerr("ATTCNT exceeded in ATTLIST");
   }
   return(KW_GROUP);
}
/* =========================================================== */
static int getdfltvalcode()
{
   REGISTER int j;
   char namearray[NAMELEN+1], *tmpptr = namearray;

   if ((j = INPPS()) == EOF)
      terminate(1, "EOF found in ATTLIST declaration");
   if (j < 1)
      syntxerr("Expected PS+ at this point");
   ADDCHAR(SPACE);
   if ((j = INPRNINAME(&tmpptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF in ATTRIBUTE declaration");
   if (j >= GOOD)
      switch (j) {
      case KW_FIXED:
         ADDSTRING(namearray);
         ADDCHAR(' ');
         if (INPPS() == EOF)
            terminate(1, "EOF found in ATTLIST declaration");
         (void) getdfltlit();
         return(j);
      case KW_REQUIRED:
      case KW_CURRENT:
      case KW_CONREF:
      case KW_IMPLIED:
         ADDSTRING(namearray);
         return (j);
      default:
         syntxerr("Illegal default value for attribute");
      }
   return(getdfltlit());
}
/* =========================================================== */
static int getdfltlit()
{
   char buff[LITLEN + 1], *ccptr = buff;
   REGISTER char *cptr = buff;
   char *src, *dest, *resptr;

   memset(buff, '\0', sizeof(buff));
   if (attrib.Adeclcode == ENUM_CDATA) {
      inputattrvalspec(&ccptr) ;
      dest = attrib.Adfltval;
      src = buff;
      do {
         if((*src == TAB) || (*src == RE))
            *dest++ = SPACE;
         else if ((*src == EE) || (*src == RS))
            continue;
         else
            *dest++ = *src;
      }       while (*src++ != '\0');
   }
   else {
      inputattrvalspec(&ccptr) ;
      dest = attrib.Adfltval;
      src = buff;
      do {
         if((*src == TAB) || (*src == RE))
            *dest++ = SPACE;
         else if ((*src == EE) || (*src == RS))
            continue;
         else
            *dest++ = *src;
      }       while (*src++ != '\0');
   }
   if (attrib.Adeclcode != ENUM_CDATA && attrib.Adeclcode != ENTITY)
      for (cptr = attrib.Adfltval; *cptr != '\0'; cptr++)
         *cptr = TOUPPER(*cptr);
   /* this code checks to make sure the default value on */
   /* entity is a valid general entity name */
   if (attrib.Adeclcode == ENTITY) {
      cptr = attrib.Adfltval;
      if ((strcmp(cptr,"#DEFAULT")) == 0 || 
          (search(GEN_ENT_NAME, cptr, &resptr)) == ILLCHAR)
         syntxerr("default value must be a valid general entity name\n");
   }
   return(KW_LIT);
}
/* =========================================================== */
static void analyzedflt(declval, dfltval, dflt)
int declval;
char *dflt;
{
   char temp[128];
   switch(declval) {
   case KW_CDATA:
      return;
   case KW_IDREF:
   case KW_NAME:
      if (dfltval == KW_LIT)
         validate(dflt, 1, ISALPHA, isnmchar);
      return;
   case KW_IDREFS:
   case KW_NAMES:
      if (dfltval == KW_LIT)
         validate(dflt, 0, ISALPHA, isnmchar);
      return;
   case KW_NUMBER:
      if (dfltval == KW_LIT)
         validate(dflt, 1, ISDIGIT, ISDIGIT);
      return;
   case KW_NUMBERS:
      if (dfltval == KW_LIT)
         validate(dflt, 0, ISDIGIT, ISDIGIT);
      return;
   case KW_NMTOKEN:
      if (dfltval == KW_LIT)
         validate(dflt, 1, isnmchar, isnmchar);
      return;
   case KW_NMTOKENS:
      if (dfltval == KW_LIT)
         validate(dflt, 0, isnmchar, isnmchar);
      return;
   case KW_NUTOKEN:
      if (dfltval == KW_LIT)
         validate(dflt, 1, ISDIGIT, isnmchar);
      return;
   case KW_NUTOKENS:
      if (dfltval == KW_LIT)
         validate(dflt, 0, ISDIGIT, isnmchar);
      return;
   case KW_GROUP:
   case KW_NOTATION:
      if (dfltval == KW_LIT)
         grpvalidate(dflt);
      return;
   case KW_ID:
      if((dfltval != KW_REQUIRED) && (dfltval != KW_IMPLIED)) {
         strcpy (temp,"DEFAULT VALUE must be #REQUIRED or #IMPLIED when ");
         strcat (temp,"DECLARED VALUE is ID\n");
         syntxerr(temp);
      }
   }
}

/* =========================================================== */
static void cvrtdeclcode(tdeclval)
int tdeclval;
{
   switch(tdeclval) {
   case KW_GROUP:
      attrib.Adeclcode = GROUP;
      break;
   case KW_CDATA:
      attrib.Adeclcode = ENUM_CDATA;
      break;
   case KW_ENTITY:
      attrib.Adeclcode = ENTITY;
      break;
   case KW_ID:
      attrib.Adeclcode = ID;
      break;
   case KW_IDREF:
      attrib.Adeclcode = IDREF;
      break;
   case KW_NAME:
      attrib.Adeclcode = NAME;
      break;
   case KW_NMTOKEN:
      attrib.Adeclcode = NMTOKEN;
      break;
   case KW_NUTOKEN:
      attrib.Adeclcode = NUTOKEN;
      break;
   case KW_IDREFS:
      attrib.Adeclcode = IDREFS;
      break;
   case KW_NAMES:
      attrib.Adeclcode = NAMES;
      break;
   case KW_NMTOKENS:
      attrib.Adeclcode = NMTOKENS;
      break;
   case KW_NUTOKENS:
      attrib.Adeclcode = NUTOKENS;
      break;
   case KW_NOTATION:
      attrib.Adeclcode = NOTATION;
      break;
   case KW_NUMBER:
      attrib.Adeclcode = NUMBER;
      break;
   case KW_NUMBERS:
      attrib.Adeclcode = NUMBERS;
      break;
   default:
      syntxerr("illegal declared value in ATTLIST");
   }
}
/* =========================================================== */
static void cvrtdfltcode(tdfltval)
int tdfltval;
{
   switch (tdfltval) {
   case KW_REQUIRED:
      attrib.Adfltcode = A_REQD;
      break;
   case KW_FIXED:
      attrib.Adfltcode = A_FIXED;
      break;
   case KW_CURRENT:
      attrib.Adfltcode = A_CURRENT;
      break;
   case KW_LIT:
      attrib.Adfltcode = A_UNFIXED;
      break;
   case KW_IMPLIED:
      attrib.Adfltcode = A_IMPLIED;
      break;
   case KW_CONREF:
      attrib.Adfltcode = A_CONREF;
      break;
   }
}
/* =========================================================== */
static void validate(buff, maxcount, firsttest, othertest)
char *buff;
int maxcount;
int (*firsttest)(), (*othertest)();
{
   int itemcount = 0;
   char outbuf[512], *temp;
   REGISTER char *src = buff, *dest = outbuf;
   int normlen = NORMSEP ;  /* one initial normsep */

   if (*src <= ' ')
      syntxerr("SEPARATOR not allowed");
   goto GETITEM;
   while (*src != '\0') {
      if (*src <= ' ') {
         *dest++ = *src++;
         *dest = '\0';
         while (*src <= ' ')
            src++;
      }
GETITEM:
      for (temp = dest; *src > ' ';) {
         *dest++ = *src++;
         *dest = '\0';
      }
      if (temp != dest) {
         if (strlen(temp) > NAMELEN)
            syntxerr("item too long in DEFAULT VALUE FIELD");
         if (!(*firsttest)(*temp++))
            syntxerr("illegal value in DEFAULT VALUE FIELD");
         normlen++;   /* increment normalized length */
         while (*temp != '\0') {
            if (!(*othertest)(*temp++))
               syntxerr("illegal value in DEFAULT VALUE FIELD");
            normlen++;   /* increment normalized length */
         }
         if ((++itemcount > maxcount) && (maxcount != 0))
            syntxerr("too many items in ATTRIBUTE VALUE SPECIFICATION");
         normlen += NORMSEP ;  /* one normsep for every item in list */
      }
   }
   if (normlen > LITLEN)
      syntxerr("normalized length of DEFAULT VALUE FIELD exceeds LITLEN");
   if (itemcount){
      totdfltcount += itemcount;
      return;
   }
   syntxerr("DEFAULT VALUE FIELD is empty!");
}
/* =========================================================== */
static void grpvalidate(buff)
char *buff;
{
   int j, itemcount = 0;
   char outbuf[512];
   REGISTER char *src = buff, *dest = outbuf;

   if (*src <= ' ')
      syntxerr("SEPARATOR not allowed");
   goto GETITEM;
   while (*src != '\0') {
      while (*src <= ' ')
         src++;
GETITEM:
      for (dest = outbuf; *src != '\0'; ) {
         if (*src <= ' ')
            break;
         *dest++ = *src++;
      }
      *dest = '\0';
      for (j = 0; j < attrib.Agrpcount ;j++) {
         if(strcmp(outbuf, attrib.Agrpnames[j].lnames) == 0)
            goto FOUNDIT;
      }
      syntxerr("DEFAULT VALUE does not agree with DECLARED VALUE");
FOUNDIT:
      itemcount++;
   }
   if (itemcount){
      totdfltcount += itemcount;
      return;
   }
   syntxerr("DEFAULT VALUE FIELD is empty!");
}
/* =========================================================== */
/* ============================================================ */
/* inputattrvalsspec() inputs an attribute value specification  */
/* delimeted by  either  LIT's or LITA's.  It only recognizes   */
/* ERO's and CRO's as mark-up due to the standards definition   */
/* of replaceable character data.                               */
/* ============================================================ */
void inputattrvalspec(litptr)
char **litptr;
{
   int delimeter, synkey, len = 0;
   REGISTER int c, d;
   char *lptr = *litptr;
   int refflag = 0; /* flag if 1 => entity reference found in */
   /*              literal with unmatch EE */
   /*      if 0 => no unmatched reference currently in */
   /*              literal */

   if((c = jgetc()) == EOF)
      terminate(1, "End of File found while processing ATTLIST");
   /* if LIT then delimeter is LIT */
   if(c == LIT)
      delimeter = LIT;
      /* else delimeter is LITA */
   else
      if(c == LITA)
         delimeter = LITA;
      else{
         ADDCHAR(c);
         jungetc(c);
         syntxerr("Delimeter not found in attribute value specification");
      }
   /* while closing delimeter not found ...*/
   while((c = jgetc()) != delimeter){
      switch ((char) c){
      case EE:
         /* EE found not matching any existing reference within 
                                                         attribute value specification */
         if(refflag == 0)
            syntxerr("EE found terminating reference not occurring within attribute value specification");
         refflag = 0;
         break;
         /* if ERO, then check if entity reference */
      case ERO:
         if ((c = jgetc()) == EOF)
            terminate(1, "End of File found while processing ATTLIST declaration");
         /* if reference ... */
         if(ISALPHA(c)){
            jungetc(c);
            /* call procedure to resolve entity reference within */
            /* replaceable parameter data */
            synkey = reslvgref(&lptr, &len);
            if ((synkey==KW_CDATA || synkey==KW_SDATA)
                && attrib.Adeclcode != ENUM_CDATA)
               syntxerr("A CDATA or SDATA entity must be referenced where character data can occur");
            if (synkey == KW_CDATA || synkey ==  KW_SDATA)
               len += 2;
            /* set flag to unmatched reference found in attribute value specification */
            refflag = 1;
         }
         else if(c == '#') {
            if ((d = jgetc()) == EOF)
               terminate(1, "End of File found while interpreting parameter literal");
            if(ISDIGIT(d) || isnmstrt(d)) {
               jungetc(d);
               /* if CRO, then call procedure to resolve char reference */
               reslvcharref(&lptr, &len);
            }
            else{
               *lptr++ = '&';
               *lptr++ = c;
               *lptr++ = d;
               *lptr = '\0';
               len += 3;
            }
         }
         else{
            /* otherwise, no reference, so treat ERO as char data */
            *lptr++ = ERO;
            *lptr++ = c;
            *lptr = '\0';
            len += 2;
         }
         break;
         /* default => char data */
      default:
         *lptr++ = c;
         *lptr = '\0';
         len++;
         break;
      }
      /* if length of interpreted literal exceeds LITLEN then ERROR */
      if(len > LITLEN){
         ADDCHAR(LIT);
         ADDSTRING(*litptr);
         syntxerr("LITLEN is exceeded in attribute value specification");
      }
   }
   ADDCHAR(LIT);
   ADDSTRING(*litptr);
   ADDCHAR(LIT);
   *litptr = lptr;
}

/* =========================================================== */
/* ============================================================ */
/* reslgref() resolves general entity references within         */
/* replaceable character data. It inputs the reference name,    */
/* searches a table on the name, and adds then entity text to   */
/* the interpreted literal string.                              */
/* ============================================================ */
int reslvgref(litptr, lenptr)
char **litptr;
int *lenptr;
{
   REGISTER char *lptr = *litptr;
   char namearray[NAMELEN + 1], *nameptr = namearray;
   REGISTER int c;
   int len;
   char *resptr;
   int synkey;

   /* input name */
   if(INPNAME(&nameptr, NAMELEN, noxlat) >= GOOD){
      nameptr = namearray;
      synkey = search(GEN_ENT_NAME, nameptr, &resptr);
      switch(synkey){
      case ILLCHAR:
         syntxerr("Entity reference not found in table");
         break;
         /* if syntactic literal is NULL, then entity text consists of */
         /* parameter literal only */
      case NULL:
      case KW_CDATA:
      case KW_SDATA:
         strcat(lptr, resptr);
         len = strlen(resptr);
         lptr += len;

         *lptr++ = EE;
         *lptr = '\0';

         break;
      case KW_STARTTAG:
         strcat(lptr, "<");
         strcat(lptr, resptr);
         strcat(lptr, ">");
         len = strlen(resptr) + 2;
         lptr += len;
         *lptr++ = EE;
         *lptr = '\0';
         break;
      case KW_ENDTAG:
         strcat(lptr, "</");
         strcat(lptr, resptr);
         strcat(lptr, ">");
         len = strlen(resptr) + 3;
         lptr += len;
         *lptr++ = EE;
         *lptr = '\0';
         break;
         /* any other syntactic literal found is ERROR */
      case KW_MS:
         strcat(lptr, "<![");
         strcat(lptr, resptr);
         strcat(lptr, "]]>");
         len = strlen(resptr) + 6;
         lptr += len;
         *lptr++ = EE;
         *lptr = '\0';
         break;
         /* any other syntactic literal found is ERROR */
      case KW_MD:
         strcat(lptr, "<!");
         strcat(lptr, resptr);
         strcat(lptr, ">");
         len = strlen(resptr) + 3;
         lptr += len;
         *lptr++ = EE;
         *lptr = '\0';
         break;
         /* any other syntactic literal found is ERROR */
      default:
         syntxerr("Unknown or illegal syntactic literal in attribute value specification.");
         break;
      }
   }
   else
      syntxerr("Reference name not found while resolving parameter entity reference.");
   /* increment length of interpreted literal by length of entity text */
   *lenptr = *lenptr + len;
   if((c = jgetc()) == EOF)
      terminate(1, "EOF found while processing ATTLIST");
   if(c != REFC)
      jungetc(c);
   *litptr = lptr;

   return(synkey);
}
