2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2001 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 lclint: lclint-request@cs.virginia.edu
21 ** To report a bug: lclint-bug@cs.virginia.edu
22 ** For more information: http://www.splint.org
27 ** Main module for Splint annotation-assisted program checker
33 ** Ensure that WIN32 and _WIN32 are both defined or both undefined.
38 # error "Inconsistent definitions."
42 # error "Inconsistent definitions."
51 # include "lclintMacros.nf"
58 # include "scanline.h"
59 # include "lclscanline.h"
60 # include "lclsyntable.h"
61 # include "lcltokentable.h"
62 # include "lslparse.h"
64 # include "syntable.h"
65 # include "tokentable.h"
73 # include "fileIdList.h"
75 # include "cgrammar.h"
80 extern /*@external@*/ int yydebug;
82 static void printMail (void);
83 static void printMaintainer (void);
84 static void printReferences (void);
85 static void printFlags (void);
86 static void printAnnotations (void);
87 static void printParseErrors (void);
88 static void printComments (void);
89 static void describePrefixCodes (void);
90 static void cleanupFiles (void);
91 static void showHelp (void);
92 static void interrupt (int p_i);
94 static bool readOptionsFile (cstring p_fname,
95 cstringSList *p_passThroughArgs,
97 /*@modifies fileSystem, internalState, *p_passThroughArgs@*/ ;
99 static void loadrc (FILE *p_rcfile, cstringSList *p_passThroughArgs)
100 /*@modifies *p_passThroughArgs@*/
101 /*@ensures closed p_rcfile@*/ ;
103 static void describeVars (void);
104 static bool specialFlagsHelp (char *p_next);
105 static bool hasShownHerald = FALSE;
106 static char *specFullName (char *p_specfile, /*@out@*/ char **p_inpath)
107 /*@modifies *p_inpath@*/ ;
109 static bool anylcl = FALSE;
110 static clock_t inittime;
112 static /*@only@*/ /*@null@*/ inputStream initFile = inputStream_undefined;
114 static fileIdList preprocessFiles (fileIdList, bool)
115 /*@modifies fileSystem@*/ ;
120 void lslCleanup (void)
121 /*@globals killed g_symtab@*/
122 /*@modifies internalState, g_symtab@*/
125 ** Cleanup all the LCL/LSL.
128 static bool didCleanup = FALSE;
130 llassert (!didCleanup);
135 lsymbol_destroyMod ();
136 LCLSynTableCleanup ();
137 LCLTokenTableCleanup ();
138 LCLScanLineCleanup ();
141 /* clean up LSL parsing */
144 ltokenTableCleanup ();
148 symtable_free (g_symtab);
154 /*@globals undef g_symtab; @*/
155 /*@modifies g_symtab, internalState, fileSystem; @*/
158 ** Open init file provided by user, or use the default LCL init file
161 cstring larchpath = context_getLarchPath ();
162 inputStream LSLinitFile = inputStream_undefined;
166 if (inputStream_isUndefined (initFile))
168 initFile = inputStream_create (cstring_makeLiteral (INITFILENAME),
169 cstring_makeLiteralTemp (LCLINIT_SUFFIX),
172 if (!inputStream_getPath (larchpath, initFile))
174 lldiagmsg (message ("Continuing without LCL init file: %s",
175 inputStream_fileName (initFile)));
179 if (!inputStream_open (initFile))
181 lldiagmsg (message ("Continuing without LCL init file: %s",
182 inputStream_fileName (initFile)));
188 if (!inputStream_open (initFile))
190 lldiagmsg (message ("Continuing without LCL init file: %s",
191 inputStream_fileName (initFile)));
195 /* Initialize checker */
203 LCLTokenTableInit ();
215 /* need this to initialize LCL checker */
217 llassert (inputStream_isDefined (initFile));
218 if (inputStream_isOpen (initFile))
222 LCLScanReset (initFile);
223 LCLProcessInitFileInit ();
224 LCLProcessInitFileReset ();
227 LCLProcessInitFile ();
228 LCLProcessInitFileCleanup ();
231 check (inputStream_close (initFile));
234 /* Initialize LSL init files, for parsing LSL signatures from LSL */
236 LSLinitFile = inputStream_create (cstring_makeLiteral ("lslinit.lsi"),
237 cstring_makeLiteralTemp (".lsi"),
240 if (!inputStream_getPath (larchpath, LSLinitFile))
242 lldiagmsg (message ("Continuing without LSL init file: %s",
243 inputStream_fileName (LSLinitFile)));
247 if (!inputStream_open (LSLinitFile))
249 lldiagmsg (message ("Continuing without LSL init file: %s",
250 inputStream_fileName (LSLinitFile)));
266 if (inputStream_isOpen (LSLinitFile))
269 LSLScanReset (LSLinitFile);
270 LSLProcessInitFileInit ();
272 LSLProcessInitFile ();
274 check (inputStream_close (LSLinitFile));
277 inputStream_free (LSLinitFile);
282 (cstring_makeLiteral ("LSL init file error. Attempting to continue."));
286 g_symtab = symtable_new ();
289 ** sort_init must come after symtab has been initialized
298 ** Equivalent to importing old spec_csupport.lcl
299 ** define immutable LCL type "bool" and bool constants TRUE and FALSE
300 ** and initialized them to be equal to LSL's "true" and "false".
302 ** Reads in CTrait.syms (derived from CTrait.lsl) on LARCH_PATH.
306 LCLReportEolTokens (FALSE);
310 lslProcess (fileIdList lclfiles)
311 /*@globals undef g_currentSpec, undef g_currentSpecName, g_currentloc,
312 undef killed g_symtab; @*/
313 /*@modifies g_currentSpec, g_currentSpecName, g_currentloc, internalState, fileSystem; @*/
316 bool parser_status = FALSE;
317 bool overallStatus = FALSE;
321 context_resetSpecLines ();
323 fileIdList_elements (lclfiles, fid)
325 cstring actualName = cstring_undefined;
326 cstring fname = fileName (fid);
328 if (osd_getPath (cstring_fromChars (g_localSpecPath),
329 fname, &actualName) == OSD_FILENOTFOUND)
331 if (mstring_equal (g_localSpecPath, "."))
333 lldiagmsg (message ("Spec file not found: %q", osd_outputPath (fname)));
337 lldiagmsg (message ("Spec file not found: %q (on %s)",
338 osd_outputPath (fname),
339 cstring_fromChars (g_localSpecPath)));
344 inputStream specFile;
346 char *namePtr = actualName;
348 while (*namePtr == '.' && *(namePtr + 1) == CONNECTCHAR)
352 /*@noaccess cstring@*/
354 g_currentSpec = cstring_fromCharsNew (namePtr);
356 specFile = inputStream_create (cstring_copy (g_currentSpec),
357 LCL_EXTENSION, TRUE);
359 llassert (inputStream_isDefined (specFile));
361 g_currentSpecName = specFullName
362 (cstring_toCharsSafe (g_currentSpec),
367 if (context_getFlag (FLG_SHOWSCAN))
369 lldiagmsg (message ("< reading spec %s >", g_currentSpec));
372 /* Open source file */
374 if (!inputStream_open (specFile))
376 lldiagmsg (message ("Cannot open file: %q",
377 osd_outputPath (inputStream_fileName (specFile))));
378 inputStream_free (specFile);
382 scopeInfo dummy_scope = (scopeInfo) dmalloc (sizeof (*dummy_scope));
383 dummy_scope->kind = SPE_INVALID;
386 LCLScanReset (specFile);
389 ** Minor hacks to allow more than one LCL file to
390 ** be scanned, while keeping initializations
393 symtable_enterScope (g_symtab, dummy_scope);
394 resetImports (cstring_fromChars (g_currentSpecName));
395 context_enterLCLfile ();
396 (void) lclHadNewError ();
398 parser_status = (ylparse () != 0);
399 context_exitLCLfile ();
401 overallStatus = parser_status || lclHadNewError ();
403 if (context_getFlag (FLG_DOLCS))
407 outputLCSFile (path, "%FAILED Output from ",
412 outputLCSFile (path, "%PASSED Output from ",
417 (void) inputStream_close (specFile);
418 inputStream_free (specFile);
420 symtable_exitScope (g_symtab);
423 cstring_free (actualName);
424 } end_fileIdList_elements;
426 /* Can cleanup lsl stuff right away */
430 g_currentSpec = cstring_undefined;
431 g_currentSpecName = NULL;
435 static void handlePassThroughFlag (char *arg)
438 char *quotechar = strchr (curarg, '\"');
441 char *freearg = NULL;
443 while (quotechar != NULL)
445 if (*(quotechar - 1) == '\\')
447 char *tp = quotechar - 2;
458 curarg = quotechar + 1;
459 quotechar = strchr (curarg, '\"');
464 llassert (quotechar != NULL);
466 offset = (quotechar - arg) + 2;
470 arg = cstring_toCharsSafe
471 (message ("%s\"\'%s",
472 cstring_fromChars (arg),
473 cstring_fromChars (quotechar + 1)));
479 arg = cstring_toCharsSafe
480 (message ("%s\'\"%s",
481 cstring_fromChars (arg),
482 cstring_fromChars (quotechar + 1)));
487 curarg = arg + offset;
488 quotechar = strchr (curarg, '\"');
494 voptgenerror (FLG_BADFLAG,
495 message ("Unclosed quote in flag: %s",
496 cstring_fromChars (arg)),
505 ** If the value is surrounded by single quotes ('), remove
506 ** them. This is an artifact of UNIX command line?
509 def = osd_fixDefine (cstring_fromChars (arg + 1));
510 DPRINTF (("Do define: %s", def));
512 DPRINTF (("After define"));
514 } else if (arg[0] == 'U') {
515 cppDoUndefine (cstring_fromChars (arg + 1));
524 void showHerald (void)
526 if (hasShownHerald || context_getFlag (FLG_QUIET)) return;
530 fprintf (g_msgstream, "%s\n\n", SPLINT_VERSION);
531 hasShownHerald = TRUE;
536 static cstring findLarchPathFile (/*@temp@*/ cstring s)
541 status = osd_getPath (context_getLarchPath (), s, &pathName);
543 if (status == OSD_FILEFOUND)
547 else if (status == OSD_FILENOTFOUND)
550 lldiagmsg (message ("Cannot find file on LARCHPATH: %s", s));
552 else if (status == OSD_PATHTOOLONG)
554 /* Directory and filename are too long. Report error. */
555 llbuglit ("soure_getPath: Filename plus directory from search path too long");
562 return cstring_undefined;
565 static void addLarchPathFile (fileIdList files, /*@temp@*/ cstring s)
567 cstring pathName = findLarchPathFile (s);
569 if (cstring_isDefined (pathName))
571 if (fileTable_exists (context_fileTable (), pathName))
574 lldiagmsg (message ("File listed multiple times: %s", pathName));
575 cstring_free (pathName);
579 fileIdList_add (files, fileTable_addFileOnly (context_fileTable (), pathName));
584 static void addFile (fileIdList files, /*@only@*/ cstring s)
586 if (fileTable_exists (context_fileTable (), s))
589 lldiagmsg (message ("File listed multiple times: %s", s));
594 fileIdList_add (files, fileTable_addFileOnly (context_fileTable (), s));
598 static void addXHFile (fileIdList files, /*@temp@*/ cstring s)
600 cstring pathName = findLarchPathFile (s);
602 if (cstring_isDefined (pathName))
604 if (fileTable_exists (context_fileTable (), pathName))
607 lldiagmsg (message ("File listed multiple times: %s", s));
611 fileIdList_add (files, fileTable_addXHFile (context_fileTable (), pathName));
615 cstring_free (pathName);
619 ** Disable MSVC++ warning about return value. Methinks humbly lclint control
620 ** comments are a mite more legible.
624 # pragma warning (disable:4035)
627 int main (int argc, char *argv[])
629 /*@globals killed undef g_currentloc,
633 /*@modifies g_currentloc, fileSystem,
637 /*@globals killed undef g_currentloc,
638 killed undef initFile,
639 killed g_localSpecPath,
640 killed undef g_currentSpec,
641 killed undef g_currentSpecName,
645 /*@modifies g_currentloc, initFile,
646 g_localSpecPath, g_currentSpec, g_currentSpecName, fileSystem,
651 bool first_time = TRUE;
652 bool showhelp = FALSE;
655 inputStream sourceFile = inputStream_undefined;
657 fileIdList dercfiles;
658 cstringSList fl = cstringSList_undefined;
659 cstringSList passThroughArgs = cstringSList_undefined;
660 fileIdList cfiles, xfiles, lclfiles, mtfiles;
661 clock_t before, lcltime, libtime, pptime, cptime, rstime;
665 _wildcard (&argc, &argv);
668 g_msgstream = stdout;
670 (void) signal (SIGINT, interrupt);
671 (void) signal (SIGSEGV, interrupt);
673 cfiles = fileIdList_create ();
674 xfiles = fileIdList_create ();
675 lclfiles = fileIdList_create ();
676 mtfiles = fileIdList_create ();
679 clabstract_initMod ();
680 typeIdSet_initMod ();
681 cppReader_initMod ();
686 g_currentloc = fileloc_createBuiltin ();
691 context_setInCommandLine ();
703 ** Add include directories from environment.
707 cstring incval = cstring_copy (osd_getEnvironmentVariable (INCLUDEPATH_VAR));
708 cstring oincval = incval;
710 if (cstring_isDefined (incval))
713 ** Each directory on the include path is a system include directory.
716 DPRINTF (("include: %s", incval));
717 context_setString (FLG_SYSTEMDIRS, cstring_copy (incval));
719 while (cstring_isDefined (incval))
722 char *nextsep = strchr (incval, PATH_SEPARATOR);
728 dir = cstring_copy (incval);
730 if (cstring_length (dir) == 0
731 || !isalpha ((int) cstring_firstChar (dir)))
734 ** win32 environment values can have special values,
740 cppAddIncludeDir (dir);
743 *nextsep = PATH_SEPARATOR;
744 incval = cstring_fromChars (nextsep + 1);
752 /*@noaccess cstring@*/
755 else /* 2001-09-09: herbert */
757 /* Put C_INCLUDE_PATH directories in sysdirs */
758 cstring cincval = osd_getEnvironmentVariable (cstring_makeLiteralTemp ("C_INCLUDE_PATH"));
759 if (cstring_isDefined (cincval))
761 context_setString (FLG_SYSTEMDIRS, cstring_copy (cincval));
766 cstring_free (oincval);
770 ** check RCFILE for default flags
774 cstring home = osd_getHomeDir ();
775 cstring fname = cstring_undefined;
777 bool defaultf = TRUE;
780 for (i = 1; i < argc; i++)
785 if (*thisarg == '-' || *thisarg == '+')
787 bool set = (*thisarg == '+');
791 opt = identifyFlag (cstring_fromChars (thisarg));
797 else if (opt == FLG_SHOWSCAN || opt == FLG_WARNRC)
800 ** Need to set it immediately, so rc file scan is displayed
803 context_userSetFlag (opt, set);
805 else if (opt == FLG_OPTF)
810 fname = cstring_fromChars (argv[i]);
811 readOptionsFile (fname, &passThroughArgs, TRUE);
815 (cstring_makeLiteral ("Flag f to select options file "
816 "requires an argument"));
820 ; /* wait to process later */
827 if (!nof && defaultf)
830 ** No explicit rc file, first try reading ~/.splintrc
833 if (cstring_isUndefined (fname))
835 if (!cstring_isEmpty (home))
837 bool readhomerc, readaltrc;
838 cstring homename, altname;
840 homename = message ("%s%h%s", home, CONNECTCHAR,
841 cstring_fromChars (RCFILE));
842 readhomerc = readOptionsFile (homename, &passThroughArgs, FALSE);
845 ** Try ~/.lclintrc also for historical accuracy
848 altname = message ("%s%h%s", home, CONNECTCHAR,
849 cstring_fromChars (ALTRCFILE));
850 readaltrc = readOptionsFile (altname, &passThroughArgs, FALSE);
852 if (readhomerc && readaltrc)
857 message ("Found both %s and %s files. Using both files, "
858 "but recommend using only %s to avoid confusion.",
859 homename, altname, homename),
866 ** Next, read .splintrc in the current working directory
870 cstring rcname = message ("%s%s",osd_getCurrentDirectory (), cstring_fromChars (RCFILE));
871 cstring altname = message ("%s%s",osd_getCurrentDirectory (), cstring_fromChars (ALTRCFILE));
872 bool readrc, readaltrc;
874 readrc = readOptionsFile (rcname, &passThroughArgs, FALSE);
875 readaltrc = readOptionsFile (altname, &passThroughArgs, FALSE);
877 if (readrc && readaltrc)
879 voptgenerror (FLG_WARNRC,
880 message ("Found both %s and %s files. Using both files, "
881 "but recommend using only %s to avoid confusion.",
882 rcname, altname, rcname),
887 cstring_free (rcname);
888 cstring_free (altname);
895 for (i = 1; i < argc; i++)
911 if (*thisarg == '-' || *thisarg == '+')
913 thisarg++; /* skip '-' */
915 if (mstring_equal (thisarg, "modes"))
917 llmsg (describeModes ());
919 else if (mstring_equal (thisarg, "vars")
920 || mstring_equal (thisarg, "env"))
924 else if (mstring_equal (thisarg, "annotations"))
928 else if (mstring_equal (thisarg, "parseerrors"))
932 else if (mstring_equal (thisarg, "comments"))
936 else if (mstring_equal (thisarg, "prefixcodes"))
938 describePrefixCodes ();
940 else if (mstring_equal (thisarg, "references")
941 || mstring_equal (thisarg, "refs"))
945 else if (mstring_equal (thisarg, "mail"))
949 else if (mstring_equal (thisarg, "maintainer")
950 || mstring_equal (thisarg, "version"))
954 else if (mstring_equal (thisarg, "flags"))
958 char *next = argv[i + 1];
960 if (specialFlagsHelp (next))
966 flagkind k = identifyCategory (cstring_fromChars (next));
982 cstring s = describeFlag (cstring_fromChars (thisarg));
984 if (cstring_isDefined (s))
992 if (*thisarg == '-' || *thisarg == '+')
994 bool set = (*thisarg == '+');
997 thisarg++; /* skip '-' */
998 flagname = cstring_fromChars (thisarg);
1000 DPRINTF (("Flag: %s", flagname));
1001 opt = identifyFlag (flagname);
1002 DPRINTF (("Flag: %s", flagcode_unparse (opt)));
1004 if (flagcode_isSkip (opt) || opt == FLG_SHOWSCAN || opt == FLG_WARNRC)
1006 /* showscan already processed */
1007 DPRINTF (("Skipping!"));
1009 else if (flagcode_isInvalid (opt))
1011 DPRINTF (("Invalid: %s", flagname));
1013 if (isMode (flagname))
1015 context_setMode (flagname);
1019 DPRINTF (("Error!"));
1020 voptgenerror (FLG_BADFLAG,
1021 message ("Unrecognized option: %s",
1022 cstring_fromChars (thisarg)),
1028 context_userSetFlag (opt, set);
1030 if (flagcode_hasArgument (opt))
1032 if (opt == FLG_HELP)
1036 else if (flagcode_isPassThrough (opt)) /* -D or -U */
1038 passThroughArgs = cstringSList_add
1039 (passThroughArgs, cstring_fromChars (thisarg));
1041 else if (flagcode_hasValue (opt))
1045 setValueFlag (opt, cstring_fromChars (argv[i]));
1051 ("Flag %s must be followed by a number",
1052 flagcode_unparse (opt)));
1055 else if (opt == FLG_INCLUDEPATH || opt == FLG_SPECPATH)
1057 cstring dir = cstring_suffix (cstring_fromChars (thisarg), 1); /* skip over I */
1061 case FLG_INCLUDEPATH:
1062 cppAddIncludeDir (dir);
1063 /*@switchbreak@*/ break;
1066 g_localSpecPath = cstring_toCharsSafe
1068 cstring_fromChars (g_localSpecPath),
1072 /*@switchbreak@*/ break;
1076 else if (flagcode_hasString (opt)
1077 || opt == FLG_INIT || opt == FLG_OPTF)
1081 cstring arg = cstring_fromChars (argv[i]);
1083 if (opt == FLG_OPTF)
1085 ; /* -f already processed */
1087 else if (opt == FLG_INIT)
1090 initFile = inputStream_create
1092 cstring_makeLiteralTemp (LCLINIT_SUFFIX),
1099 DPRINTF (("String flag: %s / %s",
1100 flagcode_unparse (opt), arg));
1101 if (opt == FLG_MTSFILE)
1104 ** arg identifies mts files
1106 cstring tmp = message ("%s%s", arg, MTS_EXTENSION);
1107 addLarchPathFile (mtfiles, tmp);
1109 tmp = message ("%s%s", arg, XH_EXTENSION);
1110 addXHFile (xfiles, tmp);
1115 setStringFlag (opt, arg);
1123 ("Flag %s must be followed by a string",
1124 flagcode_unparse (opt)));
1134 else /* its a filename */
1136 DPRINTF (("Adding filename: %s", thisarg));
1137 fl = cstringSList_add (fl, cstring_fromChars (thisarg));
1146 ** create lists of C and LCL files
1149 cstringSList_elements (fl, current)
1151 cstring ext = fileLib_getExtension (current);
1153 if (cstring_isUndefined (ext))
1155 /* no extension --- both C and LCL with default extensions */
1157 addFile (cfiles, message ("%s%s", current, C_EXTENSION));
1158 addFile (lclfiles, message ("%s%s", current, LCL_EXTENSION));
1160 else if (cstring_equal (ext, XH_EXTENSION))
1162 addXHFile (xfiles, current);
1164 else if (cstring_equal (ext, PP_EXTENSION))
1166 if (!context_getFlag (FLG_NOPP))
1169 (FLG_FILEEXTENSIONS,
1170 message ("File extension %s used without +nopp flag (will be processed as C source code): %s",
1175 addFile (cfiles, cstring_copy (current));
1177 else if (cstring_equal (ext, LCL_EXTENSION))
1179 addFile (lclfiles, cstring_copy (current));
1181 else if (fileLib_isCExtension (ext))
1183 addFile (cfiles, cstring_copy (current));
1185 else if (cstring_equal (ext, MTS_EXTENSION))
1187 addLarchPathFile (mtfiles, current);
1192 (FLG_FILEEXTENSIONS,
1193 message ("Unrecognized file extension: %s (assuming %s is C source code)",
1197 addFile (cfiles, cstring_copy (current));
1199 } end_cstringSList_elements;
1207 fprintf (g_msgstream, "\n");
1209 fileIdList_free (cfiles);
1210 fileIdList_free (xfiles);
1211 fileIdList_free (lclfiles);
1220 inittime = clock ();
1222 context_resetErrors ();
1223 context_clearInCommandLine ();
1225 anylcl = !fileIdList_isEmpty (lclfiles);
1227 if (context_doMerge ())
1229 cstring m = context_getMerge ();
1231 if (context_getFlag (FLG_SHOWSCAN))
1233 fprintf (g_msgstream, "< loading %s ", cstring_toCharsSafe (m));
1238 if (context_getFlag (FLG_SHOWSCAN))
1240 fprintf (g_msgstream, " >\n");
1243 if (!usymtab_existsType (context_getBoolName ()))
1245 usymtab_initBool ();
1250 if (!context_getFlag (FLG_NOLIB) && loadStandardState ())
1259 /* setup bool type and constants */
1260 usymtab_initBool ();
1263 fileloc_free (g_currentloc);
1264 g_currentloc = fileloc_createBuiltin ();
1267 ** Read metastate files (must happen before loading libraries)
1270 fileIdList_elements (mtfiles, mtfile)
1272 context_setFileId (mtfile);
1274 if (context_getFlag (FLG_SHOWSCAN))
1276 lldiagmsg (message ("< processing %s >", rootFileName (mtfile)));
1279 mtreader_readFile (cstring_copy (fileName (mtfile)));
1280 } end_fileIdList_elements;
1287 llfatalerror (cstring_makeLiteral ("This version of Splint does not handle LCL files."));
1289 lslProcess (lclfiles);
1293 usymtab_initGlobalMarker ();
1298 ** call the pre-preprocessor and /lib/cpp to generate appropriate
1303 context_setInCommandLine ();
1305 DPRINTF (("Pass through: %s", cstringSList_unparse (passThroughArgs)));
1307 cstringSList_elements (passThroughArgs, thisarg) {
1308 handlePassThroughFlag (cstring_toCharsSafe (thisarg));
1309 } end_cstringSList_elements;
1311 cstringSList_free (passThroughArgs);
1315 DPRINTF (("Initializing cpp reader!"));
1316 cppReader_initialize ();
1317 cppReader_saveDefinitions ();
1319 context_clearInCommandLine ();
1321 if (!context_getFlag (FLG_NOPP))
1327 if (context_getFlag (FLG_SHOWSCAN))
1329 fprintf (stderr, "< preprocessing");
1334 context_setPreprocessing ();
1335 dercfiles = preprocessFiles (xfiles, TRUE);
1336 tfiles = preprocessFiles (cfiles, FALSE);
1337 dercfiles = fileIdList_append (dercfiles, tfiles);
1338 fileIdList_free (tfiles);
1340 context_clearPreprocessing ();
1342 fileIdList_free (cfiles);
1344 if (context_getFlag (FLG_SHOWSCAN))
1346 fprintf (stderr, " >\n");
1354 dercfiles = fileIdList_append (cfiles, xfiles);
1359 ** now, check all the corresponding C files
1361 ** (for now these are just <file>.c, but after pre-processing
1362 ** will be <tmpprefix>.<file>.c)
1367 int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
1371 llbug (message ("Files unclosed: %d", nfiles));
1376 DPRINTF (("Initializing..."));
1378 exprNode_initMod ();
1380 DPRINTF (("Okay..."));
1382 fileIdList_elements (dercfiles, fid)
1384 sourceFile = inputStream_create (cstring_copy (fileName (fid)), C_EXTENSION, TRUE);
1385 context_setFileId (fid);
1387 /* Open source file */
1389 if (inputStream_isUndefined (sourceFile) || (!inputStream_open (sourceFile)))
1391 /* previously, this was ignored ?! */
1392 llbug (message ("Could not open temp file: %s", fileName (fid)));
1396 yyin = inputStream_getFile (sourceFile); /*< shared <- only */
1398 llassert (yyin != NULL);
1400 if (context_getFlag (FLG_SHOWSCAN))
1402 lldiagmsg (message ("< checking %q >", osd_outputPath (rootFileName (fid))));
1406 ** Every time, except the first time, through the loop,
1407 ** need to call yyrestart to clean up the parse buffer.
1412 (void) yyrestart (yyin);
1419 DPRINTF (("Entering..."));
1420 context_enterFile ();
1422 context_exitCFile ();
1424 (void) inputStream_close (sourceFile);
1426 } end_fileIdList_elements;
1430 /* process any leftover macros */
1432 context_processAllMacros ();
1434 /* check everything that was specified was defined */
1436 /* don't check if no c files were processed ?
1437 ** is this correct behaviour?
1440 if (context_getFlag (FLG_SHOWSCAN))
1442 lldiagmsg (cstring_makeLiteral ("< global checks >"));
1447 if (context_getLinesProcessed () > 0)
1449 usymtab_allDefined ();
1452 if (context_maybeSet (FLG_TOPUNUSED))
1454 uentry ue = usymtab_lookupSafe (cstring_makeLiteralTemp ("main"));
1456 if (uentry_isValid (ue))
1458 uentry_setUsed (ue, fileloc_observeBuiltin ());
1464 if (context_maybeSet (FLG_EXPORTLOCAL))
1466 usymtab_exportLocal ();
1470 if (context_maybeSet (FLG_EXPORTHEADER))
1472 usymtab_exportHeader ();
1475 if (context_getFlag (FLG_SHOWUSES))
1477 usymtab_displayAllUses ();
1480 context_checkSuppressCounts ();
1482 if (context_doDump ())
1484 cstring dump = context_getDump ();
1495 if (context_getFlag (FLG_SHOWSUMMARY))
1502 bool isQuiet = context_getFlag (FLG_QUIET);
1503 cstring specErrors = cstring_undefined;
1505 int nspecErrors = lclNumberErrors ();
1510 if (context_neednl ())
1511 fprintf (g_msgstream, "\n");
1514 if (nspecErrors > 0)
1516 if (nspecErrors == context_getLCLExpect ())
1519 message ("%d spec warning%&, as expected\n ",
1524 if (context_getLCLExpect () > 0)
1527 message ("%d spec warning%&, expected %d\n ",
1529 (int) context_getLCLExpect ());
1533 specErrors = message ("%d spec warning%& found\n ",
1541 if (context_getLCLExpect () > 0)
1543 specErrors = message ("No spec warnings, expected %d\n ",
1544 (int) context_getLCLExpect ());
1550 if (context_anyErrors ())
1552 if (context_numErrors () == context_getExpect ())
1555 llmsg (message ("Finished checking --- "
1556 "%s%d code warning%&, as expected",
1557 specErrors, context_numErrors ()));
1562 if (context_getExpect () > 0)
1566 ("Finished checking --- "
1567 "%s%d code warning%&, expected %d",
1568 specErrors, context_numErrors (),
1569 (int) context_getExpect ()));
1579 llmsg (message ("Finished checking --- "
1580 "%s%d code warning%& found",
1581 specErrors, context_numErrors ()));
1590 if (context_getExpect () > 0)
1594 ("Finished checking --- "
1595 "%sno code warnings, expected %d",
1597 (int) context_getExpect ()));
1604 if (context_getLinesProcessed () > 0)
1606 if (cstring_isEmpty (specErrors))
1610 llmsg (message ("Finished checking --- no warnings"));
1616 llmsg (message ("Finished checking --- %sno code warnings",
1623 llmsg (message ("Finished checking --- %sno code processed",
1630 cstring_free (specErrors);
1633 if (context_getFlag (FLG_STATS))
1635 clock_t ttime = clock () - before;
1636 int specLines = context_getSpecLinesProcessed ();
1642 fprintf (g_msgstream, "%d spec, ", specLines);
1645 # ifndef CLOCKS_PER_SEC
1646 fprintf (g_msgstream, "%d source lines in %ld time steps (steps/sec unknown)\n",
1647 context_getLinesProcessed (),
1650 fprintf (g_msgstream, "%d source lines in %.2f s.\n",
1651 context_getLinesProcessed (),
1652 (double) ttime / CLOCKS_PER_SEC);
1660 if (context_getFlag (FLG_TIMEDIST))
1662 clock_t ttime = clock () - before;
1666 char *msg = (char *) dmalloc (256 * sizeof (*msg));
1671 "Time distribution (percent): initialize %.2f / lcl %.2f / "
1672 "pre-process %.2f / c check %.2f / finalize %.2f \n",
1673 (100.0 * (double) (libtime - before) / ttime),
1674 (100.0 * (double) (lcltime - libtime) / ttime),
1675 (100.0 * (double) (pptime - lcltime) / ttime),
1676 (100.0 * (double) (cptime - pptime) / ttime),
1677 (100.0 * (double) (rstime - cptime) / ttime));
1682 "Time distribution (percent): initialize %.2f / "
1683 "pre-process %.2f / c check %.2f / finalize %.2f \n",
1684 (100.0 * (double) (libtime - before) / ttime),
1685 (100.0 * (double) (pptime - libtime) / ttime),
1686 (100.0 * (double) (cptime - pptime) / ttime),
1687 (100.0 * (double) (rstime - cptime) / ttime));
1690 llgenindentmsgnoloc (cstring_fromCharsO (msg));
1694 llexit (expsuccess ? LLSUCCESS : LLFAILURE);
1695 BADBRANCHRET (LLFAILURE);
1700 ** Reenable return value warnings.
1702 # pragma warning (default:4035)
1710 llmsg (message ("Source files are .c, .h and %s files. If there is no suffix,",
1712 llmsg (message (" Splint will look for <file>.c and <file>%s.", LCL_EXTENSION));
1714 llmsglit ("Use splint -help <topic or flag name> for more information");
1716 llmsglit ("Topics:");
1718 llmsglit (" annotations (describes source-code annotations)");
1719 llmsglit (" comments (describes control comments)");
1720 llmsglit (" flags (describes flag categories)");
1721 llmsglit (" flags <category> (describes flags in category)");
1722 llmsglit (" flags all (short description of all flags)");
1723 llmsglit (" flags alpha (list all flags alphabetically)");
1724 llmsglit (" flags full (full description of all flags)");
1725 llmsglit (" mail (information on mailing lists)");
1726 llmsglit (" modes (show mode settings)");
1727 llmsglit (" parseerrors (help on handling parser errors)");
1728 llmsglit (" prefixcodes (character codes in namespace prefixes)");
1729 llmsglit (" references (sources for more information)");
1730 llmsglit (" vars (environment variables)");
1731 llmsglit (" version (information on compilation, maintainer)");
1736 specialFlagsHelp (char *next)
1738 if ((next != NULL) && (*next != '-') && (*next != '+'))
1740 if (mstring_equal (next, "alpha"))
1745 else if (mstring_equal (next, "all"))
1747 printAllFlags (TRUE, FALSE);
1750 else if (mstring_equal (next, "categories")
1751 || mstring_equal (next, "cats"))
1753 listAllCategories ();
1756 else if (mstring_equal (next, "full"))
1758 printAllFlags (FALSE, TRUE);
1773 printParseErrors (void)
1775 llmsglit ("Parse Errors");
1776 llmsglit ("------------");
1778 llmsglit ("LCLint will sometimes encounter a parse error for code that "
1779 "can be parsed with a local compiler. There are a few likely "
1780 "causes for this and a number of techniques that can be used "
1781 "to work around the problem.");
1783 llmsglit ("Compiler extensions --- compilers sometimes extend the C "
1784 "language with compiler-specific keywords and syntax. While "
1785 "it is not advisible to use these, oftentimes one has no choice "
1786 "when the system header files use compiler extensions. ");
1788 llmsglit ("Splint supports some of the GNU (gcc) compiler extensions, "
1789 "if the +gnuextensions flag is set. You may be able to workaround "
1790 "other compiler extensions by using a pre-processor define. "
1791 "Alternately, you can surround the unparseable code with");
1793 llmsglit (" # ifndef __LCLINT__");
1795 llmsglit (" # endif");
1797 /* evans 2000-12-21 fixed typo reported by Jeroen Ruigrok/Asmodai */
1798 llmsglit ("Missing type definitions --- an undefined type name will usually "
1799 "lead to a parse error. This often occurs when a standard header "
1800 "file defines some type that is not part of the standard library. ");
1801 llmsglit ("By default, Splint does not process the local files corresponding "
1802 "to standard library headers, but uses a library specification "
1803 "instead so dependencies on local system headers can be detected. "
1804 "If another system header file that does not correspond to a "
1805 "standard library header uses one of these superfluous types, "
1806 "a parse error will result.");
1808 llmsglit ("If the parse error is inside a posix standard header file, the "
1809 "first thing to try is +posixlib. This makes Splint use "
1810 "the posix library specification instead of reading the posix "
1813 llmsglit ("Otherwise, you may need to either manually define the problematic "
1814 "type (e.g., add -Dmlink_t=int to your .lclintrc file) or force "
1815 "lclint to process the header file that defines it. This is done "
1816 "by setting -skipansiheaders or -skipposixheaders before "
1817 "the file that defines the type is #include'd.");
1818 llmsglit ("(See lclint -help "
1819 "skipansiheaders and lclint -help skipposixheaders for a list of "
1820 "standard headers.) For example, if <sys/local.h> uses a type "
1821 "defined by posix header <sys/types.h> but not defined by the "
1822 "posix library, we might do: ");
1824 llmsglit (" /*@-skipposixheaders@*/");
1825 llmsglit (" # include <sys/types.h>");
1826 llmsglit (" /*@=skipposixheaders@*/");
1827 llmsglit (" # include <sys/local.h>");
1829 llmsglit ("to force Splint to process <sys/types.h>.");
1831 llmsglit ("At last resort, +trytorecover can be used to make Splint attempt "
1832 "to continue after a parse error. This is usually not successful "
1833 "and the author does not consider assertion failures when +trytorecover "
1834 "is used to be bugs.");
1838 printAnnotations (void)
1840 llmsglit ("Annotations");
1841 llmsglit ("-----------");
1843 llmsglit ("Annotations are semantic comments that document certain "
1844 "assumptions about functions, variables, parameters, and types. ");
1846 llmsglit ("They may be used to indicate where the representation of a "
1847 "user-defined type is hidden, to limit where a global variable may "
1848 "be used or modified, to constrain what a function implementation "
1849 "may do to its parameters, and to express checked assumptions about "
1850 "variables, types, structure fields, function parameters, and "
1851 "function results.");
1853 llmsglit ("Annotations are introduced by \"/*@\". The role of the @ may be "
1854 "played by any printable character, selected using -commentchar <char>.");
1856 llmsglit ("Consult the User's Guide for descriptions of checking associated with each annotation.");
1858 llmsglit ("Globals: (in function declarations)");
1859 llmsglit (" /*@globals <globitem>,+ @*/");
1860 llmsglit (" globitem is an identifier, internalState or fileSystem");
1862 llmsglit ("Modifies: (in function declarations)");
1863 llmsglit (" /*@modifies <moditem>,+ @*/");
1864 llmsglit (" moditem is an lvalue");
1865 llmsglit (" /*@modifies nothing @*/");
1866 llmsglit (" /*@*/ (Abbreviation for no globals and modifies nothing.)");
1868 llmsglit ("Iterators:");
1869 llmsglit (" /*@iter <identifier> (<parameter-type-list>) @*/ - declare an iterator");
1871 llmsglit ("Constants:");
1872 llmsglit (" /*@constant <declaration> @*/ - declares a constant");
1874 llmsglit ("Alternate Types:");
1875 llmsglit (" /*@alt <basic-type>,+ @*/");
1876 llmsglit (" (e.g., int /*@alt char@*/ is a type matching either int or char)");
1878 llmsglit ("Declarator Annotations");
1880 llmsglit ("Type Definitions:");
1881 llmsglit (" /*@abstract@*/ - representation is hidden from clients");
1882 llmsglit (" /*@concrete@*/ - representation is visible to clients");
1883 llmsglit (" /*@immutable@*/ - instances of the type cannot change value");
1884 llmsglit (" /*@mutable@*/ - instances of the type can change value");
1885 llmsglit (" /*@refcounted@*/ - reference counted type");
1887 llmsglit ("Global Variables:");
1888 llmsglit (" /*@unchecked@*/ - weakest checking for global use");
1889 llmsglit (" /*@checkmod@*/ - check modification by not use of global");
1890 llmsglit (" /*@checked@*/ - check use and modification of global");
1891 llmsglit (" /*@checkedstrict@*/ - check use of global strictly");
1893 llmsglit ("Memory Management:");
1894 llmsglit (" /*@dependent@*/ - a reference to externally-owned storage");
1895 llmsglit (" /*@keep@*/ - a parameter that is kept by the called function");
1896 llmsglit (" /*@killref@*/ - a refcounted parameter, killed by the call");
1897 llmsglit (" /*@only@*/ - an unshared reference");
1898 llmsglit (" /*@owned@*/ - owner of storage that may be shared by /*@dependent@*/ references");
1899 llmsglit (" /*@shared@*/ - shared reference that is never deallocated");
1900 llmsglit (" /*@temp@*/ - temporary parameter");
1902 llmsglit ("Aliasing:");
1903 llmsglit (" /*@unique@*/ - may not be aliased by any other visible reference");
1904 llmsglit (" /*@returned@*/ - may be aliased by the return value");
1906 llmsglit ("Exposure:");
1907 llmsglit (" /*@observer@*/ - reference that cannot be modified");
1908 llmsglit (" /*@exposed@*/ - exposed reference to storage in another object");
1910 llmsglit ("Definition State:");
1911 llmsglit (" /*@out@*/ - storage reachable from reference need not be defined");
1912 llmsglit (" /*@in@*/ - all storage reachable from reference must be defined");
1913 llmsglit (" /*@partial@*/ - partially defined, may have undefined fields");
1914 llmsglit (" /*@reldef@*/ - relax definition checking");
1916 llmsglit ("Global State: (for globals lists, no /*@, since list is already in /*@\'s)");
1917 llmsglit (" undef - variable is undefined before the call");
1918 llmsglit (" killed - variable is undefined after the call");
1920 llmsglit ("Null State:");
1921 llmsglit (" /*@null@*/ - possibly null pointer");
1922 llmsglit (" /*@notnull@*/ - non-null pointer");
1923 llmsglit (" /*@relnull@*/ - relax null checking");
1925 llmsglit ("Null Predicates:");
1926 llmsglit (" /*@truenull@*/ - if result is TRUE, first parameter is NULL");
1927 llmsglit (" /*@falsenull@*/ - if result is TRUE, first parameter is not NULL");
1929 llmsglit ("Execution:");
1930 llmsglit (" /*@exits@*/ - function never returns");
1931 llmsglit (" /*@mayexit@*/ - function may or may not return");
1932 llmsglit (" /*@trueexit@*/ - function does not return if first parameter is TRUE");
1933 llmsglit (" /*@falseexit@*/ - function does not return if first parameter if FALSE");
1934 llmsglit (" /*@neverexit@*/ - function always returns");
1936 llmsglit ("Side-Effects:");
1937 llmsglit (" /*@sef@*/ - corresponding actual parameter has no side effects");
1939 llmsglit ("Declaration:");
1940 llmsglit (" /*@unused@*/ - need not be used (no unused errors reported)");
1941 llmsglit (" /*@external@*/ - defined externally (no undefined error reported)");
1944 llmsglit (" /*@fallthrough@*/ - fall-through case");
1946 llmsglit ("Break:");
1947 llmsglit (" /*@innerbreak@*/ - break is breaking an inner loop or switch");
1948 llmsglit (" /*@loopbreak@*/ - break is breaking a loop");
1949 llmsglit (" /*@switchbreak@*/ - break is breaking a switch");
1950 llmsglit (" /*@innercontinue@*/ - continue is continuing an inner loop");
1952 llmsglit ("Unreachable Code:");
1953 llmsglit (" /*@notreached@*/ - statement may be unreachable.");
1955 llmsglit ("Special Functions:");
1956 llmsglit (" /*@printflike@*/ - check variable arguments like printf");
1957 llmsglit (" /*@scanflike@*/ - check variable arguments like scanf");
1961 printComments (void)
1963 llmsglit ("Control Comments");
1964 llmsglit ("----------------");
1966 llmsglit ("Setting Flags");
1968 llmsglit ("Most flags (all except those characterized as \"globally-settable only\") can be set locally using control comments. A control comment can set flags locally to override the command line settings. The original flag settings are restored before processing the next file.");
1970 llmsglit ("The syntax for setting flags in control comments is the same as that of the command line, except that flags may also be preceded by = to restore their setting to the original command-line value. For instance,");
1971 llmsglit (" /*@+boolint -modifies =showfunc@*/");
1972 llmsglit ("sets boolint on (this makes bool and int indistinguishable types), sets modifies off (this prevents reporting of modification errors), and sets showfunc to its original setting (this controls whether or not the name of a function is displayed before a message).");
1974 llmsglit ("Error Suppression");
1976 llmsglit ("Several comments are provided for suppressing messages. In general, it is usually better to use specific flags to suppress a particular error permanently, but the general error suppression flags may be more convenient for quickly suppressing messages for code that will be corrected or documented later.");
1978 llmsglit ("/*@ignore@*/ ... /*@end@*/");
1980 (cstring_makeLiteral
1981 ("No errors will be reported in code regions between /*@ignore@*/ and /*@end@*/. These comments can be used to easily suppress an unlimited number of messages."));
1982 llmsglit ("/*@i@*/");
1984 (cstring_makeLiteral
1985 ("No errors will be reported from an /*@i@*/ comment to the end of the line."));
1986 llmsglit ("/*@i<n>@*/");
1988 (cstring_makeLiteral
1989 ("No errors will be reported from an /*@i<n>@*/ (e.g., /*@i3@*/) comment to the end of the line. If there are not exactly n errors suppressed from the comment point to the end of the line, Splint will report an error."));
1990 llmsglit ("/*@t@*/, /*@t<n>@*/");
1992 (cstring_makeLiteral
1993 ("Like i and i<n>, except controlled by +tmpcomments flag. These can be used to temporarily suppress certain errors. Then, -tmpcomments can be set to find them again."));
1995 llmsglit ("Type Access");
1997 llmsglit ("/*@access <type>@*/");
1998 llmsglit (" Allows the following code to access the representation of <type>");
1999 llmsglit ("/*@noaccess <type>@*/");
2000 llmsglit (" Hides the representation of <type>");
2002 llmsglit ("Macro Expansion");
2004 llmsglit ("/*@notfunction@*/");
2006 (cstring_makeLiteral
2007 ("Indicates that the next macro definition is not intended to be a "
2008 "function, and should be expanded in line instead of checked as a "
2009 "macro function definition."));
2016 llmsglit ("Flag Categories");
2017 llmsglit ("---------------");
2018 listAllCategories ();
2019 llmsglit ("\nTo see the flags in a flag category, do\n lclint -help flags <category>");
2020 llmsglit ("To see a list of all flags in alphabetical order, do\n lclint -help flags alpha");
2021 llmsglit ("To see a full description of all flags, do\n lclint -help flags full");
2025 printMaintainer (void)
2027 llmsg (message ("Maintainer: %s", cstring_makeLiteralTemp (LCLINT_MAINTAINER)));
2028 llmsglit (LCL_COMPILE);
2034 llmsglit ("Mailing Lists");
2035 llmsglit ("-------------");
2037 llmsglit ("There are two mailing lists associated with Splint: ");
2039 llmsglit (" lclint-announce@virginia.edu");
2041 llmsglit (" Reserved for announcements of new releases and bug fixes.");
2042 llmsglit (" To subscribe, send a message to majordomo@virginia.edu with body: ");
2043 llmsglit (" subscribe lclint-announce");
2045 llmsglit (" lclint-interest@virginia.edu");
2047 llmsglit (" Informal discussions on the use and development of lclint.");
2048 llmsglit (" To subscribe, send a message to majordomo@virginia.edu with body: ");
2049 llmsglit (" subscribe lclint-interest");
2053 printReferences (void)
2055 llmsglit ("References");
2056 llmsglit ("----------");
2058 llmsglit ("For more information, see the Splint web site: http://www.splint.org");
2062 describePrefixCodes (void)
2064 llmsglit ("Prefix Codes");
2065 llmsglit ("------------");
2067 llmsglit ("These characters have special meaning in name prefixes:");
2069 llmsg (message (" %h Any uppercase letter [A-Z]", PFX_UPPERCASE));
2070 llmsg (message (" %h Any lowercase letter [a-z]", PFX_LOWERCASE));
2071 llmsg (message (" %h Any character (valid in a C identifier)", PFX_ANY));
2072 llmsg (message (" %h Any digit [0-9]", PFX_DIGIT));
2073 llmsg (message (" %h Any non-uppercase letter [a-z0-9_]", PFX_NOTUPPER));
2074 llmsg (message (" %h Any non-lowercase letter [A-Z0-9_]", PFX_NOTLOWER));
2075 llmsg (message (" %h Any letter [A-Za-z]", PFX_ANYLETTER));
2076 llmsg (message (" %h Any letter or digit [A-Za-z0-9]", PFX_ANYLETTERDIGIT));
2077 llmsglit (" * Zero or more repetitions of the previous character class until the end of the name");
2086 eval = context_getLarchPath ();
2087 def = osd_getEnvironmentVariable (LARCH_PATH);
2089 if (cstring_isDefined (def) ||
2090 !cstring_equal (eval, cstring_fromChars (DEFAULT_LARCHPATH)))
2092 llmsg (message ("LARCH_PATH = %s", eval));
2096 llmsg (message ("LARCH_PATH = <not set> (default = %s)",
2097 cstring_fromChars (DEFAULT_LARCHPATH)));
2100 llmsglit (" --- path used to find larch initialization files and LSL traits");
2102 eval = context_getLCLImportDir ();
2103 def = osd_getEnvironmentVariable (cstring_makeLiteralTemp (LCLIMPORTDIR));
2105 if (cstring_isDefined (def) ||
2106 !cstring_equal (eval, cstring_fromChars (DEFAULT_LCLIMPORTDIR)))
2108 llmsg (message ("%q = %s", cstring_makeLiteral (LCLIMPORTDIR), eval));
2112 llmsg (message ("%s = <not set, default: %s>", cstring_makeLiteralTemp (LCLIMPORTDIR),
2113 cstring_makeLiteralTemp (DEFAULT_LCLIMPORTDIR)));
2116 llmsglit (" --- directory containing lcl standard library files "
2117 "(import with < ... >)");;
2120 ("include path = %q (set by environment variable %s and -I flags)",
2121 cppReader_getIncludePath (), INCLUDEPATH_VAR));
2123 llmsglit (" --- path used to find #include'd files");
2126 ("systemdirs = %s (set by -systemdirs or environment variable %s)", /*@i413223@*/
2127 context_getString (FLG_SYSTEMDIRS),
2130 llmsglit (" --- if file is found on this path, it is treated as a system file for error reporting");
2139 fprintf (stderr, "*** Interrupt\n");
2140 llexit (LLINTERRUPT);
2145 /* Cheat when there are parse errors */
2148 fprintf (stderr, "*** Segmentation Violation\n");
2150 /* Don't catch it if fileloc_unparse causes a signal */
2151 (void) signal (SIGSEGV, NULL);
2153 loc = fileloc_unparse (g_currentloc);
2155 fprintf (stderr, "*** Location (not trusted): %s\n",
2156 cstring_toCharsSafe (loc));
2159 fprintf (stderr, "*** Please report bug to %s\n", LCLINT_MAINTAINER);
2163 fprintf (stderr, "*** Signal: %d\n", i);
2165 fprintf (stderr, "*** Location (not trusted): %s\n",
2166 cstring_toCharsSafe (fileloc_unparse (g_currentloc)));
2169 fprintf (stderr, "*** Please report bug to %s ***\n", LCLINT_MAINTAINER);
2177 static bool doneCleanup = FALSE;
2179 /* make sure this is only called once! */
2181 if (doneCleanup) return;
2186 ** Close all open files
2187 ** (There should only be open files, if we exited after a fatal error.)
2190 fileTable_closeAll (context_fileTable ());
2192 if (context_getFlag (FLG_KEEP))
2194 check (fputs ("Temporary files kept:\n", stderr) != EOF);
2195 fileTable_printTemps (context_fileTable ());
2200 int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
2204 llbug (message ("Files unclosed: %d", nfiles));
2207 fileTable_cleanup (context_fileTable ());
2214 ** cleans up temp files (if necessary) and exits
2220 DPRINTF (("llexit: %d", status));
2223 if (status == LLFAILURE)
2231 if (status != LLFAILURE)
2233 context_destroyMod ();
2234 exprNode_destroyMod ();
2237 uentry_destroyMod ();
2238 typeIdSet_destroyMod ();
2241 dmalloc_shutdown ();
2245 exit ((status == LLSUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE);
2248 bool readOptionsFile (cstring fname, cstringSList *passThroughArgs, bool report)
2252 if (fileTable_exists (context_fileTable (), fname))
2258 message ("Multiple attempts to read options file: %s", fname),
2264 FILE *innerf = fileTable_openFile (context_fileTable (), fname, "r");
2268 fileloc fc = g_currentloc;
2269 g_currentloc = fileloc_createRc (fname);
2271 if (context_getFlag (FLG_SHOWSCAN))
2273 lldiagmsg (message ("< reading options from %s >",
2274 fileloc_outputFilename (g_currentloc)));
2277 loadrc (innerf, passThroughArgs);
2278 fileloc_reallyFree (g_currentloc);
2288 message ("Cannot open options file: %s", fname),
2298 ** This shouldn't be necessary, but Apple Darwin can't handle '"''s.
2302 loadrc (/*:open:*/ FILE *rcfile, cstringSList *passThroughArgs)
2303 /*@ensures closed rcfile@*/
2305 char *s = mstring_create (MAX_LINE_LENGTH);
2308 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2312 while (reader_readLine (rcfile, s, MAX_LINE_LENGTH) != NULL)
2319 DPRINTF (("Line: %s", s));
2320 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2322 while (*s == ' ' || *s == '\t')
2330 bool escaped = FALSE;
2331 bool quoted = FALSE;
2334 DPRINTF (("Process: %s", s));
2335 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2336 /* comment characters */
2337 if (c == '#' || c == ';' || c == '\n')
2343 if (c == '-' || c == '+')
2350 voptgenerror (FLG_BADFLAG,
2351 message ("Bad flag syntax (+ or - expected, "
2352 "+ is assumed): %s",
2353 cstring_fromChars (s)),
2364 while ((c = *s) != '\0')
2365 { /* remember to handle spaces and quotes in -D and -U ... */
2391 if (c == ' ' || c == '\t' || c == '\n')
2393 /*@innerbreak@*/ break;
2401 DPRINTF (("Nulling: %c", *s));
2404 if (mstring_isEmpty (thisflag))
2406 llfatalerror (message ("Missing flag: %s",
2407 cstring_fromChars (os)));
2410 DPRINTF (("Flag: %s", thisflag));
2412 opt = identifyFlag (cstring_fromChars (thisflag));
2414 if (flagcode_isSkip (opt))
2418 else if (flagcode_isInvalid (opt))
2420 DPRINTF (("Invalid: %s", thisflag));
2422 if (isMode (cstring_fromChars (thisflag)))
2424 context_setMode (cstring_fromChars (thisflag));
2428 voptgenerror (FLG_BADFLAG,
2429 message ("Unrecognized option: %s",
2430 cstring_fromChars (thisflag)),
2436 context_userSetFlag (opt, set);
2438 if (flagcode_hasArgument (opt))
2440 if (opt == FLG_HELP)
2443 voptgenerror (FLG_BADFLAG,
2444 message ("Cannot use help in rc files"),
2447 else if (flagcode_isPassThrough (opt)) /* -D or -U */
2449 cstring arg = cstring_fromCharsNew (thisflag);
2450 cstring_markOwned (arg);
2451 *passThroughArgs = cstringSList_add (*passThroughArgs, arg);
2452 DPRINTF (("Pass through: %s",
2453 cstringSList_unparse (*passThroughArgs)));
2455 else if (opt == FLG_INCLUDEPATH
2456 || opt == FLG_SPECPATH)
2458 cstring dir = cstring_suffix (cstring_fromChars (thisflag), 1); /* skip over I/S */
2462 case FLG_INCLUDEPATH:
2463 cppAddIncludeDir (dir);
2464 /*@switchbreak@*/ break;
2467 g_localSpecPath = cstring_toCharsSafe
2468 (message ("%s:%s", cstring_fromChars (g_localSpecPath), dir));
2470 /*@switchbreak@*/ break;
2474 else if (flagcode_hasString (opt)
2475 || flagcode_hasValue (opt)
2476 || opt == FLG_INIT || opt == FLG_OPTF)
2478 cstring extra = cstring_undefined;
2483 rest = mstring_copy (s);
2484 DPRINTF (("Here: rest = %s", rest));
2488 while ((rchar = *rest) != '\0'
2489 && (isspace ((int) rchar)))
2495 DPRINTF (("Yo: %s", rest));
2497 while ((rchar = *rest) != '\0'
2498 && !isspace ((int) rchar))
2500 extra = cstring_appendChar (extra, rchar);
2505 DPRINTF (("Yo: %s", extra));
2508 if (cstring_isUndefined (extra))
2514 ("Flag %s must be followed by an argument",
2515 flagcode_unparse (opt)),
2522 DPRINTF (("Here we are: %s", extra));
2524 if (flagcode_hasValue (opt))
2526 DPRINTF (("Set value flag: %s", extra));
2527 setValueFlag (opt, extra);
2528 cstring_free (extra);
2530 else if (opt == FLG_OPTF)
2532 (void) readOptionsFile (extra, passThroughArgs, TRUE);
2534 else if (opt == FLG_INIT)
2537 llassert (inputStream_isUndefined (initFile));
2539 initFile = inputStream_create
2541 cstring_makeLiteralTemp (LCLINIT_SUFFIX),
2544 cstring_free (extra);
2547 else if (flagcode_hasString (opt))
2549 if (cstring_firstChar (extra) == '\"')
2551 if (cstring_lastChar (extra) == '\"')
2553 char *extras = cstring_toCharsSafe (extra);
2555 llassert (extras[strlen(extras) - 1] == '\"');
2556 extras[strlen(extras) - 1] = '\0';
2557 extra = cstring_fromChars (extras + 1);
2558 DPRINTF (("Remove quotes: %s", extra));
2564 message ("Unmatched \" in option string: %s",
2570 setStringFlag (opt, extra);
2574 cstring_free (extra);
2587 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2588 while ((c == ' ') || (c == '\t'))
2594 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2598 DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2600 check (fileTable_closeFile (context_fileTable (), rcfile));
2603 static fileIdList preprocessFiles (fileIdList fl, bool xhfiles)
2604 /*@modifies fileSystem@*/
2606 bool msg = (context_getFlag (FLG_SHOWSCAN) && fileIdList_size (fl) > 10);
2607 int skip = (fileIdList_size (fl) / 5);
2608 int filesprocessed = 0;
2609 fileIdList dfiles = fileIdList_create ();
2611 fileloc_free (g_currentloc);
2612 g_currentloc = fileloc_createBuiltin ();
2614 fileIdList_elements (fl, fid)
2616 cstring ppfname = fileName (fid);
2618 if (!(osd_fileIsReadable (ppfname)))
2620 lldiagmsg (message ("Cannot open file: %s", osd_outputPath (ppfname)));
2621 ppfname = cstring_undefined;
2624 if (cstring_isDefined (ppfname))
2626 fileId dfile = fileTable_addCTempFile (context_fileTable (), fid);
2630 llassert (fileTable_isXHFile (context_fileTable (), dfile));
2633 llassert (cstring_isNonEmpty (ppfname));
2637 if ((filesprocessed % skip) == 0)
2639 if (filesprocessed == 0) {
2640 fprintf (stderr, " ");
2643 fprintf (stderr, ".");
2646 (void) fflush (stderr);
2651 if (cppProcess (ppfname, fileName (dfile)) != 0)
2653 llfatalerror (message ("Preprocessing error for file: %s",
2654 rootFileName (fid)));
2657 fileIdList_add (dfiles, dfile);
2659 } end_fileIdList_elements;
2664 /* This should be in an lclUtils.c file... */
2666 char *specFullName (char *specfile, /*@out@*/ char **inpath)
2668 /* extract the path and the specname associated with the given file */
2669 char *specname = (char *) dmalloc (sizeof (*specname)
2670 * (strlen (specfile) + 9));
2671 char *ospecname = specname;
2672 char *path = (char *) dmalloc (sizeof (*path) * (strlen (specfile)));
2676 /* initialized path to empty string or may have accidental garbage */
2679 /*@-mayaliasunique@*/
2680 strcpy (specname, specfile);
2681 /*@=mayaliasunique@*/
2683 /* trim off pathnames in specfile */
2684 size = strlen (specname);
2686 for (i = size_toInt (size) - 1; i >= 0; i--)
2688 if (specname[i] == CONNECTCHAR)
2690 /* strcpy (specname, (char *)specname+i+1); */
2691 for (j = 0; j <= i; j++) /* include '/' */
2693 path[j] = specname[j];
2703 ** also remove .lcl file extension, assume it's the last extension
2707 size = strlen (specname);
2709 for (i = size_toInt (size) - 1; i >= 0; i--)
2711 if (specname[i] == '.')
2721 ** If specname no longer points to the original char,
2722 ** we need to allocate a new pointer and copy the string.
2725 if (specname != ospecname) {
2726 char *rspecname = (char *) dmalloc (sizeof (*rspecname) * (strlen (specname) + 1));
2727 strcpy (rspecname, specname); /* evs 2000-05-16: Bug: was ospecname! */