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:
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 */
+