/*******************************************************************************
* YORKTOWN ADVANCED SGML PARSER (YASP)                                         *
*         An SGML Parser conforming to International Standard ISO 8879 --      *
*         Standard Generalized Markup Language                                 *
*------------------------------------------------------------------------------*
* Written 1987 - 1993 by                                                       *
*         Pierre G. Richard                                                    *
*         IBM T.J. Watson Research Center                                      *
*         Yorktown Heights, New York                                           *
*         petrus@watson.ibm.com                                                *
*------------------------------------------------------------------------------*
* FUNCTION NAME    = YASP / DTD compiler                                       *
* STATUS           = Version 0, Release 1, Level 34 and up                     *
* COMPONENT NAME   = odtcomp.c                                                 *
* DESCRIPTIVE NAME = Compile the Document Type Declaration (Main)              *
* DATE_WR          = 03/29/1988                                                *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
*                                                                              *
* IMPLEMENTATION NOTE =                                                        *
*                                                                              *
* DEPENDENCIES =  (see Include list)                                           *
*                                                                              *
* IMPORT=                                                                      *
*                                                                              *
* EXPORT=                                                                      *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & Reason (brief)                             *
*      1 08/28/88 PGR      Need to keep Parameter Entities for MDMS (<![ [)    *
*      2 01/07/90 PGR      Do not compile a DTD if the ODT already exists      *
*      3 02/23/90 PGR      RANK Feature                                        *
*      4 12/09/90 PGR      Added descriptor for CDATA attributes               *
*      5 02/24/91 PGR      Added a test to check duplicate FPI                 *
*      6 08/29/91 PGR      Write the true final size of the ATD array          *
*      7 02/29/92 PGR      Redo the unlocate descriptor algorithm              *
*      8 01/08/93 PGR      Reference count on SNODE required for safe free     *
*******************************************************************************/

/*-------------+
|Include Files |
+-------------*/
#include "machine.h"
#include "portable.h"
#include "yaspapi.h"
#include "global.h"
#include "usrlib.h"
#include "sgml.h"
#include "hash.h"
#include "odt.h"
#include "yasp.h"
#include "gtype.h"

/*--------------+
|Local Functions|
+--------------*/
static CHOICES const * _loadds LP_FCT adr_dtss_choices(void);
static boolean  LP_FCT parse_dtd         (COMMON_P);
static void     LP_FCT write_odt         (COMMON_P, uchar *, EXID *);
static void     P_FCT  check_elt_dclared (void STACK_BASED *, EHSH *);
static void     P_FCT  build_elt_atd_mdl (void STACK_BASED *, EHSH *);
static void     P_FCT  build_rkl         (void STACK_BASED *, EHSH *);
static void     LP_FCT skip_faked_ps     (GA_P);
static boolean  LP_FCT unlocate_desc_ety (COMMON_P, ADLEN *);
static void     LP_FCT check_dupl_fpi    (COMMON_P);

/*----------------+
|Local Definitions|
+----------------*/
#define SRM_HASHSIZE  5   /* For about  20 entries */
#define NOTA_HASHSIZE 5   /* For about  20 entries */
#define STEM_HASHSIZE 7   /* For about  50 entries */
#define ETY_HASHSIZE 29   /* For about 100 entries */
#define ELT_HASHSIZE 53   /* For about 200 entries */

/*STATIC--------------------------------------------------------------odt_uatd-+
|Copy descriptors ADLEN in the descriptor array "asis".                        |
|The entity pointers in the ADLEN will be unlocated later, after all ety have  |
|been solved (unlocate_desc_ety).  To make the difference between a 1st adlen  |
|describing a lit "" and a subadlen describing a empty portion of text,        |
|the former is replaced from ((void *)0, 0) to (M_p, 0).  M_p plays the role   |
|of a well-known address (cannot be an ETY *, nor (void *)0 describing text).  |
+-----------------------------------------------------------------------------*/
void P_FCT odt_uatd(COMMON_P M_p, ATD *atd_p)
{
   ADLEN *desc_p;
   uint nbr_of_desc;
   GA_P gbla_p = M_p->gbla_p;

   if IS_BITON(atd_p->is,DESC_AF) {
      if (!(desc_p = atd_p->val.desc._.p)->len) {  /* To avoid confusion */
        desc_p->_.p = M_p;                         /* put any unique address */
      }
      memcpy(
         M_p->desc_p,
         desc_p = atd_p->val.desc._.p,
         (nbr_of_desc = atd_p->val.desc.len) * sizeof(ADLEN)
      );
      atd_p->val.desc._.ix = diffptr(M_p->desc_p, M_p->desc_orig);
      M_p->desc_p += nbr_of_desc;
      free(desc_p);
   }
   *(M_p->atd_p++) = *atd_p;              /* copy atd */
}

/*STATIC--------------------------------------------------------check_dupl_fpi-+
| Check that FPI's are different each other (10.2.2, last paragraph).          |
+-----------------------------------------------------------------------------*/
static void LP_FCT check_dupl_fpi(COMMON_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   EXID *exid_pL, *exid_p1, *exid_p2;
   uint  len, len1;
   uchar *c_p, *c_p1, *c_p2;

   exid_pL = M_p->exid_orig + GA(nbr_of_exid) - 1;        /* last */
   for (exid_p1 = M_p->exid_orig; exid_p1 < exid_pL; ++exid_p1) {
      if (IS_BITON(exid_p1->is,VALIDFPI_XF)) {
         len = exid_p1->pid.len;
         c_p = sk_point(gbla_p,exid_p1->val._.ofs + exid_p1->pid.rel) - 1;
         for (exid_p2 = exid_p1+1; exid_p2 <= exid_pL; ++exid_p2) {
            if ((IS_BITON(exid_p2->is,VALIDFPI_XF))&&(len==exid_p2->pid.len)) {
               len1 = len + 1;
               c_p1 = c_p;
               c_p2 = sk_point(gbla_p,exid_p2->val._.ofs+exid_p2->pid.rel)-1;
               for (;;) {                              /* compare to the end */
                  if (--len1 == 0) {                   /* end reached ? */
                     errorh(gbla_p,DUPFPI,ERH_W_SEV,   /* YES: identical */
                        sk_point(gbla_p,exid_p1->name._.ofs),
                        sk_point(gbla_p,exid_p2->name._.ofs)
                     );
                     break;
                  }
                  if (*++c_p1 != *++c_p2) break;       /* different: OK */
               }
            }
         }
      }
   }
}

/*STATIC-----------------------------------------------------check_elt_dclared-+
|Check if elements have all been declared.                                     |
|This is done before creating and filling the ELT array, so we can             |
|add/delete(?) ELT structures with minimized troubles...                       |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
static void P_FCT check_elt_dclared(void STACK_BASED *XM_p, EHSH *ehsh_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ENODE *enode_p;

   if (!((GI *)ehsh_p->var_p)->enode_p) {
      errorh(gbla_p, INVELTREF, ERH_I_SEV, ehsh_p->name_p);
      enode_p = ((GI *)ehsh_p->var_p)->enode_p = (ENODE *)zalloc(sizeof(ENODE));
      enode_p->is.any = TRUE;                   /* Installed as O O ANY */
      /* to denote it is generated, enode_p->is.pcdata is not set to TRUE! */
   }else {
      if ((((GI *)ehsh_p->var_p)->mdl_id == 0) &&
         (((GI *)ehsh_p->var_p) != M_p->doc_elmt_gi_p)) {
         errorh(gbla_p, NOGIREF, ERH_I_SEV, ehsh_p->name_p);
      }
   }
   ((GI *)ehsh_p->var_p)->hash_ix = M_p->nbr_of_elt++;
}
#undef M_p

/*STATIC-----------------------------------------------------build_elt_atd_mdl-+
|Build ELT, MDL, and ATD arrays from ENODE, MNODE and ANHDR.                   |
|The ENODE, MNODE, ANODE, ANHDR structures are freed.                          |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
static void P_FCT build_elt_atd_mdl(void STACK_BASED * XM_p, EHSH *ehsh_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ELT *elt_p;
   ushort rx;
   SNODE *snode_p;
   ENODE *enode_p;
   ANHDR *anhdr_p;
   RKLN  *rkln_p;

   enode_p =  ((GI *)ehsh_p->var_p)->enode_p;
   snode_p =  ((GI *)ehsh_p->var_p)->snode_p;
   anhdr_p =  ((GI *)ehsh_p->var_p)->anhdr_p;
   rkln_p  =  ((GI *)ehsh_p->var_p)->rkln_p;
   elt_p = M_p->elt_orig + ((GI *)ehsh_p->var_p)->hash_ix;
   elt_p->name.ofs =
   sk_park(M_p->gbla_p,ehsh_p->name_p,1+strlen(ehsh_p->name_p));
   elt_p->is = enode_p->is;                /* copy flags */
   /*---------+
   |set up ELT|
   +---------*/
   if (!enode_p->is.solved) {              /* if not already "solved": */
      if (enode_p->mexcpt.p) {
         elt_p->mex.ix = diffptr(M_p->ex_p, M_p->ex_orig);
         elm_exbl(M_p,enode_p->mexcpt.p);
      }else {
         elt_p->mex.ix = -1;
      }
      if (enode_p->pexcpt.p) {
         elt_p->pex.ix = diffptr(M_p->ex_p, M_p->ex_orig);
         elm_exbl(M_p,enode_p->pexcpt.p);
      }else {
         elt_p->pex.ix = -1;
      }
      enode_p->mexcpt.ix = elt_p->mex.ix;
      enode_p->pexcpt.ix = elt_p->pex.ix;
      if ((enode_p->is.any)||(enode_p->is.empty)||(enode_p->is.dcdata)) {
         elt_p->mdl.ix = -1;                /* no model in such case */
      }else {
         elt_p->mdl.ix = diffptr(M_p->mdl_p, M_p->mdl_orig);
         elm_mdbl(M_p,enode_p->u.mnode_p,&rx);
      }
      enode_p->u.mdl_ix = elt_p->mdl.ix;
      /*--------------------------------+
      |solve rkl.grp_ix for a same enode|
      +--------------------------------*/
      if (rkln_p) {
         uint ix0, ix;
         ix = ix0 = rkln_p->s.rkl_ix;
         do {
            M_p->rkl_orig[ix].grp.ix = (rkln_p = rkln_p->g.group_p)->s.rkl_ix;
         }while((ix = rkln_p->s.rkl_ix) != ix0);
      }
      enode_p->is.solved = TRUE;            /* "solved" now */
   }else {
      elt_p->mex.ix = enode_p->mexcpt.ix;
      elt_p->pex.ix = enode_p->pexcpt.ix;
      elt_p->mdl.ix = enode_p->u.mdl_ix;
   }
   if (!--enode_p->ref_cnt) free(enode_p);

   /*----------------------------------+
   |set up elt.rkl.ix to the first stem|
   +----------------------------------*/
   if (!rkln_p) {
      elt_p->rkl.ix = -1;
   }else {
      RKL *rkl_p;
      uint ix;
      int sts_ix;

      ix = rkln_p->s.rkl_ix;
      rkl_p = M_p->rkl_orig + ix;
      sts_ix = rkl_p->sts.ix;
      while (ix && (sts_ix == (--rkl_p)->sts.ix)) --ix;
      elt_p->rkl.ix = ix;
      free(rkln_p);
   }

   /*---------+
   |set up SRM|
   +---------*/
   if (!snode_p) {
      elt_p->srm.ix = -1;
   }else {
      elt_p->srm.ix = snode_p->u.ix;
      /* If the snode was discarded by srf_bld, it's now time to free */
      if ((snode_p->u.ix == -1) && (!--snode_p->ref_cnt)) free(snode_p);
   }

   /*---------+
   |set up ATD|
   +---------*/
   if (!anhdr_p) {                          /* no attributes associated: */
      elt_p->is.required =
      elt_p->is.current =
      elt_p->is.conref =
      elt_p->is.notation = FALSE;           /* zero unused bits */
      elt_p->atdsize = 0;                   /* set size of ATD array to zero */
      elt_p->atd.ix = -1;                   /* put a dummy value */
   }else {
      elt_p->is.required = anhdr_p->is.required;
      elt_p->is.current = anhdr_p->is.current;
      elt_p->is.conref = anhdr_p->is.conref;
      elt_p->is.notation = anhdr_p->is.notation;
      elt_p->is.entity = anhdr_p->is.entity;
      if ((elt_p->is.required)&&(!elt_p->is.reqd_open_tag)) { /* see 7.3.1.1 */
         errorh(gbla_p,INVSTGREQ,ERH_I_SEV,ehsh_p->name_p);
         elt_p->is.reqd_open_tag = TRUE;
      }
      if ((elt_p->is.empty)&&((elt_p->is.conref)||(elt_p->is.notation))) {
         elt_p->is.empty = FALSE;
         elt_p->is.any = TRUE;
         if (elt_p->is.conref) {            /* see last paragraph in 11.3.4 */
            errorh(gbla_p,INVCONREF,ERH_W_SEV,ehsh_p->name_p);
         }
         if (elt_p->is.notation) {          /* see last paragraph in 11.3.3 */
            errorh(gbla_p,INVNOTA,ERH_W_SEV,ehsh_p->name_p);
         }
      }
      elt_p->atdsize = anhdr_p->atdsize;     /* set size of ATD array */
      elt_p->atd.ix = adl_ulad(M_p,anhdr_p); /* unload and free? anhdr */
   }
}
#undef M_p

/*STATIC-------------------------------------------------------------build_rkl-+
|This function is called while traversing the hash table of stems.             |
|At each rkln_p describing a stem, fill up the corresponding STS entry -- the  |
|next one in the STS array.  Remember in sts_ix the position of the stem in    |
|the STS array.  Then, traverse the rkln_p stem link chain of ranked element   |
|with the same stem and store sts_ix in the corresponding RKL entry -- the     |
|next one of the RKL array.                                                    |
|Reuse the room left by the now useless stem-chain pointer in the RKLN         |
|to store the position of the corresponding RKL of the RKL array.              |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
static void P_FCT build_rkl(void STACK_BASED * XM_p, EHSH *ehsh_p)
{
   RKLN *rkln_p, *rkln_pN;
   RKL *rkl_p;
   uint count, sts_ix, rkl_ix;

   M_p->sts_p->count = count = (rkln_p =  (RKLN *)ehsh_p->var_p)->g.stem_cnt;
   M_p->sts_p->suffix = -1;
   M_p->sts_p->name.ofs =sk_park(
      M_p->gbla_p,ehsh_p->name_p,1+strlen(ehsh_p->name_p)
   );
   sts_ix = diffptr(M_p->sts_p, M_p->sts_orig);
   ++M_p->sts_p;
   rkl_ix = diffptr(M_p->rkl_p, M_p->rkl_orig);
   rkl_p = M_p->rkl_p;
   rkln_pN = rkln_p->s.stem_p;
   while (count--) {
      rkl_p++->sts.ix = sts_ix;
      rkln_pN = (rkln_p = rkln_pN)->s.stem_p;
      rkln_p->s.rkl_ix = rkl_ix++;
   }
   M_p->rkl_p = rkl_p;
}
#undef M_p

/*STATIC-----------------------------------------------------unlocate_desc_ety-+
| Unlocate entity pointers in descriptors, replacing addresses by their        |
| respective indices in the ETY array.  If it is plain text, set the index     |
| to -1.  Main adlen describing an empty lit are pointing M_p, so it can be    |
| differenciated from sub-adlen describing an empty portion of text (odt_uatd).|
| Now time to reset their ofs to zero (stringtable[0] = '\0').                 |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT unlocate_desc_ety(COMMON_P M_p, ADLEN *desc_p)
{
   ETY *ety_p;
   int len;
   uint count = M_p->nbr_of_attdesc + 1;

   --desc_p;
   while (--count) {                       /* for the whole array */
      len = (++desc_p)->len;               /* get the length of the literal */
      if (len == 0) desc_p->_.ofs = 0;
      do {                                 /* for each sub-ADLEN */
         if (!count) return(FALSE);
         --count;
         if ((ety_p = (ETY *)((++desc_p)->_.p)) != (ETY *)0) {
            desc_p->_.ix = ety_p->val.ext._.ety_ix;
         }else {
            desc_p->_.ix = -1;
         }
      }while (((len -= desc_p->len)) || ((count-1)&&((desc_p+1)->_.p != M_p)));
   }
   return(TRUE);
}

/*STATIC-------------------------------------------------------------write_odt-+
|Stringize and unload the structure to an utility file                         |
+-----------------------------------------------------------------------------*/
static void LP_FCT write_odt(COMMON_P M_p, uchar *DTname_p, EXID *exid_p)
{
#define MDL_BGAL   1
#define ELT_BGAL   2
#define ATD_BGAL   3
#define GETY_BGAL  4
#define PETY_BGAL  5
#define SRM_BGAL   6
#define NCB_BGAL   7
#define EXID_BGAL  8
#define SKMG_BGAL  9           /* strbuf overflow: see skmgr.c */
#define ADS_BGAL  10
#define EXC_BGAL  11
#define UT_WRHTAB(ahl,scope,itemsize,extendable)\
   htab_size = ehsh_uld(scope,itemsize,extendable,&htab);\
   ut_vput(gbla_p,ahl,htab,htab_size)

   GA_P gbla_p = M_p->gbla_p;
   AHL  ahl;                   /* out file name handle */
   int doc_elmt_ix;            /* index in ELT array */
   uint mdltab_size;
   uint elttab_size;
   uint extab_size;
   uint desctab_size;
   uint getytab_size, petytab_size;
   uint srmtab_size;
   uint dtptab_size;
   uint nadtab_size;
   uint ncbtab_size;
   uint exidtab_size;
   uint rkltab_size;
   uint ststab_size;
   uint htab_size;
   void  *htab;
   MDL   *mdltab;
   ELT   *elttab;
   EX    *extab;
   ATD   *atdtab;
   ADLEN *desctab;
   ETY   *getytab, *petytab;
   SRM   *srmtab;
   DTP   *dtptab;
   NAD   *nadtab;
   NCB   *ncbtab;
   EXID  *exidtab;
   RKL   *rkltab;
   STS   *ststab;
   ulong cap_count[CAP_MAX_SIZE];
   uchar const *odt_banner;

   /*-----------------+
   |Write the ODT file|
   +-----------------*/
   if (!ut_open(gbla_p, UTODTW_XFC, TRUE, DTname_p, exid_p, &ahl)) {
      errorh(gbla_p,BADOPDWR,ERH_T_SEV);
   }
   ehsh_tvr(M_p->elt_scope,check_elt_dclared,M_p);
   doc_elmt_ix = M_p->doc_elmt_gi_p->hash_ix;

   /*-----------------------+
   |Save the capacity count |
   +-----------------------*/
   cap_count[ENTCAP_ix]   = GA(nbr_of_g_entities) + M_p->nbr_of_p_entities;
   cap_count[ENTCHCAP_ix] = M_p->nbr_of_g_text + M_p->nbr_of_p_text +
      M_p->nbr_of_exid_text;
   cap_count[ELEMCAP_ix]  = M_p->nbr_of_elt;
   cap_count[GRPCAP_ix]   = M_p->nbr_of_model + M_p->nbr_of_useless_model;
   cap_count[MAPCAP_ix]   = (M_p->nbr_of_srmap-1) * (1 + GA(sr_max));
   cap_count[NOTCAP_ix]   = M_p->nbr_of_ncb;
   cap_count[NOTCHCAP_ix] = M_p->nbr_of_nxid_text;
   cap_count[ATTCAP_ix]   = M_p->nbr_of_attributes;
   cap_count[AVGRPCAP_ix] = M_p->nbr_of_att_dclv;
   cap_count[ATTCHCAP_ix] = M_p->nbr_of_dclv_text;
   cap_count[EXGRPCAP_ix] = M_p->nbr_of_exceptions;
   cap_count[EXNMCAP_ix]  = M_p->nbr_of_excptnames;
   cap_count[DTACAP_ix]   = M_p->nbr_of_datatag;      /* doesn't exist in ISO */
   cap_count[DTACHCAP_ix] = M_p->nbr_of_datatag_text; /* doesn't exist in ISO */
   cap_count[IDCAP_ix]    = 0;  /* irrelevant during DTD compile */
   cap_count[IDREFCAP_ix] = 0;  /* irrelevant during DTD compile */
   cap_count[LKNMCAP_ix]  = 0;  /* NOT YET IMPLEMENTED */
   cap_count[LKSETCAP_ix] = 0;  /* NOT YET IMPLEMENTED */
   cap_count[TOTALCAP_ix] = GA(sr_max);   /* irrelevant: store "SRCAP" here */
   capa_chk(gbla_p,cap_count);

   if (M_p->nbr_of_model > (MAX_ALLOC / sizeof(MDL))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,MDL_BGAL);
   }else {
      mdltab = M_p->mdl_p = M_p->mdl_orig =
      (MDL *)malloc(mdltab_size = M_p->nbr_of_model * sizeof(MDL));
   }
   if (M_p->nbr_of_elt > (MAX_ALLOC / sizeof(ELT))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,ELT_BGAL);
   }else {
      elttab = M_p->elt_orig =
      (ELT *)malloc(elttab_size = M_p->nbr_of_elt * sizeof(ELT));
   }
   if ((M_p->nbr_of_exceptions + M_p->nbr_of_excptnames) >
      (MAX_ALLOC / sizeof(EX))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,EXC_BGAL);
   }else {
      extab = M_p->ex_p = M_p->ex_orig =
      (EX *)malloc(extab_size =
      (M_p->nbr_of_exceptions + M_p->nbr_of_excptnames) * sizeof(EX));
   }
   if (M_p->nbr_of_attributes > (MAX_ALLOC / sizeof(ATD))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,ATD_BGAL);
   }else {
      atdtab = M_p->atd_p = M_p->atd_orig =
      (ATD *)malloc(M_p->nbr_of_attributes * sizeof(ATD));
   }
   if (M_p->nbr_of_attdesc > (MAX_ALLOC / sizeof(ADLEN))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,ADS_BGAL);    /* documented ? */
   }else {
      desctab = M_p->desc_p = M_p->desc_orig =
      (ADLEN *)malloc(desctab_size = M_p->nbr_of_attdesc * sizeof(ADLEN));
   }
   if (GA(nbr_of_g_entities) > (MAX_ALLOC / sizeof(ETY))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,GETY_BGAL);
   }else {
      getytab =
      (ETY *)malloc(getytab_size = GA(nbr_of_g_entities) * sizeof(ETY));
   }
   if (M_p->nbr_of_p_entities > (MAX_ALLOC / sizeof(ETY))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,PETY_BGAL);
   }else {
      petytab =
      (ETY *)malloc(petytab_size = M_p->nbr_of_p_entities * sizeof(ETY));
   }
   if ((M_p->nbr_of_srmap * (1+GA(sr_max))) > (MAX_ALLOC / sizeof(SRM))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,SRM_BGAL);
   }else {
      srmtab = M_p->srm_p = M_p->srm_orig =
      (SRM *)malloc(srmtab_size=M_p->nbr_of_srmap * (1+GA(sr_max))*sizeof(SRM));
   }
   if (M_p->nbr_of_ncb > (MAX_ALLOC / sizeof(NCB))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,NCB_BGAL);
   }else {
      ncbtab = M_p->ncb_p = M_p->ncb_orig =
      (NCB *)malloc(ncbtab_size = M_p->nbr_of_ncb * sizeof(NCB));
   }
   if (GA(nbr_of_exid) > (MAX_ALLOC / sizeof(EXID))) {
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,EXID_BGAL);
   }else {
      exidtab = M_p->exid_p = M_p->exid_orig =
      (EXID *)malloc(exidtab_size = GA(nbr_of_exid) * sizeof(EXID));
   }
   /* No check on the following: ELT max has been reached long time ago */
   dtptab = M_p->dtp_p = M_p->dtp_orig =
      (DTP *)malloc(dtptab_size = M_p->nbr_of_dtp * sizeof(DTP));

   /* No check on the following: EXID max has been reached long time ago */
   nadtab = M_p->nad_p = M_p->nad_orig =
      (NAD *)malloc(nadtab_size = M_p->nbr_of_nad * sizeof(NAD));

   /* No check on the following: ELT max has been reached long time ago */
   rkltab = M_p->rkl_p = M_p->rkl_orig =
      (RKL *)malloc(rkltab_size = M_p->nbr_of_rkl * sizeof(RKL));
   ststab = M_p->sts_p = M_p->sts_orig =
      (STS *)malloc(ststab_size = M_p->nbr_of_sts * sizeof(STS));

   M_p->ety_ix = 0;                                 /* to compute ETY index */
   M_p->ety_p = getytab;
   ehsh_tvr(M_p->stem_scope,build_rkl,M_p);
   ehsh_tvr(M_p->nota_scope,nta_bld,M_p);           /* FIRST: build notations */
   ehsh_tvr(GA(gety_scope),ety_bld,M_p);            /* SECOND: build g_ety */
   /* M_p->ety_ix = 0;  (useless) */
   M_p->ety_p = petytab;
   ehsh_tvr(GA(pety_scope),ety_bld,M_p);
   if (IS_BITON(GA(opt).ft,FORMAL_FT)) {            /* FORMAL YES */
      check_dupl_fpi(M_p);                          /* check duplicate FPI */
   }
   ehsh_tvr(M_p->srm_scope,srf_bld,M_p);            /* THIRD:  build srm */
   ehsh_tvr(M_p->elt_scope,build_elt_atd_mdl,M_p);  /* FOURTH: build ELT */
/* unlocate_desc_ety(M_p, desctab);                 ** FIFTH: unlocate desc */
   if (!unlocate_desc_ety(M_p, desctab)) exit(107); /* FIFTH: desc */

   odt_banner = bnr_odt();
   ut_write(gbla_p,ahl,odt_banner+1,1 + *odt_banner);    /* Identify an ODT */
   ut_write(gbla_p,ahl,&doc_elmt_ix,sizeof(int));
   ut_write(gbla_p,ahl,&GA(sr_max),sizeof(uint));        /* OCS dependency */
   ut_write(gbla_p,ahl,GA(qntytab),sizeof(GA(qntytab))); /* OCS dependency */
   ut_write(gbla_p,ahl,GA(dsettab),sizeof(GA(dsettab))); /* OCS dependency */
   ut_write(gbla_p,ahl,cap_count,sizeof(cap_count));
   ut_vput(gbla_p,ahl,mdltab,mdltab_size);
   ut_vput(gbla_p,ahl,elttab,elttab_size);
   ut_vput(gbla_p,ahl,extab,extab_size);
   UT_WRHTAB(ahl,M_p->elt_scope,sizeof(ELT),FALSE);
   /*-----------------------------------------------------------+
   | The ATD array can have been shrinked if errors were found. |
   | Recompute the final size (do not use an "atdtab_size".)    |
   +-----------------------------------------------------------*/
   ut_vput(gbla_p,ahl,atdtab,diffptr((char *)M_p->atd_p,(char *)M_p->atd_orig));
   ut_vput(gbla_p,ahl,desctab,desctab_size);
   ut_vput(gbla_p,ahl,getytab,getytab_size);
   UT_WRHTAB(ahl,GA(gety_scope),sizeof(ETY),TRUE);
   ut_vput(gbla_p,ahl,petytab,petytab_size);
   UT_WRHTAB(ahl,GA(pety_scope),sizeof(ETY),FALSE);
   ut_vput(gbla_p,ahl,srmtab,srmtab_size);
   UT_WRHTAB(ahl,M_p->srm_scope,(GA(sr_max)+1)*sizeof(SRM),FALSE);
   ut_vput(gbla_p,ahl,dtptab,dtptab_size);
   ut_vput(gbla_p,ahl,nadtab,nadtab_size);
   ut_vput(gbla_p,ahl,ncbtab,ncbtab_size);
   UT_WRHTAB(ahl,M_p->nota_scope,sizeof(NCB),FALSE);
   ut_vput(gbla_p,ahl,exidtab,exidtab_size);
   ut_vput(gbla_p,ahl,ststab,ststab_size);
   UT_WRHTAB(ahl,M_p->stem_scope,sizeof(STS),FALSE);
   ut_vput(gbla_p,ahl,rkltab,rkltab_size);
   sk_out(gbla_p,ut_write,ahl);
   ut_close(gbla_p,ahl);
}

/*STATIC------------------------------------------------------adr_dtss_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_dtss_choices(void)
{
   static CHOICES const dtss_choices[] = {
      { offsetof(GNWDTAB,ENTITY),   (int)ENTITY_TK    },
      { offsetof(GNWDTAB,ELEMENT),  (int)ELEMENT_TK   },
      { offsetof(GNWDTAB,ATTLIST),  (int)ATTLIST_TK   },
      { offsetof(GNWDTAB,NOTATION), (int)NOTATION_TK  },
      { offsetof(GNWDTAB,SHORTREF), (int)SHORTREF_TK  },
      { offsetof(GNWDTAB,USEMAP),   (int)USEMAP_TK    },
      { -1,                         (int)NOT_FOUND_TK }
   };
   return(dtss_choices);
}

/*STATIC-------------------------------------------------------------parse_dtd-+
|process DTD's MDs                                                             |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT parse_dtd(COMMON_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   int curr_floor_level = GA(floor_level); /* save current floor_level */
   int init_enty_level  = GA(enty_level);  /* remember where we started */
   boolean is_real_dtd;

   psh_mode(gbla_p,DS_MODE);
   GA(quiet_at_floor) = TRUE;              /* allow an Ee to occur */
   skip_ds(gbla_p);
   GA(quiet_at_floor) = FALSE;
   if (GA(curr_token) != MDO_TK) {
      is_real_dtd = FALSE;
   }else {
      is_real_dtd = TRUE;
      do {
         GA(floor_level) = GA(enty_level);   /* set current floor_level */
         psh_mode(gbla_p,MD_MODE);
         switch (choos_in(gbla_p,adr_dtss_choices(),(GNWD *)&GA(gnwdtab))) {
         default:
            errorh(gbla_p,INVKWD,ERH_T_SEV+ERH_LOCATE);   /* Invalid */
            break;
         /*--------------- entity set */
         case ENTITY_TK:
            ety_deco(M_p);
            break;
         /*--------------- element set */
         case ELEMENT_TK:
            elm_deco(M_p);
            break;
         case ATTLIST_TK:
            adl_deco(M_p);
            break;
         case NOTATION_TK:
            nta_deco(M_p);
            break;
         /*--------------- short reference set */
         case SHORTREF_TK:
            srf_sdec(M_p);
            break;
         case USEMAP_TK:
            srf_udec(M_p);
            break;
         }
         if (GA(curr_token) != MDC_TK) {
            errorh(gbla_p,NOMDC,ERH_T_SEV+ERH_LOCATE);
         }else if (GA(enty_level) != GA(floor_level)) {
            errorh(gbla_p,INVHNEST,ERH_T_SEV+ERH_LOCATE);
         }
         GA(floor_level) = curr_floor_level;      /* restore floor_level */
         pop_mode(gbla_p);
         GA(quiet_at_floor) = TRUE;              /* allow an Ee to occur */
         skip_ds(gbla_p);
         GA(quiet_at_floor) = FALSE;
      }while (GA(curr_token) == MDO_TK);
   }
   pop_mode(gbla_p);
   if (GA(enty_level) != init_enty_level) {
      errorh(gbla_p,INVHNEST,ERH_T_SEV+ERH_LOCATE);
   }
   if (GA(ms_level)) {
      errorh(gbla_p,UBLNMSOD,ERH_W_SEV+ERH_LOCATE);
      GA(ms_level) = 0;
   }
   return(is_real_dtd);
}

/*STATIC---------------------------------------------------------skip_faked_ps-+
| Skip faked parameter separator                                               |
|                                                                              |
| Some "ps" in production [110] are not *really* ps, as stated by last         |
| paragraph of 11.1: they cannot contain Ee, nor parameter entity reference.   |
+-----------------------------------------------------------------------------*/
static void LP_FCT skip_faked_ps(GA_P gbla_p)
{
   for (;;) {
      skip_S(gbla_p);
      if (GA(curr_token) != COM_TK) break;
      if (!skip_dlm(gbla_p,GA(dstab)[COM_ix].p)) {
         errorh(gbla_p,UBLNCOM,ERH_T_SEV+ERH_LOCATE);
      }
   }
}

/*--------------------------------------------------------------------odt_main-+
|process a DTD                                                                 |
|Enter here when DOCTYPE has been decoded.                                     |
|Fill the document type name in callers area.                                  |
+-----------------------------------------------------------------------------*/
void P_FCT odt_main(
   GA_P gbla_p,
   uchar *DTname_p)    /* will be updated */
{
   boolean dtd_processed = FALSE;
   boolean external_dtd_found = FALSE;
   EXID dtd_exid, *exid_source_p;
   ETY  dtd_ety;
   COMMON_P M_p;               /* points the ODT common area on the stack */
   COMMON_ODT_AREA common;

   pl_sppsh(gbla_p);           /* do all malloc/free in another sub-pool */
   psh_mode(gbla_p,MD_MODE);
   /*---------------------------------------------------------+
   |[110] document type declaration =                         |
   |      mdo, "DOCTYPE", ps+, document type name,            |
   |      (ps +, external identifier)?,                       |
   |      (ps +, dso, document type declaration subset, dsc)?,|
   |      ps*, mdc                                            |
   +---------------------------------------------------------*/
   skip_faked_ps(gbla_p);
   get_gi(gbla_p,DTname_p);
   skip_faked_ps(gbla_p);
   GA(pety_scope) = ehsh_ini(gbla_p,ETY_HASHSIZE);
   GA(gety_scope) = ehsh_ini(gbla_p,ETY_HASHSIZE);
   GA(nbr_of_g_entities) = 0;
   GA(nbr_of_exid) = 0;

   /*-----------------------------+
   |Initialize the COMMON_ODT_AREA|
   +-----------------------------*/
   memset(&common,0,sizeof(COMMON_ODT_AREA));
   common.gbla_p = gbla_p;
   common.elt_scope = ehsh_ini(gbla_p,ELT_HASHSIZE);
   common.doc_elmt_gi_p = (GI *)ehsh_lnk(common.elt_scope,DTname_p,sizeof(GI));
   common.nota_scope = ehsh_ini(gbla_p,NOTA_HASHSIZE);
   common.srm_scope = ehsh_ini(gbla_p,SRM_HASHSIZE);
   common.stem_scope = ehsh_ini(gbla_p,STEM_HASHSIZE);
   common.names_p = malloc(
      (ATTCNT >= GRPCNT) ? (ATTCNT+1)*(NAMELEN+1) : (GRPCNT+1)*(NAMELEN+1)
   );
   M_p = &common;
   srf_mpty(M_p);                                 /* create the empty SR map */

   exid_source_p = (EXID *)0;
   switch (GA(curr_token)) {
   case NOT_FOUND_TK:
      /*------------------------+
      |If an external identifier|
      |has been found, decode it|
      +------------------------*/
      external_dtd_found = TRUE;
      exid_source_p = &dtd_exid;
      xid_fill(gbla_p, &dtd_exid, DTD_XFC);       /* Skip_ps done */
      if (GA(curr_token) != DSO_TK) break;        /* else, fall thru */
   case DSO_TK:
      dtd_processed = parse_dtd(M_p);
      if (GA(curr_token) != DSC_TK) {
         errorh(gbla_p,NODSC,ERH_W_SEV+ERH_LOCATE);
         break;
      }
   default:
      skip_faked_ps(gbla_p);
      break;
   case MDC_TK:
      break;
   }
   if (GA(curr_token) != MDC_TK) errorh(gbla_p,NOMDC,ERH_T_SEV+ERH_LOCATE);

   if (((!dtd_processed) &&
       (!(ut_find(gbla_p, UTODTR_XFC, DTname_p, exid_source_p)))) ||
      ((dtd_processed) && (external_dtd_found))) {
      /*------------------------------------+
      |If no bracketed stuff has been found,|
      |and no already compiled ODT,     OR  |
      |If bracketed stuff must be combined  |
      |to an external DTD                   |
      +------------------------------------*/
      if (!external_dtd_found) {
         /*-------------------------------+
         |No external identifier given:   |
         |build a minimal EXID. Otherwise,|
         |fill_exid already did it.       |
         +-------------------------------*/
         memset((uchar *)&dtd_exid,0,sizeof(EXID));  /* zero the XID */
         dtd_exid.asgn_cls = DTD_XFC;
      }
      /*-----------------------+
      |Build an ETY to em_open |
      |the DTD file.           |
      +-----------------------*/
      dtd_exid.name.len = strlen(dtd_exid.name._.p = DTname_p);
      dtd_ety.name.len = strlen(dtd_ety.name._.p = GA(gnwdtab).DOCTYPE.s.p);
      dtd_ety.is = EXTERNAL_EF | RCDATA_EF;       /* Tell what it is ...*/
      dtd_ety.kind = SGML_EFK;
      dtd_ety.val.ext._.exid.p = &dtd_exid;
      dtd_ety.val.ext.nad.p = (NAD *)0;
      if (em_open(gbla_p,&dtd_ety)) {
         int curr_floor_level=GA(floor_level); /* save current floor_level */
         GA(floor_level) = GA(enty_level);
         dtd_processed |= parse_dtd(M_p);
         if ((GA(curr_token) != NOT_FOUND_TK)||(GA(curr_char != Ee))) {
            errorh(gbla_p,NOEOF,ERH_W_SEV+ERH_LOCATE);
         }
         em_close(gbla_p);
         GA(floor_level)=curr_floor_level; /* restore current floor_level */
      }
   }

   if (dtd_processed) write_odt(M_p, DTname_p, exid_source_p);
   if (external_dtd_found) free(dtd_exid.val._.p);  /* free xid_fill chunk */
   free(common.names_p);
   pop_mode(gbla_p);

   /*-----------------------------------Jul 02, 1988---+
   |at this point, we checked that all mallocated      |
   |chunks (of the pool #1) were properly freed.       |
   +--------------------------------------------------*/
   pl_rels(gbla_p);
   pl_sppop(gbla_p);     /* back to previous pool */
}
/******************************************************************************/
