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