/*******************************************************************************
* 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 35 and up                     *
* COMPONENT NAME   = tag.c                                                     *
* DESCRIPTIVE NAME = Parse start tag, end_tag, attribute specif (also data)    *
* DATE_WR          = 08/04/1988                                                *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
*                                                                              *
* This module parses all that is related to element (tag) attributes.          *
* It is made of 4 main functions:                                              *
*                                                                              *
*  tg_gtavs   checks the validity of an Attribute Value Specification (AVS),   *
*             in conformance with its Attribute Definition (ATD)               *
*             It is called:                                                    *
*             - by ODTATTL, for a default value assigned in an <ATTLIST> MD    *
*             - by tg_stasl (in this module) either when an attribute          *
*               specification is found in content (tg_parse), or when an       *
*               attribute specification is given for a data attribute spec     *
*               of a data entity with a Notation (ODTENTY)                     *
*             The _TGF flags tells what kind of ASL is processed:              *
*             - DEF_TGF:   Default AVS being parsed (DATA or TAG)              *
*             - TAG_TGF:   Tag Attribute Specification being parsed            *
*             - DATA_TGF:  Data Attribute Specification being parsed           *
*                                                                              *
*  tg_stasl   parses an attribute specification list: (attribute name = AVS)*  *
*             either for a data (DATA_TGF, at compile time) or a tag           *
*             (TAG_TGF, at run time).  For each AVS, it mallocates a string    *
*             to be filled by the specified value, calling tg_gtavs to do the  *
*             job.                                                             *
*                                                                              *
*  tg_parse   Called when a tag is parsed.                                     *
*             Mallocates an ATD array and fills it with the content            *
*             of the ATD array created when the ATTLIST was parsed.            *
*             Then, calls tg_stasl.                                            *
*                                                                              *
*  tg_vldat   Validates a TAG ASL, after all "document time" value have been   *
*             parsed                                                           *
*                                                                              *
*  This can be resumed by the following diagram:                               *
*                                                                              *
*                    |   tg_parse   |   tg_stasl   |   tg_gtavs   |            *
*                    +--------------+--------------+--------------+            *
*   ASL of TAGS  ----+----> X ------+----> X ------+----> X       |            *
*   in CONTENT       |              |              |  (TAG_TGF)   |            *
*                    +--------------+--------------+--------------+            *
*   ASL of N/S/C DATA External -----+----> X ------+----> X       |            *
*   Entities (DTD Compile)          |              |  (DATA_TGF)  |            *
*                                   +--------------+--------------+            *
*   AVS of default value --------------------------+----> X       |            *
*   (ATTLIST for notation, or TAG)                 |  (DEF_TGF)   |            *
*                                                  +--------------+            *
*                                                                              *
* IMPLEMENTATION NOTES =                                                       *
* A specific memory pool is used to store the "instance-time" (user specified) *
* values.  This temporary storage will be reused after the application returns *
* from the notification call.                                                  *
*                                                                              *
* There is currently no distinction in processing DATA_TGF or TAG_TGF          *
* and we only consider if it is DEF_TGF or (DATA_TGF | TAG_TGF).               *
* If the distinction between DATA_TGF | TAG_TGF stays useless,                 *
* in a next release, TGF will be a boolean: DEF_TGF or not.                    *
*                                                                              *
* DEPENDENCIES =  (see Include list)                                           *
*                                                                              *
* IMPORT=                                                                      *
*                                                                              *
* EXPORT=                                                                      *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & Reason (brief)                             *
*      1 10/27/88 PETRUS   Enhanced tg_parse/vld_att code                      *
*      2 12/27/88 PETRUS   Data Attributes (NOTATION)                          *
*      3 09/10/89 PGR      MSO/MSI/MSS CHAR functionalities                    *
*      4 02/17/90 PGR      ENTITIES, ENTITY, ATTCHCAP computation for CURRENT  *
*      5 12/09/90 PGR      Added descriptor for CDATA attributes               *
*      6 03/01/91 PGR      Fixed CURRENT and dclv is notation or name tok grp  *
*      7 08/28/91 PETRUS   Reuse ITFCE_POOL storage                            *
*      8 11/03/91 PETRUS   IMPLIED and not specif. notations are not INVNOTASP *
*******************************************************************************/

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

/*--------------+
|Local Functions|
+--------------*/
static boolean LP_FCT check_list           (GA_P, uchar *, ATD *);
static boolean LP_FCT check_list_and_class (GA_P, uchar *, ATD *);
static boolean LP_FCT check_id             (GA_P, uchar *, ATD *);
static uint    LP_FCT valid_ety            (GA_P, ATD *);
static uint    LP_FCT valid_idref          (GA_P, ATD *);
static void    LP_FCT valid_nota           (GA_P, ATD *);
static void    LP_FCT save_new_current     (GA_P, ATD *, ATD *);

/*--------------------------------------------------------------------tg_lsfnd-+
|Check if a token belongs to a list (ASCIIZ string, tokens separated by SPACE) |
|Returns a NULL pointer if not found, or pointing past the last character      |
|(either SPACE or EOS).                                                        |
|                                                                              |
|Note: for LISTED_AF attributes, the list_p pointer is no more at              |
|      atd.val.lit.p after the attribute value has been assigned:              |
|      this assignement destroys the initial value.                            |
|      To be able to call this routine even for duplicate attributes,          |
|      we use the property that atd.name.p is followed by the list:            |
|      So, atd.p.name.p + atd.p.name.len + 1 points the original list.         |
+-----------------------------------------------------------------------------*/
uchar * P_FCT tg_lsfnd(
   GA_P gbla_p,
   uchar *tkn_p,
   register uchar *list_p)
{
   register uchar *c_p;

   for (--list_p, c_p = tkn_p-1; ;c_p = tkn_p-1) {
      while (*++c_p == *++list_p) if (!*list_p) return(list_p);
      if ((*list_p == GA(SPACE_char))&&(!*c_p)) return(list_p);
      while (*list_p != GA(SPACE_char)) if (!*list_p++) return ((uchar *)0);
   }
}

/*STATIC------------------------------------------------------------check_list-+
|Check if the AVS belongs to a list of declared value.                         |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT check_list(GA_P gbla_p, uchar *tkn_p, ATD *atd_p)
{
   if (!(tg_lsfnd(gbla_p,tkn_p,atd_p->name.p + strlen(atd_p->name.p) + 1))) {
      errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE);
      return (FALSE);
   }else {
      return(TRUE);
   }
}

/*STATIC--------------------------------------------------check_list_and_class-+
|Check if the AVS belongs to a list of declared value.                         |
|If so, reorder the list in such a manner the specified attribute value        |
|comes first, and return TRUE.                                                 |
|Return FALSE  otherwise.                                                      |
|                                                                              |
|IMPLEMENTATION NOTE: Only for parsing the DEFAULT value, in ATTL MD:          |
|this routine will put the DEFAULT value first in the list of allowable        |
|attribute value spec.                                                         |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT check_list_and_class(
   GA_P gbla_p,
   uchar *tkn_p,
   ATD   *atd_p)
{
   uchar *c_p;
   uint i, j;
   uchar *lit_p = atd_p->val.lit._.p;

   if (!(c_p = tg_lsfnd(gbla_p,tkn_p,lit_p))) {
      errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE);
      return(FALSE);
   }else {
      if ((j = c_p - lit_p - (i = strlen(tkn_p))) > 0) { /* Not already first */
         memmove(lit_p+i+1, lit_p, j-1);
         memcpy(lit_p, tkn_p, i);
         *(lit_p + i) = GA(SPACE_char);
      }
      return(TRUE);
   }
}

/*STATIC--------------------------------------------------------------check_id-+
|Check if an ID is unique                                                      |
+-----------------------------------------------------------------------------*/
static boolean LP_FCT check_id(GA_P gbla_p, uchar *tkn_p, ATD *atd_p)
{
   EHSH *ehsh_p;
   int prev_poolid;
   boolean rc;

   prev_poolid = pl_accs(gbla_p,0);      /* malloc/free in main pool */
   if (!(ehsh_p = ehsh_open(GA(id_scope),tkn_p))->var_p) { /* not set */
      /*------------------------------------------------------------+
      | Note: the following is dangerous.  the var_p field has a >0 |
      | unsigned int, rather than the address of a mallocated area. |
      | The hash table can *not* be unloaded (ehsh_uld) as it is!   |
      +------------------------------------------------------------*/
      ehsh_p->var_p = (void *)(GA(curr_elt_id));
      rc = TRUE;
   }else {
      errorh(gbla_p,DUPID,ERH_W_SEV+ERH_LOCATE, tkn_p, (ulong)(ehsh_p->var_p));
      rc = FALSE;
   }
   pl_accs(gbla_p,prev_poolid);          /* back to previous pool */
   return(rc);
}

/*--------------------------------------------------------------------tg_gtavs-+
|Get (and check) an Attribute Value Specification                              |
|                                                                              |
|See [33], [34], [35] for a definition of Attribute Value Specification        |
|                                                                              |
|On ENTRY:                                                                     |
|   "curr_token/curr_char" points the first character of the AVS,              |
|    atd_p points an ATD that defines the attribute,                           |
|         or is a NULL pointer to tell "get rid of the AVS"                    |
|    str_p is pointing a buffer to receive the specified value.                |
|    desc_pp points a caller's area where to put the address of an             |
|          array of ADLEN field(s) describing how str_p was build,             |
|          if needed (CDATA attributes).                                       |
|          This array is mallocated, it's up to the caller to free it.         |
|    attval_nln_p points a caller's area where to put the normalized length    |
|         of the AVS                                                           |
|                                                                              |
|When RETURNING:                                                               |
|  - "curr_char" is at next unknown character.                                 |
|  - Caller's buffer pointed by str_p has the attribute value,                 |
|    its length (-1 if invalid), not counting the ending EOS, is returned.     |
|  - The address field pointed by desc_pp is NULL if it wasn't used.           |
|    Otherwhise, it points to a mallocated array of ADLENS:                    |
|     - the first ADLEN.len contains the count of ADLEN in this array          |
|       (including this very first ADLEN).  ADLEN.p is irrelevant.             |
|     - Each ADLEN that follows are made of:                                   |
|           - a pointer to the entity structure (0 means just text)            |
|           - the length of the described portion of string                    |
|    It's up to the caller to free this array.                                 |
|                                                                              |
|The attribute value or attribute value literal is parsed as explained         |
|in 7.9.3 of ISO8879-1986(E).                                                  |
|"CDATA_TK" tell us if we need to keep blanks sequences, leading and           |
|trailing blanks.                                                              |
|                                                                              |
|IMPLEMENTATION NOTES:                                                         |
|                                                                              |
|1) Things are not exactly the same between:                                   |
|   - parsing of a default AVS (TAG or DATA attributes) (Compile time)         |
|   - parsing of a TAG Attribute Specification          (Compile Time)         |
|   - parsing of a DATA Attribute Specification         (Document Time)        |
|   To avoid two version of the almost_same code,                              |
|   the flag TGF makes the difference.                                         |
|                                                                              |
|2) In most cases, the parse is kept going on to the end of the avs,           |
|   whatever error occurs.  Only one message is issued.                        |
|                                                                              |
|Recognized in LITC mode:                                                      |
|  CRO_TK        , CON_MO | LITC_MO | LITP_MO                                  |
|  LIT_TK        , GRP_MO | LITC_MO | LITP_MO | MD_MO | TAG_MO                 |
|  LITA_TK       , GRP_MO | LITC_MO | LITP_MO | MD_MO | TAG_MO                 |
|  ERO_TK        , CON_MO | LITC_MO                                            |
|  ERO_GRPO_TK   , CON_MO | LITC_MO                                            |
+-----------------------------------------------------------------------------*/
int P_FCT tg_gtavs(
   GA_P    gbla_p,
   uchar   *str_p,                    /* Where to put the AVL */
   ATD     *atd_p,                    /* Attribute descriptor or NULL */
   ADLEN   **desc_pp,                 /* Describes how the string was build */
   uint    *attval_nln_p,             /* Where to put its normalized length */
   TGFLAG  tgf)
{
   uchar *c_p, *d_p, s_first_char, s_other_char;  /* s_ ==> syntax */
   TK delim_token;
   int init_enty_level = GA(enty_level);
   uchar new_char, *tkn_p;
   boolean is_space_waiting, is_namecase, is_plural, is_invalid_avs, is_cdata;
   boolean is_mss_hidden, is_mss_now, is_mss_preceed;
   boolean (LP_FCT *check_item)(GA_P, uchar *, ATD *);
   uint attval_nln = NORMSEP;
   uint adlen_ix = 0;

   *desc_pp = (ADLEN *)0;
   *attval_nln_p = 0;
   is_cdata = is_space_waiting = is_mss_now = is_mss_hidden = FALSE;
   check_item = (boolean (LP_FCT *)(GA_P, uchar *, ATD *))0;
   s_first_char = S_LETTER | S_NMSTRT;   /* default to name characters */
   s_other_char = S_LETTER | S_NMSTRT | S_DIGIT | S_NMCHAR;
   if (!atd_p) {                         /* just get rid of AVS: */
      is_namecase = FALSE;
      is_invalid_avs = TRUE;
      is_plural = FALSE;
   }else {
      is_plural = (boolean)(atd_p->kind & 1);  /* isolate plural in kind */
      is_namecase = IS_BITON(GA(opt).ot,GENERAL_CASE_OT) ? TRUE : FALSE;
      is_invalid_avs = FALSE;
      switch (atd_p->kind & ~1) {        /* kind, with or w/o plural */
      case GROUP_AFK:
         s_first_char = S_LETTER | S_NMSTRT | S_DIGIT | S_NMCHAR;
         /* fall thru */
      case NOTATION_AFK:
         check_item = ((tgf==DEF_TGF)||(atd_p->defv==CURRENT_AFV))?
            check_list_and_class : check_list;
         break;
      case ENTITY_AFK:
         is_namecase = IS_BITON(GA(opt).ot,ENTITY_CASE_OT) ? TRUE : FALSE;
         break;
      case CDATA_AFK:                    /* nothing to check */
         is_cdata = TRUE;
         is_namecase = FALSE;
         /* fall thru */
      case NMTOKEN_AFK:
         s_first_char = S_LETTER | S_NMSTRT | S_DIGIT | S_NMCHAR;
         break;
      case NUTOKEN_AFK:
         s_first_char = S_DIGIT;
         break;
      case NUMBER_AFK:
         s_first_char = s_other_char = S_DIGIT;
         break;
      case ID_AFK:
         check_item = check_id;
         /* fall thru */
      default:   /* NAME(s)_AFK IDREF(s)_AFK */
         break;
      }
   }
   tkn_p = c_p = d_p = str_p;
   switch(delim_token = GA(curr_token)) {
   case LIT_TK:
   case LITA_TK:
      psh_mode(gbla_p,LITC_MODE);
      for (;;) {
         if ((is_mss_preceed = is_mss_now) == TRUE) {
            is_mss_now = FALSE;
            GA(curr_token) = NOT_FOUND_TK;
         }else {
            dl_find(gbla_p);
         }
         switch(GA(curr_token)) {
         default:                             /* ERO_GRPO */
            /* Cannot occur in start tag (see 9.4.4) */
            errorh(gbla_p,INVDLM,ERH_T_SEV+ERH_LOCATE);
            /* Fall thru: ERH_T makes we'll never reach this line */
            /* or recovery: try to skip the name-group            */
         case ERO_TK:                         /* LITC_MODE only */
            {
               ETY *ety_p;
               if ((ety_p = em_opger(gbla_p)) == (ETY *)0) continue;
               switch (ety_p->kind) {
               case SDATA_EFK:
               case CDATA_EFK:
                  if (!is_plural) attval_nln += NORMSEP; /* 7.9.4.5, a + b */
                  is_mss_hidden = TRUE;
                  hid_mode(gbla_p);
                  break;
               default:
                  /* replace by a break to describe all entities */
                  continue;         /* to describe only S/CDATA entities */
               }
               /*----------------------------------------------------------+
               | To describe more than simply AVL of CDATA att, change the |
               | following if.  Warning: you can't do this for the AVS of  |
               | a default value (at DEF_TGF time) that is a token in a    |
               | group (name token or notation group).  See odtattl.       |
               | Only CDATA AVL are concerned at today (12/11/90)          |
               +----------------------------------------------------------*/
               if (is_cdata) {
                  /*-------------------------------------------------+
                  | If the current text-portion is not empty or was  |
                  | created (when Ee was found), set its length.     |
                  | Start a new current ety_portion and set the ety_p|
                  | in the p field.                                  |
                  +-------------------------------------------------*/
                  if (!*desc_pp) {
                     if (c_p > d_p) {
                        *desc_pp = malloc(3*sizeof(ADLEN));
                        ((*desc_pp)[1])._.p = (void *)0;
                        ((*desc_pp)[1]).len = c_p - d_p;
                        adlen_ix = 2;
                     }else {
                        *desc_pp = malloc(2*sizeof(ADLEN));
                        adlen_ix = 1;
                     }
                  }else {
                    ((*desc_pp)[adlen_ix]).len = c_p - d_p;
                    *desc_pp = realloc(*desc_pp,(1+ ++adlen_ix)*sizeof(ADLEN));
                  }
                  ((*desc_pp)[adlen_ix])._.p = (char *)ety_p;
                  d_p = c_p;
               }
               continue;
            }
         case CRO_TK:
            {
               CHRF chrf = get_chrf(gbla_p);   /* get translated char */
               if (chrf == INVALID_CHRF) {
                  continue;                    /* has been discarded if bad */
               }else if (chrf == NUMERIC_CHRF) {
                  new_char=(uchar)GA(curr_char);
                  if (is_nonsgml(new_char)) {
                     errorh(gbla_p,INVCHAR,ERH_W_SEV+ERH_LOCATE,new_char);
                  }
                  break;                       /* keep it as data */
               }
               /* Named character reference: fall thru */
            }
         case NOT_FOUND_TK:
            if (GA(curr_char) == Ee) {
               rst_mode(gbla_p);
               is_mss_hidden = FALSE;
               if (GA(enty_level) == init_enty_level) {
                  errorh(
                     gbla_p,UBLNLIT,ERH_T_SEV+ERH_LOCATE,
                     delim_token == LIT_TK ?
                     GA(dstab)[LIT_ix].p : GA(dstab)[LITA_ix].p
                  );
               }else {                /* Ignore Ee: */
                  em_close(gbla_p);   /* does em_read in previous file */
               }
               if (*desc_pp) {        /* *must* be true -- just in case... */
                  /*-------------------------------------------------+
                  |Set the length of the current (now ended) portion |
                  |and start a new current portion.                  |
                  |Assume the new portion is text (set the p field   |
                  |of the current portion to NULL).                  |
                  +-------------------------------------------------*/
                  ((*desc_pp)[adlen_ix]).len = c_p - d_p;
                  *desc_pp = realloc(*desc_pp,(1 + ++adlen_ix)*sizeof(ADLEN));
                  ((*desc_pp)[adlen_ix])._.p = (void *)0;
                  d_p = c_p;
               }
               continue;
            }
            if (is_separator(GA(curr_char))) { /* RE, RS, SPACE, SEPCHAR */
               if (GA(curr_char) == GA(RS_char)) {
                  em_read(gbla_p);
                  continue;                    /* ignore  RS */
               }
               if (!is_cdata) {
                  if (c_p > str_p) is_space_waiting = TRUE;
                  em_read(gbla_p);
                  continue;                    /* strip leading space */
               }else {
                  new_char = GA(SPACE_char);   /* do not reduce SPACE seq */
               }
            }else {
               new_char = (uchar)GA(curr_char);
            }
            break;
         case LIT_TK:
         case LITA_TK:
            if ((GA(curr_token)!=delim_token) ||
               (GA(enty_level)!=init_enty_level)) {
               em_unrd(gbla_p,GA(curr_count));
               new_char = (uchar)GA(curr_char);
               break;
            }
            /*--------------------+
            |End of literal found |
            +--------------------*/
            if (!is_invalid_avs) {
               if (!is_cdata) {
                  *c_p = '\0';
                  if (c_p==str_p) {
                     errorh(gbla_p,INVEMPTY,ERH_W_SEV+ERH_LOCATE); /* 7.9.4.1 */
                     is_invalid_avs = TRUE;
                  }else if ((uint)(c_p - tkn_p) > NAMELEN) {
                     errorh(gbla_p,BIGNAME,ERH_W_SEV+ERH_LOCATE);
                     is_invalid_avs = TRUE;
                  }else if ((check_item)&&(!(*check_item)(gbla_p,tkn_p,atd_p))){
                     is_invalid_avs = TRUE;
                  }else if ((is_plural)&&((attval_nln +=NORMSEP) > LITLEN)) {
                     errorh(gbla_p,BIGAVL,ERH_W_SEV+ERH_LOCATE,attval_nln);
                  }
               }
               if (*desc_pp) {
                  /*---------------------------------------------------+
                  | If text was entered in the current portion, (cannot|
                  | be an entity: last Ee created a text portion) set  |
                  | the length of it.  Otherwise, remove the useless   |
                  | entry created by the last Ee.                      |
                  +---------------------------------------------------*/
                  if (c_p > d_p) {
                     ((*desc_pp)[adlen_ix]).len = c_p - d_p;
                  }else {
                     *desc_pp= realloc(*desc_pp,(1 + --adlen_ix)*sizeof(ADLEN));
                  }
                  ((*desc_pp)[0]).len = adlen_ix + 1;
               }
            }
            pop_mode(gbla_p);
            if (!is_invalid_avs) {
               *attval_nln_p = attval_nln;
               return(c_p-str_p);
            }else {
               if (*desc_pp) {
                  free(*desc_pp);
                  *desc_pp = (ADLEN *)0;
               }
               return(-1);
            }
         }
         /*---------------------------+
         |MSO/MSI/MSS CHAR recognition|
         +---------------------------*/
         if ((is_ms_char(new_char))&&(!is_mss_hidden)) {  /* mso, msi, mss */
            if (is_msochar(new_char)) {
               if (!is_mss_preceed) hid_mode(gbla_p);
            }else if (is_msichar(new_char)) {
               rst_mode(gbla_p);
            }else {                      /* mss_char */
               is_mss_now = TRUE;
            }
         }
         if (!is_invalid_avs) {
            /*------------------------------------------------+
            |If a token just ended, make first specific checks|
            +------------------------------------------------*/
            if (is_space_waiting) {      /* never happens if is_cdata */
               is_space_waiting = FALSE;
               *c_p = '\0';
               if (!is_plural) {
                  errorh(gbla_p,BIGTKLIST,ERH_W_SEV+ERH_LOCATE);
                  is_invalid_avs = TRUE;
               }else if ((uint)(c_p - tkn_p) > NAMELEN) {
                  is_invalid_avs = TRUE;
                  errorh(gbla_p,BIGNAME,ERH_W_SEV+ERH_LOCATE);
               }else if ((check_item)&&(!(*check_item)(gbla_p,tkn_p,atd_p))) {
                  is_invalid_avs = TRUE;
               }else {
                  attval_nln += NORMSEP;      /* Check later for overflow */
                  *c_p++ = GA(SPACE_char);    /* enter space anyway */
                  tkn_p = c_p;
               }
            }
            /*---------------------------+
            |Then enter the new character|
            +---------------------------*/
            if (++attval_nln > LITLEN) {
               errorh(gbla_p,BIGAVL,ERH_E_SEV+ERH_LOCATE,attval_nln);
               is_invalid_avs = TRUE;
            }else if (is_cdata) {
               *c_p++ = new_char;
            }else {
               if (!(GA(gtype)[new_char]
                  & ((c_p==tkn_p) ? s_first_char : s_other_char))) {
                  errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE);
                  is_invalid_avs = TRUE;
               }else {
                  *c_p++ = is_namecase ? GA(uctab)[new_char] : new_char;
               }
            }
         }
         em_read(gbla_p);          /* get next_char ready - loop for loop */
      }                            /* end of for loop */
      /* break; */                 /* is never executed ! */
   case NOT_FOUND_TK:
      if ((!IS_BITON(GA(opt).ft,SHORTTAG_FT)&&(tgf!=DEF_TGF)) /* 7.9.3.1 a */
         || (!(GA(gtype)[GA(curr_char)] & s_first_char))) {
         if (!is_invalid_avs) {
            errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE);
            is_invalid_avs = TRUE;
         }
         s_other_char = S_LETTER | S_NMSTRT | S_DIGIT | S_NMCHAR;
      }
      do {
         *c_p++ = is_namecase ? GA(uctab)[GA(curr_char)] : (uchar)GA(curr_char);
      }while (
         (++attval_nln <= LITLEN) && (GA(gtype)[em_read(gbla_p)] & s_other_char)
      );
      if (!is_invalid_avs) {
         if (attval_nln > LITLEN) {
            errorh(gbla_p,BIGAVL,ERH_E_SEV+ERH_LOCATE,attval_nln);
         }else if ((uint)(c_p - str_p) > (is_cdata ? LITLEN : NAMELEN)) {
            errorh(gbla_p,is_cdata ? BIGLIT : BIGNAME,ERH_W_SEV+ERH_LOCATE);
         }else {
            *c_p = '\0';
            if ((!check_item)||((*check_item)(gbla_p,str_p,atd_p))) {
               *attval_nln_p = attval_nln;
               return(c_p-str_p);
            }
         }
      }
      return(-1);
   default:
      if (!is_invalid_avs) errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE);
      em_unrd(gbla_p,GA(curr_count));        /* Not for us ! */
      return(-1);
   }
}

/*--------------------------------------------------------------------tg_stasl-+
|Parse an Attribute Specification List.                                        |
|TAG_MODE (or ASL_NMO) is assumed to be active!                                |
|                                                                              |
|  [31][32] attribute specification list =                                     |
|  (s*, (name, s*, vi, s*)?, attribute value specification)*                   |
|                                                                              |
|When entering this routine:                                                   |
|    - curr_char after the GI + the S separators                               |
|      (or curr_token set, if not NOT_FOUND_TK).                               |
|    Arguments, pointed by arg_p:                                              |
|    - D_atd_p, if not 0, points a copy (Duplicate) of the Model ATD list      |
|      Update this ATD list, by changing the "value" pointers according to the |
|      new specifications and doing malloc for these values.                   |
|    - if D_atd_p is zero, this means the ASL (if any) must be discarded       |
|    - TGF is one of TAG_TGF / DATA_TGF                                        |
|      TAG_TGF: at run time, when encountering a Tag Attribute Specification   |
|      DATA_TGF: at compile time for a Data Attribute Specif (<!ENTITY MD)     |
|    - attval_nln: where to put the sum of of the normalized length            |
|      for each attribute value.                                               |
|                                                                              |
|Returns: One of the ASLRC enum (see tag.h)                                    |
+-----------------------------------------------------------------------------*/
ASLRC P_FCT tg_stasl(GA_P gbla_p, ASLARG *arg_p)
{
   ATD   *atd_p;
   uchar *lit_p;
   int    lit_len;
   ADLEN *desc_p;
   uint   attname_nln, attval_nln, new_attname_nln, new_attval_nln;
   ASLRC  aslrc = NO_CHANGE_ASLRC;

   attname_nln = attval_nln = 0;
   lit_p = GA(lit_p);                               /* at least: LITLEN + 1 */
   if ((!arg_p->D_atd_p)&&(GA(curr_token)==NOT_FOUND_TK)) {
      errorh(gbla_p,INVALS,ERH_W_SEV+ERH_LOCATE);   /* tell: throwing the ASL */
   }
   for (; GA(curr_token) == NOT_FOUND_TK; skip_S(gbla_p)) {
      if (GA(curr_char) == Ee) {
         errorh(gbla_p,UBLNTAG,ERH_T_SEV+ERH_LOCATE);
         break;
      }
      get_nmtk(gbla_p,lit_p);                       /* att_name, or avs ? */
      atd_p = arg_p->D_atd_p;                       /* set atd_p at first ATD */
      skip_S(gbla_p);
      if (GA(curr_token) == VI_TK) {                /* it's an attribute name */
         /*-------------------------------------+
         |Att name found: find if any attribute |
         |name of the ATTLIST could match it    |
         +-------------------------------------*/
         new_attname_nln = (strlen(lit_p) + NORMSEP);
         skip_S(gbla_p);
         if (atd_p) {
            for (;; ++atd_p) {
               if ((!IS_BITON(atd_p->is,LINK_AF)) && /* LINKs are transparent */
                  (!strcmp(lit_p,atd_p->name.p))) { /* Found...*/
                  if IS_BITON(atd_p->is,EXM_AF) {   /* Check if duplicated */
                     errorh(gbla_p,DUPATT,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
                     atd_p = (ATD *)0;              /* get rid of AVS */
                  }
                  break;
               }
               if IS_BITON(atd_p->is,LAST_AF) {     /* doesn't exist */
                  errorh(gbla_p,INVATTDEF,ERH_W_SEV+ERH_LOCATE,lit_p);
                  atd_p = (ATD *)0;                 /* get rid of AVS */
                  break;
               }
            }
         }
         lit_len = tg_gtavs(
            gbla_p, lit_p, atd_p, &desc_p, &new_attval_nln, arg_p->tgf
         );
      }else {                                       /* No VI: */
         em_unrd(gbla_p,GA(curr_count));            /* not for us */
         if (!IS_BITON(GA(opt).ft,SHORTTAG_FT)) {
            errorh(gbla_p,INVATTSPC,ERH_W_SEV+ERH_LOCATE); /* see 7.1.9.2 */
            continue;                               /* loop for next AVS */
         }
         if (!atd_p) continue;                      /* loop for next AVS */
         /*----------------------------------+
         |AVS found without att name, vi:    |
         |Find if any token in any GROUP_AKN |
         |or NOTATION_AKN could match it     |
         +----------------------------------*/
         for (;; ++atd_p) {
            if ((!IS_BITON(atd_p->is,LINK_AF)) &&   /* LINKs are transparent */
               IS_BITON(atd_p->is,LISTED_AF) &&
               (tg_lsfnd(gbla_p,lit_p,atd_p->name.p+strlen(atd_p->name.p)+1))) {
               if IS_BITON(atd_p->is,EXM_AF) {      /* Check if duplicated */
                  errorh(gbla_p,DUPATT,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
                  lit_len = -1;
               }else {
                  SET_BITON(atd_p->is, OMITNAME_AF);
                  new_attname_nln = 0;
                  new_attval_nln = (lit_len = strlen(lit_p));
                  desc_p = (ADLEN *)0;
               }
               break;
            }
            if IS_BITON(atd_p->is,LAST_AF) {        /* doesn't exist */
               errorh(gbla_p,NOATKDEF,ERH_W_SEV+ERH_LOCATE,lit_p);
               lit_len = -1;
               break;
            }
         }
      }
      if (lit_len == -1) continue;                  /* loop for next AVS */
      /*-------------------------------------+
      |Here, the Attribute Spec is valid:    |
      |- atd_p points the ATD (atd_p->name.p)|
      |- lit_len is the length of the AVS    |
      |- lit_p points the AVS                |
      |- desc_p possibly points a descriptor |
      +-------------------------------------*/
      switch (atd_p->defv) {
      case FIXED_AFV:                               /* check it matches */
         {
            boolean is_different = TRUE;
            uint len1;
            uchar *s1_p;
            if IS_BITON(atd_p->is,DESC_AF) {
               len1 = ((ADLEN *)(atd_p->val.desc._.p))->len;
               s1_p = ((ADLEN *)(atd_p->val.desc._.p))->_.p;
            }else {
               len1 = atd_p->val.lit.len;
               s1_p = atd_p->val.lit._.p;
            }
            if (len1==(uint)lit_len) {
               uchar *s2_p=lit_p;
               for (;;) {
                  if (!(len1--)) {
                     is_different = FALSE;
                     break;
                  }
                  if (*s1_p++ != *s2_p++) break;
               }
            }
            if (!is_different) break;
         }
         /*-----------------------------------------------+
         | FIXED and AVS different from the default AVS:  |
         | Copy the default value in lit_p, and the desc  |
         | if the default has one -- this desc will have  |
         | the same native layout as returned by tg_gtavs |
         +-----------------------------------------------*/
         errorh(gbla_p,INVFXATT,ERH_W_SEV+ERH_LOCATE);
         if (desc_p) free(desc_p);
         new_attname_nln = 0;
         new_attval_nln = 0;
         if IS_BITON(atd_p->is,DESC_AF) {           /* default is described */
            uint size = atd_p->val.desc.len * sizeof(ADLEN);
            memcpy(desc_p = malloc(size), atd_p->val.desc._.p, size);
            memcpy(lit_p,desc_p->_.p,1+(lit_len=desc_p->len));
            desc_p->len = atd_p->val.desc.len;
         }else {
            desc_p = (ADLEN *)0;
            memcpy(lit_p,atd_p->val.lit._.p,1+(lit_len=atd_p->val.lit.len));
         }
         break;
      case REQUIRED_AFV:
      case CURRENT_AFV:
         SET_BITOFF(atd_p->is,BAD_AF);              /* Relieve bad assumption */
         break;
      case CONREF_AFV:
         SET_BITON(aslrc, CONREF_CHANGED_ASLRC);
         break;
      default:  /* DEFAULT, IMPLIED */
         break;
      }
      SET_BITON(atd_p->is,EXM_AF);
      SET_BITON(aslrc, CHANGED_ASLRC);
      attname_nln += new_attname_nln;
      attval_nln += new_attval_nln;
      memcpy(lit_p=malloc(lit_len+1), GA(lit_p), lit_len+1); /* +1 for ASCIIZ */
      if (desc_p) {
         SET_BITON(atd_p->is,DESC_AF);
         atd_p->val.desc._.p = desc_p;
         atd_p->val.desc.len = desc_p->len;
         desc_p->_.p = lit_p;
         desc_p->len = lit_len;
      }else {
         SET_BITOFF(atd_p->is,DESC_AF);
         atd_p->val.lit._.p = lit_p;
         atd_p->val.lit.len = lit_len;
      }
      lit_p = GA(lit_p);                            /* reset lit_p */
      /* loop for next AVS */
   }
   if (attval_nln + attname_nln > ATTSPLEN) {
      errorh(gbla_p,BIGAVS,ERH_W_SEV+ERH_LOCATE,attval_nln + attname_nln);
   }
   arg_p->attval_nln = attval_nln;
   return(aslrc);
}

/*--------------------------------------------------------------------tg_parse-+
|This routine is called to parse the rest of start-tag or end-tag.             |
|                                                                              |
|Set caller's pointer to an ATD array that suits the attribute value spec,     |
|or 0 if no attribute value spec (or if model has no attributes).              |
|Return TRUE if a CONREF attribute was assigned, making the content EMPTY.     |
|                                                                              |
|The purpose of this is to store the Attribute Specification in a convenient   |
|form while it is parsed.  It doesn't mean the list is fully valid: more       |
|checking is done later by tg_vldat (REQUIRED for instance).                   |
|                                                                              |
|A copy of the model array is done in ITFCE_POOL pool, then modified           |
|to reflect the current attributes value.                                      |
|However, if there is no attributes or if the model has no attributes,         |
|the copy is eventually freed and callers' pointer is set to (ATD *)0.         |
|                                                                              |
|When entering:                                                                |
|     - curr_char is the next char that follows the GI                         |
|     - "elt_p" points the concerned ELT structure,                            |
|       except it could be (ELT *)0, to tell it's an end tag or invalid GI     |
|     - "atd_pp" points the address where to put the address of the copy       |
|       of the ATD list,                                                       |
|       except it could be (ATD **)0, to get a rid of the attributes           |
|       of a real (but Out of Context) element.                                |
|                                                                              |
|When returning:                                                               |
|     - curr_char is (as usual in most of our routines) the next               |
|       character that is no more concerned by this routine:                   |
|         - either the character the follow a NET or a TAGC                    |
|         - or *AT* the ETAGOs, STAGOs guys.                                   |
|     - however, curr_token has still the last value found: this trick         |
|       allows the NET-enabling start tag recognition                          |
|     - aslrc has the return code                                              |
|                                                                              |
|IMPLEMENTATION NOTE:                                                          |
|  Associated to "tg_vldat", the design tries to minimize areas allocations,   |
|  as the decision to free them belongs to the Interface (the external world). |
|  Two kinds of chunks can be allocated: the ATD array (copy of the model),    |
|  and for each of them (at least one) a "value" string.                       |
|  Note that is.exm == TRUE also means: an allocation has been issued.         |
|  Most part of the time, if the default attributes are used nothing at all    |
|  is allocated (the model itself is passed).                                  |
|  (see also note in tg_vldat)                                                 |
+-----------------------------------------------------------------------------*/
boolean P_FCT tg_parse(
   GA_P gbla_p,
   ELT *elt_p,                     /* zero if end tag */
   ATD **D_atd_pp)                 /* zero if must get rid of the AVS */
{
   int prev_poolid;
   ASLRC aslrc = NO_CHANGE_ASLRC;
   ASLARG aslarg;

   aslarg.tgf = TAG_TGF;
   psh_mode(gbla_p,TAG_MODE);
   skip_S(gbla_p);
   if ((!elt_p) ||                        /* end tag or invalid GI */
      (!elt_p->atd.p) ||                  /* valid GI but no attributes */
      (!D_atd_pp)) {                      /* valid but OOC GI with attributes */
      aslarg.D_atd_p = (ATD *)0;          /* get rid of the ASL */
      tg_stasl(gbla_p,&aslarg);
      if (D_atd_pp) *D_atd_pp = (ATD *)0; /* tell NO attributes were found */
   }else {
      /*-------------------------------------------+
      |Malloc an ATD array in the ITFCE_POOL pool, |
      |copy the model ATD from the ELT structure.  |
      +-------------------------------------------*/
      prev_poolid = pl_accs(gbla_p,ITFCE_POOL); /* malloc/free in this pool */
      pl_clrp(gbla_p);                          /* first clear it */
      memcpy(
         aslarg.D_atd_p = (ATD *)malloc(elt_p->atdsize),
         elt_p->atd.p, elt_p->atdsize
      );
      aslrc=tg_stasl(gbla_p, &aslarg);
      if (aslrc == NO_CHANGE_ASLRC) {     /* No change: no need for a copy */
         free(aslarg.D_atd_p);
         *D_atd_pp = (ATD *)0;
      }else {
         *D_atd_pp = aslarg.D_atd_p;      /* else, the list is built */
      }
      pl_accs(gbla_p,prev_poolid);        /* back to previous pool */
   }
   switch (GA(curr_token)) {
   case TAGC_TK:
   case NET_TK:                           /* a NET-enabling start_tag */
      /*----------------------------------+
      |This is not allowed if SHORTTAG NO,|
      |for empty elements, and for end tag|
      |These errors are caught later      |
      +----------------------------------*/
      break;
   case ETAGO_GRPO_TK:
   case STAGO_GRPO_TK:
   case ETAGO_TK:
   case ETAGO_TAGC_TK:
   case STAGO_TK:
   case STAGO_TAGC_TK:
      em_unrd(gbla_p,GA(curr_count));     /* Could never be recognized: */
      if (IS_BITON(GA(opt).ft,SHORTTAG_FT)) {
         break;                           /* no contextual constraints */
      }                                   /* else, rule 7.4.1 and 7.5.1 */
   default:
      errorh(gbla_p,NOTAGC,ERH_W_SEV+ERH_LOCATE);
      break;
   }
   pop_mode(gbla_p);
   return(IS_BITON(aslrc,CONREF_CHANGED_ASLRC) ? TRUE : FALSE); /*forces EMPTY*/
}

/*STATIC-------------------------------------------------------------valid_ety-+
|Validate entity name(s) in an attribute specification                         |
+-----------------------------------------------------------------------------*/
static uint LP_FCT valid_ety(
   GA_P gbla_p,
   ATD *atd_p)
{
   int prev_poolid;
   uchar *c_p = atd_p->val.lit._.p;
   uchar *d_p, sep;
   ETY  **ety_pp, **ety_pp0, *ety_p;
   uint count = 0;

   if (*c_p) {
      ety_pp = ety_pp0 = malloc(GRPCNT * sizeof(ETY *));
      d_p = c_p - 1;
      prev_poolid = pl_accs(gbla_p,0);      /* malloc/free in main pool */
      for (;;) {
         if (((sep = *++d_p) == GA(SPACE_char))||(sep == '\0')) {
            if (++count <= GRPCNT) {
               *d_p = '\0';
               if ((ety_p=em_fgety(gbla_p,c_p))!=(ETY *)0) {
                  if ((IS_BITON(ety_p->is,RCDATA_EF)) ||
                     (ety_p->kind == PI_EFK)) {   /* 7.9.4.3 */
                     errorh(gbla_p, INVETYATT, ERH_W_SEV+ERH_LOCATE, c_p);
                     ety_p = (ETY *)0;
                  }else {
                     if IS_BITON(ety_p->is,EXTERNAL_EF) {
                        em_find(gbla_p,ety_p->val.ext._.exid.p);
                        if ((ety_p->kind != SUBDOC_EFK)&&
                           (ety_p->val.ext.nad.p)) {           /* valid */
                           em_find(gbla_p,&(ety_p->val.ext.nad.p->ncb.p->exid));
                        }
                     }
                  }
               }
               *(ety_pp++) = ety_p;
            }
            if (sep == '\0') break;
            *d_p = sep;
            c_p = d_p + 1;
         }
      }
      pl_accs(gbla_p,prev_poolid);      /* back to previous pool */
      if (IS_BITON(atd_p->is,EXM_AF)) { /* if atd_p->val.lit_p in ITFCE pool */
         free(atd_p->val.lit._.p);      /* no more needed */
      }
      if (count >= GRPCNT) {
         atd_p->val.ety.pp = ety_pp0;
      }else if (count) {
         atd_p->val.ety.pp = realloc(ety_pp0, count * sizeof(ETY *));
      }else {
         free(ety_pp0);
      }
   }
   atd_p->val.ety.count = (count < GRPCNT) ? count : GRPCNT;
   return(count);
}

/*STATIC-----------------------------------------------------------valid_idref-+
|Validate id reference(s) in an attribute specification                        |
+-----------------------------------------------------------------------------*/
static uint LP_FCT valid_idref(
   GA_P gbla_p,
   ATD *atd_p)
{
   uchar *c_p = atd_p->val.lit._.p;
   uchar *d_p, sep;
   uint count = 0;
   int prev_poolid;

   prev_poolid = pl_accs(gbla_p,0);      /* malloc/free in main pool */
   d_p = c_p - 1;
   for (;;) {
      if (((sep = *++d_p) == GA(SPACE_char))||(sep == '\0')) {
         ++count;
         *d_p = '\0';
         if (*c_p) ehsh_open(GA(id_scope), c_p);
         if (sep == '\0') break;
         *d_p = sep;
         c_p = d_p + 1;
      }
   }
   pl_accs(gbla_p,prev_poolid);          /* back to previous pool */
   return(count);
}

/*STATIC------------------------------------------------------------valid_nota-+
|Validate notations in an attribute specification                              |
+-----------------------------------------------------------------------------*/
static void LP_FCT valid_nota(
   GA_P gbla_p,
   ATD *atd_p)
{
   NCB *ncb_p;
   uchar *c_p, *d_p, c;

   if (IS_BITON(atd_p->is,BAD_AF) || (atd_p->val.lit.len == -1)) {
      ncb_p = (NCB *)0;
   }else {
      c = *(d_p = (c_p = atd_p->val.lit._.p) + atd_p->val.lit.len);
      *d_p = '\0';                       /* Make an ASCIIZ string */
      if ((ncb_p = (NCB *)shsh_fnd(GA(ncb_htab),c_p))==(NCB *)0) {
         errorh(gbla_p,BADNOTA,ERH_W_SEV+ERH_LOCATE,c_p);
      }else {
         em_find(gbla_p,&(ncb_p->exid));
      }
      *d_p = c;                          /* restore original string */
      if (IS_BITON(atd_p->is,EXM_AF)) {  /* if atd_p->val.lit_p in ITFCE pool */
         free(atd_p->val.lit._.p);       /* no more needed */
      }
   }
   atd_p->val.ncb_p = ncb_p;
}

/*------------------------------------------------------------save_new_current-+
| A new CURRENT attribute has been specified: atd_Mp points the model ATD      |
| (still unchanged) model ATD, atd_p points the updated copy.                  |
| The new value has been put on the (vanishing) ITFCE_POOL by tg_stasl.        |
| This new value must be saved in pool #0, after the previous value has been   |
| freed (except in the case of a LIST).  The val.lit.p of the model ATD must   |
| be updated.                                                                  |
+-----------------------------------------------------------------------------*/
static void LP_FCT save_new_current(GA_P gbla_p, ATD *atd_p, ATD *atd_Mp)
{
   uint new_len, old_len, size;
   int prev_poolid;

   if IS_BITON(atd_p->is,DESC_AF) {
      new_len = ((ADLEN *)(atd_p->val.desc._.p))->len;
   }else {
      new_len = atd_p->val.lit.len;
   }
   prev_poolid = pl_accs(gbla_p,0);            /* now in main pool */
   if (IS_BITON(atd_Mp->is,BAD_AF)) {          /* first time */
      SET_BITOFF(atd_Mp->is,BAD_AF);           /* CURRENT now set */
      GA(capcnt)[ATTCHCAP_ix] += new_len;      /* update ATTCH capa */
   }else {
      if IS_BITON(atd_Mp->is,DESC_AF) {        /* old was described */
         old_len = ((ADLEN *)(atd_Mp->val.desc._.p))->len;
         free(((ADLEN *)(atd_Mp->val.desc._.p))->_.p); /* free lit */
         free(atd_Mp->val.desc._.p);                   /* and desc */
      }else {
         old_len = atd_Mp->val.lit.len;
         if (!IS_BITON(atd_Mp->is,LISTED_AF)){ /* not a choice-list: */
            free(atd_Mp->val.lit._.p);         /* free previous */
         }
      }
      if (new_len > old_len) GA(capcnt)[ATTCHCAP_ix] += (new_len - old_len);
   }
   if IS_BITON(atd_p->is,DESC_AF) {            /* new is described */
      SET_BITON(atd_Mp->is,DESC_AF);
      size = (atd_Mp->val.desc.len=atd_p->val.desc.len)*sizeof(ADLEN);
      memcpy(atd_Mp->val.desc._.p = malloc(size), atd_p->val.desc._.p, size);
      memcpy(
         ((ADLEN *)(atd_Mp->val.desc._.p))->_.p = malloc(new_len + 1),
         ((ADLEN *)(atd_p->val.desc._.p))->_.p,
         new_len + 1
      );
   }else {
      SET_BITOFF(atd_Mp->is,DESC_AF);
      if (!IS_BITON(atd_Mp->is,LISTED_AF)) {   /* not a choice-list: */
         memcpy(
            atd_Mp->val.lit._.p = malloc(new_len + 1),
            atd_p->val.lit._.p,
            (atd_Mp->val.lit.len = new_len) + 1
         );
      }else {
         atd_Mp->val.lit.len = new_len;
      }
   }
   pl_accs(gbla_p,prev_poolid);                 /* back to prev pool */
}

/*--------------------------------------------------------------------tg_vldat-+
|Validate attributes for elt_p;                                                |
|D_atd_p (if non null) is a copy that has been done by tg_parse (for an STG_U).|
|Returns a pointer to an ATD array, either the original one in ELT, or a copy. |
|                                                                              |
|Copies are done iff they are needed:                                          |
|   - either specific attributes were specified, (STG_U + attributes)          |
|   - or current attributes are in the list                                    |
|                                                                              |
|REQUIRED is flagged 'bad' on the original ATD.                                |
|CURRENT is flagged 'bad' on the original ATD, until one is specified.         |
|                                                                              |
|IMPLEMENTATION NOTE 1:                                                        |
|   Starting with 1.31, when a tag notification is issued, storage for         |
|   the DED, the ATD chain, and the values those structures point to are       |
|   reused after the Application returns from the Parser Notification call.    |
|   It is up to the Application to put these values in a safe place,           |
|   if it requires it.                                                         |
|   Not all ATD blocks and values require to be saved: for instance,           |
|   storage for default attribute values is never overwritten.  The following  |
|   is a detailled algorithm for the application to know when it has to put    |
|   chunks in a safe place:                                                    |
|                                                                              |
|   - for an ATD array, if one of the is.exm bit is TRUE                       |
|     OR if one of the attribute is NOTATION or ENTITY(IES)                    |
|     the ATD array has been allocated (on the ITFCE pool).                    |
|                                                                              |
|   - for a given ATD:  (this code not fully tested... )                       |
|                                                                              |
|     if ((atd_p->kind & ~1) == ENTITY_AFK) {    -* ENTITY or ENTITIES *-      |
|        if (atd_p->val.ety.count) keep_safe(atd_p->val.ety.pp);               |
|     }else if (atd_p->kind != NOTATION_AFK) {                                 |
|        if ((IS_BITON(atd_p->is,EXM_AF)) ||                                   |
|           -* CURRENT attributes always need to be kept (even when they   *-  |
|           -* belong to POOL #0): their values can vary during the parse. *-  |
|           ((atd_p->defv == CURRENT_AFV) && (!IS_BITON(atd_p->is,BAD_AF)))) { |
|           if IS_BITON(atd_p->is,DESC_AF) {                                   |
|              keep_safe((atd_p->val.desc._.p)->_.p);                          |
|              keep_safe(atd_p->val.desc._.p);                                 |
|           }else {                                                            |
|              keep_safe(atd.val.lit._.p);                                     |
|     }  }  }                                                                  |
|                                                                              |
| If a CONREF attribute has been specified, elt_p->is_empty is                 |
| artificially (and temporary) forced to TRUE (see comments in stw_otcb).      |
+-----------------------------------------------------------------------------*/
ATD * P_FCT tg_vldat(GA_P gbla_p, ELT *elt_p, ATD *D_atd_p)
{
   int prev_poolid;
   ATD *atd_p;
   boolean att_must_be_spec, working_on_a_copy;
   int nbr_of_ety_names, nbr_of_idref_names;

   if (!(elt_p->atd.p)) return((ATD *)0);

   att_must_be_spec = IS_BITON(GA(opt).ft,SHORTTAG_FT|OMITTAG_FT)? FALSE : TRUE;
   nbr_of_ety_names = 0;
   nbr_of_idref_names = 0;
   if ((!D_atd_p) && (!elt_p->is.entity) && (!elt_p->is.notation)) {
      working_on_a_copy = FALSE;                /* No copy done, none must be */
      atd_p = elt_p->atd.p;                     /* atd_p has the original */
   }else {                      /* copy done, or no copy done but one must be */
      working_on_a_copy = TRUE;
      prev_poolid = pl_accs(gbla_p,ITFCE_POOL); /* malloc/free in this pool */
      if (!D_atd_p) {                           /* no copy done, one must be */
         D_atd_p=(ATD *)malloc(elt_p->atdsize);
         memcpy(D_atd_p,elt_p->atd.p,elt_p->atdsize);
      }
      atd_p = D_atd_p;                          /* atd_p has the copy */
   }
   for (;;) {
      if (!IS_BITON(atd_p->is,LINK_AF)) {       /* LINKs are transparent */
         switch(atd_p->defv) {
         case REQUIRED_AFV:
            if (!IS_BITON(atd_p->is,EXM_AF)) {  /* value not explicitely set */
               errorh(gbla_p,NORQDATT,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
            }
            break;
         case CONREF_AFV:                       /* CONREF is impliable */
         case IMPLIED_AFV:
            break;                              /* IMPLIED needs no specif */
         case CURRENT_AFV:
            if (IS_BITON(atd_p->is,BAD_AF)) {
               errorh(gbla_p,NOCURATT,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
               break;
            }
            if (IS_BITON(atd_p->is,EXM_AF)) {   /* New value set */
               save_new_current(gbla_p, atd_p, elt_p->atd.p + (atd_p-D_atd_p));
               break;
            }
            /* Else, fall thru; */
         default:
            if ((!IS_BITON(atd_p->is,EXM_AF)) && (att_must_be_spec)) {
               errorh(gbla_p,NOATTSPC,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
            }
            break;
         }
         switch (atd_p->kind) {                 /* kind */
         case ENTITIES_AFK:
         case ENTITY_AFK:
            nbr_of_ety_names += valid_ety(gbla_p,atd_p);
            break;
         case IDREFS_AFK:
         case IDREF_AFK:
            nbr_of_idref_names += valid_idref(gbla_p,atd_p);
            break;
         case ID_AFK:
            if (IS_BITON(atd_p->is,EXM_AF)) ++GA(capcnt)[IDCAP_ix];
            break;
         case NOTATION_AFK:
            valid_nota(gbla_p,atd_p);
            if ((elt_p->is.empty) /* i.e., there is an explicit content ref */
               && (atd_p->val.ncb_p)) { /* and a NOTATION attribute was set */
               errorh(gbla_p,INVNOTASP,ERH_W_SEV+ERH_LOCATE);
            }
            break;
         default:
            break;
         }
      }
      if IS_BITON(atd_p->is,LAST_AF) break;
      ++atd_p;
   }
   GA(capcnt)[IDREFCAP_ix] += nbr_of_idref_names;
   if ((uint)nbr_of_idref_names > GRPCNT) {
      errorh(gbla_p,BIGASIDR,ERH_W_SEV+ERH_LOCATE,nbr_of_idref_names);
   }
   if ((uint)nbr_of_ety_names > GRPCNT) {
      errorh(gbla_p,BIGASETY,ERH_W_SEV+ERH_LOCATE,nbr_of_ety_names);
   }
   if (!working_on_a_copy) {
      return(elt_p->atd.p);                  /* get the model */
   }else {
      pl_accs(gbla_p,prev_poolid);           /* back to previous pool */
      return(D_atd_p);                       /* get the copy */
   }
}
/******************************************************************************/
