1 //
2 // Copyright (C) 2006-2007 Maciej Sobczak
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7 
8 #include "config.h"
9 #include "structures/SourceFiles.h"
10 #include "plugins/Profiles.h"
11 #include "plugins/Rules.h"
12 #include "plugins/Exclusions.h"
13 #include "plugins/Transformations.h"
14 #include "plugins/Parameters.h"
15 #include "plugins/Reports.h"
16 #include "plugins/RootDirectory.h"
17 #include <iostream>
18 #include <fstream>
19 #include <string>
20 #include <vector>
21 #include <cstdlib>
22 #include <sys/stat.h>
23 #include <cstring>
24 #include <cerrno>
25 #include <boost/program_options.hpp>
26 #include <boost/foreach.hpp>
27 #include "get_vera_root_default.h"
28 
29 #define foreach BOOST_FOREACH
30 
31 #ifdef _MSC_VER
32 // vm.count() used as a bool
33 #pragma warning(disable:4800)
34 #endif
35 
36 template<typename Files, typename Options, typename Reporter>
doReports(Files & reports,Options & vm,Reporter * reporter)37 void doReports(Files & reports, Options & vm, Reporter * reporter)
38 {
39     foreach (const std::string & fn, reports)
40     {
41         if (fn == "-")
42         {
43             if (vm.count("warning") || vm.count("error"))
44             {
45                 (*reporter)(std::cerr, vm.count("no-duplicate"));
46             }
47             else
48             {
49               (*reporter)(std::cout, vm.count("no-duplicate"));
50             }
51         }
52         else
53         {
54             std::ofstream file(fn.c_str());
55             if (file.is_open() == false)
56             {
57                 throw std::runtime_error(
58                     "Cannot open " + fn + ": " + strerror(errno));
59             }
60             (*reporter)(file, vm.count("no-duplicate"));
61             if (file.bad())
62             {
63                 throw std::runtime_error(
64                     "Cannot write to " + fn + ": " + strerror(errno));
65             }
66             file.close();
67         }
68     }
69 
70 }
71 
boost_main(int argc,char * argv[])72 int boost_main(int argc, char * argv[])
73 {
74     // The directory containing the profile and rule definitions
75     // by default it is (in this order, first has highest precedence):
76     // - VERA_ROOT (if VERA_ROOT is defined)
77     // - HOME/.vera++ (if HOME is defined)
78     // - current directory (if scripts and profile are present)
79     // - /usr/lib/vera++/ default debian directory
80 
81     Vera::Plugins::RootDirectory::DirectoryName veraRoot(get_vera_root_default(argv[0]));
82 
83     struct stat St;
84     bool isInCurrent = ( (stat( "./scripts", &St ) == 0 || stat( "./rules", &St ) == 0
85                              || stat( "./transforms", &St ) == 0)
86                          && stat( "./profiles", &St ) == 0 );
87 
88     // scripts and profiles folders are inside current directory
89     if (isInCurrent)
90     {
91         // we can override /usr/lib/vera++
92         veraRoot = ".";
93     }
94     char * veraRootEnv = getenv("HOME");
95     if (veraRootEnv != NULL)
96     {
97       Vera::Plugins::RootDirectory::DirectoryName veraRootTmp(veraRootEnv);
98       veraRootTmp += "/.vera++";
99       bool isInHome = ( stat( veraRootTmp.c_str(), &St ) == 0 );
100         if (isInHome)
101         {
102             // We assume that if the user has a .vera++ folder in
103             // their home then we can override the current
104             // directory
105             // We don't want to override the current directory
106             // only because $HOME is defined.
107             veraRoot = veraRootEnv;
108             veraRoot += "/.vera++";
109         }
110     }
111     veraRootEnv = getenv("VERA_ROOT");
112     if (veraRootEnv != NULL)
113     {
114         veraRoot = veraRootEnv;
115     }
116 
117     std::string profile = "default";
118     std::string transform;
119     std::vector<std::string> rules;
120     std::vector<std::string> parameters;
121     std::vector<std::string> parameterFiles;
122     std::vector<std::string> inputs;
123     std::vector<std::string> inputFiles;
124     std::vector<std::string> exclusionFiles;
125     // outputs
126     std::vector<std::string> stdreports;
127     std::vector<std::string> vcreports;
128     std::vector<std::string> xmlreports;
129     std::vector<std::string> checkstylereports;
130     /** Define and parse the program options
131     */
132     namespace po = boost::program_options;
133     po::options_description visibleOptions("Options");
134     visibleOptions.add_options()
135         ("profile,p", po::value(&profile), "execute all rules from the given profile")
136         ("rule,R", po::value(&rules), "execute the given rule (note: the .tcl extension is added"
137             " automatically. can be used many times.)")
138         ("transform", po::value(&transform), "execute the given transformation")
139         ("std-report,o", po::value(&stdreports), "write the standard (gcc-like) report to this"
140             "file. Default is standard or error output depending on the options."
141             " (note: may be used many times.)")
142         ("vc-report,v", po::value(&vcreports), "write the Visual C report to this file."
143             " Not used by default. (note: may be used many times.)")
144         ("xml-report,x", po::value(&xmlreports), "write the XML report to this file."
145             " Not used by default. (note: may be used many times.)")
146         ("checkstyle-report,c", po::value(&checkstylereports),
147             "write the checkstyle report to this file."
148             " Not used by default. (note: may be used many times.)")
149         ("show-rule,s", "include rule name in each report")
150         ("no-duplicate,d", "do not duplicate messages if a single rule is violated many times in a"
151             " single line of code")
152         ("warning,w", "reports are marked as warning and generated on error output")
153         ("error,e", "reports are marked as error and generated on error output."
154             " A non zero exit code is used when one or more reports are generated.")
155         ("quiet,q", "don't display the reports")
156         ("summary,S", "display the number of reports and the number of processed files")
157         ("parameters", po::value(&parameterFiles), "read parameters from file"
158             " (note: can be used many times)")
159         ("parameter,P", po::value(&parameters), "provide parameters to the scripts as name=value"
160             " (note: can be used many times)")
161         ("exclusions", po::value(&exclusionFiles), "read exclusions from file"
162             " (note: can be used many times)")
163         ("inputs,i", po::value(&inputFiles), "the inputs are read from that file (note: one file"
164             " per line. can be used many times.)")
165         ("root,r", po::value(&veraRoot), "use the given directory as the vera root directory")
166         ("help,h", "show this help message and exit")
167         ("version", "show vera++'s version and exit");
168 
169     // don't call it "input", as is may seems more logical, in order to get an error when
170     // --inputs is used without the "s" at the end
171     po::options_description allOptions;
172     allOptions.add(visibleOptions).add_options()
173             ("__input__", po::value(&inputs), "input files from command line");
174 
175     po::positional_options_description positionalOptions;
176     positionalOptions.add("__input__", -1);
177 
178     po::variables_map vm;
179     try
180     {
181         po::store(po::command_line_parser(argc, argv).options(allOptions)
182             .positional(positionalOptions).run(), vm);
183         if ( vm.count("help")  )
184         {
185           std::cout << "vera++ [options] [list-of-files]" << std::endl
186                     << visibleOptions << std::endl;
187           return EXIT_SUCCESS;
188         }
189         po::notify(vm); // throws on error, so do after help in case
190                         // there are any problems
191 
192         // don't do the profile stuff when we only want the version
193         if (vm.count("version"))
194         {
195             std::cout << VERA_VERSION << '\n';
196             return EXIT_SUCCESS;
197         }
198 
199         // we need the root to be able to find the profiles
200         Vera::Plugins::RootDirectory::setRootDirectory(veraRoot);
201 
202         if (vm.count("profile") != 0 || (vm.count("rule") == 0 && vm.count("transform") == 0))
203         {
204             try
205             {
206                 // backward compatibility stuff
207                 Vera::Plugins::Profiles::RuleNameCollection res =
208                     Vera::Plugins::Profiles::getListOfScriptNamesTcl(profile);
209                 rules.insert(rules.end(), res.begin(), res.end());
210             }
211             catch (...)
212             {
213                 std::string profileFile = veraRoot + "/profiles/" + profile;
214                 std::ifstream file(profileFile.c_str());
215                 if (file.is_open() == false)
216                 {
217                     throw std::runtime_error(
218                         "Cannot open profile description for profile '" + profile + "': "
219                         + strerror(errno));
220                 }
221                 po::store(po::parse_config_file(file, visibleOptions), vm);
222                 if (file.bad())
223                 {
224                     throw std::runtime_error(
225                         "Cannot read from profile description '" + profile + "': "
226                         + strerror(errno));
227                 }
228                 po::notify(vm);
229             }
230         }
231     }
232     catch (po::error& e)
233     {
234         std::cerr << "vera++: " << e.what() << std::endl << std::endl;
235         std::cerr << visibleOptions << std::endl;
236         return EXIT_FAILURE;
237     }
238     catch (std::exception& e)
239     {
240         std::cerr << "vera++: " << e.what() << std::endl;
241         return EXIT_FAILURE;
242     }
243 
244     try
245     {
246         Vera::Plugins::Reports::setShowRules(vm.count("show-rule"));
247         if (vm.count("warning"))
248         {
249             if (vm.count("error"))
250             {
251                 std::cerr << "vera++: --warning and --error can't be used at the same time."
252                     << std::endl;
253                 std::cerr << visibleOptions << std::endl;
254                 return EXIT_FAILURE;
255             }
256             Vera::Plugins::Reports::setPrefix("warning");
257         }
258         if (vm.count("error"))
259         {
260             Vera::Plugins::Reports::setPrefix("error");
261         }
262         foreach (const std::string & f, exclusionFiles)
263         {
264             Vera::Plugins::Exclusions::setExclusions(f);
265         }
266         foreach (const std::string & f, parameterFiles)
267         {
268             Vera::Plugins::Parameters::readFromFile(f);
269         }
270         foreach (const std::string & p, parameters)
271         {
272             Vera::Plugins::Parameters::ParamAssoc assoc(p);
273             Vera::Plugins::Parameters::set(assoc);
274         }
275         if (vm.count("__input__"))
276         {
277             foreach (const std::string & i, inputs)
278             {
279                 Vera::Structures::SourceFiles::addFileName(i);
280             }
281         }
282         if (vm.count("__input__") == 0 && vm.count("inputs") == 0)
283         {
284             // list of source files is provided on stdin
285             inputFiles.push_back("-");
286         }
287 
288         foreach (const std::string & f, inputFiles)
289         {
290             if (f == "-")
291             {
292                 Vera::Structures::SourceFiles::FileName name;
293                 while (std::getline(std::cin, name))
294                 {
295                     Vera::Structures::SourceFiles::addFileName(name);
296                 }
297             }
298             else
299             {
300                 std::ifstream file(f.c_str());
301                 if (file.is_open() == false)
302                 {
303                     throw std::runtime_error(
304                         "Cannot open " + f + ": " + strerror(errno));
305                 }
306                 std::string name;
307                 while (std::getline(file, name))
308                 {
309                     Vera::Structures::SourceFiles::addFileName(name);
310                 }
311                 if (file.bad())
312                 {
313                     throw std::runtime_error(
314                         "Cannot read from " + f + ": " + strerror(errno));
315                 }
316                 file.close();
317             }
318         }
319 
320         if (vm.count("std-report") == 0 && vm.count("vc-report") == 0
321             && vm.count("xml-report") == 0 && vm.count("checkstyle-report") == 0
322             && vm.count("quiet") == 0)
323         {
324             // no report set - use std report on std out/err
325             stdreports.push_back("-");
326         }
327 
328         if (rules.empty() == false)
329         {
330             if (vm.count("transform"))
331             {
332                 std::cerr << "vera++: --transform and --rule can't be used at the same time."
333                     << std::endl;
334                 std::cerr << visibleOptions << std::endl;
335                 return EXIT_FAILURE;
336             }
337             foreach (const std::string & r, rules)
338             {
339                 Vera::Plugins::Rules::executeRule(r);
340             }
341         }
342         else if (vm.count("transform"))
343         {
344             Vera::Plugins::Transformations::executeTransformation(transform);
345         }
346 
347         doReports(stdreports, vm, Vera::Plugins::Reports::writeStd);
348         doReports(vcreports, vm, Vera::Plugins::Reports::writeVc);
349         doReports(xmlreports, vm, Vera::Plugins::Reports::writeXml);
350         doReports(checkstylereports, vm, Vera::Plugins::Reports::writeCheckStyle);
351 
352         if (vm.count("summary"))
353         {
354             std::cerr << Vera::Plugins::Reports::count() << " reports in "
355                 << Vera::Structures::SourceFiles::count() << " files." << std::endl;
356         }
357     }
358     catch (const std::exception & e)
359     {
360         std::cerr << "vera++: " << e.what() << std::endl;
361         return EXIT_FAILURE;
362     }
363 
364     if (vm.count("error") && Vera::Plugins::Reports::count() !=0)
365     {
366         return EXIT_FAILURE;
367     }
368     return EXIT_SUCCESS;
369 }
370