curl: --help [option] displays documentation for given cmdline option

Since the documentation text blob might be gzipped, it needs to search
for what to output in a streaming manner. It then first searches for
"\nALL OPTIONS".

Then, it looks for the start to display at "\n    -[option]" and stops
again at "\n    -". Except for the last option in the man page, which
ends at "\nFILES" - the subtitle for the section following all options
in the manpage.

Test 1707 to 1710 verify

Closes #13997
This commit is contained in:
Daniel Stenberg 2024-08-03 20:24:12 +02:00
parent 9b1e4b4632
commit 9a0cf56471
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
22 changed files with 794 additions and 344 deletions

View File

@ -148,7 +148,6 @@
18. Command line tool
18.1 sync
18.2 glob posts
18.3 -h option
18.4 --proxycommand
18.5 UTF-8 filenames in Content-Disposition
18.6 Option to make -Z merge lined based outputs on stdout
@ -1037,12 +1036,6 @@
Globbing support for -d and -F, as in 'curl -d "name=foo[0-9]" URL'.
This is easily scripted though.
18.3 -h option
Support "curl -h --insecure" etc to output the manpage section for the
--insecure command line option in the terminal. Should be possible to work
with either long or short versions of command line options.
18.4 --proxycommand
Allow the user to make curl run a command and use its stdio to make requests

View File

@ -24,3 +24,5 @@ clean option state, except for the options that are global. Global options
retain their values and meaning even after --next.
The following options are global: `%GLOBALS`.
# ALL OPTIONS

View File

@ -2,7 +2,7 @@
c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
SPDX-License-Identifier: curl
Long: help
Arg: <category>
Arg: <subject>
Short: h
Help: Get help for commands
Category: important curl
@ -12,15 +12,28 @@ See-also:
- verbose
Example:
- --help all
- --help --insecure
- --help -f
---
# `--help`
Usage help. List all curl command line options within the given **category**.
Usage help. Provide help for the subject given as an optional argument.
If no argument is provided, curl displays the most important command line
arguments.
For category **all**, curl displays help for all options.
The argument can either be a **category** or a **command line option**. When a
category is provided, curl shows all command line options within the given
category. Specify category `all` to list all available options.
If **category** is specified, curl displays all available help categories.
If `category` is specified, curl displays all available help categories.
If the provided subject is instead an existing command line option, specified
either in its short form with a single dash and a single letter, or in the
long form with two dashes and a longer name, curl displays a help text for
that option in the terminal.
The help output is extensive for some options.
If the provided command line option is not known, curl says so.

View File

@ -23,13 +23,6 @@
#
###########################################################################
# Yeah, I know, probably 1000 other persons already wrote a script like
# this, but I'll tell ya:
# THEY DON'T FIT ME :-)
# Get readme file as parameter:
if($ARGV[0] eq "-c") {
$c=1;
shift @ARGV;
@ -53,6 +46,8 @@ print <<HEAD
*/
#ifdef USE_MANUAL
#include "tool_hugehelp.h"
#include "tool_help.h"
HEAD
;
if($c) {
@ -111,23 +106,25 @@ static void zfree_func(voidpf opaque, voidpf ptr)
(void) opaque;
free(ptr);
}
#define HEADERLEN 10
/* Decompress and send to stdout a gzip-compressed buffer */
void hugehelp(void)
{
unsigned char *buf;
int status, headerlen;
int status;
z_stream z;
/* Make sure no gzip options are set */
if(hugehelpgz[3] & 0xfe)
return;
headerlen = 10;
memset(&z, 0, sizeof(z_stream));
z.zalloc = (alloc_func)zalloc_func;
z.zfree = (free_func)zfree_func;
z.avail_in = (unsigned int)(sizeof(hugehelpgz) - headerlen);
z.next_in = (unsigned char *)hugehelpgz + headerlen;
z.avail_in = (unsigned int)(sizeof(hugehelpgz) - HEADERLEN);
z.next_in = (unsigned char *)hugehelpgz + HEADERLEN;
if(inflateInit2(&z, -MAX_WBITS) != Z_OK)
return;
@ -144,7 +141,49 @@ void hugehelp(void)
break;
}
else
break; /* Error */
break; /* error */
}
free(buf);
}
inflateEnd(&z);
}
/* Show the help text for the 'arg' curl argument on stdout */
void showhelp(const char *trigger, const char *arg, const char *endarg)
{
unsigned char *buf;
int status;
z_stream z;
struct scan_ctx ctx;
inithelpscan(&ctx, trigger, arg, endarg);
/* Make sure no gzip options are set */
if(hugehelpgz[3] & 0xfe)
return;
memset(&z, 0, sizeof(z_stream));
z.zalloc = (alloc_func)zalloc_func;
z.zfree = (free_func)zfree_func;
z.avail_in = (unsigned int)(sizeof(hugehelpgz) - HEADERLEN);
z.next_in = (unsigned char *)hugehelpgz + HEADERLEN;
if(inflateInit2(&z, -MAX_WBITS) != Z_OK)
return;
buf = malloc(BUF_SIZE);
if(buf) {
while(1) {
z.avail_out = BUF_SIZE;
z.next_out = buf;
status = inflate(&z, Z_SYNC_FLUSH);
if(status == Z_OK || status == Z_STREAM_END) {
size_t len = BUF_SIZE - z.avail_out;
if(!helpscan(buf, len, &ctx))
break;
if(status == Z_STREAM_END)
break;
}
else
break; /* error */
}
free(buf);
}
@ -188,6 +227,21 @@ void hugehelp(void)
while(curlman[i])
puts(curlman[i++]);
}
/* Show the help text for the 'arg' curl argument on stdout */
void showhelp(const char *trigger, const char *arg, const char *endarg)
{
int i = 0;
struct scan_ctx ctx;
inithelpscan(&ctx, trigger, arg, endarg);
while(curlman[i]) {
size_t len = strlen(curlman[i]);
if(!helpscan((unsigned char *)curlman[i], len, &ctx) ||
!helpscan((unsigned char *)"\\n", 1, &ctx))
break;
i++;
}
}
ENDLINE
;

View File

@ -71,306 +71,16 @@ static ParameterError getstr(char **str, const char *val, bool allowblank)
return PARAM_OK;
}
/* one enum for every command line option. The name is the verbatim long
option name, but in uppercase with periods and minuses replaced with
underscores using a "C_" prefix. */
typedef enum {
C_ABSTRACT_UNIX_SOCKET,
C_ALPN,
C_ALT_SVC,
C_ANYAUTH,
C_APPEND,
C_AWS_SIGV4,
C_BASIC,
C_BUFFER,
C_CA_NATIVE,
C_CACERT,
C_CAPATH,
C_CERT,
C_CERT_STATUS,
C_CERT_TYPE,
C_CIPHERS,
C_CLOBBER,
C_COMPRESSED,
C_COMPRESSED_SSH,
C_CONFIG,
C_CONNECT_TIMEOUT,
C_CONNECT_TO,
C_CONTINUE_AT,
C_COOKIE,
C_COOKIE_JAR,
C_CREATE_DIRS,
C_CREATE_FILE_MODE,
C_CRLF,
C_CRLFILE,
C_CURVES,
C_DATA,
C_DATA_ASCII,
C_DATA_BINARY,
C_DATA_RAW,
C_DATA_URLENCODE,
C_DELEGATION,
C_DIGEST,
C_DISABLE,
C_DISABLE_EPRT,
C_DISABLE_EPSV,
C_DISALLOW_USERNAME_IN_URL,
C_DNS_INTERFACE,
C_DNS_IPV4_ADDR,
C_DNS_IPV6_ADDR,
C_DNS_SERVERS,
C_DOH_CERT_STATUS,
C_DOH_INSECURE,
C_DOH_URL,
C_DUMP_CA_EMBED,
C_DUMP_HEADER,
C_ECH,
C_EGD_FILE,
C_ENGINE,
C_EPRT,
C_EPSV,
C_ETAG_COMPARE,
C_ETAG_SAVE,
C_EXPECT100_TIMEOUT,
C_FAIL,
C_FAIL_EARLY,
C_FAIL_WITH_BODY,
C_FALSE_START,
C_FORM,
C_FORM_ESCAPE,
C_FORM_STRING,
C_FTP_ACCOUNT,
C_FTP_ALTERNATIVE_TO_USER,
C_FTP_CREATE_DIRS,
C_FTP_METHOD,
C_FTP_PASV,
C_FTP_PORT,
C_FTP_PRET,
C_FTP_SKIP_PASV_IP,
C_FTP_SSL,
C_FTP_SSL_CCC,
C_FTP_SSL_CCC_MODE,
C_FTP_SSL_CONTROL,
C_FTP_SSL_REQD,
C_GET,
C_GLOBOFF,
C_HAPPY_EYEBALLS_TIMEOUT_MS,
C_HAPROXY_CLIENTIP,
C_HAPROXY_PROTOCOL,
C_HEAD,
C_HEADER,
C_HELP,
C_HOSTPUBMD5,
C_HOSTPUBSHA256,
C_HSTS,
C_HTTP0_9,
C_HTTP1_0,
C_HTTP1_1,
C_HTTP2,
C_HTTP2_PRIOR_KNOWLEDGE,
C_HTTP3,
C_HTTP3_ONLY,
C_IGNORE_CONTENT_LENGTH,
C_INCLUDE,
C_INSECURE,
C_INTERFACE,
C_IPFS_GATEWAY,
C_IPV4,
C_IPV6,
C_JSON,
C_JUNK_SESSION_COOKIES,
C_KEEPALIVE,
C_KEEPALIVE_CNT,
C_KEEPALIVE_TIME,
C_KEY,
C_KEY_TYPE,
C_KRB,
C_KRB4,
C_LIBCURL,
C_LIMIT_RATE,
C_LIST_ONLY,
C_LOCAL_PORT,
C_LOCATION,
C_LOCATION_TRUSTED,
C_LOGIN_OPTIONS,
C_MAIL_AUTH,
C_MAIL_FROM,
C_MAIL_RCPT,
C_MAIL_RCPT_ALLOWFAILS,
C_MANUAL,
C_MAX_FILESIZE,
C_MAX_REDIRS,
C_MAX_TIME,
C_METALINK,
C_MPTCP,
C_NEGOTIATE,
C_NETRC,
C_NETRC_FILE,
C_NETRC_OPTIONAL,
C_NEXT,
C_NOPROXY,
C_NPN,
C_NTLM,
C_NTLM_WB,
C_OAUTH2_BEARER,
C_OUTPUT,
C_OUTPUT_DIR,
C_PARALLEL,
C_PARALLEL_IMMEDIATE,
C_PARALLEL_MAX,
C_PASS,
C_PATH_AS_IS,
C_PINNEDPUBKEY,
C_POST301,
C_POST302,
C_POST303,
C_PREPROXY,
C_PROGRESS_BAR,
C_PROGRESS_METER,
C_PROTO,
C_PROTO_DEFAULT,
C_PROTO_REDIR,
C_PROXY,
C_PROXY_ANYAUTH,
C_PROXY_BASIC,
C_PROXY_CA_NATIVE,
C_PROXY_CACERT,
C_PROXY_CAPATH,
C_PROXY_CERT,
C_PROXY_CERT_TYPE,
C_PROXY_CIPHERS,
C_PROXY_CRLFILE,
C_PROXY_DIGEST,
C_PROXY_HEADER,
C_PROXY_HTTP2,
C_PROXY_INSECURE,
C_PROXY_KEY,
C_PROXY_KEY_TYPE,
C_PROXY_NEGOTIATE,
C_PROXY_NTLM,
C_PROXY_PASS,
C_PROXY_PINNEDPUBKEY,
C_PROXY_SERVICE_NAME,
C_PROXY_SSL_ALLOW_BEAST,
C_PROXY_SSL_AUTO_CLIENT_CERT,
C_PROXY_TLS13_CIPHERS,
C_PROXY_TLSAUTHTYPE,
C_PROXY_TLSPASSWORD,
C_PROXY_TLSUSER,
C_PROXY_TLSV1,
C_PROXY_USER,
C_PROXY1_0,
C_PROXYTUNNEL,
C_PUBKEY,
C_QUOTE,
C_RANDOM_FILE,
C_RANGE,
C_RATE,
C_RAW,
C_REFERER,
C_REMOTE_HEADER_NAME,
C_REMOTE_NAME,
C_REMOTE_NAME_ALL,
C_REMOTE_TIME,
C_REMOVE_ON_ERROR,
C_REQUEST,
C_REQUEST_TARGET,
C_RESOLVE,
C_RETRY,
C_RETRY_ALL_ERRORS,
C_RETRY_CONNREFUSED,
C_RETRY_DELAY,
C_RETRY_MAX_TIME,
C_SASL_AUTHZID,
C_SASL_IR,
C_SERVICE_NAME,
C_SESSIONID,
C_SHOW_ERROR,
C_SHOW_HEADERS,
C_SILENT,
C_SOCKS4,
C_SOCKS4A,
C_SOCKS5,
C_SOCKS5_BASIC,
C_SOCKS5_GSSAPI,
C_SOCKS5_GSSAPI_NEC,
C_SOCKS5_GSSAPI_SERVICE,
C_SOCKS5_HOSTNAME,
C_SPEED_LIMIT,
C_SPEED_TIME,
C_SSL,
C_SSL_ALLOW_BEAST,
C_SSL_AUTO_CLIENT_CERT,
C_SSL_NO_REVOKE,
C_SSL_REQD,
C_SSL_REVOKE_BEST_EFFORT,
C_SSLV2,
C_SSLV3,
C_STDERR,
C_STYLED_OUTPUT,
C_SUPPRESS_CONNECT_HEADERS,
C_TCP_FASTOPEN,
C_TCP_NODELAY,
C_TELNET_OPTION,
C_TEST_EVENT,
C_TFTP_BLKSIZE,
C_TFTP_NO_OPTIONS,
C_TIME_COND,
C_TLS_MAX,
C_TLS13_CIPHERS,
C_TLSAUTHTYPE,
C_TLSPASSWORD,
C_TLSUSER,
C_TLSV1,
C_TLSV1_0,
C_TLSV1_1,
C_TLSV1_2,
C_TLSV1_3,
C_TR_ENCODING,
C_TRACE,
C_TRACE_ASCII,
C_TRACE_CONFIG,
C_TRACE_IDS,
C_TRACE_TIME,
C_IP_TOS,
C_UNIX_SOCKET,
C_UPLOAD_FILE,
C_URL,
C_URL_QUERY,
C_USE_ASCII,
C_USER,
C_USER_AGENT,
C_VARIABLE,
C_VERBOSE,
C_VERSION,
C_VLAN_PRIORITY,
C_WDEBUG,
C_WRITE_OUT,
C_XATTR
} cmdline_t;
struct LongShort {
const char *lname; /* long name option */
enum {
ARG_NONE, /* stand-alone but not a boolean */
ARG_BOOL, /* accepts a --no-[name] prefix */
ARG_STRG, /* requires an argument */
ARG_FILE /* requires an argument, usually a filename */
} desc;
char letter; /* short name option or ' ' */
cmdline_t cmd;
};
/* this array MUST be alphasorted based on the 'lname' */
static const struct LongShort aliases[]= {
{"abstract-unix-socket", ARG_FILE, ' ', C_ABSTRACT_UNIX_SOCKET},
{"alpn", ARG_BOOL, ' ', C_ALPN},
{"alpn", ARG_BOOL|ARG_NO, ' ', C_ALPN},
{"alt-svc", ARG_STRG, ' ', C_ALT_SVC},
{"anyauth", ARG_BOOL, ' ', C_ANYAUTH},
{"append", ARG_BOOL, 'a', C_APPEND},
{"aws-sigv4", ARG_STRG, ' ', C_AWS_SIGV4},
{"basic", ARG_BOOL, ' ', C_BASIC},
{"buffer", ARG_BOOL, 'N', C_BUFFER},
{"buffer", ARG_BOOL|ARG_NO, 'N', C_BUFFER},
{"ca-native", ARG_BOOL, ' ', C_CA_NATIVE},
{"cacert", ARG_FILE, ' ', C_CACERT},
{"capath", ARG_FILE, ' ', C_CAPATH},
@ -378,7 +88,7 @@ static const struct LongShort aliases[]= {
{"cert-status", ARG_BOOL, ' ', C_CERT_STATUS},
{"cert-type", ARG_STRG, ' ', C_CERT_TYPE},
{"ciphers", ARG_STRG, ' ', C_CIPHERS},
{"clobber", ARG_BOOL, ' ', C_CLOBBER},
{"clobber", ARG_BOOL|ARG_NO, ' ', C_CLOBBER},
{"compressed", ARG_BOOL, ' ', C_COMPRESSED},
{"compressed-ssh", ARG_BOOL, ' ', C_COMPRESSED_SSH},
{"config", ARG_FILE, 'K', C_CONFIG},
@ -468,7 +178,7 @@ static const struct LongShort aliases[]= {
{"ipv6", ARG_NONE, '6', C_IPV6},
{"json", ARG_STRG, ' ', C_JSON},
{"junk-session-cookies", ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES},
{"keepalive", ARG_BOOL, ' ', C_KEEPALIVE},
{"keepalive", ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE},
{"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT},
{"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME},
{"key", ARG_FILE, ' ', C_KEY},
@ -498,7 +208,7 @@ static const struct LongShort aliases[]= {
{"netrc-optional", ARG_BOOL, ' ', C_NETRC_OPTIONAL},
{"next", ARG_NONE, ':', C_NEXT},
{"noproxy", ARG_STRG, ' ', C_NOPROXY},
{"npn", ARG_BOOL, ' ', C_NPN},
{"npn", ARG_BOOL|ARG_NO, ' ', C_NPN},
{"ntlm", ARG_BOOL, ' ', C_NTLM},
{"ntlm-wb", ARG_BOOL, ' ', C_NTLM_WB},
{"oauth2-bearer", ARG_STRG, ' ', C_OAUTH2_BEARER},
@ -515,7 +225,7 @@ static const struct LongShort aliases[]= {
{"post303", ARG_BOOL, ' ', C_POST303},
{"preproxy", ARG_STRG, ' ', C_PREPROXY},
{"progress-bar", ARG_BOOL, '#', C_PROGRESS_BAR},
{"progress-meter", ARG_BOOL, ' ', C_PROGRESS_METER},
{"progress-meter", ARG_BOOL|ARG_NO, ' ', C_PROGRESS_METER},
{"proto", ARG_STRG, ' ', C_PROTO},
{"proto-default", ARG_STRG, ' ', C_PROTO_DEFAULT},
{"proto-redir", ARG_STRG, ' ', C_PROTO_REDIR},
@ -573,7 +283,7 @@ static const struct LongShort aliases[]= {
{"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID},
{"sasl-ir", ARG_BOOL, ' ', C_SASL_IR},
{"service-name", ARG_STRG, ' ', C_SERVICE_NAME},
{"sessionid", ARG_BOOL, ' ', C_SESSIONID},
{"sessionid", ARG_BOOL|ARG_NO, ' ', C_SESSIONID},
{"show-error", ARG_BOOL, 'S', C_SHOW_ERROR},
{"show-headers", ARG_BOOL, 'i', C_SHOW_HEADERS},
{"silent", ARG_BOOL, 's', C_SILENT},
@ -1029,7 +739,7 @@ static int findarg(const void *a, const void *b)
return strcmp(aa->lname, bb->lname);
}
static const struct LongShort *single(char letter)
const struct LongShort *findshortopt(char letter)
{
static const struct LongShort *singles[128 - ' ']; /* ASCII => pointer */
static bool singles_done = FALSE;
@ -1295,6 +1005,15 @@ static ParameterError set_rate(struct GlobalConfig *global,
return err;
}
const struct LongShort *findlongopt(const char *opt)
{
struct LongShort key;
key.lname = opt;
return bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]),
sizeof(aliases[0]), findarg);
}
ParameterError getparameter(const char *flag, /* f or -long-flag */
char *nextarg, /* NULL if unset */
@ -1336,7 +1055,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
const char *word = ('-' == flag[0]) ? flag + 2 : flag;
bool noflagged = FALSE;
bool expand = FALSE;
struct LongShort key;
if(!strncmp(word, "no-", 3)) {
/* disable this option but ignore the "no-" part when looking for it */
@ -1349,10 +1067,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
word += 7;
expand = TRUE;
}
key.lname = word;
a = bsearch(&key, aliases, sizeof(aliases)/sizeof(aliases[0]),
sizeof(aliases[0]), findarg);
a = findlongopt(word);
if(a) {
longopt = TRUE;
}
@ -1360,7 +1076,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
err = PARAM_OPTION_UNKNOWN;
goto error;
}
if(noflagged && (a->desc != ARG_BOOL)) {
if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL)) {
/* --no- prefixed an option that is not boolean! */
err = PARAM_NO_NOT_BOOLEAN;
goto error;
@ -1369,8 +1085,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
struct curlx_dynbuf nbuf;
bool replaced;
if((a->desc != ARG_STRG) &&
(a->desc != ARG_FILE)) {
if((ARGTYPE(a->desc) != ARG_STRG) &&
(ARGTYPE(a->desc) != ARG_FILE)) {
/* --expand on an option that is not a string or a filename */
err = PARAM_EXPAND_ERROR;
goto error;
@ -1397,15 +1113,15 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
cmdline_t cmd;
if(!longopt && !a) {
a = single(*parse);
a = findshortopt(*parse);
if(!a) {
err = PARAM_OPTION_UNKNOWN;
break;
}
}
letter = a->letter;
cmd = a->cmd;
if(a->desc >= ARG_STRG) {
cmd = (cmdline_t)a->cmd;
if(ARGTYPE(a->desc) >= ARG_STRG) {
/* this option requires an extra parameter */
if(!longopt && parse[1]) {
nextarg = (char *)&parse[1]; /* this is the actual extra parameter */
@ -1422,7 +1138,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
*usedarg = TRUE; /* mark it as used */
}
if((a->desc == ARG_FILE) &&
if((ARGTYPE(a->desc) == ARG_FILE) &&
(nextarg[0] == '-') && nextarg[1]) {
/* if the filename looks like a command line option */
warnf(global, "The filename argument '%s' looks like a flag.",
@ -1434,7 +1150,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
nextarg);
}
}
else if((a->desc == ARG_NONE) && !toggle) {
else if((ARGTYPE(a->desc) == ARG_NONE) && !toggle) {
err = PARAM_NO_PREFIX;
break;
}

View File

@ -25,6 +25,301 @@
***************************************************************************/
#include "tool_setup.h"
/* one enum for every command line option. The name is the verbatim long
option name, but in uppercase with periods and minuses replaced with
underscores using a "C_" prefix. */
typedef enum {
C_ABSTRACT_UNIX_SOCKET,
C_ALPN,
C_ALT_SVC,
C_ANYAUTH,
C_APPEND,
C_AWS_SIGV4,
C_BASIC,
C_BUFFER,
C_CA_NATIVE,
C_CACERT,
C_CAPATH,
C_CERT,
C_CERT_STATUS,
C_CERT_TYPE,
C_CIPHERS,
C_CLOBBER,
C_COMPRESSED,
C_COMPRESSED_SSH,
C_CONFIG,
C_CONNECT_TIMEOUT,
C_CONNECT_TO,
C_CONTINUE_AT,
C_COOKIE,
C_COOKIE_JAR,
C_CREATE_DIRS,
C_CREATE_FILE_MODE,
C_CRLF,
C_CRLFILE,
C_CURVES,
C_DATA,
C_DATA_ASCII,
C_DATA_BINARY,
C_DATA_RAW,
C_DATA_URLENCODE,
C_DELEGATION,
C_DIGEST,
C_DISABLE,
C_DISABLE_EPRT,
C_DISABLE_EPSV,
C_DISALLOW_USERNAME_IN_URL,
C_DNS_INTERFACE,
C_DNS_IPV4_ADDR,
C_DNS_IPV6_ADDR,
C_DNS_SERVERS,
C_DOH_CERT_STATUS,
C_DOH_INSECURE,
C_DOH_URL,
C_DUMP_CA_EMBED,
C_DUMP_HEADER,
C_ECH,
C_EGD_FILE,
C_ENGINE,
C_EPRT,
C_EPSV,
C_ETAG_COMPARE,
C_ETAG_SAVE,
C_EXPECT100_TIMEOUT,
C_FAIL,
C_FAIL_EARLY,
C_FAIL_WITH_BODY,
C_FALSE_START,
C_FORM,
C_FORM_ESCAPE,
C_FORM_STRING,
C_FTP_ACCOUNT,
C_FTP_ALTERNATIVE_TO_USER,
C_FTP_CREATE_DIRS,
C_FTP_METHOD,
C_FTP_PASV,
C_FTP_PORT,
C_FTP_PRET,
C_FTP_SKIP_PASV_IP,
C_FTP_SSL,
C_FTP_SSL_CCC,
C_FTP_SSL_CCC_MODE,
C_FTP_SSL_CONTROL,
C_FTP_SSL_REQD,
C_GET,
C_GLOBOFF,
C_HAPPY_EYEBALLS_TIMEOUT_MS,
C_HAPROXY_CLIENTIP,
C_HAPROXY_PROTOCOL,
C_HEAD,
C_HEADER,
C_HELP,
C_HOSTPUBMD5,
C_HOSTPUBSHA256,
C_HSTS,
C_HTTP0_9,
C_HTTP1_0,
C_HTTP1_1,
C_HTTP2,
C_HTTP2_PRIOR_KNOWLEDGE,
C_HTTP3,
C_HTTP3_ONLY,
C_IGNORE_CONTENT_LENGTH,
C_INCLUDE,
C_INSECURE,
C_INTERFACE,
C_IPFS_GATEWAY,
C_IPV4,
C_IPV6,
C_JSON,
C_JUNK_SESSION_COOKIES,
C_KEEPALIVE,
C_KEEPALIVE_CNT,
C_KEEPALIVE_TIME,
C_KEY,
C_KEY_TYPE,
C_KRB,
C_KRB4,
C_LIBCURL,
C_LIMIT_RATE,
C_LIST_ONLY,
C_LOCAL_PORT,
C_LOCATION,
C_LOCATION_TRUSTED,
C_LOGIN_OPTIONS,
C_MAIL_AUTH,
C_MAIL_FROM,
C_MAIL_RCPT,
C_MAIL_RCPT_ALLOWFAILS,
C_MANUAL,
C_MAX_FILESIZE,
C_MAX_REDIRS,
C_MAX_TIME,
C_METALINK,
C_MPTCP,
C_NEGOTIATE,
C_NETRC,
C_NETRC_FILE,
C_NETRC_OPTIONAL,
C_NEXT,
C_NOPROXY,
C_NPN,
C_NTLM,
C_NTLM_WB,
C_OAUTH2_BEARER,
C_OUTPUT,
C_OUTPUT_DIR,
C_PARALLEL,
C_PARALLEL_IMMEDIATE,
C_PARALLEL_MAX,
C_PASS,
C_PATH_AS_IS,
C_PINNEDPUBKEY,
C_POST301,
C_POST302,
C_POST303,
C_PREPROXY,
C_PROGRESS_BAR,
C_PROGRESS_METER,
C_PROTO,
C_PROTO_DEFAULT,
C_PROTO_REDIR,
C_PROXY,
C_PROXY_ANYAUTH,
C_PROXY_BASIC,
C_PROXY_CA_NATIVE,
C_PROXY_CACERT,
C_PROXY_CAPATH,
C_PROXY_CERT,
C_PROXY_CERT_TYPE,
C_PROXY_CIPHERS,
C_PROXY_CRLFILE,
C_PROXY_DIGEST,
C_PROXY_HEADER,
C_PROXY_HTTP2,
C_PROXY_INSECURE,
C_PROXY_KEY,
C_PROXY_KEY_TYPE,
C_PROXY_NEGOTIATE,
C_PROXY_NTLM,
C_PROXY_PASS,
C_PROXY_PINNEDPUBKEY,
C_PROXY_SERVICE_NAME,
C_PROXY_SSL_ALLOW_BEAST,
C_PROXY_SSL_AUTO_CLIENT_CERT,
C_PROXY_TLS13_CIPHERS,
C_PROXY_TLSAUTHTYPE,
C_PROXY_TLSPASSWORD,
C_PROXY_TLSUSER,
C_PROXY_TLSV1,
C_PROXY_USER,
C_PROXY1_0,
C_PROXYTUNNEL,
C_PUBKEY,
C_QUOTE,
C_RANDOM_FILE,
C_RANGE,
C_RATE,
C_RAW,
C_REFERER,
C_REMOTE_HEADER_NAME,
C_REMOTE_NAME,
C_REMOTE_NAME_ALL,
C_REMOTE_TIME,
C_REMOVE_ON_ERROR,
C_REQUEST,
C_REQUEST_TARGET,
C_RESOLVE,
C_RETRY,
C_RETRY_ALL_ERRORS,
C_RETRY_CONNREFUSED,
C_RETRY_DELAY,
C_RETRY_MAX_TIME,
C_SASL_AUTHZID,
C_SASL_IR,
C_SERVICE_NAME,
C_SESSIONID,
C_SHOW_ERROR,
C_SHOW_HEADERS,
C_SILENT,
C_SOCKS4,
C_SOCKS4A,
C_SOCKS5,
C_SOCKS5_BASIC,
C_SOCKS5_GSSAPI,
C_SOCKS5_GSSAPI_NEC,
C_SOCKS5_GSSAPI_SERVICE,
C_SOCKS5_HOSTNAME,
C_SPEED_LIMIT,
C_SPEED_TIME,
C_SSL,
C_SSL_ALLOW_BEAST,
C_SSL_AUTO_CLIENT_CERT,
C_SSL_NO_REVOKE,
C_SSL_REQD,
C_SSL_REVOKE_BEST_EFFORT,
C_SSLV2,
C_SSLV3,
C_STDERR,
C_STYLED_OUTPUT,
C_SUPPRESS_CONNECT_HEADERS,
C_TCP_FASTOPEN,
C_TCP_NODELAY,
C_TELNET_OPTION,
C_TEST_EVENT,
C_TFTP_BLKSIZE,
C_TFTP_NO_OPTIONS,
C_TIME_COND,
C_TLS_MAX,
C_TLS13_CIPHERS,
C_TLSAUTHTYPE,
C_TLSPASSWORD,
C_TLSUSER,
C_TLSV1,
C_TLSV1_0,
C_TLSV1_1,
C_TLSV1_2,
C_TLSV1_3,
C_TR_ENCODING,
C_TRACE,
C_TRACE_ASCII,
C_TRACE_CONFIG,
C_TRACE_IDS,
C_TRACE_TIME,
C_IP_TOS,
C_UNIX_SOCKET,
C_UPLOAD_FILE,
C_URL,
C_URL_QUERY,
C_USE_ASCII,
C_USER,
C_USER_AGENT,
C_VARIABLE,
C_VERBOSE,
C_VERSION,
C_VLAN_PRIORITY,
C_WDEBUG,
C_WRITE_OUT,
C_XATTR
} cmdline_t;
#define ARG_NONE 0 /* stand-alone but not a boolean */
#define ARG_BOOL 1 /* accepts a --no-[name] prefix */
#define ARG_STRG 2 /* requires an argument */
#define ARG_FILE 3 /* requires an argument, usually a filename */
#define ARG_TYPEMASK 0x03
#define ARGTYPE(x) ((x) & ARG_TYPEMASK)
#define ARG_NO 0x80 /* set if the option is documented as --no-* */
struct LongShort {
const char *lname; /* long name option */
unsigned char desc; /* type, see ARG_* */
char letter; /* short name option or ' ' */
unsigned short cmd;
};
typedef enum {
PARAM_OK = 0,
PARAM_OPTION_AMBIGUOUS,
@ -57,6 +352,9 @@ typedef enum {
struct GlobalConfig;
struct OperationConfig;
const struct LongShort *findlongopt(const char *opt);
const struct LongShort *findshortopt(char letter);
ParameterError getparameter(const char *flag, char *nextarg,
argv_item_t cleararg,
bool *usedarg,

View File

@ -31,6 +31,8 @@
#include "tool_util.h"
#include "tool_version.h"
#include "tool_cb_prg.h"
#include "tool_hugehelp.h"
#include "tool_getparam.h"
#include "terminal.h"
#include "memdebug.h" /* keep this as LAST include */
@ -160,18 +162,84 @@ static void get_categories_list(unsigned int width)
}
}
#ifdef USE_MANUAL
void inithelpscan(struct scan_ctx *ctx,
const char *trigger,
const char *arg,
const char *endarg)
{
ctx->trigger = trigger;
ctx->tlen = strlen(trigger);
ctx->arg = arg;
ctx->flen = strlen(arg);
ctx->endarg = endarg;
ctx->elen = strlen(endarg);
DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) ||
(ctx->flen < sizeof(ctx->rbuf)));
ctx->show = 0;
ctx->olen = 0;
memset(ctx->rbuf, 0, sizeof(ctx->rbuf));
}
bool helpscan(unsigned char *buf, size_t len, struct scan_ctx *ctx)
{
size_t i;
for(i = 0; i < len; i++) {
if(!ctx->show) {
/* wait for the trigger */
memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->tlen - 1);
ctx->rbuf[ctx->tlen - 1] = buf[i];
if(!memcmp(ctx->rbuf, ctx->trigger, ctx->tlen))
ctx->show++;
continue;
}
/* past the trigger */
if(ctx->show == 1) {
memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->flen - 1);
ctx->rbuf[ctx->flen - 1] = buf[i];
if(!memcmp(ctx->rbuf, ctx->arg, ctx->flen)) {
/* match, now output until endarg */
fputs(&ctx->arg[1], stdout);
ctx->show++;
}
continue;
}
/* show until the end */
memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->elen - 1);
ctx->rbuf[ctx->elen - 1] = buf[i];
if(!memcmp(ctx->rbuf, ctx->endarg, ctx->elen))
return FALSE;
if(buf[i] == '\n') {
DEBUGASSERT(ctx->olen < sizeof(ctx->obuf));
ctx->obuf[ctx->olen++] = 0;
ctx->olen = 0;
puts(ctx->obuf);
}
else
ctx->obuf[ctx->olen++] = buf[i];
}
return TRUE;
}
#endif
void tool_help(char *category)
{
unsigned int cols = get_terminal_columns();
puts("Usage: curl [options...] <url>");
/* If no category was provided */
if(!category) {
const char *category_note = "\nThis is not the full help; this "
"menu is split into categories.\nUse \"--help category\" to get "
"an overview of all categories, which are:";
const char *category_note2 = "For all options use the manual"
" or \"--help all\".";
const char *category_note2 =
"Use \"--help all\" to list all options"
#ifdef USE_MANUAL
"\nUse \"--help [option]\" to view documentation for a given option"
#endif
;
puts("Usage: curl [options...] <url>");
print_category(CURLHELP_IMPORTANT, cols);
puts(category_note);
get_categories_list(cols);
@ -184,6 +252,48 @@ void tool_help(char *category)
/* Lets handle the string "category" differently to not print an errormsg */
else if(curl_strequal(category, "category"))
get_categories();
else if(category[0] == '-') {
#ifdef USE_MANUAL
/* command line option help */
const struct LongShort *a = NULL;
if(category[1] == '-') {
char *lookup = &category[2];
bool noflagged = FALSE;
if(!strncmp(lookup, "no-", 3)) {
lookup += 3;
noflagged = TRUE;
}
a = findlongopt(lookup);
if(noflagged && (ARGTYPE(a->desc) != ARG_BOOL))
/* a --no- prefix for a non-boolean is not specifying a proper
option */
a = NULL;
}
else if(!category[2])
a = findshortopt(category[1]);
if(!a) {
fprintf(tool_stderr, "Incorrect option name to show help for,"
" see curl -h\n");
}
else {
char cmdbuf[80];
if(a->letter != ' ')
msnprintf(cmdbuf, sizeof(cmdbuf), "\n -%c, --", a->letter);
else if(a->desc & ARG_NO)
msnprintf(cmdbuf, sizeof(cmdbuf), "\n --no-%s", a->lname);
else
msnprintf(cmdbuf, sizeof(cmdbuf), "\n %s", category);
if(a->cmd == C_XATTR)
/* this is the last option, which then ends when FILES starts */
showhelp("\nALL OPTIONS\n", cmdbuf, "\nFILES");
else
showhelp("\nALL OPTIONS\n", cmdbuf, "\n -");
}
#else
fprintf(tool_stderr, "Cannot comply. "
"This curl was built without built-in manual\n");
#endif
}
/* Otherwise print category and handle the case if the cat was not found */
else if(get_category_content(category, cols)) {
puts("Unknown category provided, here is a list of all categories:\n");

View File

@ -28,6 +28,24 @@
void tool_help(char *category);
void tool_list_engines(void);
void tool_version_info(void);
struct scan_ctx {
const char *trigger;
size_t tlen;
const char *arg;
size_t flen;
const char *endarg;
size_t elen;
size_t olen;
char rbuf[40];
char obuf[80];
unsigned char show; /* start as at 0.
trigger match moves it to 1
arg match moves it to 2
endarg stops the search */
};
void inithelpscan(struct scan_ctx *ctx, const char *trigger,
const char *arg, const char *endarg);
bool helpscan(unsigned char *buf, size_t len, struct scan_ctx *ctx);
struct helptxt {
const char *opt;

View File

@ -27,5 +27,12 @@
void hugehelp(void)
{
puts("This is a silly replacement for the actual file.");
puts("built-in manual was disabled at build-time");
}
void showhelp(const char *arg, const char *endarg)
{
(void)arg;
(void)endarg;
hugehelp();
}

View File

@ -25,6 +25,8 @@
***************************************************************************/
#include "tool_setup.h"
void showhelp(const char *trigger, const char *arg, const char *endarg);
#ifdef USE_MANUAL
void hugehelp(void);
#else

View File

@ -266,7 +266,7 @@ const struct helptxt helptext[] = {
{"-H, --header <header/@file>",
"Pass custom header(s) to server",
CURLHELP_HTTP | CURLHELP_IMAP | CURLHELP_SMTP},
{"-h, --help <category>",
{"-h, --help <subject>",
"Get help for commands",
CURLHELP_IMPORTANT | CURLHELP_CURL},
{" --hostpubmd5 <md5>",

View File

@ -50,6 +50,7 @@ TESTSCRIPTS = \
test1486.pl \
test1488.pl \
test1544.pl \
test1707.pl \
test971.pl
EXTRA_DIST = \

View File

@ -221,7 +221,8 @@ test1670 test1671 \
\
test1680 test1681 test1682 test1683 \
\
test1700 test1701 test1702 test1703 test1704 test1705 test1706 \
test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \
test1708 test1709 test1710 \
\
test1800 test1801 \
\

View File

@ -13,6 +13,9 @@
#
# Client-side
<client>
<features>
manual
</features>
<server>
none
</server>
@ -34,7 +37,7 @@ curl important --help
Usage: curl [options...] <url>
-d, --data <data> HTTP POST data
-f, --fail Fail fast with no output on HTTP errors
-h, --help <category> Get help for commands
-h, --help <subject> Get help for commands
-o, --output <file> Write to file instead of stdout
-O, --remote-name Write output to file named as remote file
-i, --show-headers Show response headers in output
@ -50,7 +53,8 @@ Use "--help category" to get an overview of all categories, which are:
auth, connection, curl, deprecated, dns, file, ftp, global, http, imap, ldap,
output, pop3, post, proxy, scp, sftp, smtp, ssh, telnet, tftp, timeout, tls,
upload, verbose.
For all options use the manual or "--help all".
Use "--help all" to list all options
Use "--help [option]" to view documentation for a given option
</stdout>
</verify>
</testcase>

View File

@ -31,7 +31,6 @@ curl invalid category --help
0
</errorcode>
<stdout mode="text">
Usage: curl [options...] <url>
Unknown category provided, here is a list of all categories:
auth Authentication methods

View File

@ -35,7 +35,6 @@ curl file category --help
0
</errorcode>
<stdout mode="text">
Usage: curl [options...] <url>
file: FILE protocol
--create-file-mode <mode> File mode for created files
-I, --head Show document info only

View File

@ -35,7 +35,6 @@ curl file category --help with lower/upper mix
0
</errorcode>
<stdout mode="text">
Usage: curl [options...] <url>
file: FILE protocol
--create-file-mode <mode> File mode for created files
-I, --head Show document info only

27
tests/data/test1707 Normal file
View File

@ -0,0 +1,27 @@
<testcase>
<info>
<keywords>
curl
</keywords>
</info>
#
# Client-side
<client>
<features>
manual
</features>
<server>
none
</server>
<name>
Verify curl -h --insecure
</name>
<command type="perl">
%SRCDIR/test1707.pl %CURL --insecure %LOGDIR/help%TESTNUMBER ../docs/cmdline-opts/curl.txt
</command>
</client>
</testcase>

27
tests/data/test1708 Normal file
View File

@ -0,0 +1,27 @@
<testcase>
<info>
<keywords>
curl
</keywords>
</info>
#
# Client-side
<client>
<features>
manual
</features>
<server>
none
</server>
<name>
Verify curl -h -F
</name>
<command type="perl">
%SRCDIR/test1707.pl %CURL -F %LOGDIR/help%TESTNUMBER ../docs/cmdline-opts/curl.txt
</command>
</client>
</testcase>

32
tests/data/test1709 Normal file
View File

@ -0,0 +1,32 @@
<testcase>
<info>
<keywords>
curl
</keywords>
</info>
#
# Client-side
<client>
<features>
manual
</features>
<server>
none
</server>
<name>
Verify curl -h --badone
</name>
<command>
-h --badone
</command>
</client>
<verify>
<stderr mode="text">
Incorrect option name to show help for, see curl -h
</stderr>
</verify>
</testcase>

27
tests/data/test1710 Normal file
View File

@ -0,0 +1,27 @@
<testcase>
<info>
<keywords>
curl
</keywords>
</info>
#
# Client-side
<client>
<features>
manual
</features>
<server>
none
</server>
<name>
Verify curl -h --no-clobber
</name>
<command type="perl">
%SRCDIR/test1707.pl %CURL --no-clobber %LOGDIR/help%TESTNUMBER ../docs/cmdline-opts/curl.txt
</command>
</client>
</testcase>

121
tests/test1707.pl Executable file
View File

@ -0,0 +1,121 @@
#!/usr/bin/env perl
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
#
# This script grew out of help from Przemyslaw Iskra and Balint Szilakszi
# a late evening in the #curl IRC channel.
#
use strict;
use warnings;
my $curl = shift @ARGV;
my $opt = shift @ARGV;
my $output = shift @ARGV;
my $txt = shift @ARGV;
my $longopt;
my $shortopt;
if($opt =~ /^--/) {
$longopt = $opt;
}
else {
$shortopt = $opt;
}
# first run the help command
system("$curl -h $opt > $output");
my @curlout;
open(O, "<$output");
push @curlout, <O>;
close(O);
# figure out the short+long option combo using -h all*/
open(C, "$curl -h all|");
if($shortopt) {
while(<C>) {
if(/^ +$opt, ([^ ]*)/) {
$longopt = $1;
last;
}
}
}
else {
while(<C>) {
my $f = $_;
if(/ $opt /) {
if($f =~ /^ *(-(.)), $longopt/) {
$shortopt = $1;
}
last;
}
}
}
close(C);
my $fullopt;
if($shortopt) {
$fullopt = "$shortopt, $longopt";
}
else {
$fullopt = $longopt;
}
open(R, "<$txt");
my $show = 0;
my @txtout;
while(<R>) {
if(/^ $fullopt/) {
$show = 1;
}
elsif(/^ -/ && $show) {
last;
}
if($show) {
push @txtout, $_;
}
}
close(R);
my $error;
if(scalar(@curlout) != scalar(@txtout)) {
printf "curl -h $opt is %d lines, $txt says %d lines\n",
scalar(@curlout), scalar(@txtout);
$error++;
}
else {
# same size, compare line by line
for my $i (0 .. $#curlout) {
# trim CRLF from the data
$curlout[$i] =~ s/[\r\n]//g;
$txtout[$i] =~ s/[\r\n]//g;
if($curlout[$i] ne $txtout[$i]) {
printf "Line %d\n", $i;
printf "-h : %s\n", $curlout[$i];
printf "file : %s\n", $txtout[$i];
$error++;
}
}
}
exit $error;