handle EOF from tty properly
[unix-history] / usr / src / usr.bin / ftp / main.c
CommitLineData
ba5bf9f5 1#ifndef lint
5a94754b 2static char sccsid[] = "@(#)main.c 4.9 (Berkeley) %G%";
ba5bf9f5
SL
3#endif
4
5/*
6 * FTP User Program -- Command Interface.
7 */
3b645f65 8#include <sys/param.h>
ba5bf9f5
SL
9#include <sys/socket.h>
10#include <sys/ioctl.h>
11
62c4a390
SL
12#include <arpa/ftp.h>
13
ba5bf9f5
SL
14#include <signal.h>
15#include <stdio.h>
16#include <errno.h>
17#include <ctype.h>
18#include <netdb.h>
3b645f65 19#include <pwd.h>
ba5bf9f5 20
ba5bf9f5
SL
21#include "ftp_var.h"
22
23int intr();
24int lostpeer();
3b645f65 25extern char *home;
ba5bf9f5
SL
26
27main(argc, argv)
28 char *argv[];
29{
30 register char *cp;
31 int top;
3b645f65
SL
32 struct passwd *pw;
33 char homedir[MAXPATHLEN];
ba5bf9f5
SL
34
35 sp = getservbyname("ftp", "tcp");
36 if (sp == 0) {
37 fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
38 exit(1);
39 }
a16b8edf 40 doglob = 1;
5ac6fc46 41 interactive = 1;
a16b8edf 42 autologin = 1;
ba5bf9f5
SL
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':
5ac6fc46 62 interactive = 0;
ba5bf9f5
SL
63 break;
64
65 case 'n':
66 autologin = 0;
67 break;
68
a16b8edf
SL
69 case 'g':
70 doglob = 0;
71 break;
72
ba5bf9f5
SL
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;
00775b29 88 strcpy(bytename, "8"), bytesize = 8;
ba5bf9f5
SL
89 if (fromatty)
90 verbose++;
3b645f65
SL
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 }
ba5bf9f5
SL
101 if (argc > 0) {
102 if (setjmp(toplevel))
103 exit(0);
67368a68
SL
104 signal(SIGINT, intr);
105 signal(SIGPIPE, lostpeer);
ba5bf9f5
SL
106 setpeer(argc + 1, argv - 1);
107 }
108 top = setjmp(toplevel) == 0;
109 if (top) {
67368a68
SL
110 signal(SIGINT, intr);
111 signal(SIGPIPE, lostpeer);
ba5bf9f5
SL
112 }
113 for (;;) {
114 cmdscanner(top);
115 top = 1;
116 }
117}
118
119intr()
120{
121
122 longjmp(toplevel, 1);
123}
124
125lostpeer()
126{
127 extern FILE *cout;
128 extern int data;
ba5bf9f5
SL
129
130 if (connected) {
131 if (cout != NULL) {
0b579705 132 shutdown(fileno(cout), 1+1);
ba5bf9f5
SL
133 fclose(cout);
134 cout = NULL;
135 }
136 if (data >= 0) {
0b579705 137 shutdown(data, 1+1);
ba5bf9f5
SL
138 (void) close(data);
139 data = -1;
140 }
141 connected = 0;
142 }
143 longjmp(toplevel, 1);
144}
145
146char *
147tail(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 */
166cmdscanner(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 }
5a94754b
SL
181 if (gets(line) == 0) {
182 if (feof(stdin)) {
183 clearerr(stdin);
184 putchar('\n');
185 }
ba5bf9f5 186 break;
5a94754b 187 }
ba5bf9f5
SL
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 }
5ac6fc46
SL
200 if (c->c_conn && !connected) {
201 printf ("Not connected.\n");
202 continue;
203 }
ba5bf9f5
SL
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
213struct cmd *
214getcmd(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 */
245makeargv()
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 */
263char *
264slurpstring()
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
5ac6fc46
SL
271 if (*sb == '!') { /* recognize ! as a token for shell */
272 stringbase++;
273 return ("!");
274 }
ba5bf9f5
SL
275S0:
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
289S1:
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
309S2:
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
321S3:
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
336OUT:
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 */
352help(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 */
410call(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}