]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
strparse: provide access functions
authorDaniel Stenberg <daniel@haxx.se>
Wed, 19 Feb 2025 07:49:54 +0000 (08:49 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 19 Feb 2025 11:17:32 +0000 (12:17 +0100)
To access the string and the length without having to directly use the
struct field names. Gives more freedom, flexbility and keeps
implementation specifics out of users' code.

Closes #16386

docs/internals/STRPARSE.md
lib/altsvc.c
lib/cookie.c
lib/curl_trc.c
lib/hostip.c
lib/hsts.c
lib/http_aws_sigv4.c
lib/strparse.c
lib/strparse.h

index 0784bb9345407b2123c07430ebb58e686fed60a2..2ae92595e61ea64463625842ae4f13e1a32a2b3d 100644 (file)
@@ -30,6 +30,19 @@ struct Curl_str {
 };
 ~~~
 
+Access the struct fields with `Curl_str()` for the pointer and `Curl_strlen()`
+for the length rather than using the struct fields directly.
+
+## `Curl_str_init`
+
+~~~c
+void Curl_str_init(struct Curl_str *out)
+~~~
+
+This initiates a string struct. The parser functions that store info in
+strings always init the string themselves, so this stand-alone use is often
+not necessary.
+
 ## `Curl_str_word`
 
 ~~~c
index c3aaf1dad6d8367494162ca920b8ca5f513bb8d8..58416ea994f6151dc6d47170927ee35e0df6e17b 100644 (file)
@@ -138,12 +138,14 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost,
                                     size_t srcport,
                                     size_t dstport)
 {
-  enum alpnid dstalpnid = Curl_alpn2alpnid(dstalpn->str, dstalpn->len);
-  enum alpnid srcalpnid = Curl_alpn2alpnid(srcalpn->str, srcalpn->len);
+  enum alpnid dstalpnid =
+    Curl_alpn2alpnid(Curl_str(dstalpn), Curl_strlen(dstalpn));
+  enum alpnid srcalpnid =
+    Curl_alpn2alpnid(Curl_str(srcalpn), Curl_strlen(srcalpn));
   if(!srcalpnid || !dstalpnid)
     return NULL;
-  return altsvc_createid(srchost->str, srchost->len,
-                         dsthost->str, dsthost->len,
+  return altsvc_createid(Curl_str(srchost), Curl_strlen(srchost),
+                         Curl_str(dsthost), Curl_strlen(dsthost),
                          srcalpnid, dstalpnid,
                          srcport, dstport);
 }
@@ -190,8 +192,8 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
 
     /* The date parser works on a null terminated string. The maximum length
        is upheld by Curl_str_quotedword(). */
-    memcpy(dbuf, date.str, date.len);
-    dbuf[date.len] = 0;
+    memcpy(dbuf, Curl_str(&date), Curl_strlen(&date));
+    dbuf[Curl_strlen(&date)] = 0;
     expires = Curl_getdate_capped(dbuf);
     as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
                        (size_t)srcport, (size_t)dstport);
index 22a7681752bf24e5dcbaa24871737e04246eabaa..9ef7375327683da63af7db10a8bc4a882bef07f9 100644 (file)
@@ -507,15 +507,14 @@ parse_cookie_header(struct Curl_easy *data,
           Curl_str_trimblanks(&val);
 
           /* Reject cookies with a TAB inside the value */
-          if(memchr(val.str, '\t', val.len)) {
+          if(memchr(Curl_str(&val), '\t', Curl_strlen(&val))) {
             infof(data, "cookie contains TAB, dropping");
             return CERR_TAB;
           }
         }
       }
       else {
-        val.str = NULL;
-        val.len = 0;
+        Curl_str_init(&val);
       }
 
       /*
@@ -523,10 +522,11 @@ parse_cookie_header(struct Curl_easy *data,
        * combination of name + contents. Chrome and Firefox support 4095 or
        * 4096 bytes combo
        */
-      if(name.len >= (MAX_NAME-1) || val.len >= (MAX_NAME-1) ||
-         ((name.len + val.len) > MAX_NAME)) {
+      if(Curl_strlen(&name) >= (MAX_NAME-1) ||
+         Curl_strlen(&val) >= (MAX_NAME-1) ||
+         ((Curl_strlen(&name) + Curl_strlen(&val)) > MAX_NAME)) {
         infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
-              name.len, val.len);
+              Curl_strlen(&name), Curl_strlen(&val));
         return CERR_TOO_BIG;
       }
 
@@ -536,9 +536,9 @@ parse_cookie_header(struct Curl_easy *data,
        * "the rest". Prefixes must start with '__' and end with a '-', so
        * only test for names where that can possibly be true.
        */
-      if(strncasecompare("__Secure-", name.str, 9))
+      if(strncasecompare("__Secure-", Curl_str(&name), 9))
         co->prefix_secure = TRUE;
-      else if(strncasecompare("__Host-", name.str, 7))
+      else if(strncasecompare("__Host-", Curl_str(&name), 7))
         co->prefix_host = TRUE;
 
       /*
@@ -553,8 +553,8 @@ parse_cookie_header(struct Curl_easy *data,
           /* Bad name/value pair. */
           return CERR_NO_SEP;
 
-        strstore(&co->name, name.str, name.len);
-        strstore(&co->value, val.str, val.len);
+        strstore(&co->name, Curl_str(&name), Curl_strlen(&name));
+        strstore(&co->value, Curl_str(&val), Curl_strlen(&val));
         done = TRUE;
         if(!co->name || !co->value)
           return CERR_NO_NAME_VALUE;
@@ -564,7 +564,7 @@ parse_cookie_header(struct Curl_easy *data,
           return CERR_INVALID_OCTET;
         }
       }
-      else if(!val.len) {
+      else if(!Curl_strlen(&val)) {
         /*
          * this was a "<name>=" with no content, and we must allow
          * 'secure' and 'httponly' specified this weirdly
@@ -592,7 +592,7 @@ parse_cookie_header(struct Curl_easy *data,
       if(done)
         ;
       else if(Curl_str_casecompare(&name, "path")) {
-        strstore(&co->path, val.str, val.len);
+        strstore(&co->path, Curl_str(&val), Curl_strlen(&val));
         if(!co->path)
           return CERR_OUT_OF_MEMORY;
         free(co->spath); /* if this is set again */
@@ -600,15 +600,15 @@ parse_cookie_header(struct Curl_easy *data,
         if(!co->spath)
           return CERR_OUT_OF_MEMORY;
       }
-      else if(Curl_str_casecompare(&name, "domain") && val.len) {
+      else if(Curl_str_casecompare(&name, "domain") && Curl_strlen(&val)) {
         bool is_ip;
-
+        const char *v = Curl_str(&val);
         /*
          * Now, we make sure that our host is within the given domain, or
          * the given domain is not valid and thus cannot be set.
          */
 
-        if('.' == val.str[0])
+        if('.' == *v)
           Curl_str_nudge(&val, 1);
 
 #ifndef USE_LIBPSL
@@ -617,17 +617,18 @@ parse_cookie_header(struct Curl_easy *data,
          * TLD or otherwise "protected" suffix. To reduce risk, we require a
          * dot OR the exact hostname being "localhost".
          */
-        if(bad_domain(val.str, val.len))
+        if(bad_domain(Curl_str(&val), Curl_strlen(&val)))
           domain = ":";
 #endif
 
-        is_ip = Curl_host_is_ipnum(domain ? domain : val.str);
+        is_ip = Curl_host_is_ipnum(domain ? domain : Curl_str(&val));
 
         if(!domain
-           || (is_ip && !strncmp(val.str, domain, val.len) &&
-               (val.len == strlen(domain)))
-           || (!is_ip && cookie_tailmatch(val.str, val.len, domain))) {
-          strstore(&co->domain, val.str, val.len);
+           || (is_ip && !strncmp(Curl_str(&val), domain, Curl_strlen(&val)) &&
+               (Curl_strlen(&val) == strlen(domain)))
+           || (!is_ip && cookie_tailmatch(Curl_str(&val),
+                                          Curl_strlen(&val), domain))) {
+          strstore(&co->domain, Curl_str(&val), Curl_strlen(&val));
           if(!co->domain)
             return CERR_OUT_OF_MEMORY;
 
@@ -641,14 +642,14 @@ parse_cookie_header(struct Curl_easy *data,
            * not a domain to which the current host belongs. Mark as bad.
            */
           infof(data, "skipped cookie with bad tailmatch domain: %s",
-                val.str);
+                Curl_str(&val));
           return CERR_NO_TAILMATCH;
         }
       }
       else if(Curl_str_casecompare(&name, "version")) {
         /* just ignore */
       }
-      else if(Curl_str_casecompare(&name, "max-age") && val.len) {
+      else if(Curl_str_casecompare(&name, "max-age") && Curl_strlen(&val)) {
         /*
          * Defined in RFC2109:
          *
@@ -659,7 +660,7 @@ parse_cookie_header(struct Curl_easy *data,
          * cookie should be discarded immediately.
          */
         int rc;
-        const char *maxage = val.str;
+        const char *maxage = Curl_str(&val);
         if(*maxage == '\"')
           maxage++;
         rc = Curl_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
@@ -686,8 +687,8 @@ parse_cookie_header(struct Curl_easy *data,
         }
         cap_expires(now, co);
       }
-      else if(Curl_str_casecompare(&name, "expires") && val.len) {
-        if(!co->expires && (val.len < MAX_DATE_LENGTH)) {
+      else if(Curl_str_casecompare(&name, "expires") && Curl_strlen(&val)) {
+        if(!co->expires && (Curl_strlen(&val) < MAX_DATE_LENGTH)) {
           /*
            * Let max-age have priority.
            *
@@ -695,8 +696,8 @@ parse_cookie_header(struct Curl_easy *data,
            * will be treated as a session cookie
            */
           char dbuf[MAX_DATE_LENGTH + 1];
-          memcpy(dbuf, val.str, val.len);
-          dbuf[val.len] = 0;
+          memcpy(dbuf, Curl_str(&val), Curl_strlen(&val));
+          dbuf[Curl_strlen(&val)] = 0;
           co->expires = Curl_getdate_capped(dbuf);
 
           /*
index fb3654e53bbf2c906acf7a1a8e3217e66d1775e8..2dcd6a974dc9f5bc1f6695c1aa29c2b6e6f82d11 100644 (file)
@@ -394,7 +394,7 @@ static CURLcode trc_opt(const char *config)
   struct Curl_str out;
   while(!Curl_str_until(&config, &out, 32, ',')) {
     int lvl = CURL_LOG_LVL_INFO;
-    const char *token = out.str;
+    const char *token = Curl_str(&out);
 
     if(*token == '-') {
       lvl = CURL_LOG_LVL_NONE;
index 9d0dd104041b87df6255082f1a2f392f4745c334..e0589adbfb1344751aa4ca3319d06d9d5ec443f8 100644 (file)
@@ -1150,7 +1150,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 
       if(!Curl_str_number(&host, &num, 0xffff)) {
         /* Create an entry id, based upon the hostname and port */
-        entry_len = create_hostcache_id(source.str, source.len, (int)num,
+        entry_len = create_hostcache_id(Curl_str(&source),
+                                        Curl_strlen(&source), (int)num,
                                         entry_id, sizeof(entry_id));
         if(data->share)
           Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -1224,11 +1225,11 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
         }
 #endif
 
-        if(target.len >= sizeof(address))
+        if(Curl_strlen(&target) >= sizeof(address))
           goto err;
 
-        memcpy(address, target.str, target.len);
-        address[target.len] = '\0';
+        memcpy(address, Curl_str(&target), Curl_strlen(&target));
+        address[Curl_strlen(&target)] = '\0';
 
         ai = Curl_str2addr(address, (int)port);
         if(!ai) {
@@ -1260,7 +1261,8 @@ err:
       }
 
       /* Create an entry id, based upon the hostname and port */
-      entry_len = create_hostcache_id(source.str, source.len, (int)port,
+      entry_len = create_hostcache_id(Curl_str(&source), Curl_strlen(&source),
+                                      (int)port,
                                       entry_id, sizeof(entry_id));
 
       if(data->share)
@@ -1271,7 +1273,8 @@ err:
 
       if(dns) {
         infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
-              " - old addresses discarded", (int)source.len, source.str, port);
+              " - old addresses discarded", (int)Curl_strlen(&source),
+              Curl_str(&source), port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
          2. even if entry with correct addresses is already in the cache,
@@ -1287,8 +1290,8 @@ err:
       }
 
       /* put this new host in the cache */
-      dns = Curl_cache_addr(data, head, source.str, source.len, (int)port,
-                            permanent);
+      dns = Curl_cache_addr(data, head, Curl_str(&source),
+                            Curl_strlen(&source), (int)port, permanent);
       if(dns) {
         /* release the returned reference; the cache itself will keep the
          * entry alive: */
@@ -1304,12 +1307,12 @@ err:
       }
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
-            (int)source.len, source.str, port, addresses,
+            (int)Curl_strlen(&source), Curl_str(&source), port, addresses,
             permanent ? "" : " (non-permanent)");
 #endif
 
       /* Wildcard hostname */
-      if((source.len == 1) && (source.str[0] == '*')) {
+      if(Curl_str_casecompare(&source, "*")) {
         infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
               port);
         data->state.wildcard_resolve = TRUE;
index 7fb36525983410b49e618056ce317db35ec41727..9ea4789e771631897e65351078bb8e6913d4e959 100644 (file)
@@ -433,26 +433,26 @@ static CURLcode hsts_add(struct hsts *h, const char *line)
     struct stsentry *e;
     char dbuf[MAX_HSTS_DATELEN + 1];
     time_t expires;
+    const char *hp = Curl_str(&host);
 
     /* The date parser works on a null terminated string. The maximum length
        is upheld by Curl_str_quotedword(). */
-    memcpy(dbuf, date.str, date.len);
-    dbuf[date.len] = 0;
+    memcpy(dbuf, Curl_str(&date), Curl_strlen(&date));
+    dbuf[Curl_strlen(&date)] = 0;
 
     expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
       TIME_T_MAX;
 
-    if(host.str[0] == '.') {
-      host.str++;
-      host.len--;
+    if(hp[0] == '.') {
+      Curl_str_nudge(&host, 1);
       subdomain = TRUE;
     }
     /* only add it if not already present */
-    e = Curl_hsts(h, host.str, host.len, subdomain);
+    e = Curl_hsts(h, Curl_str(&host), Curl_strlen(&host), subdomain);
     if(!e)
-      result = hsts_create(h, host.str, host.len, subdomain, expires);
-    else if((strlen(e->host) == host.len) &&
-            strncasecompare(host.str, e->host, host.len)) {
+      result = hsts_create(h, Curl_str(&host), Curl_strlen(&host),
+                           subdomain, expires);
+    else if(Curl_str_casecompare(&host, e->host)) {
       /* the same hostname, use the largest expire time */
       if(expires > e->expires)
         e->expires = expires;
index 97835eac31c1e0c51f12488dff9ea488458291b9..4ed55f0cee368ebb51b595d2adab4cae05a3cf1c 100644 (file)
@@ -652,7 +652,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     /* nothing to do */
   }
 
-  if(!service.len) {
+  if(!Curl_strlen(&service)) {
     const char *p = hostname;
     if(Curl_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
        Curl_str_single(&p, '.')) {
@@ -662,9 +662,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     }
 
     infof(data, "aws_sigv4: picked service %.*s from host",
-          (int)service.len, service.str);
+          (int)Curl_strlen(&service), Curl_str(&service));
 
-    if(!region.len) {
+    if(!Curl_strlen(&region)) {
       if(Curl_str_until(&p, &region, MAX_SIGV4_LEN, '.') ||
          Curl_str_single(&p, '.')) {
         failf(data, "aws-sigv4: region missing in parameters and hostname");
@@ -672,14 +672,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
         goto fail;
       }
       infof(data, "aws_sigv4: picked region %.*s from host",
-            (int)region.len, region.str);
+            (int)Curl_strlen(&region), Curl_str(&region));
     }
   }
 
   Curl_http_method(data, conn, &method, &httpreq);
 
-  payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len,
-                                       &payload_hash_len);
+  payload_hash =
+    parse_content_sha_hdr(data, Curl_str(&provider1), Curl_strlen(&provider1),
+                          &payload_hash_len);
 
   if(!payload_hash) {
     /* AWS S3 requires a x-amz-content-sha256 header, and supports special
@@ -688,9 +689,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
       Curl_str_casecompare(&service, "s3");
 
     if(sign_as_s3)
-      result = calc_s3_payload_hash(data, httpreq,
-                                    provider1.str, provider1.len,
-                                    sha_hash, sha_hex, content_sha256_hdr);
+      result = calc_s3_payload_hash(data, httpreq, Curl_str(&provider1),
+                                    Curl_strlen(&provider1), sha_hash, sha_hex,
+                                    content_sha256_hdr);
     else
       result = calc_payload_hash(data, sha_hash, sha_hex);
     if(result)
@@ -722,7 +723,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
 
   result = make_headers(data, hostname, timestamp,
-                        provider1.str, provider1.len,
+                        Curl_str(&provider1), Curl_strlen(&provider1),
                         &date_header, content_sha256_hdr,
                         &canonical_headers, &signed_headers);
   if(result)
@@ -767,17 +768,18 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
 
   DEBUGF(infof(data, "Canonical request: %s", canonical_request));
 
-  request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str);
+  request_type = aprintf("%.*s4_request",
+                         (int)Curl_strlen(&provider0), Curl_str(&provider0));
   if(!request_type)
     goto fail;
 
   /* provider0 is lowercased *after* aprintf() so that the buffer can be
      written to */
-  Curl_strntolower(request_type, request_type, provider0.len);
+  Curl_strntolower(request_type, request_type, Curl_strlen(&provider0));
 
-  credential_scope = aprintf("%s/%.*s/%.*s/%s",
-                             date, (int)region.len, region.str,
-                             (int)service.len, service.str,
+  credential_scope = aprintf("%s/%.*s/%.*s/%s", date,
+                             (int)Curl_strlen(&region), Curl_str(&region),
+                             (int)Curl_strlen(&service), Curl_str(&service),
                              request_type);
   if(!credential_scope)
     goto fail;
@@ -796,7 +798,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
                         "%s\n" /* RequestDateTime */
                         "%s\n" /* CredentialScope */
                         "%s",  /* HashedCanonicalRequest in hex */
-                        (int)provider0.len, provider0.str,
+                        (int)Curl_strlen(&provider0), Curl_str(&provider0),
                         timestamp,
                         credential_scope,
                         sha_hex);
@@ -804,19 +806,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     goto fail;
 
   /* make provider0 part done uppercase */
-  Curl_strntoupper(str_to_sign, provider0.str, provider0.len);
+  Curl_strntoupper(str_to_sign, Curl_str(&provider0), Curl_strlen(&provider0));
 
-  secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str,
-                   data->state.aptr.passwd ?
+  secret = aprintf("%.*s4%s", (int)Curl_strlen(&provider0),
+                   Curl_str(&provider0), data->state.aptr.passwd ?
                    data->state.aptr.passwd : "");
   if(!secret)
     goto fail;
   /* make provider0 part done uppercase */
-  Curl_strntoupper(secret, provider0.str, provider0.len);
+  Curl_strntoupper(secret, Curl_str(&provider0), Curl_strlen(&provider0));
 
   HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
-  HMAC_SHA256(sign0, sizeof(sign0), region.str, region.len, sign1);
-  HMAC_SHA256(sign1, sizeof(sign1), service.str, service.len, sign0);
+  HMAC_SHA256(sign0, sizeof(sign0),
+              Curl_str(&region), Curl_strlen(&region), sign1);
+  HMAC_SHA256(sign1, sizeof(sign1),
+              Curl_str(&service), Curl_strlen(&service), sign0);
   HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
   HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
 
@@ -833,7 +837,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
                           */
                          "%s"
                          "%s", /* optional sha256 header includes \r\n */
-                         (int)provider0.len, provider0.str,
+                         (int)Curl_strlen(&provider0), Curl_str(&provider0),
                          user,
                          credential_scope,
                          Curl_dyn_ptr(&signed_headers),
@@ -845,7 +849,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
   /* provider 0 uppercase */
   Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
-                   provider0.str, provider0.len);
+                   Curl_str(&provider0), Curl_strlen(&provider0));
 
   Curl_safefree(data->state.aptr.userpwd);
   data->state.aptr.userpwd = auth_headers;
index 5bfbdcbe0e96526ee6092adb86a8396539e3848c..076b91d69fe0854274df3df46554d53d026472cc 100644 (file)
 #include "strparse.h"
 #include "strcase.h"
 
+void Curl_str_init(struct Curl_str *out)
+{
+  out->str = NULL;
+  out->len = 0;
+}
+
 /* Get a word until the first DELIM or end of string. At least one byte long.
    return non-zero on error */
 int Curl_str_until(const char **linep, struct Curl_str *out,
@@ -34,8 +40,7 @@ int Curl_str_until(const char **linep, struct Curl_str *out,
   size_t len = 0;
   DEBUGASSERT(linep && *linep && out && max && delim);
 
-  out->str = NULL;
-  out->len = 0;
+  Curl_str_init(out);
   while(*s && (*s != delim)) {
     s++;
     if(++len > max) {
@@ -68,8 +73,7 @@ int Curl_str_quotedword(const char **linep, struct Curl_str *out,
   size_t len = 0;
   DEBUGASSERT(linep && *linep && out && max);
 
-  out->str = NULL;
-  out->len = 0;
+  Curl_str_init(out);
   if(*s != '\"')
     return STRE_BEGQUOTE;
   s++;
@@ -226,8 +230,7 @@ int Curl_str_cspn(const char **linep, struct Curl_str *out, const char *reject)
     *linep = &s[len];
     return STRE_OK;
   }
-  out->str = NULL;
-  out->len = 0;
+  Curl_str_init(out);
   return STRE_SHORT;
 }
 
index 41aa3fed105ce8b0e7f9ddae6e6dd32c7460bc6b..f096526452a31f0a17fc496cc0d5d252d2ec5e00 100644 (file)
 #define STRE_OVERFLOW 7
 #define STRE_NO_NUM   8
 
+/* public struct, but all accesses should be done using the provided
+   functions */
 struct Curl_str {
   const char *str;
   size_t len;
 };
 
+void Curl_str_init(struct Curl_str *out);
+
+#define Curl_str(x) ((x)->str)
+#define Curl_strlen(x) ((x)->len)
+
 /* Get a word until the first space
    return non-zero on error */
 int Curl_str_word(const char **linep, struct Curl_str *out, const size_t max);