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