From 8d9156660d6958c8d63b1d44692968f1b5d33920 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 19 May 2022 09:05:48 +0200 Subject: [PATCH] version comparisons: stop using locale-dependent isdigit() The docs are not entirely clear what glyphs qualify as digits. The function is supposed to be locale-dependent, but I couldn't get it to return true on any non-ascii digits I tried. But it's better to be safe than sorry, let's use our trivial replacement instead. --- src/fundamental/string-util-fundamental.c | 15 +++++++-------- src/test/test-string-util.c | 9 +++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/fundamental/string-util-fundamental.c b/src/fundamental/string-util-fundamental.c index 57b4d535f3a..feccb822ff7 100644 --- a/src/fundamental/string-util-fundamental.c +++ b/src/fundamental/string-util-fundamental.c @@ -77,19 +77,18 @@ sd_char* endswith_no_case(const sd_char *s, const sd_char *postfix) { return (sd_char*) s + sl - pl; } -#ifdef SD_BOOT -static sd_bool isdigit(sd_char a) { +static sd_bool is_digit(sd_char a) { + /* Locale-independent version of isdigit(). */ return a >= '0' && a <= '9'; } -#endif static sd_bool is_alpha(sd_char a) { - /* Locale independent version of isalpha(). */ + /* Locale-independent version of isalpha(). */ return (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z'); } static sd_bool is_valid_version_char(sd_char a) { - return isdigit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.'); + return is_digit(a) || is_alpha(a) || IN_SET(a, '~', '-', '^', '.'); } sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { @@ -187,7 +186,7 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { b++; } - if (isdigit(*a) || isdigit(*b)) { + if (is_digit(*a) || is_digit(*b)) { /* Skip leading '0', to make 00123 equivalent to 123. */ while (*a == '0') a++; @@ -196,9 +195,9 @@ sd_int strverscmp_improved(const sd_char *a, const sd_char *b) { /* Find the leading numeric segments. One may be an empty string. So, * numeric segments are always newer than alpha segments. */ - for (aa = a; isdigit(*aa); aa++) + for (aa = a; is_digit(*aa); aa++) ; - for (bb = b; isdigit(*bb); bb++) + for (bb = b; is_digit(*bb); bb++) ; /* To compare numeric segments without parsing their values, first compare the diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index d0a4eca3906..1054f9ea31d 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "alloc-util.h" #include "locale-util.h" #include "macro.h" @@ -914,6 +916,13 @@ TEST(strverscmp_improved) { /* invalid characters */ assert_se(strverscmp_improved("123_aa2-67.89", "123aa+2-67.89") == 0); + + /* non-ASCII digits */ + (void) setlocale(LC_NUMERIC, "ar_YE.utf8"); + assert_se(strverscmp_improved("1٠١٢٣٤٥٦٧٨٩", "1") == 0); + + (void) setlocale(LC_NUMERIC, "th_TH.utf8"); + assert_se(strverscmp_improved("1๐๑๒๓๔๕๖๗๘๙", "1") == 0); } #define RPMVERCMP(a, b, c) \ -- 2.47.3