diff --git a/src/include/OpenImageIO/strutil.h b/src/include/OpenImageIO/strutil.h index 55b94d8458..30a63e0075 100644 --- a/src/include/OpenImageIO/strutil.h +++ b/src/include/OpenImageIO/strutil.h @@ -1155,6 +1155,19 @@ OIIO_UTIL_API size_t edit_distance(string_view a, string_view b, EditDistMetric metric = EditDistMetric::Levenshtein); + +/// Evaluate a string as a boolean value using the following heuristic: +/// - If the string is a valid numeric value (represents an integer or +/// floating point value), return true if it's non-zero, false if it's +/// zero. +/// - If the string is one of "false", "no", or "off", or if it contains +/// only whitespace, return false. +/// - All other non-empty strings return true. +/// The comparisons are case-insensitive and ignore leading and trailing +/// whitespace. +OIIO_UTIL_API bool +eval_as_bool(string_view value); + } // namespace Strutil diff --git a/src/libutil/strutil.cpp b/src/libutil/strutil.cpp index 1f8d594214..4d0d89c9b9 100644 --- a/src/libutil/strutil.cpp +++ b/src/libutil/strutil.cpp @@ -1861,4 +1861,30 @@ Strutil::edit_distance(string_view a, string_view b, EditDistMetric metric) return levenshtein_distance(a, b); } + + +/// Interpret a string as a boolean value using the following heuristic: +/// - If the string is a valid numeric value (represents an integer or +/// floating point value), return true if it's non-zero, false if it's +/// zero. +/// - If the string is one of "false", "no", or "off", or if it contains +/// only whitespace, return false. +/// - All other non-empty strings return true. +/// The comparisons are case-insensitive and ignore leading and trailing +/// whitespace. +bool +Strutil::eval_as_bool(string_view value) +{ + Strutil::trim_whitespace(value); + if (Strutil::string_is_int(value)) { + return Strutil::stoi(value) != 0; + } else if (Strutil::string_is_float(value)) { + return Strutil::stof(value) != 0.0f; + } else { + return !(value.empty() || Strutil::iequals(value, "false") + || Strutil::iequals(value, "no") + || Strutil::iequals(value, "off")); + } +} + OIIO_NAMESPACE_END diff --git a/src/libutil/strutil_test.cpp b/src/libutil/strutil_test.cpp index 9bd4791ec2..7ae32b2d14 100644 --- a/src/libutil/strutil_test.cpp +++ b/src/libutil/strutil_test.cpp @@ -1673,6 +1673,53 @@ test_base64_encode() +void +test_eval_as_bool() +{ + using namespace Strutil; + print("testing eval_as_bool()\n"); + + // Test cases for integer values + OIIO_CHECK_EQUAL(eval_as_bool("0"), false); + OIIO_CHECK_EQUAL(eval_as_bool("1"), true); + OIIO_CHECK_EQUAL(eval_as_bool("-1"), true); + OIIO_CHECK_EQUAL(eval_as_bool("10"), true); + OIIO_CHECK_EQUAL(eval_as_bool("-10"), true); + + // Test cases for floating-point values + OIIO_CHECK_EQUAL(eval_as_bool("0.0"), false); + OIIO_CHECK_EQUAL(eval_as_bool("1.0"), true); + OIIO_CHECK_EQUAL(eval_as_bool("-1.0"), true); + OIIO_CHECK_EQUAL(eval_as_bool("10.5"), true); + OIIO_CHECK_EQUAL(eval_as_bool("-10.5"), true); + + // Test cases for string values + OIIO_CHECK_EQUAL(eval_as_bool(""), false); + OIIO_CHECK_EQUAL(eval_as_bool("false"), false); + OIIO_CHECK_EQUAL(eval_as_bool("FALSE"), false); + OIIO_CHECK_EQUAL(eval_as_bool("no"), false); + OIIO_CHECK_EQUAL(eval_as_bool("NO"), false); + OIIO_CHECK_EQUAL(eval_as_bool("off"), false); + OIIO_CHECK_EQUAL(eval_as_bool("OFF"), false); + + OIIO_CHECK_EQUAL(eval_as_bool("true"), true); + OIIO_CHECK_EQUAL(eval_as_bool("TRUE"), true); + OIIO_CHECK_EQUAL(eval_as_bool("yes"), true); + OIIO_CHECK_EQUAL(eval_as_bool("YES"), true); + OIIO_CHECK_EQUAL(eval_as_bool("on"), true); + OIIO_CHECK_EQUAL(eval_as_bool("ON"), true); + OIIO_CHECK_EQUAL(eval_as_bool("OpenImageIO"), true); + + // Test whitespace, case insensitivity, other tricky cases + OIIO_CHECK_EQUAL(eval_as_bool(" "), false); + OIIO_CHECK_EQUAL(eval_as_bool("\t \n"), false); + OIIO_CHECK_EQUAL(eval_as_bool(" faLsE"), false); + OIIO_CHECK_EQUAL(eval_as_bool("\tOFf"), false); + OIIO_CHECK_EQUAL(eval_as_bool("off OpenImageIO"), true); +} + + + int main(int /*argc*/, char* /*argv*/[]) { @@ -1708,6 +1755,7 @@ main(int /*argc*/, char* /*argv*/[]) test_datetime(); test_edit_distance(); test_base64_encode(); + test_eval_as_bool(); Strutil::debug("debug message\n"); diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp index 57e3436220..7728eaf86b 100644 --- a/src/oiiotool/oiiotool.cpp +++ b/src/oiiotool/oiiotool.cpp @@ -1121,7 +1121,7 @@ control_if(Oiiotool& ot, cspan argv) if (ot.running()) { // string_view command = ot.express(argv[0]); string_view value = ot.express(argv[1]); - cond = eval_as_bool(value); + cond = Strutil::eval_as_bool(value); // Strutil::print("while: val='{}' cond={}\n", value, cond); } else { // If not running in the outer scope, don't even evaluate the @@ -1184,7 +1184,7 @@ control_while(Oiiotool& ot, cspan argv) if (ot.running()) { // string_view command = ot.express(argv[0]); string_view value = ot.express(argv[1]); - cond = eval_as_bool(value); + cond = Strutil::eval_as_bool(value); // Strutil::print("while: val='{}' cond={}\n", value, cond); } else { // If not running in the outer scope, don't even evaluate the