advgetopt 2.0.49
Parse complex command line arguments and configuration files in C++.
atomic_names.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/cppthread
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along
17// with this program; if not, write to the Free Software Foundation, Inc.,
18// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
48// advgetopt
49//
50#include <advgetopt/advgetopt.h>
51#include <advgetopt/conf_file.h>
52#include <advgetopt/exception.h>
53
54
55// cppthread
56//
57#include <cppthread/thread.h>
58#include <cppthread/version.h>
59
60
61// libexcept
62//
63#include <libexcept/exception.h>
64#include <libexcept/file_inheritance.h>
65#include <libexcept/report_signal.h>
66
67
68// snapdev
69//
70#include <snapdev/file_contents.h>
71#include <snapdev/pathinfo.h>
72
73
74// boost
75//
76#include <boost/preprocessor/stringize.hpp>
77
78
79// C++
80//
81#include <iostream>
82#include <sstream>
83
84
85// last include
86//
87#include <snapdev/poison.h>
88
89
90
91namespace
92{
93
94
96{
98 advgetopt::Name("output-path")
104 , advgetopt::Help("path to where the output files get saved.")
105 ),
107 advgetopt::Name("verbose")
112 , advgetopt::Help("make the tool verbose.")
113 ),
115 advgetopt::Name("--")
119 , advgetopt::Help("filename with atomic name definitions; the same name is used to generate the output, only the extension gets changed.")
120 ),
122};
123
138
139constexpr char const * const g_configuration_files[] =
140{
141 "/etc/cppthread/atomic-names.conf",
142 nullptr
143};
144
145
146
147
148// TODO: once we have stdc++20, remove all defaults & pragma
149#pragma GCC diagnostic push
150#pragma GCC diagnostic ignored "-Wpedantic"
152{
153 .f_project_name = "atomic-names",
154 .f_group_name = "atomic-names",
155 .f_options = g_options,
156 .f_options_files_directory = nullptr,
157 .f_environment_variable_name = "ATOMIC_NAMES",
158 .f_environment_variable_intro = "ATOMIC_NAMES_",
159 .f_section_variables_name = nullptr,
160 .f_configuration_files = g_configuration_files,
161 .f_configuration_filename = nullptr,
162 .f_configuration_directories = nullptr,
164 .f_help_header = "Usage: %p [-<opt>]\n"
165 "where -<opt> is one or more of:",
166 .f_help_footer = "Try `man atomic-names` for more info.\n%c",
167 .f_version = CPPTHREAD_VERSION_STRING,
168 .f_license = "GPL v2 or newer",
169 .f_copyright = "Copyright (c) 2022-"
170 BOOST_PP_STRINGIZE(UTC_BUILD_YEAR)
171 " Made to Order Software Corporation",
172 .f_build_date = UTC_BUILD_DATE,
173 .f_build_time = UTC_BUILD_TIME,
174 .f_groups = g_group_descriptions
175};
176#pragma GCC diagnostic pop
177
178
179
180
181
182
184{
185public:
186 atomic_names(int argc, char * argv[]);
187
188 int run();
189
190private:
191 int get_filenames();
192 int load_input();
193 int validate_name(
194 std::string const & what
195 , std::string & name
196 , bool start_end_underscore = false);
197 int extract_value(
198 std::string const & name
199 , std::string const & value
200 , int64_t & id
201 , std::string & result);
202 int generate_files();
203
205 bool f_verbose = false;
206 std::string f_filename = std::string();
207 std::string f_basename = std::string();
208 std::string f_output_path = std::string();
211 std::string f_introducer = "atomic_name";
212 std::string f_project = std::string();
213 std::string f_sub_project = std::string();
214};
215
216
217atomic_names::atomic_names(int argc, char * argv[])
218 : f_opt(g_options_environment, argc, argv)
219{
220 f_verbose = f_opt.is_defined("verbose");
221}
222
223
225{
226 int r(0);
227
228 r = get_filenames();
229 if(r != 0)
230 {
231 return r;
232 }
233
234 r = load_input();
235 if(r != 0)
236 {
237 return r;
238 }
239
240 r = generate_files();
241 if(r != 0)
242 {
243 return r;
244 }
245
246 return 0;
247}
248
249
251{
252 if(f_verbose)
253 {
254 std::cout << "info: get filename.\n";
255 }
256
257 if(!f_opt.is_defined("--"))
258 {
259 std::cerr << "error: a <filename> is required.\n";
260 return 1;
261 }
263 if(f_filename.empty())
264 {
265 std::cerr << "error: <filename> requires a non-empty name.\n";
266 return 1;
267 }
268
269 f_basename = snapdev::pathinfo::basename(f_filename, ".*");
270 if(f_basename.empty())
271 {
272 std::cerr
273 << "error: somehow the basename of \""
274 << f_filename
275 << "\" is an empty string.\n";
276 return 1;
277 }
278
279 f_output_path = f_opt.get_string("output-path");
280 if(f_output_path.empty())
281 {
282 std::cerr << "error: the --output-path command line option requires a non-empty name.\n";
283 return 1;
284 }
285
286 return 0;
287}
288
289
291{
292 if(f_verbose)
293 {
294 std::cout
295 << "info: load input \""
296 << f_filename
297 << "\".\n";
298 }
299
302 if(f_names == nullptr)
303 {
304 std::cerr
305 << "error: could not read input file \""
306 << f_filename
307 << "\".\n";
308 return 1;
309 }
310
311 return 0;
312}
313
314
316 std::string const & what
317 , std::string & name
318 , bool start_end_underscore)
319{
320 if(name.empty())
321 {
322 std::cerr << "error: " << what << " cannot be empty.\n";
323 return 1;
324 }
325
326 for(char const * n(name.c_str()); *n != '\0'; ++n)
327 {
328 if(*n == '-')
329 {
330 name[n - name.c_str()] = '_';
331 }
332 else if((*n < 'A' || *n > 'Z')
333 && (*n < 'a' || *n > 'z')
334 && (*n < '0' || *n > '9')
335 && *n != '_')
336 {
337 std::cerr
338 << "error: "
339 << what
340 << " includes unexpected characters in \""
341 << name
342 << "\".\n";
343 return 1;
344 }
345 }
346
347 if(!start_end_underscore
348 && (name.front() == '_' || name.back() == '_'))
349 {
350 std::cerr
351 << "error: "
352 << what
353 << " cannot start and/or end with an underscore in \""
354 << name
355 << "\".\n";
356 return 1;
357 }
358
359 return 0;
360}
361
362
364 std::string const & name
365 , std::string const & value
366 , int64_t & id
367 , std::string & result)
368{
369 // check for an identifier
370 //
371 // note: it is not an error if not present; it will be given a default
372 // number when that happens
373 //
374 char const * v(value.c_str());
375 int64_t identifier(0);
376 bool found_digits(false);
377 for(; *v != '\0'; ++v)
378 {
379 if(*v < '0' || *v > '9')
380 {
381 break;
382 }
383 identifier *= 10;
384 identifier += *v - '0';
385 found_digits = true;
386 }
387 if(!found_digits
388 || *v != ':')
389 {
390 v = value.c_str();
391 }
392 else
393 {
394 id = identifier;
395 ++v;
396 }
397
398 // the value may be quoted
399 //
400 std::string const str(advgetopt::unquote(v));
401 if(str.empty())
402 {
403 std::cerr
404 << "error: empty values are not currently allowed (parameter \""
405 << name
406 << "\").\n";
407 return 1;
408 }
409
410 for(auto const & c : str)
411 {
412 switch(c)
413 {
414 case '\0':
415 std::cerr
416 << "error: found a NUL character in \""
417 << name
418 << "\".\n";
419 return 1;
420
421 case '"':
422 result += '\\';
423 result += '"';
424 break;
425
426 case '\r':
427 result += '\\';
428 result += 'r';
429 break;
430
431 case '\n':
432 result += '\\';
433 result += 'n';
434 break;
435
436 case '\t':
437 result += '\\';
438 result += 't';
439 break;
440
441 default:
442 if(c < ' ')
443 {
444 result += '\\';
445 result += ((c >> 6) & 0x03) + '0';
446 result += ((c >> 3) & 0x07) + '0';
447 result += ((c >> 6) & 0x07) + '0';
448 }
449 else
450 {
451 result += c;
452 }
453 break;
454
455 }
456 }
457
458 return 0;
459}
460
461
463{
464 int r(0);
465
466 if(f_verbose)
467 {
468 std::cout << "info: generate files.\n";
469 }
470
471 if(f_names->has_parameter("introducer"))
472 {
473 f_introducer = f_names->get_parameter("introducer");
474 r = validate_name("introducer", f_introducer);
475 if(r != 0)
476 {
477 return r;
478 }
479 }
480
481 if(!f_names->has_parameter("project"))
482 {
483 std::cerr << "error: the \"project=...\" parameter is mandatory.\n";
484 return 1;
485 }
486 f_project = f_names->get_parameter("project");
487 r = validate_name("project", f_project);
488 if(r != 0)
489 {
490 return r;
491 }
492
493 if(f_names->has_parameter("sub_project"))
494 {
495 f_sub_project = f_names->get_parameter("sub_project");
496 r = validate_name("sub_project", f_sub_project);
497 if(r != 0)
498 {
499 return r;
500 }
501 }
502 else
503 {
504 f_sub_project = std::string();
505 }
506
507 std::stringstream cpp;
508 std::stringstream h;
509
510 h
511 << "// DO NOT EDIT, see `man atomic-names` for details\n"
512 "#pragma once\n"
513 "namespace " << f_project << " {\n"
514 << (f_sub_project.empty()
515 ? std::string()
516 : "namespace " + f_sub_project + " {\n");
517
518 cpp
519 << "// DO NOT EDIT, see `man atomic-names` for details\n"
520 "#include \"./" << f_basename << ".h\"\n"
521 "namespace " << f_project << " {\n"
522 << (f_sub_project.empty()
523 ? std::string()
524 : "namespace " + f_sub_project + " {\n");
525
526 advgetopt::conf_file::parameters_t parameters(f_names->get_parameters());
527 for(auto const & p : parameters)
528 {
529 if(p.first.length() < 7)
530 {
531 continue;
532 }
533 std::string::size_type const pos(p.first.find("::"));
534 if(pos == std::string::npos)
535 {
536 continue;
537 }
538 std::string const scope(p.first.substr(0, pos));
539 bool is_public(true);
540 if(scope != "public")
541 {
542 if(scope != "private")
543 {
544 continue;
545 }
546 is_public = false;
547 }
548 std::string name(p.first.substr(pos + 2));
549 if(name.empty())
550 {
551 // as far as I know, this cannot happen
552 //
553 std::cerr
554 << "error: empty names are not allowed.\n";
555 return 1;
556 }
557 validate_name("name", name);
558
559 int64_t id(-1);
560 std::string value;
561 r = extract_value(name, p.second, id, value);
562 if(r != 0)
563 {
564 return r;
565 }
566
567 h
568 << "extern char const * g_"
569 << f_introducer
570 << '_'
571 << (f_sub_project.empty() ? f_project : f_sub_project)
572 << '_'
573 << name
574 << ";\n";
575
576 cpp << "char const * g_"
577 << f_introducer
578 << '_'
579 << (f_sub_project.empty() ? f_project : f_sub_project)
580 << '_'
581 << name
582 << " = \""
583 << value
584 << "\";\n";
585 }
586
587 if(!f_sub_project.empty())
588 {
589 h << "}\n"; // namespace sub-project
590 cpp << "}\n"; // namespace sub-project
591 }
592 h << "}\n"; // namespace project
593 cpp << "}\n"; // namespace project
594
595 if(f_verbose)
596 {
597 std::cout
598 << "info: save to \""
600 << '/'
601 << f_basename
602 << "{.cpp,.h,_private.h}\".\n";
603 }
604
605 snapdev::file_contents header_public(f_output_path + "/" + f_basename + ".h");
606 header_public.contents(h.str());
607 if(!header_public.write_all())
608 {
609 std::cerr << "error: could not save public header file to \""
610 << header_public.filename()
611 << ".h\".\n";
612 return 1;
613 }
614
615 snapdev::file_contents cpp_file(f_output_path + "/" + f_basename + ".cpp");
616 cpp_file.contents(cpp.str());
617 if(!cpp_file.write_all())
618 {
619 std::cerr << "error: could not save C++ implementation file to \""
620 << cpp_file.filename()
621 << ".cpp\".\n";
622 return 1;
623 }
624
625 return 0;
626}
627
628
629
630
631}
632// noname namespace
633
634
635
636int main(int argc, char * argv[])
637{
638 libexcept::init_report_signal();
639 libexcept::verify_inherited_files();
640
641 try
642 {
643 atomic_names n(argc, argv);
644 return n.run();
645 }
646 catch(advgetopt::getopt_exit const & e)
647 {
648 return e.code();
649 }
650 catch(libexcept::exception_t const & e)
651 {
652 std::cerr
653 << "error: a libexcept exception occurred: \""
654 << e.what()
655 << "\".\n";
656 }
657 catch(std::exception const & e)
658 {
659 std::cerr
660 << "error: a standard exception occurred: \""
661 << e.what()
662 << "\".\n";
663 }
664 catch(...)
665 {
666 std::cerr << "error: an unknown exception occurred.\n";
667 }
668
669 return 1;
670}
671
672
673
674// vim: ts=4 sw=4 et
Definitions of the advanced getopt class.
int main(int argc, char *argv[])
std::map< std::string, parameter_value > parameters_t
Definition conf_file.h:188
std::shared_ptr< conf_file > pointer_t
Definition conf_file.h:186
static pointer_t get_conf_file(conf_file_setup const &setup)
Create and read a conf_file.
Class used to parse command line options.
Definition advgetopt.h:77
std::string get_string(std::string const &name, int idx=0, bool raw=false) const
Get the content of an option as a string.
bool is_defined(std::string const &name) const
Check whether a parameter is defined.
int extract_value(std::string const &name, std::string const &value, int64_t &id, std::string &result)
int validate_name(std::string const &what, std::string &name, bool start_end_underscore=false)
Declaration of the conf_file class used to read a configuration file.
Definitions of the advanced getopt exceptions.
constexpr group_description define_group(ARGS ...args)
Definition options.h:403
constexpr group_description end_groups()
Definition options.h:419
static constexpr flag_t GETOPT_FLAG_GROUP_OPTIONS
Definition flags.h:72
constexpr option define_option(ARGS ...args)
Definition options.h:259
constexpr flag_t standalone_command_flags()
Definition flags.h:179
constexpr option end_options()
Definition options.h:294
constexpr flag_t GETOPT_ENVIRONMENT_FLAG_PROCESS_SYSTEM_PARAMETERS
Definition options.h:435
static constexpr flag_t GETOPT_FLAG_GROUP_COMMANDS
Definition flags.h:71
constexpr flag_t all_flags()
Definition flags.h:154
std::string unquote(std::string const &s, std::string const &pairs)
Remove single (') or double (") quotes from a string.
Definition utils.cpp:168
static constexpr flag_t GETOPT_FLAG_REQUIRED
Definition flags.h:52
constexpr char const *const g_configuration_files[]
advgetopt::options_environment const g_options_environment
const advgetopt::option g_options[]
advgetopt::group_description const g_group_descriptions[]
Structure representing an option.
Definition options.h:70
char const * f_environment_variable_name
Definition options.h:74

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.