]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/wave/samples/waveidl/idl.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / wave / samples / waveidl / idl.cpp
1 /*=============================================================================
2 Boost.Wave: A Standard compliant C++ preprocessor library
3
4 Sample: IDL oriented preprocessor
5
6 http://www.boost.org/
7
8 Copyright (c) 2001-2010 Hartmut Kaiser. Distributed under the Boost
9 Software License, Version 1.0. (See accompanying file
10 LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11 =============================================================================*/
12
13 #include "idl.hpp" // global configuration
14
15 #include <boost/assert.hpp>
16 #include <boost/program_options.hpp>
17 #include <boost/filesystem/path.hpp>
18
19 ///////////////////////////////////////////////////////////////////////////////
20 // Include Wave itself
21 #include <boost/wave.hpp>
22
23 ///////////////////////////////////////////////////////////////////////////////
24 // Include the lexer related stuff
25 #include <boost/wave/cpplexer/cpp_lex_token.hpp> // token type
26 #include "idllexer/idl_lex_iterator.hpp" // lexer type
27
28 ///////////////////////////////////////////////////////////////////////////////
29 // include lexer specifics, import lexer names
30 //
31 #if BOOST_WAVE_SEPARATE_LEXER_INSTANTIATION == 0
32 #include "idllexer/idl_re2c_lexer.hpp"
33 #endif
34
35 #include <iostream>
36
37 ///////////////////////////////////////////////////////////////////////////////
38 // include the grammar definitions, if these shouldn't be compiled separately
39 // (ATTENTION: _very_ large compilation times!)
40 //
41 #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION == 0
42 #include <boost/wave/grammars/cpp_intlit_grammar.hpp>
43 #include <boost/wave/grammars/cpp_chlit_grammar.hpp>
44 #include <boost/wave/grammars/cpp_grammar.hpp>
45 #include <boost/wave/grammars/cpp_expression_grammar.hpp>
46 #include <boost/wave/grammars/cpp_predef_macros_grammar.hpp>
47 #include <boost/wave/grammars/cpp_defined_grammar.hpp>
48 #include <boost/wave/grammars/cpp_has_include_grammar.hpp>
49 #endif
50
51 ///////////////////////////////////////////////////////////////////////////////
52 // import required names
53 using namespace boost::spirit::classic;
54
55 using std::string;
56 using std::pair;
57 using std::vector;
58 using std::getline;
59 using std::ifstream;
60 using std::cout;
61 using std::cerr;
62 using std::endl;
63 using std::ostream;
64 using std::istreambuf_iterator;
65
66 namespace po = boost::program_options;
67 namespace fs = boost::filesystem;
68
69 ///////////////////////////////////////////////////////////////////////////////
70 // print the current version
71 int print_version()
72 {
73 typedef boost::wave::idllexer::lex_iterator<
74 boost::wave::cpplexer::lex_token<> >
75 lex_iterator_type;
76 typedef boost::wave::context<std::string::iterator, lex_iterator_type>
77 context_type;
78
79 string version (context_type::get_version_string());
80 cout
81 << version.substr(1, version.size()-2) // strip quotes
82 << " (" << IDL_VERSION_DATE << ")" // add date
83 << endl;
84 return 0; // exit app
85 }
86
87 ///////////////////////////////////////////////////////////////////////////////
88 // print the copyright statement
89 int print_copyright()
90 {
91 char const *copyright[] = {
92 "",
93 "Sample: IDL oriented preprocessor",
94 "Based on: Wave, A Standard conformant C++ preprocessor library",
95 "It is hosted by http://www.boost.org/.",
96 "",
97 "Copyright (c) 2001-2010 Hartmut Kaiser, Distributed under the Boost",
98 "Software License, Version 1.0. (See accompanying file",
99 "LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)",
100 0
101 };
102
103 for (int i = 0; 0 != copyright[i]; ++i)
104 cout << copyright[i] << endl;
105
106 return 0; // exit app
107 }
108
109 ///////////////////////////////////////////////////////////////////////////////
110 namespace cmd_line_util {
111
112 // Additional command line parser which interprets '@something' as an
113 // option "config-file" with the value "something".
114 pair<string, string> at_option_parser(string const&s)
115 {
116 if ('@' == s[0])
117 return std::make_pair(string("config-file"), s.substr(1));
118 else
119 return pair<string, string>();
120 }
121
122 // class, which keeps include file information read from the command line
123 class include_paths {
124 public:
125 include_paths() : seen_separator(false) {}
126
127 vector<string> paths; // stores user paths
128 vector<string> syspaths; // stores system paths
129 bool seen_separator; // command line contains a '-I-' option
130
131 // Function which validates additional tokens from command line.
132 static void
133 validate(boost::any &v, vector<string> const &tokens)
134 {
135 if (v.empty())
136 v = boost::any(include_paths());
137
138 include_paths *p = boost::any_cast<include_paths>(&v);
139
140 BOOST_ASSERT(p);
141 // Assume only one path per '-I' occurrence.
142 string t = tokens[0];
143 if (t == "-") {
144 // found -I- option, so switch behaviour
145 p->seen_separator = true;
146 }
147 else if (p->seen_separator) {
148 // store this path as a system path
149 p->syspaths.push_back(t);
150 }
151 else {
152 // store this path as an user path
153 p->paths.push_back(t);
154 }
155 }
156 };
157
158 // Read all options from a given config file, parse and add them to the
159 // given variables_map
160 void read_config_file_options(string const &filename,
161 po::options_description const &desc, po::variables_map &vm,
162 bool may_fail = false)
163 {
164 ifstream ifs(filename.c_str());
165
166 if (!ifs.is_open()) {
167 if (!may_fail) {
168 cerr << filename
169 << ": command line warning: config file not found"
170 << endl;
171 }
172 return;
173 }
174
175 vector<string> options;
176 string line;
177
178 while (std::getline(ifs, line)) {
179 // skip empty lines
180 string::size_type pos = line.find_first_not_of(" \t");
181 if (pos == string::npos)
182 continue;
183
184 // skip comment lines
185 if ('#' != line[pos])
186 options.push_back(line);
187 }
188
189 if (options.size() > 0) {
190 using namespace boost::program_options::command_line_style;
191 po::store(po::command_line_parser(options)
192 .options(desc).style(unix_style).run(), vm);
193 po::notify(vm);
194 }
195 }
196
197 // predicate to extract all positional arguments from the command line
198 struct is_argument {
199 bool operator()(po::option const &opt)
200 {
201 return (opt.position_key == -1) ? true : false;
202 }
203 };
204
205 ///////////////////////////////////////////////////////////////////////////////
206 }
207
208 ///////////////////////////////////////////////////////////////////////////////
209 //
210 // Special validator overload, which allows to handle the -I- syntax for
211 // switching the semantics of an -I option.
212 //
213 ///////////////////////////////////////////////////////////////////////////////
214 namespace cmd_line_util {
215
216 void validate(boost::any &v, std::vector<std::string> const &s,
217 cmd_line_util::include_paths *, int) {
218 cmd_line_util::include_paths::validate(v, s);
219 }
220
221 } // namespace cmd_line_util
222
223 ///////////////////////////////////////////////////////////////////////////////
224 // do the actual preprocessing
225 int
226 do_actual_work (std::string file_name, po::variables_map const &vm)
227 {
228 // current file position is saved for exception handling
229 boost::wave::util::file_position_type current_position;
230
231 try {
232 // process the given file
233 ifstream instream(file_name.c_str());
234 string instring;
235
236 if (!instream.is_open()) {
237 cerr << "waveidl: could not open input file: " << file_name << endl;
238 return -1;
239 }
240 instream.unsetf(std::ios::skipws);
241
242 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
243 // this is known to be very slow for large files on some systems
244 copy (istream_iterator<char>(instream),
245 istream_iterator<char>(),
246 inserter(instring, instring.end()));
247 #else
248 instring = string(istreambuf_iterator<char>(instream.rdbuf()),
249 istreambuf_iterator<char>());
250 #endif
251
252 // This sample uses the lex_token type predefined in the Wave library, but
253 // but uses a custom lexer type.
254 typedef boost::wave::idllexer::lex_iterator<
255 boost::wave::cpplexer::lex_token<> >
256 lex_iterator_type;
257 typedef boost::wave::context<std::string::iterator, lex_iterator_type>
258 context_type;
259
260 // The C++ preprocessor iterators shouldn't be constructed directly. They
261 // are to be generated through a boost::wave::context<> object. This
262 // boost::wave::context object is additionally to be used to initialize and
263 // define different parameters of the actual preprocessing.
264 // The preprocessing of the input stream is done on the fly behind the
265 // scenes during iteration over the context_type::iterator_type stream.
266 context_type ctx (instring.begin(), instring.end(), file_name.c_str());
267
268 // add include directories to the system include search paths
269 if (vm.count("sysinclude")) {
270 vector<string> const &syspaths =
271 vm["sysinclude"].as<vector<string> >();
272 vector<string>::const_iterator end = syspaths.end();
273 for (vector<string>::const_iterator cit = syspaths.begin();
274 cit != end; ++cit)
275 {
276 ctx.add_sysinclude_path((*cit).c_str());
277 }
278 }
279
280 // add include directories to the include search paths
281 if (vm.count("include")) {
282 cmd_line_util::include_paths const &ip =
283 vm["include"].as<cmd_line_util::include_paths>();
284 vector<string>::const_iterator end = ip.paths.end();
285
286 for (vector<string>::const_iterator cit = ip.paths.begin();
287 cit != end; ++cit)
288 {
289 ctx.add_include_path((*cit).c_str());
290 }
291
292 // if on the command line was given -I- , this has to be propagated
293 if (ip.seen_separator)
294 ctx.set_sysinclude_delimiter();
295
296 // add system include directories to the include path
297 vector<string>::const_iterator sysend = ip.syspaths.end();
298 for (vector<string>::const_iterator syscit = ip.syspaths.begin();
299 syscit != sysend; ++syscit)
300 {
301 ctx.add_sysinclude_path((*syscit).c_str());
302 }
303 }
304
305 // add additional defined macros
306 if (vm.count("define")) {
307 vector<string> const &macros = vm["define"].as<vector<string> >();
308 vector<string>::const_iterator end = macros.end();
309 for (vector<string>::const_iterator cit = macros.begin();
310 cit != end; ++cit)
311 {
312 ctx.add_macro_definition(*cit);
313 }
314 }
315
316 // add additional predefined macros
317 if (vm.count("predefine")) {
318 vector<string> const &predefmacros =
319 vm["predefine"].as<vector<string> >();
320 vector<string>::const_iterator end = predefmacros.end();
321 for (vector<string>::const_iterator cit = predefmacros.begin();
322 cit != end; ++cit)
323 {
324 ctx.add_macro_definition(*cit, true);
325 }
326 }
327
328 // undefine specified macros
329 if (vm.count("undefine")) {
330 vector<string> const &undefmacros =
331 vm["undefine"].as<vector<string> >();
332 vector<string>::const_iterator end = undefmacros.end();
333 for (vector<string>::const_iterator cit = undefmacros.begin();
334 cit != end; ++cit)
335 {
336 ctx.remove_macro_definition((*cit).c_str(), true);
337 }
338 }
339
340 // open the output file
341 std::ofstream output;
342
343 if (vm.count("output")) {
344 // try to open the file, where to put the preprocessed output
345 string out_file (vm["output"].as<string>());
346
347 output.open(out_file.c_str());
348 if (!output.is_open()) {
349 cerr << "waveidl: could not open output file: " << out_file
350 << endl;
351 return -1;
352 }
353 }
354 else {
355 // output the preprocessed result to std::cout
356 output.copyfmt(cout);
357 output.clear(cout.rdstate());
358 static_cast<std::basic_ios<char> &>(output).rdbuf(cout.rdbuf());
359 }
360
361 // analyze the input file
362 context_type::iterator_type first = ctx.begin();
363 context_type::iterator_type last = ctx.end();
364
365 // loop over all generated tokens outputing the generated text
366 while (first != last) {
367 // print out the string representation of this token (skip comments)
368 using namespace boost::wave;
369
370 // store the last known good token position
371 current_position = (*first).get_position();
372
373 token_id id = token_id(*first);
374
375 if (T_CPPCOMMENT == id || T_NEWLINE == id) {
376 // C++ comment tokens contain the trailing newline
377 output << endl;
378 }
379 else if (id != T_CCOMMENT) {
380 // print out the current token value
381 output << (*first).get_value();
382 }
383 ++first; // advance to the next token
384 }
385 }
386 catch (boost::wave::cpp_exception const& e) {
387 // some preprocessing error
388 cerr
389 << e.file_name() << "(" << e.line_no() << "): "
390 << e.description() << endl;
391 return 1;
392 }
393 catch (boost::wave::cpplexer::lexing_exception const& e) {
394 // some lexing error
395 cerr
396 << e.file_name() << "(" << e.line_no() << "): "
397 << e.description() << endl;
398 return 2;
399 }
400 catch (std::exception const& e) {
401 // use last recognized token to retrieve the error position
402 cerr
403 << current_position.get_file()
404 << "(" << current_position.get_line() << "): "
405 << "exception caught: " << e.what()
406 << endl;
407 return 3;
408 }
409 catch (...) {
410 // use last recognized token to retrieve the error position
411 cerr
412 << current_position.get_file()
413 << "(" << current_position.get_line() << "): "
414 << "unexpected exception caught." << endl;
415 return 4;
416 }
417 return 0;
418 }
419
420 ///////////////////////////////////////////////////////////////////////////////
421 // main entry point
422 int
423 main (int argc, char *argv[])
424 {
425 try {
426 // analyze the command line options and arguments
427
428 // declare the options allowed from the command line only
429 po::options_description desc_cmdline ("Options allowed on the command line only");
430
431 desc_cmdline.add_options()
432 ("help,h", "print out program usage (this message)")
433 ("version,v", "print the version number")
434 ("copyright,c", "print out the copyright statement")
435 ("config-file", po::value<vector<string> >(),
436 "specify a config file (alternatively: @filepath)")
437 ;
438
439 // declare the options allowed on command line and in config files
440 po::options_description desc_generic ("Options allowed additionally in a config file");
441
442 desc_generic.add_options()
443 ("output,o", po::value<string>()->composing(),
444 "specify a file to use for output instead of stdout")
445 ("include,I", po::value<cmd_line_util::include_paths>()->composing(),
446 "specify an additional include directory")
447 ("sysinclude,S", po::value<vector<string> >()->composing(),
448 "specify an additional system include directory")
449 ("define,D", po::value<vector<string> >()->composing(),
450 "specify a macro to define (as macro[=[value]])")
451 ("predefine,P", po::value<vector<string> >()->composing(),
452 "specify a macro to predefine (as macro[=[value]])")
453 ("undefine,U", po::value<vector<string> >()->composing(),
454 "specify a macro to undefine")
455 ;
456
457 // combine the options for the different usage schemes
458 po::options_description desc_overall_cmdline;
459 po::options_description desc_overall_cfgfile;
460
461 desc_overall_cmdline.add(desc_cmdline).add(desc_generic);
462 desc_overall_cfgfile.add(desc_generic);
463
464 // parse command line and store results
465 using namespace boost::program_options::command_line_style;
466
467 po::parsed_options opts = po::parse_command_line(argc, argv,
468 desc_overall_cmdline, unix_style, cmd_line_util::at_option_parser);
469 po::variables_map vm;
470
471 po::store(opts, vm);
472 po::notify(vm);
473
474 // Try to find a waveidl.cfg in the same directory as the executable was
475 // started from. If this exists, treat it as a wave config file
476 fs::path filename = boost::wave::util::create_path(argv[0]);
477
478 filename = filename.branch_path() / "waveidl.cfg";
479 cmd_line_util::read_config_file_options(filename.string(),
480 desc_overall_cfgfile, vm, true);
481
482 // if there is specified at least one config file, parse it and add the
483 // options to the main variables_map
484 if (vm.count("config-file")) {
485 vector<string> const &cfg_files =
486 vm["config-file"].as<vector<string> >();
487 vector<string>::const_iterator end = cfg_files.end();
488 for (vector<string>::const_iterator cit = cfg_files.begin();
489 cit != end; ++cit)
490 {
491 // parse a single config file and store the results
492 cmd_line_util::read_config_file_options(*cit,
493 desc_overall_cfgfile, vm);
494 }
495 }
496
497 // ... act as required
498 if (vm.count("help")) {
499 po::options_description desc_help (
500 "Usage: waveidl [options] [@config-file(s)] file");
501
502 desc_help.add(desc_cmdline).add(desc_generic);
503 cout << desc_help << endl;
504 return 1;
505 }
506
507 if (vm.count("version")) {
508 return print_version();
509 }
510
511 if (vm.count("copyright")) {
512 return print_copyright();
513 }
514
515 // extract the arguments from the parsed command line
516 vector<po::option> arguments;
517
518 std::remove_copy_if(opts.options.begin(), opts.options.end(),
519 inserter(arguments, arguments.end()), cmd_line_util::is_argument());
520
521 // if there is no input file given, then exit
522 if (0 == arguments.size() || 0 == arguments[0].value.size()) {
523 cerr << "waveidl: no input file given, "
524 << "use --help to get a hint." << endl;
525 return 5;
526 }
527
528 // preprocess the given input file
529 return do_actual_work(arguments[0].value[0], vm);
530 }
531 catch (std::exception const& e) {
532 cout << "waveidl: exception caught: " << e.what() << endl;
533 return 6;
534 }
535 catch (...) {
536 cerr << "waveidl: unexpected exception caught." << endl;
537 return 7;
538 }
539 }
540