#endif
#ifdef USE_LIBRTMP
"rtmp",
+ "rtmpe",
+ "rtmps",
+ "rtmpt",
+ "rtmpte",
+ "rtmpts",
#endif
#ifndef CURL_DISABLE_RTSP
"rtsp",
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
*/
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
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;
#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])) {
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 */
*
***************************************************************************/
#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"
#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 */
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)
{
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 */
/* 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)
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;
+}
/* 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 */
/* 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);
}
} /* 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
}
/*
- * 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) {
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 */
struct OutStruct *etag_save;
struct HdrCbData *hdrcbdata = NULL;
struct OutStruct etag_first;
- proto_t use_proto;
+ const char *use_proto;
CURL *curl;
/* --etag-save */
/* 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);
}
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,
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);
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);
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)
(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 */
#include "tool_msgs.h"
#include "tool_paramhlp.h"
#include "tool_libinfo.h"
+#include "tool_util.h"
#include "tool_version.h"
#include "dynbuf.h"
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.
#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);
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);
action = allow;
break;
default: /* Includes case of terminating NULL */
- Curl_safefree(buffer);
+ free(buffer);
+ free((char *) protoset);
return PARAM_BAD_USE;
}
}
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);
*/
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;
}
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);
***************************************************************************/
#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 */
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);
+}
*/
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 */
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