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