1 /* $NetBSD: main.c,v 1.80 2002/03/18 20:14:03 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1996-2001 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.80 2002/03/18 20:14:03 thorpej 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 #include <locale.h> 131 132 #define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */ 133 #include "ftp_var.h" 134 135 #define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ 136 #define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ 137 #define NO_PROXY "no_proxy" /* env var with list of non-proxied 138 * hosts, comma or space separated */ 139 140 static void setupoption(char *, char *, char *); 141 int main(int, char *[]); 142 143 int 144 main(int argc, char *argv[]) 145 { 146 int ch, rval; 147 struct passwd *pw; 148 char *cp, *ep, *anonuser, *anonpass, *upload_path; 149 int dumbterm, s, len, isupload; 150 151 setlocale(LC_ALL, ""); 152 153 ftpport = "ftp"; 154 httpport = "http"; 155 gateport = NULL; 156 cp = getenv("FTPSERVERPORT"); 157 if (cp != NULL) 158 gateport = cp; 159 else 160 gateport = "ftpgate"; 161 doglob = 1; 162 interactive = 1; 163 autologin = 1; 164 passivemode = 1; 165 activefallback = 1; 166 preserve = 1; 167 verbose = 0; 168 progress = 0; 169 gatemode = 0; 170 data = -1; 171 outfile = NULL; 172 restartautofetch = 0; 173 #ifndef NO_EDITCOMPLETE 174 editing = 0; 175 el = NULL; 176 hist = NULL; 177 #endif 178 bytes = 0; 179 mark = HASHBYTES; 180 rate_get = 0; 181 rate_get_incr = DEFAULTINCR; 182 rate_put = 0; 183 rate_put_incr = DEFAULTINCR; 184 #ifdef INET6 185 epsv4 = 1; 186 #else 187 epsv4 = 0; 188 #endif 189 epsv4bad = 0; 190 upload_path = NULL; 191 isupload = 0; 192 reply_callback = NULL; 193 family = AF_UNSPEC; 194 195 netrc[0] = '\0'; 196 cp = getenv("NETRC"); 197 if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc)) 198 errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG)); 199 200 /* 201 * Get the default socket buffer sizes if we don't already have them. 202 * It doesn't matter which socket we do this to, because on the first 203 * call no socket buffer sizes will have been modified, so we are 204 * guaranteed to get the system defaults. 205 */ 206 s = socket(AF_INET, SOCK_STREAM, 0); 207 if (s == -1) 208 err(1, "can't create socket"); 209 len = sizeof(rcvbuf_size); 210 if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf_size, &len) 211 < 0) 212 err(1, "unable to get default rcvbuf size"); 213 len = sizeof(sndbuf_size); 214 if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *) &sndbuf_size, &len) 215 < 0) 216 err(1, "unable to get default sndbuf size"); 217 (void)close(s); 218 /* sanity check returned buffer sizes */ 219 if (rcvbuf_size <= 0) 220 rcvbuf_size = 8192; 221 if (sndbuf_size <= 0) 222 sndbuf_size = 8192; 223 224 marg_sl = xsl_init(); 225 if ((tmpdir = getenv("TMPDIR")) == NULL) 226 tmpdir = _PATH_TMP; 227 228 /* Set default operation mode based on FTPMODE environment variable */ 229 if ((cp = getenv("FTPMODE")) != NULL) { 230 if (strcasecmp(cp, "passive") == 0) { 231 passivemode = 1; 232 activefallback = 0; 233 } else if (strcasecmp(cp, "active") == 0) { 234 passivemode = 0; 235 activefallback = 0; 236 } else if (strcasecmp(cp, "gate") == 0) { 237 gatemode = 1; 238 } else if (strcasecmp(cp, "auto") == 0) { 239 passivemode = 1; 240 activefallback = 1; 241 } else 242 warnx("unknown $FTPMODE '%s'; using defaults", cp); 243 } 244 245 if (strcmp(getprogname(), "pftp") == 0) { 246 passivemode = 1; 247 activefallback = 0; 248 } else if (strcmp(getprogname(), "gate-ftp") == 0) 249 gatemode = 1; 250 251 gateserver = getenv("FTPSERVER"); 252 if (gateserver == NULL || *gateserver == '\0') 253 gateserver = GATE_SERVER; 254 if (gatemode) { 255 if (*gateserver == '\0') { 256 warnx( 257 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); 258 gatemode = 0; 259 } 260 } 261 262 cp = getenv("TERM"); 263 if (cp == NULL || strcmp(cp, "dumb") == 0) 264 dumbterm = 1; 265 else 266 dumbterm = 0; 267 fromatty = isatty(fileno(stdin)); 268 ttyout = stdout; 269 if (isatty(fileno(ttyout))) { 270 verbose = 1; /* verbose if to a tty */ 271 if (! dumbterm) { 272 #ifndef NO_EDITCOMPLETE 273 if (fromatty) /* editing mode on if tty is usable */ 274 editing = 1; 275 #endif 276 #ifndef NO_PROGRESS 277 if (foregroundproc()) 278 progress = 1; /* progress bar on if fg */ 279 #endif 280 } 281 } 282 283 while ((ch = getopt(argc, argv, "46AadefginN:o:pP:r:RtT:u:vV")) != -1) { 284 switch (ch) { 285 case '4': 286 family = AF_INET; 287 break; 288 289 case '6': 290 family = AF_INET6; 291 break; 292 293 case 'A': 294 activefallback = 0; 295 passivemode = 0; 296 break; 297 298 case 'a': 299 anonftp = 1; 300 break; 301 302 case 'd': 303 options |= SO_DEBUG; 304 debug++; 305 break; 306 307 case 'e': 308 #ifndef NO_EDITCOMPLETE 309 editing = 0; 310 #endif 311 break; 312 313 case 'f': 314 flushcache = 1; 315 break; 316 317 case 'g': 318 doglob = 0; 319 break; 320 321 case 'i': 322 interactive = 0; 323 break; 324 325 case 'n': 326 autologin = 0; 327 break; 328 329 case 'N': 330 if (strlcpy(netrc, optarg, sizeof(netrc)) 331 >= sizeof(netrc)) 332 errx(1, "%s: %s", optarg, 333 strerror(ENAMETOOLONG)); 334 break; 335 336 case 'o': 337 outfile = optarg; 338 if (strcmp(outfile, "-") == 0) 339 ttyout = stderr; 340 break; 341 342 case 'p': 343 passivemode = 1; 344 activefallback = 0; 345 break; 346 347 case 'P': 348 ftpport = optarg; 349 break; 350 351 case 'r': 352 retry_connect = strtol(optarg, &ep, 10); 353 if (retry_connect < 1 || *ep != '\0') 354 errx(1, "bad retry value: %s", optarg); 355 break; 356 357 case 'R': 358 restartautofetch = 1; 359 break; 360 361 case 't': 362 trace = 1; 363 break; 364 365 case 'T': 366 { 367 int targc; 368 char *targv[6], *oac; 369 370 /* look for `dir,max[,incr]' */ 371 targc = 0; 372 targv[targc++] = "-T"; 373 oac = xstrdup(optarg); 374 375 while ((cp = strsep(&oac, ",")) != NULL) { 376 if (*cp == '\0') { 377 warnx("bad throttle value: %s", optarg); 378 usage(); 379 /* NOTREACHED */ 380 } 381 targv[targc++] = cp; 382 if (targc >= 5) 383 break; 384 } 385 if (parserate(targc, targv, 1) == -1) 386 usage(); 387 free(oac); 388 break; 389 } 390 391 case 'u': 392 { 393 isupload = 1; 394 interactive = 0; 395 upload_path = xstrdup(optarg); 396 397 break; 398 } 399 400 case 'v': 401 progress = verbose = 1; 402 break; 403 404 case 'V': 405 progress = verbose = 0; 406 break; 407 408 default: 409 usage(); 410 } 411 } 412 /* set line buffering on ttyout */ 413 setvbuf(ttyout, NULL, _IOLBF, 0); 414 argc -= optind; 415 argv += optind; 416 417 cpend = 0; /* no pending replies */ 418 proxy = 0; /* proxy not active */ 419 crflag = 1; /* strip c.r. on ascii gets */ 420 sendport = -1; /* not using ports */ 421 422 /* 423 * Cache the user name and home directory. 424 */ 425 localhome = NULL; 426 localname = NULL; 427 anonuser = "anonymous"; 428 cp = getenv("HOME"); 429 if (! EMPTYSTRING(cp)) 430 localhome = xstrdup(cp); 431 pw = NULL; 432 cp = getlogin(); 433 if (cp != NULL) 434 pw = getpwnam(cp); 435 if (pw == NULL) 436 pw = getpwuid(getuid()); 437 if (pw != NULL) { 438 if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) 439 localhome = xstrdup(pw->pw_dir); 440 localname = xstrdup(pw->pw_name); 441 anonuser = localname; 442 } 443 if (netrc[0] == '\0' && localhome != NULL) { 444 if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || 445 strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) { 446 warnx("%s/.netrc: %s", localhome, 447 strerror(ENAMETOOLONG)); 448 netrc[0] = '\0'; 449 } 450 } 451 if (localhome == NULL) 452 localhome = xstrdup("/"); 453 454 /* 455 * Every anonymous FTP server I've encountered will accept the 456 * string "username@", and will append the hostname itself. We 457 * do this by default since many servers are picky about not 458 * having a FQDN in the anonymous password. 459 * - thorpej@netbsd.org 460 */ 461 len = strlen(anonuser) + 2; 462 anonpass = xmalloc(len); 463 (void)strlcpy(anonpass, anonuser, len); 464 (void)strlcat(anonpass, "@", len); 465 466 /* 467 * set all the defaults for options defined in 468 * struct option optiontab[] declared in cmdtab.c 469 */ 470 setupoption("anonpass", getenv("FTPANONPASS"), anonpass); 471 setupoption("ftp_proxy", getenv(FTP_PROXY), ""); 472 setupoption("http_proxy", getenv(HTTP_PROXY), ""); 473 setupoption("no_proxy", getenv(NO_PROXY), ""); 474 setupoption("pager", getenv("PAGER"), DEFAULTPAGER); 475 setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); 476 setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT); 477 478 free(anonpass); 479 480 setttywidth(0); 481 #ifdef SIGINFO 482 (void)xsignal(SIGINFO, psummary); 483 #endif 484 (void)xsignal(SIGQUIT, psummary); 485 (void)xsignal(SIGUSR1, crankrate); 486 (void)xsignal(SIGUSR2, crankrate); 487 (void)xsignal(SIGWINCH, setttywidth); 488 489 #ifdef __GNUC__ /* to shut up gcc warnings */ 490 (void)&argc; 491 (void)&argv; 492 #endif 493 494 if (argc > 0) { 495 if (isupload) { 496 rval = auto_put(argc, argv, upload_path); 497 exit(rval); 498 } else if (strchr(argv[0], ':') != NULL 499 && ! isipv6addr(argv[0])) { 500 rval = auto_fetch(argc, argv); 501 if (rval >= 0) /* -1 == connected and cd-ed */ 502 exit(rval); 503 } else { 504 char *xargv[4], *user, *host; 505 506 if (sigsetjmp(toplevel, 1)) 507 exit(0); 508 (void)xsignal(SIGINT, intr); 509 (void)xsignal(SIGPIPE, lostpeer); 510 user = NULL; 511 host = argv[0]; 512 cp = strchr(host, '@'); 513 if (cp) { 514 *cp = '\0'; 515 user = host; 516 host = cp + 1; 517 } 518 /* XXX discards const */ 519 xargv[0] = (char *)getprogname(); 520 xargv[1] = host; 521 xargv[2] = argv[1]; 522 xargv[3] = NULL; 523 do { 524 int oautologin; 525 526 oautologin = autologin; 527 if (user != NULL) { 528 anonftp = 0; 529 autologin = 0; 530 } 531 setpeer(argc+1, xargv); 532 autologin = oautologin; 533 if (connected == 1 && user != NULL) 534 (void)ftp_login(host, user, NULL); 535 if (!retry_connect) 536 break; 537 if (!connected) { 538 macnum = 0; 539 fprintf(ttyout, 540 "Retrying in %d seconds...\n", 541 retry_connect); 542 sleep(retry_connect); 543 } 544 } while (!connected); 545 retry_connect = 0; /* connected, stop hiding msgs */ 546 } 547 } 548 if (isupload) 549 usage(); 550 551 #ifndef NO_EDITCOMPLETE 552 controlediting(); 553 #endif /* !NO_EDITCOMPLETE */ 554 555 (void)sigsetjmp(toplevel, 1); 556 (void)xsignal(SIGINT, intr); 557 (void)xsignal(SIGPIPE, lostpeer); 558 for (;;) 559 cmdscanner(); 560 } 561 562 /* 563 * Generate a prompt 564 */ 565 char * 566 prompt(void) 567 { 568 static char **prompt; 569 static char buf[MAXPATHLEN]; 570 571 if (prompt == NULL) { 572 struct option *o; 573 574 o = getoption("prompt"); 575 if (o == NULL) 576 errx(1, "no such option `prompt'"); 577 prompt = &(o->value); 578 } 579 formatbuf(buf, sizeof(buf), *prompt ? *prompt : DEFAULTPROMPT); 580 return (buf); 581 } 582 583 /* 584 * Generate an rprompt 585 */ 586 char * 587 rprompt(void) 588 { 589 static char **rprompt; 590 static char buf[MAXPATHLEN]; 591 592 if (rprompt == NULL) { 593 struct option *o; 594 595 o = getoption("rprompt"); 596 if (o == NULL) 597 errx(1, "no such option `rprompt'"); 598 rprompt = &(o->value); 599 } 600 formatbuf(buf, sizeof(buf), *rprompt ? *rprompt : DEFAULTRPROMPT); 601 return (buf); 602 } 603 604 /* 605 * Command parser. 606 */ 607 void 608 cmdscanner(void) 609 { 610 struct cmd *c; 611 char *p; 612 int num; 613 614 for (;;) { 615 #ifndef NO_EDITCOMPLETE 616 if (!editing) { 617 #endif /* !NO_EDITCOMPLETE */ 618 if (fromatty) { 619 fputs(prompt(), ttyout); 620 p = rprompt(); 621 if (*p) 622 fprintf(ttyout, "%s ", p); 623 (void)fflush(ttyout); 624 } 625 if (fgets(line, sizeof(line), stdin) == NULL) { 626 if (fromatty) 627 putc('\n', ttyout); 628 quit(0, NULL); 629 } 630 num = strlen(line); 631 if (num == 0) 632 break; 633 if (line[--num] == '\n') { 634 if (num == 0) 635 break; 636 line[num] = '\0'; 637 } else if (num == sizeof(line) - 2) { 638 fputs("sorry, input line too long.\n", ttyout); 639 while ((num = getchar()) != '\n' && num != EOF) 640 /* void */; 641 break; 642 } /* else it was a line without a newline */ 643 #ifndef NO_EDITCOMPLETE 644 } else { 645 const char *buf; 646 HistEvent ev; 647 cursor_pos = NULL; 648 649 if ((buf = el_gets(el, &num)) == NULL || num == 0) { 650 if (fromatty) 651 putc('\n', ttyout); 652 quit(0, NULL); 653 } 654 if (buf[--num] == '\n') { 655 if (num == 0) 656 break; 657 } else if (num >= sizeof(line)) { 658 fputs("sorry, input line too long.\n", ttyout); 659 break; 660 } 661 memcpy(line, buf, num); 662 line[num] = '\0'; 663 history(hist, &ev, H_ENTER, buf); 664 } 665 #endif /* !NO_EDITCOMPLETE */ 666 667 makeargv(); 668 if (margc == 0) 669 continue; 670 c = getcmd(margv[0]); 671 if (c == (struct cmd *)-1) { 672 fputs("?Ambiguous command.\n", ttyout); 673 continue; 674 } 675 if (c == NULL) { 676 #if !defined(NO_EDITCOMPLETE) 677 /* 678 * attempt to el_parse() unknown commands. 679 * any command containing a ':' would be parsed 680 * as "[prog:]cmd ...", and will result in a 681 * false positive if prog != "ftp", so treat 682 * such commands as invalid. 683 */ 684 if (strchr(margv[0], ':') != NULL || 685 el_parse(el, margc, (const char **)margv) != 0) 686 #endif /* !NO_EDITCOMPLETE */ 687 fputs("?Invalid command.\n", ttyout); 688 continue; 689 } 690 if (c->c_conn && !connected) { 691 fputs("Not connected.\n", ttyout); 692 continue; 693 } 694 confirmrest = 0; 695 margv[0] = c->c_name; 696 (*c->c_handler)(margc, margv); 697 if (bell && c->c_bell) 698 (void)putc('\007', ttyout); 699 if (c->c_handler != help) 700 break; 701 } 702 (void)xsignal(SIGINT, intr); 703 (void)xsignal(SIGPIPE, lostpeer); 704 } 705 706 struct cmd * 707 getcmd(const char *name) 708 { 709 const char *p, *q; 710 struct cmd *c, *found; 711 int nmatches, longest; 712 713 if (name == NULL) 714 return (0); 715 716 longest = 0; 717 nmatches = 0; 718 found = 0; 719 for (c = cmdtab; (p = c->c_name) != NULL; c++) { 720 for (q = name; *q == *p++; q++) 721 if (*q == 0) /* exact match? */ 722 return (c); 723 if (!*q) { /* the name was a prefix */ 724 if (q - name > longest) { 725 longest = q - name; 726 nmatches = 1; 727 found = c; 728 } else if (q - name == longest) 729 nmatches++; 730 } 731 } 732 if (nmatches > 1) 733 return ((struct cmd *)-1); 734 return (found); 735 } 736 737 /* 738 * Slice a string up into argc/argv. 739 */ 740 741 int slrflag; 742 743 void 744 makeargv(void) 745 { 746 char *argp; 747 748 stringbase = line; /* scan from first of buffer */ 749 argbase = argbuf; /* store from first of buffer */ 750 slrflag = 0; 751 marg_sl->sl_cur = 0; /* reset to start of marg_sl */ 752 for (margc = 0; ; margc++) { 753 argp = slurpstring(); 754 xsl_add(marg_sl, argp); 755 if (argp == NULL) 756 break; 757 } 758 #ifndef NO_EDITCOMPLETE 759 if (cursor_pos == line) { 760 cursor_argc = 0; 761 cursor_argo = 0; 762 } else if (cursor_pos != NULL) { 763 cursor_argc = margc; 764 cursor_argo = strlen(margv[margc-1]); 765 } 766 #endif /* !NO_EDITCOMPLETE */ 767 } 768 769 #ifdef NO_EDITCOMPLETE 770 #define INC_CHKCURSOR(x) (x)++ 771 #else /* !NO_EDITCOMPLETE */ 772 #define INC_CHKCURSOR(x) { (x)++ ; \ 773 if (x == cursor_pos) { \ 774 cursor_argc = margc; \ 775 cursor_argo = ap-argbase; \ 776 cursor_pos = NULL; \ 777 } } 778 779 #endif /* !NO_EDITCOMPLETE */ 780 781 /* 782 * Parse string into argbuf; 783 * implemented with FSM to 784 * handle quoting and strings 785 */ 786 char * 787 slurpstring(void) 788 { 789 int got_one = 0; 790 char *sb = stringbase; 791 char *ap = argbase; 792 char *tmp = argbase; /* will return this if token found */ 793 794 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ 795 switch (slrflag) { /* and $ as token for macro invoke */ 796 case 0: 797 slrflag++; 798 INC_CHKCURSOR(stringbase); 799 return ((*sb == '!') ? "!" : "$"); 800 /* NOTREACHED */ 801 case 1: 802 slrflag++; 803 altarg = stringbase; 804 break; 805 default: 806 break; 807 } 808 } 809 810 S0: 811 switch (*sb) { 812 813 case '\0': 814 goto OUT; 815 816 case ' ': 817 case '\t': 818 INC_CHKCURSOR(sb); 819 goto S0; 820 821 default: 822 switch (slrflag) { 823 case 0: 824 slrflag++; 825 break; 826 case 1: 827 slrflag++; 828 altarg = sb; 829 break; 830 default: 831 break; 832 } 833 goto S1; 834 } 835 836 S1: 837 switch (*sb) { 838 839 case ' ': 840 case '\t': 841 case '\0': 842 goto OUT; /* end of token */ 843 844 case '\\': 845 INC_CHKCURSOR(sb); 846 goto S2; /* slurp next character */ 847 848 case '"': 849 INC_CHKCURSOR(sb); 850 goto S3; /* slurp quoted string */ 851 852 default: 853 *ap = *sb; /* add character to token */ 854 ap++; 855 INC_CHKCURSOR(sb); 856 got_one = 1; 857 goto S1; 858 } 859 860 S2: 861 switch (*sb) { 862 863 case '\0': 864 goto OUT; 865 866 default: 867 *ap = *sb; 868 ap++; 869 INC_CHKCURSOR(sb); 870 got_one = 1; 871 goto S1; 872 } 873 874 S3: 875 switch (*sb) { 876 877 case '\0': 878 goto OUT; 879 880 case '"': 881 INC_CHKCURSOR(sb); 882 goto S1; 883 884 default: 885 *ap = *sb; 886 ap++; 887 INC_CHKCURSOR(sb); 888 got_one = 1; 889 goto S3; 890 } 891 892 OUT: 893 if (got_one) 894 *ap++ = '\0'; 895 argbase = ap; /* update storage pointer */ 896 stringbase = sb; /* update scan pointer */ 897 if (got_one) { 898 return (tmp); 899 } 900 switch (slrflag) { 901 case 0: 902 slrflag++; 903 break; 904 case 1: 905 slrflag++; 906 altarg = NULL; 907 break; 908 default: 909 break; 910 } 911 return (NULL); 912 } 913 914 /* 915 * Help/usage command. 916 * Call each command handler with argc == 0 and argv[0] == name. 917 */ 918 void 919 help(int argc, char *argv[]) 920 { 921 struct cmd *c; 922 char *nargv[1], *p, *cmd; 923 int isusage; 924 925 cmd = argv[0]; 926 isusage = (strcmp(cmd, "usage") == 0); 927 if (argc == 0 || (isusage && argc == 1)) { 928 fprintf(ttyout, "usage: %s [command [...]]\n", cmd); 929 return; 930 } 931 if (argc == 1) { 932 StringList *buf; 933 934 buf = xsl_init(); 935 fprintf(ttyout, 936 "%sommands may be abbreviated. Commands are:\n\n", 937 proxy ? "Proxy c" : "C"); 938 for (c = cmdtab; (p = c->c_name) != NULL; c++) 939 if (!proxy || c->c_proxy) 940 xsl_add(buf, p); 941 list_vertical(buf); 942 sl_free(buf, 0); 943 return; 944 } 945 946 #define HELPINDENT ((int) sizeof("disconnect")) 947 948 while (--argc > 0) { 949 char *arg; 950 951 arg = *++argv; 952 c = getcmd(arg); 953 if (c == (struct cmd *)-1) 954 fprintf(ttyout, "?Ambiguous %s command `%s'\n", 955 cmd, arg); 956 else if (c == NULL) 957 fprintf(ttyout, "?Invalid %s command `%s'\n", 958 cmd, arg); 959 else { 960 if (isusage) { 961 nargv[0] = c->c_name; 962 (*c->c_handler)(0, nargv); 963 } else 964 fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, 965 c->c_name, c->c_help); 966 } 967 } 968 } 969 970 struct option * 971 getoption(const char *name) 972 { 973 const char *p; 974 struct option *c; 975 976 if (name == NULL) 977 return (NULL); 978 for (c = optiontab; (p = c->name) != NULL; c++) { 979 if (strcasecmp(p, name) == 0) 980 return (c); 981 } 982 return (NULL); 983 } 984 985 char * 986 getoptionvalue(const char *name) 987 { 988 struct option *c; 989 990 if (name == NULL) 991 errx(1, "getoptionvalue() invoked with NULL name"); 992 c = getoption(name); 993 if (c != NULL) 994 return (c->value); 995 errx(1, "getoptionvalue() invoked with unknown option `%s'", name); 996 /* NOTREACHED */ 997 } 998 999 static void 1000 setupoption(char *name, char *value, char *defaultvalue) 1001 { 1002 char *nargv[3]; 1003 int overbose; 1004 1005 nargv[0] = "setupoption()"; 1006 nargv[1] = name; 1007 nargv[2] = (value ? value : defaultvalue); 1008 overbose = verbose; 1009 verbose = 0; 1010 setoption(3, nargv); 1011 verbose = overbose; 1012 } 1013 1014 void 1015 usage(void) 1016 { 1017 const char *progname = getprogname(); 1018 1019 (void)fprintf(stderr, 1020 "usage: %s [-46AadefginpRtvV] [-N netrc] [-o outfile] [-P port] [-r retry]\n" 1021 " [-T dir,max[,inc][[user@]host [port]]] [host:path[/]]\n" 1022 " [file:///file] [ftp://[user[:pass]@]host[:port]/path[/]]\n" 1023 " [http://[user[:pass]@]host[:port]/path] [...]\n" 1024 " %s -u url file [...]\n", progname, progname); 1025 exit(1); 1026 } 1027