]> Savannah Git Hosting - gnulib.git/commitdiff
parse-duration: Work around an strtoul() misfeature.
authorBruno Haible <bruno@clisp.org>
Fri, 21 Mar 2025 12:15:55 +0000 (13:15 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 21 Mar 2025 12:15:55 +0000 (13:15 +0100)
* lib/parse-duration.c (str_const_to_ul): Reject a + or - sign between
the optional whitespace and the digits.
* tests/test-parse-duration.sh: Add some tests with expected failure.
* tests/test-parse-duration.c (main): Fix usage message.

ChangeLog
lib/parse-duration.c
tests/test-parse-duration.c
tests/test-parse-duration.sh

index 8111c9df7c1a0f23fcd224a255cfb30b9cb618af..78fc19c93ebae67c3ec307fc62c5fbfcc168e853 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-03-21  Bruno Haible  <bruno@clisp.org>
+
+       parse-duration: Work around an strtoul() misfeature.
+       * lib/parse-duration.c (str_const_to_ul): Reject a + or - sign between
+       the optional whitespace and the digits.
+       * tests/test-parse-duration.sh: Add some tests with expected failure.
+       * tests/test-parse-duration.c (main): Fix usage message.
+
 2025-03-21  Bruno Haible  <bruno@clisp.org>
 
        mountlist: Add specification comment in .h file.
index 6c5d7e0251293cd26016e134000c4924e47fdc1c..1fe3f845b3c56f9da4c9da1284f301e160bc318d 100644 (file)
@@ -56,11 +56,25 @@ typedef enum {
 #undef  MAX_DURATION
 #define MAX_DURATION    TYPE_MAXIMUM(time_t)
 
-/* Wrapper around strtoul that does not require a cast.  */
+/* Wrapper around strtoul that does not allow a sign.  */
 static unsigned long
 str_const_to_ul (cch_t * str, cch_t ** ppz, int base)
 {
-  return strtoul (str, (char **)ppz, base);
+  cch_t * orig_str = str;
+  while (isspace ((unsigned char) *str))
+    str++;
+  if (isdigit ((unsigned char) *str))
+    {
+      unsigned long ret = strtoul (str, (char **)ppz, base);
+      if (*ppz == str)
+        *ppz = orig_str;
+      return ret;
+    }
+  else
+    {
+      *ppz = orig_str;
+      return 0;
+    }
 }
 
 /* Wrapper around strtol that does not require a cast.  */
index 36c000856faa5e3405536a78d00b67be85a8b84c..29ac7732ea84df974923b7f212574516ae4d612d 100644 (file)
@@ -30,7 +30,7 @@ main (int argc, char *argv[])
 {
   if (--argc <= 0)
     {
-      fprintf (stderr, "USAGE: %s <time-spec> [...]", argv[0]);
+      fprintf (stderr, "USAGE: %s <time-spec> [...]\n", argv[0]);
       return 1;
     }
 
index cf45913261b4f898e6f9819f7d187449cb865830..5b4e01bb216b16133785be29e0411cf16f82cbdb 100755 (executable)
@@ -47,6 +47,7 @@ func_tmpdir
 trap 'rm -rf "${tmp}"' EXIT
 tmpf="${tmp}/tests.txt"
 
+# Tests where we expect success.
 cat > "${tmpf}" <<- _EOF_
        1 Y 2 M 3 W 4 d 5 h 6 m 7 s
        P 00010225 T 05:06:07
@@ -65,3 +66,26 @@ do
     test $v -eq 38898367 || die $v is not 38898367
 done
 exec 3>&-
+
+# Tests where we expect failure.
+cat > "${tmpf}" <<- _EOF_
+       T-00302
+       T-0:3:2
+       T-0 H 3 M 2 S
+       P+04 Y 03 M 02 D
+       P04 Y +03 M 02 D
+       P04 Y 03 M +02 D
+       _EOF_
+
+fail=0
+exec 3< "${tmpf}"
+while read line <&3
+do
+    if ${CHECKER} ${exe} "${line}"; then
+        echo "Succeeded: ${exe} '${line}'" 1>&2
+        fail=1
+    fi
+done
+exec 3>&-
+
+exit $fail