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