HardenedBSD src tree https://hardenedbsd.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1069 lines
24 KiB

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