/*******************************************************************************
* 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   = odtattl.c                                                 *
* DESCRIPTIVE NAME = ATTLIST declaration                                       *
* DATE_WR          = 06/08/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 12/09/90 PGR      Added descriptor for CDATA attributes               *
*      2 04/01/97 ESPERT   Added ATTLIST event reports                         *
*******************************************************************************/

/*-------------+
|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_defv_choices(void);
static CHOICES const * _loadds LP_FCT adr_dclv_choices(void);
static void    P_FCT install_adl  (void STACK_BASED *);
static void    P_FCT install_dclv (void STACK_BASED *);

/*STATIC-----------------------------------------------------------install_adl-+
|Install a new associated element type name in the hash table                  |
|Create a patch chain (the ANODE pointer will be set when all the attributes   |
|will be validated)                                                            |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
static void P_FCT install_adl(void STACK_BASED * XM_p)
{
   GA_P gbla_p = M_p->gbla_p;
   ANHDR **anhdr_pp;
   /* uchar name[NAMELEN_MAX+1]; */

   if (M_p->is_notation_adl) {
      get_name(gbla_p,M_p->c_p);
      anhdr_pp=&(((NB *)ehsh_lnk(M_p->nota_scope,M_p->c_p,sizeof(NB)))->u.anhdr_p);
   }else {
      get_gi(gbla_p,M_p->c_p);
      anhdr_pp=&(((GI *)ehsh_lnk(M_p->elt_scope,M_p->c_p,sizeof(GI)))->anhdr_p);
   }
   if (*anhdr_pp) {                                    /* already defined: */
      errorh(gbla_p,DUPNAM,ERH_W_SEV+ERH_LOCATE);      /* it's an error */
   }else {
      M_p->c_p += 1 + NAMELEN;
      *anhdr_pp = (ANHDR *)M_p->patch_pp;
      M_p->patch_pp = (void **)anhdr_pp;
   }
}
#undef M_p

/*STATIC----------------------------------------------------------install_dclv-+
|Register a new name in a name token group declared value                      |
|                                                                              |
|Build in M_p->names_p a list of allowed value separated by SPACE,             |
|ending with '\0'.                                                             |
|                                                                              |
|IMPLEMENTATION NOTES:                                                         |
|                                                                              |
|  ---------- At first time: ------------------------                          |
|                                                                              |
|         \0|                                \0| X X X X\0|                    |
|   -----+--+----                      -----+--+----------+--                  |
|         |  |               ====>              |          |                   |
| value_p-+  |                          value_p-+          |                   |
|    array_p-+                                     array_p-+                   |
|                                                                              |
|   ---------- Next time(s): -------------------------                         |
|                                                                              |
|         \0| X X X X\0|                     \0| X X X X  | Y Y Y \0|          |
|   -----+--+----------+--             -----+--+----------+---------+--        |
|            |          |    ====>              |                    |         |
|    value_p-+          |               value_p-+                    |         |
|               array_p-+                                    array_p-+         |
|                                                                              |
+-----------------------------------------------------------------------------*/
#define M_p ((COMMON_P)XM_p)
static void P_FCT install_dclv(void STACK_BASED * XM_p)
{
   GA_P gbla_p = M_p->gbla_p;
   uchar *wa_p;
   ANODE *anode_p, *anode_pF;

   anode_p = anode_pF = M_p->anode_p;

   wa_p = M_p->c_p;                       /* current pointer in M_p->names_p */
   if (anode_p->atd.kind == NOTATION_AFK) {
      get_name(gbla_p,wa_p);
   }else {
      get_nmtk(gbla_p,wa_p);
   }
   if (++M_p->attcnt > ATTCNT) {           /* count name tokens */
      errorh(gbla_p,BIGATTCNT,ERH_W_SEV+ERH_LOCATE,wa_p);
      --M_p->attcnt;
      return;
   }
   do {
      if (IS_BITON(anode_p->atd.is,LISTED_AF) &&
         (tg_lsfnd(gbla_p,wa_p,anode_p->atd.val.lit._.p))) {
         errorh(gbla_p,DUPDCLV,ERH_W_SEV+ERH_LOCATE,wa_p,anode_p->atd.name.p);
         return;
      }
   }while ((anode_p = anode_p->next_p) != anode_pF);
   M_p->c_p += 1 + strlen(wa_p);
   if (*(char *)(anode_p->atd.val.lit._.p)) {       /* not first time: */
      *(wa_p - 1) = GA(SPACE_char);                 /* extend the list */
   }else {
      anode_p->atd.val.lit._.p =                    /* else begin the list */
        (char *)(anode_p->atd.val.lit._.p) + 1;
   }
}
#undef M_p

/*STATIC------------------------------------------------------adr_dclv_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_dclv_choices(void)
{
   static CHOICES const dclv_choices[] = {
      { offsetof(GNWDTAB,CDATA),    CDATA_AFK    }, /* character data */
      { offsetof(GNWDTAB,ENTITY),   ENTITY_AFK   }, /* general entity name */
      { offsetof(GNWDTAB,ENTITIES), ENTITIES_AFK },
      { offsetof(GNWDTAB,ID),       ID_AFK       }, /* id value */
      { offsetof(GNWDTAB,IDREF),    IDREF_AFK    }, /* id reference value */
      { offsetof(GNWDTAB,IDREFS),   IDREFS_AFK   },
      { offsetof(GNWDTAB,NAME),     NAME_AFK     }, /* name */
      { offsetof(GNWDTAB,NAMES),    NAMES_AFK    },
      { offsetof(GNWDTAB,NMTOKEN),  NMTOKEN_AFK  }, /* name token */
      { offsetof(GNWDTAB,NMTOKENS), NMTOKENS_AFK },
      { offsetof(GNWDTAB,NOTATION), NOTATION_AFK }, /* nota name (+ nm grp) */
      { offsetof(GNWDTAB,NUMBER),   NUMBER_AFK   }, /* number */
      { offsetof(GNWDTAB,NUMBERS),  NUMBERS_AFK  },
      { offsetof(GNWDTAB,NUTOKEN),  NUTOKEN_AFK  }, /* number token */
      { offsetof(GNWDTAB,NUTOKENS), NUTOKENS_AFK },
      { -1,                         INVALID_AFK  }
   };
   return(dclv_choices);
}

/*STATIC------------------------------------------------------adr_defv_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_defv_choices(void)
{
   static CHOICES const defv_choices[] = {           /* attribute is */
      { offsetof(GNWDTAB,FIXED),    FIXED_AFV    },  /* a fixed attribute */
      { offsetof(GNWDTAB,REQUIRED), REQUIRED_AFV },  /* a required attribute */
      { offsetof(GNWDTAB,CURRENT),  CURRENT_AFV  },  /* a current attribute */
      { offsetof(GNWDTAB,CONREF),   CONREF_AFV   },  /* a content ref attr */
      { offsetof(GNWDTAB,IMPLIED),  IMPLIED_AFV  },  /* an impliable attr */
      { -1,                         INVALID_AFV  }
   };
   return(defv_choices);
}

/*--------------------------------------------------------------------adl_deco-+
|Process ATTLIST MD (Attribute Definition List Declaration)                    |
|                                                                              |
|Enter here when ATTLIST has been decoded,                                     |
|Returns with curr_char (supposed to ) == MDC                                  |
|                                                                              |
|[141] attribute definition list declaration =                                 |
|        mdo, "ATTLIST", ps+,                                                  |
|        (associated element type | associated notation name), ps+             |
|        attribute definition list,                                            |
|        ps*, mdc)                                                             |
|                                                                              |
|                                                                              |
|[142] attribute definition list = attribute definition,                       |
|                (ps+, attribute definition)*                                  |
|                                                                              |
|[143] attribute definition =                                                  |
|                attribute name, ps+, declared value, ps+, default value       |
|                                                                              |
|[144] attribute name = name                                                   |
|                                                                              |
|[145] declared value = "CDATA" | "ENTITY" | "ENTITIES" |                      |
|[146]                  "ID" | "IDREF" | "IDREFS" |                            |
|                       "NAME" | "NAMES" | "NMTOKEN" | "NMTOKENS" |            |
|                       "NUMBER" | "NUMBERS" | "NUTOKEN" | "NUTOKENS" |        |
|                       ("NOTATION", ps+, name group) | name token group       |
|                                                                              |
|IMPLEMENTATION NOTE:                                                          |
| If no default AVS have been specified atd.val.lit.len is -1.                 |
| However this doesn't mean atd.val.lit.ofs is *not* significant,              |
| as in <!ATTLIST LI type (ord|unord|std) #REQUIRED>.                          |
| But, if neither no "choice list", nor default AVS have been specified,       |
| as in <!ATTLIST XY ENTITIES #REQUIRED> atd.val.lit.ofs is 0.                 |
| For safety, the *application* must analyse only if atd.val.lit.len is -1.    |
|                                                                              |
+-----------------------------------------------------------------------------*/
void P_FCT adl_deco(COMMON_P M_p)
{
   GA_P gbla_p = M_p->gbla_p;
   uint ref_cnt;
   int len;
   ANODE anode, *anode_p, *anode_pX, *anode_pF;
   ANHDR *anhdr_p;
   ANHDR_FLAGS anhdr_is;
   boolean definitively_invalid, invalid_attribute;
   void **patch_pp, **patch_ppX;
   OFS ofs;
   uint atdsize = 0;   /* size of the final ATD array */
   uint attval_nln;
   ADLEN *desc_p;
   uint initial_nbr_of_attributes = M_p->nbr_of_attributes;
   ELT *elt_pG, *elt_p;
   NCB *ncb_pG, *ncb_p;
   ATD *atd_p;
   uint elt_count = 0, ncb_count = 0;
   uchar *c_p, *c_pF;
   uint ix = 0;
   
   M_p->anode_p = anode_p = anode.next_p = &anode;
   M_p->patch_pp = (void **)&M_p->patch_pp; /* patch chain */
   M_p->attcnt = 0;                     /* count attribute name + name tokens */
   *((uint *)&anhdr_is) = 0;

   /*-----------------------------------------------------+
   |Decode associated elt / notation:                     |
   |[149.1] associated notation name =                    |
   |     rni, "NOTATION, ps+, (notation name | name group)|
   |[72] associated element type =                        |
   |     generic identifier | name group                  |
   +-----------------------------------------------------*/
   M_p->c_p = M_p->names_p;
   skip_ps(gbla_p);
   if (GA(curr_token) == RNI_TK) {
      chk_gnam(gbla_p,GA(gnwdtab).NOTATION.s.p);   /* Associated Notation */
      M_p->is_notation_adl = TRUE;
      tll_ANDCST(gbla_p);
      skip_ps(gbla_p);
   }else {
      M_p->is_notation_adl = FALSE;
      tll_AEDCST(gbla_p);
   }
   switch (GA(curr_token)) {
   case GRPO_TK:
      get_nmgp(gbla_p,install_adl,M_p); /* Parse name group */
      break;
   case NOT_FOUND_TK:
      install_adl(M_p);                 /* install a new elemt/notation name */
      break;
   default:
      errorh(gbla_p,INVNAMGRP,ERH_T_SEV+ERH_LOCATE);
      break;
   }
   /* If no GI were referenced, set up invalid flag */
   definitively_invalid = (boolean)(M_p->patch_pp==(void **)&M_p->patch_pp);
   skip_ps(gbla_p);

   /* Build arrays of ELTs or NCBs */
   if (M_p->is_notation_adl == TRUE) {
      ncb_p = ncb_pG = (NCB *)malloc(sizeof(NCB));
   }else {
      elt_p = elt_pG = (ELT *)malloc(sizeof(ELT));
   }
   for (c_p=M_p->names_p, c_pF=M_p->c_p, ix=1;
        c_p != c_pF;
        c_p += 1+NAMELEN, ix++) {
      if (M_p->is_notation_adl == TRUE) {
         ncb_pG = (NCB *)realloc(ncb_pG, ix*sizeof(NCB));
         ncb_count++;
         ncb_p = ncb_pG + ix-1;
         memcpy(ncb_p->exid.name._.p = (uchar *)malloc(1+NAMELEN), c_p, 1+NAMELEN);
      }else {
         elt_pG = (ELT *)realloc(elt_pG, ix*sizeof(ELT));
         elt_count++;
         elt_p = elt_pG + ix-1;
         memcpy(elt_p->name.p = (uchar *)malloc(1+NAMELEN), c_p, 1+NAMELEN);
      }
   }

   M_p->c_p = anode.atd.name.p = M_p->names_p; /* (ATTCNT+1)*(NAMELEN+1) */

   /*--------------------------------+
   |For all attributes of this GI... |
   +--------------------------------*/
   do {                                   /* for each attribute */
      get_name(gbla_p,anode.atd.name.p);  /* get attribute name */
      if (++M_p->attcnt > ATTCNT) {       /* check count */
         errorh(gbla_p,BIGATTCNT,ERH_W_SEV+ERH_LOCATE,anode.atd.name.p);
         definitively_invalid = TRUE;
         --M_p->attcnt;
      }
      invalid_attribute = definitively_invalid;   /* initialize */
      /*-------------+
      |Declared Value|
      +-------------*/
      anode.atd.val.lit.len = -1;       /* length of the defaulted value */
      skip_ps(gbla_p);
      switch(GA(curr_token)) {
      case GRPO_TK:
         anode.atd.kind = GROUP_AFK;    /* is a list of valid names */
         anode.atd.is = LISTED_AF;
         break;
      case NOT_FOUND_TK:
         anode.atd.is = 0;
         switch (
            anode.atd.kind=
            (uchar)choos_in(gbla_p,adr_dclv_choices(),(GNWD *)&GA(gnwdtab))
         ) {
         case INVALID_AFK:
            errorh(gbla_p,INVKWD,ERH_T_SEV+ERH_LOCATE);
            break;
         case ENTITY_AFK:
         case ENTITIES_AFK:
            anhdr_is.entity = TRUE;
            /* fall thru */
         case IDREF_AFK:
         case IDREFS_AFK:
            if (M_p->is_notation_adl) {                 /* change to NAME(s?) */
               errorh(gbla_p,INVDCLVDA,ERH_W_SEV+ERH_LOCATE);
               anode.atd.kind = (uchar)(NAME_AFK | (anode.atd.kind & 1));
               anhdr_is.entity = FALSE;                   /* just in case... */
            }
            break;
         case NOTATION_AFK:
            if (anhdr_is.notation) {
               errorh(gbla_p,DUPNOTDEF,ERH_W_SEV+ERH_LOCATE);
               anode.atd.kind = GROUP_AFK;                /* Treat as a list */
            }else if (M_p->is_notation_adl) {             /* See 11.4.1 */
               errorh(gbla_p,INVDCLVDA,ERH_W_SEV+ERH_LOCATE);
               anode.atd.kind = GROUP_AFK;                /* Treat as a list */
            }else {
               anhdr_is.notation = TRUE;                  /* no more than 1 ! */
            }
            skip_ps(gbla_p);       /* 10/30/90: no ambiguity created if no ps */
            if (GA(curr_token)!=GRPO_TK) {
               errorh(gbla_p,INVATTDCL,ERH_T_SEV+ERH_LOCATE);
            }
            anode.atd.is = LISTED_AF;                     /* list of valid nm */
            break;
         case ID_AFK:
            if (anhdr_is.id) {
               errorh(gbla_p,DUPIDDEF,ERH_W_SEV+ERH_LOCATE);
               anode.atd.kind = NAME_AFK;                 /* Treat as a name */
            }else if (M_p->is_notation_adl) {             /* See 11.4.1 */
               errorh(gbla_p,INVDCLVDA,ERH_W_SEV+ERH_LOCATE);
               anode.atd.kind = NAME_AFK;                 /* Treat as a name */
            }else {
               anhdr_is.id = TRUE;                        /* no more than 1 ! */
               /*-------------------------------------+
               |ID attribute name should be unique    |
               |(see 11.3.2, + Tech. Errata 04/30/87) |
               +-------------------------------------*/
               if (M_p->idname[0]) {
                  if (strcmp(M_p->idname, anode.atd.name.p)) {
                     errorh(gbla_p,DUPIDNAME,ERH_I_SEV+ERH_LOCATE,M_p->idname);
                  }
               }else {     /* remember first idname... */
                  memcpy(
                     M_p->idname,anode.atd.name.p, 1+strlen(anode.atd.name.p)
                  );
               }
            }
            break;
         default:
            break;
         }
         break;
      default:
         errorh(gbla_p,INVATTDCL,ERH_T_SEV+ERH_LOCATE);
         anode.atd.is = 0;
         anode.atd.kind = INVALID_AFK;
         break;
      }
      /*--------------------------------+
      |Check if attribute is duplicated |
      +--------------------------------*/
      {
         ANODE *anode_pX = &anode;
         while ((anode_pX = anode_pX->next_p) != &anode) {
            if (!strcmp(anode.atd.name.p,anode_pX->atd.name.p)) {
               errorh(gbla_p,DUPATT,ERH_W_SEV+ERH_LOCATE,anode.atd.name.p);
               invalid_attribute = TRUE;
               break;
            }
         }
      }
      if (invalid_attribute) *(uchar *)(anode.atd.name.p) = '\0';
      M_p->c_p = (uchar *)(anode.atd.name.p) + 1+strlen(anode.atd.name.p);
      /*---------------------------------------------------------+
      |Create NOW the value string from the list of allowed value|
      |These values will follow the attribute name.              |
      +---------------------------------------------------------*/
      if IS_BITON(anode.atd.is,LISTED_AF) {
         anode.atd.val.lit._.p = M_p->c_p - 1;     /* point trailing zero */
         get_nmgp(gbla_p,install_dclv,M_p);        /* updates M_p->c_p */
      }else {
         anode.atd.val.lit._.ofs = 0;              /* will point a '\0' */
      }
      /*----------------------------------------------------------+
      |[147] default value =                                      |
      |    ((rni, "FIXED", ps+)?, attribute value specification) ||
      |    (rni,("REQUIRED" | "CURRENT" | " CONREF" | "IMPLIED")) |
      +----------------------------------------------------------*/
      skip_ps(gbla_p);
      switch(GA(curr_token)) {
      case RNI_TK:
         switch (
            anode.atd.defv =
            (uchar)choos_in(gbla_p,adr_defv_choices(),(GNWD *)&GA(gnwdtab))
         ) {
         case INVALID_AFV:
            errorh(gbla_p,INVKWD,ERH_T_SEV+ERH_LOCATE);
            break;
         case IMPLIED_AFV:
            break;
         case REQUIRED_AFV:
            anhdr_is.required = TRUE;
            SET_BITON(anode.atd.is,BAD_AF);      /* Assumed for REQUIRED */
            break;
         case CURRENT_AFV:
            if (M_p->is_notation_adl) {          /* See 11.4.1 */
               errorh(gbla_p,INVDEFV,ERH_W_SEV+ERH_LOCATE);
            }else {
               SET_BITON(anode.atd.is,BAD_AF);   /* Need a 1st assignement */
               anhdr_is.current = TRUE;
            }
            break;
         case CONREF_AFV:
            if (M_p->is_notation_adl) {          /* See 11.4.1 */
               errorh(gbla_p,INVDEFV,ERH_W_SEV+ERH_LOCATE);
            }else {
               anhdr_is.conref = TRUE;
            }
            break;
         case FIXED_AFV:
            skip_Wps(gbla_p);
            break;
         }
         break;
      default:
         anode.atd.defv = DEFLT_AFV;
         break;
      }
      /*------------------------------------+
      |Parse the default AVS, save it.      |
      +------------------------------------*/
      if ((anode.atd.kind == ID_AFK) &&                /* ID must be .. */
         (anode.atd.defv > REQUIRED_AFV)) {            /* IMPLIED or REQUIRED */
         errorh(gbla_p,INVIDDEF,ERH_W_SEV+ERH_LOCATE); /* tell user */
         if (anode.atd.defv >= DEFLT_AFV) {            /* AVS specified: */
            tg_gtavs(gbla_p,GA(lit_p),(ATD *)0,&desc_p,&attval_nln,DEF_TGF);
         }                                             /* get rid of it */
         anode.atd.defv = IMPLIED_AFV;                 /* assume IMPLIED */
      }else if (anode.atd.defv >= DEFLT_AFV) {         /* DEFAULT or FIXED */
         len=tg_gtavs(gbla_p,GA(lit_p),&anode.atd,&desc_p,&attval_nln,DEF_TGF);
         if (!invalid_attribute) {
            if (len == -1) {                           /* Invalid AVS */
               if IS_BITON(anode.atd.is,LISTED_AF) {
/*** removed ***  errorh(gbla_p,ASSREQD,ERH_W_SEV); ***/
                  anode.atd.defv = REQUIRED_AFV;       /* assume REQUIRED*/
                  SET_BITON(anode.atd.is,BAD_AF);      /* REQUIRED is BAD... */
                  anhdr_is.required = TRUE;            /* 'til its specified */
               }else {
/*** removed ***  errorh(gbla_p,ASSIMPL,ERH_W_SEV); ***/
                  anode.atd.defv = IMPLIED_AFV;        /* assume IMPLIED */
               }
            }else {
               M_p->nbr_of_dclv_text += attval_nln;    /* Normalized length */
               if (!desc_p) {
                  anode.atd.val.lit.len = len;
                  if (!IS_BITON(anode.atd.is,LISTED_AF)) {
                     /*-----------------------------------------------+
                     |If the default attribute was a token in a group,|
                     |tg_gtavs put it at the first place: that's it.  |
                     |Otherwise, malloc room for the deflt literal.   |
                     |Note offset 0 for an empty string.              |
                     +-----------------------------------------------*/
                     anode.atd.val.lit._.ofs =  len ?
                     sk_park(gbla_p, GA(lit_p), len + 1) : 0; /* +1: ASCIIZ */
                  }
               }else {                                 /* lit is described */
                  SET_BITON(anode.atd.is,DESC_AF);
                  M_p->nbr_of_attdesc += (anode.atd.val.desc.len = desc_p->len);
                  anode.atd.val.desc._.p = desc_p;
                  desc_p->_.ofs = ((desc_p->len = len) > 0) ?
                  sk_park(gbla_p, GA(lit_p), len + 1) : 0; /* +1 for ASCIIZ */
               }
            }
         }
      }
      /*---------------------------------------+
      |Mallocate a new anode,                  |
      |copy the local anode to it,             |
      |insert it into the circular linked list |
      +---------------------------------------*/
      if (!invalid_attribute) {
         ++(M_p->nbr_of_attributes);
         atdsize += sizeof(ATD);                 /* sizeof the ATD array */
         *(anode_p->next_p = malloc(sizeof(ANODE))) = anode; /* move */
         (anode_p = anode_p->next_p)->next_p = &anode;
         anode.atd.name.p = M_p->c_p;            /* at next free room */
      }
   }while ((skip_ps(gbla_p)) && (GA(curr_token) == NOT_FOUND_TK));

   /*-------------------------------------------------+
   |In the hash areas, set up pointer(s) to the first |
   |mallocated anode (or NULL pointers if bad)        |
   |Remove local "anode" from the list,               |
   |sk_park the "work_array" buffer,                  |
   |offset the string value within stringbuf          |
   +-------------------------------------------------*/
   if (anode.next_p != &anode) {                 /* At least 1 anode OK */
      SET_BITON(anode_p->atd.is,LAST_AF);        /* tell the last attribute */
      anhdr_p = (ANHDR *)malloc(sizeof(ANHDR));
      anhdr_p->is = anhdr_is;
      anhdr_p->atdsize = atdsize;
      anhdr_p->u.anode_p =                       /* points the first anode */
      anode_p->next_p = anode.next_p;            /* Remove local from list */
      ofs = sk_park(gbla_p, M_p->names_p, M_p->c_p - M_p->names_p);
      anode_pF =                        /* to stop in circular list */
      anode_pX = anhdr_p->u.anode_p;     /* point (save) first anode */
      if (M_p->is_notation_adl == TRUE) {
         atd_p = ncb_pG->atd.p = (ATD *)malloc(atdsize);
      }else {
         atd_p = elt_pG->atd.p = (ATD *)malloc(elt_pG->atdsize = atdsize);
      }
      do {
         *atd_p = anode_pX->atd; /* copy the atd */
         if (!IS_BITON(atd_p->is,DESC_AF)) {
            if ((!IS_BITON(atd_p->is,LISTED_AF)) &&
                (atd_p->defv >= DEFLT_AFV)) {
               /* Point default AVS string */
               atd_p->val.lit._.p = sk_point(gbla_p, atd_p->val.lit._.ofs);
            }
         }else if (atd_p->defv >= DEFLT_AFV) {
            ADLEN *desc_pX;

            /* Copy the desc array ...*/
            memcpy(desc_pX = atd_p->val.desc._.p = 
                   malloc(atd_p->val.desc.len * sizeof(ADLEN)),
                   anode_pX->atd.val.desc._.p, atd_p->val.desc.len * sizeof(ADLEN));
            /* ... and point the AVS string */
            desc_pX->_.p = sk_point(gbla_p, desc_pX->_.ofs);
         }
         atd_p++;
      }while ((anode_pX = anode_pX->next_p) != anode_pF);
      if (M_p->is_notation_adl == TRUE) {
         tll_ANDCND(gbla_p, ncb_pG, ncb_count);
         free(ncb_pG);
      }else {
         tll_AEDCND(gbla_p, elt_pG, elt_count);
         free(elt_pG);
      }
      do {                                       /* unlocate pointers */
         anode_p = anode_p->next_p;
         anode_p->atd.name.ofs =
            ((uchar *)(anode_p->atd.name.p)-M_p->names_p) + ofs;
         if IS_BITON(anode_p->atd.is,LISTED_AF) {
            anode_p->atd.val.lit._.ofs =
            ((uchar *)(anode_p->atd.val.lit._.p) - M_p->names_p) + ofs;
         }
         /* name.p and val.lit._.p no more valid from now... */
      }while (!(IS_BITON(anode_p->atd.is,LAST_AF)));
   }else {                                       /* not even one */
      anhdr_p = (ANHDR *)0;                      /* disable ahdr_p in gi */
   }
   M_p->nbr_of_att_dclv +=                       /* to compute AVGRPCAP */
      (M_p->attcnt - (M_p->nbr_of_attributes - initial_nbr_of_attributes));
   /*---------------------------------+
   |Solve (patch) hdr pointer in gi(s)|
   +---------------------------------*/
   for (ref_cnt=0,patch_pp=M_p->patch_pp;patch_pp !=(void **)&M_p->patch_pp;) {
      patch_pp = (void **)(*(patch_ppX = patch_pp));
      *patch_ppX = (void *)anhdr_p;
      ++ref_cnt;
   }
   if (anhdr_p) anhdr_p->ref_cnt = ref_cnt;      /* how many GI use this hdr */
}

/*--------------------------------------------------------------------adl_ulad-+
|Unload an ATD list from the anhdr structure,                                  |
|returns an index in the ATD table.                                            |
|                                                                              |
|The ATD are stored in the ATD table (pointed by M_p), the anode_p are freed.  |
|If the anhdr is still referenced by others (ELT or NOTATION name group),      |
|  - turn on the is.solved bit to tell ATD stowage has been done,              |
|  - put the index value in anhdr_p->u.atd_ix.                                 |
|  Next time, this value will just be returned.                                |
|If no one more uses the ANHDR node, (ref_count == 0), free the anhdr.         |
|(WARNING: anhdr_p no more usable after this routine !)                        |
|                                                                              |
|NOTE: The ATD array is also filled by ODTENTY in case of                      |
|      an N/C/SDATA entity with data attributes.                               |
+-----------------------------------------------------------------------------*/
int P_FCT adl_ulad(COMMON_P M_p, ANHDR *anhdr_p)
{
   GA_P gbla_p = M_p->gbla_p;
   int ix;
   ANODE *anode_p, *anode_pF;

   if (!anhdr_p->is.solved) {           /* if not already "solved": */
      anode_pF =                        /* to stop in circular list */
      anode_p = anhdr_p->u.anode_p;     /* point (save) first anode */
      anhdr_p->u.atd_ix = ix = diffptr(M_p->atd_p, M_p->atd_orig);
      anhdr_p->is.solved = TRUE;        /* now solved */
      do {
         odt_uatd(M_p,&(anode_p->atd)); /* save atd */
         free(anode_p);                 /* and free */
      }while ((anode_p = anode_p->next_p) != anode_pF);
   }else {
      ix = anhdr_p->u.atd_ix;
   }
   if (--anhdr_p->ref_cnt == 0) free(anhdr_p);
   return(ix);
}
/******************************************************************************/
