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