diff --git a/src/eccodes_prototypes.h b/src/eccodes_prototypes.h index 43ff0711f..40045b65d 100644 --- a/src/eccodes_prototypes.h +++ b/src/eccodes_prototypes.h @@ -1456,7 +1456,7 @@ void string_rtrim(char* s); void string_lrtrim(char** x, int do_left, int do_right); const char* extract_filename(const char* filepath); char** string_split(char* inputString, const char* delimiter); -int string_to_long(const char* input, long* output); +int string_to_long(const char* input, long* output, int strict); int string_ends_with(const char* str1, const char* str2); int string_count_char(const char* str, char c); const char* codes_get_product_name(ProductKind product); diff --git a/src/grib_accessor_class_codetable.cc b/src/grib_accessor_class_codetable.cc index e823a8659..d623f09e1 100644 --- a/src/grib_accessor_class_codetable.cc +++ b/src/grib_accessor_class_codetable.cc @@ -642,7 +642,7 @@ static int pack_string(grib_accessor* a, const char* buffer, size_t* len) { long lValue = 0; Assert(buffer); - if (is_number(buffer) && string_to_long(buffer, &lValue) == GRIB_SUCCESS) { + if (is_number(buffer) && string_to_long(buffer, &lValue, 1) == GRIB_SUCCESS) { // ECC-1654: If value is a pure number, just pack as long size_t l = 1; return grib_pack_long(a, &lValue, &l); diff --git a/src/grib_accessor_class_concept.cc b/src/grib_accessor_class_concept.cc index 20b76059d..28c09006d 100644 --- a/src/grib_accessor_class_concept.cc +++ b/src/grib_accessor_class_concept.cc @@ -382,7 +382,7 @@ static int grib_concept_apply(grib_accessor* a, const char* name) grib_context_log(h->context, GRIB_LOG_ERROR, "concept: input handle edition=%ld, centre=%s", editionNumber, centre_s); } if (strcmp(act->name, "paramId") == 0) { - if (string_to_long(name, &dummy) == GRIB_SUCCESS) { + if (string_to_long(name, &dummy, 1) == GRIB_SUCCESS) { // The paramId value is an integer. Show them the param DB grib_context_log(h->context, GRIB_LOG_ERROR, "Please check the Parameter Database 'https://codes.ecmwf.int/grib/param-db/?id=%s'", name); diff --git a/src/grib_accessor_class_long.cc b/src/grib_accessor_class_long.cc index 6caec017a..c939189a5 100644 --- a/src/grib_accessor_class_long.cc +++ b/src/grib_accessor_class_long.cc @@ -257,7 +257,7 @@ static int pack_string(grib_accessor* a, const char* val, size_t* len) // return pack_missing(a); //} - if (string_to_long(val, &v) != GRIB_SUCCESS) { + if (string_to_long(val, &v, 1) != GRIB_SUCCESS) { grib_context_log(a->context, GRIB_LOG_ERROR, "Trying to pack \"%s\" as long. String cannot be converted to an integer", val); return GRIB_WRONG_TYPE; diff --git a/src/grib_expression_class_functor.cc b/src/grib_expression_class_functor.cc index 99ab0ad36..c7ffc072e 100644 --- a/src/grib_expression_class_functor.cc +++ b/src/grib_expression_class_functor.cc @@ -144,7 +144,7 @@ static int evaluate_long(grib_expression* g, grib_handle* h, long* lres) char* env = getenv(p); if (env) { long lval = 0; - if (string_to_long(env, &lval) == GRIB_SUCCESS) { + if (string_to_long(env, &lval, 1) == GRIB_SUCCESS) { *lres = lval; return GRIB_SUCCESS; } diff --git a/src/grib_parse_utils.cc b/src/grib_parse_utils.cc index e7c752f09..bbebf7df6 100644 --- a/src/grib_parse_utils.cc +++ b/src/grib_parse_utils.cc @@ -473,7 +473,8 @@ int grib_recompose_print(grib_handle* h, grib_accessor* observer, const char* un break; case '!': pp = (char*)uname; - if (string_to_long(uname + i + 1, &numcols) == GRIB_SUCCESS) { + // Turn off strict as the input string will have a final ']' suffix + if (string_to_long(uname + i + 1, &numcols, /*strict=*/0) == GRIB_SUCCESS) { maxcols = (int)numcols; } else { diff --git a/src/string_util.cc b/src/string_util.cc index 65583f93e..831a4a4c4 100644 --- a/src/string_util.cc +++ b/src/string_util.cc @@ -124,8 +124,10 @@ char** string_split(char* inputString, const char* delimiter) return result; } -/* Return GRIB_SUCCESS if can convert input to an integer, GRIB_INVALID_ARGUMENT otherwise */ -int string_to_long(const char* input, long* output) +// Return GRIB_SUCCESS if we can convert 'input' to an integer, GRIB_INVALID_ARGUMENT otherwise. +// If 'strict' is 1 then disallow characters at the end which are not valid digits. +// E.g., in strict mode, "4i" will be rejected. Otherwise it will convert it to 4 +int string_to_long(const char* input, long* output, int strict) { const int base = 10; char* endptr; @@ -138,11 +140,15 @@ int string_to_long(const char* input, long* output) val = strtol(input, &endptr, base); if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || (errno != 0 && val == 0)) { - /*perror("strtol");*/ + // perror("strtol"); return GRIB_INVALID_ARGUMENT; } if (endptr == input) { - /*fprintf(stderr, "No digits were found. EXIT_FAILURE\n");*/ + // fprintf(stderr, "No digits were found\n"); + return GRIB_INVALID_ARGUMENT; + } + if (strict && *endptr != 0) { + // fprintf(stderr, "Left over characters at the end; not a pure number\n"); return GRIB_INVALID_ARGUMENT; } *output = val; diff --git a/tests/bufr_check_descriptors.cc b/tests/bufr_check_descriptors.cc index 76cec0f1f..eec3a14eb 100644 --- a/tests/bufr_check_descriptors.cc +++ b/tests/bufr_check_descriptors.cc @@ -68,7 +68,7 @@ int main(int argc, char** argv) return 1; } str_code = list[0]; - if (string_to_long(str_code, &lValue) != GRIB_SUCCESS) { + if (string_to_long(str_code, &lValue, 1) != GRIB_SUCCESS) { fprintf(stderr, "Error on line %zu: descriptor code '%s' (column 1) is not numeric.\n", line_number, str_code); return 1; @@ -103,17 +103,18 @@ int main(int argc, char** argv) str_scale = list[5]; str_ref = list[6]; str_width = list[7]; - if (string_to_long(str_scale, &lValue) != GRIB_SUCCESS) { + if (string_to_long(str_scale, &lValue, 1) != GRIB_SUCCESS) { fprintf(stderr, "Error on line %zu: descriptor scale '%s' (column 6) is not numeric.\n", line_number, str_scale); return 1; } - if (string_to_long(str_ref, &lValue) != GRIB_SUCCESS) { + if (string_to_long(str_ref, &lValue, 1) != GRIB_SUCCESS) { fprintf(stderr, "Error on line %zu: descriptor reference '%s' (column 7) is not numeric.\n", line_number, str_ref); return 1; } - if (string_to_long(str_width, &lValue) != GRIB_SUCCESS) { + // The final width column can have spaces etc at the end. So turn off strict mode + if (string_to_long(str_width, &lValue, /*strict=*/0) != GRIB_SUCCESS) { fprintf(stderr, "Error on line %zu: descriptor width '%s' (column 8) is not numeric.\n", line_number, str_width); return 1; diff --git a/tests/unit_tests.cc b/tests/unit_tests.cc index 6ea0b2d15..d160b63ed 100644 --- a/tests/unit_tests.cc +++ b/tests/unit_tests.cc @@ -319,6 +319,31 @@ static void test_string_ends_with() Assert( string_ends_with("GRIB2.tmpl", " ") == 0 ); } +static void test_string_to_long() +{ + printf("Testing: test_string_to_long...\n"); + long lVal = 0; + Assert( string_to_long("0", &lVal, 1) == GRIB_SUCCESS); + Assert( lVal == 0 ); + + Assert( string_to_long("42", &lVal, 1) == GRIB_SUCCESS); + Assert( lVal == 42 ); + + Assert( string_to_long("-1", &lVal, 1) == GRIB_SUCCESS); + Assert( lVal == -1 ); + Assert( string_to_long("+999", &lVal, 1) == GRIB_SUCCESS); + Assert( lVal == 999 ); + + Assert( string_to_long("15MB", &lVal, 0) == GRIB_SUCCESS); + Assert( lVal == 15 ); + + // illegal cases + Assert( string_to_long("4000000000000000000000", &lVal, 1) == GRIB_INVALID_ARGUMENT); + Assert( string_to_long("XY", &lVal, 1) == GRIB_INVALID_ARGUMENT); + Assert( string_to_long("A6", &lVal, 1) == GRIB_INVALID_ARGUMENT); + Assert( string_to_long("5K", &lVal, 1) == GRIB_INVALID_ARGUMENT); +} + static void test_gribex_mode() { grib_context* c = grib_context_get_default(); @@ -497,6 +522,7 @@ int main(int argc, char** argv) test_trimming(); test_string_ends_with(); + test_string_to_long(); test_get_git_sha1(); test_get_build_date(); diff --git a/tools/grib_repair.cc b/tools/grib_repair.cc index 00737e3e8..484bfe69f 100644 --- a/tools/grib_repair.cc +++ b/tools/grib_repair.cc @@ -64,7 +64,7 @@ int main(int argc, char** argv) sMaxNumMessages = getenv(ENV_VAR); if (sMaxNumMessages) { long lmax = 0; - if (string_to_long(sMaxNumMessages, &lmax) == GRIB_SUCCESS) { + if (string_to_long(sMaxNumMessages, &lmax, 1) == GRIB_SUCCESS) { MAX_NUM_MESSAGES = lmax; } }