]> andersk Git - splint.git/blob - src/llmain.c
o Make lltok an abstract type, a pointer to structure instead of a plain
[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   setCodePoint ();  
1191   showHerald (); 
1192   
1193   /*
1194   ** create lists of C and LCL files
1195   */
1196
1197   cstringSList_elements (fl, current)
1198     {
1199       cstring ext = fileLib_getExtension (current);
1200       
1201       if (cstring_isUndefined (ext))
1202         {
1203           /* no extension --- both C and LCL with default extensions */
1204           
1205           addFile (cfiles, message ("%s%s", current, C_EXTENSION));
1206           addFile (lclfiles, message ("%s%s", current, LCL_EXTENSION));
1207         }
1208       else if (cstring_equal (ext, XH_EXTENSION))
1209         {
1210           addXHFile (xfiles, current);
1211         }
1212       else if (cstring_equal (ext, PP_EXTENSION))
1213         {
1214           if (!context_getFlag (FLG_NOPP))
1215             {
1216               voptgenerror 
1217                 (FLG_FILEEXTENSIONS,
1218                  message ("File extension %s used without +nopp flag (will be processed as C source code): %s", 
1219                           ext, current),
1220                  g_currentloc);
1221             }
1222           
1223           addFile (cfiles, cstring_copy (current));
1224         }
1225       else if (cstring_equal (ext, LCL_EXTENSION)) 
1226         {
1227           addFile (lclfiles, cstring_copy (current));
1228         }
1229       else if (fileLib_isCExtension (ext))
1230         {
1231           addFile (cfiles, cstring_copy (current));
1232         }
1233       else if (cstring_equal (ext, MTS_EXTENSION))
1234         {
1235           addLarchPathFile (mtfiles, current);
1236         }
1237       else 
1238         {
1239           voptgenerror 
1240             (FLG_FILEEXTENSIONS,
1241              message ("Unrecognized file extension: %s (assuming %s is C source code)", 
1242                       current, ext),
1243              g_currentloc);
1244           
1245           addFile (cfiles, cstring_copy (current));
1246         }
1247     } end_cstringSList_elements;
1248   
1249   if (showhelp)
1250     {
1251       if (allhelp)
1252         {
1253           showHelp ();
1254         }
1255       fprintf (g_warningstream, "\n");
1256
1257       fileIdList_free (cfiles);
1258       fileIdList_free (xfiles);
1259       fileIdList_free (lclfiles);
1260       
1261       llexit (LLSUCCESS);
1262     }
1263
1264 # ifdef DOANNOTS
1265   initAnnots ();
1266 # endif
1267
1268   inittime = clock ();
1269
1270   context_resetErrors ();
1271   context_clearInCommandLine ();
1272
1273   anylcl = !fileIdList_isEmpty (lclfiles);
1274
1275   if (context_doMerge ())
1276     {
1277       cstring m = context_getMerge ();
1278
1279       displayScanOpen (message ("< loading %s ", m));
1280       loadState (m);
1281       displayScanClose ();
1282
1283       if (!usymtab_existsType (context_getBoolName ()))
1284         {
1285           usymtab_initBool (); 
1286         }
1287     }
1288   else
1289     {
1290       if (!context_getFlag (FLG_NOLIB) && loadStandardState ())
1291         {
1292           ;
1293         }
1294       else
1295         {
1296           ctype_initTable ();
1297         }
1298
1299       /* setup bool type and constants */
1300       usymtab_initBool (); 
1301     }
1302
1303   fileloc_free (g_currentloc);
1304   g_currentloc = fileloc_createBuiltin ();
1305
1306   /*
1307   ** Read metastate files (must happen before loading libraries) 
1308   */
1309
1310   fileIdList_elements (mtfiles, mtfile)
1311     {
1312       context_setFileId (mtfile);
1313       displayScan (message ("processing %s", fileTable_rootFileName (mtfile)));
1314       mtreader_readFile (cstring_copy (fileTable_fileName (mtfile)));
1315     } end_fileIdList_elements;
1316
1317   libtime = clock ();
1318
1319   if (anylcl)
1320     {
1321 # ifdef NOLCL
1322       llfatalerror (cstring_makeLiteral ("This version of Splint does not handle LCL files."));
1323 # else
1324       lslProcess (lclfiles);
1325 # endif
1326     }
1327
1328   usymtab_initGlobalMarker ();
1329
1330   /*
1331   ** pre-processing
1332   **
1333   ** call the pre-preprocessor and /lib/cpp to generate appropriate
1334   ** files
1335   **
1336   */
1337
1338   context_setInCommandLine ();
1339
1340   DPRINTF (("Pass through: %s", cstringSList_unparse (passThroughArgs)));
1341   
1342   cstringSList_elements (passThroughArgs, thisarg) {
1343     handlePassThroughFlag (cstring_toCharsSafe (thisarg));
1344   } end_cstringSList_elements;
1345
1346   cstringSList_free (passThroughArgs);
1347
1348   cleanupMessages ();
1349
1350   DPRINTF (("Initializing cpp reader!"));
1351   cppReader_initialize ();
1352   cppReader_saveDefinitions ();
1353   
1354   context_clearInCommandLine ();
1355
1356   if (!context_getFlag (FLG_NOPP))
1357     {
1358       fileIdList tfiles;
1359
1360       llflush ();
1361
1362       displayScanOpen (cstring_makeLiteral ("preprocessing"));
1363       
1364       lcltime = clock ();
1365
1366       context_setPreprocessing ();
1367       dercfiles = preprocessFiles (xfiles, TRUE);
1368       tfiles = preprocessFiles (cfiles, FALSE);
1369       warnSysFiles(cfiles);
1370       dercfiles = fileIdList_append (dercfiles, tfiles);
1371       fileIdList_free (tfiles);
1372
1373       context_clearPreprocessing ();
1374
1375       fileIdList_free (cfiles);
1376
1377       displayScanClose ();
1378       pptime = clock ();
1379     }
1380   else
1381     {
1382       lcltime = clock ();
1383       dercfiles = fileIdList_append (cfiles, xfiles);
1384       pptime = clock ();
1385     }
1386
1387   /*
1388   ** now, check all the corresponding C files
1389   **
1390   ** (for now these are just <file>.c, but after pre-processing
1391   **  will be <tmpprefix>.<file>.c)
1392   */
1393
1394   {
1395 # ifdef WIN32
1396     int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
1397
1398     if (nfiles != 0) 
1399       {
1400         llbug (message ("Files unclosed: %d", nfiles));
1401       }
1402 # endif
1403   }
1404
1405   DPRINTF (("Initializing..."));
1406
1407   exprNode_initMod ();
1408
1409   DPRINTF (("Okay..."));
1410
1411   fileIdList_elements (dercfiles, fid)
1412     {
1413       sourceFile = inputStream_create (cstring_copy (fileTable_fileName (fid)), C_EXTENSION, TRUE);
1414       context_setFileId (fid);
1415       
1416       /* Open source file  */
1417       
1418       if (inputStream_isUndefined (sourceFile) || (!inputStream_open (sourceFile)))
1419         {
1420           /* previously, this was ignored  ?! */
1421           llbug (message ("Could not open temp file: %s", fileTable_fileName (fid)));
1422         }
1423       else
1424         {
1425           yyin = inputStream_getFile (sourceFile); /*< shared <- only */
1426         
1427           llassert (yyin != NULL);
1428
1429           displayScan (message ("checking %q", osd_outputPath (fileTable_rootFileName (fid))));
1430           
1431           /*
1432           ** Every time, except the first time, through the loop,
1433           ** need to call yyrestart to clean up the parse buffer.
1434           */
1435
1436           if (!first_time)
1437             {
1438               (void) yyrestart (yyin);  
1439             }
1440           else
1441             {
1442               first_time = FALSE;
1443             }
1444           
1445           DPRINTF (("Entering..."));
1446           context_enterFile ();
1447           (void) yyparse ();
1448           context_exitCFile ();
1449                     
1450           (void) inputStream_close (sourceFile);
1451         }      
1452     } end_fileIdList_elements;
1453
1454   cptime = clock ();
1455   
1456   /* process any leftover macros */
1457
1458   context_processAllMacros ();
1459   
1460   /* check everything that was specified was defined */
1461   
1462   /* don't check if no c files were processed ?
1463   **   is this correct behaviour?
1464   */
1465   
1466   displayScan (cstring_makeLiteral ("global checks"));
1467
1468   cleanupMessages ();
1469   
1470   if (context_getLinesProcessed () > 0)
1471     {
1472       usymtab_allDefined ();
1473     }
1474
1475   if (context_maybeSet (FLG_TOPUNUSED))
1476     {
1477       uentry ue = usymtab_lookupSafe (cstring_makeLiteralTemp ("main"));
1478
1479       if (uentry_isValid (ue))
1480         {
1481           uentry_setUsed (ue, fileloc_observeBuiltin ());
1482         }
1483
1484       usymtab_allUsed ();
1485     }
1486
1487   if (context_maybeSet (FLG_EXPORTLOCAL))
1488     {
1489       usymtab_exportLocal ();
1490     }
1491
1492   
1493   if (context_maybeSet (FLG_EXPORTHEADER))
1494     {
1495       usymtab_exportHeader ();
1496     }
1497
1498   if (context_getFlag (FLG_SHOWUSES))
1499     {
1500       usymtab_displayAllUses ();
1501     }
1502
1503   context_checkSuppressCounts ();
1504
1505   if (context_doDump ())
1506     {
1507       cstring dump = context_getDump ();
1508
1509       dumpState (dump);
1510     }
1511
1512 # ifdef DOANNOTS
1513   printAnnots ();
1514 # endif
1515
1516   cleanupFiles ();
1517
1518   if (context_getFlag (FLG_SHOWSUMMARY))
1519     {
1520       summarizeErrors (); 
1521     }
1522
1523   
1524   {
1525     bool isQuiet = context_getFlag (FLG_QUIET);
1526     cstring specErrors = cstring_undefined;
1527 # ifndef NOLCL
1528     int nspecErrors = lclNumberErrors ();
1529 # endif
1530     
1531     expsuccess = TRUE;
1532
1533     if (context_neednl ())
1534       fprintf (g_warningstream, "\n");
1535     
1536 # ifndef NOLCL
1537     if (nspecErrors > 0)
1538       {
1539         if (nspecErrors == context_getLCLExpect ())
1540           {
1541             specErrors = 
1542               message ("%d spec warning%&, as expected\n       ", 
1543                        nspecErrors);
1544           }
1545         else
1546           {
1547             if (context_getLCLExpect () > 0)
1548               {
1549                 specErrors = 
1550                   message ("%d spec warning%&, expected %d\n       ", 
1551                            nspecErrors,
1552                            (int) context_getLCLExpect ());
1553               }
1554             else
1555               {
1556                 specErrors = message ("%d spec warning%&\n       ",
1557                                       nspecErrors);
1558                 expsuccess = FALSE;
1559               }
1560           }
1561       }
1562     else
1563         {
1564           if (context_getLCLExpect () > 0)
1565             {
1566               specErrors = message ("No spec warnings, expected %d\n       ", 
1567                                     (int) context_getLCLExpect ());
1568               expsuccess = FALSE;
1569             }
1570         }
1571 # endif
1572
1573       if (context_anyErrors ())
1574         {
1575           if (context_numErrors () == context_getExpect ())
1576             {
1577               if (!isQuiet) {
1578                 llmsg (message ("Finished checking --- "
1579                                 "%s%d code warning%&, as expected",
1580                                 specErrors, context_numErrors ()));
1581               }
1582             }
1583           else
1584             {
1585               if (context_getExpect () > 0)
1586                 {
1587                   if (!isQuiet) {
1588                     llmsg (message 
1589                            ("Finished checking --- "
1590                             "%s%d code warning%&, expected %d",
1591                             specErrors, context_numErrors (), 
1592                             (int) context_getExpect ()));
1593                   }
1594
1595                   expsuccess = FALSE;
1596                 }
1597               else
1598                 {
1599                   
1600                   if (!isQuiet)
1601                     {
1602                       llmsg (message ("Finished checking --- "
1603                                       "%s%d code warning%&", 
1604                                       specErrors, context_numErrors ()));
1605                     }
1606
1607                   expsuccess = FALSE;
1608                 }
1609             }
1610         }
1611       else
1612         {
1613           if (context_getExpect () > 0)
1614             {
1615               if (!isQuiet) {
1616                 llmsg (message
1617                        ("Finished checking --- "
1618                         "%sno code warnings, expected %d", 
1619                         specErrors,
1620                         (int) context_getExpect ()));
1621               }
1622
1623               expsuccess = FALSE;
1624             }
1625           else
1626             {
1627               if (context_getLinesProcessed () > 0)
1628                 {
1629                   if (cstring_isEmpty (specErrors))
1630                     {
1631                       if (!isQuiet) 
1632                         {
1633                           llmsg (message ("Finished checking --- no warnings"));
1634                         } 
1635                     }
1636                   else
1637                     {
1638                       if (!isQuiet) 
1639                         {
1640                           llmsg (message ("Finished checking --- %sno code warnings",
1641                                           specErrors));
1642                         }
1643                     }
1644                 }
1645               else
1646                 {
1647                   if (!isQuiet) {
1648                     llmsg (message ("Finished checking --- %sno code processed", 
1649                                     specErrors));
1650                   }
1651                 }
1652             }
1653         }
1654
1655       cstring_free (specErrors);
1656   }
1657   
1658   if (context_getFlag (FLG_STATS))
1659     {
1660       clock_t ttime = clock () - before;
1661       int specLines = context_getSpecLinesProcessed ();
1662       
1663       rstime = clock ();
1664       
1665       if (specLines > 0)
1666         {
1667           fprintf (g_warningstream, "%d spec, ", specLines);
1668         }
1669       
1670 # ifndef CLOCKS_PER_SEC
1671       fprintf (g_warningstream, "%d source lines in %ld time steps (steps/sec unknown)\n", 
1672                context_getLinesProcessed (), 
1673                (long) ttime);
1674 # else
1675       fprintf (g_warningstream, "%d source lines in %.2f s.\n", 
1676                context_getLinesProcessed (), 
1677                (double) ttime / CLOCKS_PER_SEC);
1678 # endif
1679     }
1680   else
1681     {
1682       rstime = clock ();
1683     }
1684   
1685   if (context_getFlag (FLG_TIMEDIST))
1686     {
1687       clock_t ttime = clock () - before;
1688       
1689       if (ttime > 0)
1690         {
1691           char *msg = (char *) dmalloc (256 * sizeof (*msg));
1692           
1693           if (anylcl)
1694             {
1695               sprintf (msg, 
1696                        "Time distribution (percent): initialize %.2f / lcl %.2f / "
1697                        "pre-process %.2f / c check %.2f / finalize %.2f \n", 
1698                        (100.0 * (double) (libtime - before) / ttime),
1699                        (100.0 * (double) (lcltime - libtime) / ttime),
1700                        (100.0 * (double) (pptime - lcltime) / ttime),
1701                        (100.0 * (double) (cptime - pptime) / ttime),
1702                        (100.0 * (double) (rstime - cptime) / ttime));
1703             }
1704           else
1705             {
1706               sprintf (msg, 
1707                        "Time distribution (percent): initialize %.2f / "
1708                        "pre-process %.2f / c check %.2f / finalize %.2f \n", 
1709                        (100.0 * (double) (libtime - before) / ttime),
1710                        (100.0 * (double) (pptime - libtime) / ttime),
1711                        (100.0 * (double) (cptime - pptime) / ttime),
1712                        (100.0 * (double) (rstime - cptime) / ttime));
1713             }
1714           
1715           llgenindentmsgnoloc (cstring_fromCharsO (msg));
1716         }
1717     }
1718
1719   llexit (expsuccess ? LLSUCCESS : LLFAILURE);
1720   BADBRANCHRET (LLFAILURE);
1721 }
1722
1723 # ifdef WIN32
1724 /*
1725 ** Reenable return value warnings.
1726 */
1727 # pragma warning (default:4035)
1728 # endif 
1729
1730 void
1731 showHelp (void)
1732 {
1733   showHerald ();
1734   
1735   llmsg (message ("Source files are .c, .h and %s files.  If there is no suffix,",
1736                   LCL_EXTENSION));
1737   llmsg (message ("   Splint will look for <file>.c and <file>%s.", LCL_EXTENSION));
1738   llmsglit ("");
1739   llmsglit ("Use splint -help <topic or flag name> for more information");
1740   llmsglit ("");
1741   llmsglit ("Topics:");
1742   llmsglit ("");
1743   llmsglit ("   annotations (describes source-code annotations)");
1744   llmsglit ("   comments (describes control comments)");
1745   llmsglit ("   flags (describes flag categories)");
1746   llmsglit ("   flags <category> (describes flags in category)");
1747   llmsglit ("   flags all (short description of all flags)");
1748   llmsglit ("   flags alpha (list all flags alphabetically)");
1749   llmsglit ("   flags full (full description of all flags)");
1750   llmsglit ("   mail (information on mailing lists)");
1751   llmsglit ("   modes (show mode settings)");
1752   llmsglit ("   parseerrors (help on handling parser errors)");
1753   llmsglit ("   prefixcodes (character codes in namespace prefixes)");
1754   llmsglit ("   references (sources for more information)");
1755   llmsglit ("   vars (environment variables)"); 
1756   llmsglit ("   version (information on compilation, maintainer)");
1757   llmsglit ("");
1758 }
1759
1760 static bool
1761 specialFlagsHelp (char *next)
1762 {
1763   if ((next != NULL) && (*next != '-') && (*next != '+'))
1764     {
1765       if (mstring_equal (next, "alpha"))
1766         {
1767           printAlphaFlags ();
1768           return TRUE;
1769         }
1770       else if (mstring_equal (next, "all"))
1771         {
1772           printAllFlags (TRUE, FALSE);
1773           return TRUE;
1774         }
1775       else if (mstring_equal (next, "categories")
1776                || mstring_equal (next, "cats"))
1777         {
1778           listAllCategories ();
1779           return TRUE;
1780         }
1781       else if (mstring_equal (next, "full"))
1782         {
1783           printAllFlags (FALSE, TRUE);
1784           return TRUE;
1785         }
1786       else if (mstring_equal (next, "manual"))
1787         {
1788           printFlagManual (FALSE);
1789           return TRUE;
1790         }
1791       else if (mstring_equal (next, "webmanual"))
1792         {
1793           printFlagManual (TRUE);
1794           return TRUE;
1795         }
1796       else
1797         {
1798           return FALSE;
1799         }
1800     }
1801   else
1802     {
1803       return FALSE;
1804     }
1805 }
1806
1807 void
1808 printParseErrors (void)
1809 {
1810   llmsglit ("Parse Errors");
1811   llmsglit ("------------");
1812   llmsglit ("");
1813   llmsglit ("Splint will sometimes encounter a parse error for code that "
1814             "can be parsed with a local compiler. There are a few likely "
1815             "causes for this and a number of techniques that can be used "
1816             "to work around the problem.");
1817   llmsglit ("");
1818   llmsglit ("Compiler extensions --- compilers sometimes extend the C "
1819             "language with compiler-specific keywords and syntax. While "
1820             "it is not advisible to use these, oftentimes one has no choice "
1821             "when the system header files use compiler extensions. ");
1822   llmsglit ("");
1823   llmsglit ("Splint supports some of the GNU (gcc) compiler extensions, "
1824             "if the +gnuextensions flag is set. You may be able to workaround "
1825             "other compiler extensions by using a pre-processor define. "
1826             "Alternately, you can surround the unparseable code with");
1827   llmsglit ("");
1828   llmsglit ("   # ifndef S_SPLINT_S");
1829   llmsglit ("   ...");
1830   llmsglit ("   # endif");
1831   llmsglit ("");
1832   /* evans 2000-12-21 fixed typo reported by Jeroen Ruigrok/Asmodai */
1833   llmsglit ("Missing type definitions --- an undefined type name will usually "
1834             "lead to a parse error. This often occurs when a standard header "
1835             "file defines some type that is not part of the standard library. ");
1836   llmsglit ("By default, Splint does not process the local files corresponding "
1837             "to standard library headers, but uses a library specification "
1838             "instead so dependencies on local system headers can be detected. "
1839             "If another system header file that does not correspond to a "
1840             "standard library header uses one of these superfluous types, "
1841             "a parse error will result.");
1842   llmsglit ("");
1843   llmsglit ("If the parse error is inside a posix standard header file, the "
1844             "first thing to try is +posixlib. This makes Splint use "
1845             "the posix library specification instead of reading the posix "
1846             "header files.");
1847   llmsglit ("");
1848   llmsglit ("Otherwise, you may need to either manually define the problematic "
1849             "type (e.g., add -Dmlink_t=int to your .splintrc file) or force "
1850             "splint to process the header file that defines it. This is done "
1851             "by setting -skipisoheaders or -skipposixheaders before "
1852             "the file that defines the type is #include'd.");
1853   llmsglit ("(See splint -help "
1854             "skipisoheaders and splint -help skipposixheaders for a list of "
1855             "standard headers.)  For example, if <sys/local.h> uses a type "
1856             "defined by posix header <sys/types.h> but not defined by the "
1857             "posix library, we might do: ");
1858   llmsglit ("");
1859   llmsglit ("   /*@-skipposixheaders@*/");
1860   llmsglit ("   # include <sys/types.h>");
1861   llmsglit ("   /*@=skipposixheaders@*/");
1862   llmsglit ("   # include <sys/local.h>");
1863   llmsglit ("");
1864   llmsglit ("to force Splint to process <sys/types.h>.");
1865   llmsglit ("");
1866   llmsglit ("At last resort, +trytorecover can be used to make Splint attempt "
1867             "to continue after a parse error.  This is usually not successful "
1868             "and the author does not consider assertion failures when +trytorecover "
1869             "is used to be bugs.");
1870 }
1871
1872 void
1873 printAnnotations (void)
1874 {
1875   llmsglit ("Annotations");
1876   llmsglit ("-----------");
1877   llmsglit ("");
1878   llmsglit ("Annotations are semantic comments that document certain "
1879             "assumptions about functions, variables, parameters, and types. ");
1880   llmsglit ("");
1881   llmsglit ("They may be used to indicate where the representation of a "
1882             "user-defined type is hidden, to limit where a global variable may "
1883             "be used or modified, to constrain what a function implementation "
1884             "may do to its parameters, and to express checked assumptions about "
1885             "variables, types, structure fields, function parameters, and "
1886             "function results.");
1887   llmsglit ("");
1888   llmsglit ("Annotations are introduced by \"/*@\". The role of the @ may be "
1889             "played by any printable character, selected using -commentchar <char>.");
1890   llmsglit ("");
1891   llmsglit ("Consult the User's Guide for descriptions of checking associated with each annotation.");
1892   llmsglit ("");
1893   llmsglit ("Globals: (in function declarations)");
1894   llmsglit ("   /*@globals <globitem>,+ @*/");
1895   llmsglit ("      globitem is an identifier, internalState or fileSystem");
1896   llmsglit ("");
1897   llmsglit ("Modifies: (in function declarations)");
1898   llmsglit ("   /*@modifies <moditem>,+ @*/");
1899   llmsglit ("      moditem is an lvalue");
1900   llmsglit ("   /*@modifies nothing @*/");
1901   llmsglit ("   /*@*/   (Abbreviation for no globals and modifies nothing.)");
1902   llmsglit ("");
1903   llmsglit ("Iterators:");
1904   llmsglit ("   /*@iter <identifier> (<parameter-type-list>) @*/ - declare an iterator");
1905   llmsglit ("");
1906   llmsglit ("Constants:");
1907   llmsglit ("   /*@constant <declaration> @*/ - declares a constant");
1908   llmsglit ("");
1909   llmsglit ("Alternate Types:");
1910   llmsglit ("   /*@alt <basic-type>,+ @*/");
1911   llmsglit ("   (e.g., int /*@alt char@*/ is a type matching either int or char)");
1912   llmsglit ("");
1913   llmsglit ("Declarator Annotations");
1914   llmsglit ("");
1915   llmsglit ("Type Definitions:");
1916   llmsglit ("   /*@abstract@*/ - representation is hidden from clients");
1917   llmsglit ("   /*@concrete@*/ - representation is visible to clients");
1918   llmsglit ("   /*@immutable@*/ - instances of the type cannot change value");
1919   llmsglit ("   /*@mutable@*/ - instances of the type can change value");
1920   llmsglit ("   /*@refcounted@*/ - reference counted type");
1921   llmsglit ("");
1922   llmsglit ("Global Variables:");
1923   llmsglit ("   /*@unchecked@*/ - weakest checking for global use");
1924   llmsglit ("   /*@checkmod@*/ - check modification by not use of global");
1925   llmsglit ("   /*@checked@*/ - check use and modification of global");
1926   llmsglit ("   /*@checkedstrict@*/ - check use of global strictly");
1927   llmsglit ("");
1928   llmsglit ("Memory Management:");
1929   llmsglit ("   /*@dependent@*/ - a reference to externally-owned storage");
1930   llmsglit ("   /*@keep@*/ - a parameter that is kept by the called function");
1931   llmsglit ("   /*@killref@*/ - a refcounted parameter, killed by the call");
1932   llmsglit ("   /*@only@*/ - an unshared reference");
1933   llmsglit ("   /*@owned@*/ - owner of storage that may be shared by /*@dependent@*/ references");
1934   llmsglit ("   /*@shared@*/ - shared reference that is never deallocated");
1935   llmsglit ("   /*@temp@*/ - temporary parameter");
1936   llmsglit ("");
1937   llmsglit ("Aliasing:");
1938   llmsglit ("   /*@unique@*/ - may not be aliased by any other visible reference");
1939   llmsglit ("   /*@returned@*/ - may be aliased by the return value");
1940   llmsglit ("");
1941   llmsglit ("Exposure:");
1942   llmsglit ("   /*@observer@*/ - reference that cannot be modified");
1943   llmsglit ("   /*@exposed@*/ - exposed reference to storage in another object");
1944   llmsglit ("");
1945   llmsglit ("Definition State:");
1946   llmsglit ("   /*@out@*/ - storage reachable from reference need not be defined");
1947   llmsglit ("   /*@in@*/ - all storage reachable from reference must be defined");
1948   llmsglit ("   /*@partial@*/ - partially defined, may have undefined fields");
1949   llmsglit ("   /*@reldef@*/ - relax definition checking");
1950   llmsglit ("");
1951   llmsglit ("Global State: (for globals lists, no /*@, since list is already in /*@\'s)");
1952   llmsglit ("   undef - variable is undefined before the call");
1953   llmsglit ("   killed - variable is undefined after the call");
1954   llmsglit ("");
1955   llmsglit ("Null State:");
1956   llmsglit ("   /*@null@*/ - possibly null pointer");
1957   llmsglit ("   /*@notnull@*/ - definitely non-null pointer");
1958   llmsglit ("   /*@relnull@*/ - relax null checking");
1959   llmsglit ("");
1960   llmsglit ("Null Predicates:");
1961   llmsglit ("   /*@nullwhentrue@*/ - if result is TRUE, first parameter is NULL");
1962   llmsglit ("   /*@falsewhennull@*/ - if result is TRUE, first parameter is not NULL");
1963   llmsglit ("");
1964   llmsglit ("Execution:");
1965   llmsglit ("   /*@noreturn@*/ - function never returns");
1966   llmsglit ("   /*@maynotreturn@*/ - function may or may not return");
1967   llmsglit ("   /*@noreturnwhentrue@*/ - function does not return if first parameter is TRUE");
1968   llmsglit ("   /*@noreturnwhenfalse@*/ - function does not return if first parameter if FALSE");
1969   llmsglit ("   /*@alwaysreturns@*/ - function always returns");
1970   llmsglit ("");
1971   llmsglit ("Side-Effects:");
1972   llmsglit ("   /*@sef@*/ - corresponding actual parameter has no side effects");
1973   llmsglit ("");
1974   llmsglit ("Declaration:");
1975   llmsglit ("   /*@unused@*/ - need not be used (no unused errors reported)");
1976   llmsglit ("   /*@external@*/ - defined externally (no undefined error reported)");
1977   llmsglit ("");
1978   llmsglit ("Case:");
1979   llmsglit ("   /*@fallthrough@*/ - fall-through case");
1980   llmsglit ("");
1981   llmsglit ("Break:");
1982   llmsglit ("   /*@innerbreak@*/ - break is breaking an inner loop or switch");
1983   llmsglit ("   /*@loopbreak@*/ - break is breaking a loop");
1984   llmsglit ("   /*@switchbreak@*/ - break is breaking a switch");
1985   llmsglit ("   /*@innercontinue@*/ - continue is continuing an inner loop");
1986   llmsglit ("");
1987   llmsglit ("Unreachable Code:");
1988   llmsglit ("   /*@notreached@*/ - statement may be unreachable.");
1989   llmsglit ("");
1990   llmsglit ("Special Functions:");
1991   llmsglit ("   /*@printflike@*/ - check variable arguments like printf");
1992   llmsglit ("   /*@scanflike@*/ - check variable arguments like scanf");
1993 }
1994
1995 void
1996 printComments (void)
1997 {
1998   llmsglit ("Control Comments");
1999   llmsglit ("----------------");
2000   llmsglit ("");
2001   llmsglit ("Setting Flags");
2002   llmsglit ("");
2003   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.");
2004   llmsglit ("");
2005   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,");
2006   llmsglit ("   /*@+boolint -modifies =showfunc@*/");
2007   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).");
2008   llmsglit ("");
2009   llmsglit ("Error Suppression");
2010   llmsglit ("");
2011   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.");
2012   llmsglit ("");
2013   llmsglit ("/*@ignore@*/ ... /*@end@*/");
2014   llgenindentmsgnoloc
2015     (cstring_makeLiteral 
2016      ("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."));
2017   llmsglit ("/*@i@*/");
2018     llgenindentmsgnoloc
2019     (cstring_makeLiteral 
2020      ("No errors will be reported from an /*@i@*/ comment to the end of the line."));
2021   llmsglit ("/*@i<n>@*/");
2022   llgenindentmsgnoloc
2023     (cstring_makeLiteral 
2024      ("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."));
2025   llmsglit ("/*@t@*/, /*@t<n>@*/");
2026   llgenindentmsgnoloc
2027     (cstring_makeLiteral 
2028      ("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."));
2029   llmsglit ("");
2030   llmsglit ("Type Access");
2031   llmsglit ("");
2032   llmsglit ("/*@access <type>@*/"); 
2033   llmsglit ("   Allows the following code to access the representation of <type>");
2034   llmsglit ("/*@noaccess <type>@*/");
2035   llmsglit ("   Hides the representation of <type>");
2036   llmsglit ("");
2037   llmsglit ("Macro Expansion");
2038   llmsglit ("");
2039   llmsglit ("/*@notfunction@*/");
2040   llgenindentmsgnoloc 
2041     (cstring_makeLiteral
2042      ("Indicates that the next macro definition is not intended to be a "
2043       "function, and should be expanded in line instead of checked as a "
2044       "macro function definition."));
2045 }
2046
2047   
2048 void
2049 printFlags (void)
2050 {
2051   llmsglit ("Flag Categories");
2052   llmsglit ("---------------");
2053   listAllCategories ();
2054   llmsglit ("\nTo see the flags in a flag category, do\n   splint -help flags <category>");
2055   llmsglit ("To see a list of all flags in alphabetical order, do\n   splint -help flags alpha");
2056   llmsglit ("To see a full description of all flags, do\n   splint -help flags full");
2057 }
2058
2059 void
2060 printMaintainer (void)
2061 {
2062   llmsg (message ("Maintainer: %s", cstring_makeLiteralTemp (SPLINT_MAINTAINER)));
2063   llmsglit (LCL_COMPILE);
2064 }
2065
2066 void
2067 printMail (void)
2068 {
2069   llmsglit ("Mailing Lists");
2070   llmsglit ("-------------");
2071   llmsglit ("");
2072   llmsglit ("There are two mailing lists associated with Splint: ");
2073   llmsglit ("");
2074   llmsglit ("   lclint-announce@virginia.edu");
2075   llmsglit ("");
2076   llmsglit ("      Reserved for announcements of new releases and bug fixes.");
2077   llmsglit ("      To subscribe, send a message to majordomo@virginia.edu with body: ");
2078   llmsglit ("           subscribe lclint-announce");
2079   llmsglit ("");
2080   llmsglit ("   lclint-interest@virginia.edu");
2081   llmsglit ("");
2082   llmsglit ("      Informal discussions on the use and development of Splint.");
2083   llmsglit ("      To subscribe, send a message to majordomo@virginia.edu with body: ");
2084   llmsglit ("           subscribe lclint-interest");
2085 }
2086
2087 void
2088 printReferences (void)
2089 {
2090   llmsglit ("References");
2091   llmsglit ("----------");
2092   llmsglit ("");
2093   llmsglit ("For more information, see the Splint web site: http://www.splint.org");
2094 }
2095
2096 void
2097 describePrefixCodes (void)
2098 {
2099   llmsglit ("Prefix Codes");
2100   llmsglit ("------------");
2101   llmsglit ("");
2102   llmsglit ("These characters have special meaning in name prefixes:");
2103   llmsglit ("");
2104   llmsg (message ("   %h  Any uppercase letter [A-Z]", PFX_UPPERCASE));
2105   llmsg (message ("   %h  Any lowercase letter [a-z]", PFX_LOWERCASE));
2106   llmsg (message ("   %h  Any character (valid in a C identifier)", PFX_ANY));
2107   llmsg (message ("   %h  Any digit [0-9]", PFX_DIGIT));
2108   llmsg (message ("   %h  Any non-uppercase letter [a-z0-9_]", PFX_NOTUPPER));
2109   llmsg (message ("   %h  Any non-lowercase letter [A-Z0-9_]", PFX_NOTLOWER));
2110   llmsg (message ("   %h  Any letter [A-Za-z]", PFX_ANYLETTER));
2111   llmsg (message ("   %h  Any letter or digit [A-Za-z0-9]", PFX_ANYLETTERDIGIT));
2112   llmsglit ("   *  Zero or more repetitions of the previous character class until the end of the name");
2113 }
2114
2115 void
2116 describeVars (void)
2117 {
2118   cstring eval;
2119   cstring def;
2120
2121   eval = context_getLarchPath ();
2122   def = osd_getEnvironmentVariable (LARCH_PATH);
2123
2124   if (cstring_isDefined (def) || 
2125       !cstring_equal (eval, cstring_fromChars (DEFAULT_LARCHPATH)))
2126     {
2127       llmsg (message ("LARCH_PATH = %s", eval));
2128     }
2129   else
2130     {
2131       llmsg (message ("LARCH_PATH = <not set> (default = %s)",
2132                       cstring_fromChars (DEFAULT_LARCHPATH)));
2133     }
2134   
2135   llmsglit ("   --- path used to find larch initialization files and LSL traits");
2136
2137   eval = context_getLCLImportDir ();
2138   def = osd_getEnvironmentVariable (cstring_makeLiteralTemp (LCLIMPORTDIR));
2139
2140   if (cstring_isDefined (def) ||
2141       !cstring_equal (eval, cstring_fromChars (DEFAULT_LCLIMPORTDIR)))
2142     {
2143       llmsg (message ("%q = %s", cstring_makeLiteral (LCLIMPORTDIR), eval));
2144     }
2145   else
2146     {
2147       llmsg (message ("%s = <not set, default: %s>", cstring_makeLiteralTemp (LCLIMPORTDIR), 
2148                       cstring_makeLiteralTemp (DEFAULT_LCLIMPORTDIR))); 
2149     }
2150   
2151   llmsglit ("   --- directory containing lcl standard library files "
2152             "(import with < ... >)");;
2153
2154   llmsg (message 
2155          ("include path = %q (set by environment variable %s and -I flags)",
2156           cppReader_getIncludePath (), INCLUDEPATH_VAR));
2157
2158   llmsglit ("   --- path used to find #include'd files");
2159
2160   llmsg (message 
2161          ("systemdirs = %s (set by -systemdirs or environment variable %s)", /*@i413223@*/
2162           context_getString (FLG_SYSTEMDIRS),
2163           INCLUDEPATH_VAR));
2164
2165   llmsglit ("   --- if file is found on this path, it is treated as a system file for error reporting");
2166 }
2167
2168 void
2169 interrupt (int i)
2170 {
2171   switch (i)
2172     {
2173     case SIGINT:
2174       fprintf (g_errorstream, "*** Interrupt\n");
2175       llexit (LLINTERRUPT);
2176     case SIGSEGV:
2177       {
2178         cstring loc;
2179         
2180         /* Cheat when there are parse errors */
2181         checkParseError (); 
2182         
2183         fprintf (g_errorstream, "*** Segmentation Violation\n");
2184         
2185         /* Don't catch it if fileloc_unparse causes a signal */
2186         (void) signal (SIGSEGV, NULL);
2187
2188         loc = fileloc_unparse (g_currentloc);
2189         
2190         fprintf (g_errorstream, "*** Location (not trusted): %s\n", 
2191                  cstring_toCharsSafe (loc));
2192         cstring_free (loc);
2193         printCodePoint ();
2194         fprintf (g_errorstream, "*** Please report bug to %s\n", SPLINT_MAINTAINER);
2195         exit (LLGIVEUP);
2196       }
2197     default:
2198       fprintf (g_errorstream, "*** Signal: %d\n", i);
2199       /*@-mustfree@*/
2200       fprintf (g_errorstream, "*** Location (not trusted): %s\n", 
2201                cstring_toCharsSafe (fileloc_unparse (g_currentloc)));
2202       /*@=mustfree@*/
2203       printCodePoint ();
2204       fprintf (g_errorstream, "*** Please report bug to %s ***\n", SPLINT_MAINTAINER);
2205       exit (LLGIVEUP);
2206     }
2207 }
2208
2209 void
2210 cleanupFiles (void)
2211 {
2212   static bool doneCleanup = FALSE;
2213
2214   /* make sure this is only called once! */
2215
2216   if (doneCleanup) return;
2217
2218   setCodePoint ();
2219
2220   /*
2221   ** Close all open files
2222   **    (There should only be open files, if we exited after a fatal error.)
2223   */
2224
2225   fileTable_closeAll (context_fileTable ());
2226
2227   if (context_getFlag (FLG_KEEP))
2228     {
2229       check (fputs ("Temporary files kept:\n", g_messagestream) != EOF);
2230       fileTable_printTemps (context_fileTable ());
2231     }
2232   else
2233     {
2234 # ifdef WIN32
2235       int nfiles = /*@-unrecog@*/ _fcloseall (); /*@=unrecog@*/
2236       
2237       if (nfiles != 0) 
2238         {
2239           llbug (message ("Files unclosed: %d", nfiles));
2240         }
2241 # endif
2242       fileTable_cleanup (context_fileTable ());
2243     }
2244
2245   doneCleanup = TRUE;
2246 }
2247
2248 /*
2249 ** cleans up temp files (if necessary) and exits
2250 */
2251
2252 /*@noreturn@*/ void
2253 llexit (int status)
2254 {
2255   DPRINTF (("llexit: %d", status));
2256
2257 # ifdef WIN32
2258   if (status == LLFAILURE) 
2259     {
2260       _fcloseall ();
2261     }
2262 # endif
2263
2264   cleanupFiles ();
2265
2266   if (status != LLFAILURE)
2267     {
2268       context_destroyMod ();
2269       exprNode_destroyMod ();
2270       
2271       sRef_destroyMod ();
2272       uentry_destroyMod ();
2273       typeIdSet_destroyMod ();
2274       
2275 # ifdef USEDMALLOC
2276       dmalloc_shutdown ();
2277 # endif
2278     }
2279
2280   exit ((status == LLSUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE);
2281 }
2282
2283 bool readOptionsFile (cstring fname, cstringSList *passThroughArgs, bool report)
2284 {
2285   bool res = FALSE;
2286
2287   if (fileTable_exists (context_fileTable (), fname))
2288     {
2289       if (report)
2290         {
2291           voptgenerror
2292             (FLG_WARNRC, 
2293              message ("Multiple attempts to read options file: %s", fname),
2294              g_currentloc);
2295         }
2296     }
2297   else
2298     {
2299       FILE *innerf = fileTable_openFile (context_fileTable (), fname, "r");
2300       
2301       if (innerf != NULL)
2302         {
2303           fileloc fc = g_currentloc;
2304           g_currentloc = fileloc_createRc (fname);
2305
2306           displayScan (message ("< reading options from %q >", 
2307                                 fileloc_outputFilename (g_currentloc)));
2308           
2309           loadrc (innerf, passThroughArgs);
2310           fileloc_reallyFree (g_currentloc);
2311           g_currentloc = fc;
2312           res = TRUE;
2313         }
2314       else 
2315         {
2316           if (report)
2317             {
2318               voptgenerror
2319                 (FLG_WARNRC, 
2320                  message ("Cannot open options file: %s", fname),
2321                  g_currentloc);
2322             }
2323         }
2324     }
2325
2326   return res;
2327 }
2328
2329 /*
2330 ** This shouldn't be necessary, but Apple Darwin can't handle '"''s.
2331 */
2332
2333 void
2334 loadrc (/*:open:*/ FILE *rcfile, cstringSList *passThroughArgs)
2335    /*@modifies rcfile@*/
2336    /*@ensures closed rcfile@*/
2337 {
2338   char *s = mstring_create (MAX_LINE_LENGTH);
2339   char *os = s;
2340   
2341   DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2342
2343   s = os;
2344
2345   while (reader_readLine (rcfile, s, MAX_LINE_LENGTH) != NULL)
2346     {
2347       char c;
2348       bool set = FALSE;     
2349       char *thisflag;
2350       flagcode opt;
2351
2352       DPRINTF (("Line: %s", s));
2353       DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2354             
2355       while (*s == ' ' || *s == '\t')
2356         {
2357           s++;
2358           incColumn ();
2359         }
2360       
2361       while (*s != '\0')
2362         {
2363           bool escaped = FALSE;
2364           bool quoted = FALSE;
2365           c = *s;
2366
2367           DPRINTF (("Process: %s", s));
2368           DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2369           /* comment characters */
2370           if (c == '#' || c == ';' || c == '\n') 
2371             {
2372               /*@innerbreak@*/
2373               break;
2374             }
2375           
2376           if (c == '-' || c == '+')
2377             {
2378               set = (c == '+');
2379             }
2380           else
2381             {
2382               showHerald ();
2383               voptgenerror (FLG_BADFLAG, 
2384                             message ("Bad flag syntax (+ or - expected, "
2385                                      "+ is assumed): %s", 
2386                                      cstring_fromChars (s)),
2387                             g_currentloc);
2388               s--;
2389               set = TRUE;
2390             }
2391           
2392           s++;
2393           incColumn ();
2394           
2395           thisflag = s;
2396           
2397           while ((c = *s) != '\0')
2398             { /* remember to handle spaces and quotes in -D and -U ... */
2399               if (escaped)
2400                 {
2401                   escaped = FALSE;
2402                 }
2403               else if (quoted)
2404                 {
2405                   if (c == '\\')
2406                     {
2407                       escaped = TRUE;
2408                     }
2409                   else if (c == '\"')
2410                     {
2411                       quoted = FALSE;
2412                     }
2413                   else
2414                     {
2415                       ;
2416                     }
2417                 }
2418               else if (c == '\"')
2419                 {
2420                   quoted = TRUE;
2421                 }
2422               else
2423                 {
2424                  if (c == ' ' || c == '\t' || c == '\n')
2425                    {
2426                      /*@innerbreak@*/ break;
2427                    }
2428                }
2429                   
2430               s++; 
2431               incColumn ();
2432             }
2433
2434           DPRINTF (("Nulling: %c", *s));
2435           *s = '\0';
2436
2437           if (mstring_isEmpty (thisflag))
2438             {
2439               llfatalerror (message ("Missing flag: %s",
2440                                      cstring_fromChars (os)));
2441             }
2442
2443           DPRINTF (("Flag: %s", thisflag));
2444
2445           opt = flags_identifyFlag (cstring_fromChars (thisflag));
2446           
2447           if (flagcode_isSkip (opt))
2448             {
2449               ;
2450             }
2451           else if (flagcode_isInvalid (opt))
2452             {
2453               DPRINTF (("Invalid: %s", thisflag));
2454
2455               if (isMode (cstring_fromChars (thisflag)))
2456                 {
2457                   context_setMode (cstring_fromChars (thisflag));
2458                 }
2459               else
2460                 {
2461                   voptgenerror (FLG_BADFLAG,
2462                                 message ("Unrecognized option: %s", 
2463                                          cstring_fromChars (thisflag)),
2464                                 g_currentloc);
2465                 }
2466             }
2467           else
2468             {
2469               context_userSetFlag (opt, set);
2470
2471               if (flagcode_hasArgument (opt))
2472                 {
2473                   if (opt == FLG_HELP)
2474                     {
2475                       showHerald ();
2476                       voptgenerror (FLG_BADFLAG,
2477                                     message ("Cannot use help in rc files"),
2478                                     g_currentloc);
2479                     }
2480                   else if (flagcode_isPassThrough (opt)) /* -D or -U */
2481                     {
2482                       cstring arg = cstring_fromCharsNew (thisflag);
2483                       cstring_markOwned (arg);
2484                       *passThroughArgs = cstringSList_add (*passThroughArgs, arg);
2485                       DPRINTF (("Pass through: %s",
2486                                 cstringSList_unparse (*passThroughArgs)));
2487                     }
2488                   else if (opt == FLG_INCLUDEPATH 
2489                            || opt == FLG_SPECPATH)
2490                     {
2491                       cstring dir = cstring_suffix (cstring_fromChars (thisflag), 1); /* skip over I/S */
2492                                       
2493                       switch (opt)
2494                         {
2495                         case FLG_INCLUDEPATH:
2496                           cppAddIncludeDir (dir);
2497                           /*@switchbreak@*/ break;
2498                         case FLG_SPECPATH:
2499                           /*@-mustfree@*/
2500                           g_localSpecPath = cstring_toCharsSafe
2501                             (message ("%s:%s", cstring_fromChars (g_localSpecPath), dir));
2502                           /*@=mustfree@*/
2503                           /*@switchbreak@*/ break;
2504                           BADDEFAULT;
2505                         }
2506                     }
2507                   else if (flagcode_hasString (opt)
2508                            || flagcode_hasNumber (opt)
2509                            || flagcode_hasChar (opt)
2510                            || opt == FLG_INIT || opt == FLG_OPTF)
2511                     {
2512                       cstring extra = cstring_undefined;
2513                       char *rest, *orest;
2514                       char rchar;
2515                       
2516                       *s = c;
2517                       rest = mstring_copy (s);
2518                       DPRINTF (("Here: rest = %s", rest));
2519                       orest = rest;
2520                       *s = '\0';
2521                       
2522                       while ((rchar = *rest) != '\0'
2523                              && (isspace ((int) rchar)))
2524                         {
2525                           rest++;
2526                           s++;
2527                         }
2528                       
2529                       DPRINTF (("Yo: %s", rest));
2530
2531                       while ((rchar = *rest) != '\0' 
2532                              && !isspace ((int) rchar))
2533                         {
2534                           extra = cstring_appendChar (extra, rchar);
2535                           rest++; 
2536                           s++;
2537                         }
2538                       
2539                       DPRINTF (("Yo: %s", extra));
2540                       sfree (orest);
2541
2542                       if (cstring_isUndefined (extra))
2543                         {
2544                           showHerald ();
2545                           voptgenerror 
2546                             (FLG_BADFLAG,
2547                              message
2548                              ("Flag %s must be followed by an argument",
2549                               flagcode_unparse (opt)),
2550                              g_currentloc);
2551                         }
2552                       else
2553                         {
2554                           s--;
2555                           
2556                           DPRINTF (("Here we are: %s", extra));
2557
2558                           if (flagcode_hasNumber (opt) || flagcode_hasChar (opt))
2559                             {
2560                               DPRINTF (("Set value flag: %s", extra));
2561                               setValueFlag (opt, extra);
2562                             }
2563                           else if (opt == FLG_OPTF)
2564                             {
2565                               (void) readOptionsFile (extra, passThroughArgs, TRUE);
2566                             }
2567                           else if (opt == FLG_INIT)
2568                             {
2569 # ifndef NOLCL
2570                               llassert (inputStream_isUndefined (initFile));
2571                               
2572                               initFile = inputStream_create 
2573                                 (cstring_copy (extra), 
2574                                  cstring_makeLiteralTemp (LCLINIT_SUFFIX),
2575                                  FALSE);
2576 # endif
2577                             }
2578                           else if (flagcode_hasString (opt))
2579                             {
2580                               DPRINTF (("Here: %s", extra));
2581
2582                               /*
2583                               ** If it has "'s, we need to remove them.
2584                               */
2585
2586                               if (cstring_firstChar (extra) == '\"')
2587                                 {
2588                                   if (cstring_lastChar (extra) == '\"')
2589                                     {
2590                                       cstring unquoted = cstring_copyLength 
2591                                         (cstring_toCharsSafe (cstring_suffix (extra, 1)),
2592                                          cstring_length (extra) - 2);
2593
2594                                       DPRINTF (("string flag: %s -> %s", extra, unquoted));
2595                                       setStringFlag (opt, unquoted);
2596                                       cstring_free (extra);
2597                                     }
2598                                   else
2599                                     {
2600                                       voptgenerror
2601                                         (FLG_BADFLAG, 
2602                                          message ("Unmatched \" in option string: %s", 
2603                                                   extra),
2604                                          g_currentloc);
2605                                       setStringFlag (opt, extra);
2606                                     }
2607                                 }
2608                               else
2609                                 {
2610                                   DPRINTF (("No quotes: %s", extra));
2611                                   setStringFlag (opt, extra);
2612                                 }
2613
2614                               extra = cstring_undefined;
2615                             }
2616                           else
2617                             {
2618                               BADEXIT;
2619                             }
2620                         }
2621
2622                       cstring_free (extra); 
2623                     }
2624                   else
2625                     {
2626                       BADEXIT;
2627                     }
2628                 }
2629             }
2630           
2631           *s = c;
2632           DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2633           while ((c == ' ') || (c == '\t'))
2634             {
2635               c = *(++s);
2636               incColumn ();
2637             } 
2638         }
2639       DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2640       s = os;
2641     }
2642
2643   DPRINTF (("Pass through: %s", cstringSList_unparse (*passThroughArgs)));
2644   sfree (os); 
2645   check (fileTable_closeFile (context_fileTable (), rcfile));
2646 }
2647
2648 static fileIdList preprocessFiles (fileIdList fl, bool xhfiles)
2649   /*@modifies fileSystem@*/
2650 {
2651   bool msg = (context_getFlag (FLG_SHOWSCAN) && fileIdList_size (fl) > 10);
2652   int skip = (fileIdList_size (fl) / 5);
2653   int filesprocessed = 0;
2654   fileIdList dfiles = fileIdList_create ();
2655
2656   fileloc_free (g_currentloc);
2657   g_currentloc = fileloc_createBuiltin ();
2658
2659   fileIdList_elements (fl, fid)
2660     {
2661       cstring ppfname = fileTable_fileName (fid);
2662
2663       if (!(osd_fileIsReadable (ppfname)))
2664         {
2665           lldiagmsg (message ("Cannot open file: %q", osd_outputPath (ppfname)));
2666           ppfname = cstring_undefined;
2667         }
2668
2669       if (cstring_isDefined (ppfname))
2670         {
2671           fileId dfile = fileTable_addCTempFile (context_fileTable (), fid);
2672
2673           if (xhfiles)
2674             {
2675               llassert (fileTable_isXHFile (context_fileTable (), dfile));
2676             }
2677
2678           llassert (cstring_isNonEmpty (ppfname));
2679           
2680           if (msg)
2681             {
2682               if ((filesprocessed % skip) == 0) 
2683                 {
2684                   if (filesprocessed == 0) {
2685                     fprintf (g_messagestream, " ");
2686                   }
2687                   else {
2688                     fprintf (g_messagestream, ".");
2689                   }
2690                   
2691                   (void) fflush (g_messagestream);
2692                 }
2693               filesprocessed++;
2694             }
2695
2696           DPRINTF (("outfile: %s", fileTable_fileName (dfile)));
2697
2698           if (cppProcess (ppfname, fileTable_fileName (dfile)) != 0) 
2699             {
2700               llfatalerror (message ("Preprocessing error for file: %s", 
2701                                      fileTable_rootFileName (fid)));
2702             }
2703           
2704           fileIdList_add (dfiles, dfile);
2705         }
2706     } end_fileIdList_elements; 
2707     
2708     return dfiles;
2709 }
2710
2711 /* This should be in an lclUtils.c file... */
2712 # ifndef NOLCL
2713 char *specFullName (char *specfile, /*@out@*/ char **inpath)
2714 {
2715   /* extract the path and the specname associated with the given file */
2716   char *specname = (char *) dmalloc (sizeof (*specname) 
2717                                      * (strlen (specfile) + 9));
2718   char *ospecname = specname;
2719   char *path = (char *) dmalloc (sizeof (*path) * (strlen (specfile)));
2720   size_t size;
2721   long int i, j;
2722   
2723   /* initialized path to empty string or may have accidental garbage */
2724   *path = '\0';
2725
2726   /*@-mayaliasunique@*/ 
2727   strcpy (specname, specfile);
2728   /*@=mayaliasunique@*/ 
2729
2730   /* trim off pathnames in specfile */
2731   size = strlen (specname);
2732
2733   for (i = size_toInt (size) - 1; i >= 0; i--)
2734     {
2735       if (specname[i] == CONNECTCHAR)
2736         {
2737           /* strcpy (specname, (char *)specname+i+1); */
2738           for (j = 0; j <= i; j++)      /* include '/'  */
2739             {
2740               path[j] = specname[j];
2741             }
2742
2743           path[i + 1] = '\0';
2744           specname += i + 1;
2745           break;
2746         }
2747     }
2748
2749   /* 
2750   ** also remove .lcl file extension, assume it's the last extension
2751   ** of the file name 
2752   */
2753
2754   size = strlen (specname);
2755
2756   for (i = size_toInt (size) - 1; i >= 0; i--)
2757     {
2758       if (specname[i] == '.')
2759         {
2760           specname[i] = '\0';
2761           break;
2762         }
2763     }
2764   
2765   *inpath = path;
2766
2767   /*
2768   ** If specname no longer points to the original char,
2769   ** we need to allocate a new pointer and copy the string.
2770   */
2771
2772   if (specname != ospecname) {
2773     char *rspecname = (char *) dmalloc (sizeof (*rspecname) * (strlen (specname) + 1));
2774     strcpy (rspecname, specname); /* evs 2000-05-16: Bug: was ospecname! */
2775     sfree (ospecname);
2776     return rspecname;
2777   } 
2778
2779   return specname;
2780 }
2781 # endif
2782
2783 void warnSysFiles(fileIdList files)
2784 {
2785   fileIdList_elements (files, file)
2786     {
2787       
2788       if (fileTable_isSystemFile (context_fileTable (), file) )
2789         {
2790           if (!context_getFlag( FLG_SYSTEMDIRERRORS ) )
2791             {
2792               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);
2793             }
2794         }
2795     } 
2796   end_fileIdList_elements;
2797 }
This page took 0.262337 seconds and 5 git commands to generate.