1 /* $OpenBSD: main.c,v 1.110 2016/08/13 12:55: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 <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 102 static void 103 process_ssl_options(char *cp) 104 { 105 const char *errstr; 106 long long depth; 107 char *str; 108 109 while (*cp) { 110 switch (getsubopt(&cp, ssl_verify_opts, &str)) { 111 case SSL_CAFILE: 112 if (str == NULL) 113 errx(1, "missing CA file"); 114 if (tls_config_set_ca_file(tls_config, str) != 0) 115 errx(1, "tls ca file failed: %s", 116 tls_config_error(tls_config)); 117 break; 118 case SSL_CAPATH: 119 if (str == NULL) 120 errx(1, "missing CA directory path"); 121 if (tls_config_set_ca_path(tls_config, str) != 0) 122 errx(1, "tls ca path failed: %s", 123 tls_config_error(tls_config)); 124 break; 125 case SSL_CIPHERS: 126 if (str == NULL) 127 errx(1, "missing cipher list"); 128 if (tls_config_set_ciphers(tls_config, str) != 0) 129 errx(1, "tls ciphers failed: %s", 130 tls_config_error(tls_config)); 131 break; 132 case SSL_DONTVERIFY: 133 tls_config_insecure_noverifycert(tls_config); 134 tls_config_insecure_noverifyname(tls_config); 135 break; 136 case SSL_DOVERIFY: 137 tls_config_verify(tls_config); 138 break; 139 case SSL_VERIFYDEPTH: 140 if (str == NULL) 141 errx(1, "missing depth"); 142 depth = strtonum(str, 0, INT_MAX, &errstr); 143 if (errstr) 144 errx(1, "certificate validation depth is %s", 145 errstr); 146 tls_config_set_verify_depth(tls_config, (int)depth); 147 break; 148 default: 149 errx(1, "unknown -S suboption `%s'", 150 suboptarg ? suboptarg : ""); 151 /* NOTREACHED */ 152 } 153 } 154 } 155 #endif /* !SMALL */ 156 157 int family = PF_UNSPEC; 158 int pipeout; 159 160 int 161 main(volatile int argc, char *argv[]) 162 { 163 int ch, top, rval; 164 struct passwd *pw = NULL; 165 char *cp, homedir[PATH_MAX]; 166 char *outfile = NULL; 167 const char *errstr; 168 int dumb_terminal = 0; 169 170 ftpport = "ftp"; 171 httpport = "http"; 172 #ifndef SMALL 173 httpsport = "https"; 174 #endif /* !SMALL */ 175 gateport = getenv("FTPSERVERPORT"); 176 if (gateport == NULL || *gateport == '\0') 177 gateport = "ftpgate"; 178 doglob = 1; 179 interactive = 1; 180 autologin = 1; 181 passivemode = 1; 182 activefallback = 1; 183 preserve = 1; 184 verbose = 0; 185 progress = 0; 186 gatemode = 0; 187 #ifndef SMALL 188 editing = 0; 189 el = NULL; 190 hist = NULL; 191 cookiefile = NULL; 192 resume = 0; 193 srcaddr = NULL; 194 marg_sl = sl_init(); 195 #endif /* !SMALL */ 196 mark = HASHBYTES; 197 epsv4 = 1; 198 epsv4bad = 0; 199 200 /* Set default operation mode based on FTPMODE environment variable */ 201 if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { 202 if (strcmp(cp, "passive") == 0) { 203 passivemode = 1; 204 activefallback = 0; 205 } else if (strcmp(cp, "active") == 0) { 206 passivemode = 0; 207 activefallback = 0; 208 } else if (strcmp(cp, "gate") == 0) { 209 gatemode = 1; 210 } else if (strcmp(cp, "auto") == 0) { 211 passivemode = 1; 212 activefallback = 1; 213 } else 214 warnx("unknown FTPMODE: %s. Using defaults", cp); 215 } 216 217 if (strcmp(__progname, "gate-ftp") == 0) 218 gatemode = 1; 219 gateserver = getenv("FTPSERVER"); 220 if (gateserver == NULL) 221 gateserver = ""; 222 if (gatemode) { 223 if (*gateserver == '\0') { 224 warnx( 225 "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); 226 gatemode = 0; 227 } 228 } 229 230 cp = getenv("TERM"); 231 dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || 232 !strcmp(cp, "emacs") || !strcmp(cp, "su")); 233 fromatty = isatty(fileno(stdin)); 234 if (fromatty) { 235 verbose = 1; /* verbose if from a tty */ 236 #ifndef SMALL 237 if (!dumb_terminal) 238 editing = 1; /* editing mode on if tty is usable */ 239 #endif /* !SMALL */ 240 } 241 242 ttyout = stdout; 243 if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) 244 progress = 1; /* progress bar on if tty is usable */ 245 246 #ifndef SMALL 247 cookiefile = getenv("http_cookies"); 248 if (tls_init() != 0) 249 errx(1, "tls init failed"); 250 if (tls_config == NULL) { 251 tls_config = tls_config_new(); 252 if (tls_config == NULL) 253 errx(1, "tls config failed"); 254 tls_config_set_protocols(tls_config, TLS_PROTOCOLS_ALL); 255 if (tls_config_set_ciphers(tls_config, "all") != 0) 256 errx(1, "tls set ciphers failed: %s", 257 tls_config_error(tls_config)); 258 } 259 #endif /* !SMALL */ 260 261 httpuseragent = NULL; 262 263 while ((ch = getopt(argc, argv, 264 "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vV")) != -1) { 265 switch (ch) { 266 case '4': 267 family = PF_INET; 268 break; 269 case '6': 270 family = PF_INET6; 271 break; 272 case 'A': 273 activefallback = 0; 274 passivemode = 0; 275 break; 276 277 case 'a': 278 anonftp = 1; 279 break; 280 281 case 'C': 282 #ifndef SMALL 283 resume = 1; 284 #endif /* !SMALL */ 285 break; 286 287 case 'c': 288 #ifndef SMALL 289 cookiefile = optarg; 290 #endif /* !SMALL */ 291 break; 292 293 case 'D': 294 action = optarg; 295 break; 296 case 'd': 297 #ifndef SMALL 298 options |= SO_DEBUG; 299 debug++; 300 #endif /* !SMALL */ 301 break; 302 303 case 'E': 304 epsv4 = 0; 305 break; 306 307 case 'e': 308 #ifndef SMALL 309 editing = 0; 310 #endif /* !SMALL */ 311 break; 312 313 case 'g': 314 doglob = 0; 315 break; 316 317 case 'i': 318 interactive = 0; 319 break; 320 321 case 'k': 322 keep_alive_timeout = strtonum(optarg, 0, INT_MAX, 323 &errstr); 324 if (errstr != NULL) { 325 warnx("keep alive amount is %s: %s", errstr, 326 optarg); 327 usage(); 328 } 329 break; 330 case 'M': 331 progress = 0; 332 break; 333 case 'm': 334 progress = -1; 335 break; 336 337 case 'n': 338 autologin = 0; 339 break; 340 341 case 'o': 342 outfile = optarg; 343 if (*outfile == '\0') { 344 pipeout = 0; 345 outfile = NULL; 346 ttyout = stdout; 347 } else { 348 pipeout = strcmp(outfile, "-") == 0; 349 ttyout = pipeout ? stderr : stdout; 350 } 351 break; 352 353 case 'p': 354 passivemode = 1; 355 activefallback = 0; 356 break; 357 358 case 'P': 359 ftpport = optarg; 360 break; 361 362 case 'r': 363 retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); 364 if (errstr != NULL) { 365 warnx("retry amount is %s: %s", errstr, 366 optarg); 367 usage(); 368 } 369 break; 370 371 case 'S': 372 #ifndef SMALL 373 process_ssl_options(optarg); 374 #endif /* !SMALL */ 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