/*******************************************************************************
* YORKTOWN ADVANCED SGML PARSER (YASP)                                         *
*         An SGML Parser conforming to International Standard ISO 8879 --      *
*         Standard Generalized Markup Language                                 *
*------------------------------------------------------------------------------*
* Written 1987 - 1993 by                                                       *
*         Pierre G. Richard                                                    *
*         IBM T.J. Watson Research Center                                      *
*         Yorktown Heights, New York                                           *
*         petrus@watson.ibm.com                                                *
*------------------------------------------------------------------------------*
* FUNCTION NAME    = YASP / DTD compiler                                       *
* STATUS           = Version 0, Release 1, Level 36 and up                     *
* COMPONENT NAME   = odtenty.c                                                 *
* DESCRIPTIVE NAME = ENTITY declaration / build the ETY array                  *
* DATE_WR          = 04/10/1988                                                *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
*                                                                              *
* IMPLEMENTATION NOTE =                                                        *
*                                                                              *
* DEPENDENCIES =  (see Include list)                                           *
*                                                                              *
* IMPORT=                                                                      *
*                                                                              *
* EXPORT=                                                                      *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & Reason (brief)                             *
*      1 06/06/88 PGR      First draft                                         *
*      2 12/27/88 PGR      Notation: data attribute specifications             *
*      3 11/15/89 PGR      Fixed some bugs in data attribute spec              *
*      4 11/22/90 PGR      Empty Attribute Spec List not allowed (11.4.1.2)    *
*      5 12/14/90 PGR      Added desc to isolate S/CDATA ent in CDATA attrib   *
*      6 06/27/92 PETRUS   ASL_MODE instead of TAG_MODE                        *
*      7 04/01/97 ESPERT   Added ENTITY declaration reporting                  *
*******************************************************************************/

/*-------------+
|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"
#include "odt.h"
#include "teller.h"

/*--------------+
|Local Functions|
+--------------*/
static CHOICES const * _loadds LP_FCT adr_etyk_choices(void);
static CHOICES const * _loadds LP_FCT adr_xtyk_choices(void);
static uint LP_FCT parse_data_att_spec (COMMON_P, ANHDR *, ATD **);
static int  LP_FCT fill_nad       (COMMON_P, NAD *);

/*STATIC---------------------------------------------------parse_data_att_spec-+
|Parse a data attribute specification  (N/C/S DATA external entity),           |
|                                                                              |
|On ENTRY:                                                                     |
|  - curr_token == DSO,                                                        |
|  - anhdr_p is pointing the ATD list or is zero if we must get rid of the ASL,|
|  - D_atd_pp is where to put the address of the new ATD List (irrelevant if   |
|    anhdr_p is 0).                                                            |
|                                                                              |
|This is almost equivalent to "tg_parse", except we are working with           |
|an "unstringized" ATD list: store the Attribute Specification in a convenient |
|form while it is parsed.  The "full checking" of the list is done later.      |
|                                                                              |
|  NDATA, ps+, notation name,  ps+,                                            |
|        (ps+, dso, attribute specification list, ps*, dsc)?                   |
+-----------------------------------------------------------------------------*/
static uint LP_FCT parse_data_att_spec(
   COMMON_P M_p,
   ANHDR *anhdr_p,
   ATD **D_atd_pp)
{
   GA_P gbla_p = M_p->gbla_p;
   ANODE *anode_p, *anode_pF;
   ATD   *atd_p;
   uint  nbr_of_attributes = 0;
   uint  nbr_of_attdesc = 0;
   ASLARG aslarg;
   boolean att_must_be_spec, must_keep_asl;
   OFS *ofs_tab, *ofs_p;              /* To save the offsets in desc_p */

   aslarg.tgf = DATA_TGF;
   psh_mode(gbla_p,ASL_MODE);         /* Att Spec List starting with DSO */
   skip_S(gbla_p);
   if (GA(curr_token) == DSC_TK) {
      errorh(gbla_p,INVEMPASL,ERH_W_SEV+ERH_LOCATE);  /* 11.4.1.2 last para */
   }
   if (!anhdr_p) {                    /* Invalid: */
      aslarg.D_atd_p = (ATD *)0;      /* get rid of the ASL */
      tg_stasl(gbla_p,&aslarg);
   }else {
      /*-------------------------------------------------+
      |Make a copy of the current ATD list in an array.  |
      |Regain the address of the parked literal:         |
      | - either directly: val.lit._.p, or               |
      | - or, if DESC, val.desc._.p->p and save the ofs  |
      +-------------------------------------------------*/
      ofs_p = (ofs_tab = malloc(ATTCNT * sizeof(OFS))) - 1;
      atd_p = aslarg.D_atd_p = (ATD *)malloc(anhdr_p->atdsize);
      anode_p = anode_pF = anhdr_p->u.anode_p;
      do {
         *atd_p = anode_p->atd;             /* copy */
         atd_p->name.p = sk_point(gbla_p,anode_p->atd.name.ofs);
         if IS_BITON(atd_p->is,DESC_AF) {
            ADLEN *desc_p = atd_p->val.desc._.p;
            desc_p->_.p = sk_point(gbla_p, *(++ofs_p) = desc_p->_.ofs);
         }else {
            atd_p->val.lit._.p = sk_point(gbla_p,atd_p->val.lit._.ofs);
         }
         ++atd_p;
      }while ((anode_p = anode_p->next_p) != anode_pF);

      /*------------------------------------------+
      |Now parse the Attribute Specification List.|
      +------------------------------------------*/
      att_must_be_spec =
         IS_BITON(GA(opt).ft, SHORTTAG_FT | OMITTAG_FT) ? FALSE : TRUE;
      must_keep_asl =         /* Can only be NO_CHANGE or CHANGED */
         (tg_stasl(gbla_p, &aslarg) == NO_CHANGE_ASLRC) ? FALSE : TRUE;
      anode_p = anode_pF;
      atd_p = aslarg.D_atd_p;
      ofs_p = ofs_tab - 1;
      do {                            /* unlocate and keep new specs */
         if IS_BITON(anode_p->atd.is,DESC_AF) {     /* restore ofs */
            ((ADLEN *)(anode_p->atd.val.desc._.p))->_.ofs = *(++ofs_p);
         }
         if IS_BITON(atd_p->is,EXM_AF) {
            uchar *c_p;
            ADLEN *adl_p;
            /*------------------------------------------------+
            |EXM_AF is TRUE only if tg_stasl returned CHANGED |
            |making the copy of the asl will be kept.         |
            +------------------------------------------------*/
            if IS_BITON(atd_p->is,DESC_AF) {   /* is now described */
               nbr_of_attdesc += atd_p->val.desc.len;
               adl_p = atd_p->val.desc._.p;
            }else {
               adl_p = &(atd_p->val.lit);
            }
            c_p = adl_p->_.p;                  /* save lit pointer */
            adl_p->_.p = (void *)0;            /* to differenciate headers */
            adl_p->_.ofs = (adl_p->len > 0) ?
              sk_park(gbla_p, c_p, adl_p->len +1) : 0; /* +1: ASCIIZ */
            free(c_p);
         }else {                               /* attrib was not specified */
            switch(atd_p->defv) {
            case REQUIRED_AFV:
               errorh(gbla_p,NORQDATT,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
               break;
            case IMPLIED_AFV:
               break;                          /* IMPLIED needs no specif */
            default:
               if (att_must_be_spec) {
                  errorh(gbla_p,NOATTSPC,ERH_W_SEV+ERH_LOCATE,atd_p->name.p);
               }
            }
            if (!must_keep_asl) continue;              /* No needs to pursue */
            if (!IS_BITON(atd_p->is,DESC_AF)) {        /* not described */
               atd_p->val.lit._.ofs = anode_p->atd.val.lit._.ofs;
            }
         }
         atd_p->name.ofs = anode_p->atd.name.ofs;      /* reset offset */
         ++nbr_of_attributes;
         ++atd_p;
      }while ((anode_p = anode_p->next_p) != anode_pF);

      if (!must_keep_asl) {                            /* no change done */
         free(aslarg.D_atd_p);                         /* free */
         *D_atd_pp = (ATD *)0;
      }else {
         *D_atd_pp = aslarg.D_atd_p;                   /* else, list built */
         M_p->nbr_of_attributes += nbr_of_attributes;  /* more for atdtab */
         M_p->nbr_of_attdesc += nbr_of_attdesc;        /* and descriptor */
         M_p->nbr_of_dclv_text += aslarg.attval_nln;
      }
      free(ofs_tab);
   }
   pop_mode(gbla_p);
   if (GA(curr_token) != DSC_TK) errorh(gbla_p,NODSC,ERH_W_SEV+ERH_LOCATE);
   return(nbr_of_attributes);
}

/*STATIC------------------------------------------------------adr_etyk_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_etyk_choices(void)
{
   static CHOICES const etyk_choices[] = {
      { offsetof(GNWDTAB,SDATA),    SDATA_EFK    },
      { offsetof(GNWDTAB,CDATA),    CDATA_EFK    },
      { offsetof(GNWDTAB,PI),       PI_EFK       },
      { offsetof(GNWDTAB,STARTTAG), STARTTAG_EFK },
      { offsetof(GNWDTAB,ENDTAG),   ENDTAG_EFK   },
      { offsetof(GNWDTAB,MS),       MS_EFK       },
      { offsetof(GNWDTAB,MD),       MD_EFK       },
      { -1,                         SGML_EFK     }  /* Assume so */
   };
   return(etyk_choices);
}

/*STATIC------------------------------------------------------adr_xtyk_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_xtyk_choices(void)
{
   static CHOICES const xtyk_choices[] = {
      { offsetof(GNWDTAB,SUBDOC),   SUBDOC_EFK   },
      { offsetof(GNWDTAB,NDATA),    NDATA_EFK    },
      { offsetof(GNWDTAB,CDATA),    CDATA_EFK    }, /* Xternal CDATA */
      { offsetof(GNWDTAB,SDATA),    SDATA_EFK    }, /* Xternal SDATA */
      { -1,                         INVALID_EFK  }  /* definitively invalid */
   };
   return(xtyk_choices);
}

/*--------------------------------------------------------------------ety_deco-+
|Install a new entity                                                          |
|                                                                              |
|Enter here when ENTITY has been decoded,                                      |
|Returns with curr_char (supposed to ) == MDC                                  |
|                                                                              |
|[101] entity declaration = mdo, "ENTITY",                                     |
|          ps+, entity name, ps+, entity text, ps*, mdc                        |
|                                                                              |
|[102] entity name = general entity name | parameter entity name               |
|[103] general entity name = name | (rni,"DEFAULT")                            |
|[104] parameter entity name = pero, ps+, name                                 |
|                                                                              |
|[105] entity text = parameter literal | data text | bracketed text |          |
|                    external entity specification                             |
|                                                                              |
|[106] data text = ("CDATA"|"SDATA"|"PI"), ps+, parameter literal              |
|[107] bracketed text = ("STARTAG"|"ENDTAG"|"MS"|"MD"), ps+, parameter literal |
|                                                                              |
|[108] external entity specification = external identifier, (ps+, entity type)?|
|[109] entity type = "SUBDOC" |                                                |
|                    ( ("NDATA" | "CDATA" | " SDATA"),                         |
|                       ps+, notation name, data attribute specification ?)    |
|[149.2] data attribute specification =                                        |
|                  ps+, dso, attribute specification list, ps*, dsc            |
|                                                                              |
|IMPLEMENTATION NOTE:                                                          |
|  The default entity has a null name (a name with a length of zero).          |
|  Because of the hash table structure, this ensures the default entity        |
|  will be the first when ehsh_traversing the hash table.  Because all         |
|  other no-named -> defaulted entities occur after, the algorithm             |
|  will be able to refer to the already solved default entity literal          |
|  or exid, avoiding duplication of the literal or exid.                       |
|  This is compatible with paragraph 2 of 10.5.1.2 (capacities are counted     |
|  once towards ENTCHCAP for the default entity only -- not for the            |
|  entities defaulted).                                                        |
|                                                                              |
|  The NAD used here is temporarily "extended" with the count of attributes,   |
|  followed by the notation name.                                              |
|  This is made clear by the following "#defines".                             |
+-----------------------------------------------------------------------------*/
#define ATD_COUNT(nad_p)   *((uint *)((uchar *)nad_p + sizeof(NAD)))
#define NOTA_NAME_P(nad_p)  (uchar *)nad_p + (sizeof(NAD)+sizeof(uint))
#define XNAD_MAXSIZE        sizeof(NAD)+sizeof(uint)+NAMELEN+1
void P_FCT ety_deco(COMMON_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ETY  *ety_p;
   EXID *exid_p;
   EHSH *ehsh_p;
   NB *nb_p;
   ANHDR *anhdr_p;
   NAD *nad_p;
   uint kind, bracket_len;
   boolean is_general, is_external;
   void *scope;
   uchar name[NAMELEN_MAX+1], *nota_name_p;

   ety_p=(ETY *)malloc(sizeof(ETY));
   ety_p->is = 0;                             /* reset all fields */
   ety_p->kind = 0;

   tll_ETDCST(gbla_p); /* Tell the entity decl start */
   /*-------------------+
   |Decode entity name  |
   +-------------------*/
   skip_ps(gbla_p);
   if (GA(curr_token) == RNI_TK) {
      chk_gnam(gbla_p,GA(gnwdtab).DEFAULT.s.p);
      name[0] = '\0';
      is_general = TRUE;
      scope = GA(gety_scope);
   }else {
      if (GA(curr_token)!=NOT_FOUND_TK) {
         errorh(gbla_p,NONAME,ERH_T_SEV+ERH_LOCATE);
      }
      if (get_gstr(gbla_p,GA(dstab)[PERO_ix].p)) {    /* PERO found */
         is_general = FALSE;
         scope = GA(pety_scope);
         skip_Wps(gbla_p);
         if (GA(curr_token)!=NOT_FOUND_TK) {
            errorh(gbla_p,NONAME,ERH_T_SEV+ERH_LOCATE);
         }
         get_enam(gbla_p,name);              /* get the name */
         if ((strlen(name) + strlen(GA(dstab)[PERO_ix].p)) > NAMELEN) {
            errorh(gbla_p,BIGNAMEP,ERH_W_SEV+ERH_LOCATE);
         }
      }else {
         is_general = TRUE;
         scope = GA(gety_scope);
         get_enam(gbla_p,name);              /* get the name */
      }
   }
   ehsh_p = ehsh_open(scope,name);           /* check later if duplicate */

   /*--------------------------------+
   |Find out what kind of entity text|
   | Search for:                     |
   | - a LIT/LITA (parm lit),        |
   | - an etyk_choice (data, bracket)|
   | - SGML_EFK => external entity   |
   +--------------------------------*/
   skip_ps(gbla_p);                               /* may be a lit/lita ? */
   if (GA(curr_token) != NOT_FOUND_TK) {          /* assume lit TK found... */
      kind = TEXT_EFK;
   }else {                                        /* else, keyword ? */
      kind = choos_in(gbla_p,adr_etyk_choices(),(GNWD *)&GA(gnwdtab));
   }
   if (kind != SGML_EFK) {
      bracket_len = 0;
      is_external = FALSE;
      /*--------------------------------+
      |Memory Entity:                   |
      |- decode the qualifier (if any), |
      |- get the parameter literal      |
      +--------------------------------*/
      if (kind == TEXT_EFK) {
         SET_BITON(ety_p->is,RCDATA_EF);
      }else {
         switch (kind) {
         case CDATA_EFK:
         case SDATA_EFK:
            if (!is_general) {
               errorh(gbla_p,INVPETKW,ERH_W_SEV+ERH_LOCATE);
               kind = TEXT_EFK;
               SET_BITON(ety_p->is,RCDATA_EF);
            }
            break;
         case PI_EFK:
            SET_BITON(ety_p->is,BRACKETED_EF);  /* not bracketed for ISO */
            break;                              /* (no bracket_len) */
         case STARTTAG_EFK:
            SET_BITON(ety_p->is,BRACKETED_EF | RCDATA_EF);
            bracket_len =
            strlen(GA(dstab)[STAGO_ix].p) + strlen(GA(dstab)[TAGC_ix].p);
            break;
         case ENDTAG_EFK:
            SET_BITON(ety_p->is,BRACKETED_EF | RCDATA_EF);
            bracket_len =
            strlen(GA(dstab)[ETAGO_ix].p) + strlen(GA(dstab)[TAGC_ix].p);
            break;
         case MS_EFK:
            SET_BITON(ety_p->is,BRACKETED_EF | RCDATA_EF);
            bracket_len =
            strlen(GA(dstab)[MDO_DSO_ix].p) + strlen(GA(dstab)[MSC_MDC_ix].p);
            break;
         case MD_EFK:
            SET_BITON(ety_p->is,BRACKETED_EF | RCDATA_EF);
            bracket_len =
            strlen(GA(dstab)[MDO_ix].p) + strlen(GA(dstab)[MDC_ix].p);
            break;
         }
         skip_Wps(gbla_p);
      }
      ety_p->val.lit.len = get_plit(gbla_p, GA(lit_p), FALSE);
      if ((IS_BITON(ety_p->is,BRACKETED_EF)) &&
         ((uint)(ety_p->val.lit.len + bracket_len) > LITLEN)) {
         errorh(gbla_p,BIGLITBK,ERH_W_SEV+ERH_LOCATE);
      }
      memcpy(
         ety_p->val.lit._.p = malloc(ety_p->val.lit.len),
         GA(lit_p), ety_p->val.lit.len
      );
      skip_ps(gbla_p);
   }else {                                          /* kind == SGML */
      SET_BITON(ety_p->is,EXTERNAL_EF | RCDATA_EF); /* RCDATA ON (default) */
      is_external = TRUE;
      /*-----------------------------------------+
      |External entity: get external identifier. |
      +-----------------------------------------*/
      exid_p = ety_p->val.ext._.exid.p = (EXID *)malloc(sizeof(EXID));
      xid_fill(gbla_p,exid_p, 0);                 /* does skip_ps() */
      if (!(exid_p->name.len = strlen(exid_p->name._.p = ehsh_p->name_p))) {
         SET_BITON(exid_p->is,DEFLT_XF);          /* Default Entity */
      }
      if (GA(curr_token) != NOT_FOUND_TK) {       /* assume MDC found */
         xid_cchk(gbla_p,exid_p, TEXT_XFC);
      }else {
         kind = choos_in(gbla_p,adr_xtyk_choices(),(GNWD *)&GA(gnwdtab));
         switch (kind) {
         case INVALID_EFK:
            errorh(gbla_p,INVKWD,ERH_W_SEV+ERH_LOCATE);
            kind = SGML_EFK;
            exid_p->asgn_cls = TEXT_XFC;
            break;
         case SUBDOC_EFK:
            if ((!is_general)||(!IS_BITON(GA(opt).ft,SUBDOC_FT))) {
               errorh(gbla_p,INVKWD,ERH_W_SEV+ERH_LOCATE);
               kind = SGML_EFK;
               exid_p->asgn_cls = TEXT_XFC;
            }else {
               xid_cchk(gbla_p,exid_p, SUBDOC_XFC);
               SET_BITOFF(ety_p->is,RCDATA_EF);
            }
            skip_ps(gbla_p);
            break;
         default:                                 /* NDATA, CDATA, SDATA */
            if (!is_general) {                    /* if parameter entity */
               errorh(gbla_p,INVPETKW,ERH_W_SEV+ERH_LOCATE);
               kind = SGML_EFK;
               exid_p->asgn_cls = TEXT_XFC;
            }else {
               xid_cchk(gbla_p,exid_p, NONSGML_XFC);
            }
            /* get one area for NAD + ATD count + Notation Name */
            ety_p->val.ext.nad.p = nad_p = (NAD *)malloc(XNAD_MAXSIZE);
            nota_name_p = NOTA_NAME_P(nad_p);
            skip_ps(gbla_p);
            get_name(gbla_p,nota_name_p);         /* get notation name */
            skip_ps(gbla_p);
            if ((ehsh_p->var_p)||(!is_general)) { /* if entity duplicated */
               if (GA(curr_token) == DSO_TK) {    /* throw any ASL */
                  parse_data_att_spec(M_p,(ANHDR *)0,(ATD **)0);
                  skip_ps(gbla_p);                /* else assume MDC found */
               }
               free(nad_p);                       /* discard nad */
               break;                             /* and leave */
            }
            SET_BITON(ety_p->is,NOTATION_EF);
            SET_BITOFF(ety_p->is,RCDATA_EF);
            ++M_p->nbr_of_nad;
            realloc(                              /* shrink to exact length */
               (uchar *)nad_p, XNAD_MAXSIZE - (NAMELEN-strlen(nota_name_p))
            );
            if (GA(curr_token) == DSO_TK) {
               /*-------------------------------------------------+
               |Valid entity, and ASL given:                      |
               |If an Attribute Definition exists, parse the ASL: |
               |  - put the new ATD list in ety_p->nad_p->atd_p   |
               |  - temporarily store # of ATD in ATD extension   |
               |Else, get rid of ASL, issue an Warning            |
               +-------------------------------------------------*/
               if ((!(nb_p = (NB *)ehsh_fnd(M_p->nota_scope,nota_name_p))) ||
                  (!(anhdr_p = nb_p->u.anhdr_p))) {
                  errorh(gbla_p,NOATTDEF,ERH_W_SEV+ERH_LOCATE);
                  anhdr_p = (ANHDR *)0;           /* ASL will be discarded */
               }
               ATD_COUNT(nad_p)=parse_data_att_spec(M_p,anhdr_p,&nad_p->atd.p);
               skip_ps(gbla_p);
            }else {
               ATD_COUNT(nad_p) = 0;
            }
         }
      }
   }
   ety_p->kind = (uchar)kind;                     /* set the kind */
   /*--------------------------------------+
   |Enter the new entity in the hash tables|
   +--------------------------------------*/
   if (!ehsh_p->var_p) {                          /* brand new: */
      if (is_external) {
         ++GA(nbr_of_exid);
         M_p->nbr_of_exid_text += (exid_p->sid.len + exid_p->pid.len);
      }
      if (is_general) {
         ++GA(nbr_of_g_entities);
         if (!is_external) {
            M_p->nbr_of_g_text += (ety_p->val.lit.len + bracket_len);
         }
      }else {
         SET_BITON(ety_p->is,PARAMETER_EF);
         ++M_p->nbr_of_p_entities;
         if (!is_external) {
            M_p->nbr_of_p_text += (ety_p->val.lit.len + bracket_len);
         }
      }
      ehsh_p->var_p = (void *)ety_p;              /* keep it */
      ety_p->name.len = strlen(ety_p->name._.p = ehsh_p->name_p);
      tll_ETDCND(gbla_p, ety_p);
   }else {                                        /* duplicate: */
      ety_p->name.len = strlen(ety_p->name._.p = ehsh_p->name_p);
      if (!is_general)
         SET_BITON(ety_p->is,PARAMETER_EF);
      tll_ETDCND(gbla_p, ety_p);
      errorh(gbla_p,DUPNAMETY,ERH_I_SEV+ERH_LOCATE); /* it's an error */
      if (is_external) {
         free(exid_p->val._.p);
         free(exid_p);
      }else {
         free(ety_p->val.lit._.p);
      }
      free(ety_p);
   }
}

/*--------------------------------------------------------------------fill_nad-+
|Solve the NAD and fill the current entry (M_p->nad_p) in the NAD table.       |
|Return the index of the ATD list (or -1 if invalid).                          |
|                                                                              |
|The ODT file will have the ETY block with Notation fully solved:              |
|      - ADLEN of the SID of the notation (really: OFSLEN)                     |
|      - ATD list pointer (really: offset in the ATD Table)                    |
|                                                                              |
|NOTE: nta_bld must have been issued !                                         |
|                                                                              |
|Even if no notation, an ATTLIST can still have been defined.                  |
|Too much code not to build the ATD; however: discard it.                      |
+-----------------------------------------------------------------------------*/
static int LP_FCT fill_nad(COMMON_P M_p, NAD *nad_p)
{
   GA_P gbla_p = M_p->gbla_p;
   NB   *nb_p;
   uint i;
   int ix;

   /*---------------------------------------+
   |Get in nb_p the pointer to the NB block,|
   |or 0 if no NOTATION MD declared         |
   +---------------------------------------*/
   nb_p = ehsh_fnd(M_p->nota_scope, NOTA_NAME_P(nad_p));
   if ((i=ATD_COUNT(nad_p)) > 0) {
      /*---------------------------------+
      |If data attributes were associated|
      |move them in the ATD Table        |
      +---------------------------------*/
      ATD *atd_p = nad_p->atd.p;
      uint nad_ix = diffptr(M_p->atd_p, M_p->atd_orig);
      do odt_uatd(M_p, atd_p++); while (--i);  /* save the array of atd's */
      free(nad_p->atd.p);                 /* and free it */
      nad_p->atd.ix = nad_ix;
   }else if (nb_p) {
      nad_p->atd.ix = nb_p->u.atd_ix;     /* take default attributes */
   }else {
      nad_p->atd.ix = -1;
   }
   if (!nb_p) {                           /* No notation associated */
      nad_p->ncb.ix = -1;
      nad_p->atd.ix = -1;                 /* No notation: no attributes */
      ix = -1;
   }else {
      nad_p->ncb.ix = nb_p->v.ncb_ix;
      ix = diffptr(M_p->nad_p, M_p->nad_orig);
   }
   *M_p->nad_p = *nad_p;                  /* copy nad */
   free(nad_p);                           /* free NAD + count + notation name */
   ++M_p->nad_p;
   return(ix);
}

/*---------------------------------------------------------------------ety_bld-+
|Build entities table from the entity structure in core.                       |
|                                                                              |
|IMPLEMENTATION NOTE: it's assumed the default entity (if any) will come       |
|first -- see ety_deco implementation note                                     |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
void P_FCT ety_bld(void STACK_BASED * XM_p, EHSH *ehsh_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ETY  *ety_pS, *ety_pT;      /* S as in Source, T as in Target */
   EXID *exid_pS, *exid_pT;

   ety_pS =  (ETY *)ehsh_p->var_p;
   ety_pT = M_p->ety_p++;
   ety_pT->is = ety_pS->is;
   ety_pT->kind = ety_pS->kind;
   ety_pT->name._.ofs = sk_park(gbla_p,ety_pS->name._.p,1+(
   ety_pT->name.len = ety_pS->name.len));
   if IS_BITON(ety_pS->is,EXTERNAL_EF) {
      exid_pS = ety_pS->val.ext._.exid.p;
      SET_BITOFF(exid_pS->is,SOLVED_XF | INVALID_XF); /* reset bits */
      exid_pT = M_p->exid_p++;
      ety_pT->val.ext._.exid.ix = diffptr(exid_pT, M_p->exid_orig);
      *exid_pT = *exid_pS;                     /* copy the exid */
      exid_pT->name._.ofs = ety_pT->name._.ofs;
      if (IS_BITON(ety_pS->is,DEFLT_EF)) {
         exid_pT->val._.ofs = M_p->dflety_exid_val_ofs;
         ety_pT->val.ext.nad.ix = M_p->dflety_val_nad_ix;
      }else {
         exid_pT->val._.ofs = sk_park(gbla_p,exid_pS->val._.p,exid_pS->val.len);
         if IS_BITON(ety_pS->is,NOTATION_EF) {
            if ((ety_pT->val.ext.nad.ix = fill_nad(M_p,ety_pS->val.ext.nad.p))
               == -1) {
               errorh(gbla_p,NONOTETY,ERH_W_SEV,ety_pS->name._.p);
            }
         }else {
            ety_pT->val.ext.nad.ix = -1;
         }
         if (!ety_pT->name.len) {           /* Default Entity */
            M_p->dflety_exid_val_ofs = exid_pT->val._.ofs;
            M_p->dflety_val_nad_ix = ety_pT->val.ext.nad.ix;
         }
         free(exid_pS->val._.p);
      }
      free(exid_pS);
   }else {
      if (IS_BITON(ety_pS->is,DEFLT_EF)) {
         ety_pT->val.lit._.ofs = M_p->dflety_val_lit_ofs;
      }else {
         ety_pT->val.lit._.ofs =
            sk_park(gbla_p, ety_pS->val.lit._.p,
            ety_pT->val.lit.len = ety_pS->val.lit.len);
         free(ety_pS->val.lit._.p);      /* Free allocated storage */
      }
      if (!ety_pT->name.len) {           /* Default Entity */
         M_p->dflety_val_lit_ofs = ety_pT->val.lit._.ofs;
      }
   }
   /*--------------------------------------------------------------+
   | As the entity structure in core is still valid, use the room  |
   | left by ety_pS->value.val.lit._.ofs to store the value of its |
   | matching index in the ETY table.                              |
   | This is a trick to transform entities pointer by their index  |
   | in the ODT structures (since pointer have no more sense in    |
   | the unloaded ODT file).                                       |
   | For instance, the name of entities in SHORTREF map is changed |
   | into their related index, avoiding (when reloading the ODT)   |
   | to shsh_fnd the name of the entity.  Also, the ety_p within   |
   | the descriptor for CDATA-ASL is changed into an ety_ix        |
   |                                                               |
   | The counter (M_p->ety_ix) must have been initialized to 0 !   |
   +--------------------------------------------------------------*/
   ety_pS->val.ext._.ety_ix = M_p->ety_ix++;
}
#undef M_p
/******************************************************************************/
