1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 #include "tool_setup.h"
25
26 #include "curlx.h"
27
28 #include "tool_help.h"
29 #include "tool_libinfo.h"
30 #include "tool_util.h"
31 #include "tool_version.h"
32 #include "tool_cb_prg.h"
33 #include "tool_hugehelp.h"
34 #include "tool_getparam.h"
35 #include "terminal.h"
36
37 #include "memdebug.h" /* keep this as LAST include */
38
39 #ifdef MSDOS
40 # define USE_WATT32
41 #endif
42
43 #ifndef ARRAYSIZE
44 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
45 #endif
46
47 struct category_descriptors {
48 const char *opt;
49 const char *desc;
50 unsigned int category;
51 };
52
53 static const struct category_descriptors categories[] = {
54 /* important is left out because it is the default help page */
55 {"auth", "Authentication methods", CURLHELP_AUTH},
56 {"connection", "Manage connections", CURLHELP_CONNECTION},
57 {"curl", "The command line tool itself", CURLHELP_CURL},
58 {"deprecated", "Legacy", CURLHELP_DEPRECATED},
59 {"dns", "Names and resolving", CURLHELP_DNS},
60 {"file", "FILE protocol", CURLHELP_FILE},
61 {"ftp", "FTP protocol", CURLHELP_FTP},
62 {"global", "Global options", CURLHELP_GLOBAL},
63 {"http", "HTTP and HTTPS protocol", CURLHELP_HTTP},
64 {"imap", "IMAP protocol", CURLHELP_IMAP},
65 {"ldap", "LDAP protocol", CURLHELP_LDAP},
66 {"output", "Filesystem output", CURLHELP_OUTPUT},
67 {"pop3", "POP3 protocol", CURLHELP_POP3},
68 {"post", "HTTP POST specific", CURLHELP_POST},
69 {"proxy", "Options for proxies", CURLHELP_PROXY},
70 {"scp", "SCP protocol", CURLHELP_SCP},
71 {"sftp", "SFTP protocol", CURLHELP_SFTP},
72 {"smtp", "SMTP protocol", CURLHELP_SMTP},
73 {"ssh", "SSH protocol", CURLHELP_SSH},
74 {"telnet", "TELNET protocol", CURLHELP_TELNET},
75 {"tftp", "TFTP protocol", CURLHELP_TFTP},
76 {"timeout", "Timeouts and delays", CURLHELP_TIMEOUT},
77 {"tls", "TLS/SSL related", CURLHELP_TLS},
78 {"upload", "Upload, sending data", CURLHELP_UPLOAD},
79 {"verbose", "Tracing, logging etc", CURLHELP_VERBOSE}
80 };
81
print_category(unsigned int category,unsigned int cols)82 static void print_category(unsigned int category, unsigned int cols)
83 {
84 unsigned int i;
85 size_t longopt = 5;
86 size_t longdesc = 5;
87
88 for(i = 0; helptext[i].opt; ++i) {
89 size_t len;
90 if(!(helptext[i].categories & category))
91 continue;
92 len = strlen(helptext[i].opt);
93 if(len > longopt)
94 longopt = len;
95 len = strlen(helptext[i].desc);
96 if(len > longdesc)
97 longdesc = len;
98 }
99 if(longopt + longdesc > cols)
100 longopt = cols - longdesc;
101
102 for(i = 0; helptext[i].opt; ++i)
103 if(helptext[i].categories & category) {
104 size_t opt = longopt;
105 size_t desclen = strlen(helptext[i].desc);
106 if(opt + desclen >= (cols - 2)) {
107 if(desclen < (cols - 2))
108 opt = (cols - 3) - desclen;
109 else
110 opt = 0;
111 }
112 printf(" %-*s %s\n", (int)opt, helptext[i].opt, helptext[i].desc);
113 }
114 }
115
116 /* Prints category if found. If not, it returns 1 */
get_category_content(const char * category,unsigned int cols)117 static int get_category_content(const char *category, unsigned int cols)
118 {
119 unsigned int i;
120 for(i = 0; i < ARRAYSIZE(categories); ++i)
121 if(curl_strequal(categories[i].opt, category)) {
122 printf("%s: %s\n", categories[i].opt, categories[i].desc);
123 print_category(categories[i].category, cols);
124 return 0;
125 }
126 return 1;
127 }
128
129 /* Prints all categories and their description */
get_categories(void)130 static void get_categories(void)
131 {
132 unsigned int i;
133 for(i = 0; i < ARRAYSIZE(categories); ++i)
134 printf(" %-11s %s\n", categories[i].opt, categories[i].desc);
135 }
136
137 /* Prints all categories as a comma-separated list of given width */
get_categories_list(unsigned int width)138 static void get_categories_list(unsigned int width)
139 {
140 unsigned int i;
141 size_t col = 0;
142 for(i = 0; i < ARRAYSIZE(categories); ++i) {
143 size_t len = strlen(categories[i].opt);
144 if(i == ARRAYSIZE(categories) - 1) {
145 /* final category */
146 if(col + len + 1 < width)
147 printf("%s.\n", categories[i].opt);
148 else
149 /* start a new line first */
150 printf("\n%s.\n", categories[i].opt);
151 }
152 else if(col + len + 2 < width) {
153 printf("%s, ", categories[i].opt);
154 col += len + 2;
155 }
156 else {
157 /* start a new line first */
158 printf("\n%s, ", categories[i].opt);
159 col = len + 2;
160 }
161 }
162 }
163
164 #ifdef USE_MANUAL
165
inithelpscan(struct scan_ctx * ctx,const char * trigger,const char * arg,const char * endarg)166 void inithelpscan(struct scan_ctx *ctx,
167 const char *trigger,
168 const char *arg,
169 const char *endarg)
170 {
171 ctx->trigger = trigger;
172 ctx->tlen = strlen(trigger);
173 ctx->arg = arg;
174 ctx->flen = strlen(arg);
175 ctx->endarg = endarg;
176 ctx->elen = strlen(endarg);
177 DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) ||
178 (ctx->flen < sizeof(ctx->rbuf)));
179 ctx->show = 0;
180 ctx->olen = 0;
181 memset(ctx->rbuf, 0, sizeof(ctx->rbuf));
182 }
183
helpscan(unsigned char * buf,size_t len,struct scan_ctx * ctx)184 bool helpscan(unsigned char *buf, size_t len, struct scan_ctx *ctx)
185 {
186 size_t i;
187 for(i = 0; i < len; i++) {
188 if(!ctx->show) {
189 /* wait for the trigger */
190 memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->tlen - 1);
191 ctx->rbuf[ctx->tlen - 1] = buf[i];
192 if(!memcmp(ctx->rbuf, ctx->trigger, ctx->tlen))
193 ctx->show++;
194 continue;
195 }
196 /* past the trigger */
197 if(ctx->show == 1) {
198 memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->flen - 1);
199 ctx->rbuf[ctx->flen - 1] = buf[i];
200 if(!memcmp(ctx->rbuf, ctx->arg, ctx->flen)) {
201 /* match, now output until endarg */
202 fputs(&ctx->arg[1], stdout);
203 ctx->show++;
204 }
205 continue;
206 }
207 /* show until the end */
208 memmove(&ctx->rbuf[0], &ctx->rbuf[1], ctx->elen - 1);
209 ctx->rbuf[ctx->elen - 1] = buf[i];
210 if(!memcmp(ctx->rbuf, ctx->endarg, ctx->elen))
211 return FALSE;
212
213 if(buf[i] == '\n') {
214 DEBUGASSERT(ctx->olen < sizeof(ctx->obuf));
215 if(ctx->olen == sizeof(ctx->obuf))
216 return FALSE; /* bail out */
217 ctx->obuf[ctx->olen++] = 0;
218 ctx->olen = 0;
219 puts(ctx->obuf);
220 }
221 else {
222 DEBUGASSERT(ctx->olen < sizeof(ctx->obuf));
223 if(ctx->olen == sizeof(ctx->obuf))
224 return FALSE; /* bail out */
225 ctx->obuf[ctx->olen++] = buf[i];
226 }
227 }
228 return TRUE;
229 }
230
231 #endif
232
tool_help(char * category)233 void tool_help(char *category)
234 {
235 unsigned int cols = get_terminal_columns();
236 /* If no category was provided */
237 if(!category) {
238 const char *category_note = "\nThis is not the full help; this "
239 "menu is split into categories.\nUse \"--help category\" to get "
240 "an overview of all categories, which are:";
241 const char *category_note2 =
242 "Use \"--help all\" to list all options"
243 #ifdef USE_MANUAL
244 "\nUse \"--help [option]\" to view documentation for a given option"
245 #endif
246 ;
247 puts("Usage: curl [options...] <url>");
248 print_category(CURLHELP_IMPORTANT, cols);
249 puts(category_note);
250 get_categories_list(cols);
251 puts(category_note2);
252 }
253 /* Lets print everything if "all" was provided */
254 else if(curl_strequal(category, "all"))
255 /* Print everything */
256 print_category(CURLHELP_ALL, cols);
257 /* Lets handle the string "category" differently to not print an errormsg */
258 else if(curl_strequal(category, "category"))
259 get_categories();
260 else if(category[0] == '-') {
261 #ifdef USE_MANUAL
262 /* command line option help */
263 const struct LongShort *a = NULL;
264 if(category[1] == '-') {
265 char *lookup = &category[2];
266 bool noflagged = FALSE;
267 if(!strncmp(lookup, "no-", 3)) {
268 lookup += 3;
269 noflagged = TRUE;
270 }
271 a = findlongopt(lookup);
272 if(a && noflagged && (ARGTYPE(a->desc) != ARG_BOOL))
273 /* a --no- prefix for a non-boolean is not specifying a proper
274 option */
275 a = NULL;
276 }
277 else if(!category[2])
278 a = findshortopt(category[1]);
279 if(!a) {
280 fprintf(tool_stderr, "Incorrect option name to show help for,"
281 " see curl -h\n");
282 }
283 else {
284 char cmdbuf[80];
285 if(a->letter != ' ')
286 msnprintf(cmdbuf, sizeof(cmdbuf), "\n -%c, --", a->letter);
287 else if(a->desc & ARG_NO)
288 msnprintf(cmdbuf, sizeof(cmdbuf), "\n --no-%s", a->lname);
289 else
290 msnprintf(cmdbuf, sizeof(cmdbuf), "\n %s", category);
291 if(a->cmd == C_XATTR)
292 /* this is the last option, which then ends when FILES starts */
293 showhelp("\nALL OPTIONS\n", cmdbuf, "\nFILES");
294 else
295 showhelp("\nALL OPTIONS\n", cmdbuf, "\n -");
296 }
297 #else
298 fprintf(tool_stderr, "Cannot comply. "
299 "This curl was built without built-in manual\n");
300 #endif
301 }
302 /* Otherwise print category and handle the case if the cat was not found */
303 else if(get_category_content(category, cols)) {
304 puts("Unknown category provided, here is a list of all categories:\n");
305 get_categories();
306 }
307 free(category);
308 }
309
is_debug(void)310 static bool is_debug(void)
311 {
312 const char *const *builtin;
313 for(builtin = feature_names; *builtin; ++builtin)
314 if(curl_strequal("debug", *builtin))
315 return TRUE;
316 return FALSE;
317 }
318
tool_version_info(void)319 void tool_version_info(void)
320 {
321 const char *const *builtin;
322 if(is_debug())
323 fprintf(tool_stderr, "WARNING: this libcurl is Debug-enabled, "
324 "do not use in production\n\n");
325
326 printf(CURL_ID "%s\n", curl_version());
327 #ifdef CURL_PATCHSTAMP
328 printf("Release-Date: %s, security patched: %s\n",
329 LIBCURL_TIMESTAMP, CURL_PATCHSTAMP);
330 #else
331 printf("Release-Date: %s\n", LIBCURL_TIMESTAMP);
332 #endif
333 if(built_in_protos[0]) {
334 #ifndef CURL_DISABLE_IPFS
335 const char *insert = NULL;
336 /* we have ipfs and ipns support if libcurl has http support */
337 for(builtin = built_in_protos; *builtin; ++builtin) {
338 if(insert) {
339 /* update insertion so ipfs will be printed in alphabetical order */
340 if(strcmp(*builtin, "ipfs") < 0)
341 insert = *builtin;
342 else
343 break;
344 }
345 else if(!strcmp(*builtin, "http")) {
346 insert = *builtin;
347 }
348 }
349 #endif /* !CURL_DISABLE_IPFS */
350 printf("Protocols:");
351 for(builtin = built_in_protos; *builtin; ++builtin) {
352 /* Special case: do not list rtmp?* protocols.
353 They may only appear together with "rtmp" */
354 if(!curl_strnequal(*builtin, "rtmp", 4) || !builtin[0][4])
355 printf(" %s", *builtin);
356 #ifndef CURL_DISABLE_IPFS
357 if(insert && insert == *builtin) {
358 printf(" ipfs ipns");
359 insert = NULL;
360 }
361 #endif /* !CURL_DISABLE_IPFS */
362 }
363 puts(""); /* newline */
364 }
365 if(feature_names[0]) {
366 const char **feat_ext;
367 size_t feat_ext_count = feature_count;
368 #ifdef CURL_CA_EMBED
369 ++feat_ext_count;
370 #endif
371 feat_ext = malloc(sizeof(*feature_names) * (feat_ext_count + 1));
372 if(feat_ext) {
373 memcpy((void *)feat_ext, feature_names,
374 sizeof(*feature_names) * feature_count);
375 feat_ext_count = feature_count;
376 #ifdef CURL_CA_EMBED
377 feat_ext[feat_ext_count++] = "CAcert";
378 #endif
379 feat_ext[feat_ext_count] = NULL;
380 qsort((void *)feat_ext, feat_ext_count, sizeof(*feat_ext),
381 struplocompare4sort);
382 printf("Features:");
383 for(builtin = feat_ext; *builtin; ++builtin)
384 printf(" %s", *builtin);
385 puts(""); /* newline */
386 free((void *)feat_ext);
387 }
388 }
389 if(strcmp(CURL_VERSION, curlinfo->version)) {
390 printf("WARNING: curl and libcurl versions do not match. "
391 "Functionality may be affected.\n");
392 }
393 }
394
tool_list_engines(void)395 void tool_list_engines(void)
396 {
397 CURL *curl = curl_easy_init();
398 struct curl_slist *engines = NULL;
399
400 /* Get the list of engines */
401 curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines);
402
403 puts("Build-time engines:");
404 if(engines) {
405 for(; engines; engines = engines->next)
406 printf(" %s\n", engines->data);
407 }
408 else {
409 puts(" <none>");
410 }
411
412 /* Cleanup the list of engines */
413 curl_slist_free_all(engines);
414 curl_easy_cleanup(curl);
415 }
416