1 /* $OpenBSD: main.c,v 1.95 2014/10/31 13:48:21 jsing Exp $ */ 2 /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ 3 4 /* 5 * Copyright (C) 1997 and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1985, 1989, 1993, 1994 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 /* 63 * FTP User Program -- Command Interface. 64 */ 65 #include <sys/types.h> 66 #include <sys/socket.h> 67 68 #include <ctype.h> 69 #include <err.h> 70 #include <limits.h> 71 #include <netdb.h> 72 #include <pwd.h> 73 #include <stdio.h> 74 #include <errno.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <unistd.h> 78 79 #include <tls.h> 80 81 #include "cmds.h" 82 #include "ftp_var.h" 83 84 #ifndef SMALL 85 char * const ssl_verify_opts[] = { 86 #define SSL_CAFILE 0 87 "cafile", 88 #define SSL_CAPATH 1 89 "capath", 90 #define SSL_CIPHERS 2 91 "ciphers", 92 #define SSL_DONTVERIFY 3 93 "dont", 94 #define SSL_DOVERIFY 4 95 "do", 96 #define SSL_VERIFYDEPTH 5 97 "depth", 98 NULL 99 }; 100 101 struct tls_config *tls_config; 102 #endif /* !SMALL */ 103 104 int family = PF_UNSPEC; 105 int pipeout; 106 107 int 108 main(volatile int argc, char *argv[]) 109 { 110 int ch, top, rval; 111 struct passwd *pw = NULL; 112 char *cp, homedir[MAXPATHLEN]; 113 char *outfile = NULL; 114 const char *errstr; 115 int dumb_terminal = 0; 116 #ifndef SMALL 117 long long depth; 118 #endif 119 120 ftpport = "ftp"; 121 httpport = "http"; 122 #ifndef SMALL 123 httpsport = "https"; 124 #endif /* !SMALL */ 125 gateport = getenv("FTPSERVERPORT"); 126 if (gateport == NULL || *gateport == '\0') 127 gateport = "ftpgate"; 128 doglob = 1; 129 interactive = 1; 130 autologin = 1; 131 passivemode = 1; 132 activefallback = 1; 133 preserve = 1; 134 verbose = 0; 135 progress = 0; 136 gatemode = 0; 137 #ifndef SMALL 138 editing = 0; 139 el = NULL; 140 hist = NULL; 141 cookiefile = NULL; 142 resume = 0; 143 srcaddr = NULL; 144 marg_sl = sl_init(); 145 #endif /* !SMALL */ 146 mark = HASHBYTES; 147 #ifdef INET6 148 epsv4 = 1; 149 #else 150 epsv4 = 0; 151 #endif 152 epsv4bad = 0; 153 154 /* Set default operation mode based on FTPMODE environment variable */ 155 if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { 156 if (strcmp(cp, "passive") == 0) { 157 passivemode = 1; 158 activefallback = 0; 159 } else if (strcmp(cp, "active") == 0) { 160 passivemode = 0; 161 activefallback = 0; 162 } else if (strcmp(cp, "gate") == 0) { 163 gatemode = 1; 164 } else if (strcmp(cp, "auto") == 0) { 165 passivemode = 1; 166 activefallback = 1; 167 } else 168 warnx("unknown FTPMODE: %s. Using defaults", cp); 169 } 170 171 if (strcmp(__progname, "gate-ftp") == 0) 172 gatemode = 1; 173 gateserver = getenv("FTPSERVER"); 174 if (gateserver == NULL || *gateserver == '\0') 175 gateserver = GATE_SERVER; 176 if (gatemode) { 177 if (*gateserver == '\0') { 178 warnx( 179 "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); 180 gatemode = 0; 181 } 182 } 183 184 cp = getenv("TERM"); 185 dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || 186 !strcmp(cp, "emacs") || !strcmp(cp, "su")); 187 fromatty = isatty(fileno(stdin)); 188 if (fromatty) { 189 verbose = 1; /* verbose if from a tty */ 190 #ifndef SMALL 191 if (!dumb_terminal) 192 editing = 1; /* editing mode on if tty is usable */ 193 #endif /* !SMALL */ 194 } 195 196 ttyout = stdout; 197 if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) 198 progress = 1; /* progress bar on if tty is usable */ 199 200 #ifndef SMALL 201 cookiefile = getenv("http_cookies"); 202 #endif /* !SMALL */ 203 httpuseragent = NULL; 204 205 while ((ch = getopt(argc, argv, 206 "46AaCc:dD:Eegik:mno:pP:r:S:s:tU:vV")) != -1) { 207 switch (ch) { 208 case '4': 209 family = PF_INET; 210 break; 211 case '6': 212 family = PF_INET6; 213 break; 214 case 'A': 215 activefallback = 0; 216 passivemode = 0; 217 break; 218 219 case 'a': 220 anonftp = 1; 221 break; 222 223 case 'C': 224 #ifndef SMALL 225 resume = 1; 226 #endif /* !SMALL */ 227 break; 228 229 case 'c': 230 #ifndef SMALL 231 cookiefile = optarg; 232 #endif /* !SMALL */ 233 break; 234 235 case 'D': 236 action = optarg; 237 break; 238 case 'd': 239 #ifndef SMALL 240 options |= SO_DEBUG; 241 debug++; 242 #endif /* !SMALL */ 243 break; 244 245 case 'E': 246 epsv4 = 0; 247 break; 248 249 case 'e': 250 #ifndef SMALL 251 editing = 0; 252 #endif /* !SMALL */ 253 break; 254 255 case 'g': 256 doglob = 0; 257 break; 258 259 case 'i': 260 interactive = 0; 261 break; 262 263 case 'k': 264 keep_alive_timeout = strtonum(optarg, 0, INT_MAX, 265 &errstr); 266 if (errstr != NULL) { 267 warnx("keep alive amount is %s: %s", errstr, 268 optarg); 269 usage(); 270 } 271 break; 272 case 'm': 273 progress = -1; 274 break; 275 276 case 'n': 277 autologin = 0; 278 break; 279 280 case 'o': 281 outfile = optarg; 282 if (*outfile == '\0') { 283 pipeout = 0; 284 outfile = NULL; 285 ttyout = stdout; 286 } else { 287 pipeout = strcmp(outfile, "-") == 0; 288 ttyout = pipeout ? stderr : stdout; 289 } 290 break; 291 292 case 'p': 293 passivemode = 1; 294 activefallback = 0; 295 break; 296 297 case 'P': 298 ftpport = optarg; 299 break; 300 301 case 'r': 302 retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); 303 if (errstr != NULL) { 304 warnx("retry amount is %s: %s", errstr, 305 optarg); 306 usage(); 307 } 308 break; 309 310 case 'S': 311 #ifndef SMALL 312 if (tls_config == NULL) { 313 tls_config = tls_config_new(); 314 if (tls_config == NULL) 315 errx(1, "tls config failed"); 316 } 317 318 cp = optarg; 319 while (*cp) { 320 char *str; 321 switch (getsubopt(&cp, ssl_verify_opts, &str)) { 322 case SSL_CAFILE: 323 if (str == NULL) 324 errx(1, "missing CA file"); 325 if (tls_config_set_ca_file( 326 tls_config, str) != 0) 327 errx(1, "tls ca file failed"); 328 break; 329 case SSL_CAPATH: 330 if (str == NULL) 331 errx(1, "missing CA directory" 332 " path"); 333 if (tls_config_set_ca_path( 334 tls_config, str) != 0) 335 errx(1, "tls ca path failed"); 336 break; 337 case SSL_CIPHERS: 338 if (str == NULL) 339 errx(1, "missing cipher list"); 340 if (tls_config_set_ciphers( 341 tls_config, str) != 0) 342 errx(1, "tls ciphers failed"); 343 break; 344 case SSL_DONTVERIFY: 345 tls_config_insecure_noverifyhost( 346 tls_config); 347 tls_config_insecure_noverifycert( 348 tls_config); 349 break; 350 case SSL_DOVERIFY: 351 tls_config_verify(tls_config); 352 break; 353 case SSL_VERIFYDEPTH: 354 if (str == NULL) 355 errx(1, "missing depth"); 356 depth = strtonum(str, 0, INT_MAX, 357 &errstr); 358 if (errstr) 359 errx(1, "certificate " 360 "validation depth is %s", 361 errstr); 362 tls_config_set_verify_depth( 363 tls_config, (int)depth); 364 break; 365 default: 366 errx(1, "unknown -S suboption `%s'", 367 suboptarg ? suboptarg : ""); 368 /* NOTREACHED */ 369 } 370 } 371 #endif 372 break; 373 374 case 's': 375 #ifndef SMALL 376 srcaddr = optarg; 377 #endif /* !SMALL */ 378 break; 379 380 case 't': 381 trace = 1; 382 break; 383 384 #ifndef SMALL 385 case 'U': 386 free (httpuseragent); 387 if (strcspn(optarg, "\r\n") != strlen(optarg)) 388 errx(1, "Invalid User-Agent: %s.", optarg); 389 if (asprintf(&httpuseragent, "User-Agent: %s", 390 optarg) == -1) 391 errx(1, "Can't allocate memory for HTTP(S) " 392 "User-Agent"); 393 break; 394 #endif /* !SMALL */ 395 396 case 'v': 397 verbose = 1; 398 break; 399 400 case 'V': 401 verbose = 0; 402 break; 403 404 default: 405 usage(); 406 } 407 } 408 argc -= optind; 409 argv += optind; 410 411 #ifndef SMALL 412 cookie_load(); 413 #endif /* !SMALL */ 414 if (httpuseragent == NULL) 415 httpuseragent = HTTP_USER_AGENT; 416 417 cpend = 0; /* no pending replies */ 418 proxy = 0; /* proxy not active */ 419 crflag = 1; /* strip c.r. on ascii gets */ 420 sendport = -1; /* not using ports */ 421 /* 422 * Set up the home directory in case we're globbing. 423 */ 424 cp = getlogin(); 425 if (cp != NULL) { 426 pw = getpwnam(cp); 427 } 428 if (pw == NULL) 429 pw = getpwuid(getuid()); 430 if (pw != NULL) { 431 (void)strlcpy(homedir, pw->pw_dir, sizeof homedir); 432 home = homedir; 433 } 434 435 setttywidth(0); 436 (void)signal(SIGWINCH, setttywidth); 437 438 if (argc > 0) { 439 if (isurl(argv[0])) { 440 rval = auto_fetch(argc, argv, outfile); 441 if (rval >= 0) /* -1 == connected and cd-ed */ 442 exit(rval); 443 } else { 444 #ifndef SMALL 445 char *xargv[5]; 446 447 if (setjmp(toplevel)) 448 exit(0); 449 (void)signal(SIGINT, (sig_t)intr); 450 (void)signal(SIGPIPE, (sig_t)lostpeer); 451 xargv[0] = __progname; 452 xargv[1] = argv[0]; 453 xargv[2] = argv[1]; 454 xargv[3] = argv[2]; 455 xargv[4] = NULL; 456 do { 457 setpeer(argc+1, xargv); 458 if (!retry_connect) 459 break; 460 if (!connected) { 461 macnum = 0; 462 fputs("Retrying...\n", ttyout); 463 sleep(retry_connect); 464 } 465 } while (!connected); 466 retry_connect = 0; /* connected, stop hiding msgs */ 467 #endif /* !SMALL */ 468 } 469 } 470 #ifndef SMALL 471 controlediting(); 472 top = setjmp(toplevel) == 0; 473 if (top) { 474 (void)signal(SIGINT, (sig_t)intr); 475 (void)signal(SIGPIPE, (sig_t)lostpeer); 476 } 477 for (;;) { 478 cmdscanner(top); 479 top = 1; 480 } 481 #else /* !SMALL */ 482 usage(); 483 #endif /* !SMALL */ 484 } 485 486 void 487 intr(void) 488 { 489 int save_errno = errno; 490 491 write(fileno(ttyout), "\n\r", 2); 492 alarmtimer(0); 493 494 errno = save_errno; 495 longjmp(toplevel, 1); 496 } 497 498 void 499 lostpeer(void) 500 { 501 int save_errno = errno; 502 503 alarmtimer(0); 504 if (connected) { 505 if (cout != NULL) { 506 (void)shutdown(fileno(cout), SHUT_RDWR); 507 (void)fclose(cout); 508 cout = NULL; 509 } 510 if (data >= 0) { 511 (void)shutdown(data, SHUT_RDWR); 512 (void)close(data); 513 data = -1; 514 } 515 connected = 0; 516 } 517 pswitch(1); 518 if (connected) { 519 if (cout != NULL) { 520 (void)shutdown(fileno(cout), SHUT_RDWR); 521 (void)fclose(cout); 522 cout = NULL; 523 } 524 connected = 0; 525 } 526 proxflag = 0; 527 pswitch(0); 528 errno = save_errno; 529 } 530 531 #ifndef SMALL 532 /* 533 * Generate a prompt 534 */ 535 char * 536 prompt(void) 537 { 538 return ("ftp> "); 539 } 540 541 /* 542 * Command parser. 543 */ 544 void 545 cmdscanner(int top) 546 { 547 struct cmd *c; 548 int num; 549 HistEvent hev; 550 551 if (!top && !editing) 552 (void)putc('\n', ttyout); 553 for (;;) { 554 if (!editing) { 555 if (fromatty) { 556 fputs(prompt(), ttyout); 557 (void)fflush(ttyout); 558 } 559 if (fgets(line, sizeof(line), stdin) == NULL) 560 quit(0, 0); 561 num = strlen(line); 562 if (num == 0) 563 break; 564 if (line[--num] == '\n') { 565 if (num == 0) 566 break; 567 line[num] = '\0'; 568 } else if (num == sizeof(line) - 2) { 569 fputs("sorry, input line too long.\n", ttyout); 570 while ((num = getchar()) != '\n' && num != EOF) 571 /* void */; 572 break; 573 } /* else it was a line without a newline */ 574 } else { 575 const char *buf; 576 cursor_pos = NULL; 577 578 if ((buf = el_gets(el, &num)) == NULL || num == 0) { 579 putc('\n', ttyout); 580 fflush(ttyout); 581 quit(0, 0); 582 } 583 if (buf[--num] == '\n') { 584 if (num == 0) 585 break; 586 } 587 if (num >= sizeof(line)) { 588 fputs("sorry, input line too long.\n", ttyout); 589 break; 590 } 591 memcpy(line, buf, (size_t)num); 592 line[num] = '\0'; 593 history(hist, &hev, H_ENTER, buf); 594 } 595 596 makeargv(); 597 if (margc == 0) 598 continue; 599 c = getcmd(margv[0]); 600 if (c == (struct cmd *)-1) { 601 fputs("?Ambiguous command.\n", ttyout); 602 continue; 603 } 604 if (c == 0) { 605 /* 606 * Give editline(3) a shot at unknown commands. 607 * XXX - bogus commands with a colon in 608 * them will not elicit an error. 609 */ 610 if (editing && 611 el_parse(el, margc, (const char **)margv) != 0) 612 fputs("?Invalid command.\n", ttyout); 613 continue; 614 } 615 if (c->c_conn && !connected) { 616 fputs("Not connected.\n", ttyout); 617 continue; 618 } 619 confirmrest = 0; 620 (*c->c_handler)(margc, margv); 621 if (bell && c->c_bell) 622 (void)putc('\007', ttyout); 623 if (c->c_handler != help) 624 break; 625 } 626 (void)signal(SIGINT, (sig_t)intr); 627 (void)signal(SIGPIPE, (sig_t)lostpeer); 628 } 629 630 struct cmd * 631 getcmd(const char *name) 632 { 633 const char *p, *q; 634 struct cmd *c, *found; 635 int nmatches, longest; 636 637 if (name == NULL) 638 return (0); 639 640 longest = 0; 641 nmatches = 0; 642 found = 0; 643 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 644 for (q = name; *q == *p++; q++) 645 if (*q == 0) /* exact match? */ 646 return (c); 647 if (!*q) { /* the name was a prefix */ 648 if (q - name > longest) { 649 longest = q - name; 650 nmatches = 1; 651 found = c; 652 } else if (q - name == longest) 653 nmatches++; 654 } 655 } 656 if (nmatches > 1) 657 return ((struct cmd *)-1); 658 return (found); 659 } 660 661 /* 662 * Slice a string up into argc/argv. 663 */ 664 665 int slrflag; 666 667 void 668 makeargv(void) 669 { 670 char *argp; 671 672 stringbase = line; /* scan from first of buffer */ 673 argbase = argbuf; /* store from first of buffer */ 674 slrflag = 0; 675 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 676 for (margc = 0; ; margc++) { 677 argp = slurpstring(); 678 sl_add(marg_sl, argp); 679 if (argp == NULL) 680 break; 681 } 682 if (cursor_pos == line) { 683 cursor_argc = 0; 684 cursor_argo = 0; 685 } else if (cursor_pos != NULL) { 686 cursor_argc = margc; 687 cursor_argo = strlen(margv[margc-1]); 688 } 689 } 690 691 #define INC_CHKCURSOR(x) { (x)++ ; \ 692 if (x == cursor_pos) { \ 693 cursor_argc = margc; \ 694 cursor_argo = ap-argbase; \ 695 cursor_pos = NULL; \ 696 } } 697 698 /* 699 * Parse string into argbuf; 700 * implemented with FSM to 701 * handle quoting and strings 702 */ 703 char * 704 slurpstring(void) 705 { 706 int got_one = 0; 707 char *sb = stringbase; 708 char *ap = argbase; 709 char *tmp = argbase; /* will return this if token found */ 710 711 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 712 switch (slrflag) { /* and $ as token for macro invoke */ 713 case 0: 714 slrflag++; 715 INC_CHKCURSOR(stringbase); 716 return ((*sb == '!') ? "!" : "$"); 717 /* NOTREACHED */ 718 case 1: 719 slrflag++; 720 altarg = stringbase; 721 break; 722 default: 723 break; 724 } 725 } 726 727 S0: 728 switch (*sb) { 729 730 case '\0': 731 goto OUT; 732 733 case ' ': 734 case '\t': 735 INC_CHKCURSOR(sb); 736 goto S0; 737 738 default: 739 switch (slrflag) { 740 case 0: 741 slrflag++; 742 break; 743 case 1: 744 slrflag++; 745 altarg = sb; 746 break; 747 default: 748 break; 749 } 750 goto S1; 751 } 752 753 S1: 754 switch (*sb) { 755 756 case ' ': 757 case '\t': 758 case '\0': 759 goto OUT; /* end of token */ 760 761 case '\\': 762 INC_CHKCURSOR(sb); 763 goto S2; /* slurp next character */ 764 765 case '"': 766 INC_CHKCURSOR(sb); 767 goto S3; /* slurp quoted string */ 768 769 default: 770 *ap = *sb; /* add character to token */ 771 ap++; 772 INC_CHKCURSOR(sb); 773 got_one = 1; 774 goto S1; 775 } 776 777 S2: 778 switch (*sb) { 779 780 case '\0': 781 goto OUT; 782 783 default: 784 *ap = *sb; 785 ap++; 786 INC_CHKCURSOR(sb); 787 got_one = 1; 788 goto S1; 789 } 790 791 S3: 792 switch (*sb) { 793 794 case '\0': 795 goto OUT; 796 797 case '"': 798 INC_CHKCURSOR(sb); 799 goto S1; 800 801 default: 802 *ap = *sb; 803 ap++; 804 INC_CHKCURSOR(sb); 805 got_one = 1; 806 goto S3; 807 } 808 809 OUT: 810 if (got_one) 811 *ap++ = '\0'; 812 argbase = ap; /* update storage pointer */ 813 stringbase = sb; /* update scan pointer */ 814 if (got_one) { 815 return (tmp); 816 } 817 switch (slrflag) { 818 case 0: 819 slrflag++; 820 break; 821 case 1: 822 slrflag++; 823 altarg = (char *) 0; 824 break; 825 default: 826 break; 827 } 828 return ((char *)0); 829 } 830 831 /* 832 * Help command. 833 * Call each command handler with argc == 0 and argv[0] == name. 834 */ 835 void 836 help(int argc, char *argv[]) 837 { 838 struct cmd *c; 839 840 if (argc == 1) { 841 StringList *buf; 842 843 buf = sl_init(); 844 fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", 845 proxy ? "Proxy c" : "C"); 846 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) 847 if (c->c_name && (!proxy || c->c_proxy)) 848 sl_add(buf, c->c_name); 849 list_vertical(buf); 850 sl_free(buf, 0); 851 return; 852 } 853 854 #define HELPINDENT ((int) sizeof("disconnect")) 855 856 while (--argc > 0) { 857 char *arg; 858 859 arg = *++argv; 860 c = getcmd(arg); 861 if (c == (struct cmd *)-1) 862 fprintf(ttyout, "?Ambiguous help command %s\n", arg); 863 else if (c == (struct cmd *)0) 864 fprintf(ttyout, "?Invalid help command %s\n", arg); 865 else 866 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, 867 c->c_name, c->c_help); 868 } 869 } 870 #endif /* !SMALL */ 871 872 void 873 usage(void) 874 { 875 fprintf(stderr, "usage: " 876 #ifndef SMALL 877 "%1$s [-46AadEegimnptVv] [-D title] [-k seconds] [-P port] " 878 "[-r seconds]\n" 879 " [-s srcaddr] [host [port]]\n" 880 " %1$s [-C] [-o output] [-s srcaddr]\n" 881 " ftp://[user:password@]host[:port]/file[/] ...\n" 882 " %1$s [-C] [-c cookie] [-o output] [-S ssl_options] " 883 "[-s srcaddr]\n" 884 " [-U useragent] " 885 "http[s]://[user:password@]host[:port]/file ...\n" 886 " %1$s [-C] [-o output] [-s srcaddr] file:file ...\n" 887 " %1$s [-C] [-o output] [-s srcaddr] host:/file[/] ...\n", 888 #else /* !SMALL */ 889 "%1$s [-o output] ftp://[user:password@]host[:port]/file[/] ...\n" 890 " %1$s [-o output] http://host[:port]/file ...\n" 891 " %1$s [-o output] file:file ...\n" 892 " %1$s [-o output] host:/file[/] ...\n", 893 #endif /* !SMALL */ 894 __progname); 895 exit(1); 896 } 897