/* uglify.c: copyright Mike Arnautov 1993-2016, licensed              
 * under GPL (version 3 or later) or the Modified BSD Licence, 
 * whichever is asserted by the supplied LICENCE file.   
 *
 * 08 Apr 03   MLA          bug: Report correct number of processed files.
 * 20 Feb 03   MLA          Adjusted to new naming convention.
 * 02 Feb 03   MLA          Advkern files now have tag 0.
 * 30 Dec 02   MLA          BUG: Avoid clashes with advkern.c names!!
 * 01 Oct 02   MLA          Bug: Corrected copyleft date code.
 * 18 Nov 01   MLA          Added COPYLEFT code. Also, autop files
 *                          now tagged with 2 digits.
 * 03 Oct 99   MLA          Don't translate advkern.h autod5.h twice!
 * 08 Aug 99   MLA          Rename advkern sources to auto sources.
 * 04 Mar 94   MLA          Process all files automatically.
 * 20 Jun 93   MLA          Initial coding.
 *
 * Purpose: to uglify kernel and automatically generated adventure C source
 * into a humanly inconvenient format.
 *
 * Usage: uglify <input_dir> <output_dir>
 *
 * To avoid wrecking the source, UGLIFY needs a list of keywords it is 
 * supposed to leave severely alone (i.e. C reserved words and system 
 * routine names). The list is obtained by reading the file keyword.lst.
 *
 * The mappings.lst file (if any) is read before uglification and all 
 * mappings are written off into mappings.lst after uglification. This 
 * facility allows consistency of mappings over several runs, if desired.
 */
 
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <stdlib.h>
#ifdef __CYGWIN__
#  include <malloc.h>
#endif

typedef struct node_s {
        char *key;                      /* ptr to datum */
        struct node_s *parent;          /* ptr to parent ancestor */
        struct node_s *sibls[2];        /* ptrs to L/R siblings */
        int balance;                    /* balance value (-1, 0 or +1) */
} NODE;
typedef enum {
   preorder, postorder, endorder, leaf
} VISIT;
NODE *tsearch();
NODE *tfind();
void twalk();

typedef struct Node
{
   struct Node *link;
   char *name;
   char *fake;
} Node;

Node test_node;
Node *root = NULL;
Node *node_chain = NULL;
int initialising = 1;
int verbatim = 0;
int force_eol = 0;
int char_count = 0;
int keyword_count = 0;
int mapping_count = 0;
int dump_mappings = 0;
int fake_count = 0;
int include_flag = 0;
int counts [256];
char identifier [33];
int dbname_flag = 0;
char db_name [33];
char stem [128];
char *gname = NULL;
char *dbptr = db_name;
FILE *outfile;
FILE *infile;
FILE *mapfile;
#if defined(unix) || defined(__CYGWIN__)
#  define SEP '/'
#else
#  ifdef MSDOS 
#     define SEP '\\'
#  else
#     ifdef __50SERIES
#        define SEP '>'
#     else
#        define SEP ''
#     endif
#  endif
#endif

#ifdef __STDC__
int compare (Node *node1, Node *node2)
#else
int compare (node1, node2)
Node *node1, *node2;
#endif
{
   return strcmp (node1 -> name, node2 -> name);
}

#ifdef __STDC__
void list_fakes (Node **node, VISIT order, int level)
#else
void list_fakes (node, order, level)
Node **node;
VISIT order;
int level;
#endif
{
   if (order == postorder || order == leaf)
   {
      if (initialising)
      {
         if ((*node) -> name == (*node) -> fake)
            printf ("%20s\n", (*node) -> name);
      }
      else
      {
         if ((*node) -> name != (*node) -> fake)
         {
            fprintf (mapfile, "%20s %20s\n", (*node) -> name, (*node) -> fake);
            mapping_count++;
         }
      }
   }
}

#ifdef __STDC__
void terminate (int status)
#else
void terminate (status)
int status;
#endif
{
   Node *node_ptr = node_chain;
   Node *old_ptr;
   
   if (outfile)
      fclose (outfile);
   if (infile)
      fclose (infile);
      
   while (node_ptr)
   {
      if ((node_ptr -> fake) && (node_ptr -> fake) != (node_ptr -> name))
         free (node_ptr -> fake);
      if (node_ptr -> name)
         free (node_ptr -> name);
      old_ptr = node_ptr;
      node_ptr = node_ptr -> link;
      free (old_ptr++);
   }
   exit (status);
}

#ifdef __STDC__
Node *make_node (char *name)
#else
Node *make_node (name)
char *name;
#endif
{
   char new_name [33];
   Node *node_ptr;
   char init_char;
   
   if ((node_ptr = (Node *) malloc (sizeof (Node))) == NULL)
   {
      printf ("Unable to malloc node for '%s'!\n", name);
      terminate (1);
   }
   node_ptr -> link = node_chain;
   node_chain = node_ptr;
   node_ptr -> fake = NULL;

   if ((node_ptr -> name = (char *) malloc (strlen (name) + 1)) == NULL)
   {
      printf ("Unable to allocate space for word '%s'!\n", name);
      terminate (1);
   }
   strcpy (node_ptr -> name, name);
   node_ptr -> fake = node_ptr -> name;

   if (initialising)
   {
      keyword_count++;
      return (node_ptr);
   }
   
   if (strncmp (name, "adv", 3) == 0 && isdigit (*(name + 3)))
      strcpy (new_name, name);
   else
   {
      init_char = rand () % 25 + 'a';
      if (init_char >= 'p') init_char++;
      sprintf (new_name, "%c%03d", init_char, (counts [(int)init_char])++);
   }
   if ((node_ptr -> fake = (char *) malloc (strlen (new_name) + 1)) == NULL)
   {
      printf ("Unable to allocate space for '%s' fake '%s'!\n", 
         name, new_name);
      terminate (1);
   }
   strcpy (node_ptr -> fake, new_name);
   
   fake_count++;
   return (node_ptr);
}

#ifdef __STDC__
int copy (char *srcfile, char *srcdir, char *trgfile, char *trgdir)
#else
int copy (srcfile, srcdir, trgfile, trgdir)
char *srcfile;
char *srcdir;
char *trgfile;
char *trgdir;
#endif
{
   char inpath [160];
   char outpath [160];
   int next_char;
   
   sprintf (inpath, "%s%c%s", srcdir, SEP, srcfile);
   if ((infile = fopen (inpath, "rb")) == NULL)
   {
      printf ("Can't find input file %s!\n", inpath);
      return (1);
   }

   sprintf (outpath, "%s%c%s", trgdir, SEP, trgfile);
   if ((outfile = fopen (outpath, "wb")) == NULL)
   {
      printf ("Can't open output file %s!\n", outpath);
      fclose (infile);
      return (1);
   }
   
   while (1)
   {
      next_char = fgetc (infile);
      if (feof (infile))
      {
         fclose (infile);
         fclose (outfile);
         return (0);
      }
      fputc (next_char, outfile);
   }      
}

#ifdef __STDC__
void get_identifier (FILE *input)
#else
void get_identifier (input)
FILE *input;
#endif
{
   unsigned char next_char;

   char *ptr = identifier;

   while ((isalnum (next_char = fgetc (input)) || next_char == '_') && 
      !feof (input))
         *ptr++ = next_char;
      
   *ptr = '\0';
   dbname_flag = (strcmp (identifier, "DBNAME") == 0 && force_eol);
   ungetc (next_char, input);
}

#ifdef __STDC__
void store_char (int next_char)
#else
void store_char (next_char)
int next_char;
#endif
{
   if (verbatim && dbname_flag)
   {
      *dbptr++  = next_char;
   }

   if ((next_char == ' ' && char_count > 70) && !verbatim && !force_eol)
   {
      char_count = 0;
      fputc ('\n', outfile);
   }
   else
      if (next_char != 0377)
      {
         char_count++;
         fputc (next_char, outfile);
      }
      else
      {
         fputc ('\n', outfile);
         char_count = 0;
      }
}

#ifdef __STDC__
void find_identifier (FILE *input)
#else
void find_identifier (input)
FILE *input;
#endif
{
   unsigned char next_char, temp_char, last_char = 0;
   
   while (!feof (input) && !isalpha (next_char = fgetc (input)) && 
      next_char != '_')
   {
      if (next_char  == '\\')
      {
         fputc (next_char, outfile);
         fputc ('\n', outfile);
         char_count = 0;
         while (!feof (input) && (fgetc (input) != '\n')) ;
         continue;
      }
      if ((next_char == '\'' || next_char == '"') && 
         include_flag == 0  && last_char != '\\')
      {
         if (!initialising)
            store_char (next_char);
         verbatim = 1;
         temp_char = last_char = next_char;
         while (!((next_char = fgetc (input)) == temp_char && 
            last_char != '\\') && !feof (input))
         {
            if (!initialising)
               store_char (next_char);
            if (next_char == '\\' && last_char == '\\')
               last_char = '/';
            else
               last_char = next_char;
         }
         if (feof (input))
            return;
         verbatim = 0;
      }
      if (next_char == '/')
      {
         if ((temp_char = fgetc (input)) != '*')
            ungetc (temp_char, input);
         else
         {
            verbatim = 1;
            while (1)
            {
               if ((next_char = fgetc (input)) == '*')
               {
                  if ((temp_char = fgetc (input)) == '/')
                  {
                     next_char = ' ';
                     last_char = ' ';
                     verbatim = 0;
                     break;
                  }
                  else
                     ungetc (temp_char, input);
               }
            }
         }
      }     
            
      if (next_char == '#')
      {
         if (char_count)
         {
            fputc ('\n', outfile);
            char_count = 0;
         }
         last_char = ' ';
         force_eol = 1;
      }
         
      if (next_char == '\n')
      {
         include_flag = 0;
         if (force_eol)
            force_eol = 0;
         else 
            next_char = ' ';
      }
            
      if (next_char == '\t')
         next_char = ' ';
      if ((next_char != ' ' || last_char != ' ') && !initialising)
         store_char (next_char);
      if (next_char == '\n')
      {
         char_count = 0;
         last_char = ' ';
      }
      else
         last_char = next_char;
      
      if (last_char == '#')
         last_char = ' ';
   }
   if (!feof (input))
      ungetc (next_char, input);
}

#ifdef __STDC__
int uglify (char *srcfile, char* srcdir, char *trgfile, char *trgdir)
#else
int uglify (srcfile, srcdir, trgfile, trgdir)
char *srcfile;
char *srcdir;
char *trgfile;
char *trgdir;
#endif
{
   Node *node_ptr;
   Node **temp_ptr;
   char srcpath [160];
   char outpath [160];
#ifdef COPYLEFT
   char datbuf [16];
   time_t now = time (NULL);
#endif /* COPYLEFT */

   sprintf (srcpath, "%s%c%s", srcdir, SEP, srcfile);
   if ((infile = fopen (srcpath, "r")) == NULL)
      return (1);

   sprintf (outpath, "%s%c%s", trgdir, SEP, trgfile);
   if ((outfile = fopen (outpath, "w")) == NULL)
   {
      printf ("Unable to open output file %s.\n", outpath);
      fclose (infile);
      infile = NULL;
      return (1);
   }
   
#ifdef COPYLEFT
   (void) strftime (datbuf, sizeof (datbuf), "%d %b %Y", localtime (&now));
   if (gname)
      fprintf (outfile, "/* %s - copyleft %s, %s */\n",
         gname, COPYLEFT, datbuf);
   else
      fprintf (outfile, "/* Copyleft %s, %s */\n", COPYLEFT, datbuf);
#endif /* COPYLEFT */

   while (!feof (infile))
   {
      find_identifier (infile);
      if (feof (infile))
         break;
      get_identifier (infile);
      if (strcmp  (identifier, "include") == 0)
         include_flag = 1;
      test_node.name = identifier;
      if (strlen (identifier) == 1)
      {
         fputc (*identifier, outfile);
         char_count++;
         continue;
      }
      if (strncmp (identifier, "HAVE_", 5) == 0 ||
          strncmp (identifier, "NEED_", 5) == 0)
      {
         fprintf (outfile, identifier);
         char_count += strlen (identifier);
         continue;
      }
      if ((temp_ptr = (Node **) tfind (&test_node, &root, compare)) == NULL)
      {
         node_ptr = make_node (identifier);
         tsearch (node_ptr, &root, compare);
      }
      else
      {
         node_ptr = *temp_ptr;
      }
      fprintf (outfile, "%s", node_ptr -> fake);
      char_count += strlen (node_ptr -> fake);
   }
   fclose (outfile);
   outfile = NULL;
   fclose (infile);
   infile = NULL;
   return (0);
}
   
#ifdef __STDC__
void initialise (char *mypath)
#else
void initialise (mypath)
char *mypath;
#endif
{
   FILE *keyfile;
   Node *node_ptr;
   char *ptr;
   int value;
   char keypath [128];

   srand (time(NULL));

   *dbptr = '\0';
   strncpy (keypath, mypath, 128);
   ptr = strrchr (keypath, SEP);
   if (ptr) ptr++;
   else ptr = keypath;
   strcpy (ptr, "keyword.lst");
   if ((keyfile = fopen (keypath, "r") ) == NULL)
   {
      printf ("No keyword list!\n");
      terminate (1);
   }
   
   *ptr = '\0';
   strcpy (stem, keypath);

   while (!feof (keyfile))
   {
      find_identifier (keyfile);
      if (feof (keyfile))
         break;
      get_identifier (keyfile);
      test_node.name = identifier;
      if (!tfind (&test_node, &root, compare))
         tsearch (make_node (identifier), &root, compare);
   }
   fclose (keyfile);

   printf ("Keywords read:                     %4d\n", keyword_count);
   
   strcpy (ptr, "mappings.lst");
   if ((mapfile = fopen (keypath, "r")) != NULL)
   {
      while (!feof (mapfile))
      {
         find_identifier (mapfile);
         if (feof (mapfile))
            break;
         get_identifier (mapfile);
         node_ptr = make_node (identifier);
         find_identifier (mapfile);
         get_identifier (mapfile);
         if (tfind (node_ptr, &root, compare) != NULL)
            printf ("\nString '%s' already remapped!\n", node_ptr -> name);
         else
         {
            node_ptr -> fake = (char *) malloc (strlen (identifier) + 1);
            strcpy (node_ptr -> fake, identifier);
            tsearch (node_ptr, &root, compare);
            mapping_count++;
         }      
         ptr = identifier + 1;
         value = 0;
         while (*ptr)
            value = 10 * value + *ptr++ - '0';
         if (value >= counts [(int)*identifier])
            counts [(int)*identifier] = value + 1;
      }
      fclose (mapfile);
   }
   printf ("Mappings loaded:                   %4d\n", mapping_count);
}

#ifdef __STDC__
int main (int argc, char **argv)
#else
int main (argc, argv)
int argc;
char **argv;
#endif
{
   char name [80];
   int dcount = -1;
   int pcount = -1;
   char *mypath = *argv;

   (void) printf (
      "[Adventure C uglifier, version 2.08; MLA, 08 Apr 03]\n\n");

   argv++; argc--;
   while (argc && **argv == '-')
   {
      if (strncmp (*argv, "-mappings", strlen (*argv)) == 0)
      {
         dump_mappings = 1;
         argv++; argc--;
      }
      else if (strncmp (*argv, "-name", strlen (*argv)) == 0)
      {
         argv++; argc--;
         if (argc)
         {
            gname = *argv;
            argv++; argc--;
         }
      }
   }
   
   if (argc != 2)
   {
      printf ("Usage: uglify [-mappings] [-name <name>] <source_dir> <target_dir>\n");
      return (1);
   }
   
   initialise (mypath);
   initialising = 0;

   while (1)
   {
      sprintf (name, "adv%d.h", ++dcount);
      if (uglify (name, argv [0], name, argv [1]) != 0)
      {
         if (dcount == 0)
         {
            printf ("No adv<n>.h header files found!\n");
            terminate (1);
         }
         break;
      }
   }
   printf ("Header files processed:    %12d\n", dcount);
         
   while (1)
   {
      sprintf (name, "adv%02d.c", ++pcount);
      if (uglify (name, argv [0], name, argv [1]) != 0)
      {
         if (pcount == 0)
         {
            printf ("No adv<nn>.c source files found!\n");
            terminate (1);
         }
         break;
      }
   }
   printf ("Source files processed:    %12d\n", pcount);
   
   if (dump_mappings)
   {
      char mappath [128];
      mapping_count = 0;
      sprintf (mappath, "%smappings.lst", stem);
      if ((mapfile = fopen (mappath, "w")) == NULL)
         printf ("Unable to open mappings listfor dumping.\n");
      else
         twalk (root, list_fakes);
   }
   printf ("Mappings dumped:                   %4d\n", mapping_count);
   printf ("Translations total:                %4d\n\n", 
      keyword_count + fake_count);
   
   terminate (0);
   return (0);       /* To keep nit-picking compilers happy */
}

/* tsearch.c - copyleft @ Peter Valkenburg, November '89. */

#define NIL_NODE        ((NODE *) 0)    /* ptr for empty (sub)trees */

typedef int DIR;                        /* type for indexing siblings */
#define L       ((DIR) 0)
#define R       ((DIR) 1)

#define LEFT            sibls[L]        /* left sibling pointer NODE field */
#define RIGHT           sibls[R]        /* right sibling pointer NODE field */

#define direction(p, c) ((DIR) ((p)->RIGHT == (c)))
#define cmp_dir(v)      ((DIR) ((v) > 0))
#define siblingp(n, d)  ((n)->sibls + (d))
#define parentp(r, n)   ((n)->parent == NIL_NODE ? (r) : \
                             siblingp((n)->parent, direction((n)->parent, (n))))
#define dir_bal(d)      ((d) == L ? -1 : 1)

static NODE *balance();
static NODE *single_rotation();
static NODE *double_rotation();

#ifdef __STDC__
NODE *tsearch(char *key, register NODE **rootp, int (*compar)())
#else
NODE *tsearch(key, rootp, compar)
char *key;
register NODE **rootp;
int (*compar)();
#endif
{
        register NODE *parent, *child;
        NODE *nnode;
        register DIR d;
        register int cmp;

        if (rootp == 0)
                return NIL_NODE;

        /* find place where key should go */
        parent = NIL_NODE;
        child = *rootp;
        while (child != NIL_NODE) {
                if ((cmp = compar(key, child->key)) == 0)
                        return child;
                parent = child;
                child = *siblingp(child, cmp_dir(cmp));
        }

        if ((nnode = (NODE *) malloc(sizeof(NODE))) == NIL_NODE)
                return NIL_NODE;
        nnode->key = key;
        nnode->balance = 0;
        nnode->parent = parent;
        nnode->LEFT = nnode->RIGHT = NIL_NODE;
        if (parent == NIL_NODE) {
                *rootp = nnode;
                return nnode;
        }
        *siblingp(parent, cmp_dir(cmp)) = nnode;
        child = nnode;

        do {
                d = direction(parent, child);
                if (parent->balance == dir_bal(d)) {
                        (void) balance(rootp, parent, d);
                        return nnode;
                }
                else if ((parent->balance += dir_bal(d)) == 0)
                        return nnode;
                child = parent;
                parent = parent->parent;
        } while (parent != NIL_NODE);

        return nnode;
}

#ifdef __STDC__
static NODE *balance(NODE **rootp, NODE *sb, DIR d)
#else
static NODE *balance(rootp, sb, d)
NODE **rootp;
NODE *sb;
DIR d;
#endif
{
        NODE *sb_next = *siblingp(sb, d);

        if (sb_next->balance == -dir_bal(d))
                return double_rotation(rootp, sb, sb_next, d);
        else
                return single_rotation(rootp, sb, sb_next, d);
}

#ifdef __STDC__
static NODE *single_rotation(NODE **rootp, register NODE *sb, 
                             register NODE *sb_next, DIR d)
#else
static NODE *single_rotation(rootp, sb, sb_next, d)
NODE **rootp;
register NODE *sb, *sb_next;
register DIR d;
#endif
{
        *siblingp(sb, d)       = *siblingp(sb_next, !d);
        *siblingp(sb_next, !d) = sb;

        sb->balance     -= sb_next->balance;
        sb_next->balance = -sb->balance;

        *parentp(rootp, sb) = sb_next;
        sb_next->parent = sb->parent;
        sb->parent = sb_next;
        if (*siblingp(sb, d) != NIL_NODE)
                (*siblingp(sb, d))->parent = sb;

        return sb_next;
}

#ifdef __STDC__
static NODE *double_rotation(NODE **rootp, register NODE *sb, 
                             register NODE *sb_next, DIR d)
#else
static NODE *double_rotation(rootp, sb, sb_next, d)
NODE **rootp;
register NODE *sb, *sb_next;
register DIR d;
#endif
{
        register NODE *sb_next2 = *siblingp(sb_next, !d);

        *siblingp(sb_next, !d)  = *siblingp(sb_next2, d);
        *siblingp(sb, d)        = *siblingp(sb_next2, !d);
        *siblingp(sb_next2, d)  = sb_next;
        *siblingp(sb_next2, !d) = sb;

        if (sb_next2->balance == sb_next->balance)
                sb_next->balance = -sb_next->balance;
        else
                sb_next->balance = 0;
        if (sb_next2->balance == sb->balance)
                sb->balance = -sb->balance;
        else
                sb->balance = 0;
        sb_next2->balance = 0;

        *parentp(rootp, sb) = sb_next2;
        sb_next2->parent = sb->parent;
        sb->parent = sb_next->parent = sb_next2;
        if (*siblingp(sb_next, !d) != NIL_NODE)
                (*siblingp(sb_next, !d))->parent = sb_next;
        if (*siblingp(sb, d) != NIL_NODE)
                (*siblingp(sb, d))->parent = sb;

        return sb_next2;
}

#ifdef __STDC__
NODE *tfind(char *key, NODE **rootp, int (*compar)())
#else
NODE *tfind(key, rootp, compar)
char *key;
NODE **rootp;
int (*compar)();
#endif
{
        register NODE *node;
        register int cmp;

        if (rootp == 0)
                return NIL_NODE;

        node = *rootp;
        while (node != NIL_NODE) {
                if ((cmp = compar(key, node->key)) == 0)
                        return node;
                node = *siblingp(node, cmp_dir(cmp));
        }

        return NIL_NODE;
}

#ifdef __STDC__
void twalk(register NODE *node, register void (*action)())
#else
void twalk(node, action)
register NODE *node;
register void (*action)();
#endif
{
        register VISIT visit;
        register int level;

        if (node == 0 || action == 0)
                return;

        visit = preorder;
        level = 0;
        while (node != NIL_NODE) {
                if (visit == preorder &&
                    node->LEFT == NIL_NODE && node->RIGHT == NIL_NODE)
                        visit = leaf;

                action(node, visit, level);

                switch (visit) {
                case preorder:
                        if (node->LEFT != NIL_NODE) {
                                node = node->LEFT;
                                level++;
                        }
                        else
                                visit = postorder;
                        break;
                case postorder:
                        if (node->RIGHT != NIL_NODE) {
                                node = node->RIGHT;
                                visit = preorder;
                                level++;
                        }
                        else
                                visit = endorder;
                        break;
                case endorder:
                case leaf:
                        if (node->parent != NIL_NODE)
                        {
                                if (direction(node->parent, node) == L)
                                        visit = postorder;
                                else
                                        visit = endorder;
                        }
                        node = node->parent;
                        level--;
                        break;
                }
        }
}

