xref: /csrg-svn/usr.bin/ftp/main.c (revision 66670)
1 /*
2  * Copyright (c) 1985, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1985, 1989, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 04/02/94";
16 #endif /* not lint */
17 
18 /*
19  * FTP User Program -- Command Interface.
20  */
21 /*#include <sys/ioctl.h>*/
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 
25 #include <arpa/ftp.h>
26 
27 #include <ctype.h>
28 #include <err.h>
29 #include <netdb.h>
30 #include <pwd.h>
31 #include <signal.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 
36 #include "ftp_var.h"
37 
38 int
39 main(argc, argv)
40 	int argc;
41 	char *argv[];
42 {
43 	int ch, top;
44 	struct passwd *pw = NULL;
45 	char *cp, homedir[MAXPATHLEN];
46 
47 	sp = getservbyname("ftp", "tcp");
48 	if (sp == 0)
49 		errx(1, "ftp/tcp: unknown service");
50 	doglob = 1;
51 	interactive = 1;
52 	autologin = 1;
53 
54 	while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
55 		switch (*cp) {
56 		case 'd':
57 			options |= SO_DEBUG;
58 			debug++;
59 			break;
60 
61 		case 'g':
62 			doglob = 0;
63 			break;
64 
65 		case 'i':
66 			interactive = 0;
67 			break;
68 
69 		case 'n':
70 			autologin = 0;
71 			break;
72 
73 		case 't':
74 			trace++;
75 			break;
76 
77 		case 'v':
78 			verbose++;
79 			break;
80 
81 		default:
82 			(void)fprintf(stderr,
83 				"usage: ftp [-dgintv] [host [port]]\n");
84 			exit(1);
85 		}
86 	}
87 	argc -= optind;
88 	argv += optind;
89 
90 	fromatty = isatty(fileno(stdin));
91 	if (fromatty)
92 		verbose++;
93 	cpend = 0;	/* no pending replies */
94 	proxy = 0;	/* proxy not active */
95 	crflag = 1;	/* strip c.r. on ascii gets */
96 	sendport = -1;	/* not using ports */
97 	/*
98 	 * Set up the home directory in case we're globbing.
99 	 */
100 	cp = getlogin();
101 	if (cp != NULL) {
102 		pw = getpwnam(cp);
103 	}
104 	if (pw == NULL)
105 		pw = getpwuid(getuid());
106 	if (pw != NULL) {
107 		home = homedir;
108 		(void) strcpy(home, pw->pw_dir);
109 	}
110 	if (argc > 0) {
111 		char *xargv[3];
112 		extern char *__progname;
113 
114 		if (setjmp(toplevel))
115 			exit(0);
116 		(void) signal(SIGINT, intr);
117 		(void) signal(SIGPIPE, lostpeer);
118 		xargv[0] = __progname;
119 		xargv[1] = argv[0];
120 		xargv[2] = argv[1];
121 		xargv[3] = argv[2];
122 		setpeer(argc+1, xargv);
123 	}
124 	top = setjmp(toplevel) == 0;
125 	if (top) {
126 		(void) signal(SIGINT, intr);
127 		(void) signal(SIGPIPE, lostpeer);
128 	}
129 	for (;;) {
130 		cmdscanner(top);
131 		top = 1;
132 	}
133 }
134 
135 void
136 intr()
137 {
138 
139 	longjmp(toplevel, 1);
140 }
141 
142 void
143 lostpeer()
144 {
145 
146 	if (connected) {
147 		if (cout != NULL) {
148 			(void) shutdown(fileno(cout), 1+1);
149 			(void) fclose(cout);
150 			cout = NULL;
151 		}
152 		if (data >= 0) {
153 			(void) shutdown(data, 1+1);
154 			(void) close(data);
155 			data = -1;
156 		}
157 		connected = 0;
158 	}
159 	pswitch(1);
160 	if (connected) {
161 		if (cout != NULL) {
162 			(void) shutdown(fileno(cout), 1+1);
163 			(void) fclose(cout);
164 			cout = NULL;
165 		}
166 		connected = 0;
167 	}
168 	proxflag = 0;
169 	pswitch(0);
170 }
171 
172 /*
173 char *
174 tail(filename)
175 	char *filename;
176 {
177 	char *s;
178 
179 	while (*filename) {
180 		s = strrchr(filename, '/');
181 		if (s == NULL)
182 			break;
183 		if (s[1])
184 			return (s + 1);
185 		*s = '\0';
186 	}
187 	return (filename);
188 }
189 */
190 
191 /*
192  * Command parser.
193  */
194 void
195 cmdscanner(top)
196 	int top;
197 {
198 	struct cmd *c;
199 	int l;
200 
201 	if (!top)
202 		(void) putchar('\n');
203 	for (;;) {
204 		if (fromatty) {
205 			printf("ftp> ");
206 			(void) fflush(stdout);
207 		}
208 		if (fgets(line, sizeof line, stdin) == NULL)
209 			quit(0, 0);
210 		l = strlen(line);
211 		if (l == 0)
212 			break;
213 		if (line[--l] == '\n') {
214 			if (l == 0)
215 				break;
216 			line[l] = '\0';
217 		} else if (l == sizeof(line) - 2) {
218 			printf("sorry, input line too long\n");
219 			while ((l = getchar()) != '\n' && l != EOF)
220 				/* void */;
221 			break;
222 		} /* else it was a line without a newline */
223 		makeargv();
224 		if (margc == 0) {
225 			continue;
226 		}
227 		c = getcmd(margv[0]);
228 		if (c == (struct cmd *)-1) {
229 			printf("?Ambiguous command\n");
230 			continue;
231 		}
232 		if (c == 0) {
233 			printf("?Invalid command\n");
234 			continue;
235 		}
236 		if (c->c_conn && !connected) {
237 			printf("Not connected.\n");
238 			continue;
239 		}
240 		(*c->c_handler)(margc, margv);
241 		if (bell && c->c_bell)
242 			(void) putchar('\007');
243 		if (c->c_handler != help)
244 			break;
245 	}
246 	(void) signal(SIGINT, intr);
247 	(void) signal(SIGPIPE, lostpeer);
248 }
249 
250 struct cmd *
251 getcmd(name)
252 	char *name;
253 {
254 	char *p, *q;
255 	struct cmd *c, *found;
256 	int nmatches, longest;
257 
258 	longest = 0;
259 	nmatches = 0;
260 	found = 0;
261 	for (c = cmdtab; p = c->c_name; c++) {
262 		for (q = name; *q == *p++; q++)
263 			if (*q == 0)		/* exact match? */
264 				return (c);
265 		if (!*q) {			/* the name was a prefix */
266 			if (q - name > longest) {
267 				longest = q - name;
268 				nmatches = 1;
269 				found = c;
270 			} else if (q - name == longest)
271 				nmatches++;
272 		}
273 	}
274 	if (nmatches > 1)
275 		return ((struct cmd *)-1);
276 	return (found);
277 }
278 
279 /*
280  * Slice a string up into argc/argv.
281  */
282 
283 int slrflag;
284 
285 void
286 makeargv()
287 {
288 	char **argp;
289 
290 	margc = 0;
291 	argp = margv;
292 	stringbase = line;		/* scan from first of buffer */
293 	argbase = argbuf;		/* store from first of buffer */
294 	slrflag = 0;
295 	while (*argp++ = slurpstring())
296 		margc++;
297 }
298 
299 /*
300  * Parse string into argbuf;
301  * implemented with FSM to
302  * handle quoting and strings
303  */
304 char *
305 slurpstring()
306 {
307 	int got_one = 0;
308 	char *sb = stringbase;
309 	char *ap = argbase;
310 	char *tmp = argbase;		/* will return this if token found */
311 
312 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
313 		switch (slrflag) {	/* and $ as token for macro invoke */
314 			case 0:
315 				slrflag++;
316 				stringbase++;
317 				return ((*sb == '!') ? "!" : "$");
318 				/* NOTREACHED */
319 			case 1:
320 				slrflag++;
321 				altarg = stringbase;
322 				break;
323 			default:
324 				break;
325 		}
326 	}
327 
328 S0:
329 	switch (*sb) {
330 
331 	case '\0':
332 		goto OUT;
333 
334 	case ' ':
335 	case '\t':
336 		sb++; goto S0;
337 
338 	default:
339 		switch (slrflag) {
340 			case 0:
341 				slrflag++;
342 				break;
343 			case 1:
344 				slrflag++;
345 				altarg = sb;
346 				break;
347 			default:
348 				break;
349 		}
350 		goto S1;
351 	}
352 
353 S1:
354 	switch (*sb) {
355 
356 	case ' ':
357 	case '\t':
358 	case '\0':
359 		goto OUT;	/* end of token */
360 
361 	case '\\':
362 		sb++; goto S2;	/* slurp next character */
363 
364 	case '"':
365 		sb++; goto S3;	/* slurp quoted string */
366 
367 	default:
368 		*ap++ = *sb++;	/* add character to token */
369 		got_one = 1;
370 		goto S1;
371 	}
372 
373 S2:
374 	switch (*sb) {
375 
376 	case '\0':
377 		goto OUT;
378 
379 	default:
380 		*ap++ = *sb++;
381 		got_one = 1;
382 		goto S1;
383 	}
384 
385 S3:
386 	switch (*sb) {
387 
388 	case '\0':
389 		goto OUT;
390 
391 	case '"':
392 		sb++; goto S1;
393 
394 	default:
395 		*ap++ = *sb++;
396 		got_one = 1;
397 		goto S3;
398 	}
399 
400 OUT:
401 	if (got_one)
402 		*ap++ = '\0';
403 	argbase = ap;			/* update storage pointer */
404 	stringbase = sb;		/* update scan pointer */
405 	if (got_one) {
406 		return (tmp);
407 	}
408 	switch (slrflag) {
409 		case 0:
410 			slrflag++;
411 			break;
412 		case 1:
413 			slrflag++;
414 			altarg = (char *) 0;
415 			break;
416 		default:
417 			break;
418 	}
419 	return ((char *)0);
420 }
421 
422 #define HELPINDENT ((int) sizeof ("directory"))
423 
424 /*
425  * Help command.
426  * Call each command handler with argc == 0 and argv[0] == name.
427  */
428 void
429 help(argc, argv)
430 	int argc;
431 	char *argv[];
432 {
433 	struct cmd *c;
434 
435 	if (argc == 1) {
436 		int i, j, w, k;
437 		int columns, width = 0, lines;
438 
439 		printf("Commands may be abbreviated.  Commands are:\n\n");
440 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
441 			int len = strlen(c->c_name);
442 
443 			if (len > width)
444 				width = len;
445 		}
446 		width = (width + 8) &~ 7;
447 		columns = 80 / width;
448 		if (columns == 0)
449 			columns = 1;
450 		lines = (NCMDS + columns - 1) / columns;
451 		for (i = 0; i < lines; i++) {
452 			for (j = 0; j < columns; j++) {
453 				c = cmdtab + j * lines + i;
454 				if (c->c_name && (!proxy || c->c_proxy)) {
455 					printf("%s", c->c_name);
456 				}
457 				else if (c->c_name) {
458 					for (k=0; k < strlen(c->c_name); k++) {
459 						(void) putchar(' ');
460 					}
461 				}
462 				if (c + lines >= &cmdtab[NCMDS]) {
463 					printf("\n");
464 					break;
465 				}
466 				w = strlen(c->c_name);
467 				while (w < width) {
468 					w = (w + 8) &~ 7;
469 					(void) putchar('\t');
470 				}
471 			}
472 		}
473 		return;
474 	}
475 	while (--argc > 0) {
476 		char *arg;
477 		arg = *++argv;
478 		c = getcmd(arg);
479 		if (c == (struct cmd *)-1)
480 			printf("?Ambiguous help command %s\n", arg);
481 		else if (c == (struct cmd *)0)
482 			printf("?Invalid help command %s\n", arg);
483 		else
484 			printf("%-*s\t%s\n", HELPINDENT,
485 				c->c_name, c->c_help);
486 	}
487 }
488