/*
 * lmem.c -- memory initialization and allocation; also parses arguments.
 */

#include "..\h\config.h"
#include "general.h"
#include "tproto.h"
#include "globals.h"
#include "link.h"

/*
 * The following code is operating-system dependent [@lmem.01].  It includes
 *  files that are system dependent.
 */

#if PORT
   /* nothing is needed */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA || ATARI_ST || HIGHC_386 || MACINTOSH || VMS
   /* nothing is needed */
#endif					/* AMIGA || ATARI_AT || HIGHC_386 ... */

#if MSDOS
#if MICROSOFT
#include <sys\types.h>
#include <sys\stat.h>
#endif					/* MICROSOFT */
#if TURBO
#include <sys\stat.h>
#endif					/* TURBO */
#endif					/* MSDOS */

#if MVS || VM
#if SASC
#include <fcntl.h>
#else					/* SASC */
#include <file.h>
#endif					/* SASC */
#endif					/* MVS || VM */

#if OS2
#if MICROSOFT
#include <sys\types.h>
#include <sys\stat.h>
#endif					/* MICROSOFT */
#endif					/* OS2 */

#if UNIX
#ifndef ATT3B
#ifdef CRAY
#define word word_fubar
#include <sys\types.h>
#include <sys\stat.h>
#undef word
#else					/* CRAY */
#include <sys\types.h>
#include <sys\stat.h>
#endif					/* CRAY */
#endif					/* ATT3B */
#endif					/* UNIX */

/*
 * End of operating-system specific code.
 */

/*
 * Prototypes.
 */

hidden struct	lfile *alclfile	Params((char *name));
hidden int	canread		Params((char *file));
hidden int	trypath		Params((char *name,char *file));

#ifdef MultipleRuns
hidden novalue	freelfile	Params((struct lfile *p));
#endif					/* MultipleRuns */

/*
 * Memory initialization
 */

struct gentry **lghash;		/* hash area for global table */
struct ientry **lihash;		/* hash area for identifier table */
struct fentry **lfhash;		/* hash area for field table */

struct lentry *lltable;		/* local table */
struct gentry *lgtable;		/* global table */
struct centry *lctable;		/* constant table */
struct ientry *litable;		/* identifier table */
struct fentry *lftable;		/* field table headers */
struct rentry *lrtable;		/* field table record lists */
struct ipc_fname *fnmtbl;	/* table associating ipc with file name */
struct ipc_line *lntable;	/* table associating ipc with line number */

char *lsspace;			/* string space */
word *labels;			/* label table */
char *codeb;			/* generated code space */

struct gentry *lgfree;		/* free pointer for global table */
struct ientry *lifree;		/* free pointer for identifier table */
struct fentry *lffree;		/* free pointer for field table headers */
struct rentry *lrfree;		/* free pointer for field table record lists */
struct ipc_fname *fnmfree;	/* free pointer for ipc/file name table */
struct ipc_line *lnfree;	/* free pointer for ipc/line number table */
char *lsfree;			/* free pointer for string space */
char *codep;			/* free pointer for code space */

char *lsend;			/* pointer to end of string space */

static char *ipath;		/* path for iconx */

#ifdef MultipleRuns
extern word pc;
extern int fatals;
extern int nlflag;
extern int lstatics;
extern int nfields;
#endif					/* MultipleRuns */

/*
 * linit - scan the command line arguments and initialize data structures.
 */
novalue linit()
   {
   struct gentry **gp;
   struct ientry **ip;
   struct fentry **fp;

   llfiles = NULL;		/* Zero queue of files to link. */

#ifdef EnvVars
   ipath = getenv("IPATH");
#else					/* EnvVars */
   ipath = NULL;
#endif					/* EnvVars */

   if (ipath == NULL)

/*
 * The following code is operating-system dependent [@lmem.02].  Set default for
 *  IPATH.
 */

#if PORT
   /* something is needed */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA
   /*
    * There is no environment, so set ipath to the null string. The
    *  current directory is searched anyway and there is no symbol
    *  to force current path search.
    */
      ipath = "";
#endif					/* AMIGA */

#if ATARI_ST || UNIX
      ipath = ".";
#endif					/* ATARI_ST || UNIX */

#if HIGHC_386 || MSDOS || OS2
      ipath = ";";
#endif					/* HIGHC_386 || MSDOS || OS2 */

#if MACINTOSH
#if MPW || LSC
      ipath = ":";
#endif					/* MPW || LSC */
#endif					/* MACINTOSH */

#if MVS || VM
      ipath = "";
#endif					/* MVS || VS */

#if VMS
      ipath = "[]";
#endif					/* VMS */

/*
 * End of operating-system specific code.
 */

   /*
    * Allocate the various data structures that are used by the linker.
    */
   lghash   = (struct gentry **) tcalloc(ghsize, sizeof(struct gentry *));
   lihash   = (struct ientry **) tcalloc(ihsize, sizeof(struct ientry *));
   lfhash   = (struct fentry **) tcalloc(fhsize, sizeof(struct fentry *));

   lltable  = (struct lentry *) tcalloc(lsize, sizeof(struct lentry));
   lctable  = (struct centry *) tcalloc(csize, sizeof(struct centry));

   lffree = lftable  = (struct fentry *) tcalloc(fsize, sizeof(struct fentry));
   lgfree = lgtable  = (struct gentry *) tcalloc(gsize, sizeof(struct gentry));
   lifree = litable  = (struct ientry *) tcalloc(isize, sizeof(struct ientry ));
   lnfree = lntable  = (struct ipc_line*)tcalloc(nsize,sizeof(struct ipc_line));
   lrfree = lrtable  = (struct rentry *) tcalloc(rsize, sizeof(struct rentry));

   lsfree = lsspace = (char *) tcalloc(stsize, sizeof(char));
   lsend = lsspace + stsize - 1;

   fnmtbl = (struct ipc_fname *) tcalloc(fnmsize, sizeof(struct ipc_fname));
   fnmfree = fnmtbl;

   labels  = (word *) tcalloc(maxlabels, sizeof(word));
   codep = codeb = (char *) tcalloc(maxcode, 1);

   /*
    * Zero out the hash tables.
    */
   for (gp = lghash; gp < &lghash[ghsize]; gp++)
      *gp = NULL;
   for (ip = lihash; ip < &lihash[ihsize]; ip++)
      *ip = NULL;
   for (fp = lfhash; fp < &lfhash[fhsize]; fp++)
      *fp = NULL;

#ifdef MultipleRuns

   /*
    * Initializations required for repeated program runs.
    */

   pc = 0;				/* In lcode.c	*/
   nrecords = 0;			/* In lglob.c	*/

#ifdef EvalTrace
   colmno = 0;				/* In link.c	*/
#endif					/* EvalTrace */

   lineno = 0;				/* In link.c	*/
   fatals = 0;				/* In link.c	*/
   nlflag = 0;				/* In llex.c	*/
   lstatics = 0;			/* In lsym.c	*/
   nfields = 0;				/* In lsym.c	*/
#endif					/* MultipleRuns */

   /*
    * Install "main" as a global variable in order to insure that it
    *  is the first global variable.  iconx/start.s depends on main
    *  being global number 0.
    */
   putglobal(instid("main"), F_Global, 0, 0);
   }

#ifdef DeBugLinker
/*
 * dumplfiles - print the list of files to link.  Used for debugging only.
 */

novalue dumplfiles()
   {
   struct lfile *p,*lfls;

   fprintf(stderr,"lfiles:\n");
   lfls = llfiles;
   while (p = getlfile(&lfls))
       fprintf(stderr,"'%s'\n",p->lf_name);
   fflush(stderr);
   }
#endif					/* DeBugLinker */

/*
 * alsolink - create an lfile structure for the named file and add it to the
 *  end of the list of files (llfiles) to generate link instructions for.
 */
static char *pptr;
novalue alsolink(name)
char *name;
   {
   struct lfile *nlf, *p;
   char file[256], ok;

   ok = 0;
   if (canread(name)) {
      ok++;
      strcpy(file, name);
      }
   else {
      /*
       * Can't find name in current directory so try paths in
       *   IPATH if there are any. (IPATH cannot override the
       *   current-directory-first strategy so there is probably
       *   no reason to initialize IPATH to the various current
       *   directory markers as is done above, since this will
       *   only result in a duplicate failed search. Note that
       *   the access test which is done above in some systems
       *   will have already caused ilink to exit if name is
       *   not found in the current directory anyway so ipath
       *   was never able to search other paths first in any case.)
       */


      pptr = ipath;

      while (trypath(name, file)) {
         if (canread(file)) {
            ok++;
            break;
            }
         }
      }

   if (!ok)
     quitf("cannot resolve reference to file '%s'",name);

   nlf = alclfile(file);
   if (llfiles == NULL) {
      llfiles = nlf;
      }
   else {
      p = llfiles;
      while (p->lf_link != NULL) {
        if (strcmp(p->lf_name,file) == 0)
           return;
        p = p->lf_link;
        }
      if (strcmp(p->lf_name,file) == 0)
        return;
      p->lf_link = nlf;
      }
   }

/*
 * getlfile - return a pointer (p) to the lfile structure pointed at by lptr
 *  and move lptr to the lfile structure that p points at.  That is, getlfile
 *  returns a pointer to the current (wrt. lptr) lfile and advances lptr.
 */
struct lfile *getlfile(lptr)
struct lfile **lptr;
   {
   struct lfile *p;

   if (*lptr == NULL)
      return (struct lfile *)NULL;
   else {
      p = *lptr;
      *lptr = p->lf_link;
      return p;
      }
   }

/*
 * canread - see if file can be read and be sure that it's just an
 *  ordinary file.
 */
static int canread(file)
char *file;
   {

/*
 * The following code is operating-system dependent [@lmem.03]. Check to see if
 *  .u1 file can be read.
 */

#if PORT
   /* something is needed */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA
   char lclname[MaxFileName];
   if (access(makename(lclname,TargetDir,file,U1Suffix),4) == 0)
      if (getfa(lclname) == -1)
         return 1;
#endif					/* AMIGA */

#if ATARI_ST || HIGHC_386
   {
   FILE *f;
   char lclname[MaxFileName];
   if ((f = fopen(makename(lclname,TargetDir,file,U1Suffix), ReadText)) == NULL)
      return 0;
   else {
      fclose(f);
      return 1;
      }
   }
#endif					/* ATARI_ST || HIGHC_386 */

#if MACINTOSH
#if MPW || LSC
   {
   FILE *f;
   if ((f = fopen(file,ReadText)) != NULL) {
      fclose(f);
      return 1;
      }
   }
#endif					/* MPW || LSC */
#endif					/* MACINTOSH */

#if MSDOS
#if MICROSOFT || TURBO
   struct stat statb;
   if (access(file,4) == 0) {
      stat(file,&statb);
      if (statb.st_mode & S_IFREG)
         return 1;
      }
#else					/* MICROSOFT || TURBO */
   char lclname[MaxFileName];
   if (access( makename(lclname,TargetDir,file,U1Suffix), 4 ) == 0 )
      return 1;
#endif					/* MICROSOFT || TURBO */
#endif					/* MSDOS */

#if MVS || VM
#if SASC
   char lclname[MaxFileName];
   if (access(makename(lclname,(char*)NULL,file,U1Suffix),R_OK) == 0)
      return 1;
#else					/* SASC */
   FILE *f;                     /* can't use access because it will */
                                /* accept LRECL, etc. */

   if ((f = fopen(file,"r")) != NULL {
      fclose(f);
      return 1;
      }
#endif					/* SASC */
#endif					/* MVS || VM */

#if OS2
#if MICROSOFT
   struct stat statb;
   if (access(file,4) == 0) {
      stat(file,&statb);
      if (statb.st_mode & S_IFREG)
         return 1;
      }
#endif					/* MICROSOFT || TURBO */
#endif					/* OS2 */

#if UNIX
   struct stat statb;
   if (access(file,4) == 0) {
      stat(file,&statb);
      if (statb.st_mode & S_IFREG)
         return 1;
      }
#endif					/* UNIX */

#if VMS
   if (access(file,4) == 0)
      return 1;
#endif					/* VMS */

/*
 * End of operating-system specific code.
 */

   return 0;
   }


/*
 * trypath - form a file name in file by concatenating name onto the
 *  next path element.
 */
static int trypath(name,file)
char *name, *file;
   {
   char c;

   while (*pptr == ' ')
      pptr++;
   if (!*pptr)
      return 0;
   do {
      c = (*file++ = *pptr++);
      }
      while (c != ' ' && c);
   pptr--;
   file--;

/*
 * The following code is operating-system dependent [@lmem.04].  Append path
 *  character.
 */

#if PORT
   /* nothing is needed */
Deliberate Syntax Error
#endif					/* PORT */

#if AMIGA
   file--;
   switch (*file) {

      case ':':
      case '/':
                 file++;
                 break;       /* add nothing, delimiter already there */
      default:
                 *file++ = '/';
      }
#endif					/* AMIGA */

#if ATARI_ST || MACINTOSH || MVS || VM || VMS
   /* nothing is needed */
#endif					/* ATARI_ST || MACINTOSH */

#if HIGHC_386
   *file++ = '\\';
#endif					/* HIGHC_386 */

#if UNIX || MSDOS || OS2
   *file++ = '/';			/* should check for delimiter */
#endif					/* UNIX || MSDOS || OS2 */

/*
 * End of operating-system specific code.
 */

   while (*file++ = *name++);
   *file = 0;
   return 1;
   }

/*
 * alclfile - allocate an lfile structure for the named file, fill
 *  in the name and return a pointer to it.
 */
static struct lfile *alclfile(name)
char *name;
   {
   struct lfile *p;

   p = (struct lfile *) alloc(sizeof(struct lfile));
   p->lf_link = NULL;
   p->lf_name = salloc(name);
   return p;
   }

#ifdef MultipleRuns
/*
 * freelfile - free memory of an lfile structure.
 */
static novalue freelfile(p)
struct lfile *p;
   {
   free(p->lf_name);
   free((char *) p);
   }
#endif						/* MultipleRuns */

/*
 * lmfree - free memory used by the linker
 */
novalue lmfree()
   {
   struct lfile *lf, *nlf;

   free((char *) lghash);   lghash = NULL;
   free((char *) lihash);   lihash = NULL;
   free((char *) lfhash);   lfhash = NULL;
   free((char *) lltable);   lltable = NULL;
   free((char *) lctable);   lctable = NULL;
   free((char *) lftable);   lftable = NULL;
   free((char *) lgtable);   lgtable = NULL;
   free((char *) litable);   litable = NULL;
   free((char *) lntable);   lntable = NULL;
   free((char *) lrtable);   lrtable = NULL;
   free((char *) lsspace);   lsspace = NULL;
   free((char *) fnmtbl);   fnmtbl = NULL;
   free((char *) labels);   labels = NULL;
   free((char *) codep);   codep = NULL;

#ifdef MultipleRuns
   for (lf = llfiles; lf != NULL; lf = nlf) {
      nlf = lf->lf_link;
      freelfile(lf);
      }
   llfiles = NULL;

#if MACINTOSH
#if MPW
/* #pragma unused(nlf,lf) */
#endif					/* MPW */
#endif					/* MACINTOSH */
#endif					/* MultipleRuns */

   }
