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