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