diff --git a/src/gmio_core/error.h b/src/gmio_core/error.h index ce2416e..7bb3cf3 100644 --- a/src/gmio_core/error.h +++ b/src/gmio_core/error.h @@ -31,6 +31,9 @@ enum gmio_error /*! No error occurred, success */ GMIO_ERROR_OK = 0, + /*! Unknown error */ + GMIO_ERROR_UNKNOWN, + /*! Pointer on argument memory block is NULL */ GMIO_ERROR_NULL_MEMBLOCK, @@ -50,8 +53,8 @@ enum gmio_error */ GMIO_ERROR_STDIO, - /*! Unknown error */ - GMIO_ERROR_UNKNOWN + /*! Checking of \c LC_NUMERIC failed(should be "C" or "POSIX") */ + GMIO_ERROR_BAD_LC_NUMERIC }; /*! Returns true if code == GMIO_NO_ERROR */ diff --git a/src/gmio_core/internal/locale_utils.c b/src/gmio_core/internal/locale_utils.c new file mode 100644 index 0000000..bbc8da9 --- /dev/null +++ b/src/gmio_core/internal/locale_utils.c @@ -0,0 +1,62 @@ +/**************************************************************************** +** gmio +** Copyright Fougue (24 Jun. 2016) +** contact@fougue.pro +** +** This software is a reusable library whose purpose is to provide complete +** I/O support for various CAD file formats (eg. STL) +** +** This software is governed by the CeCILL-B license under French law and +** abiding by the rules of distribution of free software. You can use, +** modify and/ or redistribute the software under the terms of the CeCILL-B +** license as circulated by CEA, CNRS and INRIA at the following URL +** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". +****************************************************************************/ + +#include "locale_utils.h" + +#include "../error.h" +#include "string_ascii_utils.h" + +#include +#include +#include + +const char *gmio_lc_numeric() +{ + const char* lcnum = setlocale(LC_NUMERIC, NULL); + return lcnum != NULL ? lcnum : ""; +} + +bool gmio_lc_numeric_is_C() +{ + const char* lc = gmio_lc_numeric(); + return (gmio_ascii_stricmp(lc, "C") == 0 + || gmio_ascii_stricmp(lc, "POSIX") == 0); +} + +bool gmio_check_lc_numeric(int *error) +{ + if (!gmio_lc_numeric_is_C()) + *error = GMIO_ERROR_BAD_LC_NUMERIC; + return gmio_no_error(*error); +} + +static char global_lc_numeric[64] = {0}; + +void gmio_lc_numeric_save() +{ + /* Save LC_NUMERIC + * POSIX specifies that the pointer returned by setlocale(), not just the + * contents of the pointed-to string, may be invalidated by subsequent calls + * to setlocale */ + strncpy(global_lc_numeric, + setlocale(LC_NUMERIC, NULL), + GMIO_ARRAY_SIZE(global_lc_numeric)); +} + +void gmio_lc_numeric_restore() +{ + if (global_lc_numeric[0] != '\0') + setlocale(LC_NUMERIC, global_lc_numeric); +} diff --git a/src/gmio_core/internal/locale_utils.h b/src/gmio_core/internal/locale_utils.h new file mode 100644 index 0000000..7fe7b8d --- /dev/null +++ b/src/gmio_core/internal/locale_utils.h @@ -0,0 +1,28 @@ +/**************************************************************************** +** gmio +** Copyright Fougue (24 Jun. 2016) +** contact@fougue.pro +** +** This software is a reusable library whose purpose is to provide complete +** I/O support for various CAD file formats (eg. STL) +** +** This software is governed by the CeCILL-B license under French law and +** abiding by the rules of distribution of free software. You can use, +** modify and/ or redistribute the software under the terms of the CeCILL-B +** license as circulated by CEA, CNRS and INRIA at the following URL +** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". +****************************************************************************/ + +#include "../global.h" + +const char* gmio_lc_numeric(); + +/*! Returns result of case-insensitive test LC_NUMERIC == "C|POSIX" */ +bool gmio_lc_numeric_is_C(); + +/*! Checks gmio_lc_numeric_is_C(), if false sets \p *error to + * \c GMIO_ERROR_BAD_LC_NUMERIC*/ +bool gmio_check_lc_numeric(int* error); + +void gmio_lc_numeric_save(); +void gmio_lc_numeric_restore(); diff --git a/src/gmio_stl/internal/stla_write.c b/src/gmio_stl/internal/stla_write.c index 73f7cbe..81438d4 100644 --- a/src/gmio_stl/internal/stla_write.c +++ b/src/gmio_stl/internal/stla_write.c @@ -25,6 +25,7 @@ #include "../../gmio_core/internal/helper_memblock.h" #include "../../gmio_core/internal/helper_stream.h" #include "../../gmio_core/internal/helper_task_iface.h" +#include "../../gmio_core/internal/locale_utils.h" #include "../../gmio_core/internal/min_max.h" #include "../../gmio_core/internal/safe_cast.h" @@ -180,6 +181,8 @@ int gmio_stla_write( const struct gmio_stl_write_options* opts) { /* Constants */ + const bool check_lcnum = + opts != NULL ? !opts->stla_dont_check_lc_numeric : true; const struct gmio_task_iface* task = opts != NULL ? &opts->task_iface : NULL; struct gmio_memblock_helper mblock_helper = gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL); @@ -225,6 +228,8 @@ int gmio_stla_write( } /* Check validity of input parameters */ + if (check_lcnum && !gmio_check_lc_numeric(&error)) + goto label_end; if (!gmio_check_memblock_size(&error, mblock, GMIO_STLA_FACET_SIZE_P2)) goto label_end; if (!gmio_stl_check_mesh(&error, mesh)) diff --git a/src/gmio_stl/stl_io_options.h b/src/gmio_stl/stl_io_options.h index df02ee3..36ef92a 100644 --- a/src/gmio_stl/stl_io_options.h +++ b/src/gmio_stl/stl_io_options.h @@ -30,7 +30,12 @@ #include "../gmio_core/task_iface.h" #include "../gmio_core/text_format.h" -/*! Options of function gmio_stl_read() */ +/*! Options of function gmio_stl_read() + * + * Initialising gmio_stl_read_options with \c {0} (or \c {} in C++) is the + * convenient way to set default values(passing \c NULL to gmio_stl_read() has + * the same effect). + */ struct gmio_stl_read_options { /*! Used by the stream to bufferize I/O operations @@ -59,9 +64,27 @@ struct gmio_stl_read_options gmio_streamsize_t (*func_stla_get_streamsize)( struct gmio_stream* stream, struct gmio_memblock* stream_memblock); + + /*! Flag allowing to disable checking of the current locale's numeric + * formatting category + * + * If \c false then \c LC_NUMERIC is checked to be "C" or "POSIX". If check + * fails then the function returns \c GMIO_ERROR_BAD_LC_NUMERIC + * + * This applies only for STL ascii, where it affects text-related standard + * C functions(snprintf(), strtod(), ...) + * + * \c LC_NUMERIC checking is enabled by default. + */ + bool stla_dont_check_lc_numeric; }; -/*! Options of function gmio_stl_write() */ +/*! Options of function gmio_stl_write() + * + * Initialising gmio_stl_write_options with \c {0} (or \c {} in C++) is the + * convenient way to set default values(passing \c NULL to gmio_stl_write() has + * the same effect). + */ struct gmio_stl_write_options { /*! See gmio_stl_read_options::stream_memblock */ @@ -70,6 +93,9 @@ struct gmio_stl_write_options /*! See gmio_stl_read_options::task_iface */ struct gmio_task_iface task_iface; + /*! See gmio_stl_read_options::stla_dont_check_lc_numeric */ + bool stla_dont_check_lc_numeric; + /*! Flag allowing to skip writting of any header/footer data, but just * triangles * diff --git a/src/gmio_stl/stla_read.c b/src/gmio_stl/stla_read.c index 98c5858..6f1ccc3 100644 --- a/src/gmio_stl/stla_read.c +++ b/src/gmio_stl/stla_read.c @@ -26,6 +26,7 @@ #include "../gmio_core/internal/helper_memblock.h" #include "../gmio_core/internal/helper_stream.h" #include "../gmio_core/internal/helper_task_iface.h" +#include "../gmio_core/internal/locale_utils.h" #include "../gmio_core/internal/min_max.h" #include "../gmio_core/internal/safe_cast.h" #include "../gmio_core/internal/stringstream.h" @@ -114,11 +115,17 @@ int gmio_stla_read( struct gmio_stl_mesh_creator* mesh_creator, const struct gmio_stl_read_options* opts) { + const bool check_lcnum = + opts != NULL ? !opts->stla_dont_check_lc_numeric : true; struct gmio_memblock_helper mblock_helper = gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL); struct gmio_memblock* const mblock = &mblock_helper.memblock; char fixed_buffer[GMIO_STLA_READ_STRING_MAX_LEN]; struct gmio_stla_parse_data parse_data; + int error = GMIO_ERROR_OK; + + if (check_lcnum && !gmio_check_lc_numeric(&error)) + goto label_end; parse_data.token = unknown_token; parse_data.error = false; @@ -145,13 +152,14 @@ int gmio_stla_read( parse_solid(&parse_data); - gmio_memblock_helper_release(&mblock_helper); - if (parse_data.error) - return GMIO_STL_ERROR_PARSING; + error = GMIO_STL_ERROR_PARSING; if (parse_data.strstream_cookie.is_stop_requested) - return GMIO_ERROR_TRANSFER_STOPPED; - return GMIO_ERROR_OK; + error = GMIO_ERROR_TRANSFER_STOPPED; + +label_end: + gmio_memblock_helper_release(&mblock_helper); + return error; } diff --git a/tests/main_test_core.c b/tests/main_test_core.c index 1095780..a3a238a 100644 --- a/tests/main_test_core.c +++ b/tests/main_test_core.c @@ -35,9 +35,10 @@ const char* all_tests() UTEST_RUN(test_internal__byte_swap); UTEST_RUN(test_internal__byte_codec); UTEST_RUN(test_internal__fast_atof); + UTEST_RUN(test_internal__locale_utils); UTEST_RUN(test_internal__safe_cast); UTEST_RUN(test_internal__stringstream); - UTEST_RUN(test_internal__string_utils); + UTEST_RUN(test_internal__string_ascii_utils); UTEST_RUN(test_internal__benchmark_gmio_fast_atof); return NULL; diff --git a/tests/main_test_stl.c b/tests/main_test_stl.c index 25f12fe..efdf788 100644 --- a/tests/main_test_stl.c +++ b/tests/main_test_stl.c @@ -46,6 +46,7 @@ const char* all_tests() UTEST_RUN(test_stl_read); UTEST_RUN(test_stl_read_multi_solid); + UTEST_RUN(test_stla_lc_numeric); UTEST_RUN(test_stla_write); UTEST_RUN(test_stlb_read); UTEST_RUN(test_stlb_write); diff --git a/tests/test_core_internal.c b/tests/test_core_internal.c index 6d410a6..5b3936a 100644 --- a/tests/test_core_internal.c +++ b/tests/test_core_internal.c @@ -17,16 +17,19 @@ #include "stream_buffer.h" +#include "../src/gmio_core/error.h" #include "../src/gmio_core/internal/byte_codec.h" #include "../src/gmio_core/internal/byte_swap.h" #include "../src/gmio_core/internal/convert.h" #include "../src/gmio_core/internal/fast_atof.h" +#include "../src/gmio_core/internal/locale_utils.h" #include "../src/gmio_core/internal/numeric_utils.h" #include "../src/gmio_core/internal/safe_cast.h" #include "../src/gmio_core/internal/stringstream.h" #include "../src/gmio_core/internal/stringstream_fast_atof.h" #include "../src/gmio_core/internal/string_ascii_utils.h" +#include #include #include #include @@ -264,7 +267,7 @@ static const char* test_internal__stringstream() return NULL; } -static const char* test_internal__string_utils() +static const char* test_internal__string_ascii_utils() { char c; /* for loop counter */ @@ -328,3 +331,40 @@ static const char* test_internal__string_utils() return NULL; } + +static const char* __tc__test_internal__locale_utils() +{ + const char* lc = setlocale(LC_NUMERIC, ""); + if (lc != NULL + && gmio_ascii_stricmp(lc, "C") != 0 + && gmio_ascii_stricmp(lc, "POSIX") != 0) + { + int error = GMIO_ERROR_OK; + UTEST_ASSERT(!gmio_lc_numeric_is_C()); + UTEST_ASSERT(!gmio_check_lc_numeric(&error)); + UTEST_COMPARE_INT(GMIO_ERROR_BAD_LC_NUMERIC, error); + } + else { + fprintf(stderr, "\nskip: default locale is NULL or already C/POSIX"); + } + + lc = setlocale(LC_NUMERIC, "C"); + if (lc != NULL) { + int error = GMIO_ERROR_OK; + UTEST_ASSERT(gmio_lc_numeric_is_C()); + UTEST_ASSERT(gmio_check_lc_numeric(&error)); + UTEST_COMPARE_INT(GMIO_ERROR_OK, error); + } + + return NULL; +} + +static const char* test_internal__locale_utils() +{ + const char* error_str = NULL; + gmio_lc_numeric_save(); + printf("\ninfo: current locale is \"%s\"", gmio_lc_numeric()); + error_str = __tc__test_internal__locale_utils(); + gmio_lc_numeric_restore(); + return error_str; +} diff --git a/tests/test_stl_io.c b/tests/test_stl_io.c index 468c47a..18e8d6c 100644 --- a/tests/test_stl_io.c +++ b/tests/test_stl_io.c @@ -22,11 +22,13 @@ #include "../src/gmio_core/error.h" #include "../src/gmio_core/internal/min_max.h" #include "../src/gmio_core/internal/string.h" +#include "../src/gmio_core/internal/locale_utils.h" #include "../src/gmio_stl/stl_error.h" #include "../src/gmio_stl/stl_infos.h" #include "../src/gmio_stl/stl_io.h" #include "../src/gmio_stl/stl_io_options.h" +#include #include #include @@ -342,6 +344,39 @@ static const char* test_stl_read_multi_solid() return res; } + +static const char* test_stla_lc_numeric() +{ + struct gmio_stream null_stream = {0}; + const struct gmio_stl_mesh null_mesh = {0}; + struct gmio_stl_mesh_creator null_meshcreator = {0}; + int error[4] = {0}; + + gmio_lc_numeric_save(); + setlocale(LC_NUMERIC, ""); + if (!gmio_lc_numeric_is_C()) { + struct gmio_stl_read_options read_opts = {0}; + struct gmio_stl_write_options write_opts = {0}; + + /* By default, check LC_NUMERIC */ + error[0] = gmio_stla_read(&null_stream, &null_meshcreator, NULL); + error[1] = gmio_stl_write( + GMIO_STL_FORMAT_ASCII, &null_stream, &null_mesh, NULL); + error[2] = gmio_stla_read(&null_stream, &null_meshcreator, &read_opts); + error[3] = gmio_stl_write( + GMIO_STL_FORMAT_ASCII, &null_stream, &null_mesh, &write_opts); + } + else { + fprintf(stderr, "\nskip: default locale is NULL or already C/POSIX"); + } + gmio_lc_numeric_restore(); + for (size_t i = 0; i < GMIO_ARRAY_SIZE(error); ++i) { + UTEST_COMPARE_INT(GMIO_ERROR_BAD_LC_NUMERIC, error[i]); + } + + return NULL; +} + static void generate_stlb_tests_models() { {