[net.micro.amiga] ALib.c MS Dos and AmigaDos object module Librarian

mykes@3comvax.UUCP (Mike Schwartz) (12/29/85)

*** REPLACE THIS LINE WITH YOUR MESSAGE ***
Here is a simple utility I wrote a while back for the Amiga, which can
save a lot of compilation time.  It is a Lattice and AmigaDos compatible
object module librarian with basic replace/add, delete, and directory
functions.  ALib is like an archive-type tool for those .o files created
by the Lattice 'C' compler.  Smaller program modules can be compiled and
added together into libraries, which can be linked with other object modules.
ALib is totally portable between the IBM PC and Native Amiga environments.

Theory of Operation:
   AmigaDos object files have a structure that allows them to be concatonated
   together and still be valid input to the linker (ALink).  ALink reads in
   object modules in two ways, although it may seem like there are two types
   of object modules.  The filenames listed in the from field are considered
   object modules, and ALL THE OBJECT CODE in the module will be included in
   the output (executable) file (Note: This last statement may not be true,
   but for purposes of this librarian, it doesn't matter).  The filenames
   listed after the library= flag on the linker command line represent
   library modules from which the linker will only include the code for
   those objects referenced by the object modules (this appears to be the
   case from the way my disk drives appear to be seeking thousands of times
   through lc.lib and amiga.lib during Alink).

   ALib allows you to create both object modules and libraries.  All of the
   modules (input files to ALink) created by ALib have .lib extensions on
   them, but may be used as either object modules or libraries, or as both.
   This allows the programmer the flexibility of including any or all of
   the linker's options while compiling smaller modules and building 
   libraries of functions that can be used in multiple programs.

   ALib also creates a .dir file for each .lib file, which contains the
   information necessary to dynamically substitute, add, or remove
   any of the individual modules that make up a library.  This information
   includes the name of the .o files used to build the library, the offset
   into the .lib file where the object module from the .o can be found,
   and the size of the object module.

   ALib works "in-place" on a library, which conserves on disk space 
   tremendously.  This limits the size of output .lib files to the
   amount of free memory that ALib has available to it (over 300K on
   my Amiga).  A block of memory is allocated for each object module
   already existing in the library, as well as for the object modules that
   are to be added.  These blocks of memory are exactly large enough to 
   hold the entire .o input file.  Disk access by ALink is extremely
   efficient, with the minimal amount of head seeks, because it reads
   the entire .o file with one Lattice read() call.  The final output
   library is also written in Large chunks of Lattice write() calls.

   The Replace module option is actually a delete module and an add
   module operation.  The delete module operation is simply "forgetting"
   about it - freeing up the memory used by the module, and deleting
   the directory entry for the module.  The add module is simply reading
   the new module into memory and adding a directory entry for it.
   When all the new additions or deletions are done, the new library
   is recorded back on disk.  

Comments and Ideas:
   I used ALib to make clib.lib, which contains lc.lib, amiga.lib, and
   debug.lib.  I can use Library=clib.lib instead of 
   Library=lc.lib+amiga.lib+debug.lib when ALink-ing Lattice 'C' Programs.
   I use clib.lib all the time, and it works fine.  I think that it even
   saves some seek time to have one big file instead of 3 separate ones.
   ALink seems to do a lot of head seeks to clib.lib on disk, but from
   ramdisk, I imagine ALink might actually run fast???  

   I also am building some libraries, which contain some real useful
   subroutines and functions to complement the AmigaDos, Intuition, and
   Rom Kernel functions.  Things like serial port/modem protocols,
   multiple window management, etc.  I hope to use these in more than one
   program.

Enjoy!

=============================Cut Here=====================================
/*
 * ALIB Rev. 1.0
 * Amiga Object Module Librarian
 * Programmed by Mike Schwartz
 *               (C)1985, MS Software, all rights reserved.
 *
 * Feel free to distribute this program on a no-charge basis.
 *
 * Use:
 *    ALIB  option  library filelist
 * Where
 *    option may be one of the following:
 *       D  =  delete object modules from library
 *       R  =  replace object modules (or add if not defined)
 *       L  =  directory of library
 *    library is the name of the library file to be created or modified.
 *       if no .lib extension is provided on the command line, it is
 *       appended.  Also note that a file library.dir is created for
 *       each library file and is managed by this program.
 *    filelist is a list of object module filenames separated by spaces
 *       or commas.  Note that if .o is not present in the filenames,
 *       .o will be appended.
 */
#include "stdio.h"
#include "fcntl.h"

/*
 * The following structure defines the records in the .dir file for
 * the library.
 */
#define  LD    struct   libdir_def
struct   libdir_def     {
   char  object_filename[80];       /* name of .o file */
   long  module_offset;             /* offset of module in .lib file */
   long  module_size;               /* size of module in bytes */
   };

/*
 * When the library is openned, each object module is read into memory
 * and managed with the following structure.
 */
#define  LL    struct   liblist_def
struct   liblist_def    {
   struct   libdir_def  dir_entry;  /* directory entry for module */
   char     *object_module;         /* actual text of module */
   LL       *next;                  /* pointer to next in list */
   };

/*
 * external functions
 */
extern   char  *getmem();
extern   long  lseek();

/*
 * Global variables
 */
LL    *objlist=NULL;                /* linked list of object modules */

char  libname[80];                  /* name of library file */
int   libfd;                        /* file descriptor for library file */
char  objname[80];                  /* name of object file */
int   objfd;                        /* file descriptor for object file */
char  dirname[80];                  /* name of directory file */
int   dirfd;                        /* file descriptor for directory file */

char  buf[512];                     /* misc buffer */

char  option;                       /* user selected option */
/*
 * This function prints the help messages and exits.
 */
help() {
   printf("Use:\n");
   printf("   ALIB  option  library filelist \n");
   printf("Where\n");
   printf("   option may be one of the following:\n");
   printf("      D  =  delete object modules from library\n");
   printf("      R  =  replace object modules (or add if not defined) \n");
   printf("      L  =  directory of library\n");
   printf("   library is the name of the library file to be created or modified.\n");
   printf("      if no .lib extension is provided on the command line, it is \n");
   printf("      appended.  Also note that a file library.dir is created for\n");
   printf("      each library file and is managed by this program. \n");
   printf("   filelist is a list of object module filenames separated by spaces\n");
   printf("      or commas.  Note that if .o is not present in the filenames, \n");
   printf("      .o will be appended. \n");
   exit(1);
   }

/*
 * The following function opens the library and directory file.  If the
 * file does not exist, then the user is asked if he wants to create it.
 * If the library exists, then it is read in and built in a linked list.
 */
open_library() {
   LL    *lp;           /* pointer to module node */

   /*
    * Open the library file and directory file.
    */
   libfd = open(libname, O_RDONLY | O_RAW);
   if (libfd == -1) {
      /*
       * Library does not exist, prompt user for creation.
       */
      while (1) {
         printf("%s does not exist, create it? (Y/N): ", libname);
         gets(buf);
         if (buf[0] == 'y' || buf[0] == 'Y')
            return !0;
         if (buf[0] == 'n' || buf[0] == 'N') {
            printf("Alib abandoned\n");
            exit(1);
            }
         }
      }
   dirfd = open(dirname, O_RDONLY | O_RAW);
   if (dirfd == -1) {
      printf("%s has been corrupted\n", dirname);
      printf("Alib abandoned\n");
      exit(1);
      }
   /*
    * library and directory files are open, read in the library.
    */
   while (1) {
      /*
       * Allocate a node to hold the object module.
       */
      lp = (LL *)getmem(sizeof(LL));
      if (lp == NULL) {
         printf("Not enough memory.\nAlib abandoned\n");
         exit(1);
         }
      /*
       * Check for end of file.
       */
      if (read(dirfd, (char *)&lp->dir_entry, sizeof(LD)) != sizeof(LD))
         break;
      /*
       * Allocate a buffer to hold the object file image.
       */
      lp->object_module = getmem(lp->dir_entry.module_size);
      if (lp->object_module == NULL) {
         printf("Not enough memory.\nAlib abandoned\n");
         exit(1);
         }
      /*
       * Read in the object module
       */
      if (read(libfd, lp->object_module, lp->dir_entry.module_size) != 
                 lp->dir_entry.module_size) {
         printf("Library file is corrupted.\nAlib abandoned\n");
         exit(1);
         }
      /*
       * Add module to linked list.
       */
      lp->next = objlist;
      objlist = lp;
      }
   /*
    * Library has been read in ok.  Close files and return.
    */
   close(libfd);
   close(dirfd);
   printf("Library file read in ok\n");
   }

/*
 * The following function removes an object module from the linked list
 * and frees up any memory used by it.
 */
kill_module(name)
char  *name;
{
   LL    *lp;
   LL    *lp2;

   if (objlist == NULL)
      return 0;
   strcpy(objname, name);
   strcat(objname, ".o");
   if (strcmp(objlist->dir_entry.object_filename,  objname) == 0) {
      lp = objlist->next;
      rlsmem(objlist->object_module, objlist->dir_entry.module_size);
      rlsmem((char *)objlist, sizeof(LL));
      objlist = lp;
      return !0;
      }
   for (lp = objlist; lp->next != NULL; lp=lp->next)
      if (strcmp(lp->next->dir_entry.object_filename, objname) == 0) {
         lp2 = lp->next;
         lp->next = lp2->next;
         rlsmem(lp2->object_module, lp2->dir_entry.module_size);
         rlsmem((char *)lp2, sizeof(LL));
         return !0;
         }
   return 0;
   }

/*
 * The following function
 */
add_module(name)
char  *name;
{
   LL    *lp;
   int   len;

   strcpy(objname, name);
   strcat(objname, ".o");
printf("adding %s\n", objname);

   /*
    * Open the object file
    */
   objfd = open(objname, O_RDONLY | O_RAW);
   if (objfd == -1) {
      printf("%s cannot be added\nAlib abandoned\n", objname);
      exit(1);
      }
   /*
    * Allocate a node to hold the file.
    */
   lp = (LL *)getmem(sizeof(LL));
   if (lp == NULL) {
      printf("Not enough memory.\nAlib abandoned\n");
      exit(1);
      }
   /*
    * Initialize it.
    */
   strcpy(lp->dir_entry.object_filename, objname);
   lp->dir_entry.module_size = lseek(objfd, 0, 2);
printf("module size = %d\n", lp->dir_entry.module_size);
   lseek(objfd, 0, 0);
   /*
    * Allocate the buffer to read the object module into.
    */
   lp->object_module = getmem(lp->dir_entry.module_size);
   if (lp->object_module == NULL) {
      printf("Not enough memory.\nAlib abandoned\n");
      exit(1);
      }
   /*
    * Read in the file.
    */
   if (read(objfd, lp->object_module, lp->dir_entry.module_size) != 
             lp->dir_entry.module_size) {
      printf("Library file is corrupted.\nAlib abandoned\n");
      exit(1);
      }
   /*
    * Add module to linked list.
    */
   lp->next = objlist;
   objlist = lp;
   /*
    * Close object file and return.
    */
   close(objfd);
   printf("%s added\n", name);
   }

/*
 * The following routine writes the library from memory and exits.
 */
close_library() {
   LL    *lp;
   long  file_position;
   long  length;

   /*
    * Open the library
    */
   libfd = open(libname, O_CREAT | O_RAW | O_WRONLY);
   if (libfd == -1) {
      printf("Error openning %s for output\nAlib abandoned", libname);
      exit(1);
      }
   dirfd = open(dirname, O_CREAT | O_RAW | O_WRONLY);
   if (dirfd == -1) {
      printf("Error openning %s for output\nAlib abandoned", dirname);
      exit(1);
      }
   /*
    * Write out the individual modules and directory records.
    */
   file_position = 0;
   for (lp = objlist; lp != NULL; lp = lp->next) {
      length = write(libfd, lp->object_module, lp->dir_entry.module_size);
      if (length != lp->dir_entry.module_size) {
         printf("Error writing to library file\nAlib abandoned\n");
         exit(1);
         }
      lp->dir_entry.module_offset = file_position;
      file_position += length;
      if (write(dirfd, (char *)&lp->dir_entry, sizeof(LD)) != sizeof(LD)) {
         printf("Error writing to directory file\nAlib abandoned\n");
         exit(1);
         }
      }
   /*
    * Close the library files
    */
   close(libfd);
   close(dirfd);
   printf("library is %ld bytes\nAlib complete\n", file_position);
   exit(0);
   }


/*
 * Main program.
 */
main(argc, argv)
int   argc;
char  *argv[];
{
   int   count;

   printf("ALIB Rev. 1.0 \n");
   printf("Amiga Object Module Librarian\n");
   printf("Programmed by Mike Schwartz\n");
   printf("(C)1985 MS Software, all rights reserved\n");

   /*
    * check for command line parameters present.
    */
   if (argc < 3)
      help();
   /*
    * setup the option and filenames
    */
   option = argv[1][0];
   strcpy(libname, argv[2]);
   strcpy(dirname, argv[2]);
   strcat(libname, ".lib");
   strcat(dirname, ".dir");
   if (option == 'l' || option == 'L') {
      directory();
      exit(0);
      }
   if (option != 'd' && option != 'D' && option != 'r' && option != 'R')
      help();
   /*
    * Open the library file
    */
   open_library();
   for (count = 3; count < argc; count++) {
      switch(option) {
         case 'd':
         case 'D':
            printf("deleting %s from library\n", argv[count]);
            if (!kill_module(argv[count]))
               printf("%s not defined in library\n", argv[count]);
            else
               printf("deleted.\n");
            break;
         case 'r':
         case 'R':
            kill_module(argv[count]);
            add_module(argv[count]);
            break;
         }
      }
   close_library();
   }

directory() {
   LD    d_entry;
   int   count;
   int   bytes;

   dirfd = open(dirname, O_RDONLY | O_RAW);
   if (dirfd == -1) {
      printf("%s not found\n", dirname);
      exit(1);
      }
   printf("Directory of library %s\n", libname);
   printf("Module Name                    Size   Offset\n");
   count = bytes = 0;
   while (read(dirfd, (char *)&d_entry, sizeof(LD)) == sizeof(LD)) {
      printf("%-30.30s %-6d %-6d\n", d_entry.object_filename, 
               d_entry.module_size, d_entry.module_offset);
      count++;
      bytes += d_entry.module_size;
      }
   printf("Library consists of %d entries totalling %d bytes\n", count,
            bytes);
   close(dirfd);
   }