]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tool: remove protocol count limitation
authorPatrick Monnerat <patrick@monnerat.net>
Wed, 21 Sep 2022 08:41:22 +0000 (10:41 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 22 Sep 2022 11:49:10 +0000 (13:49 +0200)
Replace bit mask protocol sets by null-terminated arrays of protocol
tokens. These are the addresses of the protocol names returned by
curl_version_info().

Protocol names are sorted case-insensitively before output to satisfy CI
tests matches consistency.

The protocol list returned by curl_version_info() is augmented with all
RTMP protocol variants.

Test 1401 adjusted for new alpha ordered output.

Closes #9546

12 files changed:
lib/version.c
src/tool_cb_hdr.c
src/tool_getparam.c
src/tool_help.c
src/tool_libinfo.c
src/tool_libinfo.h
src/tool_operate.c
src/tool_paramhlp.c
src/tool_paramhlp.h
src/tool_util.c
src/tool_util.h
tests/data/test1401

index 9b72b521b815ec43525f41960a01f3ef32eda50a..f45bc6739f31088a70402b4de096a9a417cbfcf4 100644 (file)
@@ -338,6 +338,11 @@ static const char * const protocols[] = {
 #endif
 #ifdef USE_LIBRTMP
   "rtmp",
+  "rtmpe",
+  "rtmps",
+  "rtmpt",
+  "rtmpte",
+  "rtmpts",
 #endif
 #ifndef CURL_DISABLE_RTSP
   "rtsp",
index f9adb4a2bcf5d595988f55c165e113b7f823ce7f..23700de222ac3b41073f402751dce68d67f1f9d3 100644 (file)
@@ -75,8 +75,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
   const char *str = ptr;
   const size_t cb = size * nmemb;
   const char *end = (char *)ptr + cb;
-  char *scheme;
-  proto_t protocol = proto_last;
+  const char *scheme = NULL;
 
   /*
    * Once that libcurl has called back tool_header_cb() the returned value
@@ -142,11 +141,10 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
    */
 
   curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme);
-  if(scheme)
-    protocol = scheme2protocol(scheme);
+  scheme = proto_token(scheme);
   if(hdrcbdata->honor_cd_filename &&
      (cb > 20) && checkprefix("Content-disposition:", str) &&
-     (protocol == proto_https || protocol == proto_http)) {
+     (scheme == proto_http || scheme == proto_https)) {
     const char *p = str + 20;
 
     /* look for the 'filename=' parameter
@@ -206,8 +204,8 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
       per->was_last_header_empty = TRUE;
   }
   if(hdrcbdata->config->show_headers &&
-    (protocol == proto_http || protocol == proto_https ||
-     protocol == proto_rtsp || protocol == proto_file)) {
+    (scheme == proto_http || scheme == proto_https ||
+     scheme == proto_rtsp || scheme == proto_file)) {
     /* bold headers only for selected protocols */
     char *value = NULL;
 
index f345ed7f7164a8a61fc60aabcc8ce854a90aa16d..b76110dd390670f0497ffbd63014fa94e38aef14 100644 (file)
@@ -578,6 +578,15 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
 #ifdef HAVE_WRITABLE_ARGV
   argv_item_t clearthis = NULL;
 #endif
+
+  static const char *redir_protos[] = {
+    "http",
+    "https",
+    "ftp",
+    "ftps",
+    NULL
+  };
+
   *usedarg = FALSE; /* default is that we don't use the arg */
 
   if(('-' != flag[0]) || ('-' == flag[1])) {
@@ -1209,15 +1218,13 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
         break;
       case 'D': /* --proto */
         config->proto_present = TRUE;
-        err = proto2num(config, PROTO_ALL, &config->proto_str, nextarg);
+        err = proto2num(config, built_in_protos, &config->proto_str, nextarg);
         if(err)
           return err;
         break;
       case 'E': /* --proto-redir */
         config->proto_redir_present = TRUE;
-        if(proto2num(config, PROTO_BIT(proto_http) | PROTO_BIT(proto_https) |
-                     PROTO_BIT(proto_ftp) | PROTO_BIT(proto_ftps),
-                     &config->proto_redir_str, nextarg))
+        if(proto2num(config, redir_protos, &config->proto_redir_str, nextarg))
           return PARAM_BAD_USE;
         break;
       case 'F': /* --resolve */
index f24a5f2ed8ae98ec0d53c0f8eed065e7091c53e8..65a1f43dfa4e94480ebc56cd7dd6f9e8d55ec194 100644 (file)
@@ -22,9 +22,6 @@
  *
  ***************************************************************************/
 #include "tool_setup.h"
-#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H)
-#include <strings.h>
-#endif
 #define ENABLE_CURLX_PRINTF
 /* use our own printf() functions */
 #include "curlx.h"
@@ -32,6 +29,7 @@
 #include "tool_panykey.h"
 #include "tool_help.h"
 #include "tool_libinfo.h"
+#include "tool_util.h"
 #include "tool_version.h"
 
 #include "memdebug.h" /* keep this as LAST include */
@@ -190,22 +188,6 @@ void tool_help(char *category)
   free(category);
 }
 
-static int
-featcomp(const void *p1, const void *p2)
-{
-  /* The arguments to this function are "pointers to pointers to char", but
-     the comparison arguments are "pointers to char", hence the following cast
-     plus dereference */
-#ifdef HAVE_STRCASECMP
-  return strcasecmp(* (char * const *) p1, * (char * const *) p2);
-#elif defined(HAVE_STRCMPI)
-  return strcmpi(* (char * const *) p1, * (char * const *) p2);
-#elif defined(HAVE_STRICMP)
-  return stricmp(* (char * const *) p1, * (char * const *) p2);
-#else
-  return strcmp(* (char * const *) p1, * (char * const *) p2);
-#endif
-}
 
 void tool_version_info(void)
 {
@@ -237,7 +219,7 @@ void tool_version_info(void)
       if(curlinfo->features & feats[i].bitmask)
         featp[numfeat++] = (char *)feats[i].name;
     }
-    qsort(&featp[0], numfeat, sizeof(char *), featcomp);
+    qsort(&featp[0], numfeat, sizeof(char *), struplocompare4sort);
     for(i = 0; i< numfeat; i++)
       printf(" %s", featp[i]);
     puts(""); /* newline */
index 21b8da97033f3ec71933f6a27a519576669a313e..801fd579f3696029e13371e11894a7b27803a9b3 100644 (file)
 
 /* global variable definitions, for libcurl run-time info */
 
-#define MAX_PROTOS      64      /* Maximum number of supported protocols. */
+static const char *no_protos = NULL;
 
 curl_version_info_data *curlinfo = NULL;
-
-proto_t proto_last = 0;
-
-proto_t proto_ftp = PROTO_NONE;
-proto_t proto_ftps = PROTO_NONE;
-proto_t proto_http = PROTO_NONE;
-proto_t proto_https = PROTO_NONE;
-proto_t proto_file = PROTO_NONE;
-proto_t proto_rtsp = PROTO_NONE;
-proto_t proto_scp = PROTO_NONE;
-proto_t proto_sftp = PROTO_NONE;
-proto_t proto_tftp = PROTO_NONE;
-
-static struct proto_name_nump {
-  const char    *proto_name;
-  proto_t       *proto_nump;
+const char * const *built_in_protos = &no_protos;
+
+size_t proto_count = 0;
+
+const char *proto_file = NULL;
+const char *proto_ftp = NULL;
+const char *proto_ftps = NULL;
+const char *proto_http = NULL;
+const char *proto_https = NULL;
+const char *proto_rtsp = NULL;
+const char *proto_scp = NULL;
+const char *proto_sftp = NULL;
+const char *proto_tftp = NULL;
+
+static struct proto_name_tokenp {
+  const char   *proto_name;
+  const char  **proto_tokenp;
 } const possibly_built_in[] = {
-  /* Keep entries in CURLPROTO_* order for sorting purpose. */
-  { "http",     &proto_http  },
-  { "https",    &proto_https },
+  { "file",     &proto_file  },
   { "ftp",      &proto_ftp   },
   { "ftps",     &proto_ftps  },
+  { "http",     &proto_http  },
+  { "https",    &proto_https },
+  { "rtsp",     &proto_rtsp  },
   { "scp",      &proto_scp   },
   { "sftp",     &proto_sftp  },
-  { "telnet",   NULL         },
-  { "ldap",     NULL         },
-  { "ldaps",    NULL         },
-  { "dict",     NULL         },
-  { "file",     &proto_file  },
   { "tftp",     &proto_tftp  },
-  { "imap",     NULL         },
-  { "imaps",    NULL         },
-  { "pop3",     NULL         },
-  { "pop3s",    NULL         },
-  { "smtp",     NULL         },
-  { "smtps",    NULL         },
-  { "rtsp",     &proto_rtsp  },
-  { "rtmp",     NULL         },
-  { "rtmpt",    NULL         },
-  { "rtmpe",    NULL         },
-  { "rtmpte",   NULL         },
-  { "rtmps",    NULL         },
-  { "rtmpts",   NULL         },
-  { "gopher",   NULL         },
-  { "smb",      NULL         },
-  { "smbs",     NULL         },
-  { "mqtt",     NULL         },
-  { "gophers",  NULL         },
-  { "ws",       NULL         },
-  { "wss",      NULL         },
   {  NULL,      NULL         }
 };
 
-static const char *built_in_protos[MAX_PROTOS + 1] = {NULL};
-
-/*
- * scheme2protocol() returns the protocol number for the specified URL scheme
- */
-proto_t scheme2protocol(const char *scheme)
-{
-  proto_t p;
-
-  for(p = 0; built_in_protos[p]; p++)
-    if(curl_strequal(scheme, built_in_protos[p]))
-      return p;
-  return PROTO_NONE;
-}
-
-/*
- * protocol2scheme() returns the name of the specified protocol.
- */
-const char *protocol2scheme(proto_t proto)
-{
-  return proto < proto_last? built_in_protos[proto]: NULL;
-}
-
-/* Enter a prototype in the built-in prototype table. */
-static CURLcode enter_proto(const char *proto)
-{
-  if(scheme2protocol(proto) == PROTO_NONE) {
-    if(proto_last >= MAX_PROTOS)
-      return CURLE_OUT_OF_MEMORY;
-    built_in_protos[proto_last] = proto;
-    built_in_protos[++proto_last] = NULL;
-  }
-
-  return CURLE_OK;
-}
-
-/* qsort helper functions for prototype array. */
-static int sortkey(const void *arg)
-{
-  const char *proto = *(const char **) arg;
-  const struct proto_name_nump *p;
-
-  for(p = possibly_built_in; p->proto_name; p++)
-    if(curl_strequal(p->proto_name, proto))
-      break;
-
-  return (int) (p - possibly_built_in);
-}
-
-static int protocmp(const void *p1, const void *p2)
-{
-  return sortkey(p1) - sortkey(p2);
-}
-
 /*
  * libcurl_info_init: retrieves run-time information about libcurl,
  * setting a global pointer 'curlinfo' to libcurl's run-time info
- * struct, Assigning numbers to specific protocols and identifying protocols
- * we are interested in.
+ * struct, count protocols and flag those we are interested in.
  */
 
 CURLcode get_libcurl_info(void)
@@ -163,40 +85,40 @@ CURLcode get_libcurl_info(void)
 
   if(curlinfo->protocols) {
     const char *const *builtin;
-    const struct proto_name_nump *p;
-
-    /* Copy protocols to local table. */
-    for(builtin = curlinfo->protocols; !result && *builtin; builtin++)
-      result = enter_proto(*builtin);
-
-    /* Special case: if RTMP is present, also include RTMPE, RTMPS, RTMPT,
-       RTMPTE and RTMPTS. */
-    if(scheme2protocol("rtmp") != PROTO_NONE) {
-      if(!result)
-        result = enter_proto("rtmpe");
-      if(!result)
-        result = enter_proto("rtmps");
-      if(!result)
-        result = enter_proto("rtmpt");
-      if(!result)
-        result = enter_proto("rtmpte");
-      if(!result)
-        result = enter_proto("rtmpts");
-    }
-
-    if(result)
-      return result;
+    const struct proto_name_tokenp *p;
 
-    /* Sort the protocols to be sure the primary ones are always accessible
-     * and to retain their list order for testing purposes. */
-    qsort((char *)built_in_protos, proto_last,
-          sizeof(built_in_protos[0]), protocmp);
+    built_in_protos = curlinfo->protocols;
 
-    /* Identify protocols we are interested in. */
-    for(p = possibly_built_in; p->proto_name; p++)
-      if(p->proto_nump)
-        *p->proto_nump = scheme2protocol(p->proto_name);
+    for(builtin = built_in_protos; !result && *builtin; builtin++) {
+      /* Identify protocols we are interested in. */
+      for(p = possibly_built_in; p->proto_name; p++)
+        if(curl_strequal(p->proto_name, *builtin)) {
+          *p->proto_tokenp = *builtin;
+          break;
+        }
+    }
+    proto_count = builtin - built_in_protos;
   }
 
   return CURLE_OK;
 }
+
+/* Tokenize a protocol name.
+ * Return the address of the protocol name listed by the library, or NULL if
+ * not found.
+ * Although this may seem useless, this always returns the same address for
+ * a given protocol and thus allows comparing pointers rather than strings.
+ * In addition, the returned pointer is not deallocated until the program ends.
+ */
+
+const char *proto_token(const char *proto)
+{
+  const char * const *builtin;
+
+  if(!proto)
+    return NULL;
+  for(builtin = built_in_protos; *builtin; builtin++)
+    if(curl_strequal(*builtin, proto))
+      break;
+  return *builtin;
+}
index 19c75b1bb6be353b04d6d5c66dd627b1b5fa0371..40e5aff3eebd835a6b7eb56994435fe40b22d6d9 100644 (file)
 
 /* global variable declarations, for libcurl run-time info */
 
-typedef unsigned int proto_t;   /* A protocol number.*/
-
-#define PROTO_NONE ((proto_t) -1)
-
-/* Protocol numbers set type. This should have enough bits for all
- * enabled protocols.
- */
-typedef unsigned int proto_set_t;
-
-#define PROTO_MAX       ((proto_t) (8 * sizeof(proto_set_t)))
-
-#define PROTO_BIT(p)    ((p) < PROTO_MAX? (proto_set_t) 1 << (p):       \
-                                          (proto_set_t) 0)
-
-#define PROTO_ALL       (PROTO_BIT(proto_last) - (proto_set_t) 1)
-
 
 extern curl_version_info_data *curlinfo;
-extern proto_t proto_last;
-
-extern proto_t proto_ftp;
-extern proto_t proto_ftps;
-extern proto_t proto_http;
-extern proto_t proto_https;
-extern proto_t proto_file;
-extern proto_t proto_rtsp;
-extern proto_t proto_scp;
-extern proto_t proto_sftp;
-extern proto_t proto_tftp;
+extern const char * const *built_in_protos;
+extern size_t proto_count;
+
+extern const char *proto_file;
+extern const char *proto_ftp;
+extern const char *proto_ftps;
+extern const char *proto_http;
+extern const char *proto_https;
+extern const char *proto_rtsp;
+extern const char *proto_scp;
+extern const char *proto_sftp;
+extern const char *proto_tftp;
 
 CURLcode get_libcurl_info(void);
-proto_t scheme2protocol(const char *scheme);
-const char *protocol2scheme(proto_t proto);
+const char *proto_token(const char *proto);
 
 #endif /* HEADER_CURL_TOOL_LIBINFO_H */
index b859ab05c59eeee15f1b8404830809ce631ba1a1..1d86927e159e147a3a729d57bc6ce0c52a6b6236 100644 (file)
@@ -465,12 +465,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
       /* If it returned OK. _or_ failonerror was enabled and it
          returned due to such an error, check for HTTP transient
          errors to retry on. */
-      char *scheme;
-      proto_t protocol = proto_last;
+      const char *scheme;
       curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
-      if(scheme)
-        protocol = scheme2protocol(scheme);
-      if(protocol == proto_http || protocol == proto_https) {
+      scheme = proto_token(scheme);
+      if(scheme == proto_http || scheme == proto_https) {
         /* This was HTTP(S) */
         curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
 
@@ -497,17 +495,13 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
       }
     } /* if CURLE_OK */
     else if(result) {
-      char *scheme;
-      proto_t protocol = proto_last;
+      const char *scheme;
 
       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
       curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme);
+      scheme = proto_token(scheme);
 
-      if(scheme)
-        protocol = scheme2protocol(scheme);
-
-      if((protocol == proto_ftp || protocol == proto_ftps) &&
-         response / 100 == 4)
+      if((scheme == proto_ftp || scheme == proto_ftps) && response / 100 == 4)
         /*
          * This is typically when the FTP server only allows a certain
          * amount of users and we are not one of them.  All 4xx codes
@@ -693,12 +687,12 @@ static void single_transfer_cleanup(struct OperationConfig *config)
 }
 
 /*
- * Return the proto bit for the scheme used in the given URL
+ * Return the protocol token for the scheme used in the given URL
  */
-static proto_t url_proto(char *url)
+static const char *url_proto(char *url)
 {
   CURLU *uh = curl_url();
-  proto_t proto = PROTO_NONE;
+  const char *proto = NULL;
 
   if(uh) {
     if(url) {
@@ -708,14 +702,14 @@ static proto_t url_proto(char *url)
         if(!curl_url_get(uh, CURLUPART_SCHEME, &schemep,
                          CURLU_DEFAULT_SCHEME) &&
            schemep) {
-          proto = scheme2protocol(schemep);
+          proto = proto_token(schemep);
           curl_free(schemep);
         }
       }
     }
     curl_url_cleanup(uh);
   }
-  return proto;
+  return proto? proto: "???";   /* Never match if not found. */
 }
 
 /* create the next (singular) transfer */
@@ -858,7 +852,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
         struct OutStruct *etag_save;
         struct HdrCbData *hdrcbdata = NULL;
         struct OutStruct etag_first;
-        proto_t use_proto;
+        const char *use_proto;
         CURL *curl;
 
         /* --etag-save */
@@ -1257,10 +1251,8 @@ static CURLcode single_transfer(struct GlobalConfig *global,
 
         /* here */
         use_proto = url_proto(per->this_url);
-        if(use_proto == PROTO_NONE)
-          use_proto = proto_last;  /* Do not match any identified protocol. */
 #if 0
-        if(use_proto >= proto_last) {
+        if(!use_proto) {
           warnf(global, "URL is '%s' but no support for the scheme\n",
                 per->this_url);
         }
@@ -1416,12 +1408,12 @@ static CURLcode single_transfer(struct GlobalConfig *global,
 
         my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers);
 
-        if(proto_http < proto_last || proto_rtsp < proto_last) {
+        if(proto_http || proto_rtsp) {
           my_setopt_str(curl, CURLOPT_REFERER, config->referer);
           my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent);
         }
 
-        if(proto_http < proto_last) {
+        if(proto_http) {
           long postRedir = 0;
 
           my_setopt(curl, CURLOPT_FOLLOWLOCATION,
@@ -1471,9 +1463,9 @@ static CURLcode single_transfer(struct GlobalConfig *global,
             return result;
           }
 
-        } /* (proto_http < proto_last) */
+        } /* (proto_http) */
 
-        if(proto_ftp < proto_last)
+        if(proto_ftp)
           my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport);
         my_setopt(curl, CURLOPT_LOW_SPEED_LIMIT,
                   config->low_speed_limit);
@@ -1972,7 +1964,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
         my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L);
 
         /* curl 7.15.1 */
-        if(proto_ftp < proto_last)
+        if(proto_ftp)
           my_setopt(curl, CURLOPT_FTP_FILEMETHOD,
                     (long)config->ftp_filemethod);
 
@@ -2009,7 +2001,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
           my_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L);
 
         /* curl 7.20.0 */
-        if(config->tftp_blksize && proto_tftp < proto_last)
+        if(config->tftp_blksize && proto_tftp)
           my_setopt(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize);
 
         if(config->mail_from)
@@ -2122,7 +2114,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
                         (long)(config->expect100timeout*1000));
 
         /* new in 7.48.0 */
-        if(config->tftp_no_options && proto_tftp < proto_last)
+        if(config->tftp_no_options && proto_tftp)
           my_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L);
 
         /* new in 7.59.0 */
index f5745b057684d62dbfeefd0063b31cc96b6c9170..a8537eacd4c96ddda7196dda5b8c91dfa98d5785 100644 (file)
@@ -35,6 +35,7 @@
 #include "tool_msgs.h"
 #include "tool_paramhlp.h"
 #include "tool_libinfo.h"
+#include "tool_util.h"
 #include "tool_version.h"
 #include "dynbuf.h"
 
@@ -260,6 +261,51 @@ ParameterError str2udouble(double *valp, const char *str, long max)
   return PARAM_OK;
 }
 
+/*
+ * Implement protocol sets in null-terminated array of protocol name pointers.
+ */
+
+/* Return index of prototype token in set, card(set) if not found.
+   Can be called with proto == NULL to get card(set). */
+static size_t protoset_index(const char * const *protoset, const char *proto)
+{
+  const char * const *p = protoset;
+
+  for(; *p; p++)
+    if(proto == *p)
+      break;
+  return p - protoset;
+}
+
+/* Include protocol token in set. */
+static void protoset_set(const char **protoset, const char *proto)
+{
+  if(proto) {
+    size_t n = protoset_index(protoset, proto);
+
+    if(!protoset[n]) {
+      DEBUGASSERT(n < proto_count);
+      protoset[n] = proto;
+      protoset[n + 1] = NULL;
+    }
+  }
+}
+
+/* Exclude protocol token from set. */
+static void protoset_clear(const char **protoset, const char *proto)
+{
+  if(proto) {
+    size_t n = protoset_index(protoset, proto);
+
+    if(protoset[n]) {
+      size_t m = protoset_index(protoset, NULL) - 1;
+
+      protoset[n] = protoset[m];
+      protoset[m] = NULL;
+    }
+  }
+}
+
 /*
  * Parse the string and provide an allocated libcurl compatible protocol
  * string output. Return non-zero on failure, zero on success.
@@ -274,13 +320,14 @@ ParameterError str2udouble(double *valp, const char *str, long max)
 #define MAX_PROTOSTRING (64*11) /* Enough room for 64 10-chars proto names. */
 
 ParameterError proto2num(struct OperationConfig *config,
-                         proto_set_t val, char **ostr, const char *str)
+                         const char * const *val, char **ostr, const char *str)
 {
   char *buffer;
   const char *sep = ",";
   char *token;
+  const char **protoset;
   struct curlx_dynbuf obuf;
-  proto_t proto;
+  size_t proto;
   CURLcode result;
 
   curlx_dyn_init(&obuf, MAX_PROTOSTRING);
@@ -292,6 +339,21 @@ ParameterError proto2num(struct OperationConfig *config,
   if(!buffer)
     return PARAM_NO_MEM;
 
+  protoset = malloc((proto_count + 1) * sizeof(*protoset));
+  if(!protoset) {
+    free(buffer);
+    return PARAM_NO_MEM;
+  }
+
+  /* Preset protocol set with default values. */
+  protoset[0] = NULL;
+  for(; *val; val++) {
+    const char *p = proto_token(*val);
+
+    if(p)
+      protoset_set(protoset, p);
+  }
+
   /* Allow strtok() here since this isn't used threaded */
   /* !checksrc! disable BANNEDFUNC 2 */
   for(token = strtok(buffer, sep);
@@ -312,7 +374,8 @@ ParameterError proto2num(struct OperationConfig *config,
         action = allow;
         break;
       default: /* Includes case of terminating NULL */
-        Curl_safefree(buffer);
+        free(buffer);
+        free((char *) protoset);
         return PARAM_BAD_USE;
       }
     }
@@ -320,47 +383,49 @@ ParameterError proto2num(struct OperationConfig *config,
     if(curl_strequal(token, "all")) {
       switch(action) {
       case deny:
-        val = 0;
+        protoset[0] = NULL;
         break;
       case allow:
       case set:
-        val = PROTO_ALL;
+        memcpy((char *) protoset,
+               built_in_protos, (proto_count + 1) * sizeof(*protoset));
         break;
       }
     }
     else {
-      proto = scheme2protocol(token);
-      if(proto != PROTO_NONE) {
+      const char *p = proto_token(token);
+
+      if(p)
         switch(action) {
         case deny:
-          val &= ~PROTO_BIT(proto);
+          protoset_clear(protoset, p);
           break;
         case set:
-          val = 0;
+          protoset[0] = NULL;
           /* FALLTHROUGH */
         case allow:
-          if(proto >= PROTO_MAX)
-            warnf(config->global, "protocol '%s' enabled but not accessible\n",
-                  token);
-          val |= PROTO_BIT(proto);
+          protoset_set(protoset, p);
           break;
         }
-      }
       else { /* unknown protocol */
         /* If they have specified only this protocol, we say treat it as
            if no protocols are allowed */
         if(action == set)
-          val = 0;
+          protoset[0] = NULL;
         warnf(config->global, "unrecognized protocol '%s'\n", token);
       }
     }
   }
-  Curl_safefree(buffer);
+  free(buffer);
+
+  /* We need the protocols in alphabetic order for CI tests requirements. */
+  qsort((char *) protoset, protoset_index(protoset, NULL), sizeof(*protoset),
+        struplocompare4sort);
 
   result = curlx_dyn_addn(&obuf, "", 0);
-  for(proto = 0; proto < proto_last && proto < PROTO_MAX && !result; proto++)
-    if(val & PROTO_BIT(proto))
-      result = curlx_dyn_addf(&obuf, "%s,", protocol2scheme(proto));
+  for(proto = 0; protoset[proto] && !result; proto++)
+    result = curlx_dyn_addf(&obuf, "%s,", protoset[proto]);
+  free((char *) protoset);
   curlx_dyn_setlen(&obuf, curlx_dyn_len(&obuf) - 1);
   *ostr = curlx_dyn_ptr(&obuf);
 
@@ -377,13 +442,10 @@ ParameterError proto2num(struct OperationConfig *config,
  */
 ParameterError check_protocol(const char *str)
 {
-  proto_t proto;
-
   if(!str)
     return PARAM_REQUIRES_PARAMETER;
 
-  proto = scheme2protocol(str);
-  if(proto < proto_last)
+  if(proto_token(str))
     return PARAM_OK;
   return PARAM_LIBCURL_UNSUPPORTED_PROTOCOL;
 }
index 95338a07ebbbe6658ff0994d57c0a50fe4f93d5b..bd7253dbda8a435432a7365b051296274c84b241 100644 (file)
@@ -39,7 +39,7 @@ ParameterError str2unummax(long *val, const char *str, long max);
 ParameterError str2udouble(double *val, const char *str, long max);
 
 ParameterError proto2num(struct OperationConfig *config,
-                         proto_set_t val, char **obuf,
+                         const char * const *val, char **obuf,
                          const char *str);
 
 ParameterError check_protocol(const char *str);
index 8d32343fe72c39b119c4089edbd9e049768db7fb..fb4829f84ac770bf1be1c72656877d35ffcfca04 100644 (file)
  ***************************************************************************/
 #include "tool_setup.h"
 
+#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H)
+#include <strings.h>
+#endif
+
 #include "tool_util.h"
 
 #include "memdebug.h" /* keep this as LAST include */
@@ -135,3 +139,27 @@ long tvdiff(struct timeval newer, struct timeval older)
   return (long)(newer.tv_sec-older.tv_sec)*1000+
     (long)(newer.tv_usec-older.tv_usec)/1000;
 }
+
+/* Case insensitive compare. Accept NULL pointers. */
+int struplocompare(const char *p1, const char *p2)
+{
+  if(!p1)
+    return p2? -1: 0;
+  if(!p2)
+    return 1;
+#ifdef HAVE_STRCASECMP
+  return strcasecmp(p1, p2);
+#elif defined(HAVE_STRCMPI)
+  return strcmpi(p1, p2);
+#elif defined(HAVE_STRICMP)
+  return stricmp(p1, p2);
+#else
+  return strcmp(p1, p2);
+#endif
+}
+
+/* Indirect version to use as qsort callback. */
+int struplocompare4sort(const void *p1, const void *p2)
+{
+  return struplocompare(* (char * const *) p1, * (char * const *) p2);
+}
index 366afe4a8bf791c3e1d1d631f4155d0cea919fc9..72b355e685e4e33f44c7490e55c826ea1e7249a1 100644 (file)
@@ -35,4 +35,8 @@ struct timeval tvnow(void);
  */
 long tvdiff(struct timeval t1, struct timeval t2);
 
+/* Case insensitive comparison support. */
+int struplocompare(const char *p1, const char *p2);
+int struplocompare4sort(const void *p1, const void *p2);
+
 #endif /* HEADER_CURL_TOOL_UTIL_H */
index 7edf76cc53daf62277ec1c9301dc984425f984ee..bbd3e7b6481a32f5d005985676a118add99c824c 100644 (file)
@@ -96,7 +96,7 @@ int main(int argc, char *argv[])
   curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L);
 %endif
   curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
-  curl_easy_setopt(hnd, CURLOPT_PROTOCOLS_STR, "http,ftp,file");
+  curl_easy_setopt(hnd, CURLOPT_PROTOCOLS_STR, "file,ftp,http");
 
   /* Here is a list of options the curl code used that cannot get generated
      as source easily. You may choose to either not use them or implement