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