Newer
Older
ubixos / src / tools / make_kernel_string_pool.c
/*
   "make_kernel_string_pool.c"

   created by: grayspace aka J. Leveille
   for: UbixOS Project
   date: July 3, 2002

   purpose: - tool to create string pools for in kernel use
            - will generate the source for including a global
            set of ASCII (for now) strings into the kernel
            to avoid memory wastage for kernel output strings
            for long lists of things such as device descriptions

   NOTEs:
      - for now only ASCII is supported

   TODO:
      - expand to support unicode
      - use huffman encoding instead

   $Id$
*/

/*
   SubStringfile format:
      - each on it's own line, each unique substring
*/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "..\grayspace-misc\gsdefines.h"

typedef struct tagSUBSTR
{
   // string
   char * p_str;

   // frequency
   int freq;

   // index
   int idx;
}
SUBSTR;


typedef struct tagSUBSTRLIST
{
   SUBSTR * p_substrsmem;
   SUBSTR ** pp_substrs;
   int numsubstrs;
}
SUBSTRLIST;

typedef struct tagSTRLIST
{
   char ** pp_strs;
   int numstrs;
}
STRLIST;

// current open string file
FILE * g_p_file_substrs = NULL;

// main/current list of strings
STRLIST g_mainstrs = { NULL, 0 };

// current open sub string file
FILE * g_p_file_strs = NULL;

// main/current list of sub strings
SUBSTRLIST g_mainsubstrs = { NULL, 0 };

// temporary lists of substrings
SUBSTRLIST g_tempsubstrs1 = { NULL, 0 };
SUBSTRLIST g_tempsubstrs2 = { NULL, 0 };

// free all memory associated with the string list
static void Free_StringList( STRLIST * p_sl )
{
   int i;
   if( p_sl->pp_strs != NULL )
   {
      for( i = 0; i < p_sl->numstrs; i++ )
      {
         if( p_sl->pp_strs[i] != NULL )
         {
            free( p_sl->pp_strs[i] );
            p_sl->pp_strs[i] = NULL;
         }
      }

      free( p_sl->pp_strs );
      p_sl->pp_strs = NULL;
      p_sl->numstrs = 0;
   }
}

// free all memory associated with the sub string list
static void Free_SubStringList( SUBSTRLIST * p_ssl )
{
   int i;
   if( p_ssl->pp_substrs != NULL )
   {
      free( p_ssl->pp_substrs );
      p_ssl->pp_substrs = NULL;
   }

   if( p_ssl->p_substrsmem != NULL )
   {
      for( i = 0; i < p_ssl->numsubstrs; i++ )
      {
         if( p_ssl->p_substrsmem[i].p_str != NULL )
         {
            free( p_ssl->p_substrsmem[i].p_str );
            p_ssl->p_substrsmem[i].p_str = NULL;
         }
      }

      free( p_ssl->p_substrsmem );
      p_ssl->p_substrsmem = NULL;
      p_ssl->numsubstrs = 0;
   }
}

// allocates only memory for sub string structures
// and pointers *not* the strings
// also:
//    - assigns sub-string pointers
//    - sets string pointers to NULL for safety
//    - sets all frequencies to 0
//    - sets all idxs to -1
// note:
//    - destroys previous contents if exists
static void MakeNew_SubStringList( SUBSTRLIST * p_ssl, int numsubstrs )
{
   int i;
   
   Free_SubStringList( p_ssl );
   p_ssl->numsubstrs = numsubstrs;
   p_ssl->pp_substrs
      = (SUBSTR **) malloc( sizeof(SUBSTR *) * p_ssl->numsubstrs );
   p_ssl->p_substrsmem
      = (SUBSTR *) malloc( sizeof(SUBSTR) * p_ssl->numsubstrs );

   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      p_ssl->pp_substrs[i] = p_ssl->p_substrsmem + i;
      p_ssl->pp_substrs[i]->p_str = NULL;
      p_ssl->pp_substrs[i]->freq = 0;
      p_ssl->pp_substrs[i]->idx = -1;
   }
}

// compare two pointers to strings
static int CompareSubStringPtr( const void * p1, const void * p2 )
{
   const SUBSTR * pss1 = *((const SUBSTR **) p1);
   const SUBSTR * pss2 = *((const SUBSTR **) p2);
   
   return strcmp( pss1->p_str, pss2->p_str );
}

// sorts a sub string list into alphabetical order
// list cannot be empty for success
// returns 1 on success, 0 on failure
static int Sort_SubStringList( SUBSTRLIST * p_ssl )
{
   if( p_ssl->numsubstrs && p_ssl->pp_substrs )
   {
      // sort by pointer
      qsort(   p_ssl->pp_substrs,
               p_ssl->numsubstrs,
               sizeof(SUBSTR *),
               CompareSubStringPtr  );

      return 1;
   }

   return 0;
}

// compare two pointers to strings
static int CompareSubStringPtrByFreq( const void * p1, const void * p2 )
{
   const SUBSTR * pss1 = *((const SUBSTR **) p1);
   const SUBSTR * pss2 = *((const SUBSTR **) p2);
   
   return pss2->freq - pss1->freq;
}

// sorts a sub string list into alphabetical order
// list cannot be empty for success
// returns 1 on success, 0 on failure
static int Sort_SubStringListByFreq( SUBSTRLIST * p_ssl )
{
   if( p_ssl->numsubstrs && p_ssl->pp_substrs )
   {
      // sort by pointer
      qsort(   p_ssl->pp_substrs,
               p_ssl->numsubstrs,
               sizeof(SUBSTR *),
               CompareSubStringPtrByFreq  );

      return 1;
   }

   return 0;
}

static SUBSTR * Find_SubStringByStr( SUBSTRLIST * p_ssl, char * p_str )
{
   SUBSTR key;
   SUBSTR * p_key;
   SUBSTR ** pp_retval;

   p_key = &key;
   key.p_str = p_str;

   if( p_ssl->numsubstrs && p_ssl->pp_substrs )
   {
      pp_retval
         = bsearch(  &p_key,
                     p_ssl->pp_substrs,
                     p_ssl->numsubstrs,
                     sizeof(SUBSTR *),
                     CompareSubStringPtr  );

      if( pp_retval )
      {
         return *pp_retval;
      }
   }

   return NULL;
}



// given an open string file, creates a 'STRLIST' with its
// contents (the file should contain a list of strings, one on each
// line and in plain text)
//
// on failure:
//    - frees/empties the given list
//    - returns 0
// on success:
//    - returns 1
static int Create_StringList( STRLIST * p_sl, FILE * p_file )
{
   int i;
   int numlines;
   char buff[8192];
   char * p_curbuff;

   // destroy the current list
   Free_StringList( p_sl );

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // count our lines:
   numlines = 0;
   while( fgets( buff, sizeof(buff)/sizeof(char) - 1, p_file ) )
   {
      numlines++;
   }

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // read each string from file
   p_sl->numstrs = numlines;
   p_sl->pp_strs = (char **) malloc( sizeof(char *) * p_sl->numstrs );
   for( i = 0; i < p_sl->numstrs; i++ )
   {
      if( fgets( buff, sizeof(buff)/sizeof(char) - 1, p_file ) )
      {
         // strip newline
         p_curbuff = strtok( buff, "\n" );
         if( p_curbuff )
         {
            // allocate string
            p_sl->pp_strs[i]
                  = (char *) malloc( sizeof(char) * (strlen( p_curbuff ) + 1) );
            // copy
            strcpy( p_sl->pp_strs[i], p_curbuff );
            continue;
         }
      }

      goto failbail;
   }

   // success
   return 1;

failbail:
   Free_StringList( p_sl );
   printf( "[Create_StringList] Error" );
   return 0;
}

// given an open sub string file, creates a 'SUBSTRLIST' with its
// contents (the file should contain a list of unique substrings
// in alphabetical order)
//
// on failure:
//    - frees/empties the given list
//    - returns 0
// on success:
//    - returns 1
static int Create_SubStringList( SUBSTRLIST * p_ssl, FILE * p_file )
{
   int i;
   int numlines;
   char buff[1024];
   char * p_curbuff;

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // count our lines:
   numlines = 0;
   while( fgets( buff, sizeof(buff)/sizeof(char) - 1, p_file ) != NULL )
   {
      numlines++;
   }

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // re-allocate our substring list:
   MakeNew_SubStringList( p_ssl, numlines );
   
   // read each substring
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      if( fgets( buff, sizeof(buff)/sizeof(char) - 1, p_file ) != NULL )
      {
         // strip newline
         p_curbuff = strtok( buff, "\n" );
         if( p_curbuff )
         {
            // allocate and copy
            p_ssl->pp_substrs[i]->p_str
               = (char *) malloc( sizeof(char) * (strlen( p_curbuff ) + 1) );
            strcpy( p_ssl->pp_substrs[i]->p_str, p_curbuff );
            continue;
         }
      }

      goto failbail;
   }

   // sort the substrings into alphabetical order
   if( Sort_SubStringList( p_ssl ) )
   {
      // success:
      return 1;
   }

failbail:
   Free_SubStringList( p_ssl );
   printf( "[Create_SubStringList] Error" );
   return 0;
}

// Given an open file which contains plain text, builds a sub string list
// of unique sub strings into the list given as 'p_ssl'
// NOTE: destroys/frees the contents of 'p_ssl'
// regardless of success or failure
const static char g_whitespace[] = " \t\n";
static int BuildFromFile_SubStrings( SUBSTRLIST * p_ssl, FILE * p_file )
{
   int i;
   int numwords;
   char buff[1024];
   char * p_curbuff;

   // destroy the current list
   Free_SubStringList( p_ssl );

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // count our words:
   numwords = 0;
   while( ( p_curbuff
               = fgets( buff,
                        sizeof(buff)/sizeof(char) - 1,
                        p_file                           )  ) != NULL   )
   {
      p_curbuff = strtok( p_curbuff, g_whitespace );
      while( p_curbuff )
      {
         numwords++;
         p_curbuff = strtok( NULL, g_whitespace );
      }
   }

   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // read each substring from words in file
   MakeNew_SubStringList( p_ssl, numwords );
   for( i = 0; i < p_ssl->numsubstrs; )
   {
      if( ( p_curbuff
               = fgets( buff,
                        sizeof(buff)/sizeof(char) - 1,
                        p_file                           )  ) != NULL   )
      {
         p_curbuff = strtok( p_curbuff, g_whitespace );
         while( p_curbuff )
         {
            p_ssl->pp_substrs[i]->p_str
               = (char *) malloc( sizeof(char) * (strlen( p_curbuff ) + 1) );
            strcpy( p_ssl->pp_substrs[i]->p_str, p_curbuff );
            i++;
            p_curbuff = strtok( NULL, g_whitespace );
         }
         continue;
      }

      goto failbail;
   }

   // sort the substrings into alphabetical order
   if( Sort_SubStringList( p_ssl ) )
   {
      // success:
      return 1;
   }

failbail:
   Free_SubStringList( p_ssl );
   printf( "[BuildFromFile_SubStrings] Error" );
   return 0;
}


// given a sub string list and a string list
// refreshes the frequency of each sub string from the strings
// in the string list
static int UpdateFrequencies_SubStrings(  SUBSTRLIST * p_ssl,
                                          const STRLIST * p_sl    )
{
   int i;
   char buff[1024];
   char * p_curbuff;
   SUBSTR * p_curss;

   // clear all current frequencies
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      p_ssl->p_substrsmem[i].freq = 0;
   }

   // parse every token in the string list
   // and update the frequency count of the
   // related sub string
   for( i = 0; i < p_sl->numstrs; i++ )
   {
      if( p_sl->pp_strs[i] )
      {
         // copy working string
         strcpy( buff, p_sl->pp_strs[i] );
         p_curbuff = strtok( buff, g_whitespace );
         while( p_curbuff )
         {
            // get related substring (*MUST* exist)
            p_curss = Find_SubStringByStr( p_ssl, p_curbuff );
            if( p_curss )
            {
               // update frequency
               p_curss->freq++;

               // next token
               p_curbuff = strtok( NULL, g_whitespace );

               // continue
               continue;
            }
            
            goto failbail;
         }

         continue;
      }

      goto failbail;
   }

   // success:
   return 1;

failbail:
   // clear all current frequencies (again)
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      p_ssl->p_substrsmem[i].freq = 0;
   }
   return 0;
}

// given a sub string list
// assigns each sub string an index
// according to the current sorting scheme
static int UpdateIndices_SubStrings( SUBSTRLIST * p_ssl )
{
   int i;

   // re-assign all current indices
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      if( p_ssl->pp_substrs[i] )
      {
         p_ssl->pp_substrs[i]->idx = i;
         continue;
      }

      goto failbail;
   }

   // success
   return 1;

failbail:
   // clear all indices
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      p_ssl->p_substrsmem[i].idx = -1;
   }

   // fail
   return 0;
}


// creates a new 'SUBSTRLIST' into 'p_ssl_dst' by
// combining the sub-strings in 'p_ssl_src1' and 'p_ssl_src2'
// then it clears/frees both 'p_ssl_src1' and 'p_ssl_src2'
// so that they are empty
// NOTE: will destroy any contents in 'p_ssl_dst' safely
static void Combine_SubStringLists( SUBSTRLIST * p_ssl_dst,
                                    SUBSTRLIST * p_ssl_src1,
                                    SUBSTRLIST * p_ssl_src2    )
{
   int src_idx_1;
   int src_idx_2;
   int src_idx;
   int dst_idx;
   SUBSTRLIST * p_ssl_src;
   
   // (re)allocate the destination list
   MakeNew_SubStringList(  p_ssl_dst,
                           p_ssl_src1->numsubstrs
                           + p_ssl_src2->numsubstrs   );

   // combine lists, preserving alphabetical order
   src_idx_1 = 0;
   src_idx_2 = 0;
   dst_idx = 0;
   while(   src_idx_1 < p_ssl_src1->numsubstrs
            && src_idx_2 < p_ssl_src2->numsubstrs  )
   {
      if( strcmp( p_ssl_src1->pp_substrs[src_idx_1]->p_str,
                  p_ssl_src2->pp_substrs[src_idx_2]->p_str  ) <= 0   )
      {
         // move string from source 1
         p_ssl_dst->pp_substrs[dst_idx]->p_str
            = p_ssl_src1->pp_substrs[src_idx_1]->p_str;
         p_ssl_src1->pp_substrs[src_idx_1]->p_str = NULL;
         dst_idx++;
         src_idx_1++;
         continue;
      }

      // move strings from source 2
      p_ssl_dst->pp_substrs[dst_idx]->p_str
         = p_ssl_src2->pp_substrs[src_idx_2]->p_str;
      p_ssl_src2->pp_substrs[src_idx_2]->p_str = NULL;
      dst_idx++;
      src_idx_2++;
   }

   // find which source still has strings
   p_ssl_src = p_ssl_src1;
   src_idx = src_idx_1;
   if( src_idx_2 < p_ssl_src2->numsubstrs )
   {
      p_ssl_src = p_ssl_src2;
      src_idx = src_idx_2;
   }

   // add remaining strings
   while( src_idx < p_ssl_src->numsubstrs )
   {
      // move strings from source
      p_ssl_dst->pp_substrs[dst_idx]->p_str
         = p_ssl_src->pp_substrs[src_idx]->p_str;
      p_ssl_src->pp_substrs[src_idx]->p_str = NULL;
      dst_idx++;
      src_idx++;
   }

   // empty source strings
   p_ssl_src1->numsubstrs = 0;
   p_ssl_src2->numsubstrs = 0;
   Free_SubStringList( p_ssl_src1 );
   Free_SubStringList( p_ssl_src2 );
}

// removes duplicate sub strings in sub string list
static void RemoveDuplicates_SubStrings( SUBSTRLIST * p_ssl )
{
   char * p_checkstr;
   int strsremoved;
   int idx;
   int dst_idx;
   int src_idx;
   
   // first pass, remove and free all duplicate strings
   // in their current position
   strsremoved = 0;
   idx = 0;
   while( idx < p_ssl->numsubstrs )
   {
      // get new check string
      p_checkstr = p_ssl->pp_substrs[idx]->p_str;
      idx++;
      
      // remove all duplicates of check string
      while( idx < p_ssl->numsubstrs )
      {
         if( strcmp( p_checkstr, p_ssl->pp_substrs[idx]->p_str ) == 0 )
         {
            // remove duplicate
            free( p_ssl->pp_substrs[idx]->p_str );
            p_ssl->pp_substrs[idx]->p_str = NULL;
            strsremoved++;
            idx++;

            // process next string
            continue;
         }

         // get new check string
         break;
      }
   }

   // second pass, re-order
   dst_idx = 0;
   src_idx = 0;
   while( src_idx < p_ssl->numsubstrs )
   {
      // find next empty string section
      while( dst_idx < p_ssl->numsubstrs )
      {
         if( p_ssl->pp_substrs[dst_idx]->p_str )
         {
            dst_idx++;
            continue;
         }
         break;
      }

      // find next non-empty string section
      src_idx = dst_idx + 1;
      while( src_idx < p_ssl->numsubstrs )
      {
         if( !p_ssl->pp_substrs[src_idx]->p_str )
         {
            src_idx++;
            continue;
         }
         break;
      }

      // move all non-empty strings into empty space
      idx = src_idx;
      while(   dst_idx < src_idx
               && idx < p_ssl->numsubstrs )
      {
         if( p_ssl->pp_substrs[idx]->p_str )
         {
            // move string
            p_ssl->pp_substrs[dst_idx]->p_str = p_ssl->pp_substrs[idx]->p_str;
            p_ssl->pp_substrs[idx]->p_str = NULL;
            dst_idx++;
            idx++;

            // process next string
            continue;
         }

         // start over
         break;
      }
   }

   // adjust number of strings in list
   p_ssl->numsubstrs -= strsremoved;
}

// moves one string list to another
// (also cleans memory a little)
// NOTES:
//    - this operation empties/frees 'p_ssl_src'
//    - this operation overwrites the contents of 'p_ssl_dst'
static void Move_SubStrings(  SUBSTRLIST * p_ssl_dst,
                              SUBSTRLIST * p_ssl_src  )
{
   int i;

   // free/clear the dest substrings
   Free_SubStringList( p_ssl_dst );

   // allocate new dest sub-strings
   MakeNew_SubStringList( p_ssl_dst, p_ssl_src->numsubstrs );

   // move the strings over
   for( i = 0; i < p_ssl_dst->numsubstrs; i++ )
   {
      p_ssl_dst->pp_substrs[i]->p_str = p_ssl_src->pp_substrs[i]->p_str;
      p_ssl_src->pp_substrs[i]->p_str = NULL;
   }

   // clear/free the source string
   p_ssl_src->numsubstrs = 0;
   Free_SubStringList( p_ssl_src );
}

// writes given string list into given file
// on success:
//    - old file contents are destroyed
//    - returns 1
// on failure:
//    - old file contents are destroyed or mangled
//    - returns 0
static int Write_Strings(  STRLIST * p_sl,
                           FILE * p_file     )
{
   int i;
   
   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // write out all strings
   for( i = 0; i < p_sl->numstrs; i++ )
   {
      if( fputs( p_sl->pp_strs[i], p_file ) != EOF )
      {
         if( fputs( "\n", p_file ) != EOF )
         {
            continue;
         }
      }

      goto failbail;
   }

   // success:
   return 1;

failbail:
   printf( "[Write_Strings] Error, cannot write to file\n" );
   return 0;
}

// writes given sub string list into given file
// on success:
//    - old file contents are destroyed
//    - returns 1
// on failure:
//    - old file contents are destroyed or mangled
//    - returns 0
static int Write_SubStrings(  SUBSTRLIST * p_ssl,
                              FILE * p_file        )
{
   int i;
   
   // go to beginning of file
   if( fseek( p_file, 0L, SEEK_SET ) )
   {
      goto failbail;
   }

   // write out all strings
   for( i = 0; i < p_ssl->numsubstrs; i++ )
   {
      if( fputs( p_ssl->pp_substrs[i]->p_str, p_file ) != EOF )
      {
         if( fputs( "\n", p_file ) != EOF )
         {
            continue;
         }
      }

      goto failbail;
   }

   // success:
   return 1;

failbail:
   printf( "[Write_SubStrings] Error, cannot write to file\n" );
   return 0;
}

// writes out and closes the current string file if possible
// and frees the current string list
// returns 1 on success, 0 on failure
int WriteAndClose_StringFile()
{
   if( g_p_file_strs )
   {
      if( Write_Strings( &g_mainstrs, g_p_file_strs ) )
      {
         // close the file
         if( fclose( g_p_file_strs ) == 0 )
         {
            // success
            g_p_file_strs = NULL;
            return 1;
         }

         printf( "[WriteAndClose_StringFile] Error closing file\n" );
         goto failbail;
      }

      printf( "[WriteAndClose_StringFile] Error, cannot write to file\n" );
      goto failbail;
   }

   printf( "[WriteAndClose_StringFile] Error, never opened \n" );

failbail:
   if( fclose( g_p_file_strs ) )
   {
      g_p_file_strs = NULL;
   }
   return 0;
}

// writes out and closes the current sub string file if possible
// and frees the current sub string list
// returns 1 on success, 0 on failure
int WriteAndClose_SubStringFile()
{
   if( g_p_file_substrs )
   {
      if( Write_SubStrings( &g_mainsubstrs, g_p_file_substrs ) )
      {
         // close the file
         if( fclose( g_p_file_substrs ) == 0 )
         {
            // success
            g_p_file_substrs = NULL;
            return 1;
         }

         printf( "[WriteAndClose_SubStringFile] Error closing file\n" );
         goto failbail;
      }

      printf( "[WriteAndClose_SubStringFile] Error, cannot write to file\n" );
      goto failbail;
   }

   printf( "[WriteAndClose_SubStringFile] Error, never opened \n" );

failbail:
   if( fclose( g_p_file_substrs ) )
   {
      g_p_file_substrs = NULL;
   }
   return 0;
}

// closes the current open string file and frees
// the current string list
// returns 1 on success, 0 on failure
int Close_StringFile()
{
   if( g_p_file_strs )
   {
      // close the file
      if( fclose( g_p_file_strs ) == 0 )
      {
         // success
         g_p_file_strs = NULL;
         return 1;
      }

      printf( "[Close_StringFile] Error closing file\n" );
      return 0;
   }

   printf( "[Close_StringFile] Error, never opened \n" );
   return 0;
}

// closes the current open sub string file and frees
// the current sub string list
// returns 1 on success, 0 on failure
int Close_SubStringFile()
{
   if( g_p_file_substrs )
   {
      // close the file
      if( fclose( g_p_file_substrs ) == 0 )
      {
         // success
         g_p_file_substrs = NULL;
         return 1;
      }

      printf( "[Close_SubStringFile] Error closing file\n" );
      return 0;
   }

   printf( "[Close_SubStringFile] Error, never opened \n" );
   return 0;
}

// given a path, opens an existing string file
// (a simple line by line list of strings)
// reads in the contents as the string list and sets
// the file as the current open string file
//
// returns 1 on success, 0 on failure
int Open_StringFile( const char * p_path )
{
   g_p_file_strs = fopen( p_path, "a+t" );
   if( g_p_file_strs )
   {
      if( Create_StringList( &g_mainstrs, g_p_file_strs ) )
      {
         // success
         return 1;
      }

      fclose( g_p_file_strs );
      printf( "[Open_StringFile] Error" );
      return 0;
   }
   
   printf( "[Open_StringFile] Error, cannot open file: %s.\n", p_path );
   return 0;
}

// given a path, opens an existing string file
// (a simple line by line list of strings)
// reads in the contents and appends them to the
// current string list and the current string file
//
// returns 1 on success, 0 on failure
// p_startidx_o <- beginning index of strings within the pool
// p_size_o <- number of strings appended
int AppendStringsFromFile( int * p_startidx_o,
                           int * p_size_o,
                           const char * p_path )
{
   char buff[8192];
   FILE * p_srcfile;
      
   if( g_p_file_strs )
   {
      p_srcfile = fopen( p_path, "rt" );
      if( p_srcfile )
      {
         // go to beginning of source file
         if( fseek( p_srcfile, 0L, SEEK_SET ) )
         {
            goto failbail;
         }

         // go to beginning of dest file
         if( fseek( g_p_file_strs, 0L, SEEK_SET ) )
         {
            goto failbail;
         }

         // count our lines:
         (*p_startidx_o) = 0;
         while( fgets( buff, sizeof(buff)/sizeof(char) - 1, g_p_file_strs ) )
         {
            (*p_startidx_o)++;
         }

         // go to end of dest file
         if( fseek( g_p_file_strs, 0L, SEEK_END ) )
         {
            goto failbail;
         }

         // copy each line from
         // the source file to the dest file
         (*p_size_o) = 0;
         while( fgets( buff, sizeof(buff)/sizeof(char) - 1, p_srcfile ) )
         {
            if( fputs( buff, g_p_file_strs ) != EOF )
            {
               (*p_size_o)++;
               continue;
            }

            goto failbail;
         }

         // flush all IO operations
         if( fflush( g_p_file_strs ) != EOF )
         {
            // cause current string list to be reloaded
            if( Create_StringList( &g_mainstrs, g_p_file_strs ) )
            {
               // close source file
               if( fclose( p_srcfile ) == 0 )
               {
                  // success
                  return 1;
               }

               printf( "[AppendStringsFromFile] Error closing file: %s\n", p_path );
               goto failbail;
            }
         }
      }
      // if( p_srcfile )
      printf( "[AppendStringsFromFile] Error, cannot open file: %s\n", p_path );
      return 0;

   }
   // if( g_p_file_strs )
   printf( "[AppendStringsFromFile] Error, String file never opened\n" );
   return 0;

failbail:
   fclose( p_srcfile );
   printf( "[AppendStringsFromFile] Error\n" );
   return 0;
}

// given a plain text files, extracts any unique sub strings
// and adds them into the current sub string list
//
// returns 1 on success, 0 on failure
int Add_SubStringsFromFile( const char * p_path )
{
   FILE * p_file = fopen( p_path, "rt" );
   if( p_file )
   {
      // build a new substring list from the file
      if( BuildFromFile_SubStrings( &g_tempsubstrs1, p_file ) )
      {
         // combine new substrings with current list
         // into a new temporary list
         // NOTE: this operation empties/frees
         //    both 'g_tempsubstrs1' and 'g_mainsubstrs'
         Combine_SubStringLists( &g_tempsubstrs2,
                                 &g_tempsubstrs1,
                                 &g_mainsubstrs    );

         // remove duplicates from new temporary list
         RemoveDuplicates_SubStrings( &g_tempsubstrs2 );

         // move new temporary list into main list
         // (also cleans memory a little)
         // NOTE: this operation empties/frees 'g_tempsubstrs2'
         Move_SubStrings( &g_mainsubstrs, &g_tempsubstrs2 );
      
         // close the file
         fclose( p_file );

         // success
         return 1;
      }

      fclose( p_file );
      printf( "[Add_SubStringsFromFile] Error" );
      return 0;
   }
   
   printf( "[Add_SubStringsFromFile] Error, cannot open file: %s.\n", p_path );
   return 0;
}

// given a path, opens an existing sub string file
// (shoudl conatin an alphabetically sorted list of unique substrings)
// reads in the contents as the current sub string list and sets
// the file as the current open sub string file
//
// returns 1 on success, 0 on failure
int Open_SubStringFile( const char * p_path )
{
   g_p_file_substrs = fopen( p_path, "r+t" );
   if( g_p_file_substrs )
   {
      if( Create_SubStringList( &g_mainsubstrs, g_p_file_substrs ) )
      {
         // success
         return 1;
      }

      fclose( g_p_file_substrs );
      printf( "[OpenSubStringFile] Error" );
      return 0;
   }
   
   printf( "[OpenSubStringFile] Error, cannot open file: %s.\n", p_path );
   return 0;
}

// given a path, creates a string file for writing to
// as the current open string file
//
// returns 1 on success, 0 on failure
int Create_StringFile( const char * p_path )
{
   g_p_file_strs = fopen( p_path, "w+t" );
   if( g_p_file_strs )
   {
      // free current list
      Free_StringList( &g_mainstrs );

      // success
      return 1;
   }

   printf( "[Create_StringFile] Error, cannot create file: %s.\n", p_path );
   return 0;
}

// given a path, creates a sub string file for writing to
// as the current open sub string file
//
// returns 1 on success, 0 on failure
int Create_SubStringFile( const char * p_path )
{
   g_p_file_substrs = fopen( p_path, "w+t" );
   if( g_p_file_substrs )
   {
      // free current list
      Free_SubStringList( &g_mainsubstrs );

      // success
      return 1;
   }

   printf( "[Create_SubStringFile] Error, cannot create file: %s.\n", p_path );
   return 0;
}

static char * GetTimeStr( char * p_dest )
{
   struct tm * p_time;
   time_t thetime;
   p_dest[0] = 0;
   time( &thetime );
   p_time = localtime( &thetime );
   strcpy( p_dest, asctime( p_time ) );
   return p_dest;
}

// write the file header for the source file
static int KSSC_WriteSourceHeader(  FILE * p_file,
                                    const char * p_src_name )
{
   char buff[1024];

/* form of source header */
#if 0
/*
   "<SRC_NAME>"

   auto-generated by: "make_kernel_string_pool.c"
   for: UbixOS Project
   date: <DATE>

   purpose: - contains constant data for a string pool
*/

#endif
   fprintf( p_file, "/*\n" );
   fprintf( p_file, "   \"%s\"\n", p_src_name );
   fprintf( p_file, "\n" );
   fprintf( p_file, "   auto-generated by: \"make_kernel_string_pool.c\"\n" );
   fprintf( p_file, "   for: UbixOS Project\n" );
   fprintf( p_file, "   date: %s", GetTimeStr( buff ) );
   fprintf( p_file, "\n" );
   fprintf( p_file, "   purpose: - contains constant data for a string pool\n" );
   fprintf( p_file, "*/\n" );
   fprintf( p_file, "\n" );

   return 1;
}

// write the include lines
static int KSSC_WriteSourceIncludes(   FILE * p_file,
                                       const char ** pp_includes,
                                       int numincludes            )
{
   int i;

   for( i = 0; i < numincludes; i++ )
   {
      fprintf( p_file, "#include %s\n", pp_includes[i] );
   }
   fprintf( p_file, "\n" );

   return 1;
}

static char * KSSC_GetSubStringID(  char * p_dst,
                                    const char * p_pool_name   )
{
   sprintf( p_dst, "%s_substrs", p_pool_name );
   return p_dst;
}

#define KSSC_BYTETYPESTR   ("BYTEg")

static int KSSC_WriteByteVal( FILE * p_file,
                              int bytevalidx,
                              int byteval,
                              int withcomma  )
{
   if( (bytevalidx & 15) == 0 )
   {
      fprintf( p_file, "\n" );
   }

   fprintf( p_file, "%d", byteval );

   if( withcomma )
   {
      fprintf( p_file, ", " );
   }

   return bytevalidx + 1;
}

// write the sub strings array
// return size of memory included into source
static int KSSC_WriteSubStrings( FILE * p_file,
                                 const char * p_pool_name   )
{
   char buff[4096];
   int i, ii;
   int bytevalidx;
   int numchars;
   int memsize;

   // write out sub strings to source file
   memsize = 0;

/* form of sub strings */
#if 0
   static <KSSC_BYTETYPESTR> <SUBSTRID>[] =
   {
   <BYTEVAL0>, <BYTEVAL1>, <BYTEVAL2>, ...
   <BYTEVAL16>, <BYTEVAL17>, <BYTEVAL18>, ...
   <BYTEVAL32>, <BYTEVAL33>, <BYTEVAL34>, ...
   .
   .
   .
   };
#endif

   fprintf( p_file,
            "static %s %s[] =\n",
            KSSC_BYTETYPESTR,
            KSSC_GetSubStringID( buff, p_pool_name )  );
   fprintf( p_file, "{" );
   bytevalidx = 0;
   for( i = 0; i < g_mainsubstrs.numsubstrs - 1; i++ )
   {
      // get number of characters in sub string
      numchars = strlen( g_mainsubstrs.pp_substrs[i]->p_str );
      if( numchars >= 1 )
      {
         if( numchars <= 256 )
         {
            // write number of characters in byte string
            numchars--;
            bytevalidx
               = KSSC_WriteByteVal( p_file,
                                    bytevalidx,
                                    numchars,
                                    1           );
            memsize++;
            numchars++;

            // write each character of byte string
            for( ii = 0; ii < numchars; ii++ )
            {
               bytevalidx
                  = KSSC_WriteByteVal( p_file,
                                       bytevalidx,
                                       (int) (g_mainsubstrs.\
                                                pp_substrs[i]->p_str)[ii],
                                       1                                   );
               memsize++;
            }

            continue;
         }
      }

      printf( "[KSSC_WriteSubStrings], ERROR! invalid substring: \"%s\"\n", g_mainsubstrs.pp_substrs[i] );
      return 0;
   }

   // get number of characters in final sub string
   numchars = strlen( g_mainsubstrs.pp_substrs[i]->p_str );
   if( numchars >= 1 )
   {
      if( numchars <= 256 )
      {
         // write number of characters in byte string
         numchars--;
         bytevalidx
            = KSSC_WriteByteVal( p_file,
                                 bytevalidx,
                                 numchars,
                                 1           );
         memsize++;
         numchars++;

         // write each character of byte string
         for( ii = 0; ii < numchars - 1; ii++ )
         {
            bytevalidx
               = KSSC_WriteByteVal( p_file,
                                    bytevalidx,
                                    (int) (g_mainsubstrs.\
                                             pp_substrs[i]->p_str)[ii],
                                    1                                   );
            memsize++;
         }

         // write final character
         KSSC_WriteByteVal(   p_file,
                              bytevalidx,
                              (int) (g_mainsubstrs.\
                                       pp_substrs[i]->p_str)[ii],
                              0                                   );
         memsize++;

         // write closing braces etc.
         fprintf( p_file, "\n};\n\n" );

         // success
         return memsize;
      }
   }

   printf( "[KSSC_WriteSubStrings], ERROR! invalid substring: \"%s\"\n", g_mainsubstrs.pp_substrs[i] );
   return 0;
}

static char * KSSC_GetSubStringOffsetID(  char * p_dst,
                                          const char * p_pool_name   )
{
   sprintf( p_dst, "%s_substroffs", p_pool_name );
   return p_dst;
}

static int KSSC_WriteOffsetVal(  FILE * p_file,
                                 int offsetidx,
                                 unsigned int offset,
                                 int withcomma        )
{
   if( (offsetidx & 7) == 0 )
   {
      fprintf( p_file, "\n" );
   }

   fprintf( p_file, "%u", offset );

   if( withcomma )
   {
      fprintf( p_file, ", " );
   }

   return offsetidx + 1;
}


// 86'd to save memory at cost of speed
#if 0

// write the sub strings offset array
// return size of memory included into source
static int KSSC_WriteSubStringOffsets( FILE * p_file,
                                       const char * p_pool_name   )
{
   char buff[4096];
   unsigned int curoffset;
   int i;
   int numchars;
   int offsetidx;
   int memsize;

   memsize = 0;

/* form of sub string offsets */
#if 0
   static unsigned int <SUBSTROFFID>[] =
   {
   <OFFSET0>, <OFFSET1>, <OFFSET2>, ...
   <OFFSET8>, <OFFSET9>, <OFFSET10>, ...
   <OFFSET16>, <OFFSET17>, <OFFSET18>, ...
   .
   .
   .
   };
#endif

   fprintf( p_file,
            "static unsigned int %s[] =\n",
            KSSC_GetSubStringOffsetID( buff, p_pool_name )  );
   fprintf( p_file, "{" );
   offsetidx = 0;
   curoffset = 0;
   for( i = 0; i < g_mainsubstrs.numsubstrs - 1; i++ )
   {
      // get number of characters in sub string
      numchars = strlen( g_mainsubstrs.pp_substrs[i]->p_str );

      // write the offset
      offsetidx
         = KSSC_WriteOffsetVal(  p_file,
                                 offsetidx,
                                 curoffset,
                                 1           );
      memsize += sizeof(unsigned int);

      // update the offset
      curoffset += numchars + 1;
   }

   // write final offset
   KSSC_WriteOffsetVal( p_file,
                        offsetidx,
                        curoffset,
                        0           );
   memsize += sizeof(unsigned int);

   // write closing braces etc.
   fprintf( p_file, "\n};\n\n" );

   // success
   return memsize;
}

#endif // #if 0

static int KSSC_GetStringProfile(   int * p_numwords_o,
                                    int * p_numwordbytes_o,
                                    BYTEg * p_wordbytes_o,
                                    const char * p_str      )
{
   char buff[4096];
   char * p_curbuff;
   int idx;
   int idxmask;
   SUBSTR * p_curss;

   // copy working string
   strcpy( buff, p_str );
   (*p_numwords_o) = 0;
   (*p_numwordbytes_o) = 1;
   if( (p_curbuff = strtok( buff, g_whitespace )) )
   {
      do
      {
         // get related substring (*MUST* exist)
         p_curss = Find_SubStringByStr( &g_mainsubstrs, p_curbuff );
         if( p_curss )
         {
            // update number of words
            (*p_numwords_o)++;

            // update bytes in KString (if needed)
            if( p_wordbytes_o )
            {
               // assume normal expression
               idxmask = 0;
               idx = p_curss->idx;
               if( p_curss->idx > 63 )
               {
                  // use escape sequence to express
                  idxmask = 0x40;
                  if( p_curss->idx > 16383 )
                  {
                     // use larger escape sequence to express
                     idxmask = 0x80;
                     p_wordbytes_o[(*p_numwordbytes_o) + 2]
                                             = (BYTEg) (idx&0xFF);
                     idx >>= 8;
                  }
                  p_wordbytes_o[(*p_numwordbytes_o) + 1]
                                          = (BYTEg) (idx&0xFF);
                  idx >>= 8;
               }
               p_wordbytes_o[(*p_numwordbytes_o)] = (BYTEg) (idx|idxmask);
            }
         
            // update total bytes
            (*p_numwordbytes_o)++;
            if( p_curss->idx > 63 )
            {
               (*p_numwordbytes_o)++;
               if( p_curss->idx > 16383 )
               {
                  (*p_numwordbytes_o)++;
               }
            }

            // next token
            p_curbuff = strtok( NULL, g_whitespace );

            // continue
            continue;
         }
      
         // fail
         return 0;
      }
      while( p_curbuff );

      if( (*p_numwordbytes_o) >= 1 && (*p_numwordbytes_o) <= 256 )
      {
         // update bytes in KString, number of bytes, (if needed)
         if( p_wordbytes_o )
         {
            p_wordbytes_o[0] = (BYTEg) ((*p_numwordbytes_o) - 1);
         }

         // success
         return 1;
      }
   }

   // fail
   return 0;
}

static char * KSSC_GetKStringID( char * p_dst,
                                 const char * p_pool_name   )
{
   sprintf( p_dst, "%s_kstrs", p_pool_name );
   return p_dst;
}

// write the kernel strings array
// returns size of memory included into source
static int KSSC_WriteStrings( FILE * p_file,
                              const char * p_pool_name   )
{
   char buff[4096];
   BYTEg buff2[4096];
   int i, ii;
   int bytevalidx;
   int numwords;
   int numwordbytes;
   int memsize;

   memsize = 0;

/* form of kernel strings */
#if 0
   static <KSSC_BYTETYPESTR> <KSTRID>[] =
   {
   <BYTEVAL0>, <BYTEVAL1>, <BYTEVAL2>, ...
   <BYTEVAL16>, <BYTEVAL17>, <BYTEVAL18>, ...
   <BYTEVAL32>, <BYTEVAL33>, <BYTEVAL34>, ...
   .
   .
   .
   };
#endif

   fprintf( p_file,
            "static %s %s[] =\n",
            KSSC_BYTETYPESTR,
            KSSC_GetKStringID( buff, p_pool_name ) );
   fprintf( p_file, "{" );
   bytevalidx = 0;
   for( i = 0; i < g_mainstrs.numstrs - 1; i++ )
   {
      // get profile of string
      if( KSSC_GetStringProfile( &numwords,
                                 &numwordbytes,
                                 buff2,
                                 g_mainstrs.pp_strs[i]   )  )
      {
         // write out each byte in kstring
         for( ii = 0; ii < numwordbytes; ii++ )
         {
            bytevalidx
               = KSSC_WriteByteVal( p_file,
                                    bytevalidx,
                                    (int) buff2[ii],
                                    1                 );
         }
         memsize += numwordbytes;

         // continue
         continue;
      }

      // fail
      printf( "[KSSC_WriteStrings], Error, invalid string? -> %s\n", g_mainstrs.pp_strs[i] );
      return 0;
   }

   // write final kstring
   if( KSSC_GetStringProfile( &numwords,
                              &numwordbytes,
                              buff2,
                              g_mainstrs.pp_strs[i]   )  )
   {
      // write out each byte in kstring
      for( ii = 0; ii < numwordbytes - 1; ii++ )
      {
         bytevalidx
            = KSSC_WriteByteVal( p_file,
                                 bytevalidx,
                                 (int) buff2[ii],
                                 1                 );
      }

      // write final byte
      KSSC_WriteByteVal(   p_file,
                           bytevalidx,
                           (int) buff2[ii],
                           0                 );
      memsize += numwordbytes;

      // write closing braces etc.
      fprintf( p_file, "\n};\n\n" );

      // success
      return memsize;
   }

   // fail
   printf( "[KSSC_WriteStrings], Error, invalid string? -> %s\n", g_mainstrs.pp_strs[i] );
   return 0;
}

// write the string pool structure itself
static int KSSC_WriteStringPool( FILE * p_file,
                                 const char * p_pool_name   )
{
   char buff1[2048];
   char buff2[2048];

/* form of kernel string pool */
#if 0
   /* kernel string pool */
   KSTR_POOL <POOLNAME> = { <KSTRID>, <SUBSTRID> };
#endif

   fprintf( p_file,
            "/* kernel string pool */\n"  );
   fprintf( p_file,
            "KSTR_POOL %s = { %s, %s };\n",
            p_pool_name,
            KSSC_GetKStringID( buff1, p_pool_name ),
            KSSC_GetSubStringID( buff2, p_pool_name ) );
   
   return 1;
}

// converts a filename to a define symbol
static char * KSSC_GetHeaderDefineSymbol( char * p_dst,
                                          const char * p_hdr_name )
{
   char * p_curdst;
   
   sprintf( p_dst, "_%s", p_hdr_name );
   p_curdst = p_dst;
   while( (*p_curdst) != 0 )
   {
      if( (*p_curdst) != '.' )
      {
         if( islower( (int) (*p_curdst) ) )
         {
            (*p_curdst) = (char) toupper( (int) (*p_curdst) );
         }

         p_curdst++;
         continue;
      }

      (*p_curdst) = '_';
      p_curdst++;
      continue;
   }
   
   return p_dst;
}

// write the file header for the header file
static int KSSC_WriteHeaderHeader(  FILE * p_file,
                                    const char * p_hdr_name )
{
   char hdrsym[1024];
   char buff[1024];

/* form of header header */
#if 0
/*
   "<HDR_NAME>"

   auto-generated by: "make_kernel_string_pool.c"
   for: UbixOS Project
   date: <DATE>

   purpose: - header file for a string pool
*/

/* #ifndef _<HDR_NAME>
#define _<HDR_NAME> */

#endif
   fprintf( p_file, "/*\n" );
   fprintf( p_file, "   \"%s\"\n", p_hdr_name );
   fprintf( p_file, "\n" );
   fprintf( p_file, "   auto-generated by: \"make_kernel_string_pool.c\"\n" );
   fprintf( p_file, "   for: UbixOS Project\n" );
   fprintf( p_file, "   date: %s", GetTimeStr( buff ) );
   fprintf( p_file, "\n" );
   fprintf( p_file, "   purpose: - header file for a string pool\n" );
   fprintf( p_file, "*/\n" );
   fprintf( p_file, "\n" );
   KSSC_GetHeaderDefineSymbol( hdrsym, p_hdr_name );
   fprintf( p_file, "#ifndef %s\n", hdrsym );
   fprintf( p_file, "#define %s\n", hdrsym );
   fprintf( p_file, "\n" );

   return 1;
}

// write the string pool structure declaration
static int KSSC_WriteStringPoolDecl(   FILE * p_file,
                                       const char * p_pool_name   )
{
/* form of kernel string pool declaration */
#if 0
   /* kernel string pool */
   extern KSTR_POOL <POOLNAME>;

#endif

   fprintf( p_file,
            "/* kernel string pool */\n"  );
   fprintf( p_file,
            "extern KSTR_POOL %s;\n",
            p_pool_name                );
   
   return 1;
}

// write the file footer for the header file
static int KSSC_WriteHeaderFooter(  FILE * p_file,
                                    const char * p_hdr_name )
{
   char buff[1024];

/* form of header footer */
#if 0

'#endif' /* _<HDR_NAME> */
#endif
   fprintf( p_file, "\n" );
   fprintf( p_file,
            "#endif /* %s */\n",
            KSSC_GetHeaderDefineSymbol( buff, p_hdr_name )  );

   return 1;
}

/*
   Input:
      - p_hdr_path <-   full path to header file to write
                        relative to current working directory
      - p_hdr_name <- name of header file
      - p_src_path <-   full path to source file to write
                        relativeto current working directory
      - p_src_name <- name of source file
      - pp_includes <- list of files to include eg. <h1.h>, "h4.h", ...
      - numincludes <- number of files in include list
      - p_pool_name <-  name of identifier to use for string pool
                        generated ( NOTE: sub-arrays of string pool
                                    will use this name to base their names )
   Output:
      - generates both a header file and a source code file which
      can be used to embed compressed strings into an object module
      based on the currently open string list and sub string list
      - returns 1 on success, 0 on failure

   Assumptions:
      - a string list is current open <see: 'Open_StringFile', etc.>
      - a sub string list is current open <see: 'Open_SubStringFile', etc.>
      - the strings in the open string file are a superset of the sub strings
      in the open string file
*/
int Create_KStringSourceCode( const char * p_hdr_path,
                              const char * p_hdr_name,
                              const char * p_src_path,
                              const char * p_src_name,
                              const char ** pp_includes,
                              int numincludes,
                              const char * p_pool_name   )
{
   FILE * p_srccode;
   FILE * p_hdr;
   int memsize;
   int totalmemsize;

   memsize = 0;
   totalmemsize = 0;

   // build full path names

   // open source code file
   p_srccode = fopen( p_src_path, "w+t" );
   if( p_srccode )
   {
      // open header file
      p_hdr = fopen( p_hdr_path, "w+t" );
      if( p_hdr )
      {
         // write the file header for the source file
         if( !KSSC_WriteSourceHeader( p_srccode, p_src_name ) )
         {
            goto failbail;
         }

         // write the include lines
         if( !KSSC_WriteSourceIncludes(   p_srccode,
                                          pp_includes,
                                          numincludes    )  )
         {
            goto failbail;
         }

         // sort by frequency
         if( !UpdateFrequencies_SubStrings( &g_mainsubstrs, &g_mainstrs ) )
         {
            // fail
            printf( "[Create_KStringSourceCode], Error assigning substring frequencies.\n" );
            goto failbail;
         }
         if( !Sort_SubStringListByFreq( &g_mainsubstrs ) )
         {
            // fail, but attempt to restore sorting to alphabetic
            Sort_SubStringList( &g_mainsubstrs );
            printf( "[Create_KStringSourceCode], Error sorting substrings by frequency.\n" );
            goto failbail;
         }

         // assign indices to sub strings based on frequency
         if( !UpdateIndices_SubStrings( &g_mainsubstrs ) )
         {
            // fail, but attempt to restore sorting to alphabetic
            Sort_SubStringList( &g_mainsubstrs );
            printf( "[Create_KStringSourceCode], Error assigning indices to sub strings based on frequency.\n" );
            goto failbail;
         }

         // write the sub strings array
         memsize = KSSC_WriteSubStrings( p_srccode, p_pool_name );
         if( !memsize )
         {
            goto failbail;
         }
         printf( "KSSC_WriteSubStrings->MemSize = %d\n", memsize );
         totalmemsize += memsize;


// 86'd to save memory at cost of speed
#if 0
         // write the sub strings offset array
         memsize = KSSC_WriteSubStringOffsets( p_srccode, p_pool_name );
         if( !memsize )
         {
            goto failbail;
         }
         printf( "KSSC_WriteSubStringOffsets->MemSize = %d\n", memsize );
         totalmemsize += memsize;
#endif

         // restore sorting to alphabetic
         if( !Sort_SubStringList( &g_mainsubstrs ) )
         {
            // fail
            printf( "[Create_KStringSourceCode], Error restoring alphabetic ordering to sub strings.\n" );
            goto failbail;
         }

         // write the kernel strings array
         memsize = KSSC_WriteStrings( p_srccode, p_pool_name );
         if( !memsize )
         {
            goto failbail;
         }
         printf( "KSSC_WriteStrings->MemSize = %d\n", memsize );
         totalmemsize += memsize;

         // write the string pool structure itself
         if( !KSSC_WriteStringPool( p_srccode, p_pool_name ) )
         {
            goto failbail;
         }

         // write the file header for the header file
         if( !KSSC_WriteHeaderHeader( p_hdr, p_hdr_name ) )
         {
            goto failbail;
         }

         // write the string pool structure declaration
         if( !KSSC_WriteStringPoolDecl( p_hdr, p_pool_name ) )
         {
            goto failbail;
         }

         // write the file footer for the header file
         if( !KSSC_WriteHeaderFooter( p_hdr, p_hdr_name ) )
         {
            goto failbail;
         }

         // close both files
         if( fclose( p_hdr ) == 0 )
         {
            p_hdr = NULL;
         }
         if( fclose( p_srccode ) == 0 )
         {
            p_srccode = NULL;
         }

         if( !p_hdr && !p_srccode )
         {
            printf( "Create_KStringSourceCode->MemSize = %d Bytes, %d KBytes\n", totalmemsize, ((totalmemsize + 1023)>>10) );

            // success
            return 1;
         }

         // close header file
         fclose( p_hdr );
      }
      // if( p_hdr )

      // close source code file
      fclose( p_srccode );
   }
   // if( p_src_path )

failbail:
   return 0;
}


// Resets *everything*, frees all memory in use, closes all open files
void MakeKSP_ResetAll()
{
   Free_StringList( &g_mainstrs );
   Free_SubStringList( &g_mainsubstrs );
   Free_SubStringList( &g_tempsubstrs1 );
   Free_SubStringList( &g_tempsubstrs2 );

   if( g_p_file_substrs )
   {
      fclose( g_p_file_substrs );
      g_p_file_substrs = NULL;
   }

   if( g_p_file_strs )
   {
      fclose( g_p_file_strs );
      g_p_file_strs = NULL;
   }
}