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