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