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