/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#define LIBRARIAN 1
#define __BIG_GENERATOR__
#include "defines.h"
#include "nasm.h"
#include <stdio.h>
#include <stdlib.h>
#include "debug.h"
#include "object.h"
#include "lib.h"
#if OS != TOS
# include <time.h>
# if OS == UNIX
#  include <sys/time.h>
# endif
#endif
#include OSBIND
#include NMALLOC_H
#include "code.h"

#include "ldebug.h"


#if ! VERSION
extern char    x1[], x2[], x3[], x4[], x5[], x6[];
char           y1[] = " _GLOBALS ",
               y2[] = " _F_INDEX ",
               y3[] = " _OBJFILE ";
#endif

byte huge      *__program, huge *__p, huge *ptohead;
byte           __c;
word           __pc, __x;
lword          __lx;
static char    trc_loss[] = "File is truncated";

static linksymbol huge  *py;
static obj_h            l;
static char huge        *space;
static char huge        *p_space, huge *old_p_space;

int               version;
#if ! VERSION
int               revision;
#endif
word              gindex, findex;
static lword      bytes, sbytes, ibytes, rbytes, ebytes, ybytes, fbytes;
static lword      magic, gbytes, xbytes, cbytes, size, tbytes;
static lword      gsize;
g_table           globals[ MAXGLOBALS];
f_table           files[ MAXFILES];
extern char       *currfile;
extern int        laber;

extern void       fix_lversion(), fix_lsizes();
#if BIGENDIAN
extern void       flip_libstructs();
#endif

#if VERSION
# undef LIBMAX
# define LIBMAX 0x4000L
#endif

#define SPARE  0x8000L

void pro_init()
{
   extern char    outofmem[];

#if OS == TOS
   if( (size = (long) Malloc( -1L)) < SPARE)
      nferror("Space dangerously low *ouch* in here, aborting...");
   p_space = space = nmalloc( size -= SPARE); /* leave 32K for symbols */
#else
# if OS == UNIX || OS == AMIGA
   p_space = space = nmalloc( size = LIBMAX);   /* ARGH! */
# else
#  if OS == MSDOS
   if( ! (p_space = space = nmalloc( size = LIBMAX)))
      nferror( outofmem);
#  else
#   error "missing code for your machinery"
#  endif
# endif
#endif
   if( laber)
      fprintf( ESTREAM, "%ld bytes space allocated for modules\n", size);
}

void pro_exit()
{
}

#if VERSION
#define thel10seek( x, y)
#else
void thel10seek( i, x)
char  *x;
{
   unsigned long   foo;
   static char     chkbuf[11];

   if( (foo = Fread( i, 10L, chkbuf)) < 10)
      ngferror( foo, trc_loss);
   if( strcmp( chkbuf, x))
      nferror("Not a good NASM library file");
   bcopy( chkbuf, p_space, 10L);
   p_space += 10;
}
#endif

int lload( afile)
char  *afile;                     /* 3 'V's: Van Halen, VfL und Veltins! */
{
   static lib_h   l;
   register long  foo;
   int            fd;

   ENTER("lload");
#if LOWERFILE
   downcase( afile);
#endif
   IMESS( "Trying to open \"%s\" ", (long) afile, 4);
   if( (fd = (int) Fopen( afile, 0)) < 0)
   {
      if( fd == -35)
         nferror("Out of GEMDOS file handles (that's strange..) ?");
      return( 0);
   }
   if( Fread( fd, sizeof( lib_h), &l) != sizeof( lib_h))
      nferror("File is too short to be a library");
   IMESS("Sizeof( lib_h) = %ld", (long) sizeof( lib_h), 4);

   magic    = lbeek( &l.magic);
   version  = dbeek( &l.version);
#if ! VERSION
   revision = dbeek( &l.revision);
#endif
   gbytes   = gsize = lbeek( &l.gbytes);
   xbytes   = lbeek( &l.xbytes);
   cbytes   = lbeek( &l.cbytes);

   if( magic != LIBMAGIC)
      nferror("This is not a library");
#if BIGENDIAN
   if( version < DVERSION)
#else
   if( version < LIB_READ_COMP)
#endif
      nferror("Library was created with an obsolete version");
   if( version > DVERSION)
      nferror("Librarian is too oldfashioned to understand object format");
#if ! VERSION
   if( revision != LIBREVISION)
      nferror("Library revision differs");
#endif
   if( version != DVERSION)
      fix_lsizes( version, &gsize);

   thel10seek( fd, y1);
   if( (foo = Fread( fd, gbytes, globals)) != gbytes)
      ngferror( foo, trc_loss);

   thel10seek( fd, y2);
   if( xbytes)
      if( (foo = Fread( fd, xbytes, &files[0])) != xbytes)
    ngferror( foo, trc_loss);

   thel10seek( fd, y3);
   if( cbytes)
   {
      if( (foo = Fread( fd, cbytes, space)) != cbytes)
         ngferror( foo, trc_loss);
      p_space += cbytes;
   }
   else
      p_space = space;

   Fclose( fd);

   if( version != DVERSION)
      fix_lversion( version, &gbytes, globals);

   gindex = (word) (gbytes / sizeof( g_table));
   findex = (word) (xbytes / sizeof( f_table));

#if BIGENDIAN
   flip_libstructs( gindex, findex, globals, files);
#endif
   if( laber)
      fprintf( ESTREAM, "Read in %s: %d modules with %d globals \
worth %ld bytes total\n", afile, findex, gindex, cbytes);
   LEAVE();
   return(1);
}

void wrapup()
{
   nfree( py);
}


void file_lib( currfile)
char  *currfile;
{
   register char huge      *src,
                 huge      *dst;
   register int            i = FSIGNIFICANT;
   register f_table huge   *p = &files[ findex++];

   ENTER("file_lib");
   src = get_filename( currfile);
   dst = p->name;
   while( (*dst++ = *src++) && i--);
   while( i--)
      *dst++ = 0;
   if( laber)
      fprintf( ESTREAM, "Put %.32s into library [%ld bytes]\n",
                           currfile, tbytes);
   p->seek  = old_p_space - space;
#if OS == TOS
   p->time  = Tgettime();
   p->date  = Tgetdate();
#else
# if OS == MSDOS
   {
      struct time  watch;
      struct date  calendar;

      gettime( &watch);
      getdate( &calendar);
      p->time = ((unsigned) watch.ti_sec >> 1) |
                ((unsigned) watch.ti_min << 5) |
                ((unsigned) watch.ti_hour << 11);
      p->date = ((unsigned) calendar.da_year - 1980 << 9) |
                ((unsigned) calendar.da_mon - 1 << 5) |
                ((unsigned) calendar.da_day - 1);
   }
# else
   {
      struct tm   *watch;
      time_t      the_time;

      if( (the_time = time( NULL)) == -1)
         nferror("Your system rudely refuses to give the current time");

      watch = localtime( &the_time);
      p->time = ((unsigned) watch->tm_sec >> 1) |
                ((unsigned) watch->tm_min << 5) |
                ((unsigned) watch->tm_hour << 11);
      p->date = ((unsigned) watch->tm_year - 1900 << 9) |
                ((unsigned) watch->tm_mon << 5) |
                watch->tm_mday;
   }
# endif
#endif
   p->bytes = tbytes;
   LEAVE();
}


int check_double( name)
register char  *name;
{
   register char huge      *s;
   register g_table huge   *p = &globals[0];
   register int            i = gindex, j;
   static char             buf[ SIGNIFICANT + 1];

   while( i--)
   {
      s = p++->name;
      j = 0;
      while( *s++ == name[j++] && j < SIGNIFICANT);
      if( j >= SIGNIFICANT)
      {
         for( j = 0; j != SIGNIFICANT; buf[j++] = name[j]);
         buf[ SIGNIFICANT] = 0;
         nserror( "Symbol is already in the library", buf);
         return( 1);
      }
   }
   return(0);
}


void sym_lib()
{
   register linksymbol huge   *p = py;
   register char huge         *dst,
                 huge         *src;
   register word              i  = (word) ybytes;
   int                        entered = 0;

   ENTER("sym_lib");
   while( i--)
   {
      if( ! p->refs && ! check_double( p->name))
      {
         if( gindex >= MAXGLOBALS)
            nferror("Sorry, global symbol table space exhausted");
         dst = globals[ gindex].name;
         src = p->name;
#if SIGNIFICANT == 8
         *dst++ = *src++;     /* SIGNIFICANT == 8 */
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;     /* eliminated one copy here */
         *dst   = *src;
#else
         {
            register int   j = SIGNIFICANT;

            while( j--)
               *dst++ = *src++;
         }
#endif
         entered++;
         globals[ gindex++].index = findex;
      }
      p++;
   }
   if( ! entered)
      nferror("Module's unusuable 'coz there's no global label w/value");
   LEAVE();
}

#if VERSION
# define S10EEKS  0
#else
# define S10EEKS  6 * 10L
#endif

void oload( afile)
char  *afile;                     /* 3 'V's: Van Halen, VfL und Veltins! */
{
   register char  *x;
   register long  foo;
   int   i;

   ENTER("oload");
#if LOWERFILE
   downcase( afile);
#endif
   IMESS( "Trying to open \"%s\" ", (long) afile, 4);
   if( (i = (int) Fopen( afile, 0)) < 0)
   {
      MESS("Open failed");
      strcpy( x = nmalloc( strlen( afile) + 5L), afile);
      complete( x, ".o65", 1);
      afile = x;
      if( (i = (int) Fopen( x, 0)) < 0)
      {
         if( i == -35)
            nferror("Out of GEMDOS file handles (that's strange..) ?");
         nferror( "Object file does not exist");
      }
   }
   if( Fread( i, sizeof( obj_h), &l) != sizeof( obj_h))
      nferror("File is too short to be an object file");
   IMESS("Sizeof( obj_h) = %ld", (long) sizeof( obj_h), 4);

   magic    = lbeek( &l.magic);
   version  = dbeek( &l.version);
#if ! VERSION
   revision = dbeek( &l.revision);
#endif
   bytes    = (lword) dbeek( &l.codesize);
   sbytes   = lbeek( &l.segsize);
   ibytes   = lbeek( &l.immsize);
   rbytes   = lbeek( &l.relsize);
   ebytes   = lbeek( &l.expsize);
   ybytes   = lbeek( &l.symsize);
   fbytes   = lbeek( &l.fixsize);

   if( magic != OBJMAGIC)
      nferror("This is not an object file");
   if( version < DVERSION)
      nferror("Object file was created with an obsolete version");
   if( version > DVERSION)
      nferror("Librarian is to oldfashioned to understand this object format");
#if ! VERSION
   if( revision != ASMREVISION)
      nferror("Object file was made under old revision");
#endif
   if( ! ybytes)
      nwarning("Object file contains no global symbols");
   tbytes = sbytes + ibytes + rbytes + ebytes + ybytes + fbytes + S10EEKS +
            bytes + sizeof( obj_h);
   if( ((old_p_space = p_space) - space) + tbytes > size)
      nferror("Can't fit all these files into memory");

   bcopy( &l, p_space, (long) sizeof( obj_h));
   p_space += sizeof( obj_h);

   if( (foo = Fread( i, bytes, p_space)) != bytes)
      ngferror( foo, trc_loss);
   p_space += bytes;

   thel10seek( i, x1);
   if( sbytes)
   {
      if( (foo = Fread( i, sbytes, p_space)) != sbytes)
         ngferror( foo, trc_loss);
      p_space += sbytes;
   }

   thel10seek( i, x2);
   if( ibytes)
   {
      if( (foo = Fread( i, ibytes, p_space)) != ibytes)
         ngferror( foo, trc_loss);
      p_space += ibytes;
   }

   thel10seek( i, x3);
   if( rbytes)
   {
      if( (foo = Fread( i, rbytes, p_space)) != rbytes)
         ngferror( foo, trc_loss);
      p_space += rbytes;
   }

   thel10seek( i, x4);
   if( ebytes)
   {
      if( (foo = Fread( i, ebytes, p_space)) != ebytes)
         ngferror( foo, trc_loss);
      p_space += ebytes;
   }

   thel10seek( i, x5);
   if( ybytes)
   {
      py = nmalloc( ybytes);
      if( (foo = Fread( i, ybytes, py)) != ybytes)
         ngferror( foo, trc_loss);
      bcopy( py, p_space, ybytes);
      p_space += ybytes;
   }

   thel10seek( i, x6);
   if( fbytes)
   {
      if( (foo = Fread( i, fbytes, p_space)) != fbytes)
         ngferror( foo, trc_loss);
      p_space += fbytes;
   }

   ybytes /= sizeof( linksymbol);
#if BIGENDIAN
   fliplinks();
#endif
   LEAVE();
}


#if BIGENDIAN
void  fliplinks()
{
   register lword            i;
   register word huge       *q;
   register linksymbol huge *p;

   for( p = py, i = ybytes; i--; p++)
   {
      q = (word *) p;
      dswap( q);  q++;           /* val   */
      dswap( q);  q++;           /* no    */
      dswap( q);  q++;           /* type  */
      q++;                       /* dummy */
      lswap( q);                 /* refs  */
      POINTER_CHECK( q);
   }
}
#endif

void extract( afile)
char  *afile;
{
   long  size;
   int   fp, i;

   for( i = findex; i--;)
      if( ! strcmp( files[i].name, afile))
      {
         if( (fp = (int) Fkreate( currfile = afile, 0x664)) < 0)
            ngferror( fp, "Couldn't create output file");
         if( Fwrite( fp, (size = files[i].bytes), space + files[ i].seek)
               != size)
            nferror("out of space");
         Fclose( fp);
         return;
      }
   nserror("Module unknown", afile);
}

static char  *months[] =
{
   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};


void list( flag)
{
   int         i;
   unsigned    j, k;

   printf("Modules in library:\n");
   for( i = 0; i != findex; i++)
   {
      register f_table  *p = &files[ i];

      j = p->time;
      k = p->date;
      printf("%5.5ld bytes [%2.2d:%2.2d-%2.2d.%s.%d] : %-32s \n",
               p->bytes,
               j >> 11, (j >> 5) & 0x3F,
               k & 0x1F, months[ ((k >> 5) & 0xF) - 1], 1980 + (k >> 9),
               p->name);
      if( flag)
      {
         for( k = j = 0; j != gindex; j++)
            if( globals[ j].index == i)
            {
               if( ! k)
                  putchar('\t');
               printf("%-8s ", globals[j].name);
               if( ++k == 7)
               {
                  k = 0;
                  putchar( '\n');
               }
            }
         if( k)
            putchar('\n');
      }
   }
}


void delete( afile, flag)
char  *afile;
{
   int   i;

#if LOWERFILE
   downcase( afile);
#endif
   for( i = 0; i != findex; i++)
   {
      register f_table huge   *p = &files[ i];

      if( ! strcmp( p->name, afile))
      {
         int         j;
         lword       k;
         long        off;
         char huge   *tmp;

         if( findex == 1)
         {
            p_space = space;
            findex = gindex = 0;
         }
         else
         {
            register f_table huge *q;

            k   = p->bytes;
            off = p->seek;
            tmp = add_offset( space, off);

            if( i + 1 < findex)         /* Collapse hole */
               bcopy( tmp + k, tmp, cbytes - off - k);
            p_space -= k;

            for( q = p, j = 0; j != findex; q++, j++)  /* Adjust seek adr */
               if( q->seek > off)
                  q->seek -= k;

            if( i < --findex)             /* Remove from file table */
               bcopy( (byte huge *) p + sizeof( f_table), p,
                       (long) (findex - i) * sizeof( f_table));

            for( k = j = 0; j != gindex; j++)  /* Adjust index in globals */
            {
               register g_table  *p = &globals[ j];

               if( p->index == i)
               {
                  if( ! k++)
                     off = j;
               }
               else
                  if( p->index > i)
                     p->index--;
            }

            if( k + off < gindex)
               bcopy( add_offset( globals, off + k),
                      add_offset( globals, off),
                      (long) (gindex - off) * sizeof( g_table));
            gindex -= k;
         }
         if( laber)
            fprintf( ESTREAM, "Deleted module \"%s\"\n", p->name);
         return;
      }
   }
   if( ! flag)
      nserror("Object not in library", afile);
}


static char err[] = "Write to output file failed (Disk full??)";

void write_results( fd)
{
   static lib_h         libheader;
   register byte huge   *p = (byte *) &libheader;
   lword                gbytes, xbytes, cbytes;

   ENTER("write_results");
   plbyte( p, LIBMAGIC);                           /* sort of stupid */
   pdbyte( p, DVERSION);
   pdbyte( p, LIBREVISION);
   plbyte( p, gbytes = (byte huge *) &globals[ gindex] - (byte huge *) globals);
   plbyte( p, xbytes = (byte huge *) &files[ findex] - (byte huge *) files);
   plbyte( p, cbytes = p_space - space);

#if BIGENDIAN
   flip_libstructs( gindex, findex, globals, files); /* do it here again */
#endif

   /* Compound this later on */
   if( Fwrite( fd, sizeof( lib_h), &libheader) != sizeof( lib_h))
      nferror( err);
#if ! VERSION
   if( Fwrite( fd, 10L, y1) != 10L)
      nferror( err);
#endif
   if( Fwrite( fd, gbytes, globals) != gbytes)
      nferror( err);
#if ! VERSION
   if( Fwrite( fd, 10L, y2) != 10L)
      nferror( err);
#endif
   if( Fwrite( fd, xbytes, &files[0]) != xbytes)
      nferror( err);
#if ! VERSION
   if( Fwrite( fd, 10L, y3) != 10L)
      nferror( err);
#endif
   if( Fwrite( fd, cbytes, space) != cbytes)
      nferror( err);
   Fclose( fd);
   LEAVE();
}


