Annotation of src/usr.bin/ftp/main.c, Revision 1.91
1.91 ! jsing 1: /* $OpenBSD: main.c,v 1.90 2014/07/14 05:54:12 deraadt Exp $ */
1.36 millert 2: /* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */
1.5 deraadt 3:
1.1 deraadt 4: /*
1.44 itojun 5: * Copyright (C) 1997 and 1998 WIDE Project.
6: * 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. Neither the name of the project nor the names of its contributors
17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: /*
1.1 deraadt 34: * Copyright (c) 1985, 1989, 1993, 1994
35: * The Regents of the University of California. All rights reserved.
36: *
37: * Redistribution and use in source and binary forms, with or without
38: * modification, are permitted provided that the following conditions
39: * are met:
40: * 1. Redistributions of source code must retain the above copyright
41: * notice, this list of conditions and the following disclaimer.
42: * 2. Redistributions in binary form must reproduce the above copyright
43: * notice, this list of conditions and the following disclaimer in the
44: * documentation and/or other materials provided with the distribution.
1.53 millert 45: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 46: * may be used to endorse or promote products derived from this software
47: * without specific prior written permission.
48: *
49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59: * SUCH DAMAGE.
60: */
61:
62: /*
63: * FTP User Program -- Command Interface.
64: */
65: #include <sys/types.h>
66: #include <sys/socket.h>
67:
1.18 millert 68: #include <ctype.h>
1.1 deraadt 69: #include <err.h>
1.86 jca 70: #include <limits.h>
1.1 deraadt 71: #include <netdb.h>
72: #include <pwd.h>
73: #include <stdio.h>
1.39 deraadt 74: #include <errno.h>
1.18 millert 75: #include <stdlib.h>
1.1 deraadt 76: #include <string.h>
77: #include <unistd.h>
78:
1.91 ! jsing 79: #include <ressl.h>
! 80:
! 81: #include "cmds.h"
1.1 deraadt 82: #include "ftp_var.h"
83:
1.86 jca 84: #ifndef SMALL
85: char * const ssl_verify_opts[] = {
86: #define SSL_CAFILE 0
87: "cafile",
88: #define SSL_CAPATH 1
89: "capath",
90: #define SSL_CIPHERS 2
91: "ciphers",
92: #define SSL_DONTVERIFY 3
93: "dont",
94: #define SSL_DOVERIFY 4
95: "do",
96: #define SSL_VERIFYDEPTH 5
97: "depth",
98: NULL
99: };
1.91 ! jsing 100:
! 101: struct ressl_config *ressl_config;
1.86 jca 102: #endif /* !SMALL */
103:
1.49 deraadt 104: int family = PF_UNSPEC;
1.81 halex 105: int pipeout;
1.49 deraadt 106:
1.1 deraadt 107: int
1.54 deraadt 108: main(volatile int argc, char *argv[])
1.1 deraadt 109: {
1.34 millert 110: int ch, top, rval;
1.1 deraadt 111: struct passwd *pw = NULL;
1.44 itojun 112: char *cp, homedir[MAXPATHLEN];
1.38 millert 113: char *outfile = NULL;
1.62 tedu 114: const char *errstr;
1.28 millert 115: int dumb_terminal = 0;
1.91 ! jsing 116: #ifndef SMALL
! 117: long long depth;
! 118: #endif
1.1 deraadt 119:
1.44 itojun 120: ftpport = "ftp";
121: httpport = "http";
1.61 deraadt 122: #ifndef SMALL
123: httpsport = "https";
1.69 martynas 124: #endif /* !SMALL */
1.44 itojun 125: gateport = getenv("FTPSERVERPORT");
1.51 millert 126: if (gateport == NULL || *gateport == '\0')
1.44 itojun 127: gateport = "ftpgate";
1.1 deraadt 128: doglob = 1;
129: interactive = 1;
130: autologin = 1;
1.37 millert 131: passivemode = 1;
132: activefallback = 1;
1.17 millert 133: preserve = 1;
134: verbose = 0;
135: progress = 0;
1.36 millert 136: gatemode = 0;
1.25 millert 137: #ifndef SMALL
1.22 millert 138: editing = 0;
1.28 millert 139: el = NULL;
140: hist = NULL;
1.63 pyr 141: cookiefile = NULL;
1.67 martynas 142: resume = 0;
1.82 haesbaer 143: srcaddr = NULL;
1.77 martynas 144: marg_sl = sl_init();
1.69 martynas 145: #endif /* !SMALL */
1.8 kstailey 146: mark = HASHBYTES;
1.47 itojun 147: #ifdef INET6
148: epsv4 = 1;
149: #else
150: epsv4 = 0;
151: #endif
152: epsv4bad = 0;
1.1 deraadt 153:
1.37 millert 154: /* Set default operation mode based on FTPMODE environment variable */
1.51 millert 155: if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
1.37 millert 156: if (strcmp(cp, "passive") == 0) {
157: passivemode = 1;
158: activefallback = 0;
159: } else if (strcmp(cp, "active") == 0) {
160: passivemode = 0;
161: activefallback = 0;
162: } else if (strcmp(cp, "gate") == 0) {
163: gatemode = 1;
164: } else if (strcmp(cp, "auto") == 0) {
165: passivemode = 1;
166: activefallback = 1;
167: } else
168: warnx("unknown FTPMODE: %s. Using defaults", cp);
169: }
170:
171: if (strcmp(__progname, "gate-ftp") == 0)
1.36 millert 172: gatemode = 1;
173: gateserver = getenv("FTPSERVER");
174: if (gateserver == NULL || *gateserver == '\0')
175: gateserver = GATE_SERVER;
176: if (gatemode) {
177: if (*gateserver == '\0') {
178: warnx(
1.37 millert 179: "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
1.36 millert 180: gatemode = 0;
181: }
182: }
1.17 millert 183:
1.31 millert 184: cp = getenv("TERM");
1.51 millert 185: dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
1.31 millert 186: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
1.17 millert 187: fromatty = isatty(fileno(stdin));
1.22 millert 188: if (fromatty) {
1.17 millert 189: verbose = 1; /* verbose if from a tty */
1.25 millert 190: #ifndef SMALL
1.28 millert 191: if (!dumb_terminal)
1.34 millert 192: editing = 1; /* editing mode on if tty is usable */
1.69 martynas 193: #endif /* !SMALL */
1.22 millert 194: }
1.30 deraadt 195:
196: ttyout = stdout;
1.33 millert 197: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
1.34 millert 198: progress = 1; /* progress bar on if tty is usable */
1.32 deraadt 199:
1.63 pyr 200: #ifndef SMALL
201: cookiefile = getenv("http_cookies");
1.69 martynas 202: #endif /* !SMALL */
1.88 lteo 203: httpuseragent = NULL;
1.63 pyr 204:
1.86 jca 205: while ((ch = getopt(argc, argv,
1.88 lteo 206: "46AaCc:dD:Eegik:mno:pP:r:S:s:tU:vV")) != -1) {
1.1 deraadt 207: switch (ch) {
1.49 deraadt 208: case '4':
209: family = PF_INET;
210: break;
211: case '6':
212: family = PF_INET6;
213: break;
1.37 millert 214: case 'A':
215: activefallback = 0;
216: passivemode = 0;
217: break;
218:
1.17 millert 219: case 'a':
220: anonftp = 1;
221: break;
222:
1.67 martynas 223: case 'C':
224: #ifndef SMALL
225: resume = 1;
1.69 martynas 226: #endif /* !SMALL */
1.67 martynas 227: break;
228:
1.63 pyr 229: case 'c':
230: #ifndef SMALL
231: cookiefile = optarg;
1.69 martynas 232: #endif /* !SMALL */
1.63 pyr 233: break;
234:
1.87 deraadt 235: case 'D':
236: action = optarg;
237: break;
1.1 deraadt 238: case 'd':
1.70 martynas 239: #ifndef SMALL
1.1 deraadt 240: options |= SO_DEBUG;
241: debug++;
1.70 martynas 242: #endif /* !SMALL */
1.1 deraadt 243: break;
1.17 millert 244:
1.59 fgsch 245: case 'E':
246: epsv4 = 0;
247: break;
248:
1.28 millert 249: case 'e':
250: #ifndef SMALL
1.22 millert 251: editing = 0;
1.69 martynas 252: #endif /* !SMALL */
1.21 kstailey 253: break;
254:
1.1 deraadt 255: case 'g':
256: doglob = 0;
257: break;
258:
259: case 'i':
260: interactive = 0;
1.41 millert 261: break;
262:
1.65 espie 263: case 'k':
264: keep_alive_timeout = strtonum(optarg, 0, INT_MAX,
265: &errstr);
266: if (errstr != NULL) {
267: warnx("keep alive amount is %s: %s", errstr,
268: optarg);
269: usage();
270: }
271: break;
1.41 millert 272: case 'm':
1.42 millert 273: progress = -1;
1.1 deraadt 274: break;
275:
276: case 'n':
277: autologin = 0;
278: break;
279:
1.38 millert 280: case 'o':
281: outfile = optarg;
1.81 halex 282: if (*outfile == '\0') {
283: pipeout = 0;
284: outfile = NULL;
285: ttyout = stdout;
286: } else {
287: pipeout = strcmp(outfile, "-") == 0;
288: ttyout = pipeout ? stderr : stdout;
289: }
1.38 millert 290: break;
291:
1.3 deraadt 292: case 'p':
1.17 millert 293: passivemode = 1;
1.37 millert 294: activefallback = 0;
1.3 deraadt 295: break;
296:
1.17 millert 297: case 'P':
1.44 itojun 298: ftpport = optarg;
1.7 mickey 299: break;
300:
1.18 millert 301: case 'r':
1.62 tedu 302: retry_connect = strtonum(optarg, 0, INT_MAX, &errstr);
1.65 espie 303: if (errstr != NULL) {
304: warnx("retry amount is %s: %s", errstr,
1.62 tedu 305: optarg);
1.65 espie 306: usage();
307: }
1.18 millert 308: break;
309:
1.86 jca 310: case 'S':
311: #ifndef SMALL
1.91 ! jsing 312: if (ressl_config == NULL) {
! 313: ressl_config = ressl_config_new();
! 314: if (ressl_config == NULL)
! 315: errx(1, "ressl config failed");
! 316: }
! 317:
1.86 jca 318: cp = optarg;
319: while (*cp) {
320: char *str;
321: switch (getsubopt(&cp, ssl_verify_opts, &str)) {
322: case SSL_CAFILE:
323: if (str == NULL)
324: errx(1, "missing CA file");
1.91 ! jsing 325: ressl_config_set_ca_file(ressl_config,
! 326: str);
1.86 jca 327: break;
328: case SSL_CAPATH:
329: if (str == NULL)
330: errx(1, "missing CA directory"
331: " path");
1.91 ! jsing 332: ressl_config_set_ca_path(ressl_config,
! 333: str);
1.86 jca 334: break;
335: case SSL_CIPHERS:
336: if (str == NULL)
337: errx(1, "missing cipher list");
1.91 ! jsing 338: ressl_config_set_ciphers(ressl_config,
! 339: str);
1.86 jca 340: break;
341: case SSL_DONTVERIFY:
1.91 ! jsing 342: ressl_config_insecure_no_verify(
! 343: ressl_config);
1.86 jca 344: break;
345: case SSL_DOVERIFY:
1.91 ! jsing 346: ressl_config_verify(ressl_config);
1.86 jca 347: break;
348: case SSL_VERIFYDEPTH:
349: if (str == NULL)
350: errx(1, "missing depth");
1.91 ! jsing 351: depth = strtonum(str, 0, INT_MAX,
! 352: &errstr);
1.86 jca 353: if (errstr)
354: errx(1, "certificate "
355: "validation depth is %s",
356: errstr);
1.91 ! jsing 357: ressl_config_set_verify_depth(
! 358: ressl_config, (int)depth);
1.86 jca 359: break;
360: default:
361: errx(1, "unknown -S suboption `%s'",
362: suboptarg ? suboptarg : "");
363: /* NOTREACHED */
364: }
365: }
366: #endif
367: break;
368:
1.82 haesbaer 369: case 's':
370: #ifndef SMALL
371: srcaddr = optarg;
372: #endif /* !SMALL */
373: break;
374:
1.1 deraadt 375: case 't':
1.17 millert 376: trace = 1;
1.1 deraadt 377: break;
378:
1.89 halex 379: #ifndef SMALL
1.88 lteo 380: case 'U':
1.89 halex 381: free (httpuseragent);
1.88 lteo 382: if (strcspn(optarg, "\r\n") != strlen(optarg))
383: errx(1, "Invalid User-Agent: %s.", optarg);
384: if (asprintf(&httpuseragent, "User-Agent: %s",
385: optarg) == -1)
386: errx(1, "Can't allocate memory for HTTP(S) "
387: "User-Agent");
1.89 halex 388: break;
1.88 lteo 389: #endif /* !SMALL */
390:
1.1 deraadt 391: case 'v':
1.17 millert 392: verbose = 1;
393: break;
394:
395: case 'V':
396: verbose = 0;
1.1 deraadt 397: break;
398:
399: default:
1.17 millert 400: usage();
1.1 deraadt 401: }
402: }
403: argc -= optind;
404: argv += optind;
1.63 pyr 405:
406: #ifndef SMALL
407: cookie_load();
1.69 martynas 408: #endif /* !SMALL */
1.89 halex 409: if (httpuseragent == NULL)
410: httpuseragent = HTTP_USER_AGENT;
1.1 deraadt 411:
412: cpend = 0; /* no pending replies */
413: proxy = 0; /* proxy not active */
414: crflag = 1; /* strip c.r. on ascii gets */
415: sendport = -1; /* not using ports */
416: /*
417: * Set up the home directory in case we're globbing.
418: */
419: cp = getlogin();
420: if (cp != NULL) {
421: pw = getpwnam(cp);
422: }
423: if (pw == NULL)
424: pw = getpwuid(getuid());
425: if (pw != NULL) {
1.52 deraadt 426: (void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
1.1 deraadt 427: home = homedir;
1.3 deraadt 428: }
1.9 michaels 429:
1.17 millert 430: setttywidth(0);
1.18 millert 431: (void)signal(SIGWINCH, setttywidth);
1.34 millert 432:
1.17 millert 433: if (argc > 0) {
1.44 itojun 434: if (isurl(argv[0])) {
1.38 millert 435: rval = auto_fetch(argc, argv, outfile);
1.17 millert 436: if (rval >= 0) /* -1 == connected and cd-ed */
437: exit(rval);
438: } else {
1.77 martynas 439: #ifndef SMALL
1.3 deraadt 440: char *xargv[5];
441:
442: if (setjmp(toplevel))
443: exit(0);
1.20 millert 444: (void)signal(SIGINT, (sig_t)intr);
445: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 446: xargv[0] = __progname;
1.17 millert 447: xargv[1] = argv[0];
448: xargv[2] = argv[1];
449: xargv[3] = argv[2];
450: xargv[4] = NULL;
1.18 millert 451: do {
452: setpeer(argc+1, xargv);
453: if (!retry_connect)
454: break;
455: if (!connected) {
456: macnum = 0;
1.30 deraadt 457: fputs("Retrying...\n", ttyout);
1.18 millert 458: sleep(retry_connect);
459: }
460: } while (!connected);
1.35 mickey 461: retry_connect = 0; /* connected, stop hiding msgs */
1.77 martynas 462: #endif /* !SMALL */
1.3 deraadt 463: }
1.1 deraadt 464: }
1.28 millert 465: #ifndef SMALL
466: controlediting();
1.1 deraadt 467: top = setjmp(toplevel) == 0;
468: if (top) {
1.20 millert 469: (void)signal(SIGINT, (sig_t)intr);
470: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 471: }
472: for (;;) {
473: cmdscanner(top);
474: top = 1;
475: }
1.77 martynas 476: #else /* !SMALL */
477: usage();
478: #endif /* !SMALL */
1.1 deraadt 479: }
480:
481: void
1.58 deraadt 482: intr(void)
1.1 deraadt 483: {
1.90 deraadt 484: int save_errno = errno;
1.1 deraadt 485:
1.90 deraadt 486: write(fileno(ttyout), "\n\r", 2);
1.17 millert 487: alarmtimer(0);
1.90 deraadt 488:
489: errno = save_errno;
1.1 deraadt 490: longjmp(toplevel, 1);
491: }
492:
493: void
1.58 deraadt 494: lostpeer(void)
1.1 deraadt 495: {
1.39 deraadt 496: int save_errno = errno;
1.1 deraadt 497:
1.17 millert 498: alarmtimer(0);
1.1 deraadt 499: if (connected) {
500: if (cout != NULL) {
1.66 moritz 501: (void)shutdown(fileno(cout), SHUT_RDWR);
1.18 millert 502: (void)fclose(cout);
1.1 deraadt 503: cout = NULL;
504: }
505: if (data >= 0) {
1.66 moritz 506: (void)shutdown(data, SHUT_RDWR);
1.18 millert 507: (void)close(data);
1.1 deraadt 508: data = -1;
509: }
510: connected = 0;
511: }
512: pswitch(1);
513: if (connected) {
514: if (cout != NULL) {
1.66 moritz 515: (void)shutdown(fileno(cout), SHUT_RDWR);
1.18 millert 516: (void)fclose(cout);
1.1 deraadt 517: cout = NULL;
518: }
519: connected = 0;
520: }
521: proxflag = 0;
522: pswitch(0);
1.39 deraadt 523: errno = save_errno;
1.1 deraadt 524: }
525:
1.77 martynas 526: #ifndef SMALL
1.1 deraadt 527: /*
1.17 millert 528: * Generate a prompt
529: */
1.1 deraadt 530: char *
1.58 deraadt 531: prompt(void)
1.1 deraadt 532: {
1.17 millert 533: return ("ftp> ");
1.1 deraadt 534: }
535:
536: /*
537: * Command parser.
538: */
539: void
1.58 deraadt 540: cmdscanner(int top)
1.1 deraadt 541: {
542: struct cmd *c;
1.17 millert 543: int num;
1.55 otto 544: HistEvent hev;
1.1 deraadt 545:
1.77 martynas 546: if (!top && !editing)
1.30 deraadt 547: (void)putc('\n', ttyout);
1.1 deraadt 548: for (;;) {
1.17 millert 549: if (!editing) {
550: if (fromatty) {
1.30 deraadt 551: fputs(prompt(), ttyout);
552: (void)fflush(ttyout);
1.17 millert 553: }
554: if (fgets(line, sizeof(line), stdin) == NULL)
555: quit(0, 0);
556: num = strlen(line);
557: if (num == 0)
558: break;
559: if (line[--num] == '\n') {
560: if (num == 0)
561: break;
562: line[num] = '\0';
563: } else if (num == sizeof(line) - 2) {
1.30 deraadt 564: fputs("sorry, input line too long.\n", ttyout);
1.17 millert 565: while ((num = getchar()) != '\n' && num != EOF)
566: /* void */;
567: break;
568: } /* else it was a line without a newline */
569: } else {
570: const char *buf;
571: cursor_pos = NULL;
572:
1.90 deraadt 573: if ((buf = el_gets(el, &num)) == NULL || num == 0) {
574: putc('\n', ttyout);
575: fflush(ttyout);
1.17 millert 576: quit(0, 0);
1.90 deraadt 577: }
1.46 fgsch 578: if (buf[--num] == '\n') {
1.17 millert 579: if (num == 0)
580: break;
1.46 fgsch 581: }
582: if (num >= sizeof(line)) {
1.30 deraadt 583: fputs("sorry, input line too long.\n", ttyout);
1.1 deraadt 584: break;
1.17 millert 585: }
1.34 millert 586: memcpy(line, buf, (size_t)num);
1.17 millert 587: line[num] = '\0';
1.55 otto 588: history(hist, &hev, H_ENTER, buf);
1.17 millert 589: }
590:
1.1 deraadt 591: makeargv();
1.17 millert 592: if (margc == 0)
1.1 deraadt 593: continue;
594: c = getcmd(margv[0]);
595: if (c == (struct cmd *)-1) {
1.30 deraadt 596: fputs("?Ambiguous command.\n", ttyout);
1.1 deraadt 597: continue;
598: }
599: if (c == 0) {
1.24 millert 600: /*
601: * Give editline(3) a shot at unknown commands.
602: * XXX - bogus commands with a colon in
603: * them will not elicit an error.
604: */
1.45 markus 605: if (editing &&
1.55 otto 606: el_parse(el, margc, (const char **)margv) != 0)
1.30 deraadt 607: fputs("?Invalid command.\n", ttyout);
1.1 deraadt 608: continue;
609: }
610: if (c->c_conn && !connected) {
1.30 deraadt 611: fputs("Not connected.\n", ttyout);
1.1 deraadt 612: continue;
613: }
1.17 millert 614: confirmrest = 0;
1.1 deraadt 615: (*c->c_handler)(margc, margv);
616: if (bell && c->c_bell)
1.30 deraadt 617: (void)putc('\007', ttyout);
1.1 deraadt 618: if (c->c_handler != help)
619: break;
620: }
1.20 millert 621: (void)signal(SIGINT, (sig_t)intr);
622: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 623: }
624:
625: struct cmd *
1.58 deraadt 626: getcmd(const char *name)
1.1 deraadt 627: {
1.17 millert 628: const char *p, *q;
1.1 deraadt 629: struct cmd *c, *found;
630: int nmatches, longest;
1.2 deraadt 631:
632: if (name == NULL)
633: return (0);
1.1 deraadt 634:
635: longest = 0;
636: nmatches = 0;
637: found = 0;
1.17 millert 638: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 639: for (q = name; *q == *p++; q++)
640: if (*q == 0) /* exact match? */
641: return (c);
642: if (!*q) { /* the name was a prefix */
643: if (q - name > longest) {
644: longest = q - name;
645: nmatches = 1;
646: found = c;
647: } else if (q - name == longest)
648: nmatches++;
649: }
650: }
651: if (nmatches > 1)
652: return ((struct cmd *)-1);
653: return (found);
654: }
655:
656: /*
657: * Slice a string up into argc/argv.
658: */
659:
660: int slrflag;
661:
662: void
1.58 deraadt 663: makeargv(void)
1.1 deraadt 664: {
1.17 millert 665: char *argp;
1.1 deraadt 666:
667: stringbase = line; /* scan from first of buffer */
668: argbase = argbuf; /* store from first of buffer */
669: slrflag = 0;
1.17 millert 670: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 671: for (margc = 0; ; margc++) {
1.17 millert 672: argp = slurpstring();
673: sl_add(marg_sl, argp);
674: if (argp == NULL)
1.1 deraadt 675: break;
676: }
1.17 millert 677: if (cursor_pos == line) {
678: cursor_argc = 0;
679: cursor_argo = 0;
680: } else if (cursor_pos != NULL) {
681: cursor_argc = margc;
682: cursor_argo = strlen(margv[margc-1]);
683: }
684: }
1.1 deraadt 685:
1.17 millert 686: #define INC_CHKCURSOR(x) { (x)++ ; \
687: if (x == cursor_pos) { \
688: cursor_argc = margc; \
689: cursor_argo = ap-argbase; \
690: cursor_pos = NULL; \
691: } }
1.1 deraadt 692:
693: /*
694: * Parse string into argbuf;
695: * implemented with FSM to
696: * handle quoting and strings
697: */
698: char *
1.58 deraadt 699: slurpstring(void)
1.1 deraadt 700: {
701: int got_one = 0;
702: char *sb = stringbase;
703: char *ap = argbase;
704: char *tmp = argbase; /* will return this if token found */
705:
706: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
707: switch (slrflag) { /* and $ as token for macro invoke */
708: case 0:
709: slrflag++;
1.17 millert 710: INC_CHKCURSOR(stringbase);
1.1 deraadt 711: return ((*sb == '!') ? "!" : "$");
712: /* NOTREACHED */
713: case 1:
714: slrflag++;
715: altarg = stringbase;
716: break;
717: default:
718: break;
719: }
720: }
721:
722: S0:
723: switch (*sb) {
724:
725: case '\0':
726: goto OUT;
727:
728: case ' ':
729: case '\t':
1.17 millert 730: INC_CHKCURSOR(sb);
731: goto S0;
1.1 deraadt 732:
733: default:
734: switch (slrflag) {
735: case 0:
736: slrflag++;
737: break;
738: case 1:
739: slrflag++;
740: altarg = sb;
741: break;
742: default:
743: break;
744: }
745: goto S1;
746: }
747:
748: S1:
749: switch (*sb) {
750:
751: case ' ':
752: case '\t':
753: case '\0':
754: goto OUT; /* end of token */
755:
756: case '\\':
1.17 millert 757: INC_CHKCURSOR(sb);
758: goto S2; /* slurp next character */
1.1 deraadt 759:
760: case '"':
1.17 millert 761: INC_CHKCURSOR(sb);
762: goto S3; /* slurp quoted string */
1.1 deraadt 763:
764: default:
1.17 millert 765: *ap = *sb; /* add character to token */
766: ap++;
767: INC_CHKCURSOR(sb);
1.1 deraadt 768: got_one = 1;
769: goto S1;
770: }
771:
772: S2:
773: switch (*sb) {
774:
775: case '\0':
776: goto OUT;
777:
778: default:
1.17 millert 779: *ap = *sb;
780: ap++;
781: INC_CHKCURSOR(sb);
1.1 deraadt 782: got_one = 1;
783: goto S1;
784: }
785:
786: S3:
787: switch (*sb) {
788:
789: case '\0':
790: goto OUT;
791:
792: case '"':
1.17 millert 793: INC_CHKCURSOR(sb);
794: goto S1;
1.1 deraadt 795:
796: default:
1.17 millert 797: *ap = *sb;
798: ap++;
799: INC_CHKCURSOR(sb);
1.1 deraadt 800: got_one = 1;
801: goto S3;
802: }
803:
804: OUT:
805: if (got_one)
806: *ap++ = '\0';
807: argbase = ap; /* update storage pointer */
808: stringbase = sb; /* update scan pointer */
809: if (got_one) {
810: return (tmp);
811: }
812: switch (slrflag) {
813: case 0:
814: slrflag++;
815: break;
816: case 1:
817: slrflag++;
818: altarg = (char *) 0;
819: break;
820: default:
821: break;
822: }
823: return ((char *)0);
824: }
825:
826: /*
827: * Help command.
828: * Call each command handler with argc == 0 and argv[0] == name.
829: */
830: void
1.58 deraadt 831: help(int argc, char *argv[])
1.1 deraadt 832: {
833: struct cmd *c;
834:
835: if (argc == 1) {
1.17 millert 836: StringList *buf;
1.1 deraadt 837:
1.17 millert 838: buf = sl_init();
1.30 deraadt 839: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1.17 millert 840: proxy ? "Proxy c" : "C");
841: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
842: if (c->c_name && (!proxy || c->c_proxy))
843: sl_add(buf, c->c_name);
844: list_vertical(buf);
845: sl_free(buf, 0);
1.1 deraadt 846: return;
847: }
1.17 millert 848:
1.18 millert 849: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 850:
1.1 deraadt 851: while (--argc > 0) {
852: char *arg;
1.17 millert 853:
1.1 deraadt 854: arg = *++argv;
855: c = getcmd(arg);
856: if (c == (struct cmd *)-1)
1.30 deraadt 857: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1.1 deraadt 858: else if (c == (struct cmd *)0)
1.30 deraadt 859: fprintf(ttyout, "?Invalid help command %s\n", arg);
1.1 deraadt 860: else
1.30 deraadt 861: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1.1 deraadt 862: c->c_name, c->c_help);
863: }
1.17 millert 864: }
1.77 martynas 865: #endif /* !SMALL */
1.17 millert 866:
867: void
1.58 deraadt 868: usage(void)
1.17 millert 869: {
1.77 martynas 870: (void)fprintf(stderr, "usage: %s "
1.70 martynas 871: #ifndef SMALL
1.87 deraadt 872: "[-46AadEegimnptVv] [-D title] [-k seconds] [-P port] "
873: "[-r seconds]\n"
874: " [-s srcaddr] [host [port]]\n"
1.77 martynas 875: " %s [-C] "
1.70 martynas 876: #endif /* !SMALL */
877: "[-o output] "
1.82 haesbaer 878: #ifndef SMALL
1.83 lteo 879: "[-s srcaddr]\n"
880: " "
1.82 haesbaer 881: #endif /* !SMALL */
1.79 martynas 882: "ftp://[user:password@]host[:port]/file[/] ...\n"
1.70 martynas 883: " %s "
884: #ifndef SMALL
885: "[-C] [-c cookie] "
886: #endif /* !SMALL */
887: "[-o output] "
1.82 haesbaer 888: #ifndef SMALL
1.86 jca 889: "[-S ssl_options] "
1.84 haesbaer 890: "[-s srcaddr]\n"
1.88 lteo 891: " [-U useragent] "
1.82 haesbaer 892: #endif /* !SMALL */
1.85 lteo 893: "http"
1.61 deraadt 894: #ifndef SMALL
1.85 lteo 895: "[s]"
896: #endif
897: "://"
898: #ifndef SMALL
899: "[user:password@]"
900: #endif
901: "host[:port]/file ...\n"
1.70 martynas 902: " %s "
903: #ifndef SMALL
904: "[-C] "
905: #endif /* !SMALL */
1.80 sobrado 906: "[-o output] "
1.82 haesbaer 907: #ifndef SMALL
908: "[-s srcaddr] "
909: #endif /* !SMALL */
1.80 sobrado 910: "file:file ...\n"
911: " %s "
912: #ifndef SMALL
913: "[-C] "
914: #endif /* !SMALL */
1.82 haesbaer 915: "[-o output] "
916: #ifndef SMALL
917: "[-s srcaddr] "
918: #endif /* !SMALL */
919: "host:/file[/] ...\n",
1.61 deraadt 920: #ifndef SMALL
1.85 lteo 921: __progname, __progname, __progname, __progname, __progname);
1.69 martynas 922: #else /* !SMALL */
1.80 sobrado 923: __progname, __progname, __progname, __progname);
1.69 martynas 924: #endif /* !SMALL */
1.17 millert 925: exit(1);
1.1 deraadt 926: }
1.77 martynas 927: