From 677266c7692ec8dca4675815a572aaad7ad9e852 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Wed, 21 Sep 2022 10:41:22 +0200 Subject: [PATCH] tool: remove protocol count limitation 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 --- lib/version.c | 5 ++ src/tool_cb_hdr.c | 12 ++- src/tool_getparam.c | 15 +++- src/tool_help.c | 22 +----- src/tool_libinfo.c | 186 +++++++++++++------------------------------- src/tool_libinfo.h | 42 ++++------ src/tool_operate.c | 48 +++++------- src/tool_paramhlp.c | 108 +++++++++++++++++++------ src/tool_paramhlp.h | 2 +- src/tool_util.c | 28 +++++++ src/tool_util.h | 4 + tests/data/test1401 | 2 +- 12 files changed, 229 insertions(+), 245 deletions(-) diff --git a/lib/version.c b/lib/version.c index 9b72b521b8..f45bc6739f 100644 --- a/lib/version.c +++ b/lib/version.c @@ -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", diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index f9adb4a2bc..23700de222 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -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; diff --git a/src/tool_getparam.c b/src/tool_getparam.c index f345ed7f71..b76110dd39 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -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 */ diff --git a/src/tool_help.c b/src/tool_help.c index f24a5f2ed8..65a1f43dfa 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -22,9 +22,6 @@ * ***************************************************************************/ #include "tool_setup.h" -#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H) -#include -#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 */ diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 21b8da9703..801fd579f3 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -35,121 +35,43 @@ /* 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; +} diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index 19c75b1bb6..40e5aff3ee 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -27,38 +27,22 @@ /* 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 */ diff --git a/src/tool_operate.c b/src/tool_operate.c index b859ab05c5..1d86927e15 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -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 */ diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c index f5745b0576..a8537eacd4 100644 --- a/src/tool_paramhlp.c +++ b/src/tool_paramhlp.c @@ -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; } diff --git a/src/tool_paramhlp.h b/src/tool_paramhlp.h index 95338a07eb..bd7253dbda 100644 --- a/src/tool_paramhlp.h +++ b/src/tool_paramhlp.h @@ -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); diff --git a/src/tool_util.c b/src/tool_util.c index 8d32343fe7..fb4829f84a 100644 --- a/src/tool_util.c +++ b/src/tool_util.c @@ -23,6 +23,10 @@ ***************************************************************************/ #include "tool_setup.h" +#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H) +#include +#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); +} diff --git a/src/tool_util.h b/src/tool_util.h index 366afe4a8b..72b355e685 100644 --- a/src/tool_util.h +++ b/src/tool_util.h @@ -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 */ diff --git a/tests/data/test1401 b/tests/data/test1401 index 7edf76cc53..bbd3e7b648 100644 --- a/tests/data/test1401 +++ b/tests/data/test1401 @@ -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 -- 2.47.2