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