main.c revision 1.24 1 /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
2
3 /*
4 * Copyright (c) 1985, 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
45 #else
46 __RCSID("$NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $");
47 #endif
48 #endif /* not lint */
49
50 /*
51 * FTP User Program -- Command Interface.
52 */
53 #include <sys/types.h>
54 #include <sys/socket.h>
55
56 #include <err.h>
57 #include <netdb.h>
58 #include <pwd.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63
64 #include "ftp_var.h"
65
66 int main __P((int, char **));
67
68 int
69 main(argc, argv)
70 int argc;
71 char *argv[];
72 {
73 struct servent *sp;
74 int ch, top, rval;
75 long port;
76 struct passwd *pw = NULL;
77 char *cp, *ep, homedir[MAXPATHLEN];
78 int dumbterm;
79
80 sp = getservbyname("ftp", "tcp");
81 if (sp == 0)
82 ftpport = htons(FTP_PORT); /* good fallback */
83 else
84 ftpport = sp->s_port;
85 sp = getservbyname("http", "tcp");
86 if (sp == 0)
87 httpport = htons(HTTP_PORT); /* good fallback */
88 else
89 httpport = sp->s_port;
90 gateport = 0;
91 cp = getenv("FTPSERVERPORT");
92 if (cp != NULL) {
93 port = strtol(cp, &ep, 10);
94 if (port < 1 || port > 0xffff || *ep != '\0')
95 warnx("bad FTPSERVERPORT port number: %s (ignored)",
96 cp);
97 else
98 gateport = htons(port);
99 }
100 if (gateport == 0) {
101 sp = getservbyname("ftpgate", "tcp");
102 if (sp == 0)
103 gateport = htons(GATE_PORT);
104 else
105 gateport = sp->s_port;
106 }
107 doglob = 1;
108 interactive = 1;
109 autologin = 1;
110 passivemode = 0;
111 preserve = 1;
112 verbose = 0;
113 progress = 0;
114 gatemode = 0;
115 #ifndef SMALL
116 editing = 0;
117 el = NULL;
118 hist = NULL;
119 #endif
120 mark = HASHBYTES;
121 marg_sl = sl_init();
122
123 cp = strrchr(argv[0], '/');
124 cp = (cp == NULL) ? argv[0] : cp + 1;
125 if (strcmp(cp, "pftp") == 0)
126 passivemode = 1;
127 else if (strcmp(cp, "gate-ftp") == 0)
128 gatemode = 1;
129
130 gateserver = getenv("FTPSERVER");
131 if (gateserver == NULL || *gateserver == '\0')
132 gateserver = GATE_SERVER;
133 if (gatemode) {
134 if (*gateserver == '\0') {
135 warnx(
136 "Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp");
137 gatemode = 0;
138 }
139 }
140
141 cp = getenv("TERM");
142 if (cp == NULL || strcmp(cp, "dumb") == 0)
143 dumbterm = 1;
144 else
145 dumbterm = 0;
146 fromatty = isatty(fileno(stdin));
147 if (fromatty) {
148 verbose = 1; /* verbose if from a tty */
149 #ifndef SMALL
150 if (! dumbterm)
151 editing = 1; /* editing mode on if tty is usable */
152 #endif
153 }
154 if (isatty(fileno(stdout)) && !dumbterm)
155 progress = 1; /* progress bar on if tty is usable */
156
157 while ((ch = getopt(argc, argv, "adeginpP:tvV")) != -1) {
158 switch (ch) {
159 case 'a':
160 anonftp = 1;
161 break;
162
163 case 'd':
164 options |= SO_DEBUG;
165 debug++;
166 break;
167
168 case 'e':
169 #ifndef SMALL
170 editing = 0;
171 #endif
172 break;
173
174 case 'g':
175 doglob = 0;
176 break;
177
178 case 'i':
179 interactive = 0;
180 break;
181
182 case 'n':
183 autologin = 0;
184 break;
185
186 case 'p':
187 passivemode = 1;
188 break;
189
190 case 'P':
191 port = strtol(optarg, &ep, 10);
192 if (port < 1 || port > 0xffff || *ep != '\0')
193 warnx("bad port number: %s (ignored)", optarg);
194 else
195 ftpport = htons(port);
196 break;
197
198 case 't':
199 trace = 1;
200 break;
201
202 case 'v':
203 verbose = 1;
204 break;
205
206 case 'V':
207 verbose = 0;
208 break;
209
210 default:
211 usage();
212 }
213 }
214 argc -= optind;
215 argv += optind;
216
217 cpend = 0; /* no pending replies */
218 proxy = 0; /* proxy not active */
219 crflag = 1; /* strip c.r. on ascii gets */
220 sendport = -1; /* not using ports */
221 /*
222 * Set up the home directory in case we're globbing.
223 */
224 cp = getlogin();
225 if (cp != NULL) {
226 pw = getpwnam(cp);
227 }
228 if (pw == NULL)
229 pw = getpwuid(getuid());
230 if (pw != NULL) {
231 home = homedir;
232 (void)strcpy(home, pw->pw_dir);
233 }
234
235 setttywidth(0);
236 (void)signal(SIGWINCH, setttywidth);
237
238 #ifdef __GNUC__ /* XXX: to shut up gcc warnings */
239 (void)&argc;
240 (void)&argv;
241 #endif
242
243 if (argc > 0) {
244 if (strchr(argv[0], ':') != NULL) {
245 anonftp = 1; /* Handle "automatic" transfers. */
246 rval = auto_fetch(argc, argv);
247 if (rval >= 0) /* -1 == connected and cd-ed */
248 exit(rval);
249 } else {
250 char *xargv[5];
251
252 if (setjmp(toplevel))
253 exit(0);
254 (void)signal(SIGINT, (sig_t)intr);
255 (void)signal(SIGPIPE, (sig_t)lostpeer);
256 xargv[0] = __progname;
257 xargv[1] = argv[0];
258 xargv[2] = argv[1];
259 xargv[3] = argv[2];
260 xargv[4] = NULL;
261 setpeer(argc+1, xargv);
262 }
263 }
264 #ifndef SMALL
265 controlediting();
266 #endif /* !SMALL */
267 top = setjmp(toplevel) == 0;
268 if (top) {
269 (void)signal(SIGINT, (sig_t)intr);
270 (void)signal(SIGPIPE, (sig_t)lostpeer);
271 }
272 for (;;) {
273 cmdscanner(top);
274 top = 1;
275 }
276 }
277
278 void
279 intr()
280 {
281
282 alarmtimer(0);
283 longjmp(toplevel, 1);
284 }
285
286 void
287 lostpeer()
288 {
289
290 alarmtimer(0);
291 if (connected) {
292 if (cout != NULL) {
293 (void)shutdown(fileno(cout), 1+1);
294 (void)fclose(cout);
295 cout = NULL;
296 }
297 if (data >= 0) {
298 (void)shutdown(data, 1+1);
299 (void)close(data);
300 data = -1;
301 }
302 connected = 0;
303 }
304 pswitch(1);
305 if (connected) {
306 if (cout != NULL) {
307 (void)shutdown(fileno(cout), 1+1);
308 (void)fclose(cout);
309 cout = NULL;
310 }
311 connected = 0;
312 }
313 proxflag = 0;
314 pswitch(0);
315 }
316
317 /*
318 * Generate a prompt
319 */
320 char *
321 prompt()
322 {
323 return ("ftp> ");
324 }
325
326 /*
327 * Command parser.
328 */
329 void
330 cmdscanner(top)
331 int top;
332 {
333 struct cmd *c;
334 int num;
335
336 if (!top
337 #ifndef SMALL
338 && !editing
339 #endif /* !SMALL */
340 )
341 (void)putchar('\n');
342 for (;;) {
343 #ifndef SMALL
344 if (!editing) {
345 #endif /* !SMALL */
346 if (fromatty) {
347 fputs(prompt(), stdout);
348 (void)fflush(stdout);
349 }
350 if (fgets(line, sizeof(line), stdin) == NULL)
351 quit(0, 0);
352 num = strlen(line);
353 if (num == 0)
354 break;
355 if (line[--num] == '\n') {
356 if (num == 0)
357 break;
358 line[num] = '\0';
359 } else if (num == sizeof(line) - 2) {
360 puts("sorry, input line too long.");
361 while ((num = getchar()) != '\n' && num != EOF)
362 /* void */;
363 break;
364 } /* else it was a line without a newline */
365 #ifndef SMALL
366 } else {
367 const char *buf;
368 cursor_pos = NULL;
369
370 if ((buf = el_gets(el, &num)) == NULL || num == 0)
371 quit(0, 0);
372 if (line[--num] == '\n') {
373 if (num == 0)
374 break;
375 } else if (num >= sizeof(line)) {
376 puts("sorry, input line too long.");
377 break;
378 }
379 memcpy(line, buf, num);
380 line[num] = '\0';
381 history(hist, H_ENTER, buf);
382 }
383 #endif /* !SMALL */
384
385 makeargv();
386 if (margc == 0)
387 continue;
388 #if 0 && !defined(SMALL) /* XXX: don't want el_parse */
389 /*
390 * el_parse returns -1 to signal that it's not been handled
391 * internally.
392 */
393 if (el_parse(el, margc, margv) != -1)
394 continue;
395 #endif /* !SMALL */
396 c = getcmd(margv[0]);
397 if (c == (struct cmd *)-1) {
398 puts("?Ambiguous command.");
399 continue;
400 }
401 if (c == 0) {
402 puts("?Invalid command.");
403 continue;
404 }
405 if (c->c_conn && !connected) {
406 puts("Not connected.");
407 continue;
408 }
409 confirmrest = 0;
410 (*c->c_handler)(margc, margv);
411 if (bell && c->c_bell)
412 (void)putchar('\007');
413 if (c->c_handler != help)
414 break;
415 }
416 (void)signal(SIGINT, (sig_t)intr);
417 (void)signal(SIGPIPE, (sig_t)lostpeer);
418 }
419
420 struct cmd *
421 getcmd(name)
422 const char *name;
423 {
424 const char *p, *q;
425 struct cmd *c, *found;
426 int nmatches, longest;
427
428 if (name == NULL)
429 return (0);
430
431 longest = 0;
432 nmatches = 0;
433 found = 0;
434 for (c = cmdtab; (p = c->c_name) != NULL; c++) {
435 for (q = name; *q == *p++; q++)
436 if (*q == 0) /* exact match? */
437 return (c);
438 if (!*q) { /* the name was a prefix */
439 if (q - name > longest) {
440 longest = q - name;
441 nmatches = 1;
442 found = c;
443 } else if (q - name == longest)
444 nmatches++;
445 }
446 }
447 if (nmatches > 1)
448 return ((struct cmd *)-1);
449 return (found);
450 }
451
452 /*
453 * Slice a string up into argc/argv.
454 */
455
456 int slrflag;
457
458 void
459 makeargv()
460 {
461 char *argp;
462
463 stringbase = line; /* scan from first of buffer */
464 argbase = argbuf; /* store from first of buffer */
465 slrflag = 0;
466 marg_sl->sl_cur = 0; /* reset to start of marg_sl */
467 for (margc = 0; ; margc++) {
468 argp = slurpstring();
469 sl_add(marg_sl, argp);
470 if (argp == NULL)
471 break;
472 }
473 #ifndef SMALL
474 if (cursor_pos == line) {
475 cursor_argc = 0;
476 cursor_argo = 0;
477 } else if (cursor_pos != NULL) {
478 cursor_argc = margc;
479 cursor_argo = strlen(margv[margc-1]);
480 }
481 #endif /* !SMALL */
482 }
483
484 #ifdef SMALL
485 #define INC_CHKCURSOR(x) (x)++
486 #else /* !SMALL */
487 #define INC_CHKCURSOR(x) { (x)++ ; \
488 if (x == cursor_pos) { \
489 cursor_argc = margc; \
490 cursor_argo = ap-argbase; \
491 cursor_pos = NULL; \
492 } }
493
494 #endif /* !SMALL */
495
496 /*
497 * Parse string into argbuf;
498 * implemented with FSM to
499 * handle quoting and strings
500 */
501 char *
502 slurpstring()
503 {
504 int got_one = 0;
505 char *sb = stringbase;
506 char *ap = argbase;
507 char *tmp = argbase; /* will return this if token found */
508
509 if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
510 switch (slrflag) { /* and $ as token for macro invoke */
511 case 0:
512 slrflag++;
513 INC_CHKCURSOR(stringbase);
514 return ((*sb == '!') ? "!" : "$");
515 /* NOTREACHED */
516 case 1:
517 slrflag++;
518 altarg = stringbase;
519 break;
520 default:
521 break;
522 }
523 }
524
525 S0:
526 switch (*sb) {
527
528 case '\0':
529 goto OUT;
530
531 case ' ':
532 case '\t':
533 INC_CHKCURSOR(sb);
534 goto S0;
535
536 default:
537 switch (slrflag) {
538 case 0:
539 slrflag++;
540 break;
541 case 1:
542 slrflag++;
543 altarg = sb;
544 break;
545 default:
546 break;
547 }
548 goto S1;
549 }
550
551 S1:
552 switch (*sb) {
553
554 case ' ':
555 case '\t':
556 case '\0':
557 goto OUT; /* end of token */
558
559 case '\\':
560 INC_CHKCURSOR(sb);
561 goto S2; /* slurp next character */
562
563 case '"':
564 INC_CHKCURSOR(sb);
565 goto S3; /* slurp quoted string */
566
567 default:
568 *ap = *sb; /* add character to token */
569 ap++;
570 INC_CHKCURSOR(sb);
571 got_one = 1;
572 goto S1;
573 }
574
575 S2:
576 switch (*sb) {
577
578 case '\0':
579 goto OUT;
580
581 default:
582 *ap = *sb;
583 ap++;
584 INC_CHKCURSOR(sb);
585 got_one = 1;
586 goto S1;
587 }
588
589 S3:
590 switch (*sb) {
591
592 case '\0':
593 goto OUT;
594
595 case '"':
596 INC_CHKCURSOR(sb);
597 goto S1;
598
599 default:
600 *ap = *sb;
601 ap++;
602 INC_CHKCURSOR(sb);
603 got_one = 1;
604 goto S3;
605 }
606
607 OUT:
608 if (got_one)
609 *ap++ = '\0';
610 argbase = ap; /* update storage pointer */
611 stringbase = sb; /* update scan pointer */
612 if (got_one) {
613 return (tmp);
614 }
615 switch (slrflag) {
616 case 0:
617 slrflag++;
618 break;
619 case 1:
620 slrflag++;
621 altarg = (char *) 0;
622 break;
623 default:
624 break;
625 }
626 return ((char *)0);
627 }
628
629 /*
630 * Help command.
631 * Call each command handler with argc == 0 and argv[0] == name.
632 */
633 void
634 help(argc, argv)
635 int argc;
636 char *argv[];
637 {
638 struct cmd *c;
639
640 if (argc == 1) {
641 StringList *buf;
642
643 buf = sl_init();
644 printf("%sommands may be abbreviated. Commands are:\n\n",
645 proxy ? "Proxy c" : "C");
646 for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
647 if (c->c_name && (!proxy || c->c_proxy))
648 sl_add(buf, c->c_name);
649 list_vertical(buf);
650 sl_free(buf, 0);
651 return;
652 }
653
654 #define HELPINDENT ((int) sizeof("disconnect"))
655
656 while (--argc > 0) {
657 char *arg;
658
659 arg = *++argv;
660 c = getcmd(arg);
661 if (c == (struct cmd *)-1)
662 printf("?Ambiguous help command %s\n", arg);
663 else if (c == (struct cmd *)0)
664 printf("?Invalid help command %s\n", arg);
665 else
666 printf("%-*s\t%s\n", HELPINDENT,
667 c->c_name, c->c_help);
668 }
669 }
670
671 void
672 usage()
673 {
674 (void)fprintf(stderr,
675 "usage: %s [-adeginptvV] [host [port]]\n"
676 " %s host:path[/]\n"
677 " %s ftp://host[:port]/path[/]\n"
678 " %s http://host[:port]/file\n",
679 __progname, __progname, __progname, __progname);
680 exit(1);
681 }
682