city_generator2

Generated conformal 3D meshes representing a city
git clone git://git.meso-star.fr/city_generator2.git
Log | Files | Refs | README | LICENSE

commit 4fc629972614452a6263f10a1eb0d444122fbc03
parent 63bbfc7cf89284bfe7dfa867bcc61f0d9a13a725
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Wed, 30 Nov 2022 15:18:28 +0100

Command line args parsing refactoring

Diffstat:
Mcmake/CMakeLists.txt | 37++++++++++++++++++++++++++-----------
Msrc/cg_args.c | 182++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/cg_args.h | 19++++++++++++++-----
Msrc/cg_city.c | 2+-
Asrc/cg_default.h.in | 23+++++++++++++++++++++++
Msrc/cg_main.c | 51++++++++++++++++++++++++++++++++++++++++++++++-----
Asrc/cg_version.h.in | 24++++++++++++++++++++++++
7 files changed, 282 insertions(+), 56 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -28,12 +28,15 @@ find_package(RSys 0.12.1 REQUIRED) find_package(StarCAD 0.1 REQUIRED) find_package(StarCPR 0.1.3 REQUIRED) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) include(rcmake_runtime) -include_directories(${SCAD_INCLUDE_DIR} ${RSys_INCLUDE_DIR}) +include_directories( + ${StarCAD_INCLUDE_DIR} + ${RSys_INCLUDE_DIR} + ${StarCPR_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR}) if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89") @@ -42,10 +45,10 @@ endif() ################################################################################ # Configure and define targets ################################################################################ -set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) -set(VERSION_PATCH 0) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) +set(CG2_VERSION_MAJOR 0) +set(CG2_VERSION_MINOR 1) +set(CG2_VERSION_PATCH 0) +set(CG2_VERSION ${CG2_VERSION_MAJOR}.${CG2_VERSION_MINOR}.${CG2_VERSION_PATCH}) set(CG2_FILES_SRC cg_main.c @@ -58,13 +61,24 @@ set(CG2_FILES_SRC set(CG2_FILES_INC cg.h - cg_city.h + cg_args.h cg_building.h cg_building_model0.h cg_building_model1.h + cg_city.h + cg_default.h.in cg_ground.h - cg_args.h - cg_parsing.h) + cg_parsing.h + cg_version.h.in) + +set(CG2_ARGS_DEFAULT_VERBOSE_LEVEL "1") +set(CG2_BINARY_EXPORT_DEFAULT "1") + +configure_file(${CG2_SOURCE_DIR}/cg_default.h.in + ${CMAKE_CURRENT_BINARY_DIR}/cg_default.h @ONLY) + +configure_file(${CG2_SOURCE_DIR}/cg_version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/cg_version.h @ONLY) set(CG2_FILES_DOC COPYING README.md) @@ -84,7 +98,7 @@ add_executable(city_generator2 target_link_libraries(city_generator2 RSys StarCAD StarCPR ${MATH_LIB}) set_target_properties(city_generator2 PROPERTIES - VERSION ${VERSION}) + VERSION ${CG2_VERSION}) ################################################################################ # Define output & install directories @@ -94,4 +108,5 @@ install(TARGETS city_generator2 LIBRARY DESTINATION lib RUNTIME DESTINATION bin) install(FILES ${CG2_FILES_DOC} DESTINATION share/doc/city_generator2) - +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cg_version.h + DESTINATION include/city_generator2) diff --git a/src/cg_args.c b/src/cg_args.c @@ -18,52 +18,179 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "cg_args.h" +#include "cg_version.h" +#include "cg_default.h" #include "cg.h" #include <rsys/rsys.h> #include <rsys/logger.h> +#include <rsys/cstr.h> #include <getopt.h> +void +print_version(void) +{ + printf( + "city-generator2 version %i.%i.%i\n", + CG2_VERSION_MAJOR, CG2_VERSION_MINOR, CG2_VERSION_PATCH); +} + +void +short_help(void) +{ + print_version(); + printf("\nUsage:\n" + "city_generator2 [-a] -b <FILE> -c <FILE> [-V verbosity]\n" + "city_generator2 [-h]\n" + "city_generator2 [-v]\n" + ); + printf( + "\nMandatory options\n" + "-----------------\n" + "-b <building_model_file>\n" + " Read a yaml text file that describes the building.\n" + "-c <city_model_file>\n" + " Read a yaml text file that describes the city.\n" + "\nOther options\n" + "-------------\n" + "-h\n" + " Print this help and exit.\n" + "-f <TYPE>\n" + " Set the output format to ASCII or BINARY (default %s).\n" + " Use TYPE=a for ASCII, or TYPE=b for BINARY.\n" + "-v\n" + " Print the software version and exit.\n" + "-V <LEVEL>\n" + " Set the verbosity level (default %i)\n", + (CG2_BINARY_EXPORT_DEFAULT ? "BINARY" : "ASCII"), + CG2_DEFAULT_VERBOSE_LEVEL + ); + printf( + "\nCopyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA.\n" + "Copyright (C) 2022 CNRS.\n" + "Copyright (C) 2022 Sorbonne Université.\n" + "Copyright (C) 2022 Université Paul Sabatier.\n" + "Copyright (C) 2022 |Meso|Star> (contact@meso-star.com).\n" + "city_generator2 is free software released under the GNU GPL license,\n" + "version 3 or later.\n" + "You are free to change or redistribute it under certain conditions\n" + "<http://gnu.org/licenses/gpl.html>.\n"); +} + res_T -parse_args(struct logger* logger, int argc, char** argv, struct args* args) +parse_args + (struct logger* logger, + int argc, + char** argv, + struct args* args) { res_T res = RES_OK; - int opt = 0; - int is_init1 = 0; - int is_init2 = 0; - const char option_list[] = "ab:c:"; + int opt; + int info_provided = 0, b_provided = 0, c_provided = 0; + struct args aaa = ARGS_NULL__; + const char option_list[] = "b:c:f:hvV:"; - str_init(NULL, &args->building_model_file); - is_init1 = 1; - str_init(NULL, &args->city_model_file); - is_init2 = 1; - - /* by default the export is binary */ - args->binary_export = 1; + /* Set default values */ + *args = aaa; + opterr = 0; /* No default error messages */ while((opt = getopt(argc, argv, option_list)) != -1) { switch (opt) { - case 'a': - args->binary_export = 0; - break; + + case '?': /* Unrecognized option */ + { + char* ptr = strchr(option_list, optopt); + res = RES_BAD_ARG; + if(ptr && ptr[1] == ':') { + logger_print(logger, LOG_ERROR, + "Missing argument for option -%c\n", + optopt); + } else { + logger_print(logger, LOG_ERROR, "Invalid option -%c.\n", optopt); + } + res = RES_BAD_ARG; + goto error; + } + case 'b': - str_set(&args->building_model_file, optarg); - break; + if(b_provided) { + logger_print(logger, LOG_ERROR, "Option -%c provided twice.\n", opt); + res = RES_BAD_ARG; + goto error; + } + args->building_model_file = optarg; + b_provided = 1; + break; + + case 'f': + if(0 == strcmp(optarg, "A") || 0 == strcmp(optarg, "a")) + args->binary_export = 0; + else if(0 == strcmp(optarg, "B") || 0 == strcmp(optarg, "b")) + args->binary_export = 1; + else { + logger_print(logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + res = RES_BAD_ARG; + goto error; + } + break; + + case 'h': + info_provided = 1; + args->print_help = 1; + break; + case 'c': - str_set(&args->city_model_file, optarg); - break; + if(c_provided) { + logger_print(logger, LOG_ERROR, "Option -%c provided twice.\n", opt); + res = RES_BAD_ARG; + goto error; + } + args->city_model_file = optarg; + c_provided = 1; + break; + + case 'v': + info_provided = 1; + args->print_version = 1; + break; + + case 'V': + res = cstr_to_int(optarg, &args->verbose); + if(res != RES_OK + || args->verbose < 0 + || args->verbose > 3) + { + if(res == RES_OK) res = RES_BAD_ARG; + logger_print(logger, LOG_ERROR, + "Invalid argument for option -%c: %s\n", + opt, optarg); + res = RES_BAD_ARG; + goto error; + } + break; } } - if (str_is_empty(&args->building_model_file)) { + if(argc > optind) { + int i; + for(i = optind; i < argc; i++) { + logger_print(logger, LOG_ERROR, "Unexpected argument: %s.\n", argv[i]); + } + res = RES_BAD_ARG; + goto error; + } + + if(!b_provided && !info_provided) { ERR(logger_print(logger, LOG_ERROR, "Missing mandatory argument: -b <building_model_file>\n")); res = RES_BAD_ARG; goto error; } - if (str_is_empty(&args->city_model_file)) { + if(!c_provided && !info_provided) { ERR(logger_print(logger, LOG_ERROR, "Missing mandatory argument: -c <city_model_file>\n")); res = RES_BAD_ARG; @@ -73,18 +200,5 @@ parse_args(struct logger* logger, int argc, char** argv, struct args* args) exit: return res; error: - if (is_init1) str_release(&args->building_model_file); - if (is_init2) str_release(&args->city_model_file); goto exit; } - -res_T -release_args(struct args* args) -{ - res_T res = RES_OK; - - str_release(&args->city_model_file); - str_release(&args->building_model_file); - - return res; -} diff --git a/src/cg_args.h b/src/cg_args.h @@ -20,23 +20,32 @@ #ifndef PARSE_ARGS_H #define PARSE_ARGS_H +#include "cg_default.h" + #include <rsys/rsys.h> -#include <rsys/str.h> struct logger; struct args; struct args { - struct str city_model_file; - struct str building_model_file; + char* city_model_file; + char* building_model_file; int binary_export; + int verbose; + int print_help; + int print_version; }; +#define ARGS_NULL__ \ + { NULL, NULL, CG2_BINARY_EXPORT_DEFAULT, CG2_DEFAULT_VERBOSE_LEVEL, 0, 0 } res_T parse_args(struct logger* logger, int argc, char** argv, struct args* args); -res_T -release_args(struct args* args); +void +print_version(void); + +void +short_help(void); #endif /*PARSE_ARGS_H*/ diff --git a/src/cg_city.c b/src/cg_city.c @@ -39,7 +39,7 @@ city_init(struct logger* logger, struct city* city, struct args* args) city->binary_export = args->binary_export; - ERR(txtrdr_file(NULL, str_cget(&args->city_model_file), '#', &reader)); + ERR(txtrdr_file(NULL, args->city_model_file, '#', &reader)); ERR(parse_city(logger, reader, city)); ERR(parse_building_params(logger, &ht_params)); diff --git a/src/cg_default.h.in b/src/cg_default.h.in @@ -0,0 +1,23 @@ +/* Copyright (C) 2018-2022 |Meso|Star> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef CG2_DEFAULT_H +#define CG2_DEFAULT_H + +#define CG2_DEFAULT_VERBOSE_LEVEL @CG2_ARGS_DEFAULT_VERBOSE_LEVEL@ +#define CG2_BINARY_EXPORT_DEFAULT @CG2_BINARY_EXPORT_DEFAULT@ + +#endif /* CG2_DEFAULT_H */ + diff --git a/src/cg_main.c b/src/cg_main.c @@ -23,35 +23,76 @@ #include <rsys/rsys.h> #include <rsys/logger.h> +#include <rsys/mem_allocator.h> char const* model_str [2] = { "model0", "model1" }; +static void +check_memory_allocator(struct mem_allocator* allocator) { + if(MEM_ALLOCATED_SIZE(allocator)) { + char dump[4096]; + MEM_DUMP(allocator, dump, sizeof(dump)/sizeof(char)); + fprintf(stderr, "%s\n", dump); + FATAL("Memory leaks.\n"); + } +} + int main -(int argc, char** argv) + (int argc, char** argv) { int err = EXIT_SUCCESS; res_T res = RES_OK; struct args args; struct city city = CITY_NULL; struct logger logger; + struct mem_allocator allocator; + int logger_initialized = 0, allocator_initialized = 0; + + /* init allocator and logger */ + ERR(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + allocator_initialized = 1; + ERR(logger_init(&allocator, &logger)); + logger_initialized = 1; - ERR(logger_init(NULL, &logger)); + /* Active loggin for args parsing */ logger_set_stream(&logger, LOG_OUTPUT, log_prt_fn, NULL); logger_set_stream(&logger, LOG_WARNING, log_warn_fn, NULL); logger_set_stream(&logger, LOG_ERROR, log_err_fn, NULL); - ERR(parse_args(&logger,argc, argv, &args)); + /* parse command line */ + ERR(parse_args(&logger, argc, argv, &args)); + if(args.print_help) { + short_help(); + goto exit; + } + else if(args.print_version) { + print_version(); + goto exit; + } + + /* Deactivate some loggin according to the -V arg */ + if(args.verbose < 1) + logger_set_stream(&logger, LOG_ERROR, NULL, NULL); + if(args.verbose < 2) + logger_set_stream(&logger, LOG_WARNING, NULL, NULL); + if(args.verbose < 3) + logger_set_stream(&logger, LOG_OUTPUT, NULL, NULL); + ERR(city_init(&logger, &city, &args)); ERR(city_cad_build(&logger, &city)); ERR(city_ground_build(&logger, &city)); exit: city_release(&city); - release_args(&args); - logger_release(&logger); + if(logger_initialized) logger_release(&logger); + if(allocator_initialized) { + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + } return err; error: err = EXIT_FAILURE; diff --git a/src/cg_version.h.in b/src/cg_version.h.in @@ -0,0 +1,24 @@ +/* Copyright (C) 2018-2022 |Meso|Star> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef CG2_APP_VERSION_H +#define CG2_APP_VERSION_H + +#define CG2_VERSION_MAJOR @CG2_VERSION_MAJOR@ +#define CG2_VERSION_MINOR @CG2_VERSION_MINOR@ +#define CG2_VERSION_PATCH @CG2_VERSION_PATCH@ + +#endif /* CG2_APP_VERSION_H */ +