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#include <snapdev/stringize.h>
73
74
75// C++
76//
77#include <iostream>
78#include <sstream>
79
80
81// last include
82//
83#include <snapdev/poison.h>
84
85
86
87namespace
88{
89
90
92{
94 advgetopt::Name("output-path")
100 , advgetopt::Help("path to where the output files get saved.")
101 ),
103 advgetopt::Name("verbose")
108 , advgetopt::Help("make the tool verbose.")
109 ),
111 advgetopt::Name("--")
115 , advgetopt::Help("filename with atomic name definitions; the same name is used to generate the output, only the extension gets changed.")
116 ),
118};
119
134
135constexpr char const * const g_configuration_files[] =
136{
137 "/etc/cppthread/atomic-names.conf",
138 nullptr
139};
140
141
142
143
144// TODO: once we have stdc++20, remove all defaults & pragma
145#pragma GCC diagnostic push
146#pragma GCC diagnostic ignored "-Wpedantic"
148{
149 .f_project_name = "atomic-names",
150 .f_group_name = "atomic-names",
151 .f_options = g_options,
152 .f_options_files_directory = nullptr,
153 .f_environment_variable_name = "ATOMIC_NAMES",
154 .f_environment_variable_intro = "ATOMIC_NAMES_",
155 .f_section_variables_name = nullptr,
156 .f_configuration_files = g_configuration_files,
157 .f_configuration_filename = nullptr,
158 .f_configuration_directories = nullptr,
160 .f_help_header = "Usage: %p [-<opt>]\n"
161 "where -<opt> is one or more of:",
162 .f_help_footer = "Try `man atomic-names` for more info.\n%c",
163 .f_version = CPPTHREAD_VERSION_STRING,
164 .f_license = "GPL v2 or newer",
165 .f_copyright = "Copyright (c) 2022-"
166 SNAPDEV_STRINGIZE(UTC_BUILD_YEAR)
167 " Made to Order Software Corporation",
168 .f_build_date = UTC_BUILD_DATE,
169 .f_build_time = UTC_BUILD_TIME,
170 .f_groups = g_group_descriptions
171};
172#pragma GCC diagnostic pop
173
174
175
176
177
178
180{
181public:
182 atomic_names(int argc, char * argv[]);
183
184 int run();
185
186private:
187 int get_filenames();
188 int load_input();
189 int validate_name(
190 std::string const & what
191 , std::string & name
192 , bool start_end_underscore = false);
193 int extract_value(
194 std::string const & name
195 , std::string const & value
196 , int64_t & id
197 , std::string & result);
198 int generate_files();
199
201 bool f_verbose = false;
202 std::string f_filename = std::string();
203 std::string f_basename = std::string();
204 std::string f_output_path = std::string();
207 std::string f_introducer = "atomic_name";
208 std::string f_project = std::string();
209 std::string f_sub_project = std::string();
210};
211
212
213atomic_names::atomic_names(int argc, char * argv[])
214 : f_opt(g_options_environment, argc, argv)
215{
216 f_verbose = f_opt.is_defined("verbose");
217}
218
219
221{
222 int r(0);
223
224 r = get_filenames();
225 if(r != 0)
226 {
227 return r;
228 }
229
230 r = load_input();
231 if(r != 0)
232 {
233 return r;
234 }
235
236 r = generate_files();
237 if(r != 0)
238 {
239 return r;
240 }
241
242 return 0;
243}
244
245
247{
248 if(f_verbose)
249 {
250 std::cout << "info: get filename.\n";
251 }
252
253 if(!f_opt.is_defined("--"))
254 {
255 std::cerr << "error: a <filename> is required.\n";
256 return 1;
257 }
259 if(f_filename.empty())
260 {
261 std::cerr << "error: <filename> requires a non-empty name.\n";
262 return 1;
263 }
264
265 f_basename = snapdev::pathinfo::basename(f_filename, ".*");
266 if(f_basename.empty())
267 {
268 std::cerr
269 << "error: somehow the basename of \""
270 << f_filename
271 << "\" is an empty string.\n";
272 return 1;
273 }
274
275 f_output_path = f_opt.get_string("output-path");
276 if(f_output_path.empty())
277 {
278 std::cerr << "error: the --output-path command line option requires a non-empty name.\n";
279 return 1;
280 }
281
282 return 0;
283}
284
285
287{
288 if(f_verbose)
289 {
290 std::cout
291 << "info: load input \""
292 << f_filename
293 << "\".\n";
294 }
295
298 if(f_names == nullptr)
299 {
300 std::cerr
301 << "error: could not read input file \""
302 << f_filename
303 << "\".\n";
304 return 1;
305 }
306
307 return 0;
308}
309
310
312 std::string const & what
313 , std::string & name
314 , bool start_end_underscore)
315{
316 if(name.empty())
317 {
318 std::cerr << "error: " << what << " cannot be empty.\n";
319 return 1;
320 }
321
322 for(char const * n(name.c_str()); *n != '\0'; ++n)
323 {
324 if(*n == '-')
325 {
326 name[n - name.c_str()] = '_';
327 }
328 else if((*n < 'A' || *n > 'Z')
329 && (*n < 'a' || *n > 'z')
330 && (*n < '0' || *n > '9')
331 && *n != '_')
332 {
333 std::cerr
334 << "error: "
335 << what
336 << " includes unexpected characters in \""
337 << name
338 << "\".\n";
339 return 1;
340 }
341 }
342
343 if(!start_end_underscore
344 && (name.front() == '_' || name.back() == '_'))
345 {
346 std::cerr
347 << "error: "
348 << what
349 << " cannot start and/or end with an underscore in \""
350 << name
351 << "\".\n";
352 return 1;
353 }
354
355 return 0;
356}
357
358
360 std::string const & name
361 , std::string const & value
362 , int64_t & id
363 , std::string & result)
364{
365 // check for an identifier
366 //
367 // note: it is not an error if not present; it will be given a default
368 // number when that happens
369 //
370 char const * v(value.c_str());
371 int64_t identifier(0);
372 bool found_digits(false);
373 for(; *v != '\0'; ++v)
374 {
375 if(*v < '0' || *v > '9')
376 {
377 break;
378 }
379 identifier *= 10;
380 identifier += *v - '0';
381 found_digits = true;
382 }
383 if(!found_digits
384 || *v != ':')
385 {
386 v = value.c_str();
387 }
388 else
389 {
390 id = identifier;
391 ++v;
392 }
393
394 // the value may be quoted
395 //
396 std::string const str(advgetopt::unquote(v));
397 if(str.empty())
398 {
399 std::cerr
400 << "error: empty values are not currently allowed (parameter \""
401 << name
402 << "\").\n";
403 return 1;
404 }
405
406 for(auto const & c : str)
407 {
408 switch(c)
409 {
410 case '\0':
411 std::cerr
412 << "error: found a NUL character in \""
413 << name
414 << "\".\n";
415 return 1;
416
417 case '"':
418 result += '\\';
419 result += '"';
420 break;
421
422 case '\r':
423 result += '\\';
424 result += 'r';
425 break;
426
427 case '\n':
428 result += '\\';
429 result += 'n';
430 break;
431
432 case '\t':
433 result += '\\';
434 result += 't';
435 break;
436
437 default:
438 if(c < ' ')
439 {
440 result += '\\';
441 result += ((c >> 6) & 0x03) + '0';
442 result += ((c >> 3) & 0x07) + '0';
443 result += ((c >> 6) & 0x07) + '0';
444 }
445 else
446 {
447 result += c;
448 }
449 break;
450
451 }
452 }
453
454 return 0;
455}
456
457
459{
460 int r(0);
461
462 if(f_verbose)
463 {
464 std::cout << "info: generate files.\n";
465 }
466
467 if(f_names->has_parameter("introducer"))
468 {
469 f_introducer = f_names->get_parameter("introducer");
470 r = validate_name("introducer", f_introducer);
471 if(r != 0)
472 {
473 return r;
474 }
475 }
476
477 if(!f_names->has_parameter("project"))
478 {
479 std::cerr << "error: the \"project=...\" parameter is mandatory.\n";
480 return 1;
481 }
482 f_project = f_names->get_parameter("project");
483 r = validate_name("project", f_project);
484 if(r != 0)
485 {
486 return r;
487 }
488
489 if(f_names->has_parameter("sub_project"))
490 {
491 f_sub_project = f_names->get_parameter("sub_project");
492 r = validate_name("sub_project", f_sub_project);
493 if(r != 0)
494 {
495 return r;
496 }
497 }
498 else
499 {
500 f_sub_project = std::string();
501 }
502
503 std::stringstream cpp;
504 std::stringstream h;
505
506 h
507 << "// DO NOT EDIT, see `man atomic-names` for details\n"
508 "#pragma once\n"
509 "namespace " << f_project << " {\n"
510 << (f_sub_project.empty()
511 ? std::string()
512 : "namespace " + f_sub_project + " {\n");
513
514 cpp
515 << "// DO NOT EDIT, see `man atomic-names` for details\n"
516 "#include \"./" << f_basename << ".h\"\n"
517 "namespace " << f_project << " {\n"
518 << (f_sub_project.empty()
519 ? std::string()
520 : "namespace " + f_sub_project + " {\n");
521
522 advgetopt::conf_file::parameters_t parameters(f_names->get_parameters());
523 for(auto const & p : parameters)
524 {
525 if(p.first.length() < 7)
526 {
527 continue;
528 }
529 std::string::size_type const pos(p.first.find("::"));
530 if(pos == std::string::npos)
531 {
532 continue;
533 }
534 std::string const scope(p.first.substr(0, pos));
535 bool is_public(true);
536 if(scope != "public")
537 {
538 if(scope != "private")
539 {
540 continue;
541 }
542 is_public = false;
543 }
544 std::string name(p.first.substr(pos + 2));
545 if(name.empty())
546 {
547 // as far as I know, this cannot happen
548 //
549 std::cerr
550 << "error: empty names are not allowed.\n";
551 return 1;
552 }
553 validate_name("name", name);
554
555 int64_t id(-1);
556 std::string value;
557 r = extract_value(name, p.second, id, value);
558 if(r != 0)
559 {
560 return r;
561 }
562
563 h
564 << "extern char const * g_"
565 << f_introducer
566 << '_'
567 << (f_sub_project.empty() ? f_project : f_sub_project)
568 << '_'
569 << name
570 << ";\n";
571
572 cpp << "char const * g_"
573 << f_introducer
574 << '_'
575 << (f_sub_project.empty() ? f_project : f_sub_project)
576 << '_'
577 << name
578 << " = \""
579 << value
580 << "\";\n";
581 }
582
583 if(!f_sub_project.empty())
584 {
585 h << "}\n"; // namespace sub-project
586 cpp << "}\n"; // namespace sub-project
587 }
588 h << "}\n"; // namespace project
589 cpp << "}\n"; // namespace project
590
591 if(f_verbose)
592 {
593 std::cout
594 << "info: save to \""
596 << '/'
597 << f_basename
598 << "{.cpp,.h,_private.h}\".\n";
599 }
600
601 snapdev::file_contents header_public(f_output_path + "/" + f_basename + ".h");
602 header_public.contents(h.str());
603 if(!header_public.write_all())
604 {
605 std::cerr << "error: could not save public header file to \""
606 << header_public.filename()
607 << ".h\".\n";
608 return 1;
609 }
610
611 snapdev::file_contents cpp_file(f_output_path + "/" + f_basename + ".cpp");
612 cpp_file.contents(cpp.str());
613 if(!cpp_file.write_all())
614 {
615 std::cerr << "error: could not save C++ implementation file to \""
616 << cpp_file.filename()
617 << ".cpp\".\n";
618 return 1;
619 }
620
621 return 0;
622}
623
624
625
626
627}
628// noname namespace
629
630
631
632int main(int argc, char * argv[])
633{
634 libexcept::init_report_signal();
635 libexcept::verify_inherited_files();
636
637 try
638 {
639 atomic_names n(argc, argv);
640 return n.run();
641 }
642 catch(advgetopt::getopt_exit const & e)
643 {
644 return e.code();
645 }
646 catch(libexcept::exception_t const & e)
647 {
648 std::cerr
649 << "error: a libexcept exception occurred: \""
650 << e.what()
651 << "\".\n";
652 }
653 catch(std::exception const & e)
654 {
655 std::cerr
656 << "error: a standard exception occurred: \""
657 << e.what()
658 << "\".\n";
659 }
660 catch(...)
661 {
662 std::cerr << "error: an unknown exception occurred.\n";
663 }
664
665 return 1;
666}
667
668
669
670// 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.