Home | History | Annotate | Line # | Download | only in ftp
main.c revision 1.17
      1 /*	$NetBSD: main.c,v 1.17 1997/02/01 10:45:07 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.17 1997/02/01 10:45:07 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 	verbose = 0;
     91 	progress = 0;
     92 	mark = HASHBYTES;
     93 	marg_sl = sl_init();
     94 
     95 	cp = strrchr(argv[0], '/');
     96 	cp = (cp == NULL) ? argv[0] : cp + 1;
     97 	if (strcmp(cp, "pftp") == 0)
     98 		passivemode = 1;
     99 
    100 	fromatty = isatty(fileno(stdin));
    101 	if (fromatty)
    102 		verbose = 1;		/* verbose if from a tty */
    103 	if (isatty(fileno(stdout)))
    104 		progress = 1;		/* progress bar on if going to a tty */
    105 
    106 	while ((ch = getopt(argc, argv, "adginpP:tvV")) != EOF) {
    107 		switch (ch) {
    108 		case 'a':
    109 			anonftp = 1;
    110 			break;
    111 
    112 		case 'd':
    113 			options |= SO_DEBUG;
    114 			debug++;
    115 			break;
    116 
    117 		case 'g':
    118 			doglob = 0;
    119 			break;
    120 
    121 		case 'i':
    122 			interactive = 0;
    123 			break;
    124 
    125 		case 'n':
    126 			autologin = 0;
    127 			break;
    128 
    129 		case 'p':
    130 			passivemode = 1;
    131 			break;
    132 
    133 		case 'P':
    134 			port = atoi(optarg);
    135 			if (port <= 0)
    136 				warnx("bad port number: %s", optarg);
    137 			else
    138 				ftpport = htons(port);
    139 			break;
    140 
    141 		case 't':
    142 			trace = 1;
    143 			break;
    144 
    145 		case 'v':
    146 			verbose = 1;
    147 			break;
    148 
    149 		case 'V':
    150 			verbose = 0;
    151 			break;
    152 
    153 		default:
    154 			usage();
    155 		}
    156 	}
    157 	argc -= optind;
    158 	argv += optind;
    159 
    160 	cpend = 0;	/* no pending replies */
    161 	proxy = 0;	/* proxy not active */
    162 	crflag = 1;	/* strip c.r. on ascii gets */
    163 	sendport = -1;	/* not using ports */
    164 	/*
    165 	 * Set up the home directory in case we're globbing.
    166 	 */
    167 	cp = getlogin();
    168 	if (cp != NULL) {
    169 		pw = getpwnam(cp);
    170 	}
    171 	if (pw == NULL)
    172 		pw = getpwuid(getuid());
    173 	if (pw != NULL) {
    174 		home = homedir;
    175 		(void) strcpy(home, pw->pw_dir);
    176 	}
    177 
    178 #ifndef SMALLFTP
    179 	editing = 0;			/* command line editing off */
    180 	if (fromatty) {
    181 		editing = 1;		/* editing mode on if a tty */
    182 		el = el_init(__progname, stdin, stdout); /* init editline */
    183 
    184 		hist = history_init();		/* init the builtin history */
    185 		history(hist, H_EVENT, 100);	/* remember 100 events */
    186 		el_set(el, EL_HIST, history, hist);	/* use history */
    187 
    188 		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
    189 		el_set(el, EL_PROMPT, prompt);	/* set the prompt function */
    190 
    191 		/* add local file completion, bind to TAB */
    192 		el_set(el, EL_ADDFN, "ftp-complete",
    193 		    "Context sensitive argument completion",
    194 		    complete);
    195 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
    196 
    197 		el_source(el, NULL);	/* read ~/.editrc */
    198 	}
    199 #endif /* !SMALLFTP */
    200 
    201 	setttywidth(0);
    202 	(void) signal(SIGWINCH, setttywidth);
    203 
    204 	if (argc > 0) {
    205 		if (strchr(argv[0], ':') != NULL) {
    206 			anonftp = 1;	/* Handle "automatic" transfers. */
    207 			rval = auto_fetch(argc, argv);
    208 			if (rval >= 0)		/* -1 == connected and cd-ed */
    209 				exit(rval);
    210 		} else {
    211 			char *xargv[5];
    212 
    213 			if (setjmp(toplevel))
    214 				exit(0);
    215 			(void) signal(SIGINT, intr);
    216 			(void) signal(SIGPIPE, lostpeer);
    217 			xargv[0] = __progname;
    218 			xargv[1] = argv[0];
    219 			xargv[2] = argv[1];
    220 			xargv[3] = argv[2];
    221 			xargv[4] = NULL;
    222 			setpeer(argc+1, xargv);
    223 		}
    224 	}
    225 	top = setjmp(toplevel) == 0;
    226 	if (top) {
    227 		(void) signal(SIGINT, intr);
    228 		(void) signal(SIGPIPE, lostpeer);
    229 	}
    230 	for (;;) {
    231 		cmdscanner(top);
    232 		top = 1;
    233 	}
    234 }
    235 
    236 void
    237 intr()
    238 {
    239 
    240 	alarmtimer(0);
    241 	longjmp(toplevel, 1);
    242 }
    243 
    244 void
    245 lostpeer()
    246 {
    247 
    248 	alarmtimer(0);
    249 	if (connected) {
    250 		if (cout != NULL) {
    251 			(void) shutdown(fileno(cout), 1+1);
    252 			(void) fclose(cout);
    253 			cout = NULL;
    254 		}
    255 		if (data >= 0) {
    256 			(void) shutdown(data, 1+1);
    257 			(void) close(data);
    258 			data = -1;
    259 		}
    260 		connected = 0;
    261 	}
    262 	pswitch(1);
    263 	if (connected) {
    264 		if (cout != NULL) {
    265 			(void) shutdown(fileno(cout), 1+1);
    266 			(void) fclose(cout);
    267 			cout = NULL;
    268 		}
    269 		connected = 0;
    270 	}
    271 	proxflag = 0;
    272 	pswitch(0);
    273 }
    274 
    275 /*
    276  * Generate a prompt
    277  */
    278 char *
    279 prompt()
    280 {
    281 	return ("ftp> ");
    282 }
    283 
    284 /*
    285  * Command parser.
    286  */
    287 void
    288 cmdscanner(top)
    289 	int top;
    290 {
    291 	struct cmd *c;
    292 	int num;
    293 
    294 	if (!top
    295 #ifndef SMALLFTP
    296 	    && !editing
    297 #endif /* !SMALLFTP */
    298 	    )
    299 		(void) putchar('\n');
    300 	for (;;) {
    301 #ifndef SMALLFTP
    302 		if (!editing) {
    303 #endif /* !SMALLFTP */
    304 			if (fromatty) {
    305 				printf("%s", prompt());
    306 				(void) fflush(stdout);
    307 			}
    308 			if (fgets(line, sizeof(line), stdin) == NULL)
    309 				quit(0, 0);
    310 			num = strlen(line);
    311 			if (num == 0)
    312 				break;
    313 			if (line[--num] == '\n') {
    314 				if (num == 0)
    315 					break;
    316 				line[num] = '\0';
    317 			} else if (num == sizeof(line) - 2) {
    318 				printf("sorry, input line too long\n");
    319 				while ((num = getchar()) != '\n' && num != EOF)
    320 					/* void */;
    321 				break;
    322 			} /* else it was a line without a newline */
    323 #ifndef SMALLFTP
    324 		} else {
    325 			const char *buf;
    326 			cursor_pos = NULL;
    327 
    328 			if ((buf = el_gets(el, &num)) == NULL || num == 0)
    329 				quit(0, 0);
    330 			if (line[--num] == '\n') {
    331 				if (num == 0)
    332 					break;
    333 			} else if (num >= sizeof(line)) {
    334 				printf("sorry, input line too long\n");
    335 				break;
    336 			}
    337 			memcpy(line, buf, num);
    338 			line[num] = '\0';
    339 			history(hist, H_ENTER, buf);
    340 		}
    341 #endif /* !SMALLFTP */
    342 
    343 		makeargv();
    344 		if (margc == 0)
    345 			continue;
    346 #if 0 && !defined(SMALLFTP)	/* XXX: don't want el_parse */
    347 		/*
    348 		 * el_parse returns -1 to signal that it's not been handled
    349 		 * internally.
    350 		 */
    351 		if (el_parse(el, margc, margv) != -1)
    352 			continue;
    353 #endif /* !SMALLFTP */
    354 		c = getcmd(margv[0]);
    355 		if (c == (struct cmd *)-1) {
    356 			printf("?Ambiguous command\n");
    357 			continue;
    358 		}
    359 		if (c == 0) {
    360 			printf("?Invalid command\n");
    361 			continue;
    362 		}
    363 		if (c->c_conn && !connected) {
    364 			printf("Not connected.\n");
    365 			continue;
    366 		}
    367 		confirmrest = 0;
    368 		(*c->c_handler)(margc, margv);
    369 		if (bell && c->c_bell)
    370 			(void) putchar('\007');
    371 		if (c->c_handler != help)
    372 			break;
    373 	}
    374 	(void) signal(SIGINT, intr);
    375 	(void) signal(SIGPIPE, lostpeer);
    376 }
    377 
    378 struct cmd *
    379 getcmd(name)
    380 	const char *name;
    381 {
    382 	const char *p, *q;
    383 	struct cmd *c, *found;
    384 	int nmatches, longest;
    385 
    386 	if (name == NULL)
    387 		return (0);
    388 
    389 	longest = 0;
    390 	nmatches = 0;
    391 	found = 0;
    392 	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
    393 		for (q = name; *q == *p++; q++)
    394 			if (*q == 0)		/* exact match? */
    395 				return (c);
    396 		if (!*q) {			/* the name was a prefix */
    397 			if (q - name > longest) {
    398 				longest = q - name;
    399 				nmatches = 1;
    400 				found = c;
    401 			} else if (q - name == longest)
    402 				nmatches++;
    403 		}
    404 	}
    405 	if (nmatches > 1)
    406 		return ((struct cmd *)-1);
    407 	return (found);
    408 }
    409 
    410 /*
    411  * Slice a string up into argc/argv.
    412  */
    413 
    414 int slrflag;
    415 
    416 void
    417 makeargv()
    418 {
    419 	char *argp;
    420 
    421 	stringbase = line;		/* scan from first of buffer */
    422 	argbase = argbuf;		/* store from first of buffer */
    423 	slrflag = 0;
    424 	marg_sl->sl_cur = 0;		/* reset to start of marg_sl */
    425 	for (margc = 0; ; margc++) {
    426 		argp = slurpstring();
    427 		sl_add(marg_sl, argp);
    428 		if (argp == NULL)
    429 			break;
    430 	}
    431 #ifndef SMALLFTP
    432 	if (cursor_pos == line) {
    433 		cursor_argc = 0;
    434 		cursor_argo = 0;
    435 	} else if (cursor_pos != NULL) {
    436 		cursor_argc = margc;
    437 		cursor_argo = strlen(margv[margc-1]);
    438 	}
    439 #endif /* !SMALLFTP */
    440 }
    441 
    442 #ifdef SMALLFTP
    443 #define INC_CHKCURSOR(x)	(x)++
    444 #else  /* !SMALLFTP */
    445 #define INC_CHKCURSOR(x)	{ (x)++ ; \
    446 				if (x == cursor_pos) { \
    447 					cursor_argc = margc; \
    448 					cursor_argo = ap-argbase; \
    449 					cursor_pos = NULL; \
    450 				} }
    451 
    452 #endif /* !SMALLFTP */
    453 
    454 /*
    455  * Parse string into argbuf;
    456  * implemented with FSM to
    457  * handle quoting and strings
    458  */
    459 char *
    460 slurpstring()
    461 {
    462 	int got_one = 0;
    463 	char *sb = stringbase;
    464 	char *ap = argbase;
    465 	char *tmp = argbase;		/* will return this if token found */
    466 
    467 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
    468 		switch (slrflag) {	/* and $ as token for macro invoke */
    469 			case 0:
    470 				slrflag++;
    471 				INC_CHKCURSOR(stringbase);
    472 				return ((*sb == '!') ? "!" : "$");
    473 				/* NOTREACHED */
    474 			case 1:
    475 				slrflag++;
    476 				altarg = stringbase;
    477 				break;
    478 			default:
    479 				break;
    480 		}
    481 	}
    482 
    483 S0:
    484 	switch (*sb) {
    485 
    486 	case '\0':
    487 		goto OUT;
    488 
    489 	case ' ':
    490 	case '\t':
    491 		INC_CHKCURSOR(sb);
    492 		goto S0;
    493 
    494 	default:
    495 		switch (slrflag) {
    496 			case 0:
    497 				slrflag++;
    498 				break;
    499 			case 1:
    500 				slrflag++;
    501 				altarg = sb;
    502 				break;
    503 			default:
    504 				break;
    505 		}
    506 		goto S1;
    507 	}
    508 
    509 S1:
    510 	switch (*sb) {
    511 
    512 	case ' ':
    513 	case '\t':
    514 	case '\0':
    515 		goto OUT;	/* end of token */
    516 
    517 	case '\\':
    518 		INC_CHKCURSOR(sb);
    519 		goto S2;	/* slurp next character */
    520 
    521 	case '"':
    522 		INC_CHKCURSOR(sb);
    523 		goto S3;	/* slurp quoted string */
    524 
    525 	default:
    526 		*ap = *sb;	/* add character to token */
    527 		ap++;
    528 		INC_CHKCURSOR(sb);
    529 		got_one = 1;
    530 		goto S1;
    531 	}
    532 
    533 S2:
    534 	switch (*sb) {
    535 
    536 	case '\0':
    537 		goto OUT;
    538 
    539 	default:
    540 		*ap = *sb;
    541 		ap++;
    542 		INC_CHKCURSOR(sb);
    543 		got_one = 1;
    544 		goto S1;
    545 	}
    546 
    547 S3:
    548 	switch (*sb) {
    549 
    550 	case '\0':
    551 		goto OUT;
    552 
    553 	case '"':
    554 		INC_CHKCURSOR(sb);
    555 		goto S1;
    556 
    557 	default:
    558 		*ap = *sb;
    559 		ap++;
    560 		INC_CHKCURSOR(sb);
    561 		got_one = 1;
    562 		goto S3;
    563 	}
    564 
    565 OUT:
    566 	if (got_one)
    567 		*ap++ = '\0';
    568 	argbase = ap;			/* update storage pointer */
    569 	stringbase = sb;		/* update scan pointer */
    570 	if (got_one) {
    571 		return (tmp);
    572 	}
    573 	switch (slrflag) {
    574 		case 0:
    575 			slrflag++;
    576 			break;
    577 		case 1:
    578 			slrflag++;
    579 			altarg = (char *) 0;
    580 			break;
    581 		default:
    582 			break;
    583 	}
    584 	return ((char *)0);
    585 }
    586 
    587 /*
    588  * Help command.
    589  * Call each command handler with argc == 0 and argv[0] == name.
    590  */
    591 void
    592 help(argc, argv)
    593 	int argc;
    594 	char *argv[];
    595 {
    596 	struct cmd *c;
    597 
    598 	if (argc == 1) {
    599 		StringList *buf;
    600 
    601 		buf = sl_init();
    602 		printf("%sommands may be abbreviated.  Commands are:\n\n",
    603 		    proxy ? "Proxy c" : "C");
    604 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
    605 			if (c->c_name && (!proxy || c->c_proxy))
    606 				sl_add(buf, c->c_name);
    607 		list_vertical(buf);
    608 		sl_free(buf, 0);
    609 		return;
    610 	}
    611 
    612 #define HELPINDENT ((int) sizeof ("disconnect"))
    613 
    614 	while (--argc > 0) {
    615 		char *arg;
    616 
    617 		arg = *++argv;
    618 		c = getcmd(arg);
    619 		if (c == (struct cmd *)-1)
    620 			printf("?Ambiguous help command %s\n", arg);
    621 		else if (c == (struct cmd *)0)
    622 			printf("?Invalid help command %s\n", arg);
    623 		else
    624 			printf("%-*s\t%s\n", HELPINDENT,
    625 				c->c_name, c->c_help);
    626 	}
    627 }
    628 
    629 void
    630 usage()
    631 {
    632 	(void)fprintf(stderr,
    633 	    "usage: %s [-adginptvV] [host [port]]\n"
    634 	    "       %s host:path[/]\n"
    635 	    "       %s ftp://host[:port]/path[/]\n"
    636 	    "       %s http://host[:port]/file\n",
    637 	    __progname, __progname, __progname, __progname);
    638 	exit(1);
    639 }
    640