]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
src: replace strto[u][ld] with curlx_str_ parsers
authorDaniel Stenberg <daniel@haxx.se>
Sun, 9 Mar 2025 11:49:24 +0000 (12:49 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 10 Mar 2025 07:09:41 +0000 (08:09 +0100)
- Better error handling (no errno mess), better limit checks.

- Also removed all uses of curlx_strtoofft()

Closes #16634

12 files changed:
lib/strparse.h
lib/strtoofft.h
src/.checksrc
src/terminal.c
src/tool_cb_hdr.c
src/tool_getparam.c
src/tool_main.c
src/tool_operate.c
src/tool_paramhlp.c
src/tool_urlglob.c
src/tool_urlglob.h
src/var.c

index 8b4d1b68e944abb3c7d22ae4b50f5d5fbfd963b4..a3f9d8b0827c0643b34df3cfbf15607b3f9451a5 100644 (file)
@@ -99,4 +99,9 @@ int Curl_str_cspn(const char **linep, struct Curl_str *out, const char *cspn);
 void Curl_str_trimblanks(struct Curl_str *out);
 void Curl_str_passblanks(const char **linep);
 
+#define curlx_str_number(x,y,z) Curl_str_number(x,y,z)
+#define curlx_str_octal(x,y,z) Curl_str_octal(x,y,z)
+#define curlx_str_single(x,y) Curl_str_single(x,y)
+#define curlx_str_passblanks(x) Curl_str_passblanks(x)
+
 #endif /* HEADER_CURL_STRPARSE_H */
index 051ab3064d8d3a53904100601f9c0591148d4e85..05b89fee69952d9e64a0ce6945f48b0608e91173 100644 (file)
@@ -25,6 +25,7 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+#include "strparse.h"
 
 typedef enum {
   CURL_OFFT_OK,    /* parsed fine */
index 272515f78f194b256bcb9750e1386a73585ab529..486670ca99f14276761665615753daf851a50adc 100644 (file)
@@ -1,3 +1,5 @@
 enable STDERR
 banfunc strncpy
 banfunc sscanf
+banfunc strtol
+banfunc strtoul
index cbee7ca386de903cd85719b9dd496a693f5926fe..de9ecb6e19c839e7d3a903854dbd357cad1e5135 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include "terminal.h"
+#include "strtoofft.h"
 
 #include "memdebug.h" /* keep this as LAST include */
 
@@ -47,10 +48,9 @@ unsigned int get_terminal_columns(void)
   unsigned int width = 0;
   char *colp = curl_getenv("COLUMNS");
   if(colp) {
-    char *endptr;
-    long num = strtol(colp, &endptr, 10);
-    if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 20) &&
-       (num < 10000))
+    curl_off_t num;
+    const char *p = colp;
+    if(!curlx_str_number(&p, &num, 10000) && (num > 20))
       width = (unsigned int)num;
     curl_free(colp);
   }
index 5696b414722364a9b6f9389b7b0f711ef150e4de..c88de3c697ba05ec7b7dcdf1d88f2f24fc9d99cc 100644 (file)
@@ -414,13 +414,14 @@ void write_linked_location(CURL *curl, const char *location, size_t loclen,
   const char *loc = location;
   size_t llen = loclen;
   int space_skipped = 0;
-  char *vver = getenv("VTE_VERSION");
+  const char *vver = getenv("VTE_VERSION");
 
   if(vver) {
-    long vvn = strtol(vver, NULL, 10);
-    /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since some
-       of those versions have formatting bugs. (#10428) */
-    if(0 < vvn && vvn <= 4801)
+    curl_off_t num;
+    if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) ||
+       /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since
+          some of those versions have formatting bugs. (#10428) */
+       (num <= 4801))
       goto locout;
   }
 
index e42f263861c27389db9df7cd226db505600be2f7..8d438bcbb9c888ff886dfb198f3d7bf0bc31e105 100644 (file)
@@ -529,18 +529,18 @@ static ParameterError GetSizeParameter(struct GlobalConfig *global,
                                        const char *which,
                                        curl_off_t *value_out)
 {
-  char *unit;
+  const char *unit = arg;
   curl_off_t value;
 
-  if(curlx_strtoofft(arg, &unit, 10, &value)) {
+  if(curlx_str_number(&unit, &value, CURL_OFF_T_MAX)) {
     warnf(global, "invalid number specified for %s", which);
     return PARAM_BAD_USE;
   }
 
   if(!*unit)
-    unit = (char *)"b";
+    unit = "b";
   else if(strlen(unit) > 1)
-    unit = (char *)"w"; /* unsupported */
+    unit = "w"; /* unsupported */
 
   switch(*unit) {
   case 'G':
@@ -973,7 +973,7 @@ static ParameterError set_rate(struct GlobalConfig *global,
      /d == per day (24 hours)
   */
   ParameterError err = PARAM_OK;
-  char *div = strchr(nextarg, '/');
+  const char *div = strchr(nextarg, '/');
   char number[26];
   long denominator;
   long numerator = 60*60*1000; /* default per hour */
@@ -991,22 +991,20 @@ static ParameterError set_rate(struct GlobalConfig *global,
     return PARAM_BAD_USE;
 
   if(div) {
-    char unit = div[1];
     curl_off_t numunits;
-    char *endp;
+    const char *s;
+    div++;
+    s = div;
 
-    if(curlx_strtoofft(&div[1], &endp, 10, &numunits)) {
-      /* if it fails, there is no legit number specified */
-      if(endp == &div[1])
-        /* if endp did not move, accept it as a 1 */
+    if(curlx_str_number(&div, &numunits, CURL_OFF_T_MAX)) {
+      if(s == div)
+        /* if div did not move, accept it as a 1 */
         numunits = 1;
       else
         return PARAM_BAD_USE;
     }
-    else
-      unit = *endp;
 
-    switch(unit) {
+    switch(*div) {
     case 's': /* per second */
       numerator = 1000;
       break;
@@ -1402,48 +1400,42 @@ static ParameterError parse_range(struct GlobalConfig *global,
                                   const char *nextarg)
 {
   ParameterError err = PARAM_OK;
+  curl_off_t value;
+  const char *orig = nextarg;
 
   if(config->use_resume) {
     errorf(global, "--continue-at is mutually exclusive with --range");
     return PARAM_BAD_USE;
   }
-  /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
-     (and will not actually be range by definition). The manpage
-     previously claimed that to be a good way, why this code is added to
-     work-around it. */
-  if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) {
+  if(!curlx_str_number(&nextarg, &value, CURL_OFF_T_MAX) &&
+     curlx_str_single(&nextarg, '-')) {
+    /* Specifying a range WITHOUT A DASH will create an illegal HTTP range
+       (and will not actually be range by definition). The manpage previously
+       claimed that to be a good way, why this code is added to work-around
+       it. */
     char buffer[32];
-    curl_off_t value;
-    if(curlx_strtoofft(nextarg, NULL, 10, &value)) {
-      warnf(global, "unsupported range point");
-      err = PARAM_BAD_USE;
-    }
-    else {
-      warnf(global,
-            "A specified range MUST include at least one dash (-). "
-            "Appending one for you");
-      msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
-                value);
-      Curl_safefree(config->range);
-      config->range = strdup(buffer);
-      if(!config->range)
-        err = PARAM_NO_MEM;
-    }
+    warnf(global, "A specified range MUST include at least one dash (-). "
+          "Appending one for you");
+    msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-",
+              value);
+    free(config->range);
+    config->range = strdup(buffer);
+    if(!config->range)
+      err = PARAM_NO_MEM;
   }
   else {
     /* byte range requested */
-    const char *tmp_range = nextarg;
-    while(*tmp_range) {
-      if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') {
+    while(*nextarg) {
+      if(!ISDIGIT(*nextarg) && *nextarg != '-' && *nextarg != ',') {
         warnf(global, "Invalid character is found in given range. "
               "A specified range MUST have only digits in "
               "\'start\'-\'stop\'. The server's response to this "
               "request is uncertain.");
         break;
       }
-      tmp_range++;
+      nextarg++;
     }
-    err = getstr(&config->range, nextarg, DENY_BLANK);
+    err = getstr(&config->range, orig, DENY_BLANK);
   }
   return err;
 }
index 5910adeb3b72d1def04c9147c99218f8c2901b17..f75007dbb111dad5789efb43f9dca9964e3665dc 100644 (file)
@@ -133,10 +133,10 @@ static void memory_tracking_init(void)
   /* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */
   env = curl_getenv("CURL_MEMLIMIT");
   if(env) {
-    char *endptr;
-    long num = strtol(env, &endptr, 10);
-    if((endptr != env) && (endptr == env + strlen(env)) && (num > 0))
-      curl_dbg_memlimit(num);
+    curl_off_t num;
+    const char *p = env;
+    if(!curlx_str_number(&p, &num, LONG_MAX))
+      curl_dbg_memlimit((long)num);
     curl_free(env);
   }
 }
index 2d9840225874d7477ac187a68266277b10a96288..453146097b6867336082e5a19280f490403c503b 100644 (file)
@@ -930,9 +930,10 @@ static CURLcode config2setopts(struct GlobalConfig *global,
 #ifdef DEBUGBUILD
     char *env = getenv("CURL_BUFFERSIZE");
     if(env) {
-      long size = strtol(env, NULL, 10);
-      if(size)
-        my_setopt(curl, CURLOPT_BUFFERSIZE, size);
+      curl_off_t num;
+      const char *p = env;
+      if(!Curl_str_number(&p, &num, LONG_MAX))
+        my_setopt(curl, CURLOPT_BUFFERSIZE, (long)num);
     }
     else
 #endif
index 76792a0099d9391120e61147c643740fa0567f03..3548e509f6879e472a9961c6fbc9260f60f76589 100644 (file)
@@ -24,7 +24,6 @@
 #include "tool_setup.h"
 
 #include "strcase.h"
-
 #include "curlx.h"
 
 #include "tool_cfgable.h"
@@ -225,17 +224,25 @@ ParameterError file2memory(char **bufp, size_t *size, FILE *file)
  */
 static ParameterError getnum(long *val, const char *str, int base)
 {
+  DEBUGASSERT((base == 8) || (base == 10));
   if(str) {
-    char *endptr = NULL;
-    long num;
-    if(!str[0])
-      return PARAM_BLANK_STRING;
-    CURL_SETERRNO(0);
-    num = strtol(str, &endptr, base);
-    if(errno == ERANGE)
-      return PARAM_NUMBER_TOO_LARGE;
-    if((endptr != str) && (*endptr == '\0')) {
-      *val = num;
+    curl_off_t num;
+    bool is_neg = FALSE;
+    if(base == 10) {
+      is_neg = (*str == '-');
+      if(is_neg)
+        str++;
+      if(curlx_str_number(&str, &num, LONG_MAX))
+        return PARAM_BAD_NUMERIC;
+    }
+    else { /* base == 8 */
+      if(curlx_str_octal(&str, &num, LONG_MAX))
+        return PARAM_BAD_NUMERIC;
+    }
+    if(!curlx_str_single(&str, '\0')) {
+      *val = (long)num;
+      if(is_neg)
+        *val = -*val;
       return PARAM_OK;  /* Ok */
     }
   }
@@ -301,40 +308,6 @@ ParameterError str2unummax(long *val, const char *str, long max)
   return PARAM_OK;
 }
 
-
-/*
- * Parse the string and write the double in the given address. Return PARAM_OK
- * on success, otherwise a parameter specific error enum.
- *
- * The 'max' argument is the maximum value allowed, as the numbers are often
- * multiplied when later used.
- *
- * Since this function gets called with the 'nextarg' pointer from within the
- * getparameter a lot, we must check it for NULL before accessing the str
- * data.
- */
-
-static ParameterError str2double(double *val, const char *str, double max)
-{
-  if(str) {
-    char *endptr;
-    double num;
-    CURL_SETERRNO(0);
-    num = strtod(str, &endptr);
-    if(errno == ERANGE)
-      return PARAM_NUMBER_TOO_LARGE;
-    if(num > max) {
-      /* too large */
-      return PARAM_NUMBER_TOO_LARGE;
-    }
-    if((endptr != str) && (endptr == str + strlen(str))) {
-      *val = num;
-      return PARAM_OK;  /* Ok */
-    }
-  }
-  return PARAM_BAD_NUMERIC; /* badness */
-}
-
 /*
  * Parse the string as seconds with decimals, and write the number of
  * milliseconds that corresponds in the given address. Return PARAM_OK on
@@ -350,14 +323,29 @@ static ParameterError str2double(double *val, const char *str, double max)
 
 ParameterError secs2ms(long *valp, const char *str)
 {
-  double value;
-  ParameterError result = str2double(&value, str, (double)LONG_MAX/1000);
-  if(result != PARAM_OK)
-    return result;
-  if(value < 0)
-    return PARAM_NEGATIVE_NUMERIC;
+  curl_off_t secs;
+  long ms = 0;
+  const unsigned int digs[] = { 1, 10, 100, 1000, 10000, 1000000,
+    1000000, 10000000, 100000000 };
+  if(!str ||
+     curlx_str_number(&str, &secs, CURL_OFF_T_MAX/100))
+    return PARAM_BAD_NUMERIC;
+  if(!curlx_str_single(&str, '.')) {
+    curl_off_t fracs;
+    const char *s = str;
+    size_t len;
+    if(curlx_str_number(&str, &fracs, CURL_OFF_T_MAX))
+      return PARAM_NUMBER_TOO_LARGE;
+    /* how many milliseconds are in fracs ? */
+    len = (str - s);
+    while((len > sizeof(CURL_ARRAYSIZE(digs)) || (fracs > LONG_MAX/100))) {
+      fracs /= 10;
+      len--;
+    }
+    ms = ((long)fracs * 100) / digs[len - 1];
+  }
 
-  *valp = (long)(value*1000);
+  *valp = (long)secs * 1000 + ms;
   return PARAM_OK;
 }
 
@@ -560,29 +548,10 @@ ParameterError check_protocol(const char *str)
  */
 ParameterError str2offset(curl_off_t *val, const char *str)
 {
-  char *endptr;
-  if(str[0] == '-')
-    /* offsets are not negative, this indicates weird input */
-    return PARAM_NEGATIVE_NUMERIC;
-
-#if(SIZEOF_CURL_OFF_T > SIZEOF_LONG)
-  {
-    CURLofft offt = curlx_strtoofft(str, &endptr, 10, val);
-    if(CURL_OFFT_FLOW == offt)
-      return PARAM_NUMBER_TOO_LARGE;
-    else if(CURL_OFFT_INVAL == offt)
-      return PARAM_BAD_NUMERIC;
-  }
-#else
-  CURL_SETERRNO(0);
-  *val = strtol(str, &endptr, 0);
-  if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)
-    return PARAM_NUMBER_TOO_LARGE;
-#endif
-  if((endptr != str) && (endptr == str + strlen(str)))
-    return PARAM_OK;
-
-  return PARAM_BAD_NUMERIC;
+  if(curlx_str_number(&str, val, CURL_OFF_T_MAX) ||
+     curlx_str_single(&str, '\0'))
+    return PARAM_BAD_NUMERIC;
+  return PARAM_OK;
 }
 
 #define MAX_USERPWDLENGTH (100*1024)
index a296e3535baf1b19fa7b536802d9e974f0ab4673..07e1f05e50cc71038df3e495cd9d0b05fe3198c9 100644 (file)
@@ -85,7 +85,7 @@ static int multiply(curl_off_t *amount, curl_off_t with)
   return 0;
 }
 
-static CURLcode glob_set(struct URLGlob *glob, char **patternp,
+static CURLcode glob_set(struct URLGlob *glob, const char **patternp,
                          size_t *posp, curl_off_t *amount,
                          int globindex)
 {
@@ -95,8 +95,8 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp,
   struct URLPattern *pat;
   bool done = FALSE;
   char *buf = glob->glob_buffer;
-  char *pattern = *patternp;
-  char *opattern = pattern;
+  const char *pattern = *patternp;
+  const char *opattern = pattern;
   size_t opos = *posp-1;
 
   pat = &glob->pattern[glob->size];
@@ -180,7 +180,7 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp,
   return CURLE_OK;
 }
 
-static CURLcode glob_range(struct URLGlob *glob, char **patternp,
+static CURLcode glob_range(struct URLGlob *glob, const char **patternp,
                            size_t *posp, curl_off_t *amount,
                            int globindex)
 {
@@ -191,8 +191,8 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp,
      expression is checked for well-formedness and collected until the next ']'
   */
   struct URLPattern *pat;
-  char *pattern = *patternp;
-  char *c;
+  const char *pattern = *patternp;
+  const char *c;
 
   pat = &glob->pattern[glob->size];
   pat->globindex = globindex;
@@ -214,13 +214,13 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp,
       pmatch = TRUE;
 
       if(end_c == ':') {
-        char *endp;
-        CURL_SETERRNO(0);
-        step = strtoul(&pattern[4], &endp, 10);
-        if(errno || &pattern[4] == endp || *endp != ']')
+        curl_off_t num;
+        const char *p = &pattern[4];
+        if(curlx_str_number(&p, &num, 256) || curlx_str_single(&p, ']'))
           step = 0;
         else
-          pattern = endp + 1;
+          step = (unsigned long)num;
+        pattern = p;
       }
       else if(end_c != ']')
         /* then this is wrong */
@@ -232,7 +232,7 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp,
 
     *posp += (pattern - *patternp);
 
-    if(!pmatch || !step || step > (unsigned)INT_MAX ||
+    if(!pmatch || !step ||
        (min_c == max_c && step != 1) ||
        (min_c != max_c && (min_c > max_c || step > (unsigned)(max_c - min_c) ||
                            (max_c - min_c) > ('z' - 'a'))))
@@ -251,10 +251,10 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp,
   }
   else if(ISDIGIT(*pattern)) {
     /* numeric range detected */
-    unsigned long min_n;
+    unsigned long min_n = 0;
     unsigned long max_n = 0;
     unsigned long step_n = 0;
-    char *endp;
+    curl_off_t num;
 
     pat->type = UPTNumRange;
     pat->content.NumRange.padlength = 0;
@@ -269,48 +269,27 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp,
       }
     }
 
-    CURL_SETERRNO(0);
-    min_n = strtoul(pattern, &endp, 10);
-    if(errno || (endp == pattern))
-      endp = NULL;
-    else {
-      if(*endp != '-')
-        endp = NULL;
-      else {
-        pattern = endp + 1;
-        while(ISBLANK(*pattern))
-          pattern++;
-        if(!ISDIGIT(*pattern)) {
-          endp = NULL;
-          goto fail;
-        }
-        CURL_SETERRNO(0);
-        max_n = strtoul(pattern, &endp, 10);
-        if(errno)
-          /* overflow */
-          endp = NULL;
-        else if(*endp == ':') {
-          pattern = endp + 1;
-          CURL_SETERRNO(0);
-          step_n = strtoul(pattern, &endp, 10);
-          if(errno)
-            /* over/underflow situation */
-            endp = NULL;
-        }
-        else
-          step_n = 1;
-        if(endp && (*endp == ']')) {
-          pattern = endp + 1;
+    if(!curlx_str_number(&pattern, &num, CURL_OFF_T_MAX)) {
+      min_n = (unsigned long)num;
+      if(!curlx_str_single(&pattern, '-')) {
+        curlx_str_passblanks(&pattern);
+        if(!curlx_str_number(&pattern, &num, CURL_OFF_T_MAX)) {
+          max_n = (unsigned long)num;
+          if(!curlx_str_single(&pattern, ']'))
+            step_n = 1;
+          else if(!curlx_str_single(&pattern, ':') &&
+                  !curlx_str_number(&pattern, &num, CURL_OFF_T_MAX) &&
+                  !curlx_str_single(&pattern, ']')) {
+            step_n = (unsigned long)num;
+          }
+          /* else bad syntax */
         }
-        else
-          endp = NULL;
       }
     }
 
-fail:
     *posp += (pattern - *patternp);
 
-    if(!endp || !step_n ||
+    if(!step_n ||
        (min_n == max_n && step_n != 1) ||
        (min_n != max_n && (min_n > max_n || step_n > (max_n - min_n))))
       /* the pattern is not well-formed */
@@ -371,7 +350,7 @@ static bool peek_ipv6(const char *str, size_t *skip)
   return rc ? FALSE : TRUE;
 }
 
-static CURLcode glob_parse(struct URLGlob *glob, char *pattern,
+static CURLcode glob_parse(struct URLGlob *glob, const char *pattern,
                            size_t pos, curl_off_t *amount)
 {
   /* processes a literal string component of a URL
@@ -626,10 +605,11 @@ CURLcode glob_next_url(char **globbed, struct URLGlob *glob)
 
 #define MAX_OUTPUT_GLOB_LENGTH (10*1024)
 
-CURLcode glob_match_url(char **result, char *filename, struct URLGlob *glob)
+CURLcode glob_match_url(char **result, const char *filename,
+                        struct URLGlob *glob)
 {
   char numbuf[18];
-  char *appendthis = (char *)"";
+  const char *appendthis = (char *)"";
   size_t appendlen = 0;
   struct curlx_dynbuf dyn;
 
@@ -642,11 +622,11 @@ CURLcode glob_match_url(char **result, char *filename, struct URLGlob *glob)
 
   while(*filename) {
     if(*filename == '#' && ISDIGIT(filename[1])) {
-      char *ptr = filename;
-      unsigned long num = strtoul(&filename[1], &filename, 10);
+      const char *ptr = filename;
+      curl_off_t num;
       struct URLPattern *pat = NULL;
-
-      if(num && (num < glob->size)) {
+      filename++;
+      if(!curlx_str_number(&filename, &num, glob->size) && num) {
         unsigned long i;
         num--; /* make it zero based */
         /* find the correct glob entry */
index e31c3d87e52208d4424c2aacbf194de5bfd489f3..6fcd0db498bff0679ed99b3c37e2f15d19163fdb 100644 (file)
@@ -72,7 +72,7 @@ struct URLGlob {
 
 CURLcode glob_url(struct URLGlob**, char *, curl_off_t *, FILE *);
 CURLcode glob_next_url(char **, struct URLGlob *);
-CURLcode glob_match_url(char **, char *, struct URLGlob *);
+CURLcode glob_match_url(char **, const char *, struct URLGlob *);
 void glob_cleanup(struct URLGlob **glob);
 
 #endif /* HEADER_CURL_TOOL_URLGLOB_H */
index 1eab942b66e8c6cd24292a0479e51903c1b27dee..7d7fd513f0d98712ea1f102f4be61cb0c4fb1713 100644 (file)
--- a/src/var.c
+++ b/src/var.c
@@ -448,25 +448,19 @@ ParameterError setvariable(struct GlobalConfig *global,
       clen = strlen(ge);
     }
   }
-  if(*line == '[') {
+  if(*line == '[' && ISDIGIT(line[1])) {
     /* is there a byte range specified? [num-num] */
-    if(ISDIGIT(line[1])) {
-      char *endp;
-      if(curlx_strtoofft(&line[1], &endp, 10, &startoffset) || (*endp != '-'))
-        return PARAM_VAR_SYNTAX;
-      else {
-        char *p = endp + 1; /* pass the '-' */
-        if(*p != ']') {
-          if(curlx_strtoofft(p, &endp, 10, &endoffset) || (*endp != ']'))
-            return PARAM_VAR_SYNTAX;
-          line = &endp[1];  /* pass the ']' */
-        }
-        else
-          line = &p[1]; /* pass the ']' */
-      }
-      if(startoffset > endoffset)
+    line++;
+    if(curlx_str_number(&line, &startoffset, CURL_OFF_T_MAX) ||
+       curlx_str_single(&line, '-'))
+      return PARAM_VAR_SYNTAX;
+    if(curlx_str_single(&line, ']')) {
+      if(curlx_str_number(&line, &endoffset, CURL_OFF_T_MAX) ||
+         curlx_str_single(&line, ']'))
         return PARAM_VAR_SYNTAX;
     }
+    if(startoffset > endoffset)
+      return PARAM_VAR_SYNTAX;
   }
   if(content)
     ;