#include <stdio.h>
#include <math.h>
#include <string.h>

#define TRUE            (~0)
#define FALSE           0
#define TABLESIZE       1100

struct entry {
        struct entry    *t_link;
        char            *t_name;
        char            *t_lex;
        unsigned short  t_id;
        } *table[TABLESIZE];

FILE            *infile, *outfile;
unsigned short  nsym = 0;
unsigned short  hashstore = TRUE;
unsigned short  f386 = TRUE;

extern char             *_strdup();
extern char             *malloc();
extern unsigned short   hash();
extern unsigned short   atoi();
static                  tfree(); /* defined below */
static struct entry     *talloc(); /* defined below */

/* Local functions */
void s_entries ( struct entry *, char * );
void enter ( char *, char *, unsigned short );

_CRTAPI1 main( ac, av )
        int     ac;
        char    **av;
        {
        char            ent[30], name[30], lex[30], ts[30], tc[30];
        unsigned short  size, count;
        register unsigned short i;
        register struct entry   *p;
        struct entry    *q;
        char            *a;
        double          n, j, ssq, sum;

        ac--;
        av++;

        while (**av == '-') {
                ac--;
                a = *av++;

                while (*++a){

                        if (_stricmp(a, "dnoV386") == 0){
                            f386 = FALSE;
                            break;
                        }
                        else
                        if (*a == 'h')
                                hashstore = ~hashstore;
                        else    {
                                fprintf( stderr, "usage: genkey [-dnoV386] [-h] [infile] [outfile]\n" );
                                exit( 1 );
                                }
                }
        }

        if (ac < 1)
                infile = stdin;
        else    {
                ac--;

                if ((infile = fopen( *av++, "r" )) == NULL) {
                        fprintf( stderr, "Cannot open input file %s\n", *--av );
                        exit( 1 );
                        }
                }

        if (ac < 1)
                outfile = stdout;
        else if ((outfile = fopen( *av, "w" )) == NULL) {
                fprintf( stderr, "Cannot open output file %s\n", *av );
                exit( 1 );
                }

#ifdef DEBUG
        setbuf( outfile, NULL );
#endif

        /* copy first line of file */

        do      {
                i = getc( infile );
                putc( i, outfile );
                } while (i != '\n');

        while (fscanf( infile, " %s %s %s\n", tc, ts, name) == 3) {
                count = atoi( tc );
                size = atoi( ts );

#ifdef DEBUG
                printf( "DEBUG: name=%s, size=%u, count=%u\n", name, size, count );
#endif

                for (i = 0; i < TABLESIZE; table[i++] = NULL)
                        ;

                for (i = 0; i < count; i++)
                        if (fscanf( infile, " %s %s\n", ent, lex ) == 2) {

#ifdef DEBUG
                                printf( "DEBUG:  ent=%s, lex=%s\n", ent, lex );
#endif

                                enter( ent, lex, size );
                                }
                        else    {
                                fprintf( stderr, "Error in input file\n" );
                                exit( 1 );
                                }

#ifdef DEBUG
                printf( "DEBUG: finished input loop\n" );
#endif

                print_table( size );

#ifdef DEBUG
                printf( "DEBUG: finished print_table()\n" );
#endif

                print_struct( name, size );

#ifdef DEBUG
                printf("DEBUG: finished print_struct()\n" );
#endif

                n = sum = ssq = 0.0;

                for (i = 0; i < TABLESIZE; i++) {
                        j = 0.0;

                        if (p = table[i]) {
                                n += 1.0;

                                do      {
                                        q = p->t_link;
                                        tfree( p );
                                        j += 1.0;
                                        } while (p = q);

                                sum += j;
                                ssq += j * j;
                                }
                        }

#ifdef DEBUG
                printf( "DEBUG: finished statistics loop\n" );
#endif

                printf( "%6s: Size = %3u,  Buckets = %3u,  Avg = %.2f,  Std = %.2f\n",
                        name, (unsigned short)sum, (unsigned short)n, sum / n,
                        sqrt( (ssq - sum * sum / n) / (n - 1.0) )
                        );

#ifdef DEBUG
                printf( "DEBUG: finished this table; looking for more\n" );
#endif
                }

        exit( 0 );
        }


/****************************************************************/
/*                                                              */
/*      enter : make an ent into the symbol table.              */
/*                                                              */
/****************************************************************/

void enter ( ent, lex, size )
        char            *ent;
        char            *lex;
        unsigned short  size;
        {
        register unsigned short hashval;
        register struct entry   *p;
        int cb;
        int fIs386Only;

        cb = strlen(ent);
        fIs386Only = !strcmp(ent + strlen(ent) - 4, ".386");

        if (!f386 && fIs386Only)
            return;

        if (fIs386Only)
            *(ent + cb - 4) = '\0';

        p = talloc();
        p->t_id = nsym++;
        hashval = hash( ent ) % size;
        p->t_link = table[hashval];
        table[hashval] = p;

        if ((p->t_name = _strdup( ent )) == NULL
                 || (p->t_lex = _strdup( lex )) == NULL)
                memerror();
        }


/****************************************************************/
/*                                                              */
/*      print_table : output the table we have built.           */
/*                                                              */
/****************************************************************/

print_table ( size )
        unsigned short  size;
        {
        register unsigned short i;
        register struct entry   *p;

        fprintf( outfile, "/***\n" );

        for (i = 0; i < size; i++) {
                fprintf( outfile, " *\t[%u]\n", i );

                for (p = table[i]; p; p = p->t_link)
                        fprintf( outfile, " *\t\t%s,\t%s\n", p->t_name,
                                p->t_lex );
                }

        fprintf( outfile, " */\n" );
        }


/****************************************************************/
/*                                                              */
/*      print_struct : print the initialization structures.     */
/*                                                              */
/****************************************************************/

print_struct ( name, size )
        char            *name;
        unsigned short  size;
        {
        register unsigned short i;

        for (i = 0; i < size; i++)
                s_entries( table[i], name );

        s_symbols( name, size );
        s_header( name, size );
        }


/****************************************************************/
/*                                                              */
/*      s_entries : print the symbol names and defs.            */
/*                                                              */
/****************************************************************/

void s_entries ( p, name )
        register struct entry   *p;
        char                    *name;
        {
        struct reverse {
                struct  entry   *actual;
                struct  reverse *next;
                } *head = NULL;
        register struct reverse *q;

        if (!p)
                return;

        while (p) {

/*
**  all definitions must be reversed so that output will be that a
**  unit will be defined before it is used.
*/

                if ((q = (struct reverse *)malloc( sizeof(struct reverse) ))
                         == NULL)
                        memerror();

                q->actual = p;
                q->next = head;
                head = q;
                p = p->t_link;
                }

        for (q = head; q; q = q->next) {
                fprintf( outfile, "static KEYSYM\t%s%u\t= {", name,
                        q->actual->t_id );

                if (hashstore)
                        if (q->actual->t_link)
                                fprintf( outfile, "&%s%u,\"%s\",%u,%s", name,
                                        q->actual->t_link->t_id,
                                        q->actual->t_name,
                                        hash( q->actual->t_name ),
                                        q->actual->t_lex
                                        );
                        else
                                fprintf( outfile, "0,\"%s\",%u,%s",
                                        q->actual->t_name,
                                        hash( q->actual->t_name ),
                                        q->actual->t_lex
                                        );
                else if (q->actual->t_link)
                        fprintf( outfile, "&%s%u,\"%s\",%s", name,
                                q->actual->t_link->t_id, q->actual->t_name,
                                q->actual->t_lex
                                );
                else
                        fprintf( outfile, "0,\"%s\",%s", q->actual->t_name,
                                q->actual->t_lex
                                );

                fprintf( outfile, "};\n" );
                }

        for (q = head; q; head = q) {
                q = q->next;
                free( head );
                }
        }


/****************************************************************/
/*                                                              */
/*      s_symbols : output the structure defining the           */
/*   symbol table.                                              */
/*                                                              */
/****************************************************************/

s_symbols ( name, size )
        char            *name;
        unsigned short  size;
        {
        register unsigned short i;

        fprintf( outfile, "\nstatic KEYSYM FARSYM *%s_words[%u] = {\n", name,
                size );

        for (i = 0; i < size; i++)
                if (table[i])
                        fprintf( outfile, "\t&%s%u%c\n", name, table[i]->t_id,
                                ((i < (size - 1)) ? ',' : ' ') );
                else
                        fprintf( outfile, "\t0%c\n",
                                ((i < (size - 1)) ? ',' : ' ') );

        fprintf( outfile, "\t};\n" );
        }


/****************************************************************/
/*                                                              */
/*      s_header : output the header for the symbol table.      */
/*                                                              */
/****************************************************************/

s_header ( name, size )
        char            *name;
        unsigned short  size;
        {
        fprintf( outfile, "\nKEYWORDS %s_table = {%s_words,%u};\n\n\n",
                name, name, size );
        }


static struct entry *head = NULL;

/****************************************************************/
/*                                                              */
/*      talloc -- allocate space for a table entry              */
/*                                                              */
/****************************************************************/

 static struct entry *
talloc ()
        {
        register struct entry *p;

        if (p = head) {
                head = head->t_link;
                return( p );
                }

        if ((p = (struct entry *)malloc( sizeof(struct entry) )))
                return( p );

        memerror();
        }


/****************************************************************/
/*                                                              */
/*      tfree -- free space for a table entry                   */
/*                                                              */
/****************************************************************/

 static
tfree ( p )
        struct entry *p;
        {
        free( p->t_name );
        free( p->t_lex );
        p->t_link = head;
        head = p;
        }


/****************************************************************/
/*                                                              */
/*      memerr -- ran out of heap space; die                    */
/*                                                              */
/****************************************************************/

memerror ()
        {
        fprintf( stderr, "Out of heap space\n" );
        exit( 1 );
        }


#ifdef XENIX

int _stricmp ( first, last )
        register char *first;
        register char *last;
        {
        register f;
        register l;

        do      {
                if ((f = *first++) >= 'A' && f <= 'Z')
                        f += 'a' - 'A';

                if ((l = *last++) >= 'A' && l <= 'Z')
                        l += 'a' - 'A';
                } while (f && f == l);

        return( f - l );
        }

#endif /* XENIX */