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
This commit is contained in:
Patrick Monnerat 2022-09-21 10:41:22 +02:00 committed by Daniel Stenberg
parent fb11e45f9c
commit 677266c769
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
12 changed files with 224 additions and 240 deletions

View 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",

View 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;

View 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 */

View 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 */

View File

@ -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;
const char * const *built_in_protos = &no_protos;
proto_t proto_last = 0;
size_t proto_count = 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;
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_nump {
const char *proto_name;
proto_t *proto_nump;
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;
const struct proto_name_tokenp *p;
/* Copy protocols to local table. */
for(builtin = curlinfo->protocols; !result && *builtin; builtin++)
result = enter_proto(*builtin);
built_in_protos = curlinfo->protocols;
/* 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");
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;
}
}
if(result)
return result;
/* 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);
/* 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);
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;
}

View File

@ -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 const char * const *built_in_protos;
extern size_t proto_count;
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 *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 */

View 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 */

View 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;
}

View 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);

View File

@ -23,6 +23,10 @@
***************************************************************************/
#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);
}

View 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 */

View 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