Annotation of src/usr.bin/ftp/main.c, Revision 1.22
1.22 ! millert 1: /* $OpenBSD: main.c,v 1.21 1997/02/18 18:04:31 kstailey Exp $ */
! 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.22 ! millert 47: static char rcsid[] = "$OpenBSD: main.c,v 1.21 1997/02/18 18:04:31 kstailey 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.22 ! millert 202: if (fromatty) {
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.17 millert 224:
225: if (argc > 0) {
226: if (strchr(argv[0], ':') != NULL) {
227: anonftp = 1; /* Handle "automatic" transfers. */
228: rval = auto_fetch(argc, argv);
229: if (rval >= 0) /* -1 == connected and cd-ed */
230: exit(rval);
231: } else {
1.3 deraadt 232: char *xargv[5];
233:
234: if (setjmp(toplevel))
235: exit(0);
1.20 millert 236: (void)signal(SIGINT, (sig_t)intr);
237: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 238: xargv[0] = __progname;
1.17 millert 239: xargv[1] = argv[0];
240: xargv[2] = argv[1];
241: xargv[3] = argv[2];
242: xargv[4] = NULL;
1.18 millert 243: do {
244: setpeer(argc+1, xargv);
245: if (!retry_connect)
246: break;
247: if (!connected) {
248: macnum = 0;
249: puts("Retrying...");
250: sleep(retry_connect);
251: }
252: } while (!connected);
1.3 deraadt 253: }
1.1 deraadt 254: }
255: top = setjmp(toplevel) == 0;
256: if (top) {
1.20 millert 257: (void)signal(SIGINT, (sig_t)intr);
258: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 259: }
260: for (;;) {
261: cmdscanner(top);
262: top = 1;
263: }
264: }
265:
266: void
267: intr()
268: {
269:
1.17 millert 270: alarmtimer(0);
1.1 deraadt 271: longjmp(toplevel, 1);
272: }
273:
274: void
275: lostpeer()
276: {
277:
1.17 millert 278: alarmtimer(0);
1.1 deraadt 279: if (connected) {
280: if (cout != NULL) {
1.18 millert 281: (void)shutdown(fileno(cout), 1+1);
282: (void)fclose(cout);
1.1 deraadt 283: cout = NULL;
284: }
285: if (data >= 0) {
1.18 millert 286: (void)shutdown(data, 1+1);
287: (void)close(data);
1.1 deraadt 288: data = -1;
289: }
290: connected = 0;
291: }
292: pswitch(1);
293: if (connected) {
294: if (cout != NULL) {
1.18 millert 295: (void)shutdown(fileno(cout), 1+1);
296: (void)fclose(cout);
1.1 deraadt 297: cout = NULL;
298: }
299: connected = 0;
300: }
301: proxflag = 0;
302: pswitch(0);
303: }
304:
305: /*
1.17 millert 306: * Generate a prompt
307: */
1.1 deraadt 308: char *
1.17 millert 309: prompt()
1.1 deraadt 310: {
1.17 millert 311: return ("ftp> ");
1.1 deraadt 312: }
313:
314: /*
315: * Command parser.
316: */
317: void
318: cmdscanner(top)
319: int top;
320: {
321: struct cmd *c;
1.17 millert 322: int num;
1.1 deraadt 323:
1.17 millert 324: if (!top
325: #ifndef SMALLFTP
326: && !editing
327: #endif /* !SMALLFTP */
328: )
1.18 millert 329: (void)putchar('\n');
1.1 deraadt 330: for (;;) {
1.17 millert 331: #ifndef SMALLFTP
332: if (!editing) {
333: #endif /* !SMALLFTP */
334: if (fromatty) {
1.18 millert 335: fputs(prompt(), stdout);
336: (void)fflush(stdout);
1.17 millert 337: }
338: if (fgets(line, sizeof(line), stdin) == NULL)
339: quit(0, 0);
340: num = strlen(line);
341: if (num == 0)
342: break;
343: if (line[--num] == '\n') {
344: if (num == 0)
345: break;
346: line[num] = '\0';
347: } else if (num == sizeof(line) - 2) {
1.22 ! millert 348: puts("sorry, input line too long.");
1.17 millert 349: while ((num = getchar()) != '\n' && num != EOF)
350: /* void */;
351: break;
352: } /* else it was a line without a newline */
353: #ifndef SMALLFTP
354: } else {
355: const char *buf;
356: cursor_pos = NULL;
357:
358: if ((buf = el_gets(el, &num)) == NULL || num == 0)
359: quit(0, 0);
360: if (line[--num] == '\n') {
361: if (num == 0)
362: break;
363: } else if (num >= sizeof(line)) {
1.22 ! millert 364: puts("sorry, input line too long.");
1.1 deraadt 365: break;
1.17 millert 366: }
367: memcpy(line, buf, num);
368: line[num] = '\0';
369: history(hist, H_ENTER, buf);
370: }
371: #endif /* !SMALLFTP */
372:
1.1 deraadt 373: makeargv();
1.17 millert 374: if (margc == 0)
1.1 deraadt 375: continue;
1.17 millert 376: #if 0 && !defined(SMALLFTP) /* XXX: don't want el_parse */
377: /*
378: * el_parse returns -1 to signal that it's not been handled
379: * internally.
380: */
381: if (el_parse(el, margc, margv) != -1)
382: continue;
383: #endif /* !SMALLFTP */
1.1 deraadt 384: c = getcmd(margv[0]);
385: if (c == (struct cmd *)-1) {
1.22 ! millert 386: puts("?Ambiguous command.");
1.1 deraadt 387: continue;
388: }
389: if (c == 0) {
1.22 ! millert 390: puts("?Invalid command.");
1.1 deraadt 391: continue;
392: }
393: if (c->c_conn && !connected) {
1.18 millert 394: puts("Not connected.");
1.1 deraadt 395: continue;
396: }
1.17 millert 397: confirmrest = 0;
1.1 deraadt 398: (*c->c_handler)(margc, margv);
399: if (bell && c->c_bell)
1.18 millert 400: (void)putchar('\007');
1.1 deraadt 401: if (c->c_handler != help)
402: break;
403: }
1.20 millert 404: (void)signal(SIGINT, (sig_t)intr);
405: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 406: }
407:
408: struct cmd *
409: getcmd(name)
1.17 millert 410: const char *name;
1.1 deraadt 411: {
1.17 millert 412: const char *p, *q;
1.1 deraadt 413: struct cmd *c, *found;
414: int nmatches, longest;
1.2 deraadt 415:
416: if (name == NULL)
417: return (0);
1.1 deraadt 418:
419: longest = 0;
420: nmatches = 0;
421: found = 0;
1.17 millert 422: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 423: for (q = name; *q == *p++; q++)
424: if (*q == 0) /* exact match? */
425: return (c);
426: if (!*q) { /* the name was a prefix */
427: if (q - name > longest) {
428: longest = q - name;
429: nmatches = 1;
430: found = c;
431: } else if (q - name == longest)
432: nmatches++;
433: }
434: }
435: if (nmatches > 1)
436: return ((struct cmd *)-1);
437: return (found);
438: }
439:
440: /*
441: * Slice a string up into argc/argv.
442: */
443:
444: int slrflag;
445:
446: void
447: makeargv()
448: {
1.17 millert 449: char *argp;
1.1 deraadt 450:
451: stringbase = line; /* scan from first of buffer */
452: argbase = argbuf; /* store from first of buffer */
453: slrflag = 0;
1.17 millert 454: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 455: for (margc = 0; ; margc++) {
1.17 millert 456: argp = slurpstring();
457: sl_add(marg_sl, argp);
458: if (argp == NULL)
1.1 deraadt 459: break;
460: }
1.17 millert 461: #ifndef SMALLFTP
462: if (cursor_pos == line) {
463: cursor_argc = 0;
464: cursor_argo = 0;
465: } else if (cursor_pos != NULL) {
466: cursor_argc = margc;
467: cursor_argo = strlen(margv[margc-1]);
468: }
469: #endif /* !SMALLFTP */
470: }
1.1 deraadt 471:
1.17 millert 472: #ifdef SMALLFTP
473: #define INC_CHKCURSOR(x) (x)++
474: #else /* !SMALLFTP */
475: #define INC_CHKCURSOR(x) { (x)++ ; \
476: if (x == cursor_pos) { \
477: cursor_argc = margc; \
478: cursor_argo = ap-argbase; \
479: cursor_pos = NULL; \
480: } }
481:
482: #endif /* !SMALLFTP */
1.1 deraadt 483:
484: /*
485: * Parse string into argbuf;
486: * implemented with FSM to
487: * handle quoting and strings
488: */
489: char *
490: slurpstring()
491: {
492: int got_one = 0;
493: char *sb = stringbase;
494: char *ap = argbase;
495: char *tmp = argbase; /* will return this if token found */
496:
497: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
498: switch (slrflag) { /* and $ as token for macro invoke */
499: case 0:
500: slrflag++;
1.17 millert 501: INC_CHKCURSOR(stringbase);
1.1 deraadt 502: return ((*sb == '!') ? "!" : "$");
503: /* NOTREACHED */
504: case 1:
505: slrflag++;
506: altarg = stringbase;
507: break;
508: default:
509: break;
510: }
511: }
512:
513: S0:
514: switch (*sb) {
515:
516: case '\0':
517: goto OUT;
518:
519: case ' ':
520: case '\t':
1.17 millert 521: INC_CHKCURSOR(sb);
522: goto S0;
1.1 deraadt 523:
524: default:
525: switch (slrflag) {
526: case 0:
527: slrflag++;
528: break;
529: case 1:
530: slrflag++;
531: altarg = sb;
532: break;
533: default:
534: break;
535: }
536: goto S1;
537: }
538:
539: S1:
540: switch (*sb) {
541:
542: case ' ':
543: case '\t':
544: case '\0':
545: goto OUT; /* end of token */
546:
547: case '\\':
1.17 millert 548: INC_CHKCURSOR(sb);
549: goto S2; /* slurp next character */
1.1 deraadt 550:
551: case '"':
1.17 millert 552: INC_CHKCURSOR(sb);
553: goto S3; /* slurp quoted string */
1.1 deraadt 554:
555: default:
1.17 millert 556: *ap = *sb; /* add character to token */
557: ap++;
558: INC_CHKCURSOR(sb);
1.1 deraadt 559: got_one = 1;
560: goto S1;
561: }
562:
563: S2:
564: switch (*sb) {
565:
566: case '\0':
567: goto OUT;
568:
569: default:
1.17 millert 570: *ap = *sb;
571: ap++;
572: INC_CHKCURSOR(sb);
1.1 deraadt 573: got_one = 1;
574: goto S1;
575: }
576:
577: S3:
578: switch (*sb) {
579:
580: case '\0':
581: goto OUT;
582:
583: case '"':
1.17 millert 584: INC_CHKCURSOR(sb);
585: goto S1;
1.1 deraadt 586:
587: default:
1.17 millert 588: *ap = *sb;
589: ap++;
590: INC_CHKCURSOR(sb);
1.1 deraadt 591: got_one = 1;
592: goto S3;
593: }
594:
595: OUT:
596: if (got_one)
597: *ap++ = '\0';
598: argbase = ap; /* update storage pointer */
599: stringbase = sb; /* update scan pointer */
600: if (got_one) {
601: return (tmp);
602: }
603: switch (slrflag) {
604: case 0:
605: slrflag++;
606: break;
607: case 1:
608: slrflag++;
609: altarg = (char *) 0;
610: break;
611: default:
612: break;
613: }
614: return ((char *)0);
615: }
616:
617: /*
618: * Help command.
619: * Call each command handler with argc == 0 and argv[0] == name.
620: */
621: void
622: help(argc, argv)
623: int argc;
624: char *argv[];
625: {
626: struct cmd *c;
627:
628: if (argc == 1) {
1.17 millert 629: StringList *buf;
1.1 deraadt 630:
1.17 millert 631: buf = sl_init();
632: printf("%sommands may be abbreviated. Commands are:\n\n",
633: proxy ? "Proxy c" : "C");
634: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
635: if (c->c_name && (!proxy || c->c_proxy))
636: sl_add(buf, c->c_name);
637: list_vertical(buf);
638: sl_free(buf, 0);
1.1 deraadt 639: return;
640: }
1.17 millert 641:
1.18 millert 642: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 643:
1.1 deraadt 644: while (--argc > 0) {
645: char *arg;
1.17 millert 646:
1.1 deraadt 647: arg = *++argv;
648: c = getcmd(arg);
649: if (c == (struct cmd *)-1)
650: printf("?Ambiguous help command %s\n", arg);
651: else if (c == (struct cmd *)0)
652: printf("?Invalid help command %s\n", arg);
653: else
654: printf("%-*s\t%s\n", HELPINDENT,
655: c->c_name, c->c_help);
656: }
1.17 millert 657: }
658:
659: void
660: usage()
661: {
662: (void)fprintf(stderr,
1.22 ! millert 663: "usage: %s [-adeginprtvV] [host [port]]\n"
1.17 millert 664: " %s host:path[/]\n"
665: " %s ftp://host[:port]/path[/]\n"
666: " %s http://host[:port]/file\n",
667: __progname, __progname, __progname, __progname);
668: exit(1);
1.1 deraadt 669: }