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