xref: /csrg-svn/usr.bin/ftp/main.c (revision 26082)
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.5 (Berkeley) 02/05/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 				quit();
212 			break;
213 		}
214 		if (line[0] == 0)
215 			break;
216 		makeargv();
217 		if (margc == 0) {
218 			continue;
219 		}
220 		c = getcmd(margv[0]);
221 		if (c == (struct cmd *)-1) {
222 			printf("?Ambiguous command\n");
223 			continue;
224 		}
225 		if (c == 0) {
226 			printf("?Invalid command\n");
227 			continue;
228 		}
229 		if (c->c_conn && !connected) {
230 			printf ("Not connected.\n");
231 			continue;
232 		}
233 		(*c->c_handler)(margc, margv);
234 		if (bell && c->c_bell)
235 			putchar(CTRL(g));
236 		if (c->c_handler != help)
237 			break;
238 	}
239 	signal(SIGINT, intr);
240 	signal(SIGPIPE, lostpeer);
241 }
242 
243 struct cmd *
244 getcmd(name)
245 	register char *name;
246 {
247 	register char *p, *q;
248 	register struct cmd *c, *found;
249 	register int nmatches, longest;
250 
251 	longest = 0;
252 	nmatches = 0;
253 	found = 0;
254 	for (c = cmdtab; p = c->c_name; c++) {
255 		for (q = name; *q == *p++; q++)
256 			if (*q == 0)		/* exact match? */
257 				return (c);
258 		if (!*q) {			/* the name was a prefix */
259 			if (q - name > longest) {
260 				longest = q - name;
261 				nmatches = 1;
262 				found = c;
263 			} else if (q - name == longest)
264 				nmatches++;
265 		}
266 	}
267 	if (nmatches > 1)
268 		return ((struct cmd *)-1);
269 	return (found);
270 }
271 
272 /*
273  * Slice a string up into argc/argv.
274  */
275 
276 int slrflag;
277 
278 makeargv()
279 {
280 	char **argp;
281 	char *slurpstring();
282 
283 	margc = 0;
284 	argp = margv;
285 	stringbase = line;		/* scan from first of buffer */
286 	argbase = argbuf;		/* store from first of buffer */
287 	slrflag = 0;
288 	while (*argp++ = slurpstring())
289 		margc++;
290 }
291 
292 /*
293  * Parse string into argbuf;
294  * implemented with FSM to
295  * handle quoting and strings
296  */
297 char *
298 slurpstring()
299 {
300 	int got_one = 0;
301 	register char *sb = stringbase;
302 	register char *ap = argbase;
303 	char *tmp = argbase;		/* will return this if token found */
304 
305 	if (*sb == '!' || *sb == '$') {	/* recognize ! as a token for shell */
306 		switch (slrflag) {	/* and $ as token for macro invoke */
307 			case 0:
308 				slrflag++;
309 				stringbase++;
310 				return ((*sb == '!') ? "!" : "$");
311 				break;
312 			case 1:
313 				slrflag++;
314 				altarg = stringbase;
315 				break;
316 			default:
317 				break;
318 		}
319 	}
320 
321 S0:
322 	switch (*sb) {
323 
324 	case '\0':
325 		goto OUT;
326 
327 	case ' ':
328 	case '\t':
329 		sb++; goto S0;
330 
331 	default:
332 		switch (slrflag) {
333 			case 0:
334 				slrflag++;
335 				break;
336 			case 1:
337 				slrflag++;
338 				altarg = sb;
339 				break;
340 			default:
341 				break;
342 		}
343 		goto S1;
344 	}
345 
346 S1:
347 	switch (*sb) {
348 
349 	case ' ':
350 	case '\t':
351 	case '\0':
352 		goto OUT;	/* end of token */
353 
354 	case '\\':
355 		sb++; goto S2;	/* slurp next character */
356 
357 	case '"':
358 		sb++; goto S3;	/* slurp quoted string */
359 
360 	default:
361 		*ap++ = *sb++;	/* add character to token */
362 		got_one = 1;
363 		goto S1;
364 	}
365 
366 S2:
367 	switch (*sb) {
368 
369 	case '\0':
370 		goto OUT;
371 
372 	default:
373 		*ap++ = *sb++;
374 		got_one = 1;
375 		goto S1;
376 	}
377 
378 S3:
379 	switch (*sb) {
380 
381 	case '\0':
382 		goto OUT;
383 
384 	case '"':
385 		sb++; goto S1;
386 
387 	default:
388 		*ap++ = *sb++;
389 		got_one = 1;
390 		goto S3;
391 	}
392 
393 OUT:
394 	if (got_one)
395 		*ap++ = '\0';
396 	argbase = ap;			/* update storage pointer */
397 	stringbase = sb;		/* update scan pointer */
398 	if (got_one) {
399 		return(tmp);
400 	}
401 	switch (slrflag) {
402 		case 0:
403 			slrflag++;
404 			break;
405 		case 1:
406 			slrflag++;
407 			altarg = (char *) 0;
408 			break;
409 		default:
410 			break;
411 	}
412 	return((char *)0);
413 }
414 
415 #define HELPINDENT (sizeof ("directory"))
416 
417 /*
418  * Help command.
419  * Call each command handler with argc == 0 and argv[0] == name.
420  */
421 help(argc, argv)
422 	int argc;
423 	char *argv[];
424 {
425 	register struct cmd *c;
426 
427 	if (argc == 1) {
428 		register int i, j, w, k;
429 		int columns, width = 0, lines;
430 		extern int NCMDS;
431 
432 		printf("Commands may be abbreviated.  Commands are:\n\n");
433 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
434 			int len = strlen(c->c_name);
435 
436 			if (len > width)
437 				width = len;
438 		}
439 		width = (width + 8) &~ 7;
440 		columns = 80 / width;
441 		if (columns == 0)
442 			columns = 1;
443 		lines = (NCMDS + columns - 1) / columns;
444 		for (i = 0; i < lines; i++) {
445 			for (j = 0; j < columns; j++) {
446 				c = cmdtab + j * lines + i;
447 				if (c->c_name && (!proxy || c->c_proxy)) {
448 					printf("%s", c->c_name);
449 				}
450 				else if (c->c_name) {
451 					for (k=0; k < strlen(c->c_name); k++) {
452 						putchar(' ');
453 					}
454 				}
455 				if (c + lines >= &cmdtab[NCMDS]) {
456 					printf("\n");
457 					break;
458 				}
459 				w = strlen(c->c_name);
460 				while (w < width) {
461 					w = (w + 8) &~ 7;
462 					putchar('\t');
463 				}
464 			}
465 		}
466 		return;
467 	}
468 	while (--argc > 0) {
469 		register char *arg;
470 		arg = *++argv;
471 		c = getcmd(arg);
472 		if (c == (struct cmd *)-1)
473 			printf("?Ambiguous help command %s\n", arg);
474 		else if (c == (struct cmd *)0)
475 			printf("?Invalid help command %s\n", arg);
476 		else
477 			printf("%-*s\t%s\n", HELPINDENT,
478 				c->c_name, c->c_help);
479 	}
480 }
481 
482 /*
483  * Call routine with argc, argv set from args (terminated by 0).
484  */
485 /* VARARGS2 */
486 call(routine, args)
487 	int (*routine)();
488 	int args;
489 {
490 	register int *argp;
491 	register int argc;
492 
493 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
494 		;
495 	(*routine)(argc, &args);
496 }
497