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