CCCC - C and C++ Code Counter  9999-git
CCCC Development version (post-3.1.4)
ccccmain.cc
Go to the documentation of this file.
1 // ccccmain.cc
2 // command line interface implementation for the cccc project
3 
4 
5 
6 #include "cccc.h"
7 
8 #include "cccc_ver.h"
9 
10 #include <fstream>
11 #include <list>
12 #include <iterator>
13 
14 #ifdef _WIN32
15 #include <direct.h>
16 #include <io.h>
17 #define HANDLE intptr_t
18 #define INVALID_HANDLE_VALUE -1
19 #else
20 #include <sys/stat.h>
21 #endif
22 
23 #include "cccc_itm.h"
24 #include "cccc_opt.h"
25 #include "cccc_met.h"
26 #include "cccc_db.h"
27 #include "cccc_utl.h"
28 #include "cccc_htm.h"
29 #include "cccc_xml.h"
30 
31 // support for languages is now a compile-time option
32 #ifdef CC_INCLUDED
33 #include "CParser.h"
34 #include "CLexer.h"
35 #endif
36 
37 #ifdef JAVA_INCLUDED
38 #include "JParser.h"
39 #include "JLexer.h"
40 #endif
41 
42 #ifdef ADA_INCLUDED
43 #include "AdaPrser.h"
44 #include "ALexer.h"
45 #endif
46 
47 #define NEW_PAGE "\f\n"
48 
50 int DebugMask=0;
51 int dont_free=0;
52 
54 
55 #if defined(_WIN32) && defined(__GNUG__)
56 // Cygnus gcc for Win32 B19 will try to expand wildcards that are given at
57 // the commandline. Sadly this "globbing" will not work the way it is
58 // supposed in Win32, but luckily the whole globbing can be disabled by
59 // defining the following variable:
60 int _CRT_glob = 0;
61 #endif
62 
63 
64 /*
65 ** global variables to hold default values for various things
66 */
68 
69 // class Main encapsulates the top level of control for the program
70 // including command line handling
71 class Main
72 {
73  // most of the data members of this class are either set
74  // by default or gleaned from the command line
75 
76 // each of the data members of type string or int can be
77 // set by a command line flag of the type --<member name>=<value>
78 
79  string outdir;
80  string db_infile;
81  string db_outfile;
82  string opt_infile;
83  string opt_outfile;
84  string html_outfile;
85  string xml_outfile;
86  string lang;
90 
91  // As we gather up the list of files to be processed
92  // we work out and record the appropriate language to
93  // use for each.
94  typedef std::pair<string,string> file_entry;
95  std::list<file_entry> file_list;
96 
97 // this function encapsulates adding an argument to the file_list
98 // for the time being, on Win32 only, it also performs filename globbing
99  void AddFileArgument(const string&);
100 
101 public:
102 
103  Main();
104  void HandleArgs(int argc, char**argv);
105  void HandleDebugOption(const string&);
106  void HandleReportOption(const string&);
107  void PrintCredits(ostream& os);
108  void PrintUsage(ostream& os);
109  int ParseFiles();
110  int DumpDatabase();
111  int LoadDatabase();
112  void GenerateHtml();
113  void GenerateXml();
114  void DescribeOutput();
115  int filesParsed();
116 
117  friend int main(int argc, char** argv);
118 };
119 
120 Main *app=NULL;
121 
123 {
124  report_mask=0xFFFF&(~(rtPROC2|rtSTRUCT2)) ;
125  debug_mask=0;
126  files_parsed=0;
127 }
128 
129 void Main::HandleArgs(int argc, char **argv)
130 {
131  bool accepting_options=true;
132 
133  for(int i=1; i<argc; i++)
134  {
135  string next_arg=argv[i];
136  if(
137  (accepting_options==false) ||
138  (next_arg.substr(0,2)!="--")
139  )
140  {
141  // normally this will be a single file name, but
142  // the function below also encapsulates handling of
143  // globbing (only required under Win32) and
144  // the conventional interpretation of '-' to mean
145  // read a list of files from standard input
146  AddFileArgument(next_arg);
147  }
148  else if(next_arg=="--")
149  {
150  // we support the conventional use of a bare -- to turn
151  // off option processing and allow filenames that look like
152  // options to be accepted
153  accepting_options=false;
154  }
155  else if(next_arg=="--help")
156  {
157  PrintUsage(cout);
158  exit(1);
159  }
160  else
161  {
162  // the options below this point are all of the form --opt=val,
163  // so we parse the argument to find the assignment
164  unsigned int assignment_pos=next_arg.find("=");
165  if(assignment_pos==string::npos)
166  {
167  cerr << "Unexpected option " << next_arg << endl;
168  PrintUsage(cerr);
169  exit(2);
170  }
171  else
172  {
173  string next_opt=next_arg.substr(0,assignment_pos);
174  string next_val=next_arg.substr(assignment_pos+1);
175 
176  if(next_opt=="--outdir")
177  {
178  outdir=next_val;
179  }
180  else if(next_opt=="--db_infile")
181  {
182  db_infile=next_val;
183  }
184  else if(next_opt=="--db_outfile")
185  {
186  db_outfile=next_val;
187  }
188  else if(next_opt=="--opt_infile")
189  {
190  opt_infile=next_val;
191  }
192  else if(next_opt=="--opt_outfile")
193  {
194  opt_outfile=next_val;
195  }
196  else if(next_opt=="--html_outfile")
197  {
198  html_outfile=next_val;
199  }
200  else if(next_opt=="--xml_outfile")
201  {
202  xml_outfile=next_val;
203  }
204 
205  else if(next_opt=="--lang")
206  {
207  lang=next_val;
208  }
209  else if(next_opt=="--report_mask")
210  {
211  // The report option may either be an integer flag vector
212  // in numeric format (including hex) or may be a string of
213  // characters (see HandleReportOption for what they mean).
214  HandleReportOption(next_val.c_str());
215  }
216  else if(next_opt=="--debug_mask")
217  {
218  // The report option may either be an integer flag vector
219  // in numeric format (including hex) or may be a string of
220  // characters (see HandleDebugOption for what they mean).
221  HandleDebugOption(next_val.c_str());
222  }
223  else
224  {
225  cerr << "Unexpected option " << next_opt << endl;
226  PrintUsage(cerr);
227  exit(3);
228  }
229  }
230  }
231  }
232 
233  // we fill in defaults for things which have not been set
234  if(outdir=="")
235  {
236  outdir=".cccc";
237  }
238  if(db_outfile=="")
239  {
240  db_outfile=outdir+"/cccc.db";
241  }
242  if(html_outfile=="")
243  {
244  html_outfile=outdir+"/cccc.html";
245  }
246  if(xml_outfile=="")
247  {
248  xml_outfile=outdir+"/cccc.xml";
249  }
250  if(opt_outfile=="")
251  {
252  opt_outfile=outdir+"/cccc.opt";
253  }
254 
255  // the other strings all default to empty values
256 
257 
258  if(opt_infile=="")
259  {
261 
262  // save the options so that they can be edited
264  }
265  else
266  {
268  }
269 }
270 
271 void Main::AddFileArgument(const string& file_arg)
272 {
273  if(file_arg=="-")
274  {
275  /*
276  ** add files listed on standard input to the list of files
277  ** to be processed
278  */
279  while(!std::cin.eof())
280  {
281  string filename;
282  std::cin >> filename;
283  file_entry file_entry(filename,lang);
284  file_list.push_back(file_entry);
285  }
286  }
287  else
288  {
289 
290 #ifdef _WIN32
291  /*
292  ** In Win32 we will have to expand all wildcards ourself, because the
293  ** shell won't do this for us.
294  ** This code reworked for 3.1.1 because we are now using the Visual C++ 2003
295  ** Toolkit, which does not provide the same APIs as Visual Studio 5/6.
296  */
297  _finddata_t fd;
298  HANDLE sh = _findfirst(file_arg.c_str(), &fd);
299 
300  // 3.pre40
301  // I discovered (by behaviour, not documentation) that
302  // the structure returned by FindFirstFile etc. only includes
303  // the final filename component, even if the search pattern
304  // included a directory.
305  // This is going to be a bugger to fix...
306  string directoryPrefix;
307  size_t directoryPrefixLength = file_arg.find_last_of("/\\");
308  if(directoryPrefixLength!=string::npos)
309  {
310  directoryPrefix = string(file_arg,0,directoryPrefixLength+1);
311  }
312  // Was it as easy as that?...
313 
314  int findnextReturnValue = 0;
315  while (findnextReturnValue==0)
316  {
317  string sFileName=directoryPrefix;
318  sFileName.append(fd.name);
319  file_entry file_entry(sFileName,lang);
320  file_list.push_back(file_entry);
321  findnextReturnValue = _findnext(sh, &fd);
322  }
323  _findclose(sh);
324 #else
325  file_entry file_entry(file_arg,lang);
326  file_list.push_back(file_entry);
327  cout << file_arg << endl;
328 #endif
329  }
330 }
331 
332 
333 /*
334 ** method to parse all of the supplied list of files
335 */
337 {
338  FILE *f;
339 
340  std::list<file_entry>::iterator file_iterator=file_list.begin();
341  while(file_iterator!=file_list.end())
342  {
343  const file_entry &entry=*file_iterator;
344 
345  string filename=entry.first;
346  string file_language=entry.second;
347  ParseStore ps(filename);
348 
349  // The following objects are used to assist in the parsing
350  // process.
351 
352  if(file_language.size()==0)
353  {
354  file_language=CCCC_Options::getFileLanguage(filename);
355  }
356 
357  // CCCC supports a convention that the language may include an
358  // embedded '.', in which case the part before the . controls
359  // which parser runs, while the whole can be examined inside
360  // the parser to check for special dialect handling.
361  unsigned int period_pos=file_language.find(".");
362  string base_language=file_language.substr(0,period_pos);
363 
364  f=fopen(filename.c_str(),"r");
365  if( f == NULL )
366  {
367  cerr << "Couldn't open " << filename << endl;
368  } else {
369  DLGFileInput in(f);
370 
371  // show progress
372  cerr << "Processing " << filename;
373 
374  // The first case is just to allow symetric handling
375  // of the optional inclusion of support for each language
376  if(0)
377  {
378  }
379 #ifdef CC_INCLUDED
380  else if(
381  (base_language=="c++") ||
382  (base_language=="c")
383  )
384  {
385  cerr << " as C/C++ (" << file_language << ")"
386  << endl;
387 
388  CLexer theLexer(&in);
389  ANTLRTokenBuffer thePipe(&theLexer);
390  theLexer.setToken(&currentLexerToken);
391  CParser theParser(&thePipe);
392  ParseUtility pu(&theParser);
393 
394  theParser.init(filename,file_language);
395 
396  // This function turns of the annoying "guess failed" messages
397  // every time a syntactic predicate fails.
398  // This message is enabled by default when PCCTS is run with
399  // tracing turned on (as it is by default in this application).
400  // In the current case this is inappropriate as the C++ parser
401  // uses guessing heavily to break ambiguities, and we expect
402  // large numbers of guesses to be tested and to fail.
403  // This message and the flag which gates it were added around
404  // PCCTS 1.33 MR10.
405  // If you are building with an earlier version, this line should
406  // cause an error and can safely be commented out.
407  theParser.traceGuessOption(-1);
408 
409  theParser.start();
410  files_parsed++;
411  }
412 #endif // CC_INCLUDED
413 #ifdef JAVA_INCLUDED
414  else if(base_language=="java")
415  {
416  cerr << " as Java" << endl;
417 
418  JLexer theLexer(&in);
419  ANTLRTokenBuffer thePipe(&theLexer);
420  theLexer.setToken(&currentLexerToken);
421  JParser theParser(&thePipe);
422  theParser.init(filename,file_language);
423  theParser.traceGuessOption(-1);
424  theParser.compilationUnit();
425  files_parsed++;
426  }
427 #endif // JAVA_INCLUDED
428 #ifdef ADA_INCLUDED
429  else if(base_language=="ada")
430  {
431  cerr << " as Ada" << endl;
432 
433  ALexer theLexer(&in);
434  ANTLRTokenBuffer thePipe(&theLexer);
435  theLexer.setToken(&currentLexerToken);
436  AdaPrser theParser(&thePipe);
437  theParser.init(filename,file_language);
438  theParser.traceGuessOption(-1);
439  theParser.goal_symbol();
440  files_parsed++;
441  }
442 #endif // ADA_INCLUDED
443  else if(base_language=="")
444  {
445  cerr << " - no parseable language identified";
446  }
447  else
448  {
449  cerr << "Unexpected language " << base_language.c_str()
450  << " (" << file_language.c_str()
451  << ") for file " << filename.c_str() << endl;
452  }
453 
454  // close the file
455  fclose(f);
456  }
457 
458  file_iterator++;
459  }
460 
461  return 0;
462 }
463 
465 {
466  ofstream outfile(db_outfile.c_str());
467  return prj->ToFile(outfile);
468 }
469 
471 {
472  int retval=0;
473  if(db_infile!="")
474  {
475  ifstream infile(db_infile.c_str());
476  retval=prj->FromFile(infile);
477  }
478  return retval;
479 }
480 
482 {
483  cerr << endl << "Generating HTML reports" << endl;
484 
486 
487 }
488 
490 {
491  cerr << endl << "Generating XML reports" << endl;
492 
494 
495 }
496 
497 void Main::HandleDebugOption(const string& arg)
498 {
499  /*
500  ** arg may either be a number, or a string of letters denoting
501  ** facilities to be debugged.
502  ** the string of letters is the public way - allowing input of a
503  ** number is just there to support quickly adding a category of
504  ** debug messages without having to change this file immediately
505  */
506  DebugMask=atoi(arg.c_str());
507  for (int i=0; arg[i]!='\0'; i++)
508  {
509  switch (arg[i]) {
510  case 'p' :
511  DebugMask |= PARSER;
512  break;
513 
514  case 'l' :
515  DebugMask |= LEXER;
516  break;
517 
518  case 'c' :
519  DebugMask |= COUNTER;
520  break;
521 
522  case 'm' :
523  DebugMask |= MEMORY;
524  break;
525 
526  case 'x' :
527  case 'X' :
528  DebugMask = 0xFF;
529  break;
530  }
531  }
532 }
533 
534 void Main::HandleReportOption(const string& arg) {
535  /*
536  ** arg may either be a number, or a string of letters denoting
537  ** reports to be generated
538  */
539  report_mask=atoi(arg.c_str());
540  for (int i=0; arg[i]!='\0'; i++) {
541  switch (arg[i]) {
542 
543  case 'c':
545  break;
546 
547  case 's' :
549  break;
550 
551  case 'p' :
552  report_mask |= rtPROC1;
553  break;
554 
555  case 'P':
556  report_mask |= rtPROC2;
557  break;
558 
559  case 'r' :
561  break;
562 
563  case 'R' :
565  break;
566 
567  case 'S' :
569  break;
570 
571  case 'o' :
573  break;
574 
575  case 'L' : // for 'listing' as s and S for 'source' are already used
577  break;
578 
579  case 'j' :
580  report_mask |= rtOTHER;
581  break;
582 
583  case 'h' :
584  report_mask |= rtCCCC;
585  break;
586 
587  case 't' :
589  break;
590 
591  default:
592  cerr << "Unexpected report requested:" << arg[i] << endl;
593  PrintUsage(cerr);
594  exit(-1);
595  }
596  }
597 }
598 
599 /*
600 ** giving credit where it is due
601 */
602 void Main::PrintCredits(ostream& os)
603 {
604  // the principal purpose of the constructor is to set up the
605  // two lots of boilerplate text that this class requires
606  string version_string="Version ";
607  version_string.append(CCCC_VERSION_STRING);
608 
609  const char *credit_strings[] =
610  {
611  "CCCC - a code counter for C and C++",
612  "===================================",
613  "",
614  "A program to analyse C and C++ source code and report on",
615  "some simple software metrics",
616  version_string.c_str(),
617  "Copyright Tim Littlefair, 1995, 1996, 1997, 1998, 1999, 2000",
618  "with contributions from Bill McLean, Herman Hueni, Lynn Wilson ",
619  "Peter Bell, Thomas Hieber and Kenneth H. Cox.",
620  "",
621  "The development of this program was heavily dependent on",
622  "the Purdue Compiler Construction Tool Set (PCCTS) ",
623  "by Terence Parr, Will Cohen, Hank Dietz, Russel Quoung,",
624  "Tom Moog and others.",
625  "",
626  "CCCC comes with ABSOLUTELY NO WARRANTY.",
627  "This is free software, and you are welcome to redistribute it",
628  "under certain conditions. See the file COPYING in the source",
629  "code distribution for details.",
630  NULL
631  };
632  const char **string_ptr=credit_strings;
633  while(*string_ptr!=NULL)
634  {
635  os << *string_ptr << endl;
636  string_ptr++;
637  }
638 
639 
640 }
641 
643 {
644  if(files_parsed>0)
645  {
646  // make sure the user knows where the real output went
647  // make sure the user knows where the real output went
648  cerr << endl
649  << "Primary HTML output is in " << html_outfile << endl;
651  {
652  cerr << "Detailed HTML reports on modules and source are in " << outdir << endl;
653  }
654  cerr << "Primary XML output is in " << xml_outfile << endl ;
655  if(report_mask & rtSEPARATE_MODULES)
656  {
657  cerr << "Detailed XML reports on modules are in " << outdir << endl;
658  }
659  cerr << "Database dump is in " << db_outfile << endl << endl;
660  }
661  else
662  {
663  cerr << endl << "No files parsed on this run" << endl << endl;
664  }
665 }
666 
667 /*
668 ** the usage message is printed on cerr if unexpected options are found,
669 ** and on cout if option --help is found.
670 */
671 void Main::PrintUsage(ostream& os)
672 {
673  const char *usage_strings[] =
674  {
675  "Usage: ",
676  "cccc [options] file1.c ... ",
677  "Process files listed on command line.",
678  "If the filenames include '-', read a list of files from standard input.",
679  "Command Line Options: (default arguments/behaviour specified in braces)",
680  "--help * generate this help message",
681  "--outdir=<dname> * directory for generated files {.cccc}",
682  "--html_outfile=<fname> * name of main HTML report {<outdir>/cccc.html}",
683  "--xml_outfile=<fname> * name of main XML report {<outdir>/cccc.xml}",
684  "--db_infile=<fname> * preload internal database from named file",
685  " {empty file}",
686  "--db_outfile=<fname> * save internal database to file {<outdir>/cccc.db}",
687  "--opt_infile=<fname> * load options from named file {hard coded, see below}",
688  "--opt_outfile=<fname> * save options to named file {<outdir>/cccc.opt}",
689  "--lang=<string> * use language specified for files specified ",
690  " after this option (c,c++,ada,java, no default)",
691  "--report_mask=<hex> * control report content ",
692  "--debug_mask=<hex> * control debug output content ",
693  " (refer to ccccmain.cc for mask values)",
694  "Refer to ccccmain.cc for usage of --report_mask and --debug_mask.",
695  "Refer to cccc_opt.cc for hard coded default option values, including default ",
696  "extension/language mapping and metric treatment thresholds.",
697  NULL
698  };
699  const char **string_ptr=usage_strings;
700  while(*string_ptr!=NULL)
701  {
702  os << *string_ptr << endl;
703  string_ptr++;
704  }
705 }
706 
708 {
709  return files_parsed;
710 }
711 
712 int main(int argc, char **argv)
713 {
714  app=new Main;
715  prj=new CCCC_Project;
716 
717  // process command line
718  app->HandleArgs(argc, argv);
719 
720  // If we are still running, acknowledge those who helped
721  app->PrintCredits(cerr);
722 
723  cerr << "Parsing" << endl;
725  app->ParseFiles();
727 
728  if(app->filesParsed()>0)
729  {
730  prj->reindex();
731 #ifdef _WIN32
732  _mkdir(app->outdir.c_str());
733 #else
734  mkdir(app->outdir.c_str(),0777);
735 #endif
736  app->DumpDatabase();
737 
738  // generate html output
739  app->GenerateHtml();
740  app->GenerateXml();
741  }
742 
743  app->DescribeOutput();
744  delete app;
745  delete prj;
746 
747  return 0;
748 }
749 
static void Save_Options(const string &filename)
Definition: cccc_opt.cc:118
void PrintUsage(ostream &os)
Definition: ccccmain.cc:671
#define SKIP_IDENTIFIERS_ARRAY_SIZE
Definition: cccc.h:58
void GenerateHtml()
Definition: ccccmain.cc:481
int dont_free
Definition: ccccmain.cc:51
char * skip_identifiers[SKIP_IDENTIFIERS_ARRAY_SIZE]
Definition: ccccmain.cc:53
int debug_mask
Definition: ccccmain.cc:88
int DebugMask
Definition: ccccmain.cc:50
string html_outfile
Definition: ccccmain.cc:84
CCCC_Project * prj
Definition: ccccmain.cc:49
string db_outfile
Definition: ccccmain.cc:81
Language file_language
Definition: cccc.h:46
static void GenerateReports(CCCC_Project *project, int report_mask, const string &outfile, const string &outdir)
Definition: cccc_htm.cc:55
void GenerateXml()
Definition: ccccmain.cc:489
string db_infile
Definition: ccccmain.cc:80
string xml_outfile
Definition: ccccmain.cc:85
string outdir
Definition: ccccmain.cc:79
string opt_infile
Definition: ccccmain.cc:82
static void Load_Options()
Definition: cccc_opt.cc:182
int FromFile(ifstream &infile)
Definition: cccc_prj.cc:364
string lang
Definition: ccccmain.cc:86
int ParseFiles()
Definition: ccccmain.cc:336
void reindex()
Definition: cccc_prj.cc:151
void HandleReportOption(const string &)
Definition: ccccmain.cc:534
#define CCCC_VERSION_STRING
Definition: cccc_ver.h:3
ANTLRToken currentLexerToken
Definition: cccc_tok.cc:34
static string getFileLanguage(const string &filename)
Definition: cccc_opt.cc:220
Definition: cccc.h:47
friend int main(int argc, char **argv)
Definition: ccccmain.cc:712
static void set_active_project(CCCC_Project *prj)
Definition: cccc_rec.cc:29
static void GenerateReports(CCCC_Project *project, int report_mask, const string &outfile, const string &outdir)
Definition: cccc_xml.cc:147
int ToFile(ofstream &outfile)
Definition: cccc_prj.cc:317
Main * app
Definition: ccccmain.cc:120
int LoadDatabase()
Definition: ccccmain.cc:470
int files_parsed
Definition: ccccmain.cc:89
Definition: ccccmain.cc:71
int report_mask
Definition: ccccmain.cc:87
string parse_language
Definition: ccccmain.cc:67
Definition: cccc.h:45
std::pair< string, string > file_entry
Definition: ccccmain.cc:94
int DumpDatabase()
Definition: ccccmain.cc:464
string current_filename
Definition: ccccmain.cc:67
int main(int argc, char **argv)
Definition: ccccmain.cc:712
Definition: cccc.h:44
std::list< file_entry > file_list
Definition: ccccmain.cc:95
string opt_outfile
Definition: ccccmain.cc:83
void HandleDebugOption(const string &)
Definition: ccccmain.cc:497
string current_rule
Definition: ccccmain.cc:67
void PrintCredits(ostream &os)
Definition: ccccmain.cc:602
void HandleArgs(int argc, char **argv)
Definition: ccccmain.cc:129
Main()
Definition: ccccmain.cc:122
void DescribeOutput()
Definition: ccccmain.cc:642
void AddFileArgument(const string &)
Definition: ccccmain.cc:271
int filesParsed()
Definition: ccccmain.cc:707