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