Annotation of src/usr.bin/ftp/main.c, Revision 1.18
1.18 ! millert 1: /* $OpenBSD: main.c,v 1.17 1997/02/01 10:45:07 lukem Exp $ */
1.17 millert 2: /* $NetBSD: main.c,v 1.17 1997/02/01 10:45:07 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.18 ! millert 47: static char rcsid[] = "$OpenBSD: main.c,v 1.17 1997/02/01 10:45:07 lukem 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.8 kstailey 95: mark = HASHBYTES;
1.17 millert 96: marg_sl = sl_init();
1.1 deraadt 97:
1.17 millert 98: cp = strrchr(argv[0], '/');
99: cp = (cp == NULL) ? argv[0] : cp + 1;
100: if (strcmp(cp, "pftp") == 0)
101: passivemode = 1;
102:
103: fromatty = isatty(fileno(stdin));
104: if (fromatty)
105: verbose = 1; /* verbose if from a tty */
106: if (isatty(fileno(stdout)))
107: progress = 1; /* progress bar on if going to a tty */
108:
1.18 ! millert 109: while ((ch = getopt(argc, argv, "adginpPr:tvV")) != -1) {
1.1 deraadt 110: switch (ch) {
1.17 millert 111: case 'a':
112: anonftp = 1;
113: break;
114:
1.1 deraadt 115: case 'd':
116: options |= SO_DEBUG;
117: debug++;
118: break;
1.17 millert 119:
1.1 deraadt 120: case 'g':
121: doglob = 0;
122: break;
123:
124: case 'i':
125: interactive = 0;
126: break;
127:
128: case 'n':
129: autologin = 0;
130: break;
131:
1.3 deraadt 132: case 'p':
1.17 millert 133: passivemode = 1;
1.3 deraadt 134: break;
135:
1.17 millert 136: case 'P':
137: port = atoi(optarg);
138: if (port <= 0)
1.18 ! millert 139: warnx("bad port number: %s (ignored)", optarg);
1.17 millert 140: else
141: ftpport = htons(port);
1.7 mickey 142: break;
143:
1.18 ! millert 144: case 'r':
! 145: if (isdigit(*optarg))
! 146: retry_connect = atoi(optarg);
! 147: else
! 148: errx(1, "-r requires numeric argument");
! 149: break;
! 150:
1.1 deraadt 151: case 't':
1.17 millert 152: trace = 1;
1.1 deraadt 153: break;
154:
155: case 'v':
1.17 millert 156: verbose = 1;
157: break;
158:
159: case 'V':
160: verbose = 0;
1.1 deraadt 161: break;
162:
163: default:
1.17 millert 164: usage();
1.1 deraadt 165: }
166: }
167: argc -= optind;
168: argv += optind;
169:
170: cpend = 0; /* no pending replies */
171: proxy = 0; /* proxy not active */
172: crflag = 1; /* strip c.r. on ascii gets */
173: sendport = -1; /* not using ports */
174: /*
175: * Set up the home directory in case we're globbing.
176: */
177: cp = getlogin();
178: if (cp != NULL) {
179: pw = getpwnam(cp);
180: }
181: if (pw == NULL)
182: pw = getpwuid(getuid());
183: if (pw != NULL) {
184: home = homedir;
1.18 ! millert 185: (void)strcpy(home, pw->pw_dir);
1.3 deraadt 186: }
1.9 michaels 187:
1.17 millert 188: #ifndef SMALLFTP
189: editing = 0; /* command line editing off */
190: if (fromatty) {
191: editing = 1; /* editing mode on if a tty */
192: el = el_init(__progname, stdin, stdout); /* init editline */
193:
194: hist = history_init(); /* init the builtin history */
195: history(hist, H_EVENT, 100); /* remember 100 events */
196: el_set(el, EL_HIST, history, hist); /* use history */
197:
198: el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
199: el_set(el, EL_PROMPT, prompt); /* set the prompt function */
200:
201: /* add local file completion, bind to TAB */
202: el_set(el, EL_ADDFN, "ftp-complete",
203: "Context sensitive argument completion",
204: complete);
205: el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
206:
207: el_source(el, NULL); /* read ~/.editrc */
208: }
209: #endif /* !SMALLFTP */
1.3 deraadt 210:
1.17 millert 211: setttywidth(0);
1.18 ! millert 212: (void)signal(SIGWINCH, setttywidth);
1.17 millert 213:
214: if (argc > 0) {
215: if (strchr(argv[0], ':') != NULL) {
216: anonftp = 1; /* Handle "automatic" transfers. */
217: rval = auto_fetch(argc, argv);
218: if (rval >= 0) /* -1 == connected and cd-ed */
219: exit(rval);
220: } else {
1.3 deraadt 221: char *xargv[5];
222:
223: if (setjmp(toplevel))
224: exit(0);
1.18 ! millert 225: (void)signal(SIGINT, intr);
! 226: (void)signal(SIGPIPE, lostpeer);
1.3 deraadt 227: xargv[0] = __progname;
1.17 millert 228: xargv[1] = argv[0];
229: xargv[2] = argv[1];
230: xargv[3] = argv[2];
231: xargv[4] = NULL;
1.18 ! millert 232: do {
! 233: setpeer(argc+1, xargv);
! 234: if (!retry_connect)
! 235: break;
! 236: if (!connected) {
! 237: macnum = 0;
! 238: puts("Retrying...");
! 239: sleep(retry_connect);
! 240: }
! 241: } while (!connected);
1.3 deraadt 242: }
1.1 deraadt 243: }
244: top = setjmp(toplevel) == 0;
245: if (top) {
1.18 ! millert 246: (void)signal(SIGINT, intr);
! 247: (void)signal(SIGPIPE, lostpeer);
1.1 deraadt 248: }
249: for (;;) {
250: cmdscanner(top);
251: top = 1;
252: }
253: }
254:
255: void
256: intr()
257: {
258:
1.17 millert 259: alarmtimer(0);
1.1 deraadt 260: longjmp(toplevel, 1);
261: }
262:
263: void
264: lostpeer()
265: {
266:
1.17 millert 267: alarmtimer(0);
1.1 deraadt 268: if (connected) {
269: if (cout != NULL) {
1.18 ! millert 270: (void)shutdown(fileno(cout), 1+1);
! 271: (void)fclose(cout);
1.1 deraadt 272: cout = NULL;
273: }
274: if (data >= 0) {
1.18 ! millert 275: (void)shutdown(data, 1+1);
! 276: (void)close(data);
1.1 deraadt 277: data = -1;
278: }
279: connected = 0;
280: }
281: pswitch(1);
282: if (connected) {
283: if (cout != NULL) {
1.18 ! millert 284: (void)shutdown(fileno(cout), 1+1);
! 285: (void)fclose(cout);
1.1 deraadt 286: cout = NULL;
287: }
288: connected = 0;
289: }
290: proxflag = 0;
291: pswitch(0);
292: }
293:
294: /*
1.17 millert 295: * Generate a prompt
296: */
1.1 deraadt 297: char *
1.17 millert 298: prompt()
1.1 deraadt 299: {
1.17 millert 300: return ("ftp> ");
1.1 deraadt 301: }
302:
303: /*
304: * Command parser.
305: */
306: void
307: cmdscanner(top)
308: int top;
309: {
310: struct cmd *c;
1.17 millert 311: int num;
1.1 deraadt 312:
1.17 millert 313: if (!top
314: #ifndef SMALLFTP
315: && !editing
316: #endif /* !SMALLFTP */
317: )
1.18 ! millert 318: (void)putchar('\n');
1.1 deraadt 319: for (;;) {
1.17 millert 320: #ifndef SMALLFTP
321: if (!editing) {
322: #endif /* !SMALLFTP */
323: if (fromatty) {
1.18 ! millert 324: fputs(prompt(), stdout);
! 325: (void)fflush(stdout);
1.17 millert 326: }
327: if (fgets(line, sizeof(line), stdin) == NULL)
328: quit(0, 0);
329: num = strlen(line);
330: if (num == 0)
331: break;
332: if (line[--num] == '\n') {
333: if (num == 0)
334: break;
335: line[num] = '\0';
336: } else if (num == sizeof(line) - 2) {
1.18 ! millert 337: puts("sorry, input line too long");
1.17 millert 338: while ((num = getchar()) != '\n' && num != EOF)
339: /* void */;
340: break;
341: } /* else it was a line without a newline */
342: #ifndef SMALLFTP
343: } else {
344: const char *buf;
345: cursor_pos = NULL;
346:
347: if ((buf = el_gets(el, &num)) == NULL || num == 0)
348: quit(0, 0);
349: if (line[--num] == '\n') {
350: if (num == 0)
351: break;
352: } else if (num >= sizeof(line)) {
1.18 ! millert 353: puts("sorry, input line too long");
1.1 deraadt 354: break;
1.17 millert 355: }
356: memcpy(line, buf, num);
357: line[num] = '\0';
358: history(hist, H_ENTER, buf);
359: }
360: #endif /* !SMALLFTP */
361:
1.1 deraadt 362: makeargv();
1.17 millert 363: if (margc == 0)
1.1 deraadt 364: continue;
1.17 millert 365: #if 0 && !defined(SMALLFTP) /* XXX: don't want el_parse */
366: /*
367: * el_parse returns -1 to signal that it's not been handled
368: * internally.
369: */
370: if (el_parse(el, margc, margv) != -1)
371: continue;
372: #endif /* !SMALLFTP */
1.1 deraadt 373: c = getcmd(margv[0]);
374: if (c == (struct cmd *)-1) {
1.18 ! millert 375: puts("?Ambiguous command");
1.1 deraadt 376: continue;
377: }
378: if (c == 0) {
1.18 ! millert 379: puts("?Invalid command");
1.1 deraadt 380: continue;
381: }
382: if (c->c_conn && !connected) {
1.18 ! millert 383: puts("Not connected.");
1.1 deraadt 384: continue;
385: }
1.17 millert 386: confirmrest = 0;
1.1 deraadt 387: (*c->c_handler)(margc, margv);
388: if (bell && c->c_bell)
1.18 ! millert 389: (void)putchar('\007');
1.1 deraadt 390: if (c->c_handler != help)
391: break;
392: }
1.18 ! millert 393: (void)signal(SIGINT, intr);
! 394: (void)signal(SIGPIPE, lostpeer);
1.1 deraadt 395: }
396:
397: struct cmd *
398: getcmd(name)
1.17 millert 399: const char *name;
1.1 deraadt 400: {
1.17 millert 401: const char *p, *q;
1.1 deraadt 402: struct cmd *c, *found;
403: int nmatches, longest;
1.2 deraadt 404:
405: if (name == NULL)
406: return (0);
1.1 deraadt 407:
408: longest = 0;
409: nmatches = 0;
410: found = 0;
1.17 millert 411: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 412: for (q = name; *q == *p++; q++)
413: if (*q == 0) /* exact match? */
414: return (c);
415: if (!*q) { /* the name was a prefix */
416: if (q - name > longest) {
417: longest = q - name;
418: nmatches = 1;
419: found = c;
420: } else if (q - name == longest)
421: nmatches++;
422: }
423: }
424: if (nmatches > 1)
425: return ((struct cmd *)-1);
426: return (found);
427: }
428:
429: /*
430: * Slice a string up into argc/argv.
431: */
432:
433: int slrflag;
434:
435: void
436: makeargv()
437: {
1.17 millert 438: char *argp;
1.1 deraadt 439:
440: stringbase = line; /* scan from first of buffer */
441: argbase = argbuf; /* store from first of buffer */
442: slrflag = 0;
1.17 millert 443: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 444: for (margc = 0; ; margc++) {
1.17 millert 445: argp = slurpstring();
446: sl_add(marg_sl, argp);
447: if (argp == NULL)
1.1 deraadt 448: break;
449: }
1.17 millert 450: #ifndef SMALLFTP
451: if (cursor_pos == line) {
452: cursor_argc = 0;
453: cursor_argo = 0;
454: } else if (cursor_pos != NULL) {
455: cursor_argc = margc;
456: cursor_argo = strlen(margv[margc-1]);
457: }
458: #endif /* !SMALLFTP */
459: }
1.1 deraadt 460:
1.17 millert 461: #ifdef SMALLFTP
462: #define INC_CHKCURSOR(x) (x)++
463: #else /* !SMALLFTP */
464: #define INC_CHKCURSOR(x) { (x)++ ; \
465: if (x == cursor_pos) { \
466: cursor_argc = margc; \
467: cursor_argo = ap-argbase; \
468: cursor_pos = NULL; \
469: } }
470:
471: #endif /* !SMALLFTP */
1.1 deraadt 472:
473: /*
474: * Parse string into argbuf;
475: * implemented with FSM to
476: * handle quoting and strings
477: */
478: char *
479: slurpstring()
480: {
481: int got_one = 0;
482: char *sb = stringbase;
483: char *ap = argbase;
484: char *tmp = argbase; /* will return this if token found */
485:
486: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
487: switch (slrflag) { /* and $ as token for macro invoke */
488: case 0:
489: slrflag++;
1.17 millert 490: INC_CHKCURSOR(stringbase);
1.1 deraadt 491: return ((*sb == '!') ? "!" : "$");
492: /* NOTREACHED */
493: case 1:
494: slrflag++;
495: altarg = stringbase;
496: break;
497: default:
498: break;
499: }
500: }
501:
502: S0:
503: switch (*sb) {
504:
505: case '\0':
506: goto OUT;
507:
508: case ' ':
509: case '\t':
1.17 millert 510: INC_CHKCURSOR(sb);
511: goto S0;
1.1 deraadt 512:
513: default:
514: switch (slrflag) {
515: case 0:
516: slrflag++;
517: break;
518: case 1:
519: slrflag++;
520: altarg = sb;
521: break;
522: default:
523: break;
524: }
525: goto S1;
526: }
527:
528: S1:
529: switch (*sb) {
530:
531: case ' ':
532: case '\t':
533: case '\0':
534: goto OUT; /* end of token */
535:
536: case '\\':
1.17 millert 537: INC_CHKCURSOR(sb);
538: goto S2; /* slurp next character */
1.1 deraadt 539:
540: case '"':
1.17 millert 541: INC_CHKCURSOR(sb);
542: goto S3; /* slurp quoted string */
1.1 deraadt 543:
544: default:
1.17 millert 545: *ap = *sb; /* add character to token */
546: ap++;
547: INC_CHKCURSOR(sb);
1.1 deraadt 548: got_one = 1;
549: goto S1;
550: }
551:
552: S2:
553: switch (*sb) {
554:
555: case '\0':
556: goto OUT;
557:
558: default:
1.17 millert 559: *ap = *sb;
560: ap++;
561: INC_CHKCURSOR(sb);
1.1 deraadt 562: got_one = 1;
563: goto S1;
564: }
565:
566: S3:
567: switch (*sb) {
568:
569: case '\0':
570: goto OUT;
571:
572: case '"':
1.17 millert 573: INC_CHKCURSOR(sb);
574: goto S1;
1.1 deraadt 575:
576: default:
1.17 millert 577: *ap = *sb;
578: ap++;
579: INC_CHKCURSOR(sb);
1.1 deraadt 580: got_one = 1;
581: goto S3;
582: }
583:
584: OUT:
585: if (got_one)
586: *ap++ = '\0';
587: argbase = ap; /* update storage pointer */
588: stringbase = sb; /* update scan pointer */
589: if (got_one) {
590: return (tmp);
591: }
592: switch (slrflag) {
593: case 0:
594: slrflag++;
595: break;
596: case 1:
597: slrflag++;
598: altarg = (char *) 0;
599: break;
600: default:
601: break;
602: }
603: return ((char *)0);
604: }
605:
606: /*
607: * Help command.
608: * Call each command handler with argc == 0 and argv[0] == name.
609: */
610: void
611: help(argc, argv)
612: int argc;
613: char *argv[];
614: {
615: struct cmd *c;
616:
617: if (argc == 1) {
1.17 millert 618: StringList *buf;
1.1 deraadt 619:
1.17 millert 620: buf = sl_init();
621: printf("%sommands may be abbreviated. Commands are:\n\n",
622: proxy ? "Proxy c" : "C");
623: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
624: if (c->c_name && (!proxy || c->c_proxy))
625: sl_add(buf, c->c_name);
626: list_vertical(buf);
627: sl_free(buf, 0);
1.1 deraadt 628: return;
629: }
1.17 millert 630:
1.18 ! millert 631: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 632:
1.1 deraadt 633: while (--argc > 0) {
634: char *arg;
1.17 millert 635:
1.1 deraadt 636: arg = *++argv;
637: c = getcmd(arg);
638: if (c == (struct cmd *)-1)
639: printf("?Ambiguous help command %s\n", arg);
640: else if (c == (struct cmd *)0)
641: printf("?Invalid help command %s\n", arg);
642: else
643: printf("%-*s\t%s\n", HELPINDENT,
644: c->c_name, c->c_help);
645: }
1.17 millert 646: }
647:
648: void
649: usage()
650: {
651: (void)fprintf(stderr,
1.18 ! millert 652: "usage: %s [-adginprtvV] [host [port]]\n"
1.17 millert 653: " %s host:path[/]\n"
654: " %s ftp://host[:port]/path[/]\n"
655: " %s http://host[:port]/file\n",
656: __progname, __progname, __progname, __progname);
657: exit(1);
1.1 deraadt 658: }