]> andersk Git - splint.git/blob - src/llmain.c
Merged this branch with the one in the splint.sf.net repository.
[splint.git] / src / llmain.c
1 /*
2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2002 University of Virginia,
4 **         Massachusetts Institute of Technology
5 **
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.
10 ** 
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.
15 ** 
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.
19 **
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
23 */
24 /*
25 ** llmain.c
26 **
27 ** Main module for Splint annotation-assisted program checker
28 */
29
30 # include <signal.h>
31 # include <time.h>
32 /*
33 ** Ensure that WIN32 and _WIN32 are both defined or both undefined.
34 */
35
36 # ifdef WIN32
37 # ifndef _WIN32
38 # error "Inconsistent definitions."
39 # endif
40 # else
41 # ifdef _WIN32
42 # error "Inconsistent definitions."
43 # endif
44 # endif
45
46 # ifdef WIN32
47 # include <windows.h>
48 # include <process.h>
49 # endif
50
51 # include "splintMacros.nf"
52 # include "llbasic.h"
53 # include "osd.h"
54
55 # ifndef NOLCL
56 # include "gram.h"
57 # include "lclscan.h"
58 # include "scanline.h"
59 # include "lclscanline.h"
60 # include "lclsyntable.h"
61 # include "lcltokentable.h"
62 # include "lslparse.h"
63 # include "scan.h"
64 # include "syntable.h"
65 # include "tokentable.h"
66 # include "lslinit.h"
67 # include "lclinit.h"
68 # include "lh.h"
69 # include "imports.h"
70 # endif
71
72 # include "Headers/version.h" /* Visual C++ finds the wrong version.h */
73 # include "fileIdList.h"
74 # include "lcllib.h"
75 # include "cgrammar.h"
76 # include "llmain.h"
77 # include "portab.h"
78
79
80 extern /*@external@*/ int yydebug;
81
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);
93
94 static bool readOptionsFile (cstring p_fname,
95                              cstringSList *p_passThroughArgs,
96                              bool p_report) 
97    /*@modifies fileSystem, internalState, *p_passThroughArgs@*/ ;
98    
99 static void loadrc (FILE *p_rcfile, cstringSList *p_passThroughArgs)
100    /*@modifies *p_passThroughArgs, p_rcfile@*/
101    /*@ensures closed p_rcfile@*/ ;
102
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@*/ ;
108
109 static bool anylcl = FALSE;
110 static clock_t inittime;
111
112 static /*@only@*/ /*@null@*/ inputStream initFile = inputStream_undefined;
113
114 static fileIdList preprocessFiles (fileIdList, bool)
115   /*@modifies fileSystem@*/ ;
116
117 static void warnSysFiles(fileIdList p_files) /*@modifies fileSystem@*/;
118
119 # ifndef NOLCL
120
121 static
122 void lslCleanup (void)
123    /*@globals killed g_symtab@*/
124    /*@modifies internalState, g_symtab@*/
125 {
126   /*
127   ** Cleanup all the LCL/LSL.
128   */
129
130   static bool didCleanup = FALSE;
131
132   llassert (!didCleanup);
133   llassert (anylcl);
134
135   didCleanup = TRUE;
136
137   lsymbol_destroyMod ();
138   LCLSynTableCleanup ();
139   LCLTokenTableCleanup ();
140   LCLScanLineCleanup ();
141   LCLScanCleanup ();
142
143   /* clean up LSL parsing */
144
145   lsynTableCleanup ();
146   ltokenTableCleanup ();
147   lscanLineCleanup ();
148   LSLScanCleanup ();
149
150   symtable_free (g_symtab);
151   sort_destroyMod (); 
152 }
153
154 static
155   void lslInit (void)
156   /*@globals undef g_symtab; @*/
157   /*@modifies g_symtab, internalState, fileSystem; @*/
158 {
159   /*
160   ** Open init file provided by user, or use the default LCL init file 
161   */
162   
163   cstring larchpath = context_getLarchPath ();
164   inputStream LSLinitFile = inputStream_undefined;
165
166   setCodePoint ();
167
168   if (inputStream_isUndefined (initFile))
169     {
170       initFile = inputStream_create (cstring_makeLiteral (INITFILENAME), 
171                                      cstring_makeLiteralTemp (LCLINIT_SUFFIX),
172                                      FALSE);
173       
174       if (!inputStream_getPath (larchpath, initFile))
175         {
176           lldiagmsg (message ("Continuing without LCL init file: %s",
177                               inputStream_fileName (initFile)));
178         }
179       else 
180         {
181           if (!inputStream_open (initFile))
182             {
183               lldiagmsg (message ("Continuing without LCL init file: %s",
184                                   inputStream_fileName (initFile)));
185             }
186         }
187     }
188   else 
189     {
190       if (!inputStream_open (initFile))
191         {
192           lldiagmsg (message ("Continuing without LCL init file: %s",
193                               inputStream_fileName (initFile)));
194         }
195     }
196
197   /* Initialize checker */
198
199   lsymbol_initMod ();
200   LCLSynTableInit ();
201
202   setCodePoint ();
203
204   LCLSynTableReset ();
205   LCLTokenTableInit ();
206
207   setCodePoint ();
208
209   LCLScanLineInit ();
210   setCodePoint ();
211   LCLScanLineReset ();
212   setCodePoint ();
213   LCLScanInit ();
214
215   setCodePoint ();
216
217   /* need this to initialize LCL checker */
218
219   llassert (inputStream_isDefined (initFile));      
220   if (inputStream_isOpen (initFile))
221     {
222       setCodePoint ();
223
224       LCLScanReset (initFile);
225       LCLProcessInitFileInit ();
226       LCLProcessInitFileReset ();
227
228       setCodePoint ();
229       LCLProcessInitFile ();
230       LCLProcessInitFileCleanup ();
231
232       setCodePoint ();
233       check (inputStream_close (initFile));
234     }
235   
236   /* Initialize LSL init files, for parsing LSL signatures from LSL */
237   
238   LSLinitFile = inputStream_create (cstring_makeLiteral ("lslinit.lsi"), 
239                                     cstring_makeLiteralTemp (".lsi"),
240                                     FALSE);
241   
242   if (!inputStream_getPath (larchpath, LSLinitFile))
243     {
244       lldiagmsg (message ("Continuing without LSL init file: %s",
245                           inputStream_fileName (LSLinitFile)));
246     }
247   else 
248     {
249       if (!inputStream_open (LSLinitFile))
250         {
251           lldiagmsg (message ("Continuing without LSL init file: %s",
252                               inputStream_fileName (LSLinitFile)));
253         }
254     }
255       
256   setCodePoint ();
257   lsynTableInit ();
258   lsynTableReset ();
259
260   setCodePoint ();
261   ltokenTableInit ();
262
263   setCodePoint ();
264   lscanLineInit ();
265   lscanLineReset ();
266   LSLScanInit ();
267
268   if (inputStream_isOpen (LSLinitFile))
269     {
270       setCodePoint ();
271       LSLScanReset (LSLinitFile);
272       LSLProcessInitFileInit ();
273       setCodePoint ();
274       LSLProcessInitFile ();
275       setCodePoint ();
276       check (inputStream_close (LSLinitFile));
277     }
278       
279   inputStream_free (LSLinitFile);
280   
281   if (lclHadError ())
282     {
283       lclplainerror 
284         (cstring_makeLiteral ("LSL init file error.  Attempting to continue."));
285     }
286   
287   setCodePoint ();
288   g_symtab = symtable_new ();
289   
290   /* 
291   ** sort_init must come after symtab has been initialized 
292   */
293   sort_init ();
294   abstract_init ();
295   setCodePoint ();
296   
297   inittime = clock ();
298   
299   /* 
300   ** Equivalent to importing old spec_csupport.lcl
301   ** define immutable LCL type "bool" and bool constants TRUE and FALSE
302   ** and initialized them to be equal to LSL's "true" and "false".
303   **
304   ** Reads in CTrait.syms (derived from CTrait.lsl) on LARCH_PATH.
305   */
306       
307   LCLBuiltins (); 
308   LCLReportEolTokens (FALSE);
309 }
310
311 static void
312 lslProcess (fileIdList lclfiles)
313    /*@globals undef g_currentSpec, undef g_currentSpecName, g_currentloc,
314               undef killed g_symtab; @*/
315    /*@modifies g_currentSpec, g_currentSpecName, g_currentloc, internalState, fileSystem; @*/
316 {
317   char *path = NULL;
318   bool parser_status = FALSE;
319   bool overallStatus = FALSE;
320   
321   lslInit ();
322   
323   context_resetSpecLines ();
324
325   fileIdList_elements (lclfiles, fid)
326     {
327       cstring actualName = cstring_undefined;
328       cstring fname = fileTable_fileName (fid);
329       
330       if (osd_getPath (cstring_fromChars (g_localSpecPath), 
331                        fname, &actualName) == OSD_FILENOTFOUND)
332         {
333           if (mstring_equal (g_localSpecPath, "."))
334             {
335               lldiagmsg (message ("Spec file not found: %q", osd_outputPath (fname)));
336             }
337           else
338             {
339               lldiagmsg (message ("Spec file not found: %q (on %s)", 
340                                   osd_outputPath (fname), 
341                                   cstring_fromChars (g_localSpecPath)));
342             }
343         }
344       else
345         {
346           inputStream specFile;
347           /*@access cstring@*/
348           char *namePtr = actualName;
349
350           while (*namePtr == '.' && *(namePtr + 1) == CONNECTCHAR) 
351             {
352               namePtr += 2;
353             }
354           /*@noaccess cstring@*/
355
356           g_currentSpec = cstring_fromCharsNew (namePtr);
357
358           specFile = inputStream_create (cstring_copy (g_currentSpec),
359                                          LCL_EXTENSION, TRUE);
360           
361           llassert (inputStream_isDefined (specFile));
362           
363           g_currentSpecName = specFullName 
364             (cstring_toCharsSafe (g_currentSpec),
365              &path);
366
367           setSpecFileId (fid);
368                   
369           displayScan (message ("reading spec %s", g_currentSpec));
370           
371           /* Open the source file */
372           
373           if (!inputStream_open (specFile))
374             {
375               lldiagmsg (message ("Cannot open file: %q",
376                                   osd_outputPath (inputStream_fileName (specFile))));
377               inputStream_free (specFile);
378             }
379           else
380             {
381               scopeInfo dummy_scope = (scopeInfo) dmalloc (sizeof (*dummy_scope));
382               dummy_scope->kind = SPE_INVALID;
383               
384               lhInit (specFile);
385               LCLScanReset (specFile);
386               
387               /* 
388               ** Minor hacks to allow more than one LCL file to
389               ** be scanned, while keeping initializations
390               */
391               
392               symtable_enterScope (g_symtab, dummy_scope);
393               resetImports (cstring_fromChars (g_currentSpecName));
394               context_enterLCLfile ();
395               (void) lclHadNewError ();
396               
397               parser_status = (ylparse () != 0);
398               context_exitLCLfile ();
399               lhCleanup ();
400               overallStatus = parser_status || lclHadNewError (); 
401
402               if (context_getFlag (FLG_DOLCS))
403                 {
404                   if (overallStatus)
405                     {
406                       outputLCSFile (path, "%FAILED Output from ",
407                                      g_currentSpecName);
408                     }
409                   else
410                     {
411                       outputLCSFile (path, "%PASSED Output from ", 
412                                      g_currentSpecName);
413                     }
414                 }
415
416               (void) inputStream_close (specFile);
417               inputStream_free (specFile);
418
419               symtable_exitScope (g_symtab);
420             }      
421         }
422       cstring_free (actualName);
423     } end_fileIdList_elements; 
424     
425     /* Can cleanup lsl stuff right away */
426     
427     lslCleanup ();
428     
429     g_currentSpec = cstring_undefined;
430     g_currentSpecName = NULL;
431 }
432 # endif
433
434 static void handlePassThroughFlag (char *arg)
435 {
436   char *curarg = arg;
437   char *quotechar = strchr (curarg, '\"');
438   int offset = 0;
439   bool open = FALSE;
440   char *freearg = NULL;
441
442   while (quotechar != NULL)
443     {
444       if (*(quotechar - 1) == '\\')
445         {
446           char *tp = quotechar - 2;
447           bool escape = TRUE;
448
449           while (*tp == '\\')
450             {
451               escape = !escape;
452               tp--;
453             }
454           
455           if (escape)
456             {
457               curarg = quotechar + 1;
458               quotechar = strchr (curarg, '\"');
459               continue;
460             }
461         }
462       
463       llassert (quotechar != NULL);
464       *quotechar = '\0';
465       offset = (quotechar - arg) + 2;
466       
467       if (open)
468         {
469           arg = cstring_toCharsSafe
470             (message ("%s\"\'%s", 
471                       cstring_fromChars (arg), 
472                       cstring_fromChars (quotechar + 1))); 
473           freearg = arg;
474           open = FALSE;
475         }
476       else
477         {
478           arg = cstring_toCharsSafe
479             (message ("%s\'\"%s", 
480                       cstring_fromChars (arg), 
481                       cstring_fromChars (quotechar + 1)));
482           freearg = arg;
483           open = TRUE;
484         }
485       
486       curarg = arg + offset;
487       quotechar = strchr (curarg, '\"');
488     }
489
490   if (open)
491     {
492       showHerald ();
493       voptgenerror (FLG_BADFLAG,
494                     message ("Unclosed quote in flag: %s",
495                              cstring_fromChars (arg)),
496                     g_currentloc);
497     }
498   else
499     {
500       if (arg[0] == 'D') {
501         cstring def;
502         
503         /* 
504         ** If the value is surrounded by single quotes ('), remove
505         ** them.  This is an artifact of UNIX command line?
506         */
507
508         def = osd_fixDefine (cstring_fromChars (arg + 1));
509         DPRINTF (("Do define: %s", def));
510         cppDoDefine (def);
511         DPRINTF (("After define"));
512         cstring_free (def);
513       } else if (arg[0] == 'U') {
514         cppDoUndefine (cstring_fromChars (arg + 1));
515       } else {
516         BADBRANCH;
517       }
518     }
519   
520   sfree (freearg);
521 }
522
523 void showHerald (void)
524 {
525   if (hasShownHerald || context_getFlag (FLG_QUIET)) 
526     {
527       return;
528     }
529   else
530     {
531       fprintf (g_messagestream, "%s\n\n", SPLINT_VERSION);
532       hasShownHerald = TRUE;
533       llflush ();
534     }
535 }
536
537 static cstring findLarchPathFile (/*@temp@*/ cstring s)
538 {
539   cstring pathName;
540   filestatus status;
541   
542   status = osd_getPath (context_getLarchPath (), s, &pathName);
543   
544   if (status == OSD_FILEFOUND)
545     {
546       return pathName;
547     }
548   else if (status == OSD_FILENOTFOUND)
549     {
550       showHerald ();
551       lldiagmsg (message ("Cannot find file on LARCH_PATH: %s", s));
552     }
553   else if (status == OSD_PATHTOOLONG)
554     {
555       /* Directory and filename are too long.  Report error. */
556       llbuglit ("soure_getPath: Filename plus directory from search path too long");
557     }
558   else
559     {
560       BADBRANCH;
561     }
562
563   return cstring_undefined;
564 }
565
566 static void addLarchPathFile (fileIdList files, /*@temp@*/ cstring s)
567 {
568   cstring pathName = findLarchPathFile (s);
569
570   if (cstring_isDefined (pathName))
571     {
572       if (fileTable_exists (context_fileTable (), pathName))
573         {
574           showHerald ();
575           lldiagmsg (message ("File listed multiple times: %s", pathName));
576           cstring_free (pathName);
577         }
578       else
579         {
580           fileIdList_add (files, fileTable_addFileOnly (context_fileTable (), pathName));
581         }
582     }
583 }
584
585 static void addFile (fileIdList files, /*@only@*/ cstring s)
586 {
587   if (fileTable_exists (context_fileTable (), s))
588     {
589       showHerald ();
590       lldiagmsg (message ("File listed multiple times: %s", s));
591       cstring_free (s);
592     }
593   else
594     {
595       fileIdList_add (files, fileTable_addFileOnly (context_fileTable (), s));
596     }
597 }
598
599 static void addXHFile (fileIdList files, /*@temp@*/ cstring s)
600 {
601   cstring pathName = findLarchPathFile (s);
602
603   if (cstring_isDefined (pathName))
604     {
605       if (fileTable_exists (context_fileTable (), pathName))
606         {
607           showHerald ();
608           lldiagmsg (message ("File listed multiple times: %s", s));
609         }
610       else
611         {
612           fileIdList_add (files, fileTable_addXHFile (context_fileTable (), pathName));
613         }
614     }
615
616   cstring_free (pathName);
617 }
618
619 /*
620 ** Disable MSVC++ warning about return value.  Methinks humbly splint control
621 ** comments are a mite more legible.
622 */
623
624 # ifdef WIN32
625 # pragma warning (disable:4035) 
626 # endif
627
628 int main (int argc, char *argv[])
629 # ifdef NOLCL
630   /*@globals killed undef g_currentloc,
631              killed undef yyin,
632                     undef g_warningstream, g_messagestream, g_errorstream;
633    @*/
634   /*@modifies g_currentloc, fileSystem,
635               yyin; 
636   @*/
637 # else
638   /*@globals killed undef g_currentloc,
639              killed undef initFile,
640              killed       g_localSpecPath,  
641              killed undef g_currentSpec,
642              killed undef g_currentSpecName,
643              killed undef yyin,
644                     undef g_warningstream, g_messagestream, g_errorstream;
645    @*/
646   /*@modifies g_currentloc, initFile, 
647               g_localSpecPath, g_currentSpec, g_currentSpecName, fileSystem,
648               yyin; 
649   @*/
650 # endif
651 {
652   bool first_time = TRUE;
653   bool showhelp = FALSE;
654   bool allhelp = TRUE;
655   bool expsuccess;
656   inputStream sourceFile = inputStream_undefined;
657  
658   fileIdList dercfiles;
659   cstringSList fl = cstringSList_undefined;
660   cstringSList passThroughArgs = cstringSList_undefined;
661   fileIdList cfiles, xfiles, lclfiles, mtfiles;
662   clock_t before, lcltime, libtime, pptime, cptime, rstime;
663   int i = 0;
664
665 # ifdef __EMX__
666   _wildcard (&argc, &argv);
667 # endif
668
669   g_warningstream = stdout;
670   g_messagestream = stderr;
671   g_errorstream = stderr;
672
673   (void) signal (SIGINT, interrupt);
674   (void) signal (SIGSEGV, interrupt); 
675
676   cfiles = fileIdList_create ();
677   xfiles = fileIdList_create ();
678   lclfiles = fileIdList_create ();
679   mtfiles = fileIdList_create ();
680
681   flags_initMod ();
682   clabstract_initMod ();
683   typeIdSet_initMod ();
684   cppReader_initMod ();
685   osd_initMod ();
686
687   setCodePoint ();
688   
689   g_currentloc = fileloc_createBuiltin ();
690     
691   before = clock ();
692   context_initMod ();
693
694   context_setInCommandLine ();
695
696   if (argc <= 1)
697     {
698       showHelp ();
699       llexit (LLGIVEUP);
700     }
701
702   setCodePoint ();
703   yydebug = 0;
704
705   /*
706   ** Add include directories from environment.
707   */
708
709   {
710     cstring incval = cstring_copy (osd_getEnvironmentVariable (INCLUDEPATH_VAR));
711     cstring oincval = incval;
712
713     if (cstring_isDefined (incval))
714       {
715         /*
716         ** Each directory on the include path is a system include directory.
717         */
718
719         DPRINTF (("include: %s", incval));
720         context_setString (FLG_SYSTEMDIRS, cstring_copy (incval));
721
722         while (cstring_isDefined (incval))
723           {
724             /*@access cstring@*/
725             char *nextsep = strchr (incval, PATH_SEPARATOR);
726
727             if (nextsep != NULL)
728               {
729                 cstring dir;
730                 *nextsep = '\0';
731                 dir = cstring_copy (incval);
732
733                 if (cstring_length (dir) == 0
734                     || !isalpha ((int) cstring_firstChar (dir)))
735                   {
736                     /* 
737                     ** win32 environment values can have special values,
738                     ** ignore them
739                     */
740                   }
741                 else
742                   {
743                     cppAddIncludeDir (dir);
744                   }
745
746                 *nextsep = PATH_SEPARATOR;
747                 incval = cstring_fromChars (nextsep + 1);
748                 cstring_free (dir);
749               }
750             else
751               {
752                 break;
753               }
754
755             /*@noaccess cstring@*/
756           }
757       }
758     else /* 2001-09-09: herbert */
759       {
760         /* Put C_INCLUDE_PATH directories in sysdirs */
761         cstring cincval = osd_getEnvironmentVariable (cstring_makeLiteralTemp ("C_INCLUDE_PATH"));
762         if (cstring_isDefined (cincval))
763           {
764             context_setString (FLG_SYSTEMDIRS, cstring_copy (cincval));
765           }
766       }
767     /* /herbert */
768
769     cstring_free (oincval);
770   }
771
772   /*
773   ** check RCFILE for default flags
774   */
775
776   {
777     cstring home = osd_getHomeDir ();
778     cstring fname  = cstring_undefined;
779     bool defaultf = TRUE;
780     bool nof = FALSE;
781
782     for (i = 1; i < argc; i++)
783       {
784         char *thisarg;
785         thisarg = argv[i];
786         
787         if (*thisarg == '-' || *thisarg == '+')
788           {
789             bool set = (*thisarg == '+');
790             flagcode opt;
791
792             thisarg++;
793
794             /*
795             ** Don't report warnings this time
796             */
797
798             opt = flags_identifyFlagQuiet (cstring_fromChars (thisarg));
799
800             if (opt == FLG_NOF)
801               {
802                 nof = TRUE;
803               }
804             else if (flagcode_isMessageControlFlag (opt))
805               {
806                 /*
807                 ** Need to set it immediately, so rc file scan is displayed
808                 */
809
810                 context_userSetFlag (opt, set);
811
812                 if (flagcode_hasArgument (opt))
813                   {
814                     llassert (flagcode_hasString (opt));
815                     
816                     if (++i < argc)
817                       {
818                         fname = cstring_fromChars (argv[i]);
819                         setStringFlag (opt, fname);
820                       }
821                     else
822                       {
823                         llfatalerror 
824                           (message
825                            ("Flag %s must be followed by a string",
826                             flagcode_unparse (opt)));
827                       }
828                   }
829               }
830             else if (opt == FLG_OPTF)
831               {
832                 if (++i < argc)
833                   {
834                     defaultf = FALSE;
835                     fname = cstring_fromChars (argv[i]);
836                     (void) readOptionsFile (fname, &passThroughArgs, TRUE);
837                   }
838                 else
839                   llfatalerror
840                     (cstring_makeLiteral ("Flag f to select options file "
841                                           "requires an argument"));
842               }
843             else
844               {
845                 ; /* wait to process later */
846               }
847           }
848       }
849         
850     setCodePoint ();
851
852     if (!nof && defaultf)
853       {
854         /*
855         ** No explicit rc file, first try reading ~/.splintrc
856         */
857
858         if (cstring_isUndefined (fname))
859           {
860             if (!cstring_isEmpty (home)) 
861               {
862                 bool readhomerc, readaltrc;
863                 cstring homename, altname;
864
865                 homename = message ("%s%h%s", home, CONNECTCHAR,
866                                  cstring_fromChars (RCFILE));
867                 readhomerc = readOptionsFile (homename, &passThroughArgs, FALSE);
868                 
869                 /*
870                 ** Try ~/.splintrc also for historical accuracy
871                 */
872                 
873                 altname = message ("%s%h%s", home, CONNECTCHAR,
874                                  cstring_fromChars (ALTRCFILE));
875                 readaltrc = readOptionsFile (altname, &passThroughArgs, FALSE);
876
877                 if (readhomerc && readaltrc)
878                   {
879
880                     voptgenerror 
881                       (FLG_WARNRC,
882                        message ("Found both %s and %s files. Using both files, "
883                                 "but recommend using only %s to avoid confusion.",
884                                 homename, altname, homename),
885                        g_currentloc);
886                   }
887
888                 cstring_free (homename);
889                 cstring_free (altname);
890               }
891           }
892         
893         /*
894         ** Next, read .splintrc in the current working directory
895         */
896         
897         {
898           cstring rcname = message ("%s%s",osd_getCurrentDirectory (), cstring_fromChars (RCFILE));
899           cstring altname = message ("%s%s",osd_getCurrentDirectory (), cstring_fromChars (ALTRCFILE));
900           bool readrc, readaltrc;
901           
902           readrc = readOptionsFile (rcname, &passThroughArgs, FALSE);
903           readaltrc = readOptionsFile (altname, &passThroughArgs, FALSE);
904           
905           if (readrc && readaltrc)
906             {
907               voptgenerror (FLG_WARNRC,
908                             message ("Found both %s and %s files. Using both files, "
909                                      "but recommend using only %s to avoid confusion.",
910                                      rcname, altname, rcname),
911                             g_currentloc);
912               
913             }
914
915           cstring_free (rcname);
916           cstring_free (altname);
917         }
918       }
919   }
920   
921   setCodePoint ();
922   
923   for (i = 1; i < argc; i++)
924     {
925       char *thisarg;
926       flagcode opt;
927       
928       thisarg = argv[i];
929       
930       if (showhelp)
931         {
932           if (allhelp)
933             {
934               showHerald ();
935             }
936           
937           allhelp = FALSE;
938           
939           if (*thisarg == '-' || *thisarg == '+')
940             {
941               thisarg++;        /* skip '-' */
942             }
943           if (mstring_equal (thisarg, "modes"))
944             {
945               llmsg (describeModes ());
946             }
947           else if (mstring_equal (thisarg, "vars")
948                    || mstring_equal (thisarg, "env"))
949             {
950               describeVars ();
951             }
952           else if (mstring_equal (thisarg, "annotations"))
953             {
954               printAnnotations ();
955             }
956           else if (mstring_equal (thisarg, "parseerrors"))
957             {
958               printParseErrors ();
959             }
960           else if (mstring_equal (thisarg, "comments"))
961             {
962               printComments ();
963             }
964           else if (mstring_equal (thisarg, "prefixcodes"))
965             {
966               describePrefixCodes ();
967             }
968           else if (mstring_equal (thisarg, "references") 
969                    || mstring_equal (thisarg, "refs"))
970             {
971               printReferences ();
972             }
973           else if (mstring_equal (thisarg, "mail"))
974             {
975               printMail ();
976             }
977           else if (mstring_equal (thisarg, "maintainer")
978                    || mstring_equal (thisarg, "version"))
979             {
980               printMaintainer ();
981             }
982           else if (mstring_equal (thisarg, "flags"))
983             {
984               if (i + 1 < argc)
985                 {
986                   char *next = argv[i + 1];
987                   
988                   if (specialFlagsHelp (next))
989                     {
990                       i++;
991                     }
992                   else
993                     {
994                       flagkind k = identifyCategory (cstring_fromChars (next));
995                       
996                       if (k != FK_NONE)
997                         {
998                           printCategory (k);
999                           i++;
1000                         }
1001                     }
1002                 }
1003               else
1004                 {
1005                   printFlags ();
1006                 }
1007             }
1008           else
1009             {
1010               cstring s = describeFlag (cstring_fromChars (thisarg));
1011               
1012               if (cstring_isDefined (s))
1013                 {
1014                   llmsg (s);
1015                 }
1016             }
1017         }
1018       else
1019         {
1020           if (*thisarg == '-' || *thisarg == '+')
1021             {
1022               bool set = (*thisarg == '+');
1023               cstring flagname;
1024               
1025               thisarg++;        /* skip '-' */
1026               flagname = cstring_fromChars (thisarg);
1027
1028               DPRINTF (("Flag: %s", flagname));
1029               opt = flags_identifyFlag (flagname);
1030               DPRINTF (("Flag: %s", flagcode_unparse (opt)));
1031
1032               if (flagcode_isMessageControlFlag (opt))
1033                 {
1034                   /*
1035                   ** Processed on first pass
1036                   */
1037
1038                   if (flagcode_hasArgument (opt))
1039                     {
1040                       ++i;
1041                     }
1042                 }
1043               else if (flagcode_isInvalid (opt))
1044                 {
1045                   DPRINTF (("Invalid: %s", flagname));
1046
1047                   if (isMode (flagname))
1048                     {
1049                       context_setMode (flagname);
1050                     }
1051                   else
1052                     {
1053                       DPRINTF (("Error!"));
1054                       voptgenerror (FLG_BADFLAG,
1055                                     message ("Unrecognized option: %s", 
1056                                              cstring_fromChars (thisarg)),
1057                                     g_currentloc);
1058                     }
1059                 }
1060               else
1061                 {
1062                   context_userSetFlag (opt, set);
1063                   
1064                   if (flagcode_hasArgument (opt))
1065                     {
1066                       if (opt == FLG_HELP)
1067                         {
1068                           showhelp = TRUE;
1069                         }
1070                       else if (flagcode_isPassThrough (opt)) /* -D or -U */
1071                         { 
1072                           passThroughArgs = cstringSList_add 
1073                             (passThroughArgs, cstring_fromChars (thisarg));
1074                         }
1075                       else if (flagcode_hasNumber (opt))
1076                         {
1077                           if (++i < argc)
1078                             {
1079                               setValueFlag (opt, cstring_fromChars (argv[i]));
1080                             }
1081                           else
1082                             {
1083                               llfatalerror 
1084                                 (message
1085                                  ("Flag %s must be followed by a number",
1086                                   flagcode_unparse (opt)));
1087                             }
1088                         } 
1089                       else if (flagcode_hasChar (opt))
1090                         {
1091                           if (++i < argc)
1092                             {
1093                               setValueFlag (opt, cstring_fromChars (argv[i]));
1094                             }
1095                           else
1096                             {
1097                               llfatalerror 
1098                                 (message
1099                                  ("Flag %s must be followed by a character",
1100                                   flagcode_unparse (opt)));
1101                             }
1102                         } 
1103                       else if (opt == FLG_INCLUDEPATH || opt == FLG_SPECPATH)
1104                         {
1105                           cstring dir = cstring_suffix (cstring_fromChars (thisarg), 1); /* skip over I */
1106                           
1107                           switch (opt)
1108                             {
1109                             case FLG_INCLUDEPATH:
1110                               cppAddIncludeDir (dir);
1111                               /*@switchbreak@*/ break;
1112                             case FLG_SPECPATH:
1113                               /*@-mustfree@*/
1114                               g_localSpecPath = cstring_toCharsSafe
1115                                 (message ("%s%h%s", 
1116                                           cstring_fromChars (g_localSpecPath), 
1117                                           PATH_SEPARATOR,
1118                                           dir));
1119                               /*@=mustfree@*/
1120                               /*@switchbreak@*/ break;
1121                               BADDEFAULT;
1122                             }
1123                         }
1124                       else if (flagcode_hasString (opt)
1125                                || opt == FLG_INIT || opt == FLG_OPTF)
1126                         {
1127                           if (++i < argc)
1128                             {
1129                               cstring arg = cstring_fromChars (argv[i]);
1130                               
1131                               if (opt == FLG_OPTF)
1132                                 {
1133                                   ; /* -f already processed */
1134                                 }
1135                               else if (opt == FLG_INIT)
1136                                 {
1137 # ifndef NOLCL
1138                                   initFile = inputStream_create 
1139                                     (arg, 
1140                                      cstring_makeLiteralTemp (LCLINIT_SUFFIX),
1141                                      FALSE);
1142 # endif
1143                                   break;
1144                                 }
1145                               else
1146                                 {
1147                                   DPRINTF (("String flag: %s / %s",
1148                                             flagcode_unparse (opt), arg));
1149                                   if (opt == FLG_MTSFILE)
1150                                     {
1151                                       /*
1152                                       ** arg identifies mts files
1153                                       */
1154                                       cstring tmp =  message ("%s%s", arg, MTS_EXTENSION);
1155                                       addLarchPathFile (mtfiles, tmp);
1156                                       cstring_free (tmp);
1157                                       tmp = message ("%s%s", arg, XH_EXTENSION);
1158                                       addXHFile (xfiles, tmp);
1159                                       cstring_free (tmp);
1160                                     }
1161                                   else
1162                                     {
1163                                       setStringFlag (opt, cstring_copy (arg));
1164                                     }
1165                                 }
1166                             }
1167                           else
1168                             {
1169                               llfatalerror 
1170                                 (message
1171                                  ("Flag %s must be followed by a string",
1172                                   flagcode_unparse (opt)));
1173                             }
1174                         }
1175                       else
1176                         {
1177                           /* no argument */
1178                         }
1179                     }
1180                 }
1181             }
1182           else /* its a filename */
1183             {
1184               DPRINTF (("Adding filename: %s", thisarg));
1185               fl = cstringSList_add (fl, cstring_fromChars (thisarg));
1186             }
1187         }
1188     }
1189
1190   showHerald (); 
1191   
1192   /*
1193   ** create lists of C and LCL files
1194   */
1195
1196   cstringSList_elements (fl, current)
1197     {
1198       cstring ext = fileLib_getExtension (current);
1199       
1200       if (cstring_isUndefined (ext))
1201         {
1202           /* no extension --- both C and LCL with default extensions */
1203           
1204           addFile (cfiles, message ("%s%s", current, C_EXTENSION));
1205           addFile (lclfiles, message ("%s%s", current, LCL_EXTENSION));
1206         }
1207       else if (cstring_equal (ext, XH_EXTENSION))
1208         {
1209           addXHFile (xfiles, current);
1210         }
1211       else if (cstring_equal (ext, PP_EXTENSION))
1212         {
1213           if (!context_getFlag (FLG_NOPP))
1214             {
1215               voptgenerror 
1216                 (FLG_FILEEXTENSIONS,
1217                  message ("File extension %s used without +nopp flag (will be processed as C source code): %s", 
1218                           ext, current),
1219                  g_currentloc);
1220             }
1221           
1222           addFile (cfiles, cstring_copy (current));
1223         }
1224       else if (cstring_equal (ext, LCL_EXTENSION)) 
1225         {
1226           addFile (lclfiles, cstring_copy (current));
1227         }
1228       else if (fileLib_isCExtension (ext))
1229         {
1230           addFile (cfiles, cstring_copy (current));
1231         }
1232       else if (cstring_equal (ext, MTS_EXTENSION))
1233         {
1234           addLarchPathFile (mtfiles, current);
1235         }
1236       
1237       else if (cstring_equal (ext, OBJECT_EXTENSION))
1238         {
1239           // Make sure this is the correct default
1240           /* Ignore object files */
1241         }
1242       else 
1243         {
1244           // drl temporary measure .... //
1245           // drl //  make sure this is right
1246           // on make it optional with a flag
1247           //voptgenerror 
1248           //  (FLG_FILEEXTENSIONS,
1249           //  message ("Unrecognized file extension: %s (assuming %s is C source code)", 
1250           //      current, ext),
1251           // g_currentloc);
1252           
1253           //  addFile (cfiles, cstring_copy (current));
1254         }
1255     } end_cstringSList_elements;
1256   
1257   if (showhelp)
1258     {
1259       if (allhelp)
1260         {
1261           showHelp ();
1262         }
1263       fprintf (g_warningstream, "\n");
1264
1265       fileIdList_free (cfiles);
1266       fileIdList_free (xfiles);
1267       fileIdList_free (lclfiles);
1268       
1269       llexit (LLSUCCESS);
1270     }
1271
1272 # ifdef DOANNOTS
1273   initAnnots ();
1274 # endif
1275
1276   inittime = clock ();
1277
1278   context_resetErrors ();
1279   context_clearInCommandLine ();
1280
1281   anylcl = !fileIdList_isEmpty (lclfiles);
1282
1283   if (context_doMerge ())
1284     {
1285       cstring m = context_getMerge ();
1286
1287       displayScanOpen (message ("< loading %s ", m));
1288       loadState (m);
1289       displayScanClose ();
1290
1291       if (!usymtab_existsType (context_getBoolName ()))
1292         {
1293           usymtab_initBool (); 
1294         }
1295     }
1296   else
1297     {
1298       if (!context_getFlag (FLG_NOLIB) && loadStandardState ())
1299         {
1300           ;
1301         }
1302       else
1303         {
1304           ctype_initTable ();
1305         }
1306
1307       /* setup bool type and constants */
1308       usymtab_initBool (); 
1309     }
1310
1311   fileloc_free (g_currentloc);
1312   g_currentloc = fileloc_createBuiltin ();
1313
1314   /*
1315   ** Read metastate files (must happen before loading libraries) 
1316   */
1317
1318   fileIdList_elements (mtfiles, mtfile)
1319     {
1320       context_setFileId (mtfile);
1321       displayScan (message ("processing %s", fileTable_rootFileName (mtfile)));
1322       mtreader_readFile (cstring_copy (fileTable_fileName (mtfile)));
1323     } end_fileIdList_elements;
1324
1325   libtime = clock ();
1326
1327   if (anylcl)
1328     {
1329 # ifdef NOLCL
1330       llfatalerror (cstring_makeLiteral ("This version of Splint does not handle LCL files."));
1331 # else
1332       lslProcess (lclfiles);
1333 # endif
1334     }
1335
1336   usymtab_initGlobalMarker ();
1337
1338   /*
1339   ** pre-processing
1340   **
1341   ** call the pre-preprocessor and /lib/cpp to generate appropriate
1342   ** files
1343   **
1344   */
1345
1346   context_setInCommandLine ();
1347
1348   DPRINTF (("Pass through: %s", cstringSList_unparse (passThroughArgs)));
1349   
1350   cstringSList_elements (passThroughArgs, thisarg) {
1351     handlePassThroughFlag (cstring_toCharsSafe (thisarg));
1352   } end_cstringSList_elements;
1353
1354   cstringSList_free (passThroughArgs);
1355
1356   cleanupMessages ();
1357
1358   DPRINTF (("Initializing cpp reader!"));
1359   cppReader_initialize ();
1360   cppReader_saveDefinitions ();
1361   
1362   context_clearInCommandLine ();
1363
1364   if (!context_getFlag (FLG_NOPP))
1365     {
1366       fileIdList tfiles;
1367
1368       llflush ();
1369
1370       displayScanOpen (cstring_makeLiteral ("preprocessing"));
1371       
1372       lcltime = clock ();
1373
1374       context_setPreprocessing ();
1375       dercfiles = preprocessFiles (xfiles, TRUE);
1376       tfiles = preprocessFiles (cfiles, FALSE);
1377       warnSysFiles(cfiles);
1378       dercfiles = fileIdList_append (dercfiles, tfiles);
1379       fileIdList_free (tfiles);
1380
1381       context_clearPreprocessing ();
1382
1383       fileIdList_free (cfiles);
1384
1385       displayScanClose ();
1386       pptime = clock ();
1387     }
1388   else
1389     {
1390       lcltime = clock ();
1391       dercfiles = fileIdList_append (cfiles, xfiles);
1392       pptime = clock ();
1393     }
1394
1395   /*
1396   ** now, check all the corresponding C files
1397   **
1398   ** (for now these are just <file>.c, but after pre-processing
1399   **  will be <tmpprefix>.<file>.c)
1400   */
1401
1402   {
1403 # ifdef WIN32
1404     int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
1405
1406     if (nfiles != 0) 
1407       {
1408         llbug (message ("Files unclosed: %d", nfiles));
1409       }
1410 # endif
1411   }
1412
1413   DPRINTF (("Initializing..."));
1414
1415   exprNode_initMod ();
1416
1417   DPRINTF (("Okay..."));
1418
1419   fileIdList_elements (dercfiles, fid)
1420     {
1421       sourceFile = inputStream_create (cstring_copy (fileTable_fileName (fid)), C_EXTENSION, TRUE);
1422       context_setFileId (fid);
1423       
1424       /* Open source file  */
1425       
1426       if (inputStream_isUndefined (sourceFile) || (!inputStream_open (sourceFile)))
1427         {
1428           /* previously, this was ignored  ?! */
1429           llbug (message ("Could not open temp file: %s", fileTable_fileName (fid)));
1430         }
1431       else
1432         {
1433           yyin = inputStream_getFile (sourceFile); /*< shared <- only */
1434         
1435           llassert (yyin != NULL);
1436
1437           displayScan (message ("checking %q", osd_outputPath (fileTable_rootFileName (fid))));
1438           
1439           /*
1440           ** Every time, except the first time, through the loop,
1441           ** need to call yyrestart to clean up the parse buffer.
1442           */
1443
1444           if (!first_time)
1445             {
1446               (void) yyrestart (yyin);  
1447             }
1448           else
1449             {
1450               first_time = FALSE;
1451             }
1452           
1453           DPRINTF (("Entering..."));
1454           context_enterFile ();
1455           (void) yyparse ();
1456           context_exitCFile ();
1457                     
1458           (void) inputStream_close (sourceFile);
1459         }      
1460     } end_fileIdList_elements;
1461
1462   cptime = clock ();
1463   
1464   /* process any leftover macros */
1465
1466   context_processAllMacros ();
1467   
1468   /* check everything that was specified was defined */
1469   
1470   /* don't check if no c files were processed ?
1471   **   is this correct behaviour?
1472   */
1473   
1474   displayScan (cstring_makeLiteral ("global checks"));
1475
1476   cleanupMessages ();
1477   
1478   if (context_getLinesProcessed () > 0)
1479     {
1480       usymtab_allDefined ();
1481     }
1482
1483   if (context_maybeSet (FLG_TOPUNUSED))
1484     {
1485       uentry ue = usymtab_lookupSafe (cstring_makeLiteralTemp ("main"));
1486
1487       if (uentry_isValid (ue))
1488         {
1489           uentry_setUsed (ue, fileloc_observeBuiltin ());
1490         }
1491
1492       usymtab_allUsed ();
1493     }
1494
1495   if (context_maybeSet (FLG_EXPORTLOCAL))
1496     {
1497       usymtab_exportLocal ();
1498     }
1499
1500   
1501   if (context_maybeSet (FLG_EXPORTHEADER))
1502     {
1503       usymtab_exportHeader ();
1504     }
1505
1506   if (context_getFlag (FLG_SHOWUSES))
1507     {
1508       usymtab_displayAllUses ();
1509     }
1510
1511   context_checkSuppressCounts ();
1512
1513   if (context_doDump ())
1514     {
1515       cstring dump = context_getDump ();
1516
1517       dumpState (dump);
1518     }
1519
1520 # ifdef DOANNOTS
1521   printAnnots ();
1522 # endif
1523
1524   cleanupFiles ();
1525
1526   if (context_getFlag (FLG_SHOWSUMMARY))
1527     {
1528       summarizeErrors (); 
1529     }
1530
1531   
1532   {
1533     bool isQuiet = context_getFlag (FLG_QUIET);
1534     cstring specErrors = cstring_undefined;
1535 # ifndef NOLCL
1536     int nspecErrors = lclNumberErrors ();
1537 # endif
1538     
1539     expsuccess = TRUE;
1540
1541     if (context_neednl ())
1542       fprintf (g_warningstream, "\n");
1543     
1544 # ifndef NOLCL
1545     if (nspecErrors > 0)
1546       {
1547         if (nspecErrors == context_getLCLExpect ())
1548           {
1549             specErrors = 
1550               message ("%d spec warning%&, as expected\n       ", 
1551                        nspecErrors);
1552           }
1553         else
1554           {
1555             if (context_getLCLExpect () > 0)
1556               {
1557                 specErrors = 
1558                   message ("%d spec warning%&, expected %d\n       ", 
1559                            nspecErrors,
1560                            (int) context_getLCLExpect ());
1561               }
1562             else
1563               {
1564                 specErrors = message ("%d spec warning%&\n       ",
1565                                       nspecErrors);
1566                 expsuccess = FALSE;
1567               }
1568           }
1569       }
1570     else
1571         {
1572           if (context_getLCLExpect () > 0)
1573             {
1574               specErrors = message ("No spec warnings, expected %d\n       ", 
1575                                     (int) context_getLCLExpect ());
1576               expsuccess = FALSE;
1577             }
1578         }
1579 # endif
1580
1581       if (context_anyErrors ())
1582         {
1583           if (context_numErrors () == context_getExpect ())
1584             {
1585               if (!isQuiet) {
1586                 llmsg (message ("Finished checking --- "
1587                                 "%s%d code warning%&, as expected",
1588                                 specErrors, context_numErrors ()));
1589               }
1590             }
1591           else
1592             {
1593               if (context_getExpect () > 0)
1594                 {
1595                   if (!isQuiet) {
1596                     llmsg (message 
1597                            ("Finished checking --- "
1598                             "%s%d code warning%&, expected %d",
1599                             specErrors, context_numErrors (), 
1600                             (int) context_getExpect ()));
1601                   }
1602
1603                   expsuccess = FALSE;
1604                 }
1605               else
1606                 {
1607                   
1608                   if (!isQuiet)
1609                     {
1610                       llmsg (message ("Finished checking --- "
1611                                       "%s%d code warning%&", 
1612                                       specErrors, context_numErrors ()));
1613                     }
1614
1615                   expsuccess = FALSE;
1616                 }
1617             }
1618         }
1619       else
1620         {
1621           if (context_getExpect () > 0)
1622             {
1623               if (!isQuiet) {
1624                 llmsg (message
1625                        ("Finished checking --- "
1626                         "%sno code warnings, expected %d", 
1627                         specErrors,
1628                         (int) context_getExpect ()));
1629               }
1630
1631               expsuccess = FALSE;
1632             }
1633           else
1634             {
1635               if (context_getLinesProcessed () > 0)
1636                 {
1637                   if (cstring_isEmpty (specErrors))
1638                     {
1639                       if (!isQuiet) 
1640                         {
1641                           llmsg (message ("Finished checking --- no warnings"));
1642                         } 
1643                     }
1644                   else
1645                     {
1646                       if (!isQuiet) 
1647                         {
1648                           llmsg (message ("Finished checking --- %sno code warnings",
1649                                           specErrors));
1650                         }
1651                     }
1652                 }
1653               else
1654                 {
1655                   if (!isQuiet) {
1656                     llmsg (message ("Finished checking --- %sno code processed", 
1657                                     specErrors));
1658                   }
1659                 }
1660             }
1661         }
1662
1663       cstring_free (specErrors);
1664   }
1665   
1666   if (context_getFlag (FLG_STATS))
1667     {
1668       clock_t ttime = clock () - before;
1669       int specLines = context_getSpecLinesProcessed ();
1670       
1671       rstime = clock ();
1672       
1673       if (specLines > 0)
1674         {
1675           fprintf (g_warningstream, "%d spec, ", specLines);
1676         }
1677       
1678 # ifndef CLOCKS_PER_SEC
1679       fprintf (g_warningstream, "%d source lines in %ld time steps (steps/sec unknown)\n", 
1680                context_getLinesProcessed (), 
1681                (long) ttime);
1682 # else
1683       fprintf (g_warningstream, "%d source lines in %.2f s.\n", 
1684                context_getLinesProcessed (), 
1685                (double) ttime / CLOCKS_PER_SEC);
1686 # endif
1687     }
1688   else
1689     {
1690       rstime = clock ();
1691     }
1692   
1693   if (context_getFlag (FLG_TIMEDIST))
1694     {
1695       clock_t ttime = clock () - before;
1696       
1697       if (ttime > 0)
1698         {
1699           char *msg = (char *) dmalloc (256 * sizeof (*msg));
1700           
1701           if (anylcl)
1702             {
1703               sprintf (msg, 
1704                        "Time distribution (percent): initialize %.2f / lcl %.2f / "
1705                        "pre-process %.2f / c check %.2f / finalize %.2f \n", 
1706                        (100.0 * (double) (libtime - before) / ttime),
1707                        (100.0 * (double) (lcltime - libtime) / ttime),
1708                        (100.0 * (double) (pptime - lcltime) / ttime),
1709                        (100.0 * (double) (cptime - pptime) / ttime),
1710                        (100.0 * (double) (rstime - cptime) / ttime));
1711             }
1712           else
1713             {
1714               sprintf (msg, 
1715                        "Time distribution (percent): initialize %.2f / "
1716                        "pre-process %.2f / c check %.2f / finalize %.2f \n", 
1717                        (100.0 * (double) (libtime - before) / ttime),
1718                        (100.0 * (double) (pptime - libtime) / ttime),
1719                        (100.0 * (double) (cptime - pptime) / ttime),
1720                        (100.0 * (double) (rstime - cptime) / ttime));
1721             }
1722           
1723           llgenindentmsgnoloc (cstring_fromCharsO (msg));
1724         }
1725     }
1726
1727   if (context_getFlag(FLG_FORCEEXITSUCESS))
1728     {
1729       llexit (LLSUCCESS);
1730     }
1731   else
1732     {
1733       llexit (expsuccess ? LLSUCCESS : LLFAILURE);
1734     }
1735   
1736   BADBRANCHRET (LLFAILURE);
1737 }
1738
1739 # ifdef WIN32
1740 /*
1741 ** Reenable return value warnings.
1742 */
1743 # pragma warning (default:4035)
1744 # endif 
1745
1746 void
1747 showHelp (void)
1748 {
1749   showHerald ();
1750   
1751   llmsg (message ("Source files are .c, .h and %s files.  If there is no suffix,",
1752                   LCL_EXTENSION));
1753   llmsg (message ("   Splint will look for <file>.c and <file>%s.", LCL_EXTENSION));
1754   llmsglit ("");
1755   llmsglit ("Use splint -help <topic or flag name> for more information");
1756   llmsglit ("");
1757   llmsglit ("Topics:");
1758   llmsglit ("");
1759   llmsglit ("   annotations (describes source-code annotations)");
1760   llmsglit ("   comments (describes control comments)");
1761   llmsglit ("   flags (describes flag categories)");
1762   llmsglit ("   flags <category> (describes flags in category)");
1763   llmsglit ("   flags all (short description of all flags)");
1764   llmsglit ("   flags alpha (list all flags alphabetically)");
1765   llmsglit ("   flags full (full description of all flags)");
1766   llmsglit ("   mail (information on mailing lists)");
1767   llmsglit ("   modes (show mode settings)");
1768   llmsglit ("   parseerrors (help on handling parser errors)");
1769   llmsglit ("   prefixcodes (character codes in namespace prefixes)");
1770   llmsglit ("   references (sources for more information)");
1771   llmsglit ("   vars (environment variables)"); 
1772   llmsglit ("   version (information on compilation, maintainer)");
1773   llmsglit ("");
1774 }
1775
1776 static bool
1777 specialFlagsHelp (char *next)
1778 {
1779   if ((next != NULL) && (*next != '-') && (*next != '+'))
1780     {
1781       if (mstring_equal (next, "alpha"))
1782         {
1783           printAlphaFlags ();
1784           return TRUE;
1785         }
1786       else if (mstring_equal (next, "all"))
1787         {
1788           printAllFlags (TRUE, FALSE);
1789           return TRUE;
1790         }
1791       else if (mstring_equal (next, "categories")
1792                || mstring_equal (next, "cats"))
1793         {
1794           listAllCategories ();
1795           return TRUE;
1796         }
1797       else if (mstring_equal (next, "full"))
1798         {
1799           printAllFlags (FALSE, TRUE);
1800           return TRUE;
1801         }
1802       else if (mstring_equal (next, "manual"))
1803         {
1804           printFlagManual (FALSE);
1805           return TRUE;
1806         }
1807       else if (mstring_equal (next, "webmanual"))
1808         {
1809           printFlagManual (TRUE);
1810           return TRUE;
1811         }
1812       else
1813         {
1814           return FALSE;
1815         }
1816     }
1817   else
1818     {
1819       return FALSE;
1820     }
1821 }
1822
1823 void
1824 printParseErrors (void)
1825 {
1826   llmsglit ("Parse Errors");
1827   llmsglit ("------------");
1828   llmsglit ("");
1829   llmsglit ("Splint will sometimes encounter a parse error for code that "
1830             "can be parsed with a local compiler. There are a few likely "
1831             "causes for this and a number of techniques that can be used "
1832             "to work around the problem.");
1833   llmsglit ("");
1834   llmsglit ("Compiler extensions --- compilers sometimes extend the C "
1835             "language with compiler-specific keywords and syntax. While "
1836             "it is not advisible to use these, oftentimes one has no choice "
1837             "when the system header files use compiler extensions. ");
1838   llmsglit ("");
1839   llmsglit ("Splint supports some of the GNU (gcc) compiler extensions, "
1840             "if the +gnuextensions flag is set. You may be able to workaround "
1841             "other compiler extensions by using a pre-processor define. "
1842             "Alternately, you can surround the unparseable code with");
1843   llmsglit ("");
1844   llmsglit ("   # ifndef S_SPLINT_S");
1845   llmsglit ("   ...");
1846   llmsglit ("   # endif");
1847   llmsglit ("");
1848   /* evans 2000-12-21 fixed typo reported by Jeroen Ruigrok/Asmodai */
1849   llmsglit ("Missing type definitions --- an undefined type name will usually "
1850             "lead to a parse error. This often occurs when a standard header "
1851             "file defines some type that is not part of the standard library. ");
1852   llmsglit ("By default, Splint does not process the local files corresponding "
1853             "to standard library headers, but uses a library specification "
1854             "instead so dependencies on local system headers can be detected. "
1855             "If another system header file that does not correspond to a "
1856             "standard library header uses one of these superfluous types, "
1857             "a parse error will result.");
1858   llmsglit ("");
1859   llmsglit ("If the parse error is inside a posix standard header file, the "
1860             "first thing to try is +posixlib. This makes Splint use "
1861             "the posix library specification instead of reading the posix "
1862             "header files.");
1863   llmsglit ("");
1864   llmsglit ("Otherwise, you may need to either manually define the problematic "
1865             "type (e.g., add -Dmlink_t=int to your .splintrc file) or force "
1866             "splint to process the header file that defines it. This is done "
1867             "by setting -skipisoheaders or -skipposixheaders before "
1868             "the file that defines the type is #include'd.");
1869   llmsglit ("(See splint -help "
1870             "skipisoheaders and splint -help skipposixheaders for a list of "
1871             "standard headers.)  For example, if <sys/local.h> uses a type "
1872             "defined by posix header <sys/types.h> but not defined by the "
1873             "posix library, we might do: ");
1874   llmsglit ("");
1875   llmsglit ("   /*@-skipposixheaders@*/");
1876   llmsglit ("   # include <sys/types.h>");
1877   llmsglit ("   /*@=skipposixheaders@*/");
1878   llmsglit ("   # include <sys/local.h>");
1879   llmsglit ("");
1880   llmsglit ("to force Splint to process <sys/types.h>.");
1881   llmsglit ("");
1882   llmsglit ("At last resort, +trytorecover can be used to make Splint attempt "
1883             "to continue after a parse error.  This is usually not successful "
1884             "and the author does not consider assertion failures when +trytorecover "
1885             "is used to be bugs.");
1886 }
1887
1888 void
1889 printAnnotations (void)
1890 {
1891   llmsglit ("Annotations");
1892   llmsglit ("-----------");
1893   llmsglit ("");
1894   llmsglit ("Annotations are semantic comments that document certain "
1895             "assumptions about functions, variables, parameters, and types. ");
1896   llmsglit ("");
1897   llmsglit ("They may be used to indicate where the representation of a "
1898             "user-defined type is hidden, to limit where a global variable may "
1899             "be used or modified, to constrain what a function implementation "
1900             "may do to its parameters, and to express checked assumptions about "
1901             "variables, types, structure fields, function parameters, and "
1902             "function results.");
1903   llmsglit ("");
1904   llmsglit ("Annotations are introduced by \"/*@\". The role of the @ may be "
1905             "played by any printable character, selected using -commentchar <char>.");
1906   llmsglit ("");
1907   llmsglit ("Consult the User's Guide for descriptions of checking associated with each annotation.");
1908   llmsglit ("");
1909   llmsglit ("Globals: (in function declarations)");
1910   llmsglit ("   /*@globals <globitem>,+ @*/");
1911   llmsglit ("      globitem is an identifier, internalState or fileSystem");
1912   llmsglit ("");
1913   llmsglit ("Modifies: (in function declarations)");
1914   llmsglit ("   /*@modifies <moditem>,+ @*/");
1915   llmsglit ("      moditem is an lvalue");
1916   llmsglit ("   /*@modifies nothing @*/");
1917   llmsglit ("   /*@*/   (Abbreviation for no globals and modifies nothing.)");
1918   llmsglit ("");
1919   llmsglit ("Iterators:");
1920   llmsglit ("   /*@iter <identifier> (<parameter-type-list>) @*/ - declare an iterator");
1921   llmsglit ("");
1922   llmsglit ("Constants:");
1923   llmsglit ("   /*@constant <declaration> @*/ - declares a constant");
1924   llmsglit ("");
1925   llmsglit ("Alternate Types:");
1926   llmsglit ("   /*@alt <basic-type>,+ @*/");
1927   llmsglit ("   (e.g., int /*@alt char@*/ is a type matching either int or char)");
1928   llmsglit ("");
1929   llmsglit ("Declarator Annotations");
1930   llmsglit ("");
1931   llmsglit ("Type Definitions:");
1932   llmsglit ("   /*@abstract@*/ - representation is hidden from clients");
1933   llmsglit ("   /*@concrete@*/ - representation is visible to clients");
1934   llmsglit ("   /*@immutable@*/ - instances of the type cannot change value");
1935   llmsglit ("   /*@mutable@*/ - instances of the type can change value");
1936   llmsglit ("   /*@refcounted@*/ - reference counted type");
1937   llmsglit ("");
1938   llmsglit ("Global Variables:");
1939   llmsglit ("   /*@unchecked@*/ - weakest checking for global use");
1940   llmsglit ("   /*@checkmod@*/ - check modification by not use of global");
1941   llmsglit ("   /*@checked@*/ - check use and modification of global");
1942   llmsglit ("   /*@checkedstrict@*/ - check use of global strictly");
1943   llmsglit ("");
1944   llmsglit ("Memory Management:");
1945   llmsglit ("   /*@dependent@*/ - a reference to externally-owned storage");
1946   llmsglit ("   /*@keep@*/ - a parameter that is kept by the called function");
1947   llmsglit ("   /*@killref@*/ - a refcounted parameter, killed by the call");
1948   llmsglit ("   /*@only@*/ - an unshared reference");
1949   llmsglit ("   /*@owned@*/ - owner of storage that may be shared by /*@dependent@*/ references");
1950   llmsglit ("   /*@shared@*/ - shared reference that is never deallocated");
1951   llmsglit ("   /*@temp@*/ - temporary parameter");
1952   llmsglit ("");
1953   llmsglit ("Aliasing:");
1954   llmsglit ("   /*@unique@*/ - may not be aliased by any other visible reference");
1955   llmsglit ("   /*@returned@*/ - may be aliased by the return value");
1956   llmsglit ("");
1957   llmsglit ("Exposure:");
1958   llmsglit ("   /*@observer@*/ - reference that cannot be modified");
1959   llmsglit ("   /*@exposed@*/ - exposed reference to storage in another object");
1960   llmsglit ("");
1961   llmsglit ("Definition State:");
1962   llmsglit ("   /*@out@*/ - storage reachable from reference need not be defined");
1963   llmsglit ("   /*@in@*/ - all storage reachable from reference must be defined");
1964   llmsglit ("   /*@partial@*/ - partially defined, may have undefined fields");
1965   llmsglit ("   /*@reldef@*/ - relax definition checking");
1966   llmsglit ("");
1967   llmsglit ("Global State: (for globals lists, no /*@, since list is already in /*@\'s)");
1968   llmsglit ("   undef - variable is undefined before the call");
1969   llmsglit ("   killed - variable is undefined after the call");
1970   llmsglit ("");
1971   llmsglit ("Null State:");
1972   llmsglit ("   /*@null@*/ - possibly null pointer");
1973   llmsglit ("   /*@notnull@*/ - definitely non-null pointer");
1974   llmsglit ("   /*@relnull@*/ - relax null checking");
1975   llmsglit ("");
1976   llmsglit ("Null Predicates:");
1977   llmsglit ("   /*@nullwhentrue@*/ - if result is TRUE, first parameter is NULL");
1978   llmsglit ("   /*@falsewhennull@*/ - if result is TRUE, first parameter is not NULL");
1979   llmsglit ("");
1980   llmsglit ("Execution:");
1981   llmsglit ("   /*@noreturn@*/ - function never returns");
1982   llmsglit ("   /*@maynotreturn@*/ - function may or may not return");
1983   llmsglit ("   /*@noreturnwhentrue@*/ - function does not return if first parameter is TRUE");
1984   llmsglit ("   /*@noreturnwhenfalse@*/ - function does not return if first parameter if FALSE");
1985   llmsglit ("   /*@alwaysreturns@*/ - function always returns");
1986   llmsglit ("");
1987   llmsglit ("Side-Effects:");
1988   llmsglit ("   /*@sef@*/ - corresponding actual parameter has no side effects");
1989   llmsglit ("");
1990   llmsglit ("Declaration:");
1991   llmsglit ("   /*@unused@*/ - need not be used (no unused errors reported)");
1992   llmsglit ("   /*@external@*/ - defined externally (no undefined error reported)");
1993   llmsglit ("");
1994   llmsglit ("Case:");
1995   llmsglit ("   /*@fallthrough@*/ - fall-through case");
1996   llmsglit ("");
1997   llmsglit ("Break:");
1998   llmsglit ("   /*@innerbreak@*/ - break is breaking an inner loop or switch");
1999   llmsglit ("   /*@loopbreak@*/ - break is breaking a loop");
2000   llmsglit ("   /*@switchbreak@*/ - break is breaking a switch");
2001   llmsglit ("   /*@innercontinue@*/ - continue is continuing an inner loop");
2002   llmsglit ("");
2003   llmsglit ("Unreachable Code:");
2004   llmsglit ("   /*@notreached@*/ - statement may be unreachable.");
2005   llmsglit ("");
2006   llmsglit ("Special Functions:");
2007   llmsglit ("   /*@printflike@*/ - check variable arguments like printf");
2008   llmsglit ("   /*@scanflike@*/ - check variable arguments like scanf");
2009 }
2010
2011 void
2012 printComments (void)
2013 {
2014   llmsglit ("Control Comments");
2015   llmsglit ("----------------");
2016   llmsglit ("");
2017   llmsglit ("Setting Flags");
2018   llmsglit ("");
2019   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.");
2020   llmsglit ("");
2021   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,");
2022   llmsglit ("   /*@+boolint -modifies =showfunc@*/");
2023   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).");
2024   llmsglit ("");
2025   llmsglit ("Error Suppression");
2026   llmsglit ("");
2027   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.");
2028   llmsglit ("");
2029   llmsglit ("/*@ignore@*/ ... /*@end@*/");
2030   llgenindentmsgnoloc
2031     (cstring_makeLiteral 
2032      ("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."));
2033   llmsglit ("/*@i@*/");
2034     llgenindentmsgnoloc
2035     (cstring_makeLiteral 
2036      ("No errors will be reported from an /*@i@*/ comment to the end of the line."));
2037   llmsglit ("/*@i<n>@*/");
2038   llgenindentmsgnoloc
2039     (cstring_makeLiteral 
2040      ("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."));
2041   llmsglit ("/*@t@*/, /*@t<n>@*/");
2042   llgenindentmsgnoloc
2043     (cstring_makeLiteral 
2044      ("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."));
2045   llmsglit ("");
2046   llmsglit ("Type Access");
2047   llmsglit ("");
2048   llmsglit ("/*@access <type>@*/"); 
2049   llmsglit ("   Allows the following code to access the representation of <type>");
2050   llmsglit ("/*@noaccess <type>@*/");
2051   llmsglit ("   Hides the representation of <type>");
2052   llmsglit ("");
2053   llmsglit ("Macro Expansion");
2054   llmsglit ("");
2055   llmsglit ("/*@notfunction@*/");
2056   llgenindentmsgnoloc 
2057     (cstring_makeLiteral
2058      ("Indicates that the next macro definition is not intended to be a "
2059       "function, and should be expanded in line instead of checked as a "
2060       "macro function definition."));
2061 }
2062
2063   
2064 void
2065 printFlags (void)
2066 {
2067   llmsglit ("Flag Categories");
2068   llmsglit ("---------------");
2069   listAllCategories ();
2070   llmsglit ("\nTo see the flags in a flag category, do\n   splint -help flags <category>");
2071   llmsglit ("To see a list of all flags in alphabetical order, do\n   splint -help flags alpha");
2072   llmsglit ("To see a full description of all flags, do\n   splint -help flags full");
2073 }
2074
2075 void
2076 printMaintainer (void)
2077 {
2078   llmsg (message ("Maintainer: %s", cstring_makeLiteralTemp (SPLINT_MAINTAINER)));
2079   llmsglit (LCL_COMPILE);
2080 }
2081
2082 void
2083 printMail (void)
2084 {
2085   llmsglit ("Mailing Lists");
2086   llmsglit ("-------------");
2087   llmsglit ("");
2088   llmsglit ("There are two mailing lists associated with Splint: ");
2089   llmsglit ("");
2090   llmsglit ("   lclint-announce@virginia.edu");
2091   llmsglit ("");
2092   llmsglit ("      Reserved for announcements of new releases and bug fixes.");
2093   llmsglit ("      To subscribe, send a message to majordomo@virginia.edu with body: ");
2094   llmsglit ("           subscribe lclint-announce");
2095   llmsglit ("");
2096   llmsglit ("   lclint-interest@virginia.edu");
2097   llmsglit ("");
2098   llmsglit ("      Informal discussions on the use and development of Splint.");
2099   llmsglit ("      To subscribe, send a message to majordomo@virginia.edu with body: ");
2100   llmsglit ("           subscribe lclint-interest");
2101 }
2102
2103 void
2104 printReferences (void)
2105 {
2106   llmsglit ("References");
2107   llmsglit ("----------");
2108   llmsglit ("");
2109   llmsglit ("For more information, see the Splint web site: http://www.splint.org");
2110 }
2111
2112 void
2113 describePrefixCodes (void)
2114 {
2115   llmsglit ("Prefix Codes");
2116   llmsglit ("------------");
2117   llmsglit ("");
2118   llmsglit ("These characters have special meaning in name prefixes:");
2119   llmsglit ("");
2120   llmsg (message ("   %h  Any uppercase letter [A-Z]", PFX_UPPERCASE));
2121   llmsg (message ("   %h  Any lowercase letter [a-z]", PFX_LOWERCASE));
2122   llmsg (message ("   %h  Any character (valid in a C identifier)", PFX_ANY));
2123   llmsg (message ("   %h  Any digit [0-9]", PFX_DIGIT));
2124   llmsg (message ("   %h  Any non-uppercase letter [a-z0-9_]", PFX_NOTUPPER));
2125   llmsg (message ("   %h  Any non-lowercase letter [A-Z0-9_]", PFX_NOTLOWER));
2126   llmsg (message ("   %h  Any letter [A-Za-z]", PFX_ANYLETTER));
2127   llmsg (message ("   %h  Any letter or digit [A-Za-z0-9]", PFX_ANYLETTERDIGIT));
2128   llmsglit ("   *  Zero or more repetitions of the previous character class until the end of the name");
2129 }
2130
2131 void
2132 describeVars (void)
2133 {
2134   cstring eval;
2135   cstring def;
2136
2137   eval = context_getLarchPath ();
2138   def = osd_getEnvironmentVariable (LARCH_PATH);
2139
2140   if (cstring_isDefined (def) || 
2141       !cstring_equal (eval, cstring_fromChars (DEFAULT_LARCHPATH)))
2142     {
2143       llmsg (message ("LARCH_PATH = %s", eval));
2144     }
2145   else
2146     {
2147       llmsg (message ("LARCH_PATH = <not set> (default = %s)",
2148                       cstring_fromChars (DEFAULT_LARCHPATH)));
2149     }
2150   
2151   llmsglit ("   --- path used to find larch initialization files and LSL traits");
2152
2153   eval = context_getLCLImportDir ();
2154   def = osd_getEnvironmentVariable (cstring_makeLiteralTemp (LCLIMPORTDIR));
2155
2156   if (cstring_isDefined (def) ||
2157       !cstring_equal (eval, cstring_fromChars (DEFAULT_LCLIMPORTDIR)))
2158     {
2159       llmsg (message ("%q = %s", cstring_makeLiteral (LCLIMPORTDIR), eval));
2160     }
2161   else
2162     {
2163       llmsg (message ("%s = <not set, default: %s>", cstring_makeLiteralTemp (LCLIMPORTDIR), 
2164                       cstring_makeLiteralTemp (DEFAULT_LCLIMPORTDIR))); 
2165     }
2166   
2167   llmsglit ("   --- directory containing lcl standard library files "
2168             "(import with < ... >)");;
2169
2170   llmsg (message 
2171          ("include path = %q (set by environment variable %s and -I flags)",
2172           cppReader_getIncludePath (), INCLUDEPATH_VAR));
2173
2174   llmsglit ("   --- path used to find #include'd files");
2175
2176   llmsg (message 
2177          ("systemdirs = %s (set by -systemdirs or environment variable %s)", /*@i413223@*/
2178           context_getString (FLG_SYSTEMDIRS),
2179           INCLUDEPATH_VAR));
2180
2181   llmsglit ("   --- if file is found on this path, it is treated as a system file for error reporting");
2182 }
2183
2184 void
2185 interrupt (int i)
2186 {
2187   switch (i)
2188     {
2189     case SIGINT:
2190       fprintf (g_errorstream, "*** Interrupt\n");
2191       llexit (LLINTERRUPT);
2192     case SIGSEGV:
2193       {
2194         cstring loc;
2195         
2196         /* Cheat when there are parse errors */
2197         checkParseError (); 
2198         
2199         fprintf (g_errorstream, "*** Segmentation Violation\n");
2200         
2201         /* Don't catch it if fileloc_unparse causes a signal */
2202         (void) signal (SIGSEGV, NULL);
2203
2204         loc = fileloc_unparse (g_currentloc);
2205         
2206         fprintf (g_errorstream, "*** Location (not trusted): %s\n", 
2207                  cstring_toCharsSafe (loc));
2208         cstring_free (loc);
2209         printCodePoint ();
2210         fprintf (g_errorstream, "*** Please report bug to %s\n", SPLINT_MAINTAINER);
2211         exit (LLGIVEUP);
2212       }
2213     default:
2214       fprintf (g_errorstream, "*** Signal: %d\n", i);
2215       /*@-mustfree@*/
2216       fprintf (g_errorstream, "*** Location (not trusted): %s\n", 
2217                cstring_toCharsSafe (fileloc_unparse (g_currentloc)));
2218       /*@=mustfree@*/
2219       printCodePoint ();
2220       fprintf (g_errorstream, "*** Please report bug to %s ***\n", SPLINT_MAINTAINER);
2221       exit (LLGIVEUP);
2222     }
2223 }
2224
2225 void
2226 cleanupFiles (void)
2227 {
2228   static bool doneCleanup = FALSE;
2229
2230   /* make sure this is only called once! */
2231
2232   if (doneCleanup) return;
2233
2234   setCodePoint ();
2235
2236   /*
2237   ** Close all open files
2238   **    (There should only be open files, if we exited after a fatal error.)
2239   */
2240
2241   fileTable_closeAll (context_fileTable ());
2242
2243   if (context_getFlag (FLG_KEEP))
2244     {
2245       check (fputs ("Temporary files kept:\n", g_messagestream) != EOF);
2246       fileTable_printTemps (context_fileTable ());
2247     }
2248   else
2249     {
2250 # ifdef WIN32
2251       int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
2252       
2253       if (nfiles != 0) 
2254         {
2255           llbug (message ("Files unclosed: %d", nfiles));
2256         }
2257 # endif
2258       fileTable_cleanup (context_fileTable ());
2259     }
2260
2261   doneCleanup = TRUE;
2262 }
2263
2264 /*
2265 ** cleans up temp files (if necessary) and exits
2266 */
2267
2268 /*@noreturn@*/ void
2269 llexit (int status)
2270 {
2271   DPRINTF (("llexit: %d", status));
2272
2273 # ifdef WIN32
2274   if (status == LLFAILURE) 
2275     {
2276       _fcloseall ();
2277     }
2278 # endif
2279
2280   cleanupFiles ();
2281
2282   if (status != LLFAILURE)
2283     {
2284       context_destroyMod ();
2285       exprNode_destroyMod ();
2286       
2287       sRef_destroyMod ();
2288       uentry_destroyMod ();
2289       typeIdSet_destroyMod ();
2290       
2291 # ifdef USEDMALLOC
2292       dmalloc_shutdown ();
2293 # endif
2294     }
2295
2296   exit ((status == LLSUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE);
2297 }
2298
2299 bool readOptionsFile (cstring fname, cstringSList *passThroughArgs, bool report)
2300 {
2301   bool res = FALSE;
2302
2303   if (fileTable_exists (context_fileTable (), fname))
2304     {
2305       if (report)
2306         {
2307           voptgenerror
2308             (FLG_WARNRC, 
2309              message ("Multiple attempts to read options file: %s", fname),
2310              g_currentloc);
2311         }
2312     }
2313   else
2314     {
2315       FILE *innerf = fileTable_openReadFile (context_fileTable (), fname);
2316       
2317       if (innerf != NULL)
2318         {
2319           fileloc fc = g_currentloc;
2320           g_currentloc = fileloc_createRc (fname);
2321
2322           displayScan (message ("< reading options from %q >", 
2323                                 fileloc_outputFilename (g_currentloc)));
2324           
2325           loadrc (innerf, passThroughArgs);
2326           fileloc_reallyFree (g_currentloc);
2327           g_currentloc = fc;
2328           res = TRUE;
2329         }
2330       else 
2331         {
2332           if (report)
2333             {
2334               voptgenerror
2335                 (FLG_WARNRC, 
2336                  message ("Cannot open options file: %s", fname),
2337                  g_currentloc);
2338             }
2339         }
2340     }
2341
2342   return res;
2343 }
2344
2345 /*
2346 ** This shouldn't be necessary, but Apple Darwin can't handle '"''s.
2347 */
2348
2349 void
2350 loadrc (/*:open:*/ FILE *rcfile, cstringSList *passThroughArgs)
2351    /*@modifies rcfile@*/
2352    /*@ensures closed rcfile@*/
2353 {
2354   char *s = mstring_create (MAX_LINE_LENGTH);
2355   char *os = s;
2356   
2357   DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2358
2359   s = os;
2360
2361   while (reader_readLine (rcfile, s, MAX_LINE_LENGTH) != NULL)
2362     {
2363       char c;
2364       bool set = FALSE;     
2365       char *thisflag;
2366       flagcode opt;
2367
2368       DPRINTF (("Line: %s", s));
2369       DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2370             
2371       while (*s == ' ' || *s == '\t')
2372         {
2373           s++;
2374           incColumn ();
2375         }
2376       
2377       while (*s != '\0')
2378         {
2379           bool escaped = FALSE;
2380           bool quoted = FALSE;
2381           c = *s;
2382
2383           DPRINTF (("Process: %s", s));
2384           DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2385           /* comment characters */
2386           if (c == '#' || c == ';' || c == '\n') 
2387             {
2388               /*@innerbreak@*/
2389               break;
2390             }
2391           
2392           if (c == '-' || c == '+')
2393             {
2394               set = (c == '+');
2395             }
2396           else
2397             {
2398               showHerald ();
2399               voptgenerror (FLG_BADFLAG, 
2400                             message ("Bad flag syntax (+ or - expected, "
2401                                      "+ is assumed): %s", 
2402                                      cstring_fromChars (s)),
2403                             g_currentloc);
2404               s--;
2405               set = TRUE;
2406             }
2407           
2408           s++;
2409           incColumn ();
2410           
2411           thisflag = s;
2412           
2413           while ((c = *s) != '\0')
2414             { /* remember to handle spaces and quotes in -D and -U ... */
2415               if (escaped)
2416                 {
2417                   escaped = FALSE;
2418                 }
2419               else if (quoted)
2420                 {
2421                   if (c == '\\')
2422                     {
2423                       escaped = TRUE;
2424                     }
2425                   else if (c == '\"')
2426                     {
2427                       quoted = FALSE;
2428                     }
2429                   else
2430                     {
2431                       ;
2432                     }
2433                 }
2434               else if (c == '\"')
2435                 {
2436                   quoted = TRUE;
2437                 }
2438               else
2439                 {
2440                  if (c == ' ' || c == '\t' || c == '\n')
2441                    {
2442                      /*@innerbreak@*/ break;
2443                    }
2444                }
2445                   
2446               s++; 
2447               incColumn ();
2448             }
2449
2450           DPRINTF (("Nulling: %c", *s));
2451           *s = '\0';
2452
2453           if (mstring_isEmpty (thisflag))
2454             {
2455               llfatalerror (message ("Missing flag: %s",
2456                                      cstring_fromChars (os)));
2457             }
2458
2459           DPRINTF (("Flag: %s", thisflag));
2460
2461           opt = flags_identifyFlag (cstring_fromChars (thisflag));
2462           
2463           if (flagcode_isSkip (opt))
2464             {
2465               ;
2466             }
2467           else if (flagcode_isInvalid (opt))
2468             {
2469               DPRINTF (("Invalid: %s", thisflag));
2470
2471               if (isMode (cstring_fromChars (thisflag)))
2472                 {
2473                   context_setMode (cstring_fromChars (thisflag));
2474                 }
2475               else
2476                 {
2477                   voptgenerror (FLG_BADFLAG,
2478                                 message ("Unrecognized option: %s", 
2479                                          cstring_fromChars (thisflag)),
2480                                 g_currentloc);
2481                 }
2482             }
2483           else
2484             {
2485               context_userSetFlag (opt, set);
2486
2487               if (flagcode_hasArgument (opt))
2488                 {
2489                   if (opt == FLG_HELP)
2490                     {
2491                       showHerald ();
2492                       voptgenerror (FLG_BADFLAG,
2493                                     message ("Cannot use help in rc files"),
2494                                     g_currentloc);
2495                     }
2496                   else if (flagcode_isPassThrough (opt)) /* -D or -U */
2497                     {
2498                       cstring arg = cstring_fromCharsNew (thisflag);
2499                       cstring_markOwned (arg);
2500                       *passThroughArgs = cstringSList_add (*passThroughArgs, arg);
2501                       DPRINTF (("Pass through: %s",
2502                                 cstringSList_unparse (*passThroughArgs)));
2503                     }
2504                   else if (opt == FLG_INCLUDEPATH 
2505                            || opt == FLG_SPECPATH)
2506                     {
2507                       cstring dir = cstring_suffix (cstring_fromChars (thisflag), 1); /* skip over I/S */
2508                                       
2509                       switch (opt)
2510                         {
2511                         case FLG_INCLUDEPATH:
2512                           cppAddIncludeDir (dir);
2513                           /*@switchbreak@*/ break;
2514                         case FLG_SPECPATH:
2515                           /*@-mustfree@*/
2516                           g_localSpecPath = cstring_toCharsSafe
2517                             (message ("%s:%s", cstring_fromChars (g_localSpecPath), dir));
2518                           /*@=mustfree@*/
2519                           /*@switchbreak@*/ break;
2520                           BADDEFAULT;
2521                         }
2522                     }
2523                   else if (flagcode_hasString (opt)
2524                            || flagcode_hasNumber (opt)
2525                            || flagcode_hasChar (opt)
2526                            || opt == FLG_INIT || opt == FLG_OPTF)
2527                     {
2528                       cstring extra = cstring_undefined;
2529                       char *rest, *orest;
2530                       char rchar;
2531                       
2532                       *s = c;
2533                       rest = mstring_copy (s);
2534                       DPRINTF (("Here: rest = %s", rest));
2535                       orest = rest;
2536                       *s = '\0';
2537                       
2538                       while ((rchar = *rest) != '\0'
2539                              && (isspace ((int) rchar)))
2540                         {
2541                           rest++;
2542                           s++;
2543                         }
2544                       
2545                       DPRINTF (("Yo: %s", rest));
2546
2547                       while ((rchar = *rest) != '\0' 
2548                              && !isspace ((int) rchar))
2549                         {
2550                           extra = cstring_appendChar (extra, rchar);
2551                           rest++; 
2552                           s++;
2553                         }
2554                       
2555                       DPRINTF (("Yo: %s", extra));
2556                       sfree (orest);
2557
2558                       if (cstring_isUndefined (extra))
2559                         {
2560                           showHerald ();
2561                           voptgenerror 
2562                             (FLG_BADFLAG,
2563                              message
2564                              ("Flag %s must be followed by an argument",
2565                               flagcode_unparse (opt)),
2566                              g_currentloc);
2567                         }
2568                       else
2569                         {
2570                           s--;
2571                           
2572                           DPRINTF (("Here we are: %s", extra));
2573
2574                           if (flagcode_hasNumber (opt) || flagcode_hasChar (opt))
2575                             {
2576                               DPRINTF (("Set value flag: %s", extra));
2577                               setValueFlag (opt, extra);
2578                             }
2579                           else if (opt == FLG_OPTF)
2580                             {
2581                               (void) readOptionsFile (extra, passThroughArgs, TRUE);
2582                             }
2583                           else if (opt == FLG_INIT)
2584                             {
2585 # ifndef NOLCL
2586                               llassert (inputStream_isUndefined (initFile));
2587                               
2588                               initFile = inputStream_create 
2589                                 (cstring_copy (extra), 
2590                                  cstring_makeLiteralTemp (LCLINIT_SUFFIX),
2591                                  FALSE);
2592 # endif
2593                             }
2594                           else if (flagcode_hasString (opt))
2595                             {
2596                               DPRINTF (("Here: %s", extra));
2597
2598                               /*
2599                               ** If it has "'s, we need to remove them.
2600                               */
2601
2602                               if (cstring_firstChar (extra) == '\"')
2603                                 {
2604                                   if (cstring_lastChar (extra) == '\"')
2605                                     {
2606                                       cstring unquoted = cstring_copyLength 
2607                                         (cstring_toCharsSafe (cstring_suffix (extra, 1)),
2608                                          cstring_length (extra) - 2);
2609
2610                                       DPRINTF (("string flag: %s -> %s", extra, unquoted));
2611                                       setStringFlag (opt, unquoted);
2612                                       cstring_free (extra);
2613                                     }
2614                                   else
2615                                     {
2616                                       voptgenerror
2617                                         (FLG_BADFLAG, 
2618                                          message ("Unmatched \" in option string: %s", 
2619                                                   extra),
2620                                          g_currentloc);
2621                                       setStringFlag (opt, extra);
2622                                     }
2623                                 }
2624                               else
2625                                 {
2626                                   DPRINTF (("No quotes: %s", extra));
2627                                   setStringFlag (opt, extra);
2628                                 }
2629
2630                               extra = cstring_undefined;
2631                             }
2632                           else
2633                             {
2634                               BADEXIT;
2635                             }
2636                         }
2637
2638                       cstring_free (extra); 
2639                     }
2640                   else
2641                     {
2642                       BADEXIT;
2643                     }
2644                 }
2645             }
2646           
2647           *s = c;
2648           DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2649           while ((c == ' ') || (c == '\t'))
2650             {
2651               c = *(++s);
2652               incColumn ();
2653             } 
2654         }
2655       DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2656       s = os;
2657     }
2658
2659   DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2660   sfree (os); 
2661   check (fileTable_closeFile (context_fileTable (), rcfile));
2662 }
2663
2664 static fileIdList preprocessFiles (fileIdList fl, bool xhfiles)
2665   /*@modifies fileSystem@*/
2666 {
2667   bool msg = (context_getFlag (FLG_SHOWSCAN) && fileIdList_size (fl) > 10);
2668   int skip = (fileIdList_size (fl) / 5);
2669   int filesprocessed = 0;
2670   fileIdList dfiles = fileIdList_create ();
2671
2672   fileloc_free (g_currentloc);
2673   g_currentloc = fileloc_createBuiltin ();
2674
2675   fileIdList_elements (fl, fid)
2676     {
2677       cstring ppfname = fileTable_fileName (fid);
2678
2679       if (!(osd_fileIsReadable (ppfname)))
2680         {
2681           lldiagmsg (message ("Cannot open file: %q", osd_outputPath (ppfname)));
2682           ppfname = cstring_undefined;
2683         }
2684
2685       if (cstring_isDefined (ppfname))
2686         {
2687           fileId dfile = fileTable_addCTempFile (context_fileTable (), fid);
2688
2689           if (xhfiles)
2690             {
2691               llassert (fileTable_isXHFile (context_fileTable (), dfile));
2692             }
2693
2694           llassert (cstring_isNonEmpty (ppfname));
2695           
2696           if (msg)
2697             {
2698               if ((filesprocessed % skip) == 0) 
2699                 {
2700                   if (filesprocessed == 0) {
2701                     fprintf (g_messagestream, " ");
2702                   }
2703                   else {
2704                     fprintf (g_messagestream, ".");
2705                   }
2706                   
2707                   (void) fflush (g_messagestream);
2708                 }
2709               filesprocessed++;
2710             }
2711
2712           DPRINTF (("outfile: %s", fileTable_fileName (dfile)));
2713
2714           if (cppProcess (ppfname, fileTable_fileName (dfile)) != 0) 
2715             {
2716               llfatalerror (message ("Preprocessing error for file: %s", 
2717                                      fileTable_rootFileName (fid)));
2718             }
2719           
2720           fileIdList_add (dfiles, dfile);
2721         }
2722     } end_fileIdList_elements; 
2723     
2724     return dfiles;
2725 }
2726
2727 /* This should be in an lclUtils.c file... */
2728 # ifndef NOLCL
2729 char *specFullName (char *specfile, /*@out@*/ char **inpath)
2730 {
2731   /* extract the path and the specname associated with the given file */
2732   char *specname = (char *) dmalloc (sizeof (*specname) 
2733                                      * (strlen (specfile) + 9));
2734   char *ospecname = specname;
2735   char *path = (char *) dmalloc (sizeof (*path) * (strlen (specfile)));
2736   size_t size;
2737   long int i, j;
2738   
2739   /* initialized path to empty string or may have accidental garbage */
2740   *path = '\0';
2741
2742   /*@-mayaliasunique@*/ 
2743   strcpy (specname, specfile);
2744   /*@=mayaliasunique@*/ 
2745
2746   /* trim off pathnames in specfile */
2747   size = strlen (specname);
2748
2749   for (i = size_toInt (size) - 1; i >= 0; i--)
2750     {
2751       if (specname[i] == CONNECTCHAR)
2752         {
2753           /* strcpy (specname, (char *)specname+i+1); */
2754           for (j = 0; j <= i; j++)      /* include '/'  */
2755             {
2756               path[j] = specname[j];
2757             }
2758
2759           path[i + 1] = '\0';
2760           specname += i + 1;
2761           break;
2762         }
2763     }
2764
2765   /* 
2766   ** also remove .lcl file extension, assume it's the last extension
2767   ** of the file name 
2768   */
2769
2770   size = strlen (specname);
2771
2772   for (i = size_toInt (size) - 1; i >= 0; i--)
2773     {
2774       if (specname[i] == '.')
2775         {
2776           specname[i] = '\0';
2777           break;
2778         }
2779     }
2780   
2781   *inpath = path;
2782
2783   /*
2784   ** If specname no longer points to the original char,
2785   ** we need to allocate a new pointer and copy the string.
2786   */
2787
2788   if (specname != ospecname) {
2789     char *rspecname = (char *) dmalloc (sizeof (*rspecname) * (strlen (specname) + 1));
2790     strcpy (rspecname, specname); /* evs 2000-05-16: Bug: was ospecname! */
2791     sfree (ospecname);
2792     return rspecname;
2793   } 
2794
2795   return specname;
2796 }
2797 # endif
2798
2799 void warnSysFiles(fileIdList files)
2800 {
2801   fileIdList_elements (files, file)
2802     {
2803       
2804       if (fileTable_isSystemFile (context_fileTable (), file) )
2805         {
2806           if (!context_getFlag( FLG_SYSTEMDIRERRORS ) )
2807             {
2808               voptgenerror (FLG_WARNSYSFILES, message ("Warning %s is a considered a system file. No errors in this file will be reported.", fileTable_rootFileName (file) ), g_currentloc);
2809             }
2810         }
2811     } 
2812   end_fileIdList_elements;
2813 }
This page took 0.262114 seconds and 5 git commands to generate.