/*******************************************************************************
* 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   = extid.c                                                   *
* DESCRIPTIVE NAME = Process Ressource Id definition                           *
* DATE_WR          = 06/13/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 02/14/91 PETRUS   Reworked the algorithms - More explicit messages    *
*                          Inform on error in Identifier found in <!SGML       *
*      2 11/09/91 PETRUS   New strategy for xid_fill, xid_cchk                 *
*******************************************************************************/

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

/*--------------+
|Local Functions|
+--------------*/
static boolean         _loadds LP_FCT is_iso_reg_nbr(uchar *, uint);
static CHOICES const * _loadds LP_FCT adr_ptxc_choices(void);
static CHOICES const * _loadds LP_FCT adr_xidk_choices(void);
static int                     LP_FCT skip_past_solidi(GA_P);

/*STATIC--------------------------------------------------------is_iso_reg_nbr-+
|Check if the Owner Identifier begin with:  "ISO Registration Number"          |
|(then the Public Text Class must be CHARSET                                   |
+-----------------------------------------------------------------------------*/
static boolean _loadds LP_FCT is_iso_reg_nbr(uchar *c_p, uint len)
{
   static uchar const IRN_string[] = "ISO Registration Number";
   uchar const *d_p;

   d_p = IRN_string;
   while (len--) {
      if (*c_p++ != *d_p++) return(FALSE);
      if (!*d_p) return(TRUE);
   }
   return(FALSE);
}

/*STATIC------------------------------------------------------adr_xidk_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_xidk_choices(void)
{
   static CHOICES const xidk_choices[] = {
      { offsetof(GNWDTAB,PUBLIC), (int)PUBLIC_TK    },
      { offsetof(GNWDTAB,SYSTEM), (int)SYSTEM_TK    },
      { -1,                       (int)NOT_FOUND_TK }
   };
   return(xidk_choices);
}

/*STATIC------------------------------------------------------adr_ptxc_choices-+
| Allow temporary access to local constant area (choices)                      |
+-----------------------------------------------------------------------------*/
static CHOICES const * _loadds LP_FCT adr_ptxc_choices(void)
{
   static CHOICES const ptxc_choices[] = {          /* Public Text Classes */
      { offsetof(GNWDTAB,CAPACITY), CAPACITY_XFC }, /* capacity set */
      { offsetof(GNWDTAB,CHARSET),  CHARSET_XFC  }, /* character data */
      { offsetof(GNWDTAB,DOCUMENT), DOCUMENT_XFC }, /* SGML document */
      { offsetof(GNWDTAB,DTD),      DTD_XFC      }, /* doc type decl subset */
      { offsetof(GNWDTAB,ELEMENTS), ELEMENTS_XFC }, /* element set */
      { offsetof(GNWDTAB,ENTITIES), ENTITIES_XFC }, /* entity set */
      { offsetof(GNWDTAB,LPD),      LPD_XFC      }, /* link type decl subset */
      { offsetof(GNWDTAB,NONSGML),  NONSGML_XFC  }, /* non-SGML data entity */
      { offsetof(GNWDTAB,NOTATION), NOTATION_XFC }, /* character data */
      { offsetof(GNWDTAB,SHORTREF), SHORTREF_XFC }, /* short reference set */
      { offsetof(GNWDTAB,SUBDOC),   SUBDOC_XFC   }, /* SGML subdoc entity */
      { offsetof(GNWDTAB,SYNTAX),   SYNTAX_XFC   }, /* concrete syntax */
      { offsetof(GNWDTAB,TEXT),     TEXT_XFC     }, /* SGML text entity */
      { -1,                         INVALID_XFC  }  /* INVALID */
   };
   return(ptxc_choices);
}

/*--------------------------------------------------------------------xid_fill-+
|Fill an EXID for an external identifier.                                      |
|                                                                              |
|Caller must have created an EXID that will be filled by this routine.         |
|All fields are filled, except for exid.name which is left as it is.           |
|Required data areas are mallocated by this routine in one chunk,              |
|pointed by exid_p->val._.p, the length of it is returned.                     |
|                                                                              |
|If (cls != 0), then "xid_cchk" is called.                                     |
|                                                                              |
|[73] external identifier = ("SYSTEM" | ("PUBLIC", ps+, public identifier)),   |
|                     (ps+, system identifier)?                                |
|                                                                              |
|Nota: all ending ps are skipped                                               |
|                                                                              |
|[74] public identifier = minimum literal                                      |
|                                                                              |
|[75] system identifier = ((lit, system data, lit)|(lita, system data, lita))  |
|                                                                              |
|[45] system data = character data  (no markup recognized)                     |
+-----------------------------------------------------------------------------*/
void P_FCT xid_fill(GA_P gbla_p, EXID *exid_p, uchar cls)
{
   ETY lit_ety;                        /* treat literal as a pseudo entity */
   uint   len;
   ADLEN name;

   name = exid_p->name;                        /* save the name ADLEN */
   memset((uchar *)exid_p,0,sizeof(EXID));     /* zero the XID */
   exid_p->name = name;                        /* restore the name ADLEN */
   exid_p->val._.p = GA(lit_p);                /* at least (2*(LITLEN+1)) */
   switch(choos_in(gbla_p, adr_xidk_choices(),(GNWD *)&GA(gnwdtab))) {
   case PUBLIC_TK:
      skip_ps(gbla_p);
      SET_BITON(exid_p->is,PUBLIC_XF);
      len = 1 + (exid_p->pid.len = get_lit_min(gbla_p, GA(lit_p)));
      if (IS_BITON(GA(opt).ft,FORMAL_FT) ||    /* FORMAL YES */
         (!GA(is_ocs_done))) {                 /* or still parsing <!SGML */
         lit_ety.is = RCDATA_EF;               /* tell what it is.. */
         lit_ety.kind = TEXT_EFK;
         lit_ety.name._.p = (uchar *)0;        /* pseudo entity */
         lit_ety.val.lit.len = len - 1;
         lit_ety.val.lit._.p = GA(lit_p);
         em_open(gbla_p,&lit_ety);             /* re-read it */
         xid_bfpi(gbla_p, exid_p);
         if (cls) xid_cchk(gbla_p,exid_p,cls); /* check class */
         em_close(gbla_p);
      }else {
         exid_p->asgn_cls = cls;
      }
      break;
   case SYSTEM_TK:
      len = 0;
      /* SET_BITON(exid_p->is, SYSTEM_XF); hmmmmm.... */
      exid_p->asgn_cls = cls;
      break;
   default:
      errorh(gbla_p,INVKWD,ERH_T_SEV+ERH_LOCATE);
      len = 0;  /* avoid compiler warning - can't return from ERH_T */
      break;
   }
   skip_ps(gbla_p);
   if ((GA(curr_token) == LIT_TK)||(GA(curr_token) == LITA_TK)) {
      SET_BITON(exid_p->is,SYSTEM_XF);
      exid_p->sid.rel = len;
      len += 1 + (exid_p->sid.len = get_lit_cdata(gbla_p, GA(lit_p) + len));
      skip_ps(gbla_p);
   }
   exid_p->val.len = len;                         /* set real length */
   memcpy(exid_p->val._.p = malloc(len), GA(lit_p), len);
}

/*--------------------------------------------------------------------xid_bfpi-+
|Build a public identifier or a formal public identifier                       |
|                                                                              |
|The FPI structure is filled "the best we can": unrecognized field are set     |
|to zero.  While parsing the <!SGML, just issue informative messages.          |
|                                                                              |
|This code must be enhanced: the following "DEFINE"(s) will permit (some day)  |
|implementing a CHARSET_independent version.                                   |
+-----------------------------------------------------------------------------*/
#define SOLIDUS       '/'
#define SOLIDI_LEN    2
#define PLUS_PREFIX   '+'
#define MINUS_PREFIX  '-'
void P_FCT xid_bfpi(GA_P gbla_p, EXID *exid_p)
{
   int i;
   uint rel = 0;
   uint msg_sev;
   boolean check_charset;

   msg_sev = GA(is_ocs_done) ? ERH_W_SEV : ERH_I_SEV;

   /*------------------------------------------+
   | (minimum data)//  => ISO Owner            |
   | +//(minimum data)// => Registered Owner   |
   | -//(minimum data)// => Unregistered Owner |
   +------------------------------------------*/
   switch(GA(curr_char)) {
   default:                 /* ISO */
      SET_BITON(exid_p->is,REGISTERED_XF | ISOREG_XF);
      check_charset = TRUE;
      break;
   case PLUS_PREFIX:
      SET_BITON(exid_p->is,REGISTERED_XF);
      /* fall thru */
   case MINUS_PREFIX:       /* unregistered */
      em_read(gbla_p);
      if ((i=skip_past_solidi(gbla_p)) != SOLIDI_LEN) {
         errorh(gbla_p,INVOWNKN,msg_sev+ERH_LOCATE); /* Invalid Owner Kind */
         return;
      }
      rel=1+SOLIDI_LEN;
      check_charset = FALSE;
      break;
   }
   exid_p->ownid.rel = rel;
   if ((i=skip_past_solidi(gbla_p)) <= 0) {
      errorh(gbla_p,INVOWNID,msg_sev+ERH_LOCATE); /* Invalid Owner Ident */
      return;
   }
   exid_p->ownid.len = i-SOLIDI_LEN;
   if (check_charset) {
      check_charset = is_iso_reg_nbr(exid_p->val._.p, exid_p->ownid.len);
   }

   /*-----------------------+
   |Public_text_class, SPACE|
   |Entered in upper-case   |
   +-----------------------*/
   i = IS_BITON(GA(opt).ot,GENERAL_CASE_OT);      /* save GENERAL CASE option */
   SET_BITOFF(GA(opt).ot,GENERAL_CASE_OT);        /* turn it off */
   exid_p->spec_cls = (uchar)choos_in(
      gbla_p, adr_ptxc_choices(), (GNWD *)&GA(gnwdtab)
   );
   if (i) SET_BITON(GA(opt).ot,GENERAL_CASE_OT);  /* and restore it */
   switch(exid_p->spec_cls) {
   case INVALID_XFC:
      errorh(gbla_p,INVTXTCL,msg_sev+ERH_LOCATE); /* Invalid Text Class */
      return;
   case CHARSET_XFC:
      SET_BITON(exid_p->is,PTDS_XF | PROVPTDV_XF);
      break;
   case NOTATION_XFC:
   case CAPACITY_XFC:
   case SYNTAX_XFC:
      SET_BITON(exid_p->is,PROVPTDV_XF);          /* PTDV must be omitted */
   default:
      if (check_charset) {                        /* ISO Reg Nmbr w/o CHARSET */
         errorh(gbla_p,INVPTC,msg_sev+ERH_LOCATE);
      }
      break;
   }
   if (GA(curr_char) != GA(SPACE_char)) {
      errorh(gbla_p,NOSPTXCL,msg_sev+ERH_LOCATE);
      return;
   }
   em_read(gbla_p);

   /*-----------------------------------------+
   |"-//"?,(public text description),"//"     |
   |-// => unavailable public text (optional) |
   +-----------------------------------------*/
   rel=exid_p->txtdesc.rel=(uint)em_ofs(gbla_p);  /* regain relative offset */
   if (GA(curr_char) == MINUS_PREFIX) {           /* unavailable public text ?*/
      if ((i=skip_past_solidi(gbla_p))==1+SOLIDI_LEN) {  /* check better */
         rel += 1+SOLIDI_LEN;                     /* Yep: update rel */
         exid_p->txtdesc.rel = rel;               /* point text description */
         i=skip_past_solidi(gbla_p);              /* skip over */
      }else {                                     /* No: */
         SET_BITON(exid_p->is,AVAILPTEXT_XF);     /* Text is available */
      }
   }else {
      SET_BITON(exid_p->is,AVAILPTEXT_XF);
      i=skip_past_solidi(gbla_p);
   }
   if (i > 0) {
      exid_p->txtdesc.len = i-SOLIDI_LEN;
      rel += i;
   }else {
      SET_BITOFF(exid_p->is,AVAILPTEXT_XF);
      errorh(gbla_p,NOPTXDS,msg_sev+ERH_LOCATE);
      return;
   }

   /*----------------------------------------+
   |(public text language                    |
   | | public text designating sequence),    |
   | ("//",public text display version)?     |
   +----------------------------------------*/
   exid_p->lang.rel = rel;                    /* Lang or Pub txt design seq */
   if ((i=skip_past_solidi(gbla_p)) <= 0 ) {  /* Ee reached */
      i = -i;
      rel += i;
   }else {
      rel += i;
      i -= SOLIDI_LEN;
   }
   exid_p->lang.len = i;
   if (IS_BITON(exid_p->is,PTDS_XF)) {         /* Public Text Design Seq: */
      if (!i) {
         errorh(gbla_p,NOPTDS,msg_sev+ERH_LOCATE);
         return;
      }
   }else {                                     /* Language: */
      /*-------------------------------+
      | Check "a posteriori" the       |
      | language name is OK (10.2.2.3) |
      +-------------------------------*/
      {
         uchar *c_p = ((uchar *)exid_p->val._.p) + exid_p->lang.rel;
         uint j;
         boolean first_time = TRUE;

         if ((!i)||(!is_namestrt(*c_p))) {
            errorh(gbla_p,INVPTL,msg_sev+ERH_LOCATE);
            return;
         }
         j = 0;
         do {
            if (!is_name(*c_p)) {
               errorh(gbla_p,INVPTL,msg_sev+ERH_LOCATE);
               return;
            }
            if (++j > NAMELEN) {
               errorh(gbla_p,INVPTL,msg_sev+ERH_LOCATE); /* Too big */
               return;
            }
            if (*c_p != GA(uctab)[*c_p]) {
               *c_p = GA(uctab)[*c_p];
               if (first_time) {
                  errorh(gbla_p,NOLANGUP,msg_sev+ERH_LOCATE);
                  first_time = FALSE;
               }
            }
            ++c_p;
         }while (j < (uint)i);
      }
   }
   if (GA(curr_char) == Ee) {                  /* PTDV omitted */
      SET_BITON(exid_p->is,PROVPTDV_XF);       /* system may provide it */
   }else {
      if (IS_BITON(exid_p->is,PROVPTDV_XF)) {  /* PDTV given, but not allowed */
         errorh(gbla_p,OOCPTDV,msg_sev+ERH_LOCATE);
         return;
      }
      exid_p->ptdv.rel = rel;
      if ((i=skip_past_solidi(gbla_p)) <= 0) { /* try to reach Ee */
         exid_p->ptdv.len = -i;
      }else {                                  /* Ee not reached */
         exid_p->ptdv.len = i;
         errorh(gbla_p,INVPTDV,msg_sev+ERH_LOCATE);
         return;
      }
   }
   SET_BITON(exid_p->is,VALIDFPI_XF);
}

/*------------------------------------------------------------skip_past_solidi-+
|Skip after next solidi ("//").                                                |
|Return number of bytes skipped, negated if Ee was reached.                    |
+-----------------------------------------------------------------------------*/
static int LP_FCT skip_past_solidi(GA_P gbla_p)
{
   int count = 0;
   boolean first_solidus_found = FALSE;

   for (;;) {
      switch (GA(curr_char)) {
      case SOLIDUS:
         if (first_solidus_found) {
            em_read(gbla_p);
            return(count+1);
         }else {
            first_solidus_found=TRUE;
         }
         break;
      case Ee:
         return(-count);
      default:
         first_solidus_found=FALSE;
         break;
      }
      em_read(gbla_p);
      ++count;
   }
}

/*--------------------------------------------------------------------xid_cchk-+
| Check the public text class of an external identifier                        |
| and set the parser assigned class exid.asgn_cls.                             |
|                                                                              |
| This routine is called systematically each time an external identifier       |
| has been found.  It checks that the spec_cls is compatible with the          |
| spec_cls (class specified in the formal public identifier).                  |
|                                                                              |
|   Asgn_cls:       Comes from:           If incompatible class,               |
|                                         Err Lvl:  VALIDFPI is:               |
|                                                                              |
|   CHARSET_XFC     <!SGML... BASESET        I      Off                        |
|   SYNTAX_XFC      <!SGML... CAPACITY       I      Off                        |
|   CAPACITY_XFC    <!SGML... SYNTAX         I      Off                        |
|   NOTATION_XFC    <!NOTATION               W      Off                        |
|   NONSGML_XFC     <!ENTITY... N/C/SDATA    I      Unchanged                  |
|   SUBDOC_XFC      <!ENTITY... SUBDOC       I      Unchanged                  |
|   DTD_XFC         <!DOCTYPE                I      Unchanged                  |
|   LPD_XFC         <!LINKTYPE               I      Unchanged                  |
|   0               <!ENTITY... (no type)    I      Unchanged                  |
|                                                                              |
|                                                                              |
| A Public Text Class is said compatible:                                      |
|                                                                              |
|   - When asgn_cls is one of CHARSET_XFC, SYNTAX_XFC, CAPACITY_XFC            |
|     and (asgn_cls == spec_cls).  If not, only an Informational               |
|     message is issued: if later FORMAL YES is found, then a final            |
|     Warning message will be issued.                                          |
|                                                                              |
|   - When FORMAL is YES and                                                   |
|      - asgn_cls is one of NOTATION_XFC, NONSGML_XFC,                         |
|        SUBDOC_XFC, DTD_XFC, LPD_XFC and (asgn_cls == spec_cls).              |
|        If not, messages W or I will be issued depending whether this         |
|        case was flagged as an error in ISO8879, or not.                      |
|      - asgn_cls is 0 and spec_cls is one of DOCUMENT_XFC, DTD_XFC,           |
|        ELEMENTS_XFC, ENTITIES_XFC, LPD_XFC, SHORTREF_XFC, TEXT_XFC.          |
|        If not, -- i.e in an ENTITY declaration, the spec_cls is one          |
|        of: CAPACITY_XFC, CHARSET_XFC, NONSGML_XFC, NOTATION_XFC,             |
|        SUBDOC_XFC, SYNTAX_XFC -- message I will be issued                    |
|        and the asgn_cls will be TEXT_XFC.                                    |
|                                                                              |
+-----------------------------------------------------------------------------*/
void P_FCT xid_cchk(GA_P gbla_p, EXID *exid_p, uchar asgn_cls)
{
   exid_p->asgn_cls = asgn_cls;             /* set the assigned class anyway */
   if (!IS_BITON(exid_p->is,VALIDFPI_XF)) { /* return: message already issued */
      return;                               /* or no message, but FORMAL NO */
   }
   switch (asgn_cls) {
   case CAPACITY_XFC:
   case CHARSET_XFC:
   case SYNTAX_XFC:
      if (asgn_cls != exid_p->spec_cls) {
         SET_BITOFF(exid_p->is, VALIDFPI_XF);
         errorh(gbla_p,BADCLS,ERH_I_SEV+ERH_LOCATE);
      }
      break;
   case NOTATION_XFC:
      if (IS_BITON(GA(opt).ft,FORMAL_FT) &&  /* FORMAL YES */
         (asgn_cls != exid_p->spec_cls)) {
         SET_BITOFF(exid_p->is, VALIDFPI_XF);
         errorh(gbla_p,BADCLS,ERH_W_SEV+ERH_LOCATE);
      }
      break;
   case NONSGML_XFC:
   case SUBDOC_XFC:
   case DTD_XFC:
   case LPD_XFC:
      if (IS_BITON(GA(opt).ft,FORMAL_FT) &&  /* FORMAL YES */
         (asgn_cls != exid_p->spec_cls)) {
         errorh(gbla_p,BADCLS,ERH_I_SEV+ERH_LOCATE);
      }
      break;
   default:            /* case TEXT_XFC */
      if (IS_BITON(GA(opt).ft,FORMAL_FT)) {  /* FORMAL YES */
         switch (exid_p->spec_cls) {
         case DOCUMENT_XFC:
         case DTD_XFC:
         case ELEMENTS_XFC:
         case ENTITIES_XFC:
         case LPD_XFC:
         case SHORTREF_XFC:
         case TEXT_XFC:
            break;
         default:
            errorh(gbla_p,BADCLS,ERH_I_SEV+ERH_LOCATE);
            break;
         }
      }
      break;
   }
}
/******************************************************************************/
