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