/*******************************************************************************
* 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 / Document Instance Parsing                          *
* STATUS           = Version 0, Release 1, Level 33 and up                     *
* COMPONENT NAME   = parsedoc.c                                                *
* DESCRIPTIVE NAME = Parse the Document Instance                               *
* DATE_WR          = June 1988                                                 *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
*                                                                              *
*     Parse the document while telling the tcb "stower" to modify the          *
*     tcb stack structure.                                                     *
*                                                                              *
* IMPLEMENTATION NOTES=                                                        *
* Record boundaries conditions (7.6.1) are handled by FG macros (see stower.h).*
*                                                                              *
* DEPENDENCIES =  (see Include list)                                           *
*                                                                              *
* IMPORT=                                                                      *
*                                                                              *
* EXPORT=                                                                      *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & Reason (brief)                             *
*      1 09/16/88 PETRUS   Allow BACKUP when TAG or DATA out of context:       *
*                          - Suppressed static DATA (also usefull for SUBDOC)  *
*                          - Differed some DOXX tells (pushed on a stack)      *
*      2 11/06/88 PETRUS   elt_p Requested is now returned at tcb_p+1          *
*      3 12/05/88 PETRUS   DATATAG feature                                     *
*      4 09/10/89 PETRUS   MSO/MSI/MSS CHAR functionalities                    *
*      5 12/18/90 PETRUS   Imply base elt start-tag if omitted                 *
*      6 12/18/90 PETRUS   Check Marked Section level before returning         *
*      7 02/03/91 PETRUS   DATATAG algorithm reworked                          *
*      8 02/10/91 PETRUS   Main loop reworked (more modularity)                *
*      9 02/22/91 PETRUS   "check_datatag" algorithm fixes                     *
*     10 03/01/91 DUROT    Positions and Skipped Data                          *
*     11 03/07/91 PETRUS   CON_MODE set by init_COM, and dl_find issued here   *
*     12 08/29/91 PETRUS   Tell what implied a tag                             *
*     13 10/13/91 PETRUS   Accelerator                                         *
*     14 12/07/91 PETRUS   DTD SR_max vs. Syntax SR_max                        *
*******************************************************************************/

/*-------------+
|Include Files |
+-------------*/
#include "banner.h"    /* This module includes code depending on YASP version */
#include "machine.h"
#include "portable.h"
#include "yaspapi.h"
#include "global.h"
#include "hash.h"
#include "usrlib.h"
#include "sgml.h"
#include "gtype.h"
#include "tag.h"
#include "modele.h"
#include "stower.h"
#include "teller.h"
#include "yasp.h"

typedef enum t_CHDT {
   NO_END_CHDT,
   END_BEFORE_CHDT,
   END_HERE_CHDT
}CHDT;

/*--------------+
|Local Functions|
+--------------*/
static CHDT    LP_FCT check_datatag     (COM_P, boolean);
static uint    LP_FCT how_many_NET_end  (TCB_P);
static uint    LP_FCT how_many_tag_end  (GA_P, TCB_P);
static boolean LP_FCT process_EndDoc    (COM_P);
static void     P_FCT check_idref_set   (void STACK_BASED *, EHSH *);
static CHOICES const * _loadds LP_FCT adr_usedcl_choices(void);
static void    LP_FCT process_usedcl    (COM_P);
static boolean LP_FCT process_omsect    (COM_P);
static boolean LP_FCT process_entity    (COM_P, ETY *);
static void    LP_FCT process_PI        (COM_P);
static boolean LP_FCT process_MSx       (COM_P);
static void    LP_FCT process_sepa      (COM_P);
static void    LP_FCT parse_doc         (GA_P);
static boolean LP_FCT initialize_COMMON (GA_P, ELT *);

#define ID_HASHSIZE 53   /* For about 200 entries */

/*STATIC---------------------------------------------------------check_datatag-+
|DATATAG pattern recognition.                                                  |
|                                                                              |
|From the point where a TARGET Tag has been found, the main scan loop          |
|(parse_doc) call this routine for each character of the content,              |
|after replacement of the references.                                          |
|                                                                              |
|When entering this routine:                                                   |
|  - it's assumed at least one target element exists on the TCB stack          |
|    (ie we are in DATATAG_PMO)                                                |
|  - the current character has not been entered in the content                 |
|    (by either stw_data or stw_stRE).  It may not be entered                  |
|    at all (a discarded RE).                                                  |
|                                                                              |
|IMPLEMENTATION NOTE:                                                          |
|   When either a STAGO, ETAGO, or EOF is encountered, we need to check        |
|   if datatag are pending, (i.e. DTMs in SCAN_PATTERN mode).                  |
|   As the curr_char is irrelevant, the boolean must_finish is raised          |
|   to force curr_char comparison to be false.                                 |
|   In such a case, the algorithm will free the DTM structure even             |
|   if no DTM matches (FALSE returned).                                        |
|                                                                              |
|WARNING: (02/18/90)  -- RESTRICTION --                                        |
|   With multiple open target-element, matching the data-tag-patterns AFTER    |
|   recognition of markup would imply "look-ahead".  :-(                       |
|   Since we do *not* want implement such an algorithm now,                    |
|   we know the check-datatag function will NOT work if:                       |
|   - multiple target-element                                                  |
|   - AND:                                                                     |
|     1) extra-characters to unread, part of them being in another entity, OR  |
|     2) very long comments ( > BUFSIZE of global.h) in the data-tag pattern   |
|     In these cases, the unread can produce unexpected results.               |
|   Also, markup (such as PI) ends the matching because we want them to be     |
|   reported once in their exact sequence in terms of ESIS events.             |
|   However, these are not casual cases.  We hope the 8879 will be amended.    |
|   (note sent to Charles Goldfarb on 01/14/91).                               |
|   To make it work: be able to seek anywhere on input, stack all markup       |
|   events until their context is well known.                                  |
| ** OPTIMIZED **                                                              |
+-----------------------------------------------------------------------------*/
static CHDT LP_FCT check_datatag(COM_P M_p, boolean must_finish)
{
   GA_P gbla_p = M_p->gbla_p;
   typedef struct t_DTM {       /* to store the data matching Datatag */
      struct t_DTM *next_p;
      enum {
         MATCH_FOUND,
         SCAN_TEMPLATE,
         SCAN_PADDING
      }status;
      uchar STACK_BASED *noted_start_c_p;
      uchar STACK_BASED *noted_end_c_p;
      uchar *lit_p;             /* template or padding */
      TCB_P  tcb_p;
      int   count;              /* To check the length of the DataTag */
      ulong start_vofs;         /* extra-chars offset in EM buffer (ebuf) */
      boolean  is_extra;        /* To tell extra-chars have been read */
   }DTM;

   DTP   *dtp_p;                /* datatag template */
   TCB_P  tcb_p = M_p->tcb_p;
   TCB_P  tcb_pP = (TCB_P)0;
   DTM **dtm_pp, *dtm_p, *dtm_p_next;
   uchar STACK_BASED *current_noted_c_p;
   ulong start_vofs;
   CHDT  chdt;

   dtm_p  = (DTM *)GA(dtm_base_p);
   dtm_pp = (DTM **)&GA(dtm_base_p);
   current_noted_c_p = stw_note(gbla_p); /* curr char not yet entered */
   start_vofs = em_vofs(gbla_p);
   /*------------------------------------------------------+
   |In the actual DTM chain, find if curr_char could match |
   |one character more.  Returns successfully if a pattern |
   |ended.                                                 |
   +------------------------------------------------------*/
   if (dtm_p) {
      for (;;) {
         switch (dtm_p->status) {
         case SCAN_TEMPLATE:
            if (!*++(dtm_p->lit_p)) {                /* EO template found */
               if ((!*(dtm_p->lit_p = dtm_p->tcb_p->dtp_p->c_p))||
                  (must_finish)||                    /* no padding / no match */
                  (*dtm_p->lit_p != (uchar)GA(curr_char)) ||
                  (++dtm_p->count >= 0)) {           /* or too long */
                  dtm_p->status = MATCH_FOUND;       /* pattern matches */
                  continue;                          /* pursue... */
               }else {
                  dtm_p->status = SCAN_PADDING;
               }
            }else if ((must_finish)||                /* template dislikes */
               (*dtm_p->lit_p != (uchar)GA(curr_char)) ||
               (++dtm_p->count >= 0)) {              /* or too long */
               *dtm_pp = dtm_p->next_p;              /* Unlink */
               free(dtm_p);
               break;
            }
            dtm_pp = &(dtm_p->next_p);               /* next dtm */
            break;
         case SCAN_PADDING:
            if (!*++(dtm_p->lit_p)) {                /* EO padding found */
               dtm_p->lit_p = dtm_p->tcb_p->dtp_p->c_p; /* try once more */
            }
            if ((must_finish)||                      /* doesn't match: */
               (*dtm_p->lit_p != (uchar)GA(curr_char))||
               (++dtm_p->count >= 0)) {              /* or too long */
               dtm_p->status = MATCH_FOUND;          /* padding exhausted */
               continue;                             /* pattern fnd, continue */
            }else {
               dtm_pp = &(dtm_p->next_p);            /* next dtm */
               break;
            }
         case MATCH_FOUND:
            if (dtm_p->tcb_p != tcb_pP) {            /* first == longest */
               stw_xpel(M_p,dtm_p->noted_start_c_p); /* flush what before */
               stw_etcb(M_p, 1 + tcb_p - dtm_p->tcb_p, DATA_ET_MNF, EDTG_IMF);
               if (dtm_p->is_extra) {
                  stw_xpel(M_p,dtm_p->noted_end_c_p);
                  em_unrd(gbla_p,(ushort)(start_vofs - dtm_p->start_vofs));
                  stw_thro(gbla_p);
                  chdt=END_BEFORE_CHDT;
               }else {
                  stw_flsh(M_p);
                  chdt=END_HERE_CHDT;
               }
               mdl_edtg(M_p->tcb_p);                 /* End the datatag */
               FG_ETG(M_p);
               dtm_p = (DTM *)GA(dtm_base_p);        /* free the dtm chain */
               do {
                  dtm_p_next = dtm_p->next_p;
                  free(dtm_p);
               }while ((dtm_p = dtm_p_next) != (DTM *)0);
               GA(dtm_base_p) = (void *)0;
               return(chdt);
            }else {                                  /* must be the longest */
               if (!dtm_p->is_extra) {               /* extra-chars, wait.. */
                  dtm_p->is_extra = TRUE;            /* at first time, */
                  dtm_p->noted_end_c_p = current_noted_c_p; /* remember... */
                  dtm_p->start_vofs = start_vofs;           /* where we are */
               }
               dtm_pp = &(dtm_p->next_p);            /* next dtm */
               break;
            }
         }
         tcb_pP = dtm_p->tcb_p;
         if (!(dtm_p = *dtm_pp)) break;
      }
   }

   /*----------------------------------------------------------------+
   |Add all new candidates to the actual DTM chain, i.e. all datatag |
   |the 1st character pattern of which matches curr_char.            |
   +----------------------------------------------------------------*/
   if (!must_finish) do {
      register uchar *c_p;

      if ((dtp_p = tcb_p->dtp_p) != (DTP *)0) {
         c_p = (++dtp_p)->c_p;
         do {
            if (*c_p == (uchar)GA(curr_char)) {
               dtm_p = (DTM *)malloc(sizeof(DTM));
               dtm_p->next_p = (DTM *)0;
               dtm_p->status = SCAN_TEMPLATE;
               dtm_p->noted_start_c_p = current_noted_c_p;
               dtm_p->lit_p = c_p;
               dtm_p->tcb_p = tcb_p;
               dtm_p->count = -DTAGLEN;            /* to check datatag len */
               dtm_p->is_extra = FALSE;
               *dtm_pp = dtm_p;
               dtm_pp = &dtm_p->next_p;
            }
         }while(*(c_p = (++dtp_p)->c_p) != (uchar)0);
      }
   }while ((--tcb_p)->is.b.dtag_enabled);
   return(NO_END_CHDT);
}

/*STATIC------------------------------------------------------how_many_NET_end-+
| Return the count of element a valid trapped NET would end.                   |
+-----------------------------------------------------------------------------*/
static uint LP_FCT how_many_NET_end(TCB_P tcb_p)
{
   uint ecnt;

   for (++tcb_p, ecnt=1; !(--tcb_p)->is.b.net_enabling; ++ecnt);
   return(ecnt);
}

/*STATIC------------------------------------------------------how_many_tag_end-+
| Check the validity of an end tag - returns 0 if invalid or the               |
| count of element the end tag is ending.                                      |
+-----------------------------------------------------------------------------*/
static uint LP_FCT how_many_tag_end(GA_P gbla_p, TCB_P tcb_p0)
{
   ELT  *elt_p;
   TCB_P tcb_p;
   uint ecnt;
   boolean is_invalid_end;

   /*----------------------------------------------------+
   |Set ecnt to the # of times "end_elmt" must be issued |
   +----------------------------------------------------*/
   if (!(elt_p=stw_gfgi(gbla_p))) { /* Get the full Generic Identifier */
      is_invalid_end = TRUE;        /* Unknown element */
   }else {
      is_invalid_end = FALSE;       /* be optimistic */
      /*-----------------------------------------------+
      |Find out if End-tag matches any tag on the stack|
      +-----------------------------------------------*/
      for (tcb_p = tcb_p0 + 1, ecnt=1; (--tcb_p)->elt_p != elt_p; ++ecnt) {
         if (tcb_p->is.b.zero) {    /* No match found */
            is_invalid_end = TRUE;  /* invalid end */
            break;
         }
      }
   }
   if (is_invalid_end) {
      if (!tcb_p0->elt_p->is.dcdata) {
         errorh(gbla_p,IGNOREEND,ERH_W_SEV+ERH_LOCATE);
         ecnt = 0;                  /* invalid */
      }else {                       /* RCDATA or CDATA */
         /* see ISO 8879-1986(E) 7.6 p.24, last paragraph */
         errorh(gbla_p,FAKEEND,ERH_W_SEV+ERH_LOCATE);
         is_invalid_end = FALSE;
         ecnt = 1;                  /* so it ends ones */
      }
   }
   tg_parse(gbla_p,(ELT *)0, (ATD **)0);   /* jump to the end of the end tag */
   if (GA(curr_token) == NET_TK) errorh(gbla_p,NOTAGC,ERH_W_SEV+ERH_LOCATE);
   return(ecnt);
}

/*STATIC--------------------------------------------------------process_EndDoc-+
| Process Ee of the PRIMARY or a SUBDOC entity                                 |
| Return FALSE if we must reparse, TRUE is the document ended                  |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT process_EndDoc(register COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;

   switch (M_p->parse_mode) {
   case DATATAG_PMO:
      switch(check_datatag(M_p,TRUE)) {          /* finish any datatag */
      case END_BEFORE_CHDT:                      /* characters were unread */
         M_p->parse_mode = TAG_PMO;              /* back to TAG_PMO */
         M_p->forced_data = 0;                   /* broken ShortRef anyway */
         M_p->is_shortref_in_process=FALSE;      /* disable SR remap check */
         return(FALSE);                          /* must redo the parse */
      case END_HERE_CHDT:                        /* DataTag ended here */
         /* M_p->parse_mode = TAG_PMO;*/         /* who cares ? */
         break;
      case NO_END_CHDT:                          /* No DataTag ended */
         break;                                  /* still in DATATAG_PMO */
      }
      /* fall thru */
   case CHAR_PMO:
      stw_flsh(M_p);
      /* M_p->parse_mode = TAG_PMO; */           /* who cares ? */
      /* fall thru */
   default:                                      /* ICHAR / TAG */
      break;
   }
   /*-----------------------------------------+
   |If the document element at least started, |
   |end the maximum of elements               |
   +-----------------------------------------*/
   if (!M_p->tcb_p->is.b.zero) stw_etcb(M_p,TAGLVL+1, NO_MNF, EOF_IMF);
   if (GA(ms_level)) errorh(gbla_p,UBLNMSO,ERH_T_SEV+ERH_LOCATE);

   /*-----------------------------------------+
   |Check ID and IDREF                        |
   +-----------------------------------------*/
   ehsh_tvr(GA(id_scope), check_idref_set, M_p);
   return(TRUE);
}

/*STATIC-------------------------------------------------------check_idref_set-+
|Check if all idref have been set (defined)                                    |
+-----------------------------------------------------------------------------*/
static void P_FCT check_idref_set(void STACK_BASED *M_p, EHSH *ehsh_p)
{
   if (!((ulong)ehsh_p->var_p)) {
      errorh(((COM_P)M_p)->gbla_p, INVIDREF, ERH_W_SEV, ehsh_p->name_p);
   }
}

/*STATIC----------------------------------------------------adr_usedcl_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
#define INVALID_VL      0      /* Value doesn't belong to the list */
#define USEMAP_VL       1      /* To find if it's USEMAP or      */
#define USELINK_VL      2      /* USELINK */

static CHOICES const * _loadds LP_FCT adr_usedcl_choices(void)
{
   static CHOICES const usedcl_choices[] = {
      { offsetof(GNWDTAB,USELINK), USELINK_VL },
      { offsetof(GNWDTAB,USEMAP),  USEMAP_VL  },
      { -1,                        INVALID_VL }
   };
   return(usedcl_choices);
}

/*STATIC--------------------------------------------------------process_usedcl-+
| Enter here to process either USELINK or USEMAP, when an MDO has been found   |
| in content.                                                                  |
+-----------------------------------------------------------------------------*/
static void LP_FCT process_usedcl(COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   uchar name[NAMELEN_MAX+1];
   SRM *srm_p;

   psh_mode(gbla_p,MD_MODE);
   switch(choos_in(gbla_p,adr_usedcl_choices(),(GNWD *)&GA(gnwdtab))) {
   case INVALID_VL:
      errorh(gbla_p,INVMDIGN,ERH_W_SEV+ERH_LOCATE);
      if (!skip_dlm(gbla_p,GA(dstab)[MDC_ix].p)) {
         GA(curr_token) = NOT_FOUND_TK;
      }else {
         GA(curr_token) = MDC_TK;               /* micmic dl_find */
         GA(curr_count) = strlen(GA(dstab)[MDC_ix].p);
      }
#if YASP_RVL_GE(0,1,26)
      tll_SKIP(gbla_p,ERROR_SDC);
#endif
      break;
   case USELINK_VL:
      errorh(gbla_p,NIMPL,ERH_T_SEV+ERH_LOCATE,1002);
      skip_ps(gbla_p);
#if YASP_RVL_GE(0,1,26)
      tll_SKIP(gbla_p,ERROR_SDC);
#endif
      break;
   case USEMAP_VL:
      skip_ps(gbla_p);
      if (GA(curr_token) == RNI_TK) {
         chk_gnam(gbla_p,GA(gnwdtab).EMPTY.s.p);
         name[0] = '\0';
      }else {
         if (GA(curr_token) != NOT_FOUND_TK) {
            errorh(gbla_p,NONAME,ERH_T_SEV+ERH_LOCATE);
         }
         get_name(gbla_p,name);                 /* get the name */
      }
      if (!(srm_p=(SRM *)shsh_fnd(GA(srm_htab),name))) {
         errorh(gbla_p,NOSRM,ERH_W_SEV+ERH_LOCATE,name);
         skip_ps(gbla_p);
#if YASP_RVL_GE(0,1,26)
         tll_SKIP(gbla_p,ERROR_SDC);
#endif
      }else {
         /* at srm_p + 0 is a dummy entry that has the mapname  */
         M_p->tcb_p->srm_p = srm_p + 1;         /* new short map */
         skip_ps(gbla_p);
         tll_MAP(gbla_p,srm_p + 1);
      }
      break;
   }
   pop_mode(gbla_p);
   if (GA(curr_token) != MDC_TK) errorh(gbla_p,NOMDC,ERH_T_SEV+ERH_LOCATE);
}

#undef INVALID_VL
#undef USEMAP_VL
#undef USELINK_VL

/*STATIC--------------------------------------------------------process_omsect-+
| Process opening a marked section (MSO_DSO found)                             |
| Return FALSE if we are Out of Document (so parse_doc will exit)              |
| TRUE otherwise                                                               |
| Note: occurs only for INCLUDE or TEMP (not in IGNORE/RCDATA/CDATA modes)     |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT process_omsect(register COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   SDC sdc = NULL_SDC;

   msd_psh(gbla_p);
   switch (GA(curr_mode)) {
   case FCDMS_MODE:
      sdc = MSCDATA_SDC;
   case RCDMS_MODE:
      if (sdc == NULL_SDC) sdc = MSRCDATA_SDC;
      for (;;) {                                 /* In case PMO changes... */
         switch (M_p->parse_mode) {
         case DATATAG_PMO:                       /* see 7.3.2, 5th para */
            switch(check_datatag(M_p,TRUE)) {    /* finish any datatag */
            case END_BEFORE_CHDT:                /* characters were unread */
               M_p->parse_mode = TAG_PMO;        /* back to TAG_PMO */
               M_p->is_shortref_in_process=FALSE;/* disable SR remap check */
               msd_pop(gbla_p);                  /* backup msd_psh */
               return(TRUE);                     /* leave: must reparse */
            case END_HERE_CHDT:                  /* DataTag ended here */
               M_p->parse_mode = TAG_PMO;        /* back to TAG_PMO */
               continue;                         /* redo analysis */
            case NO_END_CHDT:                    /* No DataTag ended */
               M_p->parse_mode = CHAR_PMO;       /* CHAR only are now allowed */
               break;                            /* No further check needed */
            }
            break;
         case TAG_PMO:                           /* check if data is allowed */
            if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
               errorh(gbla_p,OUTDOCDAT,ERH_W_SEV+ERH_LOCATE);
               return(FALSE);                    /* leave (abnormal exit) */
            }
            if (stw_dtcb(M_p)) {                 /* prepare for DATA */
               M_p->parse_mode = CHAR_PMO;       /* OK, now in CHAR_PMO */
            }else {                              /* model disagrees */
               errorh(gbla_p,OOCDATA,ERH_W_SEV+ERH_LOCATE);
               M_p->parse_mode = ICHAR_PMO;      /* Invalid Char PMO */
               sdc = ERROR_SDC;                  /* Invalid CDATA */
            }
            break;
         default:                                /* CHAR_PMO, ICHAR_PMO */
            break;
         }
         break;                                  /* break the for loop */
      }
      break;
   case IGNMS_MODE:
      /*-----------------------------------------+
      | MSC_MDC reached if IGNMS_MODE:           |
      | mds_push skipped over the ignored stuff  |
      +-----------------------------------------*/
      sdc = MSIGN_SDC;
      msd_pop(gbla_p);
      if ((M_p->parse_mode == CHAR_PMO) && (M_p->tcb_p->is.b.dtag_enabled)) {
         M_p->parse_mode = DATATAG_PMO;          /* Reset DATATAG_PMO */
      }
      break;
   default:                                      /* INCLUDE or TEMP */
      sdc = MSINCL_SDC;
      break;
   }
   FG_OTHER(M_p);                                /* Register new state */
   M_p->is_shortref_in_process = FALSE;          /* SR was not remapped */
#if YASP_RVL_GE(0,1,26)
   if (M_p->parse_mode != DATATAG_PMO) stw_flsh(M_p); /* Try to be in sync. */
   tll_SKIP(gbla_p,sdc);
#endif
   return(TRUE);                                 /* leave: continue parse */
}

/*STATIC--------------------------------------------------------process_entity-+
| Process an entity found in contents                                          |
| Return FALSE if we are Out of Document (so parse_doc will exit)              |
| TRUE otherwise                                                               |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT process_entity(register COM_P M_p, ETY *ety_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ETY *ret_ety_p;

   if ((ety_p)&&(!IS_BITON(ety_p->is,RCDATA_EF))) { /* R/C/S Data, PI, SUBDOC */
      if (M_p->parse_mode == DATATAG_PMO) {
         switch(check_datatag(M_p,TRUE)) {       /* finish any DataTag */
         case END_BEFORE_CHDT:                   /* characters were unread */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            M_p->is_shortref_in_process=FALSE;   /* disable SR remap check */
            return(TRUE);                        /* leave: must reparse */
         case END_HERE_CHDT:                     /* DataTag ended here */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            break;
         case NO_END_CHDT:                       /* No DataTag ended */
            break;                               /* stay in DATATAG_PMO */
         }
      }
      if (ety_p->kind == PI_EFK) {               /* PI entity */
         FG_OTHER(M_p);                          /* Register new state */
         stw_flsh(M_p);                          /* Flush the buffer */
         ret_ety_p = ety_p;
         while (((ret_ety_p = tll_ePI(gbla_p, ret_ety_p)) != (ETY *)0)
            && (em_open(gbla_p,ret_ety_p))) {    /* Valid entity returned */
            if (ret_ety_p->kind != PI_EFK) {
               process_entity(M_p, ret_ety_p);   /* Not a PI: recurse */
               break;                            /* and break */
            }                                    /* a PI: loop again */
         }
      }else {                                    /* CDATA,SDATA,NDATA,SUBDOC */
         switch (M_p->parse_mode) {
         case TAG_PMO:                           /* check if data is allowed */
            if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
               errorh(gbla_p,OUTDOCDAT,ERH_W_SEV+ERH_LOCATE);
               return(FALSE);                    /* return (abnormal exit) */
            }
            if (stw_dtcb(M_p)) {                 /* prepare for data OK */
               if (M_p->tcb_p->is.b.dtag_enabled) {
                  M_p->parse_mode = DATATAG_PMO;
               }else {
                  M_p->parse_mode = CHAR_PMO;
               }
               break;
            }
            M_p->parse_mode = ICHAR_PMO;
            /* Fall thru */
         case ICHAR_PMO:                         /* Invalid data entity */
            errorh(gbla_p,OOCDATA,ERH_W_SEV+ERH_LOCATE);
#if YASP_RVL_GE(0,1,26)
            em_pos(gbla_p,&GA(ooc_position),0);  /* Out Of Context data POS */
#endif
            M_p->is_shortref_in_process = FALSE;
            return(TRUE);
         default:                                /* DATATAG_PMO, CHAR_PMO */
            break;
         }
         M_p->tcb_p->is.b.bad_empty = FALSE;
         FG_DATA(M_p);                           /* register new state */
         stw_flsh(M_p);                          /* flush the buffer */
         switch (ety_p->kind) {
         case SUBDOC_EFK:                        /* SUBDOC entity */
            submain(gbla_p);                     /* recurse in parse_doc */
            break;
         default:                                /* C/S/N Data */
            if (((ret_ety_p = tll_ETY(gbla_p, ety_p)) != (ETY *)0)
               && (em_open(gbla_p,ret_ety_p))) { /* Valid entity returned */
               process_entity(M_p, ret_ety_p);   /* recurse */
            }
            break;
         }
      }
      M_p->is_shortref_in_process=FALSE;         /* SR was not remapped */
   }
#if YASP_RVL_GE(0,1,26)
   if (!ety_p) {                                 /* Entity not found! Error. */
      if (M_p->parse_mode == DATATAG_PMO) {
         switch(check_datatag(M_p,TRUE)) {       /* finish any DataTag */
         case END_BEFORE_CHDT:                   /* characters were unread */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            M_p->is_shortref_in_process=FALSE;   /* disable SR remap check */
            return(TRUE);                        /* leave: must reparse */
         case END_HERE_CHDT:                     /* DataTag ended here */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            break;
         case NO_END_CHDT:                       /* No DataTag ended */
            break;                               /* stay in DATATAG_PMO */
         }
      }
      stw_flsh(M_p);
      tll_SKIP(gbla_p,ERROR_SDC);
   }
#endif
   return(TRUE);
}

/*STATIC------------------------------------------------------------process_PI-+
| Process a PI found in contents                                               |
+-----------------------------------------------------------------------------*/
static void LP_FCT process_PI(register COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;

   if (M_p->parse_mode == DATATAG_PMO) {
      switch(check_datatag(M_p,TRUE)) {          /* finish any DataTag */
      case END_BEFORE_CHDT:                      /* characters were unread */
         M_p->parse_mode = TAG_PMO;              /* back to TAG_PMO */
         M_p->is_shortref_in_process = FALSE;    /* disable SR remap check */
         return;                                 /* must reparse */
      case END_HERE_CHDT:                        /* DataTag ended here */
         M_p->parse_mode = TAG_PMO;              /* back to TAG_PMO */
         break;
      case NO_END_CHDT:                          /* No DataTag ended */
         break;                                  /* stay in DATATAG_PMO */
      }
   }
   FG_OTHER(M_p);                                /* Register new state */
   stw_flsh(M_p);                                /* flush output buffer */
   get_PI(gbla_p);                               /* get (and tell) PI */
   M_p->is_shortref_in_process = FALSE;          /* SR was not remapped */
   return;                                       /* pursue the parse */
}

/*STATIC-----------------------------------------------------------process_MSx-+
| Process a MSO / MSI / MSS found in contents                                  |
| Return FALSE if we are Out of Document (so parse_doc will exit)              |
| TRUE otherwise                                                               |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT process_MSx(register COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   boolean is_sup;                               /* TRUE if MSO or MSS */

   for (;;) {                                    /* If the PMO is changed */
      switch(M_p->parse_mode) {
      case DATATAG_PMO:
         /*-------------------------------------------+
         | If MSO or MSS supressed markup recognition |
         | the DATATAG_PMO is disabled - therefore    |
         | only MSO, MSS or orphans MSI can be found: |
         | is_sup==FALSE means a orphan MSI is found. |
         +-------------------------------------------*/
         is_sup = is_msrchar(GA(curr_char)) ? TRUE : FALSE;  /* MSO or MSS */
         switch(check_datatag(M_p,is_sup)) {     /* see 7.3.2, 5th para */
         case END_BEFORE_CHDT:                   /* characters were unread */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            M_p->is_shortref_in_process=FALSE;   /* disable SR remap check */
            return(TRUE);                        /* leave: must reparse */
         case END_HERE_CHDT:                     /* DataTag ended here */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            continue;                            /* redo analysis */
         case NO_END_CHDT:                       /* No DataTag ended */
            break;                               /* stay in DATATAG_PMO */
         }                                       /* fall thru */
         /* fall thru */
      case CHAR_PMO:                             /* or still DATATAG_PMO */
         FG_DATA(M_p);                           /* register new state */
         stw_data(M_p);                          /* enter curr char */
         if (is_msochar(GA(curr_char))) {        /* if MSO, */
            M_p->parse_mode = CHAR_PMO;          /* DATATAG_PMO to CHAR_PMO */
            hid_mode(gbla_p);                    /* suppress delim rec */
         }else {                                 /* MSS, MSI: */
            if (M_p->tcb_p->is.b.dtag_enabled) { /* if DATATAG was enabled */
               M_p->parse_mode = DATATAG_PMO;    /* back to DATATAG */
            }
            if (is_msichar(GA(curr_char))) {     /* MSI, orphan or not */
               rst_mode(gbla_p);                 /* restore mode anyway */
            }else {                              /* MSS: process it directly */
               if (em_read(gbla_p) == Ee) {      /* Ee found: like an MSI */
                  M_p->is_shortref_in_process=FALSE;
                  return(TRUE);
               }
               stw_data(M_p);                    /* enter new curr char */
            }
         }
         break;
      case TAG_PMO:
         if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
            errorh(gbla_p,OUTDOCDAT,ERH_W_SEV+ERH_LOCATE);
            return(FALSE);                       /* go back (abnormal exit) */
         }
         if (stw_dtcb(M_p)) {                    /* prepare for data OK */
            if (M_p->tcb_p->is.b.dtag_enabled) { /* if DataTag enabled */
               M_p->parse_mode = DATATAG_PMO;    /* reset corresponding PMO */
            }else {                              /* not enabled: */
               M_p->parse_mode = CHAR_PMO;       /* Now in char_PMO */
            }
            M_p->is_shortref_in_process = FALSE; /* SR remap check done */
            continue;                            /* redo analysis */
         }
         errorh(gbla_p,OOCDATA,ERH_W_SEV+ERH_LOCATE);
         M_p->parse_mode = ICHAR_PMO;
         /* Fall thru */
      case ICHAR_PMO:                            /* Invalid data */
#if YASP_RVL_GE(0,1,26)
         em_pos(gbla_p,&GA(ooc_position),0);     /* Out Of Context data POS */
#endif
         break;
      }
      M_p->is_shortref_in_process = FALSE;       /* SR was not remapped */
      em_read(gbla_p);                           /* read next char */
      return(TRUE);                              /* continue parsing */
   }
}

/*STATIC----------------------------------------------------------process_sepa-+
| Process a separator found in contents                                        |
+-----------------------------------------------------------------------------*/
static void LP_FCT process_sepa(register COM_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;

   for (;;) {                                    /* In case PMO is changed */
      switch (M_p->parse_mode) {
      case TAG_PMO:
         /*-------------------------------+
         |An RS that is not interpreted as|
         |markup is ignored  (7.6.1)      |
         +-------------------------------*/
         if (GA(curr_char) == GA(RS_char)) {     /* never generates RE */
            FG_RS(M_p);
            break;
         }
         /*----------------------------------------+
         |Even an RE is recognized as DATA, before |
         |to consider if it can be ignored.        |
         |If stw_dtcb is FALSE, issue an error and |
         |consider it like a separator.            |
         +----------------------------------------*/
         if ((M_p->tcb_p->elt_p->is.pcdata)||    /* In mixed content */
            (M_p->tcb_p->elt_p->is.dcdata)) {    /* or declared content */
            if (stw_dtcb(M_p)) {                 /* and data OK */
               if (M_p->tcb_p->is.b.dtag_enabled) {
                  M_p->parse_mode = DATATAG_PMO;
               }else {
                  M_p->parse_mode = CHAR_PMO;
               }
               M_p->is_shortref_in_process=FALSE;
               continue;                         /* in a new parse mode */
            }else {
               errorh(gbla_p,OOCSEP,ERH_W_SEV+ERH_LOCATE);
#if YASP_RVL_GE(0,1,26)
               stw_flsh(M_p);                    /* NOT in DATATAG_PMO */
               tll_SKIP(gbla_p,ERROR_SDC);
#endif
            }
         }
         /* ignore a character recognized just like an "S" */
         break;
      case DATATAG_PMO:                          /* data is allowed */
         switch(check_datatag(M_p,FALSE)){       /* check if datatag ended */
         case END_BEFORE_CHDT:                   /* characters were unread */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            M_p->forced_data = 0;                /* broken ShortRef anyway */
            M_p->is_shortref_in_process=FALSE;   /* disable SR remap check */
            return;                              /* redo the parse */
         case END_HERE_CHDT:                     /* DataTag ended here */
            M_p->parse_mode = TAG_PMO;           /* back to TAG_PMO */
            M_p->forced_data=0;                  /* broken ShortRef anyway */
            return;                              /* continue parsing */
         case NO_END_CHDT:                       /* No DataTag ended */
            break;                               /* stay in DATATAG_PMO */
         }                                       /* fall thru */
         /* fall thru */
      case CHAR_PMO:
         if (GA(curr_char) == GA(RS_char)) {     /* never generates RE */
            FG_RS(M_p);                          /* Change for RS state */
         }else if (GA(curr_char)==GA(RE_char)) {
            FG_RE(M_p);                          /* RE state */
#if YASP_RVL_GE(0,1,26)
            em_pos(gbla_p,&GA(RE_position),0);   /* Keep position... */
#endif
         }else {
            FG_DATA(M_p);                        /* Change for DATA state */
            stw_data(M_p);                       /* enter curr_char */
         }
         break;
      case ICHAR_PMO:
#if YASP_RVL_GE(0,1,26)
         em_pos(gbla_p,&GA(ooc_position),0);     /* Out Of Context data POS */
#endif
         break;
      }
      M_p->is_shortref_in_process = FALSE;
      em_read(gbla_p);
      return;
   }
}

/*STATIC-------------------------------------------------------------parse_doc-+
| Main loop when parsing the document instance.                                |
|                                                                              |
| IMPLEMENTATION NOTE 1:                                                       |
|   To reach the DATATAG_PMO, an element on the stack must have enabled        |
|   a DATATAG to occur AND the current model allow data character              |
|   (stw_dtcb() must have been successfully issued).  After the                |
|   datatag ending pattern matched, the parse-mode returns to TAG_PMO,         |
|   NOT to CHAR_PMO, as the #PCDATA equivalent in the datatag model            |
|   is considered to be ended.                                                 |
|                                                                              |
| IMPLEMENTATION NOTE 2:                                                       |
|   Despite our coding rules, goto's are used here.  This was mandatory        |
|   in order to improve the performance.  Also, it was found the code was      |
|   more readable ("goto main_loop" more explicit than a "continue").          |
+-----------------------------------------------------------------------------*/
static void LP_FCT parse_doc(GA_P gbla_p)
{
   int primary_enty_level;
   register COM_P M_p = gbla_p->M_p;
   primary_enty_level =  GA(enty_level);
   GA(id_scope) = ehsh_ini(gbla_p,ID_HASHSIZE);
   GA(curr_elt_id) = 1;
   GA(ooc_position).ety_p=NULL;

main_loop:
   switch (GA(curr_token)) {
   case ERO_TK:                                  /* & */
#if YASP_RVL_GE(0,1,26)
      if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p); /* Try to be in sync. */
#endif
      if (!process_entity(M_p,em_opger(gbla_p))) {
         return;                                 /* OUT of DOC: abnormal exit */
      }
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case PIO_TK:                                  /* <? */
      process_PI(M_p);
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case CRO_TK:                                  /* &# (character reference) */
      switch(get_chrf(gbla_p)) {
      case INVALID_CHRF:                         /* Invalid (discarded) */
      case NAMED_CHRF:                           /* Named */
         dl_find(gbla_p);                        /* get next token */
         goto main_loop;                         /* and loop */
      case NUMERIC_CHRF:                         /* Numeric */
         goto process_data;                      /* is data, for sure */
      }
   case NOT_FOUND_TK:                            /* PCDATA/DCDATA (C or RC) */
      /*---------------+
      | End of Entity  |
      +---------------*/
      if (GA(curr_char) == Ee) {
         if ((GA(rcgn_mo)&RCD_MO)&&(GA(enty_level) <= GA(cn_enty_level))) {
            errorh(gbla_p,INVLNEST,ERH_W_SEV+ERH_LOCATE);
         }
         rst_mode(gbla_p);                       /* recognize MARKUP,see 9.7 */
         if ((M_p->parse_mode==CHAR_PMO)&&(M_p->tcb_p->is.b.dtag_enabled)) {
            M_p->parse_mode = DATATAG_PMO;       /* MSO on: back to DATATAG */
         }
         if (GA(enty_level) > primary_enty_level) {
            M_p->is_shortref_in_process=FALSE;   /* shortref not remapped */
#if YASP_RVL_GE(0,1,26)
            if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p); /* Be in sync. */
#endif
            em_close(gbla_p);                  /* get next char in prev. file */
            dl_find(gbla_p);                     /* get next token */
            goto main_loop;                      /* and loop */
         }
         if (!process_EndDoc(M_p)) {             /* PRIMARY entity or SUBDOC */
            dl_find(gbla_p);                     /* not yet finish */
            goto main_loop;                      /* loop */
         }
         return;                                 /* go back (normal exit) */
      }
      /*----------+
      | Separator |
      +----------*/
      if (is_separator(GA(curr_char))) {
         process_sepa(M_p);
         if (!M_p->forced_data) dl_find(gbla_p); else --(M_p->forced_data);
         goto main_loop;
      }
      /*----------------+
      | MSO / MSI / MSS |
      +----------------*/
      if ((!M_p->forced_data)&&(is_ms_char(GA(curr_char)))) {
         if (!process_MSx(M_p)) return;          /* OUT of DOC: abnormal exit */
         dl_find(gbla_p);
         goto main_loop;
      }
      /*------------------------------+
      | "True" Data: no RE/RS, nor Ee,|
      | nor separators, nor MSx chars |
      +------------------------------*/
process_data:
      if (is_nonsgml(GA(curr_char))) {           /* Report non SGML in sync. */
         if (M_p->parse_mode == DATATAG_PMO) {
            switch(check_datatag(M_p,TRUE)) {    /* finish any datatag */
            case END_BEFORE_CHDT:                /* characters were unread: */
               M_p->is_shortref_in_process = FALSE;
               M_p->parse_mode = TAG_PMO;
               M_p->forced_data = 0;
               dl_find(gbla_p);                  /* get next token */
               goto main_loop;                   /* and loop */
            case END_HERE_CHDT:
               M_p->parse_mode = TAG_PMO;
            case NO_END_CHDT:
               break;
            }
         }
         stw_flsh(M_p);                          /* flush the buffer */
         tll_NGML(gbla_p);                       /* tell application */
         M_p->is_shortref_in_process=FALSE;
         em_read(gbla_p);                        /* eat Non SGML char */
         if (!M_p->forced_data) dl_find(gbla_p); else --(M_p->forced_data);
         goto main_loop;                         /* continue parsing */
      }
      /* Process normal data */
      switch (M_p->parse_mode) {
      case DATATAG_PMO:
         switch(check_datatag(M_p,FALSE)) {      /* check if datatag ended */
         case END_BEFORE_CHDT:                   /* tag ended before */
            M_p->parse_mode = TAG_PMO;
            M_p->is_shortref_in_process=FALSE;
            M_p->forced_data=0;
            dl_find(gbla_p);                     /* get next token */
            goto main_loop;                      /* and loop */
         case END_HERE_CHDT:                     /* tag ended here */
            M_p->parse_mode = TAG_PMO;
            if (GA(curr_token) == CRO_TK) {      /* numeric CR is data */
               goto process_data;                /* stay here */
            }else {
               M_p->forced_data=0;               /* broken shortref */
               dl_find(gbla_p);                  /* get next token */
               goto main_loop;                   /* and loop */
            }
         case NO_END_CHDT:
            break;
         }
         FG_DATA(M_p);
         stw_data(M_p);                          /* enter curr char */
         break;
      case CHAR_PMO:
         FG_DATA(M_p);
         stw_data(M_p);                          /* enter curr char */

#ifdef PURE_CODE_WANTED
         break;
#else
         /*--------------------- Accelerator --------------------+
         | Here is where YASP spend most of its processing time. |
         | The purpose of this code is to increase the speed by  |
         | making a loop shorter than the one that was generated |
         | by the single "break" instruction this code replaces. |
         | Note that this adds redundancies with the rest of the |
         | code, breaking (the beauty of? the program structure).|
         |                                                       |
         | The code will run even faster when separators (SPACE) |
         | or punctuation (like comma, semi-colon) have not been |
         | SHORTREFerenced in the SGML MD -- and therefore do    |
         | not break the loop.                                   |
         +------------------------------------------------------*/
         M_p->is_shortref_in_process = FALSE;
         em_read(gbla_p);
         if (M_p->forced_data) {
            --M_p->forced_data;
            goto main_loop;
         }
         while ((dl_find(gbla_p) == NOT_FOUND_TK) && (GA(curr_char != Ee))) {
            if (is_separator(GA(curr_char))) {
               if (GA(curr_char) == GA(RS_char)) {
                  FG_RS(M_p);
               }else if (GA(curr_char)==GA(RE_char)) {
                  FG_RE(M_p);
#if YASP_RVL_GE(0,1,26)
                  em_pos(gbla_p, &GA(RE_position),0);
#endif
               }else {
                  FG_DATA(M_p);
                  stw_data(M_p);
               }
               em_read(gbla_p);
            }else if (is_ms_char(GA(curr_char))) {
               break;
            }else if (is_nonsgml(GA(curr_char))) {
               stw_flsh(M_p);
               tll_NGML(gbla_p);
               em_read(gbla_p);
            }else {
               FG_DATA(M_p);
               stw_data(M_p);
               em_read(gbla_p);
            }
         }
         goto main_loop;
#endif
      case TAG_PMO:
         if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
            errorh(gbla_p,OUTDOCDAT,ERH_W_SEV+ERH_LOCATE);
            return;                              /* go back (abnormal exit) */
         }
         if (stw_dtcb(M_p)) {                    /* prepare for data OK */
            if (M_p->tcb_p->is.b.dtag_enabled) {
               M_p->parse_mode = DATATAG_PMO;
            }else {
               M_p->parse_mode = CHAR_PMO;
            }
            M_p->is_shortref_in_process = FALSE;
            goto process_data;                   /* redo analysis */
         }
         errorh(gbla_p,OOCDATA,ERH_W_SEV+ERH_LOCATE);
         M_p->parse_mode = ICHAR_PMO;
         /* Fall thru */
      case ICHAR_PMO:                            /* Invalid data */
#if YASP_RVL_GE(0,1,26)
         em_pos(gbla_p,&GA(ooc_position),0);     /* Out Of Context data POS */
#endif
         break;
      }
      M_p->is_shortref_in_process = FALSE;
      em_read(gbla_p);
      if (!M_p->forced_data) dl_find(gbla_p); else --(M_p->forced_data);
      goto main_loop;
   case NET_TK:                                  /* NET "/" */
      if (!M_p->tcb_p->is.b.net_enabled) {       /* if no NET unabled */
         em_unrd(gbla_p,GA(curr_count));         /* consider NET like data */
         GA(curr_token) = NOT_FOUND_TK;
         goto main_loop;
      }
      /* else fall thru */
   case ETAGO_TK:                                /* ETAGO      "</"  */
   case ETAGO_TAGC_TK:                           /* ETAGO_TAGC "</>" */
      if (M_p->parse_mode == DATATAG_PMO) {
         switch(check_datatag(M_p,TRUE)) {       /* finish any datatag */
         case END_BEFORE_CHDT:                   /* characters were unread: */
            M_p->parse_mode = TAG_PMO;
            M_p->is_shortref_in_process = FALSE;
            dl_find(gbla_p);                     /* get next token */
            goto main_loop;                      /* and loop */
         case END_HERE_CHDT:
         case NO_END_CHDT:
            break;
         }
      }
      if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
         errorh(gbla_p,OUTDOCETG,ERH_W_SEV+ERH_LOCATE);
         return;                                 /* go back (abnormal exit) */
      }
      stw_flsh(M_p);
      M_p->parse_mode = TAG_PMO;
      switch (GA(curr_token)) {
      case ETAGO_TAGC_TK:
         stw_etcb(M_p, 0, EMPTY_ET_MNF, ETG_IMF);
         break;
      case NET_TK:
         stw_etcb(M_p, how_many_NET_end(M_p->tcb_p), NULL_ET_MNF, ETG_IMF);
         break;
      case ETAGO_TK:
         {
            uint ecnt;

            if ((ecnt=how_many_tag_end(gbla_p, M_p->tcb_p)) > 0) {
               stw_etcb(
                  M_p, ecnt,
                  (uchar)((GA(curr_token)==TAGC_TK) ? NO_MNF : UNCL_ET_MNF),
                  ETG_IMF
               );
#if YASP_RVL_GE(0,1,26)
            }else {                              /* Invalid: has been ignored */
               if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p);
               tll_SKIP(gbla_p,ERROR_SDC);
#endif
            }
         }
         break;
      }
      M_p->is_shortref_in_process = FALSE;
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case STAGO_TK:                                /* STAGO      "<"  */
   case STAGO_TAGC_TK:                           /* STAGO_TAGC "<>" */
      if (M_p->parse_mode == DATATAG_PMO) {
         switch(check_datatag(M_p,TRUE)) {       /* finish any datatag */
         case END_BEFORE_CHDT:                   /* characters were unread: */
            M_p->parse_mode = TAG_PMO;
            M_p->is_shortref_in_process = FALSE;
            dl_find(gbla_p);                     /* get next token */
            goto main_loop;                      /* and loop */
         case END_HERE_CHDT:
         case NO_END_CHDT:
            break;
         }
      }
      if ((M_p->tcb_p->is.b.zero)&&(M_p->tcb_p->is.b.mhit)) {
         errorh(gbla_p,OUTDOCSTG,ERH_W_SEV+ERH_LOCATE);
         return;                                 /* go back (abnormal exit) */
      }
      stw_flsh(M_p);
      M_p->parse_mode = TAG_PMO;
      stw_otcb(M_p);
      M_p->is_shortref_in_process = FALSE;
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case MDO_COM_TK:                              /* <!-- */
      FG_OTHER(M_p);
      skip_cdc(gbla_p);                          /* does em_read */
#if YASP_RVL_GE(0,1,26)
      if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p);
      tll_SKIP(gbla_p,COMMENT_SDC);
#endif
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case MDO_DSO_TK:                              /* <![  */
      process_omsect(M_p);
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case MSC_MDC_TK:                              /* ]]>  */
      FG_OTHER(M_p);
#if YASP_RVL_GE(0,1,26)
      if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p); /* Try to be in sync. */
      if (!msd_pop(gbla_p)) {                    /* Too many ]]>  ... */
         tll_SKIP(gbla_p,ERROR_SDC);
      }else {
         tll_SKIP(gbla_p,MSC_SDC);
      }
#else
      msd_pop(gbla_p);
#endif
      if ((M_p->parse_mode == CHAR_PMO) && (M_p->tcb_p->is.b.dtag_enabled)) {
         M_p->parse_mode = DATATAG_PMO;
      }
      M_p->is_shortref_in_process = FALSE;
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case MDO_TK:
      FG_OTHER(M_p);
#if YASP_RVL_GE(0,1,26)
      if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p); /* Try to be in sync. */
#endif
      process_usedcl(M_p);
      M_p->is_shortref_in_process = FALSE;
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   case MDO_MDC_TK:                              /* <!>  */
      FG_OTHER(M_p);
      M_p->is_shortref_in_process = FALSE;
#if YASP_RVL_GE(0,1,26)
      if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p);
      tll_SKIP(gbla_p,MDOMDC_SDC);
#endif
      dl_find(gbla_p);                           /* get next token */
      goto main_loop;                            /* and loop */
   default:                                      /* SHORTREF */
      {
         /*---------------------------------------+
         |SHORTREFS: If a current map is activated|
         |AND the shortref number is not bigger   |
         |than the one the DTD was compiled with  |
         |AND the shortref is mapped...           |
         +---------------------------------------*/
         ETY *ety_p;
         int  sr_id;

         if ((M_p->tcb_p->srm_p) &&              /* If a current map exists */
            ((sr_id = (int)GA(curr_token)-(int)SR_first) < GA(dtd_sr_max)) &&
            ((ety_p = (M_p->tcb_p->srm_p + sr_id)->ety_p) != (ETY *)0)) {
#if YASP_RVL_GE(0,1,26)
            if (M_p->parse_mode!=DATATAG_PMO) stw_flsh(M_p); /* Try sync. */
#endif
            tll_SR(gbla_p, M_p->tcb_p->srm_p, sr_id);
            if (!M_p->is_shortref_in_process) {  /* Remember current SR */
               M_p->is_shortref_in_process = TRUE;
               M_p->sr_ety_p = ety_p;
               M_p->sr_id = sr_id;
            }
            if (!em_open(gbla_p,ety_p)) ety_p = (ETY *)0;
            if (!process_entity(M_p,ety_p)) {
               return;                           /* OUT of DOC: abnormal exit */
            }
            dl_find(gbla_p);                     /* get next token */
            goto main_loop;                      /* and loop */
         }
      }
      em_unrd(gbla_p,GA(curr_count));
      M_p->forced_data = GA(curr_count) - 1;
      GA(curr_token) = NOT_FOUND_TK;
      goto main_loop;
   case ERO_GRPO_TK:
      errorh(gbla_p,NIMPL,ERH_T_SEV+ERH_LOCATE,1001);  /*CONCUR/LINK*/
      return;
   case ETAGO_GRPO_TK:
   case STAGO_GRPO_TK:
      errorh(gbla_p,NIMPL,ERH_T_SEV+ERH_LOCATE,1004);  /*CONCUR*/
      return;
   }
}

/*STATIC-----------------------------------------------------initialize_COMMON-+
|Initializes the COMMON structure when starting a new document                 |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT initialize_COMMON(GA_P gbla_p, ELT  *base_elt_p)
{
   COM_P M_p;

   M_p = GA(M_p);
   M_p->is_shortref_in_process = FALSE;
   M_p->last_ended_elt_p = (ELT *)0;
   M_p->parse_mode = TAG_PMO;
   M_p->forced_data = 0;
   M_p->tcb_stk_ix = M_p->tcb_stk_max = -1;
   M_p->tcb_p = M_p->tcb_stack;
   M_p->tcb_p->is.b.mhit  = FALSE;
   M_p->tcb_p->is.b.ms = MATCH_MS;
   M_p->tcb_p->elt_p->mdl.p->l.elt.p = base_elt_p;
   *(M_p->state_p = M_p->state) = STG_STATE;         /* Initiate first state */
   set_mode(gbla_p,CON_MODE);
   dl_find(gbla_p);
   if ((GA(curr_token) != STAGO_TK)&&(GA(curr_token) != STAGO_TAGC_TK)) {
      if ((GA(curr_token) == NOT_FOUND_TK) && (GA(curr_char) == Ee) &&
         (tll_DAPN(gbla_p))) {         /* Give Application a chance to stop */
         return(FALSE);                /* secret return code or wild exit */
      }                                /* If it didn't, */
      stw_base(M_p);                   /* start the base element */
   }
   return(TRUE);                       /* We must continue */
}

/*--------------------------------------------------------------------prs_prid-+
| Parse the main (primary) document instance                                   |
+-----------------------------------------------------------------------------*/
void P_FCT prs_prid(GA_P gbla_p, ELT *base_elt_p)
{
   ELT elt_zero;
   MDL mdl_base;
   COM_P M_p;

#if (defined(DOS_SYS) || defined(OS2V1_SYS))    /* 16-bit systems */
   COMMON M;                                    /* COMMON is on the stack */
   M_p = &M;
#else
   M_p = malloc(sizeof(COMMON));
#endif

   M_p->gbla_p = gbla_p;
   /*----------------------------------+
   |Initiate TCB with the Zero Element |
   +----------------------------------*/
   memset((uchar *)&elt_zero,0,sizeof(ELT));
   memset((uchar *)&mdl_base,0,sizeof(MDL));
#if (defined(IBMC370) || defined(IBMCSET2))  /* C370 *and* C/Set2 bug */
   M_p->tcb_stack[0].grp_p0[0].mdl_p = elt_zero.mdl.p = &mdl_base;
#else
   M_p->tcb_stack[0].grp_p0->mdl_p = elt_zero.mdl.p = &mdl_base;
#endif
   M_p->tcb_stack[0].elt_p = &elt_zero;
   M_p->tcb_stack[0].dtp_p = (DTP *)0;
   M_p->tcb_stack[0].srm_p = (SRM *)0;
   M_p->tcb_stack[0].grp_p = M_p->tcb_stack[0].grp_p0 - 1;
   M_p->tcb_stack[0].is.all_bits = 0;
   M_p->tcb_stack[0].is.b.zero = TRUE;

   GA(M_p) = M_p;
   stw_init(gbla_p);                 /* Init the stower's output buffers */
   tll_init();                       /* Tell we've just started */
   if (initialize_COMMON(gbla_p,base_elt_p)) {  /* no show stoppers */
      GA(N_p) = malloc(sizeof(COMMON));         /* make a save for backuping */
      parse_doc(gbla_p);                        /* parse the document */
      free(GA(N_p));
   }
#if (!defined(DOS_SYS) && !defined(OS2V1_SYS))  /* 32-bit systems */
   free(M_p);
#endif
}

/*--------------------------------------------------------------------prs_subd-+
| Parse a SUBDOC                                                               |
+-----------------------------------------------------------------------------*/
void P_FCT prs_subd(GA_P gbla_p, ELT *base_elt_p)
{
   COMMON *previous_M_p;

   memcpy(previous_M_p = malloc(sizeof(COMMON)), GA(M_p), sizeof(COMMON));
   if (initialize_COMMON(gbla_p,base_elt_p)) {
      parse_doc(gbla_p);
   }
   memcpy(GA(M_p), previous_M_p, sizeof(COMMON));
   free(previous_M_p);
}
/******************************************************************************/
