curl/src/tool_help.c
Daniel Stenberg 6946b3a799
curl: make warnings and other messages aware of terminal width
This removes unnecessary line wraps when the terminal is wider than 79
columns and it also makes messages look better in narrower terminals.

The get_terminal_columns() function is not split out into its own source
file.

Suggested-by: Elliott Balsley
Fixes #13804
Closes #13808
2024-05-28 23:12:32 +02:00

246 lines
7.7 KiB
C

/***************************************************************************
* _ _ ____ _
* 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
*
***************************************************************************/
#include "tool_setup.h"
#define ENABLE_CURLX_PRINTF
/* use our own printf() functions */
#include "curlx.h"
#include "tool_help.h"
#include "tool_libinfo.h"
#include "tool_util.h"
#include "tool_version.h"
#include "tool_cb_prg.h"
#include "terminal.h"
#include "memdebug.h" /* keep this as LAST include */
#ifdef MSDOS
# define USE_WATT32
#endif
struct category_descriptors {
const char *opt;
const char *desc;
curlhelp_t category;
};
static const struct category_descriptors categories[] = {
{"auth", "Different types of authentication methods", CURLHELP_AUTH},
{"connection", "Low level networking operations",
CURLHELP_CONNECTION},
{"curl", "The command line tool itself", CURLHELP_CURL},
{"dns", "General DNS options", CURLHELP_DNS},
{"file", "FILE protocol options", CURLHELP_FILE},
{"ftp", "FTP protocol options", CURLHELP_FTP},
{"http", "HTTP and HTTPS protocol options", CURLHELP_HTTP},
{"imap", "IMAP protocol options", CURLHELP_IMAP},
/* important is left out because it is the default help page */
{"misc", "Options that don't fit into any other category", CURLHELP_MISC},
{"output", "Filesystem output", CURLHELP_OUTPUT},
{"pop3", "POP3 protocol options", CURLHELP_POP3},
{"post", "HTTP Post specific options", CURLHELP_POST},
{"proxy", "All options related to proxies", CURLHELP_PROXY},
{"scp", "SCP protocol options", CURLHELP_SCP},
{"sftp", "SFTP protocol options", CURLHELP_SFTP},
{"smtp", "SMTP protocol options", CURLHELP_SMTP},
{"ssh", "SSH protocol options", CURLHELP_SSH},
{"telnet", "TELNET protocol options", CURLHELP_TELNET},
{"tftp", "TFTP protocol options", CURLHELP_TFTP},
{"tls", "All TLS/SSL related options", CURLHELP_TLS},
{"ech", "All Encrypted Client Hello (ECH) options", CURLHELP_ECH},
{"upload", "All options for uploads",
CURLHELP_UPLOAD},
{"verbose", "Options related to any kind of command line output of curl",
CURLHELP_VERBOSE},
{NULL, NULL, CURLHELP_HIDDEN}
};
static void print_category(curlhelp_t category, unsigned int cols)
{
unsigned int i;
size_t longopt = 5;
size_t longdesc = 5;
for(i = 0; helptext[i].opt; ++i) {
size_t len;
if(!(helptext[i].categories & category))
continue;
len = strlen(helptext[i].opt);
if(len > longopt)
longopt = len;
len = strlen(helptext[i].desc);
if(len > longdesc)
longdesc = len;
}
if(longopt + longdesc > cols)
longopt = cols - longdesc;
for(i = 0; helptext[i].opt; ++i)
if(helptext[i].categories & category) {
size_t opt = longopt;
size_t desclen = strlen(helptext[i].desc);
if(opt + desclen >= (cols - 2)) {
if(desclen < (cols - 2))
opt = (cols - 3) - desclen;
else
opt = 0;
}
printf(" %-*s %s\n", (int)opt, helptext[i].opt, helptext[i].desc);
}
}
/* Prints category if found. If not, it returns 1 */
static int get_category_content(const char *category, unsigned int cols)
{
unsigned int i;
for(i = 0; categories[i].opt; ++i)
if(curl_strequal(categories[i].opt, category)) {
printf("%s: %s\n", categories[i].opt, categories[i].desc);
print_category(categories[i].category, cols);
return 0;
}
return 1;
}
/* Prints all categories and their description */
static void get_categories(void)
{
unsigned int i;
for(i = 0; categories[i].opt; ++i)
printf(" %-11s %s\n", categories[i].opt, categories[i].desc);
}
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 stripped into categories.\nUse \"--help category\" to get "
"an overview of all categories.\nFor all options use the manual"
" or \"--help all\".";
print_category(CURLHELP_IMPORTANT, cols);
puts(category_note);
}
/* Lets print everything if "all" was provided */
else if(curl_strequal(category, "all"))
/* Print everything except hidden */
print_category(~(CURLHELP_HIDDEN), cols);
/* Lets handle the string "category" differently to not print an errormsg */
else if(curl_strequal(category, "category"))
get_categories();
/* Otherwise print category and handle the case if the cat was not found */
else if(get_category_content(category, cols)) {
puts("Invalid category provided, here is a list of all categories:\n");
get_categories();
}
free(category);
}
static bool is_debug(void)
{
const char *const *builtin;
for(builtin = feature_names; *builtin; ++builtin)
if(curl_strequal("debug", *builtin))
return TRUE;
return FALSE;
}
void tool_version_info(void)
{
const char *const *builtin;
if(is_debug())
fprintf(tool_stderr, "WARNING: this libcurl is Debug-enabled, "
"do not use in production\n\n");
printf(CURL_ID "%s\n", curl_version());
#ifdef CURL_PATCHSTAMP
printf("Release-Date: %s, security patched: %s\n",
LIBCURL_TIMESTAMP, CURL_PATCHSTAMP);
#else
printf("Release-Date: %s\n", LIBCURL_TIMESTAMP);
#endif
if(built_in_protos[0]) {
const char *insert = NULL;
/* we have ipfs and ipns support if libcurl has http support */
for(builtin = built_in_protos; *builtin; ++builtin) {
if(insert) {
/* update insertion so ipfs will be printed in alphabetical order */
if(strcmp(*builtin, "ipfs") < 0)
insert = *builtin;
else
break;
}
else if(!strcmp(*builtin, "http")) {
insert = *builtin;
}
}
printf("Protocols:");
for(builtin = built_in_protos; *builtin; ++builtin) {
/* Special case: do not list rtmp?* protocols.
They may only appear together with "rtmp" */
if(!curl_strnequal(*builtin, "rtmp", 4) || !builtin[0][4])
printf(" %s", *builtin);
if(insert && insert == *builtin) {
printf(" ipfs ipns");
insert = NULL;
}
}
puts(""); /* newline */
}
if(feature_names[0]) {
printf("Features:");
for(builtin = feature_names; *builtin; ++builtin)
printf(" %s", *builtin);
puts(""); /* newline */
}
if(strcmp(CURL_VERSION, curlinfo->version)) {
printf("WARNING: curl and libcurl versions do not match. "
"Functionality may be affected.\n");
}
}
void tool_list_engines(void)
{
CURL *curl = curl_easy_init();
struct curl_slist *engines = NULL;
/* Get the list of engines */
curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
puts("Build-time engines:");
if(engines) {
for(; engines; engines = engines->next)
printf(" %s\n", engines->data);
}
else {
puts(" <none>");
}
/* Cleanup the list of engines */
curl_slist_free_all(engines);
curl_easy_cleanup(curl);
}