/* ** Splint - annotation-assisted static program checker ** Copyright (C) 1994-2003 University of Virginia, ** Massachusetts Institute of Technology ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the ** Free Software Foundation; either version 2 of the License, or (at your ** option) any later version. ** ** This program is distributed in the hope that it will be useful, but ** WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** General Public License for more details. ** ** The GNU General Public License is available from http://www.gnu.org/ or ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ** MA 02111-1307, USA. ** ** For information on splint: info@splint.org ** To report a bug: splint-bug@splint.org ** For more information: http://www.splint.org */ /* ** fileTable.c ** ** replaces filenamemap.c ** based (loosely) on typeTable.c ** ** entries in the fileTable are: ** ** name - name of the file ** type - kind of file (a temp file to be deleted?) ** link - derived from this file ** */ /* * Herbert 04/1997: * - Added conditional stuff (macros OS2 and MSDOS) to make names of temporary * files under Windows or OS/2 not larger than 8+3 characters to avoid * trouble with FAT file systems or Novell Netware volumes. * - Added include of new header file portab.h containing OS dependent stuff. * - Changed occurance of '/' as path delimiter to a macro. * - Added conditional stuff (#define and #include) for IBM's compiler. */ # include # include # include # include "splintMacros.nf" # include "basic.h" # include "osd.h" # include "llmain.h" # ifdef WIN32 # include # else # if defined(__IBMC__) && defined(OS2) # include # include # define getpid _getpid # define S_IRUSR S_IREAD # define S_IWUSR S_IWRITE # define S_IXUSR S_IEXEC # endif # endif /*@access fileId*/ static void fileTable_addOpen (fileTable p_ft, /*@observer@*/ FILE *p_f, /*@only@*/ cstring p_fname) /*@modifies p_ft@*/ ; static bool fileTable_inRange (fileTable ft, fileId fid) /*@*/ { return (fileTable_isDefined (ft) && (fid >= 0) && (fid < ft->nentries)); } static fileId fileTable_internAddEntry (fileTable p_ft, /*@only@*/ ftentry p_e) /*@modifies p_ft@*/ ; static /*@only@*/ cstring makeTempName (cstring p_dir, cstring p_pre, cstring p_suf); static /*@only@*/ cstring fileType_unparse (fileType ft) { switch (ft) { case FILE_NORMAL: return cstring_makeLiteral ("normal"); case FILE_NODELETE: return cstring_makeLiteral ("normal"); case FILE_LSLTEMP: return cstring_makeLiteral ("ltemp"); case FILE_HEADER: return cstring_makeLiteral ("header"); case FILE_XH: return cstring_makeLiteral ("xh"); case FILE_MACROS: return cstring_makeLiteral ("macros"); case FILE_METASTATE: return cstring_makeLiteral ("metastate"); } BADEXIT; } static int fileTable_getIndex (fileTable ft, cstring s) { int res; cstring abspath; if (ft == NULL) return NOT_FOUND; abspath = osd_absolutePath (cstring_undefined, s); if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES)) { abspath = cstring_downcase (abspath); } DPRINTF (("Absolute path: %s: %s", s, abspath)); res = cstringTable_lookup (ft->htable, abspath); cstring_free (abspath); return res; } static cstring ftentry_unparse (fileTable ft, ftentry fte) { if (fileId_isValid (fte->fder)) { llassert (fileTable_isDefined (ft)); return message ("%s %q %d (%s)", fte->fname, fileType_unparse (fte->ftype), fte->fder, ft->elements[fte->fder]->fname); } else { return message ("%s %q", fte->fname, fileType_unparse (fte->ftype)); } } /*@only@*/ cstring fileTable_unparse (fileTable ft) { cstring s = cstring_undefined; int i; if (fileTable_isUndefined (ft)) { return (cstring_makeLiteral ("")); } for (i = 0; i < ft->nentries; i++) { s = message ("%s\n[%d] %q", s, i, ftentry_unparse (ft, ft->elements[i])); } return s; } void fileTable_printTemps (fileTable ft) { if (fileTable_isDefined (ft)) { int i; for (i = 0; i < ft->nentries; i++) { if (ft->elements[i]->ftemp) { if (fileId_isValid (ft->elements[i]->fder)) { fprintf (stderr, " %s:1\n\t%s:1\n", cstring_toCharsSafe (ft->elements[ft->elements[i]->fder]->fname), cstring_toCharsSafe (ft->elements[i]->fname)); } else { fprintf (stderr, "[no file]\n\t%s:1\n", cstring_toCharsSafe (ft->elements[i]->fname)); } } } } } /* ** loads in fileTable from fileTable_dump */ static /*@notnull@*/ ftentry ftentry_create (/*@keep@*/ cstring tn, bool temp, fileType typ, fileId der) { ftentry t = (ftentry) dmalloc (sizeof (*t)); if (cstring_isUndefined (tn)) { llbug (cstring_makeLiteral ("Undefined filename!")); } t->fname = tn; t->basename = cstring_undefined; t->ftemp = temp; t->ftype = typ; t->fder = der; /* Don't set these until the basename is needed. */ t->fsystem = FALSE; t->fspecial = FALSE; return t; } static void ftentry_free (/*@only@*/ ftentry t) { cstring_free (t->fname); cstring_free (t->basename); sfree (t); } /*@only@*/ /*@notnull@*/ fileTable fileTable_create () { fileTable ft = (fileTable) dmalloc (sizeof (*ft)); ft->nentries = 0; ft->nspace = FTBASESIZE; ft->elements = (ftentry *) dmalloc (FTBASESIZE * sizeof (*ft->elements)); ft->htable = cstringTable_create (FTHASHSIZE); ft->nopen = 0; ft->nopenspace = FTBASESIZE; ft->openelements = (foentry *) dmalloc (FTBASESIZE * sizeof (*ft->openelements)); return (ft); } /*@-bounds@*/ static void fileTable_grow (fileTable ft) { int i; ftentry *newent; llassert (fileTable_isDefined (ft)); ft->nspace = FTBASESIZE; newent = (ftentry *) dmalloc ((ft->nentries + ft->nspace) * sizeof (*newent)); for (i = 0; i < ft->nentries; i++) { newent[i] = ft->elements[i]; } sfree (ft->elements); ft->elements = newent; } /*@=bounds@*/ static void fileTable_growOpen (fileTable ft) { int i; foentry *newent; llassert (fileTable_isDefined (ft)); ft->nopenspace = FTBASESIZE; newent = (foentry *) dmalloc ((ft->nopen + ft->nopenspace) * sizeof (*newent)); for (i = 0; i < ft->nopen; i++) { newent[i] = ft->openelements[i]; } sfree (ft->openelements); ft->openelements = newent; } static fileId fileTable_internAddEntry (fileTable ft, /*@only@*/ ftentry e) { llassert (fileTable_isDefined (ft)); if (ft->nspace <= 0) fileTable_grow (ft); ft->nspace--; DPRINTF (("Adding: %s", e->fname)); if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES)) { cstring sd = cstring_downcase (e->fname); cstringTable_insert (ft->htable, sd, ft->nentries); } else { cstringTable_insert (ft->htable, cstring_copy (e->fname), ft->nentries); } /* evans 2002-07-12: Before, there was no cstring_copy above, and e->fname was free'd in the if branch. Splint should have caught this, and produced a warning for this assignment. Why not? */ ft->elements[ft->nentries] = e; ft->nentries++; return (ft->nentries - 1); } void fileTable_noDelete (fileTable ft, cstring name) { fileId fid = fileTable_lookup (ft, name); if (fileId_isValid (fid)) { llassert (fileTable_isDefined (ft)); ft->elements[fid]->ftype = FILE_NODELETE; } else { DPRINTF (("Invalid no delete: %s", name)); } } static fileId fileTable_addFilePrim (fileTable ft, /*@temp@*/ cstring name, bool temp, fileType typ, fileId der) /*@modifies ft@*/ { cstring absname = osd_absolutePath (NULL, name); int tindex = fileTable_getIndex (ft, absname); llassert (ft != fileTable_undefined); if (tindex != NOT_FOUND) { llcontbug (message ("fileTable_addFilePrim: duplicate entry: %q", absname)); return tindex; } else { ftentry e = ftentry_create (absname, temp, typ, der); if (der == fileId_invalid) { llassert (cstring_isUndefined (e->basename)); e->basename = fileLib_removePathFree (fileLib_removeAnyExtension (absname)); e->fsystem = context_isSystemDir (absname); /* ** evans 2002-03-15: change suggested by Jim Zelenka ** support relative paths for system directories */ if (!e->fsystem) { e->fsystem = context_isSystemDir (name); } e->fspecial = context_isSpecialFile (absname); if (e->fspecial) { cstring srcname = cstring_concatFree1 (fileLib_removeAnyExtension (absname), C_EXTENSION); fileId fid = fileTable_lookup (ft, srcname); cstring_free (srcname); if (fileId_isValid (fid)) { fileId derid = ft->elements[fid]->fder; ft->elements[fid]->fspecial = TRUE; if (fileId_isValid (derid)) { ft->elements[derid]->fspecial = TRUE; } } } } else { ftentry de = ft->elements[der]; llassert (cstring_isUndefined (e->basename)); e->basename = cstring_copy (de->basename); e->fsystem = de->fsystem; e->fspecial = de->fspecial; } return (fileTable_internAddEntry (ft, e)); } } fileId fileTable_addFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_NORMAL, fileId_invalid)); } fileId fileTable_addFileOnly (fileTable ft, /*@only@*/ cstring name) { fileId res = fileTable_addFilePrim (ft, name, FALSE, FILE_NORMAL, fileId_invalid); cstring_free (name); return res; } fileId fileTable_addHeaderFile (fileTable ft, cstring name) { fileId res; res = fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid); return res; } void fileTable_addStreamFile (fileTable ft, FILE *fstream, cstring name) { fileTable_addOpen (ft, fstream, cstring_copy (name)); } bool fileTable_isHeader (fileTable ft, fileId fid) { if (fileId_isInvalid (fid)) { return FALSE; } llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid)); return (ft->elements[fid]->ftype == FILE_HEADER); } bool fileTable_isSystemFile (fileTable ft, fileId fid) { if (fileId_isInvalid (fid)) { return FALSE; } llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid)); return (ft->elements[fid]->fsystem); } bool fileTable_isXHFile (fileTable ft, fileId fid) { if (fileId_isInvalid (fid)) { return FALSE; } if (!(fileTable_isDefined (ft) && fileTable_inRange (ft, fid))) { llcontbug (message ("Bad file table or id: %s %d", bool_unparse (fileTable_isDefined (ft)), fid)); return FALSE; } else { return (ft->elements[fid]->ftype == FILE_XH); } } bool fileTable_isSpecialFile (fileTable ft, fileId fid) { if (fileId_isInvalid (fid)) { return FALSE; } llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid)); return (ft->elements[fid]->fspecial); } fileId fileTable_addLibraryFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid)); } fileId fileTable_addXHFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_XH, fileId_invalid)); } fileId fileTable_addImportFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid)); } fileId fileTable_addLCLFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid)); } static int tmpcounter = 0; fileId fileTable_addMacrosFile (fileTable ft) { cstring newname = makeTempName (context_tmpdir (), cstring_makeLiteralTemp ("lmx"), cstring_makeLiteralTemp (".llm")); fileId res = fileTable_addFilePrim (ft, newname, TRUE, FILE_MACROS, fileId_invalid); cstring_free (newname); return res; } fileId fileTable_addMetastateFile (fileTable ft, cstring name) { return (fileTable_addFilePrim (ft, name, FALSE, FILE_METASTATE, fileId_invalid)); } fileId fileTable_addCTempFile (fileTable ft, fileId fid) { cstring newname = makeTempName (context_tmpdir (), cstring_makeLiteralTemp ("cl"), C_EXTENSION); fileId res; DPRINTF (("tmp dir: %s", context_tmpdir ())); DPRINTF (("new name: %s", newname)); llassert (fileTable_isDefined (ft)); if (!fileId_isValid (ft->elements[fid]->fder)) { if (fileTable_isXHFile (ft, fid)) { res = fileTable_addFilePrim (ft, newname, TRUE, FILE_XH, fid); } else { res = fileTable_addFilePrim (ft, newname, TRUE, FILE_NORMAL, fid); } } else { if (fileTable_isXHFile (ft, fid)) { res = fileTable_addFilePrim (ft, newname, TRUE, FILE_XH, ft->elements[fid]->fder); } else { res = fileTable_addFilePrim (ft, newname, TRUE, FILE_NORMAL, ft->elements[fid]->fder); } } DPRINTF (("Added file: %s", fileTable_fileName (res))); cstring_free (newname); return res; } fileId fileTable_addltemp (fileTable ft) { cstring newname = makeTempName (context_tmpdir (), cstring_makeLiteralTemp ("ls"), cstring_makeLiteralTemp (".lsl")); fileId ret; if (cstring_hasNonAlphaNumBar (newname)) { char *lastpath = (char *)NULL; if (tmpcounter == 0) { lldiagmsg (message ("Operating system generates tmp filename containing invalid charater: %s", newname)); lldiagmsg (cstring_makeLiteral ("Try cleaning up the tmp directory. Attempting to continue.")); } /*@access cstring@*/ llassert (cstring_isDefined (newname)); lastpath = strrchr (newname, CONNECTCHAR); /* get the directory */ llassert (lastpath != NULL); *lastpath = '\0'; newname = message ("%q%hlsl%d.lsl", newname, CONNECTCHAR, tmpcounter); /*@noaccess cstring@*/ tmpcounter++; } /* ** this is kind of yucky...need to make the result of cstring_fromChars ** refer to the same storage as its argument. Of course, this loses, ** since cstring is abstract. Should make it an only? */ ret = fileTable_addFilePrim (ft, newname, TRUE, FILE_LSLTEMP, fileId_invalid); cstring_free (newname); return (ret); } bool fileTable_exists (fileTable ft, cstring s) { int tindex = fileTable_getIndex (ft, s); if (tindex == NOT_FOUND) { DPRINTF (("Not found: %s", s)); return FALSE; } else { return TRUE; } } fileId fileTable_lookup (fileTable ft, cstring s) { int tindex = fileTable_getIndex (ft, s); if (tindex == NOT_FOUND) { return fileId_invalid; } else { return tindex; } } /* ** This is pretty awkward --- when we find the real path of ** a .xh file, we may need to change the recorded name. [Sigh] */ void fileTable_setFilePath (fileTable ft, fileId fid, cstring path) { llassert (fileId_isValid (fid)); llassert (fileTable_isDefined (ft)); /* Need to put new string in hash table */ cstringTable_insert (ft->htable, cstring_copy (path), fid); ft->elements[fid]->fname = cstring_copy (path); } fileId fileTable_lookupBase (fileTable ft, cstring base) { int tindex; if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES)) { cstring dbase = cstring_downcase (base); tindex = fileTable_getIndex (ft, dbase); cstring_free (dbase); } else { tindex = fileTable_getIndex (ft, base); } if (tindex == NOT_FOUND) { return fileId_invalid; } else { fileId der; llassert (fileTable_isDefined (ft)); der = ft->elements[tindex]->fder; if (!fileId_isValid (der)) { der = tindex; } return der; } } cstring fileTable_getName (fileTable ft, fileId fid) { if (!fileId_isValid (fid)) { llcontbug (message ("fileTable_getName: called with invalid type id: %d", fid)); return cstring_makeLiteralTemp (""); } llassert (fileTable_isDefined (ft)); return (ft->elements[fid]->fname); } cstring fileTable_getRootName (fileTable ft, fileId fid) { fileId fder; if (!fileId_isValid (fid)) { llcontbug (message ("fileTable_getName: called with invalid id: %d", fid)); return cstring_makeLiteralTemp (""); } if (!fileTable_isDefined (ft)) { return cstring_makeLiteralTemp (""); } fder = ft->elements[fid]->fder; if (fileId_isValid (fder)) { return (ft->elements[fder]->fname); } else { return (ft->elements[fid]->fname); } } cstring fileTable_getNameBase (fileTable ft, fileId fid) { if (!fileId_isValid (fid)) { llcontbug (message ("fileTable_getName: called with invalid id: %d", fid)); return cstring_makeLiteralTemp (""); } if (!fileTable_isDefined (ft)) { return cstring_makeLiteralTemp (""); } return (ft->elements[fid]->basename); } bool fileTable_sameBase (fileTable ft, fileId f1, fileId f2) { fileId fd1, fd2; if (!fileId_isValid (f1)) { return FALSE; } if (!fileId_isValid (f2)) { return FALSE; } llassert (fileTable_isDefined (ft)); if (f1 == f2) { return TRUE; } fd1 = ft->elements[f1]->fder; if (!fileId_isValid (fd1)) { fd1 = f1; } fd2 = ft->elements[f2]->fder; if (!fileId_isValid (fd2)) { fd2 = f2; } return (fd1 == fd2); } void fileTable_cleanup (fileTable ft) { int i; bool msg; int skip; llassert (fileTable_isDefined (ft)); msg = ((ft->nentries > 40) && context_getFlag (FLG_SHOWSCAN)); skip = ft->nentries / 10; if (msg) { (void) fflush (g_warningstream); displayScanOpen (cstring_makeLiteral ("cleaning")); } for (i = 0; i < ft->nentries; i++) { ftentry fe = ft->elements[i]; if (fe->ftemp) { /* let's be real careful now, hon! */ /* ** Make sure it is really a derived file */ if (fe->ftype == FILE_LSLTEMP || fe->ftype == FILE_NODELETE) { ; /* already removed */ } else if (fileId_isValid (fe->fder)) { /* this should use close (fd) also... */ (void) osd_unlink (fe->fname); } else if (fe->ftype == FILE_MACROS) { (void) osd_unlink (fe->fname); } else { llbug (message ("Temporary file is not derivative: %s " "(not deleted)", fe->fname)); } } else { ; } if (msg && ((i % skip) == 0)) { displayScanContinue (cstring_makeLiteral (i == 0 ? " " : ".")); } } if (msg) { displayScanClose (); } } void fileTable_free (/*@only@*/ fileTable f) { int i = 0; if (f == (fileTable)NULL) { return; } while ( i < f->nentries ) { ftentry_free (f->elements[i]); i++; } cstringTable_free (f->htable); sfree (f->elements); sfree (f->openelements); /*!! why didn't splint report this? */ sfree (f); } /* ** unique temp filename are constructed from
.
** requires:  must end in '/'
*/

static void nextMsg (char *msg)
{
  /*@+charint@*/
  if (msg[0] < 'Z') 
    {
      msg[0]++; 
    }
  else 
    {
      msg[0] = 'A';
      if (msg[1] < 'Z')
	{ 
	  msg[1]++; 
	}
      else
	{
	  msg[1] = 'A';
	  if (msg[2] < 'Z') 
	    {
	      msg[2]++;
	    }
	  else
	    {
	      msg[2] = 'A';
	      if (msg[3] < 'Z') 
		{
		  msg[3]++; 
		}
	      else
		{
		  llassertprint (FALSE, ("nextMsg: out of unique names!!!"));
		}
	    }
	}
    }
  /*@-charint@*/
}

static /*@only@*/ cstring makeTempName (cstring dir, cstring pre, cstring suf)
{
  static int pid = 0; 
  static /*@owned@*/ char *msg = NULL; 
  static /*@only@*/ cstring pidname = NULL;
  size_t maxlen;
  cstring smsg;

  llassert (cstring_length (pre) <= 3);

  /*
  ** We limit the temp name to 8 characters:
  **   pre: 3 or less
  **   msg: 3
  **   pid: 2  (% 100)
  */

  if (msg == NULL)
    {
      msg = mstring_copy ("AAA"); /* there are 26^3 temp names */
    }

  if (pid == 0) 
    {
      /*@+matchanyintegral@*/
      pid = osd_getPid ();
      /*@=matchanyintegral@*/
    }

  if (cstring_isUndefined (pidname)) 
    {
      pidname = message ("%d", pid % 100);
    }
  
  maxlen = (cstring_length (dir) + cstring_length (pre) + mstring_length (msg) 
	    + cstring_length (pidname) + cstring_length (suf) + 2);

  DPRINTF (("Dir: %s / %s / %s / %s / %s",
	    dir, pre, pidname, msg, suf));

  smsg = message ("%s%s%s%s%s", dir, pre, pidname, cstring_fromChars (msg), suf);
  nextMsg (msg);

  DPRINTF (("Trying: %s", smsg));

  while (osd_fileExists (smsg))
    {
      cstring_free (smsg);
      smsg = message ("%s%s%s%s%s", dir, pre, pidname, cstring_fromChars (msg), suf);
      nextMsg (msg);
    }

  return smsg;
}

static foentry
foentry_create (/*@exposed@*/ FILE *f, /*@only@*/ cstring fname)
{
  foentry t = (foentry) dmalloc (sizeof (*t));
  t->f = f;
  t->fname = fname;
  return t;
}

static void 
foentry_free (/*@only@*/ foentry foe)
{
  cstring_free (foe->fname);
  sfree (foe);
}

static void 
fileTable_addOpen (fileTable ft, /*@observer@*/ FILE *f, /*@only@*/ cstring fname)
{
  llassert (fileTable_isDefined (ft));

  if (ft->nopenspace <= 0) 
    {
      fileTable_growOpen (ft);
    }

  ft->nopenspace--;
  ft->openelements[ft->nopen] = foentry_create (f, fname);
  ft->nopen++;
}

FILE *fileTable_createFile (fileTable ft, cstring fname)
{
# if defined (WIN32) && !defined (BCC32)
  int fdesc = _open (cstring_toCharsSafe (fname), 
		     O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 
		     _S_IWRITE | S_IREAD);
# else
   int fdesc = open (cstring_toCharsSafe (fname), 
		     O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
		     S_IRUSR | S_IWUSR);
# endif

  if (fdesc == -1)
    {
      osd_setTempError ();
      llfatalerror (message ("Temporary file for "
			     "pre-processor output already exists.  Trying to "
			     "open: %s.",
			     fname));

      /*@notreached@*/ return NULL;
    }
  else
    {
      FILE *res = fdopen (fdesc, "w");
  
      if (res != NULL) 
	{
	  fileTable_addOpen (ft, res, cstring_copy (fname));
	  DPRINTF (("Opening file: %s / %p", fname, res));
	}
      else
	{
	  DPRINTF (("Error opening: %s", fname));
	}

      return res;
    }
}

FILE *fileTable_createMacrosFile (fileTable ft, cstring fname)
{
# if defined (WIN32) && !defined (BCC32)
  int fdesc = _open (cstring_toCharsSafe (fname), 
		     O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
		     _S_IREAD | _S_IWRITE);
# else
  int fdesc = open (cstring_toCharsSafe (fname), 
		    O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 
		    S_IRUSR | S_IWUSR);
# endif

  if (fdesc == -1)
    {
      osd_setTempError ();
      llfatalerror (message ("Temporary file for "
			     "pre-processor output already exists.  Trying to "
			     "open: %s.",
			     fname));

      /*@notreached@*/ return NULL;
    }
  else
    {
      FILE *res = fdopen (fdesc, "w+");
  
      if (res != NULL) 
	{
	  fileTable_addOpen (ft, res, cstring_copy (fname));
	  DPRINTF (("Opening file: %s / %p", fname, res));
	}
      else
	{
	  DPRINTF (("Error opening: %s", fname));
	}

      return res;
    }
}

FILE *fileTable_openReadFile (fileTable ft, cstring fname)
{
  FILE *res = fopen (cstring_toCharsSafe (fname), "r");

  if (res != NULL) 
    {
      fileTable_addOpen (ft, res, cstring_copy (fname));
      DPRINTF (("Opening read file: %s / %p", fname, res));
    }
  else
    {
      DPRINTF (("Cannot open read file: %s", fname));
    }

  return res;
}

/*
** Allows overwriting
*/

FILE *fileTable_openWriteFile (fileTable ft, cstring fname)
{
  FILE *res = fopen (cstring_toCharsSafe (fname), "w");

  if (res != NULL) {
    fileTable_addOpen (ft, res, cstring_copy (fname));
    DPRINTF (("Opening file: %s / %p", fname, res));
  }

  return res;
}

FILE *fileTable_openWriteUpdateFile (fileTable ft, cstring fname)
{
  FILE *res = fopen (cstring_toCharsSafe (fname), "w+");

  if (res != NULL) {
    fileTable_addOpen (ft, res, cstring_copy (fname));
    DPRINTF (("Opening file: %s / %p", fname, res));
  }

  return res;
}

bool fileTable_closeFile (fileTable ft, FILE *f)
{
  bool foundit = FALSE;
  int i = 0;

  llassert (fileTable_isDefined (ft));

  DPRINTF (("Closing file: %p", f));

  for (i = 0; i < ft->nopen; i++) 
    {
      if (ft->openelements[i]->f == f)
	{
	  DPRINTF (("Closing file: %p = %s", f, ft->openelements[i]->fname));
	  
	  if (i == ft->nopen - 1)
	    {
	      foentry_free (ft->openelements[i]);
	      ft->openelements[i] = NULL;
	    }
	  else
	    {
	      foentry_free (ft->openelements[i]);
	      ft->openelements[i] = ft->openelements[ft->nopen - 1];
	      ft->openelements[ft->nopen - 1] = NULL;
	    }

	  ft->nopen--;
	  ft->nopenspace++;
	  foundit = TRUE;
	  break;
	}
    }
  
  llassert (foundit);
  return (fclose (f) == 0);
}

void fileTable_closeAll (fileTable ft)
{
  int i = 0;

  llassert (fileTable_isDefined (ft));

  for (i = 0; i < ft->nopen; i++) 
    {
      /* 
	 lldiagmsg (message ("Unclosed file at exit: %s", ft->openelements[i]->fname)); 
      */
      
      if (ft->openelements[i]->f != NULL)
	{
	  (void) fclose (ft->openelements[i]->f); /* No check - cleaning up after errors */
	}

      ft->openelements[i]->f = NULL;
      foentry_free (ft->openelements[i]);
      ft->openelements[i] = NULL;
    }
  
  ft->nopenspace += ft->nopen;
  ft->nopen = 0;
}