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