manual page first distributed with 4.2BSD
[unix-history] / usr / src / usr.bin / ftp / main.c
CommitLineData
ba5bf9f5 1#ifndef lint
ed129162 2static char sccsid[] = "@(#)main.c 4.10 (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)) {
ed129162
RC
183 if (!fromatty)
184 quit();
5a94754b
SL
185 clearerr(stdin);
186 putchar('\n');
187 }
ba5bf9f5 188 break;
5a94754b 189 }
ba5bf9f5
SL
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 }
5ac6fc46
SL
202 if (c->c_conn && !connected) {
203 printf ("Not connected.\n");
204 continue;
205 }
ba5bf9f5
SL
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
215struct cmd *
216getcmd(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 */
247makeargv()
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 */
265char *
266slurpstring()
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
5ac6fc46
SL
273 if (*sb == '!') { /* recognize ! as a token for shell */
274 stringbase++;
275 return ("!");
276 }
ba5bf9f5
SL
277S0:
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
291S1:
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
311S2:
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
323S3:
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
338OUT:
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 */
354help(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 */
412call(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}