2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2003 University of Virginia,
4 ** Massachusetts Institute of Technology
6 ** This program is free software; you can redistribute it and/or modify it
7 ** under the terms of the GNU General Public License as published by the
8 ** Free Software Foundation; either version 2 of the License, or (at your
9 ** option) any later version.
11 ** This program is distributed in the hope that it will be useful, but
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ** General Public License for more details.
16 ** The GNU General Public License is available from http://www.gnu.org/ or
17 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18 ** MA 02111-1307, USA.
20 ** For information on splint: info@splint.org
21 ** To report a bug: splint-bug@splint.org
22 ** For more information: http://www.splint.org
27 ** replaces filenamemap.c
28 ** based (loosely) on typeTable.c
30 ** entries in the fileTable are:
32 ** name - name of the file
33 ** type - kind of file (a temp file to be deleted?)
34 ** link - derived from this file
39 * - Added conditional stuff (macros OS2 and MSDOS) to make names of temporary
40 * files under Windows or OS/2 not larger than 8+3 characters to avoid
41 * trouble with FAT file systems or Novell Netware volumes.
42 * - Added include of new header file portab.h containing OS dependent stuff.
43 * - Changed occurance of '/' as path delimiter to a macro.
44 * - Added conditional stuff (#define and #include) for IBM's compiler.
47 # include <sys/types.h>
48 # include <sys/stat.h>
50 # include "splintMacros.nf"
58 # if defined(__IBMC__) && defined(OS2)
61 # define getpid _getpid
62 # define S_IRUSR S_IREAD
63 # define S_IWUSR S_IWRITE
64 # define S_IXUSR S_IEXEC
71 fileTable_addOpen (fileTable p_ft, /*@observer@*/ FILE *p_f, /*@only@*/ cstring p_fname)
74 static bool fileTable_inRange (fileTable ft, fileId fid) /*@*/
76 return (fileTable_isDefined (ft) && (fid >= 0) && (fid < ft->nentries));
79 static fileId fileTable_internAddEntry (fileTable p_ft, /*@only@*/ ftentry p_e)
81 static /*@only@*/ cstring makeTempName (cstring p_dir, cstring p_pre, cstring p_suf);
83 static /*@only@*/ cstring
84 fileType_unparse (fileType ft)
88 case FILE_NORMAL: return cstring_makeLiteral ("normal");
89 case FILE_NODELETE: return cstring_makeLiteral ("normal");
90 case FILE_LSLTEMP: return cstring_makeLiteral ("ltemp");
91 case FILE_HEADER: return cstring_makeLiteral ("header");
92 case FILE_XH: return cstring_makeLiteral ("xh");
93 case FILE_MACROS: return cstring_makeLiteral ("macros");
94 case FILE_METASTATE: return cstring_makeLiteral ("metastate");
101 fileTable_getIndex (fileTable ft, cstring s)
105 if (ft == NULL) return NOT_FOUND;
106 abspath = osd_absolutePath (cstring_undefined, s);
108 if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES))
110 abspath = cstring_downcase (abspath);
113 DPRINTF (("Absolute path: %s: %s", s, abspath));
114 res = cstringTable_lookup (ft->htable, abspath);
115 cstring_free (abspath);
119 static cstring ftentry_unparse (fileTable ft, ftentry fte)
121 if (fileId_isValid (fte->fder))
123 llassert (fileTable_isDefined (ft));
125 return message ("%s %q %d (%s)",
127 fileType_unparse (fte->ftype),
129 ft->elements[fte->fder]->fname);
133 return message ("%s %q", fte->fname,
134 fileType_unparse (fte->ftype));
139 fileTable_unparse (fileTable ft)
141 cstring s = cstring_undefined;
144 if (fileTable_isUndefined (ft))
146 return (cstring_makeLiteral ("<fileTable undefined>"));
149 for (i = 0; i < ft->nentries; i++)
151 s = message ("%s\n[%d] %q", s, i, ftentry_unparse (ft, ft->elements[i]));
157 void fileTable_printTemps (fileTable ft)
159 if (fileTable_isDefined (ft))
163 for (i = 0; i < ft->nentries; i++)
165 if (ft->elements[i]->ftemp)
167 if (fileId_isValid (ft->elements[i]->fder))
169 fprintf (stderr, " %s:1\n\t%s:1\n",
170 cstring_toCharsSafe (ft->elements[ft->elements[i]->fder]->fname),
171 cstring_toCharsSafe (ft->elements[i]->fname));
175 fprintf (stderr, "[no file]\n\t%s:1\n",
176 cstring_toCharsSafe (ft->elements[i]->fname));
184 ** loads in fileTable from fileTable_dump
187 static /*@notnull@*/ ftentry
188 ftentry_create (/*@keep@*/ cstring tn, bool temp, fileType typ, fileId der)
190 ftentry t = (ftentry) dmalloc (sizeof (*t));
192 if (cstring_isUndefined (tn))
194 llbug (cstring_makeLiteral ("Undefined filename!"));
198 t->basename = cstring_undefined;
203 /* Don't set these until the basename is needed. */
211 ftentry_free (/*@only@*/ ftentry t)
213 cstring_free (t->fname);
214 cstring_free (t->basename);
218 /*@only@*/ /*@notnull@*/ fileTable
221 fileTable ft = (fileTable) dmalloc (sizeof (*ft));
224 ft->nspace = FTBASESIZE;
225 ft->elements = (ftentry *) dmalloc (FTBASESIZE * sizeof (*ft->elements));
226 ft->htable = cstringTable_create (FTHASHSIZE);
229 ft->nopenspace = FTBASESIZE;
230 ft->openelements = (foentry *) dmalloc (FTBASESIZE * sizeof (*ft->openelements));
237 fileTable_grow (fileTable ft)
242 llassert (fileTable_isDefined (ft));
244 ft->nspace = FTBASESIZE;
246 newent = (ftentry *) dmalloc ((ft->nentries + ft->nspace) * sizeof (*newent));
248 for (i = 0; i < ft->nentries; i++)
250 newent[i] = ft->elements[i];
253 sfree (ft->elements);
254 ft->elements = newent;
258 fileTable_growOpen (fileTable ft)
263 llassert (fileTable_isDefined (ft));
265 ft->nopenspace = FTBASESIZE;
267 newent = (foentry *) dmalloc ((ft->nopen + ft->nopenspace) * sizeof (*newent));
269 for (i = 0; i < ft->nopen; i++)
271 newent[i] = ft->openelements[i];
274 sfree (ft->openelements);
275 ft->openelements = newent;
279 fileTable_internAddEntry (fileTable ft, /*@only@*/ ftentry e)
281 llassert (fileTable_isDefined (ft));
288 DPRINTF (("Adding: %s", e->fname));
290 if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES))
292 cstring sd = cstring_downcase (e->fname);
293 cstringTable_insert (ft->htable, sd, ft->nentries);
297 cstringTable_insert (ft->htable, cstring_copy (e->fname), ft->nentries);
301 Before, there was no cstring_copy above, and e->fname was free'd in the if branch.
302 Splint should have caught this, and produced a warning for this assignment.
305 ft->elements[ft->nentries] = e;
308 return (ft->nentries - 1);
311 void fileTable_noDelete (fileTable ft, cstring name)
313 fileId fid = fileTable_lookup (ft, name);
315 if (fileId_isValid (fid))
317 llassert (fileTable_isDefined (ft));
318 ft->elements[fid]->ftype = FILE_NODELETE;
322 DPRINTF (("Invalid no delete: %s", name));
327 fileTable_addFilePrim (fileTable ft, /*@temp@*/ cstring name,
328 bool temp, fileType typ, fileId der)
331 cstring absname = osd_absolutePath (NULL, name);
332 int tindex = fileTable_getIndex (ft, absname);
334 llassert (ft != fileTable_undefined);
336 if (tindex != NOT_FOUND)
338 llcontbug (message ("fileTable_addFilePrim: duplicate entry: %q", absname));
343 ftentry e = ftentry_create (absname, temp, typ, der);
345 if (der == fileId_invalid)
347 llassert (cstring_isUndefined (e->basename));
349 e->basename = fileLib_removePathFree (fileLib_removeAnyExtension (absname));
350 e->fsystem = context_isSystemDir (absname);
353 ** evans 2002-03-15: change suggested by Jim Zelenka
354 ** support relative paths for system directories
359 e->fsystem = context_isSystemDir (name);
362 e->fspecial = context_isSpecialFile (absname);
366 cstring srcname = cstring_concatFree1 (fileLib_removeAnyExtension (absname),
368 fileId fid = fileTable_lookup (ft, srcname);
369 cstring_free (srcname);
371 if (fileId_isValid (fid))
373 fileId derid = ft->elements[fid]->fder;
375 ft->elements[fid]->fspecial = TRUE;
377 if (fileId_isValid (derid))
379 ft->elements[derid]->fspecial = TRUE;
386 ftentry de = ft->elements[der];
388 llassert (cstring_isUndefined (e->basename));
389 e->basename = cstring_copy (de->basename);
390 e->fsystem = de->fsystem;
391 e->fspecial = de->fspecial;
394 return (fileTable_internAddEntry (ft, e));
399 fileTable_addFile (fileTable ft, cstring name)
401 return (fileTable_addFilePrim (ft, name, FALSE, FILE_NORMAL, fileId_invalid));
405 fileTable_addFileOnly (fileTable ft, /*@only@*/ cstring name)
407 fileId res = fileTable_addFilePrim (ft, name, FALSE, FILE_NORMAL, fileId_invalid);
413 fileTable_addHeaderFile (fileTable ft, cstring name)
416 res = fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid);
422 fileTable_addStreamFile (fileTable ft, FILE *fstream, cstring name)
424 fileTable_addOpen (ft, fstream, cstring_copy (name));
428 fileTable_isHeader (fileTable ft, fileId fid)
430 if (fileId_isInvalid (fid))
435 llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid));
436 return (ft->elements[fid]->ftype == FILE_HEADER);
440 fileTable_isSystemFile (fileTable ft, fileId fid)
442 if (fileId_isInvalid (fid))
447 llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid));
448 return (ft->elements[fid]->fsystem);
452 fileTable_isXHFile (fileTable ft, fileId fid)
454 if (fileId_isInvalid (fid))
459 if (!(fileTable_isDefined (ft) && fileTable_inRange (ft, fid)))
461 llcontbug (message ("Bad file table or id: %s %d", bool_unparse (fileTable_isDefined (ft)), fid));
466 return (ft->elements[fid]->ftype == FILE_XH);
471 fileTable_isSpecialFile (fileTable ft, fileId fid)
473 if (fileId_isInvalid (fid))
478 llassert (fileTable_isDefined (ft) && fileTable_inRange (ft, fid));
479 return (ft->elements[fid]->fspecial);
483 fileTable_addLibraryFile (fileTable ft, cstring name)
485 return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid));
489 fileTable_addXHFile (fileTable ft, cstring name)
491 return (fileTable_addFilePrim (ft, name, FALSE, FILE_XH, fileId_invalid));
495 fileTable_addImportFile (fileTable ft, cstring name)
497 return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid));
501 fileTable_addLCLFile (fileTable ft, cstring name)
503 return (fileTable_addFilePrim (ft, name, FALSE, FILE_HEADER, fileId_invalid));
506 static int tmpcounter = 0;
509 fileTable_addMacrosFile (fileTable ft)
512 makeTempName (context_tmpdir (), cstring_makeLiteralTemp ("lmx"),
513 cstring_makeLiteralTemp (".llm"));
514 fileId res = fileTable_addFilePrim (ft, newname, TRUE, FILE_MACROS, fileId_invalid);
515 cstring_free (newname);
520 fileTable_addMetastateFile (fileTable ft, cstring name)
522 return (fileTable_addFilePrim (ft, name, FALSE, FILE_METASTATE, fileId_invalid));
526 fileTable_addCTempFile (fileTable ft, fileId fid)
529 makeTempName (context_tmpdir (), cstring_makeLiteralTemp ("cl"),
533 DPRINTF (("tmp dir: %s", context_tmpdir ()));
534 DPRINTF (("new name: %s", newname));
536 llassert (fileTable_isDefined (ft));
538 if (!fileId_isValid (ft->elements[fid]->fder))
540 if (fileTable_isXHFile (ft, fid))
542 res = fileTable_addFilePrim (ft, newname, TRUE, FILE_XH, fid);
546 res = fileTable_addFilePrim (ft, newname, TRUE, FILE_NORMAL, fid);
551 if (fileTable_isXHFile (ft, fid))
553 res = fileTable_addFilePrim (ft, newname, TRUE, FILE_XH,
554 ft->elements[fid]->fder);
558 res = fileTable_addFilePrim (ft, newname, TRUE, FILE_NORMAL,
559 ft->elements[fid]->fder);
563 DPRINTF (("Added file: %s", fileTable_fileName (res)));
564 cstring_free (newname);
569 fileTable_addltemp (fileTable ft)
571 cstring newname = makeTempName (context_tmpdir (),
572 cstring_makeLiteralTemp ("ls"),
573 cstring_makeLiteralTemp (".lsl"));
576 if (cstring_hasNonAlphaNumBar (newname))
578 char *lastpath = (char *)NULL;
584 ("Operating system generates tmp filename containing invalid charater: %s",
586 lldiagmsg (cstring_makeLiteral
587 ("Try cleaning up the tmp directory. Attempting to continue."));
591 llassert (cstring_isDefined (newname));
592 lastpath = strrchr (newname, CONNECTCHAR); /* get the directory */
593 llassert (lastpath != NULL);
596 newname = message ("%q%hlsl%d.lsl",
600 /*@noaccess cstring@*/
605 ** this is kind of yucky...need to make the result of cstring_fromChars
606 ** refer to the same storage as its argument. Of course, this loses,
607 ** since cstring is abstract. Should make it an only?
610 ret = fileTable_addFilePrim (ft, newname, TRUE, FILE_LSLTEMP, fileId_invalid);
611 cstring_free (newname);
616 fileTable_exists (fileTable ft, cstring s)
618 int tindex = fileTable_getIndex (ft, s);
620 if (tindex == NOT_FOUND)
622 DPRINTF (("Not found: %s", s));
632 fileTable_lookup (fileTable ft, cstring s)
634 int tindex = fileTable_getIndex (ft, s);
636 if (tindex == NOT_FOUND)
638 return fileId_invalid;
647 ** This is pretty awkward --- when we find the real path of
648 ** a .xh file, we may need to change the recorded name. [Sigh]
652 fileTable_setFilePath (fileTable ft, fileId fid, cstring path)
654 llassert (fileId_isValid (fid));
655 llassert (fileTable_isDefined (ft));
656 /* Need to put new string in hash table */
657 cstringTable_insert (ft->htable, cstring_copy (path), fid);
658 ft->elements[fid]->fname = cstring_copy (path);
662 fileTable_lookupBase (fileTable ft, cstring base)
666 if (context_getFlag (FLG_CASEINSENSITIVEFILENAMES))
668 cstring dbase = cstring_downcase (base);
669 tindex = fileTable_getIndex (ft, dbase);
670 cstring_free (dbase);
674 tindex = fileTable_getIndex (ft, base);
677 if (tindex == NOT_FOUND)
679 return fileId_invalid;
685 llassert (fileTable_isDefined (ft));
687 der = ft->elements[tindex]->fder;
689 if (!fileId_isValid (der))
699 fileTable_getName (fileTable ft, fileId fid)
701 if (!fileId_isValid (fid))
704 (message ("fileTable_getName: called with invalid type id: %d", fid));
705 return cstring_makeLiteralTemp ("<invalid>");
708 llassert (fileTable_isDefined (ft));
709 return (ft->elements[fid]->fname);
713 fileTable_getRootName (fileTable ft, fileId fid)
717 if (!fileId_isValid (fid))
719 llcontbug (message ("fileTable_getName: called with invalid id: %d", fid));
720 return cstring_makeLiteralTemp ("<invalid>");
723 if (!fileTable_isDefined (ft))
725 return cstring_makeLiteralTemp ("<no file table>");
728 fder = ft->elements[fid]->fder;
730 if (fileId_isValid (fder))
732 return (ft->elements[fder]->fname);
736 return (ft->elements[fid]->fname);
741 fileTable_getNameBase (fileTable ft, fileId fid)
743 if (!fileId_isValid (fid))
745 llcontbug (message ("fileTable_getName: called with invalid id: %d", fid));
746 return cstring_makeLiteralTemp ("<invalid>");
749 if (!fileTable_isDefined (ft))
751 return cstring_makeLiteralTemp ("<no file table>");
754 return (ft->elements[fid]->basename);
758 fileTable_sameBase (fileTable ft, fileId f1, fileId f2)
762 if (!fileId_isValid (f1))
767 if (!fileId_isValid (f2))
772 llassert (fileTable_isDefined (ft));
779 fd1 = ft->elements[f1]->fder;
781 if (!fileId_isValid (fd1))
786 fd2 = ft->elements[f2]->fder;
789 if (!fileId_isValid (fd2))
798 fileTable_cleanup (fileTable ft)
804 llassert (fileTable_isDefined (ft));
806 msg = ((ft->nentries > 40) && context_getFlag (FLG_SHOWSCAN));
807 skip = ft->nentries / 10;
811 (void) fflush (g_warningstream);
812 displayScanOpen (cstring_makeLiteral ("cleaning"));
815 for (i = 0; i < ft->nentries; i++)
817 ftentry fe = ft->elements[i];
821 /* let's be real careful now, hon! */
824 ** Make sure it is really a derived file
828 if (fe->ftype == FILE_LSLTEMP || fe->ftype == FILE_NODELETE)
830 ; /* already removed */
832 else if (fileId_isValid (fe->fder))
834 /* this should use close (fd) also... */
835 (void) osd_unlink (fe->fname);
837 else if (fe->ftype == FILE_MACROS)
839 (void) osd_unlink (fe->fname);
843 llbug (message ("Temporary file is not derivative: %s "
844 "(not deleted)", fe->fname));
852 if (msg && ((i % skip) == 0))
854 displayScanContinue (cstring_makeLiteral (i == 0 ? " " : "."));
865 fileTable_free (/*@only@*/ fileTable f)
869 if (f == (fileTable)NULL)
874 while ( i < f->nentries )
876 ftentry_free (f->elements[i]);
880 cstringTable_free (f->htable);
882 sfree (f->openelements); /*!! why didn't splint report this? */
887 ** unique temp filename are constructed from <dir><pre><pid><msg>.<suf>
888 ** requires: <dir> must end in '/'
891 static void nextMsg (char *msg)
921 llassertprint (FALSE, ("nextMsg: out of unique names!!!"));
929 static /*@only@*/ cstring makeTempName (cstring dir, cstring pre, cstring suf)
932 static /*@owned@*/ char *msg = NULL;
933 static /*@only@*/ cstring pidname = NULL;
937 llassert (cstring_length (pre) <= 3);
940 ** We limit the temp name to 8 characters:
948 msg = mstring_copy ("AAA"); /* there are 26^3 temp names */
953 /*@+matchanyintegral@*/
955 /*@=matchanyintegral@*/
958 if (cstring_isUndefined (pidname))
960 pidname = message ("%d", pid % 100);
963 maxlen = (cstring_length (dir) + cstring_length (pre) + mstring_length (msg)
964 + cstring_length (pidname) + cstring_length (suf) + 2);
966 DPRINTF (("Dir: %s / %s / %s / %s / %s",
967 dir, pre, pidname, msg, suf));
969 smsg = message ("%s%s%s%s%s", dir, pre, pidname, cstring_fromChars (msg), suf);
972 DPRINTF (("Trying: %s", smsg));
974 while (osd_fileExists (smsg))
977 smsg = message ("%s%s%s%s%s", dir, pre, pidname, cstring_fromChars (msg), suf);
985 foentry_create (/*@exposed@*/ FILE *f, /*@only@*/ cstring fname)
987 foentry t = (foentry) dmalloc (sizeof (*t));
994 foentry_free (/*@only@*/ foentry foe)
996 cstring_free (foe->fname);
1001 fileTable_addOpen (fileTable ft, /*@observer@*/ FILE *f, /*@only@*/ cstring fname)
1003 llassert (fileTable_isDefined (ft));
1005 if (ft->nopenspace <= 0)
1007 fileTable_growOpen (ft);
1011 ft->openelements[ft->nopen] = foentry_create (f, fname);
1015 FILE *fileTable_createFile (fileTable ft, cstring fname)
1017 # if defined (WIN32) && !defined (BCC32)
1018 int fdesc = _open (cstring_toCharsSafe (fname),
1019 O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
1020 _S_IWRITE | S_IREAD);
1022 int fdesc = open (cstring_toCharsSafe (fname),
1023 O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
1029 osd_setTempError ();
1030 llfatalerror (message ("Temporary file for "
1031 "pre-processor output already exists. Trying to "
1035 /*@notreached@*/ return NULL;
1039 FILE *res = fdopen (fdesc, "w");
1043 fileTable_addOpen (ft, res, cstring_copy (fname));
1044 DPRINTF (("Opening file: %s / %p", fname, res));
1048 DPRINTF (("Error opening: %s", fname));
1055 FILE *fileTable_createMacrosFile (fileTable ft, cstring fname)
1057 # if defined (WIN32) && !defined (BCC32)
1058 int fdesc = _open (cstring_toCharsSafe (fname),
1059 O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1060 _S_IREAD | _S_IWRITE);
1062 int fdesc = open (cstring_toCharsSafe (fname),
1063 O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
1069 osd_setTempError ();
1070 llfatalerror (message ("Temporary file for "
1071 "pre-processor output already exists. Trying to "
1075 /*@notreached@*/ return NULL;
1079 FILE *res = fdopen (fdesc, "w+");
1083 fileTable_addOpen (ft, res, cstring_copy (fname));
1084 DPRINTF (("Opening file: %s / %p", fname, res));
1088 DPRINTF (("Error opening: %s", fname));
1095 FILE *fileTable_openReadFile (fileTable ft, cstring fname)
1097 FILE *res = fopen (cstring_toCharsSafe (fname), "r");
1101 fileTable_addOpen (ft, res, cstring_copy (fname));
1102 DPRINTF (("Opening read file: %s / %p", fname, res));
1106 DPRINTF (("Cannot open read file: %s", fname));
1113 ** Allows overwriting
1116 FILE *fileTable_openWriteFile (fileTable ft, cstring fname)
1118 FILE *res = fopen (cstring_toCharsSafe (fname), "w");
1121 fileTable_addOpen (ft, res, cstring_copy (fname));
1122 DPRINTF (("Opening file: %s / %p", fname, res));
1128 FILE *fileTable_openWriteUpdateFile (fileTable ft, cstring fname)
1130 FILE *res = fopen (cstring_toCharsSafe (fname), "w+");
1133 fileTable_addOpen (ft, res, cstring_copy (fname));
1134 DPRINTF (("Opening file: %s / %p", fname, res));
1140 bool fileTable_closeFile (fileTable ft, FILE *f)
1142 bool foundit = FALSE;
1145 llassert (fileTable_isDefined (ft));
1147 DPRINTF (("Closing file: %p", f));
1149 for (i = 0; i < ft->nopen; i++)
1151 if (ft->openelements[i]->f == f)
1153 DPRINTF (("Closing file: %p = %s", f, ft->openelements[i]->fname));
1155 if (i == ft->nopen - 1)
1157 foentry_free (ft->openelements[i]);
1158 ft->openelements[i] = NULL;
1162 foentry_free (ft->openelements[i]);
1163 ft->openelements[i] = ft->openelements[ft->nopen - 1];
1164 ft->openelements[ft->nopen - 1] = NULL;
1175 return (fclose (f) == 0);
1178 void fileTable_closeAll (fileTable ft)
1182 llassert (fileTable_isDefined (ft));
1184 for (i = 0; i < ft->nopen; i++)
1187 lldiagmsg (message ("Unclosed file at exit: %s", ft->openelements[i]->fname));
1190 if (ft->openelements[i]->f != NULL)
1192 (void) fclose (ft->openelements[i]->f); /* No check - cleaning up after errors */
1195 ft->openelements[i]->f = NULL;
1196 foentry_free (ft->openelements[i]);
1197 ft->openelements[i] = NULL;
1200 ft->nopenspace += ft->nopen;