main.c revision 1.10 1 /*
2 * Copyright (c) 1985, 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
43 #else
44 static char rcsid[] = "$NetBSD: main.c,v 1.10 1995/09/15 00:32:33 pk Exp $";
45 #endif
46 #endif /* not lint */
47
48 /*
49 * FTP User Program -- Command Interface.
50 */
51 /*#include <sys/ioctl.h>*/
52 #include <sys/types.h>
53 #include <sys/socket.h>
54
55 #include <arpa/ftp.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <netdb.h>
60 #include <pwd.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66
67 #include "ftp_var.h"
68
69 int
70 main(argc, argv)
71 int argc;
72 char *argv[];
73 {
74 int ch, top;
75 struct passwd *pw = NULL;
76 char *cp, homedir[MAXPATHLEN];
77
78 sp = getservbyname("ftp", "tcp");
79 if (sp == 0)
80 errx(1, "ftp/tcp: unknown service");
81 doglob = 1;
82 interactive = 1;
83 autologin = 1;
84
85 while ((ch = getopt(argc, argv, "dgintv")) != EOF) {
86 switch (ch) {
87 case 'd':
88 options |= SO_DEBUG;
89 debug++;
90 break;
91
92 case 'g':
93 doglob = 0;
94 break;
95
96 case 'i':
97 interactive = 0;
98 break;
99
100 case 'n':
101 autologin = 0;
102 break;
103
104 case 't':
105 trace++;
106 break;
107
108 case 'v':
109 verbose++;
110 break;
111
112 default:
113 (void)fprintf(stderr,
114 "usage: ftp [-dgintv] [host [port]]\n");
115 exit(1);
116 }
117 }
118 argc -= optind;
119 argv += optind;
120
121 fromatty = isatty(fileno(stdin));
122 if (fromatty)
123 verbose++;
124 cpend = 0; /* no pending replies */
125 proxy = 0; /* proxy not active */
126 passivemode = 0; /* passive mode not active */
127 crflag = 1; /* strip c.r. on ascii gets */
128 sendport = -1; /* not using ports */
129 /*
130 * Set up the home directory in case we're globbing.
131 */
132 cp = getlogin();
133 if (cp != NULL) {
134 pw = getpwnam(cp);
135 }
136 if (pw == NULL)
137 pw = getpwuid(getuid());
138 if (pw != NULL) {
139 home = homedir;
140 (void) strcpy(home, pw->pw_dir);
141 }
142 if (argc > 0) {
143 char *xargv[5];
144 extern char *__progname;
145
146 if (setjmp(toplevel))
147 exit(0);
148 (void) signal(SIGINT, intr);
149 (void) signal(SIGPIPE, lostpeer);
150 xargv[0] = __progname;
151 xargv[1] = argv[0];
152 xargv[2] = argv[1];
153 xargv[3] = argv[2];
154 xargv[4] = NULL;
155 setpeer(argc+1, xargv);
156 }
157 top = setjmp(toplevel) == 0;
158 if (top) {
159 (void) signal(SIGINT, intr);
160 (void) signal(SIGPIPE, lostpeer);
161 }
162 for (;;) {
163 cmdscanner(top);
164 top = 1;
165 }
166 }
167
168 void
169 intr()
170 {
171
172 longjmp(toplevel, 1);
173 }
174
175 void
176 lostpeer()
177 {
178
179 if (connected) {
180 if (cout != NULL) {
181 (void) shutdown(fileno(cout), 1+1);
182 (void) fclose(cout);
183 cout = NULL;
184 }
185 if (data >= 0) {
186 (void) shutdown(data, 1+1);
187 (void) close(data);
188 data = -1;
189 }
190 connected = 0;
191 }
192 pswitch(1);
193 if (connected) {
194 if (cout != NULL) {
195 (void) shutdown(fileno(cout), 1+1);
196 (void) fclose(cout);
197 cout = NULL;
198 }
199 connected = 0;
200 }
201 proxflag = 0;
202 pswitch(0);
203 }
204
205 /*
206 char *
207 tail(filename)
208 char *filename;
209 {
210 char *s;
211
212 while (*filename) {
213 s = strrchr(filename, '/');
214 if (s == NULL)
215 break;
216 if (s[1])
217 return (s + 1);
218 *s = '\0';
219 }
220 return (filename);
221 }
222 */
223
224 /*
225 * Command parser.
226 */
227 void
228 cmdscanner(top)
229 int top;
230 {
231 struct cmd *c;
232 int l;
233
234 if (!top)
235 (void) putchar('\n');
236 for (;;) {
237 if (fromatty) {
238 printf("ftp> ");
239 (void) fflush(stdout);
240 }
241 if (fgets(line, sizeof line, stdin) == NULL)
242 quit(0, 0);
243 l = strlen(line);
244 if (l == 0)
245 break;
246 if (line[--l] == '\n') {
247 if (l == 0)
248 break;
249 line[l] = '\0';
250 } else if (l == sizeof(line) - 2) {
251 printf("sorry, input line too long\n");
252 while ((l = getchar()) != '\n' && l != EOF)
253 /* void */;
254 break;
255 } /* else it was a line without a newline */
256 makeargv();
257 if (margc == 0) {
258 continue;
259 }
260 c = getcmd(margv[0]);
261 if (c == (struct cmd *)-1) {
262 printf("?Ambiguous command\n");
263 continue;
264 }
265 if (c == 0) {
266 printf("?Invalid command\n");
267 continue;
268 }
269 if (c->c_conn && !connected) {
270 printf("Not connected.\n");
271 continue;
272 }
273 (*c->c_handler)(margc, margv);
274 if (bell && c->c_bell)
275 (void) putchar('\007');
276 if (c->c_handler != help)
277 break;
278 }
279 (void) signal(SIGINT, intr);
280 (void) signal(SIGPIPE, lostpeer);
281 }
282
283 struct cmd *
284 getcmd(name)
285 char *name;
286 {
287 char *p, *q;
288 struct cmd *c, *found;
289 int nmatches, longest;
290
291 longest = 0;
292 nmatches = 0;
293 found = 0;
294 for (c = cmdtab; p = c->c_name; c++) {
295 for (q = name; *q == *p++; q++)
296 if (*q == 0) /* exact match? */
297 return (c);
298 if (!*q) { /* the name was a prefix */
299 if (q - name > longest) {
300 longest = q - name;
301 nmatches = 1;
302 found = c;
303 } else if (q - name == longest)
304 nmatches++;
305 }
306 }
307 if (nmatches > 1)
308 return ((struct cmd *)-1);
309 return (found);
310 }
311
312 /*
313 * Slice a string up into argc/argv.
314 */
315
316 int slrflag;
317
318 void
319 makeargv()
320 {
321 char **argp;
322
323 argp = margv;
324 stringbase = line; /* scan from first of buffer */
325 argbase = argbuf; /* store from first of buffer */
326 slrflag = 0;
327 for (margc = 0; ; margc++) {
328 /* Expand array if necessary */
329 if (margc == margvlen) {
330 margv = (margvlen == 0)
331 ? (char **)malloc(20 * sizeof(char *))
332 : (char **)realloc(margv,
333 (margvlen + 20)*sizeof(char *));
334 if (margv == NULL)
335 errx(1, "cannot realloc argv array");
336 margvlen += 20;
337 argp = margv + margc;
338 }
339
340 if ((*argp++ = slurpstring()) == NULL)
341 break;
342 }
343
344 }
345
346 /*
347 * Parse string into argbuf;
348 * implemented with FSM to
349 * handle quoting and strings
350 */
351 char *
352 slurpstring()
353 {
354 int got_one = 0;
355 char *sb = stringbase;
356 char *ap = argbase;
357 char *tmp = argbase; /* will return this if token found */
358
359 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
360 switch (slrflag) { /* and $ as token for macro invoke */
361 case 0:
362 slrflag++;
363 stringbase++;
364 return ((*sb == '!') ? "!" : "$");
365 /* NOTREACHED */
366 case 1:
367 slrflag++;
368 altarg = stringbase;
369 break;
370 default:
371 break;
372 }
373 }
374
375 S0:
376 switch (*sb) {
377
378 case '\0':
379 goto OUT;
380
381 case ' ':
382 case '\t':
383 sb++; goto S0;
384
385 default:
386 switch (slrflag) {
387 case 0:
388 slrflag++;
389 break;
390 case 1:
391 slrflag++;
392 altarg = sb;
393 break;
394 default:
395 break;
396 }
397 goto S1;
398 }
399
400 S1:
401 switch (*sb) {
402
403 case ' ':
404 case '\t':
405 case '\0':
406 goto OUT; /* end of token */
407
408 case '\\':
409 sb++; goto S2; /* slurp next character */
410
411 case '"':
412 sb++; goto S3; /* slurp quoted string */
413
414 default:
415 *ap++ = *sb++; /* add character to token */
416 got_one = 1;
417 goto S1;
418 }
419
420 S2:
421 switch (*sb) {
422
423 case '\0':
424 goto OUT;
425
426 default:
427 *ap++ = *sb++;
428 got_one = 1;
429 goto S1;
430 }
431
432 S3:
433 switch (*sb) {
434
435 case '\0':
436 goto OUT;
437
438 case '"':
439 sb++; goto S1;
440
441 default:
442 *ap++ = *sb++;
443 got_one = 1;
444 goto S3;
445 }
446
447 OUT:
448 if (got_one)
449 *ap++ = '\0';
450 argbase = ap; /* update storage pointer */
451 stringbase = sb; /* update scan pointer */
452 if (got_one) {
453 return (tmp);
454 }
455 switch (slrflag) {
456 case 0:
457 slrflag++;
458 break;
459 case 1:
460 slrflag++;
461 altarg = (char *) 0;
462 break;
463 default:
464 break;
465 }
466 return ((char *)0);
467 }
468
469 #define HELPINDENT ((int) sizeof ("directory"))
470
471 /*
472 * Help command.
473 * Call each command handler with argc == 0 and argv[0] == name.
474 */
475 void
476 help(argc, argv)
477 int argc;
478 char *argv[];
479 {
480 struct cmd *c;
481
482 if (argc == 1) {
483 int i, j, w, k;
484 int columns, width = 0, lines;
485
486 printf("Commands may be abbreviated. Commands are:\n\n");
487 for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
488 int len = strlen(c->c_name);
489
490 if (len > width)
491 width = len;
492 }
493 width = (width + 8) &~ 7;
494 columns = 80 / width;
495 if (columns == 0)
496 columns = 1;
497 lines = (NCMDS + columns - 1) / columns;
498 for (i = 0; i < lines; i++) {
499 for (j = 0; j < columns; j++) {
500 c = cmdtab + j * lines + i;
501 if (c->c_name && (!proxy || c->c_proxy)) {
502 printf("%s", c->c_name);
503 }
504 else if (c->c_name) {
505 for (k=0; k < strlen(c->c_name); k++) {
506 (void) putchar(' ');
507 }
508 }
509 if (c + lines >= &cmdtab[NCMDS]) {
510 printf("\n");
511 break;
512 }
513 w = strlen(c->c_name);
514 while (w < width) {
515 w = (w + 8) &~ 7;
516 (void) putchar('\t');
517 }
518 }
519 }
520 return;
521 }
522 while (--argc > 0) {
523 char *arg;
524 arg = *++argv;
525 c = getcmd(arg);
526 if (c == (struct cmd *)-1)
527 printf("?Ambiguous help command %s\n", arg);
528 else if (c == (struct cmd *)0)
529 printf("?Invalid help command %s\n", arg);
530 else
531 printf("%-*s\t%s\n", HELPINDENT,
532 c->c_name, c->c_help);
533 }
534 }
535