xref: /csrg-svn/usr.bin/ftp/main.c (revision 11239)
1 #ifndef lint
2 static char sccsid[] = "@(#)main.c	4.3 (Berkeley) 02/23/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 
108 	if (connected) {
109 		if (cout != NULL) {
110 			shutdown(fileno(cout), 1+1);
111 			fclose(cout);
112 			cout = NULL;
113 		}
114 		if (data >= 0) {
115 			shutdown(data, 1+1);
116 			(void) close(data);
117 			data = -1;
118 		}
119 		connected = 0;
120 	}
121 	longjmp(toplevel, 1);
122 }
123 
124 char *
125 tail(filename)
126 	char *filename;
127 {
128 	register char *s;
129 
130 	while (*filename) {
131 		s = rindex(filename, '/');
132 		if (s == NULL)
133 			break;
134 		if (s[1])
135 			return (s + 1);
136 		*s = '\0';
137 	}
138 	return (filename);
139 }
140 
141 /*
142  * Command parser.
143  */
144 cmdscanner(top)
145 	int top;
146 {
147 	register struct cmd *c;
148 	struct cmd *getcmd();
149 	extern struct cmd cmdtab[];
150 	extern int help();
151 
152 	if (!top)
153 		putchar('\n');
154 	for (;;) {
155 		if (fromatty) {
156 			printf("ftp> ");
157 			fflush(stdout);
158 		}
159 		if (gets(line) == 0)
160 			break;
161 		if (line[0] == 0)
162 			break;
163 		makeargv();
164 		c = getcmd(margv[0]);
165 		if (c == (struct cmd *)-1) {
166 			printf("?Ambiguous command\n");
167 			continue;
168 		}
169 		if (c == 0) {
170 			printf("?Invalid command\n");
171 			continue;
172 		}
173 		(*c->c_handler)(margc, margv);
174 		if (bell && c->c_bell)
175 			putchar(CTRL(g));
176 		if (c->c_handler != help)
177 			break;
178 	}
179 	longjmp(toplevel, 0);
180 }
181 
182 struct cmd *
183 getcmd(name)
184 	register char *name;
185 {
186 	register char *p, *q;
187 	register struct cmd *c, *found;
188 	register int nmatches, longest;
189 
190 	longest = 0;
191 	nmatches = 0;
192 	found = 0;
193 	for (c = cmdtab; p = c->c_name; c++) {
194 		for (q = name; *q == *p++; q++)
195 			if (*q == 0)		/* exact match? */
196 				return (c);
197 		if (!*q) {			/* the name was a prefix */
198 			if (q - name > longest) {
199 				longest = q - name;
200 				nmatches = 1;
201 				found = c;
202 			} else if (q - name == longest)
203 				nmatches++;
204 		}
205 	}
206 	if (nmatches > 1)
207 		return ((struct cmd *)-1);
208 	return (found);
209 }
210 
211 /*
212  * Slice a string up into argc/argv.
213  */
214 makeargv()
215 {
216 	char **argp;
217 	char *slurpstring();
218 
219 	margc = 0;
220 	argp = margv;
221 	stringbase = line;		/* scan from first of buffer */
222 	argbase = argbuf;		/* store from first of buffer */
223 	while (*argp++ = slurpstring())
224 		margc++;
225 }
226 
227 /*
228  * Parse string into argbuf;
229  * implemented with FSM to
230  * handle quoting and strings
231  */
232 char *
233 slurpstring()
234 {
235 	int got_one = 0;
236 	register char *sb = stringbase;
237 	register char *ap = argbase;
238 	char *tmp = argbase;		/* will return this if token found */
239 
240 S0:
241 	switch (*sb) {
242 
243 	case '\0':
244 		goto OUT;
245 
246 	case ' ':
247 	case '\t':
248 		sb++; goto S0;
249 
250 	default:
251 		goto S1;
252 	}
253 
254 S1:
255 	switch (*sb) {
256 
257 	case ' ':
258 	case '\t':
259 	case '\0':
260 		goto OUT;	/* end of token */
261 
262 	case '\\':
263 		sb++; goto S2;	/* slurp next character */
264 
265 	case '"':
266 		sb++; goto S3;	/* slurp quoted string */
267 
268 	default:
269 		*ap++ = *sb++;	/* add character to token */
270 		got_one = 1;
271 		goto S1;
272 	}
273 
274 S2:
275 	switch (*sb) {
276 
277 	case '\0':
278 		goto OUT;
279 
280 	default:
281 		*ap++ = *sb++;
282 		got_one = 1;
283 		goto S1;
284 	}
285 
286 S3:
287 	switch (*sb) {
288 
289 	case '\0':
290 		goto OUT;
291 
292 	case '"':
293 		sb++; goto S1;
294 
295 	default:
296 		*ap++ = *sb++;
297 		got_one = 1;
298 		goto S3;
299 	}
300 
301 OUT:
302 	if (got_one)
303 		*ap++ = '\0';
304 	argbase = ap;			/* update storage pointer */
305 	stringbase = sb;		/* update scan pointer */
306 	if (got_one)
307 		return(tmp);
308 	return((char *)0);
309 }
310 
311 #define HELPINDENT (sizeof ("directory"))
312 
313 /*
314  * Help command.
315  * Call each command handler with argc == 0 and argv[0] == name.
316  */
317 help(argc, argv)
318 	int argc;
319 	char *argv[];
320 {
321 	register struct cmd *c;
322 
323 	if (argc == 1) {
324 		register int i, j, w;
325 		int columns, width = 0, lines;
326 		extern int NCMDS;
327 
328 		printf("Commands may be abbreviated.  Commands are:\n\n");
329 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
330 			int len = strlen(c->c_name);
331 
332 			if (len > width)
333 				width = len;
334 		}
335 		width = (width + 8) &~ 7;
336 		columns = 80 / width;
337 		if (columns == 0)
338 			columns = 1;
339 		lines = (NCMDS + columns - 1) / columns;
340 		for (i = 0; i < lines; i++) {
341 			for (j = 0; j < columns; j++) {
342 				c = cmdtab + j * lines + i;
343 				printf("%s", c->c_name);
344 				if (c + lines >= &cmdtab[NCMDS]) {
345 					printf("\n");
346 					break;
347 				}
348 				w = strlen(c->c_name);
349 				while (w < width) {
350 					w = (w + 8) &~ 7;
351 					putchar('\t');
352 				}
353 			}
354 		}
355 		return;
356 	}
357 	while (--argc > 0) {
358 		register char *arg;
359 		arg = *++argv;
360 		c = getcmd(arg);
361 		if (c == (struct cmd *)-1)
362 			printf("?Ambiguous help command %s\n", arg);
363 		else if (c == (struct cmd *)0)
364 			printf("?Invalid help command %s\n", arg);
365 		else
366 			printf("%-*s\t%s\n", HELPINDENT,
367 				c->c_name, c->c_help);
368 	}
369 }
370 
371 /*
372  * Call routine with argc, argv set from args (terminated by 0).
373  */
374 /* VARARGS2 */
375 call(routine, args)
376 	int (*routine)();
377 	int args;
378 {
379 	register int *argp;
380 	register int argc;
381 
382 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
383 		;
384 	(*routine)(argc, &args);
385 }
386