Home | History | Annotate | Line # | Download | only in ftp
main.c revision 1.16
      1 /*	$NetBSD: main.c,v 1.16 1997/01/19 14:19:15 lukem Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1985, 1989, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #ifndef lint
     37 static char copyright[] =
     38 "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
     39 	The Regents of the University of California.  All rights reserved.\n";
     40 #endif /* not lint */
     41 
     42 #ifndef lint
     43 #if 0
     44 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 10/9/94";
     45 #else
     46 static char rcsid[] = "$NetBSD: main.c,v 1.16 1997/01/19 14:19:15 lukem Exp $";
     47 #endif
     48 #endif /* not lint */
     49 
     50 /*
     51  * FTP User Program -- Command Interface.
     52  */
     53 #include <sys/types.h>
     54 #include <sys/socket.h>
     55 
     56 #include <err.h>
     57 #include <netdb.h>
     58 #include <pwd.h>
     59 #include <stdio.h>
     60 #include <string.h>
     61 #include <unistd.h>
     62 
     63 #include "ftp_var.h"
     64 
     65 int
     66 main(argc, argv)
     67 	int argc;
     68 	char *argv[];
     69 {
     70 	struct servent *sp;
     71 	int ch, top, port, rval;
     72 	struct passwd *pw = NULL;
     73 	char *cp, homedir[MAXPATHLEN];
     74 
     75 	sp = getservbyname("ftp", "tcp");
     76 	if (sp == 0)
     77 		ftpport = htons(FTP_PORT);	/* good fallback */
     78 	else
     79 		ftpport = sp->s_port;
     80 	sp = getservbyname("http", "tcp");
     81 	if (sp == 0)
     82 		httpport = htons(HTTP_PORT);	/* good fallback */
     83 	else
     84 		httpport = sp->s_port;
     85 	doglob = 1;
     86 	interactive = 1;
     87 	autologin = 1;
     88 	passivemode = 0;
     89 	preserve = 1;
     90 	mark = HASHBYTES;
     91 	marg_sl = sl_init();
     92 
     93 	cp = strrchr(argv[0], '/');
     94 	cp = (cp == NULL) ? argv[0] : cp + 1;
     95 	if (strcmp(cp, "pftp") == 0)
     96 		passivemode = 1;
     97 
     98 	while ((ch = getopt(argc, argv, "adginpP:tv")) != EOF) {
     99 		switch (ch) {
    100 		case 'a':
    101 			anonftp = 1;
    102 			break;
    103 
    104 		case 'd':
    105 			options |= SO_DEBUG;
    106 			debug++;
    107 			break;
    108 
    109 		case 'g':
    110 			doglob = 0;
    111 			break;
    112 
    113 		case 'i':
    114 			interactive = 0;
    115 			break;
    116 
    117 		case 'n':
    118 			autologin = 0;
    119 			break;
    120 
    121 		case 'p':
    122 			passivemode = 1;
    123 			break;
    124 
    125 		case 'P':
    126 			port = atoi(optarg);
    127 			if (port <= 0)
    128 				warnx("bad port number: %s", optarg);
    129 			else
    130 				ftpport = htons(port);
    131 			break;
    132 
    133 		case 't':
    134 			trace++;
    135 			break;
    136 
    137 		case 'v':
    138 			verbose++;
    139 			break;
    140 
    141 		default:
    142 			usage();
    143 		}
    144 	}
    145 	argc -= optind;
    146 	argv += optind;
    147 
    148 	fromatty = isatty(fileno(stdin));
    149 	if (fromatty) {
    150 		verbose++;
    151 		progress = 1;		/* progress bar on if a tty */
    152 	}
    153 	cpend = 0;	/* no pending replies */
    154 	proxy = 0;	/* proxy not active */
    155 	crflag = 1;	/* strip c.r. on ascii gets */
    156 	sendport = -1;	/* not using ports */
    157 	/*
    158 	 * Set up the home directory in case we're globbing.
    159 	 */
    160 	cp = getlogin();
    161 	if (cp != NULL) {
    162 		pw = getpwnam(cp);
    163 	}
    164 	if (pw == NULL)
    165 		pw = getpwuid(getuid());
    166 	if (pw != NULL) {
    167 		home = homedir;
    168 		(void) strcpy(home, pw->pw_dir);
    169 	}
    170 
    171 #ifndef SMALLFTP
    172 	editing = 0;			/* command line editing off */
    173 	if (fromatty) {
    174 		editing = 1;		/* editing mode on if a tty */
    175 		el = el_init(__progname, stdin, stdout); /* init editline */
    176 
    177 		hist = history_init();		/* init the builtin history */
    178 		history(hist, H_EVENT, 100);	/* remember 100 events */
    179 		el_set(el, EL_HIST, history, hist);	/* use history */
    180 
    181 		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
    182 		el_set(el, EL_PROMPT, prompt);	/* set the prompt function */
    183 
    184 		/* add local file completion, bind to TAB */
    185 		el_set(el, EL_ADDFN, "ftp-complete",
    186 		    "Context sensitive argument completion",
    187 		    complete);
    188 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
    189 
    190 		el_source(el, NULL);	/* read ~/.editrc */
    191 	}
    192 #endif /* !SMALLFTP */
    193 
    194 	if (argc > 0) {
    195 		if (strchr(argv[0], ':') != NULL) {
    196 			anonftp = 1;	/* Handle "automatic" transfers. */
    197 			rval = auto_fetch(argc, argv);
    198 			if (rval >= 0)		/* -1 == connected and cd-ed */
    199 				exit(rval);
    200 		} else {
    201 			char *xargv[5];
    202 
    203 			if (setjmp(toplevel))
    204 				exit(0);
    205 			(void) signal(SIGINT, intr);
    206 			(void) signal(SIGPIPE, lostpeer);
    207 			xargv[0] = __progname;
    208 			xargv[1] = argv[0];
    209 			xargv[2] = argv[1];
    210 			xargv[3] = argv[2];
    211 			xargv[4] = NULL;
    212 			setpeer(argc+1, xargv);
    213 		}
    214 	}
    215 	top = setjmp(toplevel) == 0;
    216 	if (top) {
    217 		(void) signal(SIGINT, intr);
    218 		(void) signal(SIGPIPE, lostpeer);
    219 	}
    220 	for (;;) {
    221 		cmdscanner(top);
    222 		top = 1;
    223 	}
    224 }
    225 
    226 void
    227 intr()
    228 {
    229 
    230 	longjmp(toplevel, 1);
    231 }
    232 
    233 void
    234 lostpeer()
    235 {
    236 
    237 	if (connected) {
    238 		if (cout != NULL) {
    239 			(void) shutdown(fileno(cout), 1+1);
    240 			(void) fclose(cout);
    241 			cout = NULL;
    242 		}
    243 		if (data >= 0) {
    244 			(void) shutdown(data, 1+1);
    245 			(void) close(data);
    246 			data = -1;
    247 		}
    248 		connected = 0;
    249 	}
    250 	pswitch(1);
    251 	if (connected) {
    252 		if (cout != NULL) {
    253 			(void) shutdown(fileno(cout), 1+1);
    254 			(void) fclose(cout);
    255 			cout = NULL;
    256 		}
    257 		connected = 0;
    258 	}
    259 	proxflag = 0;
    260 	pswitch(0);
    261 }
    262 
    263 /*
    264  * Generate a prompt
    265  */
    266 char *
    267 prompt()
    268 {
    269 	return ("ftp> ");
    270 }
    271 
    272 /*
    273  * Command parser.
    274  */
    275 void
    276 cmdscanner(top)
    277 	int top;
    278 {
    279 	struct cmd *c;
    280 	int num;
    281 
    282 	if (!top
    283 #ifndef SMALLFTP
    284 	    && !editing
    285 #endif /* !SMALLFTP */
    286 	    )
    287 		(void) putchar('\n');
    288 	for (;;) {
    289 #ifndef SMALLFTP
    290 		if (!editing) {
    291 #endif /* !SMALLFTP */
    292 			if (fromatty) {
    293 				printf("%s", prompt());
    294 				(void) fflush(stdout);
    295 			}
    296 			if (fgets(line, sizeof(line), stdin) == NULL)
    297 				quit(0, 0);
    298 			num = strlen(line);
    299 			if (num == 0)
    300 				break;
    301 			if (line[--num] == '\n') {
    302 				if (num == 0)
    303 					break;
    304 				line[num] = '\0';
    305 			} else if (num == sizeof(line) - 2) {
    306 				printf("sorry, input line too long\n");
    307 				while ((num = getchar()) != '\n' && num != EOF)
    308 					/* void */;
    309 				break;
    310 			} /* else it was a line without a newline */
    311 #ifndef SMALLFTP
    312 		}
    313 		else {
    314 			const char *buf;
    315 			cursor_pos = NULL;
    316 
    317 			if ((buf = el_gets(el, &num)) == NULL || num == 0)
    318 				break;
    319 			if (line[--num] == '\n') {
    320 				if (num == 0)
    321 					break;
    322 			} else if (num >= sizeof(line)) {
    323 				printf("sorry, input line too long\n");
    324 				break;
    325 			}
    326 			memcpy(line, buf, num);
    327 			line[num] = '\0';
    328 			history(hist, H_ENTER, buf);
    329 		}
    330 #endif /* !SMALLFTP */
    331 
    332 		makeargv();
    333 		if (margc == 0)
    334 			continue;
    335 #if 0 || !defined(SMALLFTP)	/* XXX: don't want el_parse */
    336 		/*
    337 		 * el_parse returns -1 to signal that it's not been handled
    338 		 * internally.
    339 		 */
    340 		if (el_parse(el, margc, margv) != -1)
    341 			continue;
    342 #endif /* !SMALLFTP */
    343 		c = getcmd(margv[0]);
    344 		if (c == (struct cmd *)-1) {
    345 			printf("?Ambiguous command\n");
    346 			continue;
    347 		}
    348 		if (c == 0) {
    349 			printf("?Invalid command\n");
    350 			continue;
    351 		}
    352 		if (c->c_conn && !connected) {
    353 			printf("Not connected.\n");
    354 			continue;
    355 		}
    356 		confirmrest = 0;
    357 		(*c->c_handler)(margc, margv);
    358 		if (bell && c->c_bell)
    359 			(void) putchar('\007');
    360 		if (c->c_handler != help)
    361 			break;
    362 	}
    363 	(void) signal(SIGINT, intr);
    364 	(void) signal(SIGPIPE, lostpeer);
    365 }
    366 
    367 struct cmd *
    368 getcmd(name)
    369 	const char *name;
    370 {
    371 	const char *p, *q;
    372 	struct cmd *c, *found;
    373 	int nmatches, longest;
    374 
    375 	if (name == NULL)
    376 		return (0);
    377 
    378 	longest = 0;
    379 	nmatches = 0;
    380 	found = 0;
    381 	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
    382 		for (q = name; *q == *p++; q++)
    383 			if (*q == 0)		/* exact match? */
    384 				return (c);
    385 		if (!*q) {			/* the name was a prefix */
    386 			if (q - name > longest) {
    387 				longest = q - name;
    388 				nmatches = 1;
    389 				found = c;
    390 			} else if (q - name == longest)
    391 				nmatches++;
    392 		}
    393 	}
    394 	if (nmatches > 1)
    395 		return ((struct cmd *)-1);
    396 	return (found);
    397 }
    398 
    399 /*
    400  * Slice a string up into argc/argv.
    401  */
    402 
    403 int slrflag;
    404 
    405 void
    406 makeargv()
    407 {
    408 	char *argp;
    409 
    410 	stringbase = line;		/* scan from first of buffer */
    411 	argbase = argbuf;		/* store from first of buffer */
    412 	slrflag = 0;
    413 	marg_sl->sl_cur = 0;		/* reset to start of marg_sl */
    414 	for (margc = 0; ; margc++) {
    415 		argp = slurpstring();
    416 		sl_add(marg_sl, argp);
    417 		if (argp == NULL)
    418 			break;
    419 	}
    420 #ifndef SMALLFTP
    421 	if (cursor_pos == line) {
    422 		cursor_argc = 0;
    423 		cursor_argo = 0;
    424 	} else if (cursor_pos != NULL) {
    425 		cursor_argc = margc;
    426 		cursor_argo = strlen(margv[margc-1]);
    427 	}
    428 #endif /* !SMALLFTP */
    429 }
    430 
    431 #ifdef SMALLFTP
    432 #define INC_CHKCURSOR(x)	(x)++
    433 #else  /* !SMALLFTP */
    434 #define INC_CHKCURSOR(x)	{ (x)++ ; \
    435 				if (x == cursor_pos) { \
    436 					cursor_argc = margc; \
    437 					cursor_argo = ap-argbase; \
    438 					cursor_pos = NULL; \
    439 				} }
    440 
    441 #endif /* !SMALLFTP */
    442 
    443 /*
    444  * Parse string into argbuf;
    445  * implemented with FSM to
    446  * handle quoting and strings
    447  */
    448 char *
    449 slurpstring()
    450 {
    451 	int got_one = 0;
    452 	char *sb = stringbase;
    453 	char *ap = argbase;
    454 	char *tmp = argbase;		/* will return this if token found */
    455 
    456 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
    457 		switch (slrflag) {	/* and $ as token for macro invoke */
    458 			case 0:
    459 				slrflag++;
    460 				INC_CHKCURSOR(stringbase);
    461 				return ((*sb == '!') ? "!" : "$");
    462 				/* NOTREACHED */
    463 			case 1:
    464 				slrflag++;
    465 				altarg = stringbase;
    466 				break;
    467 			default:
    468 				break;
    469 		}
    470 	}
    471 
    472 S0:
    473 	switch (*sb) {
    474 
    475 	case '\0':
    476 		goto OUT;
    477 
    478 	case ' ':
    479 	case '\t':
    480 		INC_CHKCURSOR(sb);
    481 		goto S0;
    482 
    483 	default:
    484 		switch (slrflag) {
    485 			case 0:
    486 				slrflag++;
    487 				break;
    488 			case 1:
    489 				slrflag++;
    490 				altarg = sb;
    491 				break;
    492 			default:
    493 				break;
    494 		}
    495 		goto S1;
    496 	}
    497 
    498 S1:
    499 	switch (*sb) {
    500 
    501 	case ' ':
    502 	case '\t':
    503 	case '\0':
    504 		goto OUT;	/* end of token */
    505 
    506 	case '\\':
    507 		INC_CHKCURSOR(sb);
    508 		goto S2;	/* slurp next character */
    509 
    510 	case '"':
    511 		INC_CHKCURSOR(sb);
    512 		goto S3;	/* slurp quoted string */
    513 
    514 	default:
    515 		*ap = *sb;	/* add character to token */
    516 		ap++;
    517 		INC_CHKCURSOR(sb);
    518 		got_one = 1;
    519 		goto S1;
    520 	}
    521 
    522 S2:
    523 	switch (*sb) {
    524 
    525 	case '\0':
    526 		goto OUT;
    527 
    528 	default:
    529 		*ap = *sb;
    530 		ap++;
    531 		INC_CHKCURSOR(sb);
    532 		got_one = 1;
    533 		goto S1;
    534 	}
    535 
    536 S3:
    537 	switch (*sb) {
    538 
    539 	case '\0':
    540 		goto OUT;
    541 
    542 	case '"':
    543 		INC_CHKCURSOR(sb);
    544 		goto S1;
    545 
    546 	default:
    547 		*ap = *sb;
    548 		ap++;
    549 		INC_CHKCURSOR(sb);
    550 		got_one = 1;
    551 		goto S3;
    552 	}
    553 
    554 OUT:
    555 	if (got_one)
    556 		*ap++ = '\0';
    557 	argbase = ap;			/* update storage pointer */
    558 	stringbase = sb;		/* update scan pointer */
    559 	if (got_one) {
    560 		return (tmp);
    561 	}
    562 	switch (slrflag) {
    563 		case 0:
    564 			slrflag++;
    565 			break;
    566 		case 1:
    567 			slrflag++;
    568 			altarg = (char *) 0;
    569 			break;
    570 		default:
    571 			break;
    572 	}
    573 	return ((char *)0);
    574 }
    575 
    576 /*
    577  * Help command.
    578  * Call each command handler with argc == 0 and argv[0] == name.
    579  */
    580 void
    581 help(argc, argv)
    582 	int argc;
    583 	char *argv[];
    584 {
    585 	struct cmd *c;
    586 
    587 	if (argc == 1) {
    588 		StringList *buf;
    589 
    590 		buf = sl_init();
    591 		printf("%sommands may be abbreviated.  Commands are:\n\n",
    592 		    proxy ? "Proxy c" : "C");
    593 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
    594 			if (c->c_name && (!proxy || c->c_proxy))
    595 				sl_add(buf, c->c_name);
    596 		list_vertical(buf);
    597 		sl_free(buf, 0);
    598 		return;
    599 	}
    600 
    601 #define HELPINDENT ((int) sizeof ("disconnect"))
    602 
    603 	while (--argc > 0) {
    604 		char *arg;
    605 
    606 		arg = *++argv;
    607 		c = getcmd(arg);
    608 		if (c == (struct cmd *)-1)
    609 			printf("?Ambiguous help command %s\n", arg);
    610 		else if (c == (struct cmd *)0)
    611 			printf("?Invalid help command %s\n", arg);
    612 		else
    613 			printf("%-*s\t%s\n", HELPINDENT,
    614 				c->c_name, c->c_help);
    615 	}
    616 }
    617 
    618 void
    619 usage()
    620 {
    621 	(void)fprintf(stderr,
    622 	    "usage: %s [-adginptv] [host [port]]\n"
    623 	    "       %s host:path[/]\n"
    624 	    "       %s ftp://host[:port]/path[/]\n"
    625 	    "       %s http://host[:port]/file\n",
    626 	    __progname, __progname, __progname, __progname);
    627 	exit(1);
    628 }
    629