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