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