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