xref: /curl/src/tool_help.c (revision ce7d0d41)
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