xref: /original-bsd/usr.bin/ftp/main.c (revision c43e4352)
1 #ifndef lint
2 static char sccsid[] = "@(#)main.c	4.9 (Berkeley) 07/18/83";
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 				clearerr(stdin);
184 				putchar('\n');
185 			}
186 			break;
187 		}
188 		if (line[0] == 0)
189 			break;
190 		makeargv();
191 		c = getcmd(margv[0]);
192 		if (c == (struct cmd *)-1) {
193 			printf("?Ambiguous command\n");
194 			continue;
195 		}
196 		if (c == 0) {
197 			printf("?Invalid command\n");
198 			continue;
199 		}
200 		if (c->c_conn && !connected) {
201 			printf ("Not connected.\n");
202 			continue;
203 		}
204 		(*c->c_handler)(margc, margv);
205 		if (bell && c->c_bell)
206 			putchar(CTRL(g));
207 		if (c->c_handler != help)
208 			break;
209 	}
210 	longjmp(toplevel, 0);
211 }
212 
213 struct cmd *
214 getcmd(name)
215 	register char *name;
216 {
217 	register char *p, *q;
218 	register struct cmd *c, *found;
219 	register int nmatches, longest;
220 
221 	longest = 0;
222 	nmatches = 0;
223 	found = 0;
224 	for (c = cmdtab; p = c->c_name; c++) {
225 		for (q = name; *q == *p++; q++)
226 			if (*q == 0)		/* exact match? */
227 				return (c);
228 		if (!*q) {			/* the name was a prefix */
229 			if (q - name > longest) {
230 				longest = q - name;
231 				nmatches = 1;
232 				found = c;
233 			} else if (q - name == longest)
234 				nmatches++;
235 		}
236 	}
237 	if (nmatches > 1)
238 		return ((struct cmd *)-1);
239 	return (found);
240 }
241 
242 /*
243  * Slice a string up into argc/argv.
244  */
245 makeargv()
246 {
247 	char **argp;
248 	char *slurpstring();
249 
250 	margc = 0;
251 	argp = margv;
252 	stringbase = line;		/* scan from first of buffer */
253 	argbase = argbuf;		/* store from first of buffer */
254 	while (*argp++ = slurpstring())
255 		margc++;
256 }
257 
258 /*
259  * Parse string into argbuf;
260  * implemented with FSM to
261  * handle quoting and strings
262  */
263 char *
264 slurpstring()
265 {
266 	int got_one = 0;
267 	register char *sb = stringbase;
268 	register char *ap = argbase;
269 	char *tmp = argbase;		/* will return this if token found */
270 
271 	if (*sb == '!') {		/* recognize ! as a token for shell */
272 		stringbase++;
273 		return ("!");
274 	}
275 S0:
276 	switch (*sb) {
277 
278 	case '\0':
279 		goto OUT;
280 
281 	case ' ':
282 	case '\t':
283 		sb++; goto S0;
284 
285 	default:
286 		goto S1;
287 	}
288 
289 S1:
290 	switch (*sb) {
291 
292 	case ' ':
293 	case '\t':
294 	case '\0':
295 		goto OUT;	/* end of token */
296 
297 	case '\\':
298 		sb++; goto S2;	/* slurp next character */
299 
300 	case '"':
301 		sb++; goto S3;	/* slurp quoted string */
302 
303 	default:
304 		*ap++ = *sb++;	/* add character to token */
305 		got_one = 1;
306 		goto S1;
307 	}
308 
309 S2:
310 	switch (*sb) {
311 
312 	case '\0':
313 		goto OUT;
314 
315 	default:
316 		*ap++ = *sb++;
317 		got_one = 1;
318 		goto S1;
319 	}
320 
321 S3:
322 	switch (*sb) {
323 
324 	case '\0':
325 		goto OUT;
326 
327 	case '"':
328 		sb++; goto S1;
329 
330 	default:
331 		*ap++ = *sb++;
332 		got_one = 1;
333 		goto S3;
334 	}
335 
336 OUT:
337 	if (got_one)
338 		*ap++ = '\0';
339 	argbase = ap;			/* update storage pointer */
340 	stringbase = sb;		/* update scan pointer */
341 	if (got_one)
342 		return(tmp);
343 	return((char *)0);
344 }
345 
346 #define HELPINDENT (sizeof ("directory"))
347 
348 /*
349  * Help command.
350  * Call each command handler with argc == 0 and argv[0] == name.
351  */
352 help(argc, argv)
353 	int argc;
354 	char *argv[];
355 {
356 	register struct cmd *c;
357 
358 	if (argc == 1) {
359 		register int i, j, w;
360 		int columns, width = 0, lines;
361 		extern int NCMDS;
362 
363 		printf("Commands may be abbreviated.  Commands are:\n\n");
364 		for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
365 			int len = strlen(c->c_name);
366 
367 			if (len > width)
368 				width = len;
369 		}
370 		width = (width + 8) &~ 7;
371 		columns = 80 / width;
372 		if (columns == 0)
373 			columns = 1;
374 		lines = (NCMDS + columns - 1) / columns;
375 		for (i = 0; i < lines; i++) {
376 			for (j = 0; j < columns; j++) {
377 				c = cmdtab + j * lines + i;
378 				printf("%s", c->c_name);
379 				if (c + lines >= &cmdtab[NCMDS]) {
380 					printf("\n");
381 					break;
382 				}
383 				w = strlen(c->c_name);
384 				while (w < width) {
385 					w = (w + 8) &~ 7;
386 					putchar('\t');
387 				}
388 			}
389 		}
390 		return;
391 	}
392 	while (--argc > 0) {
393 		register char *arg;
394 		arg = *++argv;
395 		c = getcmd(arg);
396 		if (c == (struct cmd *)-1)
397 			printf("?Ambiguous help command %s\n", arg);
398 		else if (c == (struct cmd *)0)
399 			printf("?Invalid help command %s\n", arg);
400 		else
401 			printf("%-*s\t%s\n", HELPINDENT,
402 				c->c_name, c->c_help);
403 	}
404 }
405 
406 /*
407  * Call routine with argc, argv set from args (terminated by 0).
408  */
409 /* VARARGS2 */
410 call(routine, args)
411 	int (*routine)();
412 	int args;
413 {
414 	register int *argp;
415 	register int argc;
416 
417 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
418 		;
419 	(*routine)(argc, &args);
420 }
421