/******************************************************************************
* SMP00 Sample of an SGML Application Interfaced with the YASP Parser         *
*-----------------------------------------------------------------------------*
* Written 1987 - 1993 by                                                      *
*         Pierre G. Richard                                                   *
*         IBM T.J. Watson Research Center                                     *
*         Yorktown Heights, New York                                          *
*         petrus@watson.ibm.com                                               *
*-----------------------------------------------------------------------------*
* FUNCTION NAME    = YASP / Application                                       *
* STATUS           = N/A                                                      *
* COMPONENT NAME   = server.c                                                 *
* DESCRIPTIVE NAME = Interface to services / SAA and ANSI platforms           *
* DATE_WR          = 08/15/1989                                               *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                     *
*                                                                             *
* FUNCTIONAL DESCRIPTION =                                                    *
* System-dependent Services.                                                  *
* This module redirects IO and Memory services to the system.                 *
*                                                                             *
* CHANGE HISTORY =                                                            *
* Number Date     Inits    Change & reason (brief)                            *
*      1 08/24/91 PETRUS   Reentrancy                                         *
*      2 12/09/91 PETRUS   Map a CHARSET to a specific id                     *
*      3 09/28/92 PETRUS   Added getpos/setpos for LINK                       *
*      4 10/06/94 ESPERT   Added CATALOG for FPIs with hashtable              *
*      6 04/03/95 ESPERT   Added more character sets                          *
******************************************************************************/

/*-------------+
|Include Files |
+-------------*/
#include "gendef.h"            /* general definitions */
#include "yaspapi.h"           /* YASP Application Program Interface */
#include "server.h"            /* xxxserv h file */
#include <stdio.h>             /* Standard I/O */
#include <stdlib.h>            /* Standard Library */
#include <string.h>            /* String manipulation */
#include <errno.h>
#include <ctype.h>

#if (defined(M_I86))           /* If C2 or C6.00 */
#undef FILENAME_MAX            /* FILENAME_MAX (stdio) is too small */
#define FILENAME_MAX _MAX_PATH /* use _MAX_PATH (from stdlib) */
#endif

#if (defined(GNUCC))
#define FILENAME_MAX 512
#define fpos_t long
#endif

/*------------------+
| Local Definitions |
+------------------*/
#define FILE_BUFSIZE   BUFSIZ  /* Buffer size: deflt value from stdio.h */
#define YASP_MSGFILE "ysp.txt" /* YASP message file name */
#define TERM_BUFSIZE   140     /* see CMS RDTERM */

typedef struct t_FYLE {        /* For bufferizing the I/O */
   struct t_FYLE *next;        /* Files chain */
   FILE  *fd;                  /* File Descriptor */
   char  *name_p;              /* File name */
   int    buf_size;            /* Buffer size */
   struct {                    /* Flags: */
      uint truncated : 1;      /* when a record is greater than BUFSIZE */
      uint first     : 1;      /* First read is issued */
   }is;
   uchar *buffer;              /* Data transfer buffer */
}FYLE;

typedef struct t_POSBLK {      /* Block to get/set a physical position */
   struct {
      uint first     : 1;
      uint at_eof    : 1;
      uint truncated : 1;
   }is;                        /* flags for the pointed record */
   fpos_t fpos;                /* system position */
}POSBLK;

typedef struct t_SRAREA {
   boolean is_compil;          /* Are we in compilation mode? */
   char const *pri_fn;         /* Primary Entity Name */
   char const *ocs_fn;         /* OCS file name */
   char const *TERM_fn;        /* Reserved name for terminal */
   char const *CATALOG_fn;     /* FPI Catalog file name */
   void *FPI_htab;             /* FPI hash table */
   FYLE *top_fyle_p;           /* first fyle in chain */
   FYLE *cur_fyle_p;           /* current fyle in chain */
   FYLE *term_fyle_p;          /* fyle describing the TERMINAL */
   char *fnchain_p;            /* filename chain */
   POSBLK posblk;              /* Temporary storage to return */
   uchar **msg_pp;             /* Messages in core */
}SRAREA;

#define HASHSIZE (((sr_EHTBE *)hashtable-1)->size)
#define sr_ehsh_open(scope,name)   sr_ehsh_acc(scope,name,TRUE)

typedef struct sr_t_EHSH {
   struct sr_t_EHSH *next;     /* For hashing: collision chain */
   uchar *name_p;              /* offset of the entry name */
   void  *var_p;               /* for user usage */
}sr_EHSH;

typedef union {
   int  size;
   sr_EHSH *ehsh_p;        /* to align on an array of (EHSH *) */
}sr_EHTBE;

/*--------------+
|Local Functions|
+--------------*/
static boolean  set_current      (FYLE *, SRAREA *);
static uint     find_file        (uchar const * const, uchar *);
static uint     build_fspec      (uchar const *,uchar const *,uchar *,boolean);
static void   * build_FPI_htab   (SRAREA *);
static uint     pubid_to_fspec   (uchar *, int, uchar *, SRAREA *);
static int      charset_to_ahlid (uchar *, int);
static uchar ** init_message     (uchar *);
static uint GetMessage(
   ADLEN *, ushort, uchar *, uchar *, ushort, ushort, uchar *, ushort *,
   uchar ***
);
static void *    sr_ehsh_ini      (uint);
static void *    sr_ehsh_fnd      (void *, uchar *);
static sr_EHSH * sr_ehsh_acc      (void *, uchar *, boolean);
static void *    sr_ehsh_lnk      (void *, uchar *, uint);

/*STATIC----------------------------------------------------------set_current-+
| Function Name: set_current                                                  |
|                                                                             |
| Functional Description: Check the fyle pointer f_pS belongs to the list     |
|                         of pointers to opened FYLE structures               |
|                                                                             |
| Parameters: fyle_p   fyle pointer                                           |
|                                                                             |
| Return: TRUE  fyle_p points one of the opened FYLE structures               |
|         FALSE fyle_p is not a known FYLE pointer                            |
+----------------------------------------------------------------------------*/
static boolean set_current(FYLE *f_pS, SRAREA *sra_p)
{
   FYLE *f_p;

   for (f_p=sra_p->top_fyle_p; f_p; f_p=f_p->next) { /* loop on all the FYLEs */
      if (f_p == f_pS) {                      /* Is it the correct one ? */
         sra_p->cur_fyle_p = f_p;             /* Yes: update current FYLE */
         return(TRUE);                        /* Exit: found! */
      }
   }
   return(FALSE);                             /* Exit: not found... */
}

/*--------------------------------------------------------------------sr_init-+
| Function Name: sr_init                                                      |
|                                                                             |
| Functional Description: initialize variables before call parser.            |
|                         Create the server area (SRAREA).                    |
|                         SRAREA is a structure which keeps the internals of  |
|                         the server isolated of the rest of the application. |
|                         It is passed among most sr_ calls for reentrancy.   |
|                                                                             |
| Parameters:                                                                 |
|      is_compil:    Are we in compilation mode, vs. production mode ?        |
|      pri_fn:       Primary Entity Name                                      |
|      ocs_fn:       OCS file name                                            |
|      TERM_fn:      Reserved name for the terminal                           |
|                                                                             |
| Return: A non-NULL pointer (to the SRAREA) if the initialization was OK     |
|         A NULL pointer if an error occured                                  |
+----------------------------------------------------------------------------*/
void *sr_init(
	      boolean is_compil,
	      char const *pri_fn, char const *ocs_fn, char const *TERM_fn, 
	      char const *CAT_fn
) {
   SRAREA *sra_p;

   if (!(sra_p = (SRAREA *)malloc(sizeof(SRAREA)))) {
      return((SRAREA *)0);
   }
   memset(sra_p, '\0', sizeof(SRAREA));
   sra_p->is_compil = is_compil;
   sra_p->pri_fn = pri_fn;
   sra_p->ocs_fn = ocs_fn;
   sra_p->TERM_fn = TERM_fn;
   sra_p->CATALOG_fn = CAT_fn;
   sra_p->FPI_htab = build_FPI_htab(sra_p);
   return((void *)sra_p);
}

/*--------------------------------------------------------------------sr_term-+
| Function Name: sr_term                                                      |
|                                                                             |
| Functional Description: Do cleanup after finished with system services.     |
|                         Release storage for all filenames.  Delete          |
|                         temporary files and release storage for all FYLES.  |
|                                                                             |
| Parameters: NONE                                                            |
|                                                                             |
| Return: none                                                                |
+----------------------------------------------------------------------------*/
void sr_term(void *srarea_p)
{
   char *c_p, *d_p;
   SRAREA *sra_p = (SRAREA *)srarea_p;

   if (!sra_p) return;

   /*-------------------------+
   |delete temporary files and|
   |release the filename areas|
   +-------------------------*/
   c_p = sra_p->fnchain_p;                   /* First filename area */
   while (c_p) {                             /* Loop on the filenames */
      d_p = *((char **)c_p);                 /* Next one in d_p */
      if (*(c_p + sizeof(uchar *)) == 'T') { /* temporary files are marked T */
         remove(c_p + 1 + sizeof(uchar *));  /* delete temporary files */
      }
      free(c_p);                             /* free this filename area */
      c_p = d_p;                             /* loop to the next one */
   }
   if (sra_p->msg_pp) {
      free(*(sra_p ->msg_pp));
      free(sra_p ->msg_pp);
   }
   free(sra_p);
}

/*STATIC------------------------------------------------------------find_file-+
| Function Name: find_file                                                    |
|                                                                             |
| Functional Description: Find a file in the current directory then thru      |
|                         the DPATH environment variable.  This function      |
|                         will use a buffer provided by the caller that       |
|                         must be at least FILENAME_MAX+1 bytes long          |
|                         Returns the length of the filespec or 0 if          |
|                         not found.                                          |
|                                                                             |
| Parameters: fspec_p     pointer to file spec                                |
|             buffer_p    pointer to callers buffer                           |
|                                                                             |
| Return: 0  if not found or                                                  |
|         length of the filespec                                              |
+----------------------------------------------------------------------------*/
static uint find_file(uchar const * const fspec, uchar *buffer)
{
   FILE *fd;
   char *dpathvar, *d_p;
   uint len, len2;

   memcpy(buffer, fspec, 1 + (len=strlen(fspec))); /* look in curr directory */
   if (!(fd=fopen(buffer, "r"))) {                 /* try to open the file */
      if (!(dpathvar = ENV_ROUTE("DPATH"))) {      /* not found: get routes */
         return(0);                                /* no route: abandon */
      }
      for (;;) {                                   /* loop for all routes ...*/
         if (!(d_p=strchr(dpathvar, ROUTE_DLM))) { /* search next separator */
            len2 = strlen(dpathvar);               /* none: take the rest */
         }else {
            len2 = d_p - dpathvar;                 /* else isolate this route */
         }
         memcpy(buffer, dpathvar, len2);           /* get the route */
         *(buffer + len2) = SUBDIR_DLM;            /* catenate SUBDIR_DLM */
         memcpy(buffer+len2+1, fspec, 1+len);      /* then the file spec */
         if ((fd=fopen(buffer, "r"))!= NULL) {     /* try opening the file */
            len += 1+len2;                         /* Success: set the fq len */
            break;                                 /* Leave the search loop */
         }
         if (*(dpathvar += len2)=='\0') return(0); /* Bad and no more route */
         ++dpathvar;                               /* Bad, try another route */
      }
   }
   fclose(fd);                                     /* close the file */
   return(len);
}

/*STATIC----------------------------------------------------------build_fspec-+
| Function Name: build_fspec                                                  |
|                                                                             |
| Functional Description: build a complete file spec from a filename and      |
|                         a filetype.  Find the file in the current           |
|                         directory or DPATH if required.                     |
|                         The buffer must be at least FILENAME_MAX+1          |
|                         bytes long.                                         |
|                                                                             |
| Parameters: fname_p     filename pointer                                    |
|             ftype_p     filetype pointer                                    |
|             fspec_buf_p pointer to where built filespec will go             |
|             check_exist find the file in DPATH                              |
|                                                                             |
| Return: 0  if not found or                                                  |
|         length of the filespec                                              |
+----------------------------------------------------------------------------*/
static uint build_fspec(
   uchar const *name,           /* name of the file */
   uchar const *ext,            /* extension, including the leading dot */
   uchar *buffer,               /* resulting file name */
   boolean check_existence)
{
   uint len1, len2;

   len1 = strlen(name);
   memcpy(buffer, name, len1);
   memcpy(buffer+len1, ext, 1+(len2 = strlen(ext)));
   if (check_existence) {
      uchar fspec[FILENAME_MAX+1];

      memcpy(fspec, buffer, len1+len2+1);  /* store fspec in temp storage */
      return(find_file(fspec, buffer));    /* length of found spec */
   }else {
      return(len1+len2);                   /* length of current spec */
   }
}

/*STATIC-------------------------------------------------------build_FPI_htab-+
| Builds a hash table to map FPIs to system identifiers.  The table is built  |
| through a catalog file whose name must be given by the application.         |
| This code is terrible, particularly the parsing of the catalog file.  Sorry |
| no time to do better.  A catalog should be an SGML document!!!              |
+----------------------------------------------------------------------------*/
#define FPI_HASHSIZE 11
#define CAT_LINESIZE 100
static void * build_FPI_htab(SRAREA *sra_p)
{
  uchar catbuf[FILENAME_MAX+1] = "";
  FILE *fd;
  void *scope_id;
  char catline[CAT_LINESIZE] = "";
  uint i, strt, end;
  sr_EHSH *ehsh_p;
  char varname[CAT_LINESIZE];
  char filespec[CAT_LINESIZE];
  void *var_p;

  if ((!find_file(sra_p->CATALOG_fn,catbuf)) ||
      (!(fd=fopen((char *)catbuf, "r")))) {
    return((void *)0);                     /* cannot access the file */
  }

  /* Initialize FPI hash table */
  if ((scope_id=sr_ehsh_ini(FPI_HASHSIZE)) == NULL) {
    return ((void *)0);
  }else {
	/* Parse the CATALOG and fill the hash table */
    while (fgets(catline, CAT_LINESIZE, fd) != NULL) {
      if (catline[0] == 'P') {
		for(i=0;i<strlen(catline);i++)
		  if (catline[i] == '"') {
			strt = i+1;
			break;
		  }
		for(i=strt;i<strlen(catline);i++)
		  if (catline[i] == '"') {
			end = i-1;
			break;
		  }
		memset(varname, '\0', CAT_LINESIZE);
		ehsh_p = sr_ehsh_acc(scope_id, 
							memcpy(varname,&catline[strt], end-strt+1),
							TRUE);
      }else {
		for(i=0;i<strlen(catline);i++)
		  if (catline[i] == '"') {
			strt = i+1;
			break;
		  }
		for(i=strt;i<strlen(catline);i++)
		  if (catline[i] == '"') {
			end = i-1;
			break;
		  }
		var_p = sr_ehsh_lnk(scope_id, varname, end-strt+1);
		memset(var_p, '\0', end-strt+1);
		memset(filespec, '\0', CAT_LINESIZE);
		memcpy(filespec, &catline[strt], end-strt+1);
		strcpy((char *)var_p, filespec);
      }
      memset(catline, '\0', CAT_LINESIZE);
      strt = end = 0;
    }
  }
  return(scope_id);   
}


/*STATIC-------------------------------------------------------pubid_to_fspec-+
| Map a PUBLIC Identifier to a true filespec.                                 |
|                                                                             |
| A static table is currently used.  A real Application could instead use     |
| an external file.                                                           |
+----------------------------------------------------------------------------*/
static uint pubid_to_fspec(uchar *id, int id_len, uchar *buffer, SRAREA *sra_p)
{
   static uchar const * const pubdef[] = {
      "ISO 8879:1986//CAPACITY Reference//EN"                   ,"standard.cap",
      "ISO 8879:1986//SYNTAX Reference//EN"                     ,"standard.csd",
      "ISO 8879:1986//ENTITIES Added Latin 1//EN"               ,"iso$lat1.ent",
      "ISO 8879:1986//ENTITIES Added Latin 2//EN"               ,"iso$lat2.ent",
      "ISO 8879:1986//ENTITIES Greek Letters//EN"               ,"iso$grk1.ent",
      "ISO 8879:1986//ENTITIES Monotoniko Greek//EN"            ,"iso$grk2.ent",
      "ISO 8879:1986//ENTITIES Greek Symbols//EN"               ,"iso$grk3.ent",
      "ISO 8879:1986//ENTITIES Alternative Greek Symbols//EN"   ,"iso$grk4.ent",
      "ISO 8879:1986//ENTITIES Russian Cyrillic//EN"            ,"iso$cyr1.ent",
      "ISO 8879:1986//ENTITIES Non-Russian Cyrillic//EN"        ,"iso$cyr2.ent",
      "ISO 8879:1986//ENTITIES Numeric and Special Graphic//EN" ,"iso$num.ent",
      "ISO 8879:1986//ENTITIES Diacritical Marks//EN"           ,"iso$dia.ent",
      "ISO 8879:1986//ENTITIES Publishing//EN"                  ,"iso$pub.ent",
      "ISO 8879:1986//ENTITIES Box and Line Drawing//EN"        ,"iso$box.ent",
      "ISO 8879:1986//ENTITIES General Technical//EN"           ,"iso$tech.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Ordinary//EN","iso$amso.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Binary Operators//EN",
                                                                 "iso$amsb.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Relations//EN",
                                                                 "iso$amsr.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Negated Relations//EN",
                                                                 "iso$amsn.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Arrow Relations//EN",
                                                                 "iso$amsa.ent",
      "ISO 8879:1986//ENTITIES Added Math Symbols: Delimiters//EN",
                                                                 "iso$amsc.ent",
      "-//IBM/PGM 5684-025//ENTITIES Added Math Symbols//EN",    "ibmmath.ent"
   };
   uchar const * const * c_pp = pubdef - 1;
   int i = sizeof(pubdef)/(2 * sizeof(char *));
   int rc;

   while (strncmp(*++c_pp,id,id_len)) {
      if (!(--i)) break;
      ++c_pp;
   }
/*   return(find_file(*(++c_pp),buffer));*/
   if (i != 0) {
     if ((rc = find_file(*(++c_pp),buffer)) == 0) {
       uchar *hash_id = (uchar *)sr_ehsh_fnd(sra_p->FPI_htab, id);
       if (hash_id != NULL) {
	 return(find_file(hash_id, buffer));
       }else {
	 return(0);
       }
     }else {
       return rc;
     }
   }else {
     uchar *hash_id = (uchar *)sr_ehsh_fnd(sra_p->FPI_htab, id);
     if (hash_id != NULL) {
       return(find_file(hash_id, buffer));
     }else {
       return(0);
     }
   }
}

/*STATIC-----------------------------------------------------charset_to_ahlid-+
| Function Name: charset_to_ahlid                                             |
|                                                                             |
| Functional Description: Map a PUBLIC CHARSET Identifier to an AHLID (ahl.i) |
|                                                                             |
| Return: the AHLID                                                           |
+----------------------------------------------------------------------------*/
static int charset_to_ahlid(uchar *pubid, int pubid_len)
{
   static struct t_CHSETID {
      const int    ahlid;
      const uchar *pubid;
   }chsetid_tbl[] = {
      {
         ISO_646_1983_AHLID,
         "ISO 646-1983//CHARSET "
         "International Reference Version (IRV)//ESC 2/5 4/0"
      }, {
         ISO_646_1983_AHLID,
         "ISO 646-1983//CHARSET "
         "International Reference Version (IRV)//ESC 2/8 4/0"
      }, {
         ISO_646_1983_AHLID,
         "ISO 646-1983//CHARSET "
         "International Reference Version (IRV)//ESC 2/9 4/0"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/8 4/2"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/13 4/1"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/14 4/1"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right Part of Latin Alphabet Nr. 1//ESC 2/15 4/1"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right-hand Part of Latin Alphabet Nr.1//ESC 2/8 4/2"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right-hand Part of Latin Alphabet Nr.1//ESC 2/13 4/1"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right-hand Part of Latin Alphabet Nr.1//ESC 2/14 4/1"
      }, {
         ISO_100_ECMA_AHLID,
         "ISO Registration Number 100//CHARSET "
         "ECMA-94 Right-hand Part of Latin Alphabet Nr.1//ESC 2/15 4/1"
      }, {
         ANSI_X3_4_1986_AHLID,
         "+//ANSI//CHARSET "
         "American Standard Code for Information Interchange (ASCII) "
         "ANSI X3.4-1986//ESC 2/8 4/2"
      }, {
         ANSI_X3_4_1986_AHLID,
         "+//ANSI//CHARSET "
         "American Standard Code for Information Interchange (ASCII) "
         "ANSI X3.4-1986//ESC 2/9 4/2"
      }, {
         ANSI_X3_4_1986_AHLID,
         "ANSI X3.4-1986//CHARSET "
         "American Standard Code for Information Interchange (ASCII)//"
         "ESC 2/8 4/2"
      }, {
         IBM_395_AHLID,
         "-//IBM//CHARSET "
   /**** "Codepage 00395 1984-05//" ****/
         "Codepage 00395//EN"
      }, {
         IBM_850_AHLID,
         "-//IBM//CHARSET "
         "Codepage 00850 1986-08//"
      }
   };
   struct t_CHSETID *chsetid_p = chsetid_tbl - 1;
   int i = sizeof(chsetid_tbl) / sizeof(struct t_CHSETID);

   while (strncmp((++chsetid_p)->pubid,pubid,pubid_len)) {
      if (!(--i)) return(UNKNOWN_AHLID);
   }
   return(chsetid_p->ahlid);
}

/*--------------------------------------------------------------------sr_find-+
| Function Name: sr_find                                                      |
|                                                                             |
| Functional Description: Given a named resource identifier descriptor,       |
|                         find an associated ID_handle.  Set in               |
|                         pac_p->ahl the Id_AHL.                              |
|                         The class of the external identifier will           |
|                         determine how to get the system ID handle.          |
|                         When not in compilation mode, don't save the        |
|                         compiled .ODT and .OCS files.                       |
|                                                                             |
|                         Allocate space for remembering each filename.       |
|                         This filename is prefixed by a letter to describe   |
|                         the kind of file (see sr_xenop).                    |
|                                                                             |
|  FIND is called:                                                            |
|  - before to open an external resource, or                                  |
|  - before to pass an external resource to the *application*:                |
|       - not parseable or SUBDOC ENTITY found in content or                  |
|         in an Attribute Value Spec of an Element                            |
|       - NOTATION (either directly in an AVS or ENTITY associated)           |
|  It is guaranteed this routine will not be called more than once for the    |
|  same resource.                                                             |
|                                                                             |
|  This routine must put in the field AHL an unique identifier                |
|  that will be used to:                                                      |
|  - open the file or                                                         |
|  - update the AHL field in the ETY block / NAD block                        |
|                                                                             |
|  When there is not a SYSTEM of PUBLIC external id the application should    |
|  have a system default.  For the purposes of this application there is no   |
|  system default.                                                            |
|                                                                             |
| NOTES: NOTATIONs are generally mapped to an executable module               |
|       while ENTITIES are just Non-SGML files, or SUBDOC.                    |
|                                                                             |
|       It's up to the *application* to map the external ressource to         |
|       whatever kind of "system address" is suitable.  Some prefer a         |
|       handle, some prefer a pointer to whatever they want.  For this        |
|       reason, the AHL is an union that can handle both.                     |
|                                                                             |
|       There is a conceptual difference between Id_AHL (ID Handles           |
|       updated by FIND) and Acc_AHL (ACCESS handles returned by OPEN).       |
|       (even if these values are identical for some *applications*)          |
|                                                                             |
|       Here, the ID Handles are pointers to the fully qualified name,        |
|       and the ACCESS handles are pointers to the FYLE structure associated  |
|       to opened entities.                                                   |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0:  Normal                                                          |
|         2:  Warning, external entity is already opened                      |
|         4:  Could not associate a Fileid-AHL                                |
+----------------------------------------------------------------------------*/
int sr_find(PAC *pac_p, void *srarea_p)
{
#if defined (__32BIT__)
   static uchar const LOCS_NAME[] = "LOCAL32";      /* System OCS */
#else
   static uchar const LOCS_NAME[] = "LOCAL";        /* System OCS */
#endif
   static uchar const UOCS_NAME[] = "USER";         /* User's defined OCS */
   static uchar const OCS_SUITE[] = ".OCS";         /* OCS extension */
   static uchar const ODT_SUITE[] = ".ODT";         /* ODT extension */
   static uchar const OLT_SUITE[] = ".OLT";         /* OLT extension */

   EXID *exid_p = pac_p->dad.p;
   uchar const *fspec_p;
   uint fspec_len;
   uchar **c_pp, *c_p;
   uchar buffer[FILENAME_MAX+1];
   boolean is_tempfile = FALSE;
   SRAREA *sra_p = (SRAREA *)srarea_p;

   switch(exid_p->asgn_cls) {                 /* Which class ? */
   case PRIMARY_XFC:         /* (primary entity)    --,  --, No Name */
      fspec_p = sra_p->pri_fn;                /* set the name */
      fspec_len = strlen(fspec_p);
      break;
   case UTODTR_XFC:          /* ODT file read       --,  --, doctype name */
   case UTOLTR_XFC:          /* OLT file read       --,  --, linktype name */
      if (sra_p->is_compil) return(4);        /* pretend it's not found */
      if (!(fspec_len = build_fspec(
         exid_p->name.p,
         (exid_p->asgn_cls == UTODTR_XFC)? ODT_SUITE : OLT_SUITE,
         buffer, TRUE
      ))) {
         return(4);
      }
      fspec_p = buffer;
      break;
   case UTODTW_XFC:          /* ODT file write      --,  --, doctype name */
   case UTOLTW_XFC:          /* OLT file write      --,  --, linktype name */
      if (!sra_p->is_compil) {                /* production mode */
         if (!tmpnam(buffer)) return(4);
         fspec_len = strlen(buffer);
         is_tempfile = TRUE;                  /* to tell it's a tempfile */
      }else {
         fspec_len = build_fspec(
            exid_p->name.p,
            (exid_p->asgn_cls == UTODTW_XFC)? ODT_SUITE : OLT_SUITE,
            buffer, FALSE
         );
      }
      fspec_p = buffer;
      break;
   case UTSYNL_XFC:          /* Default OCS file (read) */
   case UTSYNR_XFC:          /* Is like RefCon.OCS */
      if (!(fspec_len = build_fspec(
           sra_p->ocs_fn ? sra_p->ocs_fn : (char *)LOCS_NAME, OCS_SUITE, buffer, TRUE
      ))) {
         return(4);
      }
      fspec_p = buffer;
      break;
   case UTSYNU_XFC:          /* User OCS file (read/write) */
      if (!sra_p->is_compil) {                /* production mode */
         if (!tmpnam(buffer)) return(4);
         fspec_len = strlen(buffer);
         is_tempfile = TRUE;
      }else {
         fspec_len = build_fspec(
            sra_p->ocs_fn ? sra_p->ocs_fn : (char *)UOCS_NAME, OCS_SUITE, buffer, FALSE
         );
      }
      fspec_p = buffer;
      break;
   case CHARSET_XFC:         /* <!SGML.. CHARSET   Sid, Pid, no name */
      pac_p->ahl.i = charset_to_ahlid(        /* set the pac ahl.i */
         (uchar *)exid_p->val.p+exid_p->pid.rel,
         exid_p->pid.len
      );
      return(0);
   case CAPACITY_XFC:        /* <!SGML.. CAPACITY  Sid, PId, no name */
   case SYNTAX_XFC:          /* <!SGML.. SYNTAX    Sid, Pid, No Name */
   case DOCUMENT_XFC:        /* <!ENTITY (no type) Sid, Pid, entity name */
   case ELEMENTS_XFC:        /* <!ENTITY (no type) Sid, Pid, entity name */
   case ENTITIES_XFC:        /* <!ENTITY (no type) Sid, Pid, entity name */
   case SHORTREF_XFC:        /* <!ENTITY (no type) Sid, Pid, entity name */
   case TEXT_XFC:            /* <!ENTITY (no type) Sid, Pid, entity name */
   case DTD_XFC:             /* <!DOCTYPE          Sid, Pid, doctype name */
   case LPD_XFC:             /* <!LINKTYPE         Sid, Pid, linktype name */
   case NONSGML_XFC:         /* <!ENTITY C/N/SDATA Sid, Pid, entity name */
   case SUBDOC_XFC:          /* <!ENTITY SUBDOC    Sid, Pid, entity name */
   case NOTATION_XFC:        /* <!NOTATION         Sid, Pid, notation name */
   default:
      if (exid_p->sid.len) {                  /* system id given: take as is */
         fspec_p = (uchar *)exid_p->val.p + exid_p->sid.rel;
         fspec_len = exid_p->sid.len;
      }else if ((exid_p->pid.len) &&          /* public identifier given and */
         (0 != (fspec_len=pubid_to_fspec(     /* map to a known system ident */
         (uchar *)exid_p->val.p+exid_p->pid.rel, exid_p->pid.len, 
					 buffer, sra_p)))) {
         fspec_p = buffer;                    /* take mapped system ident. */
      }else {                                 /* no SID, no valid PID: */
         return(4);                           /* bad return code */
      }
      break;
   }
   /*----------------------------------+
   |Copy the fspec into a mallocated   |
   |structure, linked with the previous|
   |one to be able to release it later |
   |The filename is preceded by letter |
   |'T' if it is temporary, else 'X'.  |
   +----------------------------------*/
   if (!(c_pp = malloc(sizeof(uchar *) + fspec_len + 2))) return(4);
   *c_pp = sra_p->fnchain_p;                  /* chain to previous filenames */
   sra_p->fnchain_p=(char *)c_pp;             /* remember current filename */
   pac_p->ahl.p = c_p = (char *)(c_pp+1) + 1; /* set the pac FileId-AHL */
   *((uchar *)memcpy(c_p,fspec_p,fspec_len)+fspec_len) = '\0';
   if (is_tempfile) {
      *(c_p-1) = 'T';
   }else {
      *(c_p-1) = 'X';
   }
   return(0);
}

/*-------------------------------------------------------------------sr_xenop-+
| Function Name: sr_xenop                                                     |
|                                                                             |
| Functional Description: Open a text file (external entity) using the        |
|                         FileID-AHL found by sr_find.                        |
|                         Open the file.                                      |
|                         Allocate space for a FYLE struct to remember things |
|                         about the file.                                     |
|                         Place the FYLE pointer in pac_p->ahl.p.             |
|                                                                             |
| Note: some "applications" can consider identifying and accessing returns    |
|       the same handle (FileId-AHL == Access-AHL), some don't.               |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0  Normal (pac_p->ahl.p = FYLE pointer)                             |
|         4  File is already opened                                           |
|         6  Cannot open (ex: UNIX exclusive access)                          |
|         8  Invalid FileId-AHL                                               |
+----------------------------------------------------------------------------*/
int sr_xenop(PAC *pac_p, void *srarea_p)
{
   FYLE **f_pp, *f_p;
   uchar *filespec = (uchar *)pac_p->ahl.p;
   SRAREA *sra_p = (SRAREA *)srarea_p;

   if (!strcmp(filespec, sra_p->TERM_fn)) {   /* TERMINAL:  */
      if (sra_p->term_fyle_p) return(4);      /* already opened */
      if (!(f_p = malloc((1*sizeof(FYLE)) + TERM_BUFSIZE + 1))) {
         return(6);                           /* or cannot be "opened" */
      }
      f_p->buffer = (uchar *)(f_p + 1);
      f_p->buf_size = TERM_BUFSIZE;
      sra_p->term_fyle_p = f_p;               /* remember fyle pointer */
   }else {
      FILE *fd;

      if (!(fd = fopen(filespec, "r"))) {
         return(8);
      }
      /*--------------------------------------------+
      | Associate a FYLE structure for bufferizing, |
      | RS/RE handling, a.s.o                       |
      +--------------------------------------------*/
      if (!(f_p = malloc(sizeof(FYLE) + FILE_BUFSIZE))) {
         return(6);
      }
      f_p->buffer = (uchar *)(f_p + 1);       /* set buffer pointer */
      f_p->buf_size = FILE_BUFSIZE;           /* and buffer size */
      f_p->fd = fd;                           /* remember handle */
   }
   f_p->name_p = filespec;                    /* mallocated string in fnchain */
   f_p->is.truncated = FALSE;
   f_p->is.first = TRUE;
   /*--------------------+
   | Enter the new FYLE  |
   | into the FYLE queue |
   +--------------------*/
   for (f_pp=&(sra_p->top_fyle_p); *f_pp; f_pp = &(*f_pp)->next);
   pac_p->ahl.p = sra_p->cur_fyle_p = *f_pp = f_p;
   f_p->next = (FYLE *)0;                     /* tells it's the last file */
   return(0);
}

/*-------------------------------------------------------------------sr_xencl-+
| Function Name: sr_xencl                                                     |
|                                                                             |
| Functional Description: Close a text file (external entity).                |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0     Normal                                                        |
|         8     Cannot close                                                  |
+----------------------------------------------------------------------------*/
int sr_xencl(PAC *pac_p, void *srarea_p)
{
   FYLE *f_p = (FYLE *)pac_p->ahl.p;     /* use pointer part of AHL struct */
   FYLE **f_pp;
   SRAREA *sra_p = (SRAREA *)srarea_p;

   if ((!f_p) || ((f_p != sra_p->cur_fyle_p) && (!set_current(f_p, sra_p)))) {
      return(8);                           /* Bad Handle: return */
   }
   if (f_p == sra_p->term_fyle_p) {        /* Do not fclose the TERMINAL */
      sra_p->term_fyle_p = (FYLE *)0;      /* just nullify the pointer */
   }else {                                 /* for regular files */
      if (fclose(f_p->fd)) return(8);      /* if fclose fails, return */
   }
   for (                                   /* Unlink the FYLE chain */
      f_p = sra_p->cur_fyle_p, f_pp=(FYLE **)&sra_p->top_fyle_p;
      *f_pp != f_p;                        /* until file to close is found */
      f_pp = &(*f_pp)->next                /* loop... */
   );
   *f_pp = f_p->next;                      /* Fix link chain */
   free(f_p);                              /* remove FYLE structure */
   sra_p->cur_fyle_p = (FYLE *)0;          /* current file no more valid */
   return(0);
}

/*-------------------------------------------------------------------sr_xenrd-+
| Function Name: sr_xenrd                                                     |
|                                                                             |
| Functional Description: Read a text file (external entity).                 |
|                                                                             |
| There is no function to perform basic IO in the IBM C SAA/CPI set:          |
| for text files, we use fgets and assume there is no such things as          |
| extra-control characters or '\0' within these files.                        |
| - if there is a '\0' within the record read, then strlen does not return    |
|   the number of bytes read                                                  |
| - if the record is longer than the receiving buffer but contains            |
|   a '\n' at offset BUFSIZE, then record truncation is not detected          |
|                                                                             |
| Note 1: When a document is reparsed (RESUME_PSF), only the Application knows|
|         what the row, col and offset the primary entity is resumed from.    |
|         These informations need to be put in the oeb structure.  In such a  |
|         case, insert the code at the location indicated "** See Note 1 **". |
|         For instance:                                                       |
|                                                                             |
|         if ((!pac_p->pos.ety_p)         (* If PRIMARY (no parent entity) *) |
|            && (is_resume_in_process)) { (* and if we are reparsing *)       |
|            pac_p->oeb_p->row = ??; ...  (* set row, col, offset *)          |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 1:    RE/RS to be inserted                                          |
|         4:    End-of-File                                                   |
|         8:    Bad handle                                                    |
|         >100: system code + 100                    -- Bad IO --             |
+----------------------------------------------------------------------------*/
int sr_xenrd(PAC *pac_p, void *srarea_p)
{
   FYLE *f_p = (FYLE *)pac_p->ahl.p;     /* use pointer part of AHL struct */
   SRAREA *sra_p = (SRAREA *)srarea_p;
   int len_read, rc;
   uchar *c_p;

   if ((!f_p) || ((f_p != sra_p->cur_fyle_p) && (!set_current(f_p, sra_p)))) {
      return(8);                         /* Bad Handle */
   }
   if (f_p->buf_size==-1) {              /* EOF was in progress */
      return(4);                         /* EOF is now reached */
   }
   if (f_p->is.first) {                  /* for first read */
      f_p->is.first = FALSE;
      pac_p->oeb_p->appl_info = f_p->name_p; /* Tell the file name */
      /* ** See Note 1 ** */
   }
   if (f_p == sra_p->term_fyle_p) {      /* Read TERMINAL: no RE/RS inserted */
      /*--------------+
      |Terminal entry |
      +--------------*/
      int ch;
      c_p = f_p->buffer - 1;
      len_read = -1;
      while ((++len_read < TERM_BUFSIZE) &&
         ((ch=getc(stdin)) != '\n') && (ch != EOF)) {
         *(++c_p) = (uchar)ch;
      }
      if ((len_read >= 6) &&             /* check for $(EOF) */
         (*(c_p - 5) == '$') && (*(c_p - 4) == '(') && (*(c_p - 3) == 'E') &&
         (*(c_p - 2) == 'O') && (*(c_p - 1) == 'F') && (*c_p       == ')')) {
         len_read -= 6;
         ch = EOF;
      }
      if (ch == EOF) {                   /* EOF in progress: */
         f_p->buf_size = -1;             /* no more further reads! */
         rc = len_read? 1 : 4;           /* signal EOF reached now */
      }else {
         rc = 1;                         /* RE/RS need to be inserted */
      }
   }else {
      /*-----------------------------+
      |fill the buffer up to bufsize |
      +-----------------------------*/
      c_p = fgets(f_p->buffer, f_p->buf_size, f_p->fd);
      if (!c_p) {                           /* EOF or ERROR */
         f_p->buf_size = -1;                /* avoid further reads */
         if (feof(f_p->fd)) {               /* just EOF */
            rc = 4;                         /* set rc */
         }else {                            /* ERROR */
            rc = 100+errno;                 /* add 100 to system code */
         }
      }else if (*(c_p+ (len_read = strlen(c_p)-1)) == '\n') { /* '\n' ended */
         if (f_p->is.truncated) {           /* truncated record */
            rc = 3;                         /* only RE to insert */
            f_p->is.truncated = FALSE;
         }else {
            rc = 1;                         /* RE/RS to insert */
         }
      }else {                               /* last char not '\n' */
         ++len_read;                        /* take it into account */
         if (feof(f_p->fd)) {               /* EOF reached */
            f_p->buf_size = -1;             /* remember this */
            if (f_p->is.truncated)  {       /* truncated record */
               rc = 3;                      /* only RE to insert */
               f_p->is.truncated = FALSE;   /* for next calls... */
            }else {
               rc = 1;                      /* RE/RS to insert */
            }
         }else {                            /* not at EOF */
            if (f_p->is.truncated)  {       /* truncated record */
               rc = 0;                      /* nothing to insert */
            }else {
               rc = 2;                      /* only RS to insert */
               f_p->is.truncated = TRUE;    /* cos' it's truncated */
            }
         }
      }
   }
   if (rc < 4) {                                   /* EOF not reached */
      pac_p->dad.len = len_read;
      pac_p->dad.p = f_p->buffer;
   }
   return(rc);
}

/*-------------------------------------------------------------------sr_xengp-+
| Function Name: sr_xengp                                                     |
|                                                                             |
| Functional Description: Get the position of an opened entity                |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0:    OK                                                            |
|         8:    Bad handle                                                    |
|         >100: system code + 100                    -- Bad IO --             |
+----------------------------------------------------------------------------*/
int sr_xengp(PAC *pac_p, void *srarea_p)
{
   FYLE *f_p = (FYLE *)pac_p->ahl.p;     /* use pointer part of AHL struct */
   SRAREA *sra_p = (SRAREA *)srarea_p;
   int rc;

   if ((!f_p) || ((f_p != sra_p->cur_fyle_p) && (!set_current(f_p, sra_p)))) {
      return(8);                         /* Bad Handle */
   }
   if (f_p != sra_p->term_fyle_p) {      /* not for Terminal! */
      if (f_p->buf_size == -1) {         /* EOF reached */
         sra_p->posblk.is.at_eof = TRUE;
      }else {
         sra_p->posblk.is.at_eof = FALSE;
         sra_p->posblk.is.first = f_p->is.first;
         sra_p->posblk.is.truncated = f_p->is.truncated;
         if ((rc = fgetpos(f_p->fd, &(sra_p->posblk.fpos))) != 0) {
            return(rc + 100);
         }
      }
   }
   pac_p->dad.p = &(sra_p->posblk);
   pac_p->dad.len = sizeof(POSBLK);
   return(0);
}

/*-------------------------------------------------------------------sr_xensp-+
| Function Name: sr_xensp                                                     |
|                                                                             |
| Functional Description: Reset the position of an opened entity              |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0:    OK                                                            |
|         8:    Bad handle                                                    |
|         >100: system code + 100                    -- Bad IO --             |
+----------------------------------------------------------------------------*/
int sr_xensp(PAC *pac_p, void *srarea_p)
{
   FYLE *f_p = (FYLE *)pac_p->ahl.p;     /* use pointer part of AHL struct */
   POSBLK *posblk_p = (POSBLK *)pac_p->dad.p;  /* Saved by YASP */
   SRAREA *sra_p = (SRAREA *)srarea_p;
   int rc;

   if ((!f_p) || ((f_p != sra_p->cur_fyle_p) && (!set_current(f_p, sra_p)))) {
      return(8);                         /* Bad Handle */
   }
   if (f_p != sra_p->term_fyle_p) {      /* not for Terminal! */
      if (posblk_p->is.at_eof) {
         f_p->buf_size = -1;             /* EOF reached */
      }else {
         if ((rc = fsetpos(f_p->fd, &posblk_p->fpos)) != 0) {
            f_p->buf_size = -1;          /* avoid further reads */
            return(rc + 100);
         }
         f_p->is.first = posblk_p->is.first;
         f_p->is.truncated = posblk_p->is.truncated;
         f_p->buf_size = FILE_BUFSIZE;
      }
   }
   return(0);
}

/*-------------------------------------------------------------------sr_utopr-+
| Function Name: sr_utopr                                                     |
|                                                                             |
| Functional Description: Open a Utility file for reading.                    |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0  Normal (pac_p->ahl.p = FILE pointer)                             |
|         6: Cannot open                                                      |
+----------------------------------------------------------------------------*/
int sr_utopr(PAC *pac_p)
{
   FILE *fd;

   if (!(fd=fopen((char *)pac_p->ahl.p, "rb"))) {
      return(6);
   }else {
      pac_p->ahl.p = fd;
      return(0);
   }
}

/*-------------------------------------------------------------------sr_utopw-+
| Function Name: sr_utopw                                                     |
|                                                                             |
| Functional Description: Open a Utility file for writing.                    |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0  Normal (pac_p->ahl.p = FILE pointer)                             |
|         6: Cannot open                                                      |
+----------------------------------------------------------------------------*/
int sr_utopw(PAC *pac_p)
{
   FILE *fd;

   if (!(fd=fopen((char *)pac_p->ahl.p, "wb"))) {
      return(6);
   }else {
      pac_p->ahl.p = fd;
      return(0);
   }
}

/*--------------------------------------------------------------------sr_utrd-+
| Function Name: sr_utrd                                                      |
|                                                                             |
| Functional Description: Read pac.dad.len bytes from file pac.ahl.i to       |
|                         buffer pac.dad.p.  This function is valid only for  |
|                         utility files.                                      |
|                                                                             |
| IMPLEMENTATION NOTES:                                                       |
|    Bug in OS2 V1.1: read cannot read 0 bytes !                              |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0     Normal                                                        |
|         8     Invalid Access-AHL                                            |
+----------------------------------------------------------------------------*/
int sr_utrd(PAC *pac_p)
{
   uint count = pac_p->dad.len;

   if (count && (fread(pac_p->dad.p, 1, count, pac_p->ahl.p) != count)) {
      return(8);
   }else {
      return(0);
   }
}

/*-------------------------------------------------------------------sr_utwrt-+
| Function Name: sr_utwrt                                                     |
|                                                                             |
| Functional Description: Write pac.dad.len bytes from pac.dad.p to file      |
|                         pac.ahl.i.  This function is valid only for         |
|                         utility files.                                      |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0     Normal                                                        |
|         8     Invalid Access-AHL                                            |
+----------------------------------------------------------------------------*/
int sr_utwrt(PAC *pac_p)
{
   uint count = pac_p->dad.len;

   if (count && (fwrite(pac_p->dad.p, 1, count, pac_p->ahl.p) != count)) {
      return(8);
   }else {
      return(0);
   }
}

/*--------------------------------------------------------------------sr_utcl-+
| Function Name: sr_utcl                                                      |
|                                                                             |
| Functional Description: Close a Utility file.                               |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0:    OK                                                            |
|         8:    Cannot close                                                  |
+----------------------------------------------------------------------------*/
int sr_utcl(PAC *pac_p)
{
   return (fclose((FILE *)pac_p->ahl.p) ? 8 : 0);
}

/*--------------------------------------------------------------------sr_allo-+
| Function Name: sr_allo                                                      |
|                                                                             |
| Functional Description: Allocate memory using standard malloc.              |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0     Storage was allocated                                         |
|        10     Storage was not allocated                                     |
+----------------------------------------------------------------------------*/
int sr_allo(PAC *pac_p)
{
   static uchar *noheap = "Out of heap space!\n";  /* Non NLS !!! */

   if (!pac_p->stg.len) return(0); /* In OS2 DosAllocSeg(0) means 65536 ! */
   if (!(pac_p->stg.p = malloc(pac_p->stg.len))) {
      fwrite(noheap,strlen(noheap),1,stderr);
      return(10);
   }else {
      return(0);
   }
}

/*--------------------------------------------------------------------sr_free-+
| Function Name: sr_free                                                      |
|                                                                             |
| Functional Description: Free memory allocated from the system with sr_allo. |
|                                                                             |
| Parameters: pac_p    address of PAC                                         |
|                                                                             |
| Return: 0: Normal (Storage was freed)                                       |
|        10: Storage was not freed                                            |
+----------------------------------------------------------------------------*/
int sr_free(PAC *pac_p)
{
   if (!pac_p->stg.len) return(0);
   free(pac_p->stg.p);
   return(0);
}

/*---------------------------------------------------------------------sr_msg-+
| Function Name: sr_msg                                                       |
|                                                                             |
| Functional Description: Retrieve a message from the parser "ysp.txt" file   |
|                         and print to the error log file.                    |
|                         Return "Abandon Ship" if parser error severity      |
|                         is a terminating error after the last message.      |
|                                                                             |
| Parameters: pac_p       address of PAC                                      |
|                                                                             |
| Return: 0  Normal                                                           |
|         10 END_RESUME_RC (code or parser error too severe)                  |
+----------------------------------------------------------------------------*/
#define DATAAREA_SIZE 200
int sr_msg(PAC *pac_p, void *sra_p)
{
   uchar dataarea[DATAAREA_SIZE];
   ushort count;
   uchar *sev_strng;

   if (pac_p->errcod==pac_p->msgcod) {   /* for the main message */
      switch (pac_p->errsev) {
      case ERH_I_SEV:
         sev_strng = "Informational: ";
         break;
      case ERH_W_SEV:
         sev_strng = "Error: ";
         break;
      case ERH_E_SEV:
         sev_strng = "Severe error: ";
         break;
      case ERH_S_SEV:
         sev_strng = "Unrecoverable error: ";
         break;
      case ERH_T_SEV:
         sev_strng = "Terminating error: ";
         break;
      }
   }else {
      sev_strng = "";
   }
   GetMessage(
      (ADLEN *)pac_p->dad.p,       /* Table of adlen to variables to insert */
      pac_p->dad.len,              /* Number of variables to insert */
      sev_strng,                   /* severity string */
      dataarea,                    /* Buffer address to return message */
      DATAAREA_SIZE,               /* Length of the buffer */
      pac_p->msgcod,               /* Number of the message */
      YASP_MSGFILE,                /* Message file name */
      &count,                      /* Length of the returned message */
      &((SRAREA *)sra_p)->msg_pp   /* Messages template anchor */
   );
   fwrite(dataarea, count, 1, stderr);
   dataarea[count] = '\0';
   if (pac_p->msglast) {
      /*      fwrite("--------\n",9,1,stderr);*/   /* for tests: sloppy... */
      if (pac_p->errsev == ERH_T_SEV) return(10);
   }
   return(0);
}

/******************************************************************************
*  "DosGetMessage" functions                                                  *
******************************************************************************/

/*STATIC-----------------------------------------------------------GetMessage-+
| Format a message                                                            |
+----------------------------------------------------------------------------*/
static uint GetMessage(
   ADLEN  *adlen_p,          /* Table of adlens of variables to insert */
   ushort  adlen_count,      /* Number of variables */
   uchar  *sev_strng,        /* Severity String (to be put after message id) */
   uchar  *buf,              /* data area (where message is returned) */
   ushort  bufsize,          /* length of data area */
   ushort  msgid,            /* number of the message requested */
   uchar  *filespec,         /* message path and file name */
   ushort *msglen_p,         /* length of message (returned) */
   uchar  ***msg_ppp)        /* Where to put the messages template */
{
   static uchar *nomsgf = "Cannot open message file: ";  /* Non NLS !!! */
   char *s_p, *c_p, *v_p;
   ushort totlen, varlen;
   int i;

   if ((!*msg_ppp)&&(!(*msg_ppp = init_message(filespec)))) {
      fwrite(nomsgf,strlen(nomsgf),1,stderr);
      fwrite(filespec,strlen(filespec),1,stderr);
      exit(111);
   }
   totlen=(*msg_ppp)[msgid+1] - (uchar *)(s_p = (*msg_ppp)[msgid]) - 8;
   varlen = strlen(sev_strng);
   memcpy(buf, s_p, 9);            /* "YSPxxxx: " */
   if (varlen) memcpy(buf+9, sev_strng, varlen);
   s_p += 8;                       /* s_p is one byte behind */
   c_p = buf + 8 + varlen;         /* so is c_p */
   ++bufsize;
   while ((--totlen)&&(bufsize)) {
      if ((*++s_p != '%') || (totlen==1) || (!isdigit(*(s_p+1))) ||
         ((uint)(i=*(s_p+1) - '0') > adlen_count) || (--i < 0)) {
         if (--bufsize != 0) *++c_p = *s_p;
      }else {
         v_p = (adlen_p + i)->p;
         varlen = (adlen_p + i)->len;
         while ((varlen--)&&(--bufsize)) *++c_p = *v_p++;
         --totlen;
         ++s_p;
      }
   }
   *msglen_p = (uchar *)c_p+1 - buf;
   return(0);
}

/*STATIC---------------------------------------------------------init_message-+
| Create an array of pointers to each message.                                |
| First entry is the beginning of the message array (to free it)              |
+----------------------------------------------------------------------------*/
static uchar **init_message(uchar *filespec)
{
   FILE *fd;
   uchar *c_p, *databuf;
   uint databuf_maxsize, databuf_remsize;
   uint msglngt_maxsize, rec_len, msg_count, msg_max;
   uint *msglngt, *i_p;
   uchar **msgptr, **c_pp;
   uchar buffer[FILENAME_MAX+1];

   if ((!find_file(filespec,buffer)) ||
      (!(fd=fopen((char *)buffer, "r")))) {
      return((uchar **)0);                     /* cannot access the file */
   }
   c_p = databuf = malloc(databuf_remsize=databuf_maxsize=100 * DATAAREA_SIZE);
   i_p = (msglngt = malloc(msglngt_maxsize=100*sizeof(uint))) - 1;
   msg_count = 0;
   msg_max = 100;
   if (!(fgets(c_p,DATAAREA_SIZE,fd))) return((uchar **)0); /* Discard 1st */
   for (;;) {
      if (!databuf) return(uchar **)0;
      if (!(fgets(c_p,DATAAREA_SIZE,fd))) {
         if (feof(fd)) break;
         return(uchar **)0;                    /* fgets failed */
      }
      if ((rec_len = strlen(c_p)) >= 10) {     /* "YSPxxxxW: " */
         memmove(c_p+7, c_p+8, rec_len - 7);   /* remove this stupid 'W' */
         --rec_len;
      }
      if (++msg_count >= msg_max) {
         msglngt=realloc(msglngt,msglngt_maxsize += 100*sizeof(uint *));
         msg_max += 100;
         i_p = msglngt + msg_count - 2;
      }
      if (!msglngt) return(uchar **)0;         /* malloc failed */
      if ((databuf_remsize -= (*++i_p = rec_len)) < DATAAREA_SIZE) {
         databuf = realloc(databuf, databuf_maxsize += 100 * DATAAREA_SIZE);
         databuf_remsize += 100 * DATAAREA_SIZE;
         c_p =  databuf + (databuf_maxsize - databuf_remsize);
      }else {
         c_p += *i_p;
      }
   }
   fclose(fd);
   if ((!(databuf = realloc(databuf, databuf_maxsize - databuf_remsize))) ||
      (!(c_pp = msgptr = malloc((++msg_count) * sizeof(uchar *))))) {
      return((uchar **)0);
   }
   for (*c_pp = databuf, i_p = msglngt - 1; --msg_count != 0; ++c_pp) {
      *(c_pp+1) = *c_pp + *++i_p;
   }
   free(msglngt);
   return(msgptr);
}

/*STATIC----------------------------------------------------------sr_ehsh_ini-+
|Init the hash table - Keep the hashsize in hashtable[-1]                     |
+----------------------------------------------------------------------------*/
static void * sr_ehsh_ini(uint hashsize)
{
   sr_EHTBE *hashtable;
   int i;

   hashtable = (sr_EHTBE *)malloc((2+hashsize)*sizeof(sr_EHTBE));
   hashtable++->size = hashsize;
   for (i=0;i<HASHSIZE;i++)
     ((sr_EHTBE *)hashtable + i)->ehsh_p = (sr_EHSH *)NULL;
   return((void *)hashtable);
}

/*STATIC----------------------------------------------------------sr_ehsh_acc-+
|Access a member and, if not found:                                           |
|     - if create_it is TRUE: create a new one, zeroe ehsh_p->var_p           |
|     - if create_it is FALSE: return a NULL ptr                              |
|Returns ehsh_p                                                               |
|The collision list is kept in alphabetic order, to speed up the process.     |
|See also the implementation note in ety_deco (ODTENTY.C).                    |
+----------------------------------------------------------------------------*/
static sr_EHSH * sr_ehsh_acc(
   void  *hashtable,
   uchar *var_name_p,
   boolean create_it)
{
   register uchar   *c_p;
   register sr_EHSH **ehsh_pp;
   int hashval, cond;
   uint i;
   sr_EHSH *ehsh_p;

   /*----------------------------------------+
   |point the EHSH pointer in the hash table |
   +----------------------------------------*/
   for (hashval=0, c_p = var_name_p; *c_p; hashval += *c_p++);

   /*---------------+
   |look for a match|
   +---------------*/
   for (
      ehsh_pp = &(((sr_EHTBE *)hashtable + hashval%HASHSIZE)->ehsh_p);
      ;
      ehsh_pp = &(*ehsh_pp)->next ) {
      if ((!*ehsh_pp)||                         /* No more ehsh */
         ((cond=strcmp(var_name_p,(*ehsh_pp)->name_p)) < 0)) {
         if (!create_it) return((sr_EHSH *)0);
         ehsh_p = malloc(sizeof(sr_EHSH) + (i=1+strlen(var_name_p)));
         ehsh_p->next = *ehsh_pp;
         ehsh_p->var_p = (void *)0;
         ehsh_p->name_p = (uchar *)ehsh_p + sizeof(sr_EHSH);
         memcpy(ehsh_p->name_p,var_name_p,i);
         *ehsh_pp = ehsh_p;
         break;
      }else if (!cond) {                       /* match found */
         ehsh_p = *ehsh_pp;
         break;
      }
   }
   return(ehsh_p);
}

/*STATIC----------------------------------------------------------sr_ehsh_fnd-+
|Get the structure associated to a member,                                    |
|or returns a NULL pointer if not found.                                      |
+----------------------------------------------------------------------------*/
static void * sr_ehsh_fnd(
   void *hashtable,
   uchar *name_p)
{
   sr_EHSH *ehsh_p;
   if (!(ehsh_p = sr_ehsh_acc(hashtable,name_p,FALSE))) {
      return((void *)0);
   }else {
      return(ehsh_p->var_p);
   }
}

/*STATIC----------------------------------------------------------sr_ehsh_lnk-+
|Link a structure to a member in a given hash table.                          |
|If the member is new, create the structure and zero it.                      |
|Return a pointer to the structure.                                           |
|ehsh_p->var_p stores the address of the structure.                           |
+----------------------------------------------------------------------------*/
static void * sr_ehsh_lnk(
   void  *hashtable,
   uchar *name,
   uint   size)
{
   sr_EHSH *ehsh_p;

   if (!(ehsh_p = sr_ehsh_open(hashtable,name))->var_p) ehsh_p->var_p=malloc(size);
   return(ehsh_p->var_p);
}
/*****************************************************************************/

