/*******************************************************************************
* 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 / SGML Library                                       *
* STATUS           = Version 0, Release 1, Level 25 and up                     *
* COMPONENT NAME   = skip.c                                                    *
* DESCRIPTIVE NAME = Skip miscellaneous separator in prolog (ps, ts, ds, com)  *
*                    Other miscellaneous subroutines.                          *
* DATE_WR          = 07/28/1987                                                *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
*                                                                              *
* DEPENDENCIES =  (see Include list)                                           *
*                                                                              *
* IMPORT=                                                                      *
*                                                                              *
* EXPORT=                                                                      *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & Reason (brief)                             *
*      1 11/11/89 PGR      Handle Marked Section Declaration                   *
*      2 12/23/90 PGR      PER in a group are made of one ore more tokens      *
*      3 01/03/91 PGR      R/CDATA marked section not recognized in prolog     *
*      4 03/07/91 PGR      Separator can also start delimiters                 *
*******************************************************************************/

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

/*--------------+
|Local Functions|
+--------------*/
static CHOICES const * _loadds LP_FCT adr_msdk_choices(void);

/*----------------------------------------------------------------------skip_S-+
|Skip S separator and issue dl_find                                            |
+-----------------------------------------------------------------------------*/
boolean P_FCT skip_S(GA_P gbla_p)
{
   if (!is_separator(GA(curr_char))) {
      dl_find(gbla_p);
      return(FALSE);
   }
   while (dl_find(gbla_p) == NOT_FOUND_TK) {
      if (!is_separator(em_read(gbla_p))) {
         dl_find(gbla_p);
         break;
      }
   }
   return(TRUE);
}

/*---------------------------------------------------------------------skip_ts-+
| Skip token separator                                                         |
|                                                                              |
| [70] ts = s | Ee | parameter entity reference                                |
|  [5]  s = SPACE | RE | RS | SEPCHAR                                          |
|                                                                              |
| IMPLEMENTATION NOTE:                                                         |
| In a group, parameter entities reference must consist at least of one        |
| token (skip_ts has been called directly, not from skip_ps or skip_ds).       |
|                                                                              |
| RETURN:                                                                      |
|    TRUE if at least one ts was found                                         |
|    FALSE if no ts were found                                                 |
+-----------------------------------------------------------------------------*/
boolean P_FCT skip_ts(GA_P gbla_p)
{
   boolean found_it = FALSE;
   boolean check_empty_per;

   if ((GA(curr_mode) == GRP_MODE) && (GA(enty_level) == GA(floor_level))) {
      check_empty_per = TRUE;
   }else {
      check_empty_per = FALSE;
   }
   for (;;) {
      found_it |= skip_S(gbla_p);
      switch (GA(curr_token)) {
      case PERO_TK:            /* Parameter Entity */
         found_it = TRUE;
         em_opper(gbla_p);
         continue;
      case PERO_GRPO_TK:       /* Specific Parameter Entity */
         found_it = TRUE;                       /* 9.4.4: */
         if (!GA(is_odt_done)) {                /* still in prolog */
            errorh(gbla_p,INVDLM,ERH_T_SEV+ERH_LOCATE);  /* error */
         }else {
            if (!get_ngac(gbla_p)) {            /* Not active: */
               uchar name[NAMELEN_MAX+1];
               get_enmd(gbla_p,name);           /* get rid of it (9.4.4) */
            }else {
               errorh(gbla_p,NIMPL,ERH_T_SEV+ERH_LOCATE,1010);  /*CONCUR*/
            }
         }
         continue;
      case NOT_FOUND_TK:
         if (GA(curr_char) == Ee) {
            found_it = TRUE;
            if (GA(enty_level) > GA(floor_level)) {
               em_close(gbla_p);
               /* Except if the PER is kept unparsed (see get.c), */
               /* enty_level is always equal here to floor_level. */
               if ((check_empty_per) && (GA(enty_level) == GA(floor_level))) {
                  errorh(gbla_p,INVEMPPER,ERH_W_SEV+ERH_LOCATE);
               }
               continue;
            }else {
               if (!GA(quiet_at_floor)) {
                  errorh(gbla_p,INVLNEST,ERH_T_SEV+ERH_LOCATE);
                  /* can't return from this */
               }
            }
         }
         /* fall thru */
      default:
         return(found_it);
      }
   }
}

/*---------------------------------------------------------------------skip_ps-+
| Skip parameter separator                                                     |
|                                                                              |
| ASSUME: mode is MD_MODE                                                      |
|                                                                              |
| [65] ps = s | Ee | parameter entity reference | comment                      |
|                                                                              |
| IMPLEMENTATION NOTE:                                                         |
| [70] ts = s | Ee | parameter entity reference                                |
|     ==> ps = ts | comment                                                    |
|                                                                              |
| RETURN:                                                                      |
|    TRUE if at least one ps was found                                         |
|    FALSE if no ps were found                                                 |
|                                                                              |
+-----------------------------------------------------------------------------*/
boolean P_FCT skip_ps(GA_P gbla_p)
{
   boolean found_it = FALSE;

   for (;;) {
      found_it |= skip_ts(gbla_p);
      if (GA(curr_token) == COM_TK) {              /* COM (start) found */
         if (!skip_dlm(gbla_p,GA(dstab)[COM_ix].p)) { /* skip to ending COM */
            errorh(gbla_p,UBLNCOM,ERH_T_SEV+ERH_LOCATE);
         }
         found_it = TRUE;
         continue;
      }
      return(found_it);
   }
}

/*--------------------------------------------------------------------skip_Wps-+
| Required by ISO 8879, when para 3 of 10.1.1 doesn't apply.                   |
| However YASP can pursue (severity W).                                        |
+-----------------------------------------------------------------------------*/
void P_FCT skip_Wps(GA_P gbla_p)
{
   TK prev_token;

   prev_token = GA(curr_token);
   if ((!skip_ps(gbla_p)) &&
      (prev_token == NOT_FOUND_TK) &&
      (GA(curr_token) == NOT_FOUND_TK)) {
      errorh(gbla_p,NOPS,ERH_W_SEV+ERH_LOCATE);
   }
}

/*---------------------------------------------------------------------skip_ds-+
| Skip declaration separator                                                   |
|                                                                              |
|                                                                              |
| [71] ds = s | Ee | parameter entity reference | comment declaration          |
|         | processing instruction | marked section declaration                |
|                                                                              |
| IMPLEMENTATION NOTE:                                                         |
| [70] ts = s | Ee | parameter entity reference                                |
|     ==> ds = ts | comment declaration                                        |
|         | processing instruction | marked section declaration                |
|                                                                              |
| RETURN:                                                                      |
|    TRUE if at least one ds was found                                         |
|    FALSE if no ds were found                                                 |
|                                                                              |
+-----------------------------------------------------------------------------*/
boolean P_FCT skip_ds(GA_P gbla_p)
{
   boolean found_it = FALSE;

   for (;;) {
      found_it |= skip_ts(gbla_p);
      switch (GA(curr_token)) {
      case MDO_COM_TK:
         skip_cdc(gbla_p);     /* skip comment declaration */
         found_it = TRUE;
         continue;
      case MDO_MDC_TK:         /* "short" comment */
         found_it = TRUE;
         continue;
      case PIO_TK:             /* Processing Instruction */
         get_PI(gbla_p);
         found_it = TRUE;
         continue;
      case MDO_DSO_TK:         /* marked section declaration */
         msd_psh(gbla_p);
         found_it = TRUE;
         switch (GA(curr_mode)) {
         case FCDMS_MODE:
         case RCDMS_MODE:
         default:              /* INCLUDE or TEMP */
            continue;
         case IGNMS_MODE:
            break;             /* msd_psh skipped over the ignored stuff */
         }
         /* fall thru */
      case MSC_MDC_TK:         /* ]]>  */
         msd_pop(gbla_p);
         continue;
      default:                 /* DSC, MDO, NOT_FOUND */
         return(found_it);
      }
   }
}

/*--------------------------------------------------------------------skip_dlm-+
| Skip to an ending delimiter.                                                 |
| On Entry:                                                                    |
|    dlm_p points the delimiter to find.                                       |
| Returns:                                                                     |
|    TRUE: if the ending delimiter was found,                                  |
|          curr_char is the character that follows the delimiter.              |
|    FALSE: If End-of-Entity was found,                                        |
|          curr_char is the Ee character.                                      |
| -- OPTIMIZED --                                                              |
+-----------------------------------------------------------------------------*/
boolean P_FCT skip_dlm(GA_P gbla_p, uchar *dlm_p)
{
   uchar dlm_1 = *dlm_p;
   uchar *d_p, c;

   c = (uchar)GA(curr_char);
   for (;;) {                                  /* main loop */
      if (dlm_1 != c) {                        /* until dlm_1 is found */
         if (Ee != c) {                        /* while no Ee */
            c = (uchar)em_read(gbla_p);        /* read */
            continue;
         }else {                               /* Ee was found */
            return(FALSE);                     /* return in error */
         }
      }
      for (d_p=dlm_p ;;) {                     /* dlm_1 was found */
         c = (uchar)em_read(gbla_p);           /* get next character ready */
         if (*++d_p) {                         /* not at end */
            if (*d_p == c) continue;           /* and does match: continue */
            c = (uchar)em_unrd(gbla_p,d_p-dlm_p-1); /* mismatch: unread - 1 */
            break;                             /* back to main loop */
         }else {                               /* end of delimiter */
            return(TRUE);                      /* return OK */
         }
      }
   }
}

/*--------------------------------------------------------------------skip_cdc-+
| Skip a comment declaration                                                   |
|                                                                              |
| [91] comment declaration = mdo, (comment, (s | comment)*)?, mdc              |
+-----------------------------------------------------------------------------*/
void P_FCT skip_cdc(GA_P gbla_p)
{
   psh_mode(gbla_p,MD_MODE);
   for (;;) {
      if (!skip_dlm(gbla_p,GA(dstab)[COM_ix].p)) {   /* skip to ending COM */
         errorh(gbla_p,UBLNCOM,ERH_T_SEV+ERH_LOCATE);
      }
      skip_S(gbla_p);
      switch (GA(curr_token)) {
      case MDC_TK:
           break;
      case COM_TK:
           continue;
      default:
           errorh(gbla_p,NOMDC,ERH_T_SEV+ERH_LOCATE);
           break;
      }
      break;
   }
   pop_mode(gbla_p);
}
/******************************************************************************/

/*STATIC------------------------------------------------------adr_msdk_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
#define INVALID_VL      0   /* Value doesn't belong to the list */
#define TEMP_VL         1   /* Keywords code, weight shows precedence order */
#define INCLUDE_VL      2
#define RCDATA_VL       4
#define CDATA_VL        8
#define IGNORE_VL      16

static CHOICES const * _loadds LP_FCT adr_msdk_choices(void)
{
   static CHOICES const msdk_choices[] = {
      { offsetof(GNWDTAB,TEMP),    TEMP_VL     },
      { offsetof(GNWDTAB,INCLUDE), INCLUDE_VL  },
      { offsetof(GNWDTAB,RCDATA),  RCDATA_VL   },
      { offsetof(GNWDTAB,CDATA),   CDATA_VL    },
      { offsetof(GNWDTAB,IGNORE),  IGNORE_VL   },
      { -1,                        INVALID_VL  }
   };
   return(msdk_choices);
}

/*---------------------------------------------------------------------msd_psh-+
| Enter here when a Marked Section Start (MDO_DSO_TK) has been decoded         |
| Returns with the right mode pushed, if not INCLUDE or TEMP.                  |
|                                                                              |
| [93] marked section declaration =                                            |
|              marked section start, status keyword specification, dso,        |
|              marked section, marked section end                              |
|                                                                              |
| [94] marked section start = mdo, dso                                         |
|                                                                              |
| [95] marked section end = msc, mdc                                           |
|                                                                              |
| [96] marked section = SGML character*                                        |
|                                                                              |
| [97] status keyword specification =                                          |
|                (ps+, (status keyword | "TEMP"))*, ps*                        |
|                                                                              |
| [100] status keyword = "CDATA" | "IGNORE" | "INCLUDE" | "RCDATA"             |
|                                                                              |
| As stated in 9.1 and note of 4.263, a "marked section end" in an RCDATA      |
| marked section is not recognized as such if it occurs in an entity           |
| imbedded in the marked section.                                              |
| If RCDATA is specified, remember in ms_enty_level the current                |
| entity level, to avoid false "]]>" recognition.                              |
| "check_contextual_constraint" in dlfind.c will then deny a delimiter to be   |
| an MSC_MDC if it occurs at a higher entity level than the entity_level       |
| the RCDATA marked section started with.                                      |
+-----------------------------------------------------------------------------*/
void P_FCT msd_psh(GA_P gbla_p)
{
   int value, new_value;

   value = 0;
   psh_mode(gbla_p,MD_MODE);
   for (;;) {
      skip_ps(gbla_p);    /* was while (skip(ps)) (one ps required) */
      /*-----------------------+
      |(ps+, keywd)*, ps*, DSO,|
      +-----------------------*/
      switch(GA(curr_token)) {
      case NOT_FOUND_TK:
         switch(
            new_value=choos_in(gbla_p,adr_msdk_choices(),(GNWD *)&GA(gnwdtab))
         ) {
         case INVALID_VL:
            errorh(gbla_p,INVKWD,ERH_T_SEV+ERH_LOCATE);
            break;
         case TEMP_VL:
         case INCLUDE_VL:
            break;
         default:                /* RCDATA, CDATA, IGNORE */
            value |= new_value;
            break;
         }
         continue;
      case DSO_TK:
         break;           /* OK... */
      default:            /* PLUS_GRPO, MINUS_GRPO, RNI, LIT, LITA, */
         break;           /* MDC, DSC, GRPO: INVDLM will be issued ... */
      }
      break;    /* break the for loop, if no "continue" bypassed it */
   }
   pop_mode(gbla_p);
   if (GA(curr_token) != DSO_TK) {
      /* may be we could try to forgive the user for the DSO omission... */
      errorh(gbla_p,INVDLM,ERH_T_SEV+ERH_LOCATE);
   }
   if (++GA(ms_level) > (int)TAGLVL) {
      errorh(gbla_p,BIGMSLVL,ERH_W_SEV+ERH_LOCATE);
      value = IGNORE_VL;
   }
   if ((!GA(is_odt_done)) &&                             /* In prolog,      */
      ((value < IGNORE_VL) && (value >= RCDATA_VL))) {   /* CDATA or RCDATA */
      errorh(gbla_p,INVMSPRO,ERH_W_SEV+ERH_LOCATE);
      value = IGNORE_VL;
   }
   if (value >= IGNORE_VL) {
      psh_mode(gbla_p,IGNMS_MODE);
      /*--------------------------------------------------+
      |In IGNORE mode, it's faster to skip all sub-MS     |
      |in a separate procedure, while matching MDSO/MSDCs |
      +--------------------------------------------------*/
      {
         uint ms_ignore_depth = 1;

         for (;;) {
#if (defined(IBMC370))
            dl_find(gbla_p);  switch (GA(curr_token)) {  /* IBM C370 Bug */
#else
            switch (dl_find(gbla_p)) {
#endif
            case MDO_DSO_TK:
               ++ms_ignore_depth;
               continue;
            case MSC_MDC_TK:
               if (--ms_ignore_depth == 0) break;
               continue;
            case NOT_FOUND_TK:
            default:
               if (GA(curr_char) == Ee) {
                  errorh(gbla_p,UBLNMSO,ERH_T_SEV+ERH_LOCATE);
               }
               em_read(gbla_p);
               continue;
            }
         break;
         }
      }
   }else if (value >= CDATA_VL) {
      psh_mode(gbla_p,FCDMS_MODE);
   }else if (value >= RCDATA_VL) {
      GA(ms_enty_level) = GA(enty_level); /* avoid false MSC_MDC recognition */
      psh_mode(gbla_p,RCDMS_MODE);
   }
}
#undef INVALID_VL
#undef TEMP_VL
#undef INCLUDE_VL
#undef RCDATA_VL
#undef CDATA_VL
#undef IGNORE_VL

/*---------------------------------------------------------------------msd_pop-+
| Enter here when a Marked Section End (MSC_DSC_TK) has been decoded           |
| Pop previous mode if not INCLUDE or TEMP.                                    |
| Return FALSE if unable to pop.                                               |
+-----------------------------------------------------------------------------*/
boolean P_FCT msd_pop(GA_P gbla_p)
{
   if (--GA(ms_level) < 0) {
      ++GA(ms_level);
      errorh(gbla_p,INVMSE,ERH_W_SEV+ERH_LOCATE);
      return(FALSE);  /* report the error to the caller */
   }else {
      /* Pop regular mode if not INCLUDE (one of IGNORE/RCDATA/CDATA) */
      if (GA(curr_mode) >= RCDMS_MODE) pop_mode(gbla_p);
      return(TRUE);   /* report OK to the caller */
   }
}

/*--------------------------------------------------------------------capa_chk-+
| Check the capacities do not exceed the max allowed values                    |
+-----------------------------------------------------------------------------*/
void P_FCT capa_chk(GA_P gbla_p, ulong *capcnt_p)
{
   int i = -1;
   ulong j, total;
   ulong *capmax_p = GA(capmax) - 1;
   uint  *cappoint_p = GA(cappoint) - 1;

   total = 0L;
   --capcnt_p;
   while (++i < CAP_MAX_SIZE) {
      if (i == CAP_MAX_SIZE - 1) {               /* TOTALCAP reached */
         j = total;
      }else {
         total += (j = *++capcnt_p * *++cappoint_p);
      }
      if (j > *++capmax_p) {
         errorh(gbla_p,BIGCAP,ERH_W_SEV,GA(cap_names)[i], j ,*capmax_p);
      }
   }
}
/******************************************************************************/
