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