1 /* $NetBSD: main.c,v 1.65 1999/10/24 12:31:41 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1996-1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Copyright (c) 1985, 1989, 1993, 1994 41 * The Regents of the University of California. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 */ 71 72 /* 73 * Copyright (C) 1997 and 1998 WIDE Project. 74 * All rights reserved. 75 * 76 * Redistribution and use in source and binary forms, with or without 77 * modification, are permitted provided that the following conditions 78 * are met: 79 * 1. Redistributions of source code must retain the above copyright 80 * notice, this list of conditions and the following disclaimer. 81 * 2. Redistributions in binary form must reproduce the above copyright 82 * notice, this list of conditions and the following disclaimer in the 83 * documentation and/or other materials provided with the distribution. 84 * 3. Neither the name of the project nor the names of its contributors 85 * may be used to endorse or promote products derived from this software 86 * without specific prior written permission. 87 * 88 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 89 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 90 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 91 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 92 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 93 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 94 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 95 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 96 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 97 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 98 * SUCH DAMAGE. 99 */ 100 101 #include <sys/cdefs.h> 102 #ifndef lint 103 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\n\ 104 The Regents of the University of California. All rights reserved.\n"); 105 #endif /* not lint */ 106 107 #ifndef lint 108 #if 0 109 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; 110 #else 111 __RCSID("$NetBSD: main.c,v 1.65 1999/10/24 12:31:41 lukem Exp $"); 112 #endif 113 #endif /* not lint */ 114 115 /* 116 * FTP User Program -- Command Interface. 117 */ 118 #include <sys/types.h> 119 #include <sys/socket.h> 120 121 #include <err.h> 122 #include <errno.h> 123 #include <netdb.h> 124 #include <paths.h> 125 #include <pwd.h> 126 #include <stdio.h> 127 #include <stdlib.h> 128 #include <string.h> 129 #include <unistd.h> 130 131 #define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */ 132 #include "ftp_var.h" 133 134 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ 135 #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ 136 #define NO_PROXY "no_proxy" /* env var with list of non-proxied 137 * hosts, comma or space separated */ 138 139 static void setupoption __P((char *, char *, char *)); 140 int main __P((int, char **)); 141 142 int 143 main(argc, argv) 144 int argc; 145 char *argv[]; 146 { 147 int ch, rval; 148 struct passwd *pw = NULL; 149 char *cp, *ep, *anonuser, *anonpass; 150 int dumbterm, s, len; 151 152 ftpport = "ftp"; 153 httpport = "http"; 154 gateport = NULL; 155 cp = getenv("FTPSERVERPORT"); 156 if (cp != NULL) 157 gateport = cp; 158 else 159 gateport = "ftpgate"; 160 doglob = 1; 161 interactive = 1; 162 autologin = 1; 163 passivemode = 1; 164 activefallback = 1; 165 preserve = 1; 166 verbose = 0; 167 progress = 0; 168 gatemode = 0; 169 data = -1; 170 outfile = NULL; 171 restartautofetch = 0; 172 #ifndef NO_EDITCOMPLETE 173 editing = 0; 174 el = NULL; 175 hist = NULL; 176 #endif 177 bytes = 0; 178 mark = HASHBYTES; 179 rate_get = 0; 180 rate_get_incr = DEFAULTINCR; 181 rate_put = 0; 182 rate_put_incr = DEFAULTINCR; 183 #ifdef INET6 184 epsv4 = 1; 185 #else 186 epsv4 = 0; 187 #endif 188 epsv4bad = 0; 189 190 /* 191 * Get the default socket buffer sizes if we don't already have them. 192 * It doesn't matter which socket we do this to, because on the first 193 * call no socket buffer sizes will have been modified, so we are 194 * guaranteed to get the system defaults. 195 */ 196 s = socket(AF_INET, SOCK_STREAM, 0); 197 if (s == -1) 198 err(1, "can't create socket"); 199 len = sizeof(rcvbuf_size); 200 if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size, &len) 201 < 0) 202 err(1, "unable to get default rcvbuf size"); 203 len = sizeof(sndbuf_size); 204 if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size, &len) 205 < 0) 206 err(1, "unable to get default sndbuf size"); 207 (void)close(s); 208 /* sanity check returned buffer sizes */ 209 if (rcvbuf_size <= 0) 210 rcvbuf_size = 8192; 211 if (sndbuf_size <= 0) 212 sndbuf_size = 8192; 213 214 marg_sl = sl_init(); 215 if ((tmpdir = getenv("TMPDIR")) == NULL) 216 tmpdir = _PATH_TMP; 217 218 /* Set default operation mode based on FTPMODE environment variable */ 219 if ((cp = getenv("FTPMODE")) != NULL) { 220 if (strcasecmp(cp, "passive") == 0) { 221 passivemode = 1; 222 activefallback = 0; 223 } else if (strcasecmp(cp, "active") == 0) { 224 passivemode = 0; 225 activefallback = 0; 226 } else if (strcasecmp(cp, "gate") == 0) { 227 gatemode = 1; 228 } else if (strcasecmp(cp, "auto") == 0) { 229 passivemode = 1; 230 activefallback = 1; 231 } else 232 warnx("unknown $FTPMODE '%s'; using defaults", cp); 233 } 234 235 if (strcmp(__progname, "pftp") == 0) { 236 passivemode = 1; 237 activefallback = 0; 238 } else if (strcmp(__progname, "gate-ftp") == 0) 239 gatemode = 1; 240 241 gateserver = getenv("FTPSERVER"); 242 if (gateserver == NULL || *gateserver == '\0') 243 gateserver = GATE_SERVER; 244 if (gatemode) { 245 if (*gateserver == '\0') { 246 warnx( 247 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); 248 gatemode = 0; 249 } 250 } 251 252 cp = getenv("TERM"); 253 if (cp == NULL || strcmp(cp, "dumb") == 0) 254 dumbterm = 1; 255 else 256 dumbterm = 0; 257 fromatty = isatty(fileno(stdin)); 258 ttyout = stdout; 259 if (isatty(fileno(ttyout))) { 260 verbose = 1; /* verbose if to a tty */ 261 if (! dumbterm) { 262 #ifndef NO_EDITCOMPLETE 263 if (fromatty) /* editing mode on if tty is usable */ 264 editing = 1; 265 #endif 266 #ifndef NO_PROGRESS 267 if (foregroundproc()) 268 progress = 1; /* progress bar on if fg */ 269 #endif 270 } 271 } 272 273 while ((ch = getopt(argc, argv, "Aadefgino:pP:r:RtT:vV")) != -1) { 274 switch (ch) { 275 case 'A': 276 activefallback = 0; 277 passivemode = 0; 278 break; 279 280 case 'a': 281 anonftp = 1; 282 break; 283 284 case 'd': 285 options |= SO_DEBUG; 286 debug++; 287 break; 288 289 case 'e': 290 #ifndef NO_EDITCOMPLETE 291 editing = 0; 292 #endif 293 break; 294 295 case 'f': 296 flushcache = 1; 297 break; 298 299 case 'g': 300 doglob = 0; 301 break; 302 303 case 'i': 304 interactive = 0; 305 break; 306 307 case 'n': 308 autologin = 0; 309 break; 310 311 case 'o': 312 outfile = optarg; 313 if (strcmp(outfile, "-") == 0) 314 ttyout = stderr; 315 break; 316 317 case 'p': 318 passivemode = 1; 319 activefallback = 0; 320 break; 321 322 case 'P': 323 ftpport = optarg; 324 break; 325 326 case 'r': 327 retry_connect = strtol(optarg, &ep, 10); 328 if (retry_connect < 1 || *ep != '\0') 329 errx(1, "bad retry value: %s", optarg); 330 break; 331 332 case 'R': 333 restartautofetch = 1; 334 break; 335 336 case 't': 337 trace = 1; 338 break; 339 340 case 'T': 341 { 342 int targc; 343 char *targv[6], *oac; 344 345 /* look for `dir,max[,incr]' */ 346 targc = 0; 347 targv[targc++] = "-T"; 348 oac = xstrdup(optarg); 349 350 while ((cp = strsep(&oac, ",")) != NULL) { 351 if (*cp == '\0') { 352 warnx("bad throttle value: %s", optarg); 353 usage(); 354 /* NOTREACHED */ 355 } 356 targv[targc++] = cp; 357 if (targc >= 5) 358 break; 359 } 360 if (parserate(targc, targv, 1) == -1) 361 usage(); 362 free(oac); 363 break; 364 } 365 366 case 'v': 367 progress = verbose = 1; 368 break; 369 370 case 'V': 371 progress = verbose = 0; 372 break; 373 374 default: 375 usage(); 376 } 377 } 378 /* set line buffering on ttyout */ 379 setvbuf(ttyout, NULL, _IOLBF, 0); 380 argc -= optind; 381 argv += optind; 382 383 cpend = 0; /* no pending replies */ 384 proxy = 0; /* proxy not active */ 385 crflag = 1; /* strip c.r. on ascii gets */ 386 sendport = -1; /* not using ports */ 387 /* 388 * Set up the home directory in case we're globbing. 389 */ 390 cp = getlogin(); 391 if (cp != NULL) 392 pw = getpwnam(cp); 393 if (pw == NULL) 394 pw = getpwuid(getuid()); 395 if (pw != NULL) { 396 (void)strlcpy(home, pw->pw_dir, sizeof(home)); 397 anonuser = pw->pw_name; 398 } else { 399 (void)strlcpy(home, "/", sizeof(home)); 400 anonuser = "anonymous"; 401 } 402 403 /* 404 * Every anonymous FTP server I've encountered will accept the 405 * string "username@", and will append the hostname itself. We 406 * do this by default since many servers are picky about not 407 * having a FQDN in the anonymous password. 408 * - thorpej@netbsd.org 409 */ 410 len = strlen(anonuser) + 2; 411 anonpass = xmalloc(len); 412 (void)strlcpy(anonpass, anonuser, len); 413 (void)strlcat(anonpass, "@", len); 414 415 setupoption("anonpass", getenv("FTPANONPASS"), anonpass); 416 setupoption("ftp_proxy", getenv(FTP_PROXY), xstrdup("")); 417 setupoption("http_proxy", getenv(HTTP_PROXY), xstrdup("")); 418 setupoption("no_proxy", getenv(NO_PROXY), xstrdup("")); 419 setupoption("pager", getenv("PAGER"), xstrdup(DEFAULTPAGER)); 420 421 setttywidth(0); 422 #ifdef SIGINFO 423 (void)xsignal(SIGINFO, psummary); 424 #endif 425 (void)xsignal(SIGQUIT, psummary); 426 (void)xsignal(SIGUSR1, crankrate); 427 (void)xsignal(SIGUSR2, crankrate); 428 (void)xsignal(SIGWINCH, setttywidth); 429 430 #ifdef __GNUC__ /* to shut up gcc warnings */ 431 (void)&argc; 432 (void)&argv; 433 #endif 434 435 if (argc > 0) { 436 if (strchr(argv[0], ':') != NULL && ! isipv6addr(argv[0])) { 437 rval = auto_fetch(argc, argv); 438 if (rval >= 0) /* -1 == connected and cd-ed */ 439 exit(rval); 440 } else { 441 char *xargv[5]; 442 443 if (sigsetjmp(toplevel, 1)) 444 exit(0); 445 (void)xsignal(SIGINT, intr); 446 (void)xsignal(SIGPIPE, lostpeer); 447 xargv[0] = __progname; 448 xargv[1] = argv[0]; 449 xargv[2] = argv[1]; 450 xargv[3] = argv[2]; 451 xargv[4] = NULL; 452 do { 453 setpeer(argc+1, xargv); 454 if (!retry_connect) 455 break; 456 if (!connected) { 457 macnum = 0; 458 fprintf(ttyout, 459 "Retrying in %d seconds...\n", 460 retry_connect); 461 sleep(retry_connect); 462 } 463 } while (!connected); 464 retry_connect = 0; /* connected, stop hiding msgs */ 465 } 466 } 467 #ifndef NO_EDITCOMPLETE 468 controlediting(); 469 #endif /* !NO_EDITCOMPLETE */ 470 471 (void)sigsetjmp(toplevel, 1); 472 (void)xsignal(SIGINT, intr); 473 (void)xsignal(SIGPIPE, lostpeer); 474 for (;;) 475 cmdscanner(); 476 } 477 478 /* 479 * Generate a prompt 480 */ 481 char * 482 prompt() 483 { 484 return ("ftp> "); 485 } 486 487 /* 488 * Command parser. 489 */ 490 void 491 cmdscanner() 492 { 493 struct cmd *c; 494 int num; 495 496 for (;;) { 497 #ifndef NO_EDITCOMPLETE 498 if (!editing) { 499 #endif /* !NO_EDITCOMPLETE */ 500 if (fromatty) { 501 fputs(prompt(), ttyout); 502 (void)fflush(ttyout); 503 } 504 if (fgets(line, sizeof(line), stdin) == NULL) { 505 if (fromatty) 506 putc('\n', ttyout); 507 quit(0, 0); 508 } 509 num = strlen(line); 510 if (num == 0) 511 break; 512 if (line[--num] == '\n') { 513 if (num == 0) 514 break; 515 line[num] = '\0'; 516 } else if (num == sizeof(line) - 2) { 517 fputs("sorry, input line too long.\n", ttyout); 518 while ((num = getchar()) != '\n' && num != EOF) 519 /* void */; 520 break; 521 } /* else it was a line without a newline */ 522 #ifndef NO_EDITCOMPLETE 523 } else { 524 const char *buf; 525 HistEvent ev; 526 cursor_pos = NULL; 527 528 if ((buf = el_gets(el, &num)) == NULL || num == 0) { 529 if (fromatty) 530 putc('\n', ttyout); 531 quit(0, 0); 532 } 533 if (buf[--num] == '\n') { 534 if (num == 0) 535 break; 536 } else if (num >= sizeof(line)) { 537 fputs("sorry, input line too long.\n", ttyout); 538 break; 539 } 540 memcpy(line, buf, num); 541 line[num] = '\0'; 542 history(hist, &ev, H_ENTER, buf); 543 } 544 #endif /* !NO_EDITCOMPLETE */ 545 546 makeargv(); 547 if (margc == 0) 548 continue; 549 c = getcmd(margv[0]); 550 if (c == (struct cmd *)-1) { 551 fputs("?Ambiguous command.\n", ttyout); 552 continue; 553 } 554 if (c == NULL) { 555 #if !defined(NO_EDITCOMPLETE) 556 /* 557 * attempt to el_parse() unknown commands. 558 * any command containing a ':' would be parsed 559 * as "[prog:]cmd ...", and will result in a 560 * false positive if prog != "ftp", so treat 561 * such commands as invalid. 562 */ 563 if (strchr(margv[0], ':') != NULL || 564 el_parse(el, margc, margv) != 0) 565 #endif /* !NO_EDITCOMPLETE */ 566 fputs("?Invalid command.\n", ttyout); 567 continue; 568 } 569 if (c->c_conn && !connected) { 570 fputs("Not connected.\n", ttyout); 571 continue; 572 } 573 confirmrest = 0; 574 (*c->c_handler)(margc, margv); 575 if (bell && c->c_bell) 576 (void)putc('\007', ttyout); 577 if (c->c_handler != help) 578 break; 579 } 580 (void)xsignal(SIGINT, intr); 581 (void)xsignal(SIGPIPE, lostpeer); 582 } 583 584 struct cmd * 585 getcmd(name) 586 const char *name; 587 { 588 const char *p, *q; 589 struct cmd *c, *found; 590 int nmatches, longest; 591 592 if (name == NULL) 593 return (0); 594 595 longest = 0; 596 nmatches = 0; 597 found = 0; 598 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 599 for (q = name; *q == *p++; q++) 600 if (*q == 0) /* exact match? */ 601 return (c); 602 if (!*q) { /* the name was a prefix */ 603 if (q - name > longest) { 604 longest = q - name; 605 nmatches = 1; 606 found = c; 607 } else if (q - name == longest) 608 nmatches++; 609 } 610 } 611 if (nmatches > 1) 612 return ((struct cmd *)-1); 613 return (found); 614 } 615 616 /* 617 * Slice a string up into argc/argv. 618 */ 619 620 int slrflag; 621 622 void 623 makeargv() 624 { 625 char *argp; 626 627 stringbase = line; /* scan from first of buffer */ 628 argbase = argbuf; /* store from first of buffer */ 629 slrflag = 0; 630 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 631 for (margc = 0; ; margc++) { 632 argp = slurpstring(); 633 sl_add(marg_sl, argp); 634 if (argp == NULL) 635 break; 636 } 637 #ifndef NO_EDITCOMPLETE 638 if (cursor_pos == line) { 639 cursor_argc = 0; 640 cursor_argo = 0; 641 } else if (cursor_pos != NULL) { 642 cursor_argc = margc; 643 cursor_argo = strlen(margv[margc-1]); 644 } 645 #endif /* !NO_EDITCOMPLETE */ 646 } 647 648 #ifdef NO_EDITCOMPLETE 649 #define INC_CHKCURSOR(x) (x)++ 650 #else /* !NO_EDITCOMPLETE */ 651 #define INC_CHKCURSOR(x) { (x)++ ; \ 652 if (x == cursor_pos) { \ 653 cursor_argc = margc; \ 654 cursor_argo = ap-argbase; \ 655 cursor_pos = NULL; \ 656 } } 657 658 #endif /* !NO_EDITCOMPLETE */ 659 660 /* 661 * Parse string into argbuf; 662 * implemented with FSM to 663 * handle quoting and strings 664 */ 665 char * 666 slurpstring() 667 { 668 int got_one = 0; 669 char *sb = stringbase; 670 char *ap = argbase; 671 char *tmp = argbase; /* will return this if token found */ 672 673 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 674 switch (slrflag) { /* and $ as token for macro invoke */ 675 case 0: 676 slrflag++; 677 INC_CHKCURSOR(stringbase); 678 return ((*sb == '!') ? "!" : "$"); 679 /* NOTREACHED */ 680 case 1: 681 slrflag++; 682 altarg = stringbase; 683 break; 684 default: 685 break; 686 } 687 } 688 689 S0: 690 switch (*sb) { 691 692 case '\0': 693 goto OUT; 694 695 case ' ': 696 case '\t': 697 INC_CHKCURSOR(sb); 698 goto S0; 699 700 default: 701 switch (slrflag) { 702 case 0: 703 slrflag++; 704 break; 705 case 1: 706 slrflag++; 707 altarg = sb; 708 break; 709 default: 710 break; 711 } 712 goto S1; 713 } 714 715 S1: 716 switch (*sb) { 717 718 case ' ': 719 case '\t': 720 case '\0': 721 goto OUT; /* end of token */ 722 723 case '\\': 724 INC_CHKCURSOR(sb); 725 goto S2; /* slurp next character */ 726 727 case '"': 728 INC_CHKCURSOR(sb); 729 goto S3; /* slurp quoted string */ 730 731 default: 732 *ap = *sb; /* add character to token */ 733 ap++; 734 INC_CHKCURSOR(sb); 735 got_one = 1; 736 goto S1; 737 } 738 739 S2: 740 switch (*sb) { 741 742 case '\0': 743 goto OUT; 744 745 default: 746 *ap = *sb; 747 ap++; 748 INC_CHKCURSOR(sb); 749 got_one = 1; 750 goto S1; 751 } 752 753 S3: 754 switch (*sb) { 755 756 case '\0': 757 goto OUT; 758 759 case '"': 760 INC_CHKCURSOR(sb); 761 goto S1; 762 763 default: 764 *ap = *sb; 765 ap++; 766 INC_CHKCURSOR(sb); 767 got_one = 1; 768 goto S3; 769 } 770 771 OUT: 772 if (got_one) 773 *ap++ = '\0'; 774 argbase = ap; /* update storage pointer */ 775 stringbase = sb; /* update scan pointer */ 776 if (got_one) { 777 return (tmp); 778 } 779 switch (slrflag) { 780 case 0: 781 slrflag++; 782 break; 783 case 1: 784 slrflag++; 785 altarg = NULL; 786 break; 787 default: 788 break; 789 } 790 return (NULL); 791 } 792 793 /* 794 * Help/usage command. 795 * Call each command handler with argc == 0 and argv[0] == name. 796 */ 797 void 798 help(argc, argv) 799 int argc; 800 char *argv[]; 801 { 802 struct cmd *c; 803 char *nargv[1], *p; 804 int isusage; 805 806 isusage = (strcmp(argv[0], "usage") == 0); 807 if (argc == 0 || (isusage && argc == 1)) { 808 fprintf(ttyout, "usage: %s [command [...]]\n", argv[0]); 809 return; 810 } 811 if (argc == 1) { 812 StringList *buf; 813 814 buf = sl_init(); 815 fprintf(ttyout, 816 "%sommands may be abbreviated. Commands are:\n\n", 817 proxy ? "Proxy c" : "C"); 818 for (c = cmdtab; (p = c->c_name) != NULL; c++) 819 if (!proxy || c->c_proxy) 820 sl_add(buf, p); 821 list_vertical(buf); 822 sl_free(buf, 0); 823 return; 824 } 825 826 #define HELPINDENT ((int) sizeof("disconnect")) 827 828 while (--argc > 0) { 829 char *arg; 830 831 arg = *++argv; 832 c = getcmd(arg); 833 if (c == (struct cmd *)-1) 834 fprintf(ttyout, "?Ambiguous %s command `%s'\n", 835 argv[0], arg); 836 else if (c == NULL) 837 fprintf(ttyout, "?Invalid %s command `%s'\n", 838 argv[0], arg); 839 else { 840 if (isusage) { 841 nargv[0] = arg; 842 (*c->c_handler)(0, nargv); 843 } else 844 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, 845 c->c_name, c->c_help); 846 } 847 } 848 } 849 850 struct option * 851 getoption(name) 852 const char *name; 853 { 854 const char *p; 855 struct option *c; 856 857 if (name == NULL) 858 return (NULL); 859 for (c = optiontab; (p = c->name) != NULL; c++) { 860 if (strcasecmp(p, name) == 0) 861 return (c); 862 } 863 return (NULL); 864 } 865 866 char * 867 getoptionvalue(name) 868 const char *name; 869 { 870 const char *p; 871 struct option *c; 872 873 if (name == NULL) 874 errx(1, "getoptionvalue() invoked with NULL name"); 875 for (c = optiontab; (p = c->name) != NULL; c++) { 876 if (strcasecmp(p, name) == 0) 877 return (c->value); 878 } 879 errx(1, "getoptionvalue() invoked with unknown option `%s'", name); 880 } 881 882 static void 883 setupoption(name, value, defaultvalue) 884 char *name, *value, *defaultvalue; 885 { 886 char *nargv[3]; 887 int overbose; 888 889 nargv[0] = "setupoption()"; 890 nargv[1] = name; 891 nargv[2] = (value ? value : defaultvalue); 892 overbose = verbose; 893 verbose = 0; 894 setoption(3, nargv); 895 verbose = overbose; 896 } 897 898 void 899 usage() 900 { 901 (void)fprintf(stderr, 902 "usage: %s [-AadefginpRtvV] [-r retry] [-o outfile] [-P port] [-T dir,max[,inc]\n" 903 " [host [port]] [file:///file] [ftp://[user[:pass]@]host[:port]/path[/]]\n" 904 " [http://[user[:pass]@]host[:port]/path] [host:path[/]]\n", __progname); 905 exit(1); 906 } 907