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