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