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