/*******************************************************************************
* 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 / General Library                                    *
* STATUS           = Version 0, Release 1, Level 35 and up                     *
* COMPONENT NAME   = skmgr.c                                                   *
* DESCRIPTIVE NAME = Strings Safekeeping                                       *
* DATE_WR          = 06/23/1987                                                *
* AUTHOR           = Pierre G. Richard - PETRUS at YKTVMX                      *
*                                                                              *
* FUNCTIONAL DESCRIPTION =                                                     *
* This module stores characters string in contiguous large areas, to:          *
*  - avoid the overhead to store them individually,                            *
*  - permit creation on an external medium of a unique record of all the       *
*    strings that have been declared here.                                     *
*                                                                              *
* For this purpose, when the caller needs nbytes of storage for a given        *
* string, it calls "sk_park" that stores the string and return an OFS_type     *
* value.                                                                       *
*                                                                              *
* The OFS_type value is unique to each string.  After all strings have been    *
* processed, and written in unique record on the external media, OFS values    *
* will be the offset of each string within the beginning of the record.        *
*                                                                              *
* "sk_point" allow recovery of the address of a string, given its OFS value.   *
*                                                                              *
* Note that only string of chars can be handled this way, no boundary          *
* alignment is done by this module.                                            *
*                                                                              *
* CHANGE HISTORY =                                                             *
* Number Date     Inits    Change & reason (brief)                             *
*      1 03/05/88 PETRUS   Keep up the count of the size used, so sk_out is    *
*                          able to write it first.                             *
*      2 11/10/88 PETRUS   Value 0 for offset is reserved (empty string).      *
*      3 01/28/89 PETRUS   Make it reentrant                                   *
*      4 04/03/92 PETRUS   Fixed a Microsoft C6 bug with aliasing              *
*      5 10/07/92 PETRUS   Added the lst_ functions                            *
*******************************************************************************/

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

#define room_left room_size  /* reuses the same field */

/*******************************************************************************
*  lst_ functions                                                              *
*******************************************************************************/

/*---------------------------------------------------------------------lst_add-+
| Add a new entry to a list                                                    |
|                                                                              |
| Note: the list is kept in alphabetic order.   Duplicates are suppressed.     |
+-----------------------------------------------------------------------------*/
void P_FCT lst_add(GA_P gbla_p, ADLEN *adl_p, uchar *entry_p, int entry_len)
{
   uchar  *c_p;

   memcpy(c_p = malloc(entry_len + 1), entry_p, entry_len);
   *(c_p + entry_len) = '\0';
   if (!adl_p->len) {
      adl_p->len = 1;
      *((uchar **)(adl_p->_.p = malloc(sizeof(uchar *)))) = c_p;
   }else {
      int comp_result;
      int nb = adl_p->len + 1;
      uchar **c_pp = (uchar **)adl_p->_.p - 1;

      while (--nb) {
         comp_result = strcmp(c_p, *(++c_pp));
         if (comp_result  < 0) break;      /*  c_p > already_listed  */
         if (comp_result == 0) {           /*  c_p == already_listed */
            free(c_p);
            return;
         }
      }
      adl_p->_.p = realloc(adl_p->_.p, ++adl_p->len * sizeof(uchar *));
      c_pp = (uchar **)(adl_p->_.p) + adl_p->len - nb - 1;
      if (nb) memmove(c_pp+1, c_pp, nb * sizeof(uchar *));
      *c_pp = c_p;
   }
}

/*---------------------------------------------------------------------lst_del-+
| Remove an entry from the list                                                |
| Returns TRUE if the entry was sucessfully removed.                           |
+-----------------------------------------------------------------------------*/
boolean P_FCT lst_del(GA_P gbla_p, ADLEN *adl_p, uchar *c_p)
{
   boolean is_found = FALSE;
   int nb = adl_p->len;

   if (nb) {
      int comp_result;
      uchar **c_pp = (uchar **)adl_p->_.p -1;

      while (nb--) {
         comp_result = strcmp(c_p, *(++c_pp));
         if (comp_result  < 0) break;      /*  c_p > already_listed  */
         if (comp_result == 0) {           /*  c_p == already_listed */
            free(*c_pp);
            if (--adl_p->len == 0) {
               free(adl_p->_.p);
            }else {                        /* shrink */
               if (nb) memmove(c_pp, c_pp+1, nb * sizeof(uchar *));
               adl_p->_.p = realloc(adl_p->_.p, adl_p->len * sizeof(uchar *));
            }
            is_found = TRUE;
            break;
         }
      }
   }
   return(is_found);
}

/*---------------------------------------------------------------------lst_tvr-+
| Traverse the list and call a check_function for each entry found.            |
| If the check function returns TRUE, the entry is erased.                     |
+-----------------------------------------------------------------------------*/
void P_FCT lst_tvr(
   GA_P gbla_p, ADLEN *adl_p,
   boolean (P_FCT *check_fct)(GA_P gbla_p, uchar *entry_p)
) {
   int nb = adl_p->len;

   if (nb) {
      uchar  **c_pp = (uchar **)(adl_p->_.p) - 1;

      while (nb--) {
         if (check_fct(gbla_p, *(++c_pp))) {
           free(*c_pp);
           if (nb) memmove(c_pp, c_pp+1, nb * sizeof(uchar *));
           --c_pp;
           --adl_p->len;
         }
      };
      if (!adl_p->len) {
         free(adl_p->_.p);
         adl_p->_.p = NULL;  /* for safety */
      }else {                /* shrink */
         adl_p->_.p = realloc(adl_p->_.p, adl_p->len * sizeof(uchar *));
      }
   }
}

/*******************************************************************************
*  sk_ functions                                                               *
*******************************************************************************/

/*---------------------------------------------------------------------sk_park-+
|Park a new string - return its OFS.                                           |
|Offset zero is reputed to have a null string.                                 |
|c_p is a NULL pointer if a reservation is just needed.                        |
+-----------------------------------------------------------------------------*/
OFS P_FCT sk_park(GA_P gbla_p, uchar *c_p, uint len)
{
#define SKMG_BGAL  9           /* strbuf overflow: see odtcomp.c */
   register PARKING *what_park = GA(this_park);
   OFS this_ofs;

   if (len > PARKING_LOT_SIZE) exit(104);
#if (defined(IBMC370))
   if (len > (((MAX_ALLOC <<1) >> 1)  - GA(parks_ofs))) {   /* IBM C370 Bug */
#else
   if (len > (MAX_ALLOC  - GA(parks_ofs))) {
#endif
      errorh(gbla_p,BIGALLOC,ERH_T_SEV,SKMG_BGAL);
   }

   if ((!what_park) || (len > what_park->room_left)) {
      if (what_park) {
         what_park = (PARKING *)
         realloc(what_park,sizeof(PARKING) - what_park->room_left);
         what_park->room_size = PARKING_LOT_SIZE - what_park->room_left;
      }
      what_park = (PARKING *)malloc(sizeof(PARKING));
      what_park->prev = GA(this_park);
      what_park->room_left = PARKING_LOT_SIZE;
      what_park->ofs_start = GA(parks_ofs);
      GA(this_park) = what_park;         /* new park */
      if (!GA(parks_ofs)) {              /* at first time... */
         this_ofs = GA(parks_ofs) = 1;
         --(what_park->room_left);
         *what_park->lot = '\0';         /* create a null string */
         return(sk_park(gbla_p,c_p,len));
      }
   }
   this_ofs = GA(parks_ofs);
   if (c_p) memcpy(what_park->lot + (this_ofs-what_park->ofs_start),c_p,len);
   GA(parks_ofs) += len;
   what_park->room_left -= len;
   return(this_ofs);
}

/*--------------------------------------------------------------------sk_point-+
|Given a parked string OFS value, return a pointer to the string.              |
+-----------------------------------------------------------------------------*/
uchar * P_FCT sk_point(GA_P gbla_p, OFS ofs)
{
   register PARKING *what_park;

   for (
      what_park=GA(this_park);
      what_park->ofs_start > ofs;
      what_park = what_park->prev
   );
   return(what_park->lot + (ofs - what_park->ofs_start));
}

/*----------------------------------------------------------------------sk_out-+
|Output all strings in a whole unique record, preceeded by its length.         |
|output is a "write" kind of function                                          |
+-----------------------------------------------------------------------------*/
#if (defined(C6_00))                                 /* Microsoft C6.00 bug */
/* Optimizer bug: w1_park=w2_park->prev; w2_park_prev=w3_park; wrong sequence */
#pragma optimize("a",off)
#endif
void P_FCT sk_out(
   GA_P gbla_p,
   int (P_FCT *output)(GA_P, AHL, void const *, uint),
   AHL  ahl)
{
   register PARKING *w1_park, *w2_park, *w3_park;

   (*output)(gbla_p,ahl,(void *)(&GA(parks_ofs)),sizeof(uint));
   w1_park = GA(this_park);
   w1_park->room_size = PARKING_LOT_SIZE - w1_park->room_left;
   w2_park = (PARKING *)0;
   do {                              /* Inverse the pointer chain */
      w3_park = w2_park;             /* w3 is the next park */
      w2_park = w1_park;             /* w2 is the current park */
      w1_park = w2_park->prev;       /* w1 is the previous park */
      w2_park->prev = w3_park;       /* so "prev" is changed to "next" */
   }while (w1_park);
   do {
      (*output)(gbla_p,ahl,(void *)w2_park->lot,w2_park->room_size);
      w1_park = w2_park;
      w2_park = w2_park->prev;       /* rather be named "next" */
      free(w1_park);
   }while (w2_park);

   GA(this_park) = (PARKING *)0;     /* Reset initial values */
   GA(parks_ofs) = (OFS)0;
}
#if (defined(C6_00))                            /* Microsoft C6.00 bug (end) */
#pragma optimize("",on)
#endif
/******************************************************************************/
