Annotation of src/usr.bin/ftp/main.c, Revision 1.53
1.53 ! millert 1: /* $OpenBSD: main.c,v 1.52 2003/04/05 17:19:47 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: #ifndef lint
63: static char copyright[] =
64: "@(#) Copyright (c) 1985, 1989, 1993, 1994\n\
65: The Regents of the University of California. All rights reserved.\n";
66: #endif /* not lint */
67:
68: #ifndef lint
69: #if 0
70: static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94";
71: #else
1.53 ! millert 72: static char rcsid[] = "$OpenBSD: main.c,v 1.52 2003/04/05 17:19:47 deraadt Exp $";
1.1 deraadt 73: #endif
74: #endif /* not lint */
75:
76: /*
77: * FTP User Program -- Command Interface.
78: */
79: #include <sys/types.h>
80: #include <sys/socket.h>
81:
1.18 millert 82: #include <ctype.h>
1.1 deraadt 83: #include <err.h>
84: #include <netdb.h>
85: #include <pwd.h>
86: #include <stdio.h>
1.39 deraadt 87: #include <errno.h>
1.18 millert 88: #include <stdlib.h>
1.1 deraadt 89: #include <string.h>
90: #include <unistd.h>
91:
92: #include "ftp_var.h"
93:
1.49 deraadt 94: int family = PF_UNSPEC;
95:
1.1 deraadt 96: int
97: main(argc, argv)
1.48 millert 98: volatile int argc;
99: char ** volatile argv;
1.1 deraadt 100: {
1.34 millert 101: int ch, top, rval;
1.1 deraadt 102: struct passwd *pw = NULL;
1.44 itojun 103: char *cp, homedir[MAXPATHLEN];
1.38 millert 104: char *outfile = NULL;
1.28 millert 105: int dumb_terminal = 0;
1.1 deraadt 106:
1.44 itojun 107: ftpport = "ftp";
108: httpport = "http";
109: gateport = getenv("FTPSERVERPORT");
1.51 millert 110: if (gateport == NULL || *gateport == '\0')
1.44 itojun 111: gateport = "ftpgate";
1.1 deraadt 112: doglob = 1;
113: interactive = 1;
114: autologin = 1;
1.37 millert 115: passivemode = 1;
116: activefallback = 1;
1.17 millert 117: preserve = 1;
118: verbose = 0;
119: progress = 0;
1.36 millert 120: gatemode = 0;
1.25 millert 121: #ifndef SMALL
1.22 millert 122: editing = 0;
1.28 millert 123: el = NULL;
124: hist = NULL;
1.22 millert 125: #endif
1.8 kstailey 126: mark = HASHBYTES;
1.17 millert 127: marg_sl = sl_init();
1.47 itojun 128: #ifdef INET6
129: epsv4 = 1;
130: #else
131: epsv4 = 0;
132: #endif
133: epsv4bad = 0;
1.1 deraadt 134:
1.37 millert 135: /* Set default operation mode based on FTPMODE environment variable */
1.51 millert 136: if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') {
1.37 millert 137: if (strcmp(cp, "passive") == 0) {
138: passivemode = 1;
139: activefallback = 0;
140: } else if (strcmp(cp, "active") == 0) {
141: passivemode = 0;
142: activefallback = 0;
143: } else if (strcmp(cp, "gate") == 0) {
144: gatemode = 1;
145: } else if (strcmp(cp, "auto") == 0) {
146: passivemode = 1;
147: activefallback = 1;
148: } else
149: warnx("unknown FTPMODE: %s. Using defaults", cp);
150: }
151:
152: if (strcmp(__progname, "gate-ftp") == 0)
1.36 millert 153: gatemode = 1;
154: gateserver = getenv("FTPSERVER");
155: if (gateserver == NULL || *gateserver == '\0')
156: gateserver = GATE_SERVER;
157: if (gatemode) {
158: if (*gateserver == '\0') {
159: warnx(
1.37 millert 160: "Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp");
1.36 millert 161: gatemode = 0;
162: }
163: }
1.17 millert 164:
1.31 millert 165: cp = getenv("TERM");
1.51 millert 166: dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") ||
1.31 millert 167: !strcmp(cp, "emacs") || !strcmp(cp, "su"));
1.17 millert 168: fromatty = isatty(fileno(stdin));
1.22 millert 169: if (fromatty) {
1.17 millert 170: verbose = 1; /* verbose if from a tty */
1.25 millert 171: #ifndef SMALL
1.28 millert 172: if (!dumb_terminal)
1.34 millert 173: editing = 1; /* editing mode on if tty is usable */
1.22 millert 174: #endif
175: }
1.30 deraadt 176:
177: ttyout = stdout;
1.33 millert 178: if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc())
1.34 millert 179: progress = 1; /* progress bar on if tty is usable */
1.32 deraadt 180:
1.49 deraadt 181: while ((ch = getopt(argc, argv, "46Aadegimno:pP:r:tvV")) != -1) {
1.1 deraadt 182: switch (ch) {
1.49 deraadt 183: case '4':
184: family = PF_INET;
185: break;
186: case '6':
187: family = PF_INET6;
188: break;
1.37 millert 189: case 'A':
190: activefallback = 0;
191: passivemode = 0;
192: break;
193:
1.17 millert 194: case 'a':
195: anonftp = 1;
196: break;
197:
1.1 deraadt 198: case 'd':
199: options |= SO_DEBUG;
200: debug++;
201: break;
1.17 millert 202:
1.28 millert 203: case 'e':
204: #ifndef SMALL
1.22 millert 205: editing = 0;
206: #endif
1.21 kstailey 207: break;
208:
1.1 deraadt 209: case 'g':
210: doglob = 0;
211: break;
212:
213: case 'i':
214: interactive = 0;
1.41 millert 215: break;
216:
217: case 'm':
1.42 millert 218: progress = -1;
1.1 deraadt 219: break;
220:
221: case 'n':
222: autologin = 0;
223: break;
224:
1.38 millert 225: case 'o':
226: outfile = optarg;
227: if (strcmp(outfile, "-") == 0)
228: ttyout = stderr;
229: break;
230:
1.3 deraadt 231: case 'p':
1.17 millert 232: passivemode = 1;
1.37 millert 233: activefallback = 0;
1.3 deraadt 234: break;
235:
1.17 millert 236: case 'P':
1.44 itojun 237: ftpport = optarg;
1.7 mickey 238: break;
239:
1.18 millert 240: case 'r':
241: if (isdigit(*optarg))
242: retry_connect = atoi(optarg);
243: else
244: errx(1, "-r requires numeric argument");
245: break;
246:
1.1 deraadt 247: case 't':
1.17 millert 248: trace = 1;
1.1 deraadt 249: break;
250:
251: case 'v':
1.17 millert 252: verbose = 1;
253: break;
254:
255: case 'V':
256: verbose = 0;
1.1 deraadt 257: break;
258:
259: default:
1.17 millert 260: usage();
1.1 deraadt 261: }
262: }
263: argc -= optind;
264: argv += optind;
265:
266: cpend = 0; /* no pending replies */
267: proxy = 0; /* proxy not active */
268: crflag = 1; /* strip c.r. on ascii gets */
269: sendport = -1; /* not using ports */
270: /*
271: * Set up the home directory in case we're globbing.
272: */
273: cp = getlogin();
274: if (cp != NULL) {
275: pw = getpwnam(cp);
276: }
277: if (pw == NULL)
278: pw = getpwuid(getuid());
279: if (pw != NULL) {
1.52 deraadt 280: (void)strlcpy(homedir, pw->pw_dir, sizeof homedir);
1.1 deraadt 281: home = homedir;
1.3 deraadt 282: }
1.9 michaels 283:
1.17 millert 284: setttywidth(0);
1.18 millert 285: (void)signal(SIGWINCH, setttywidth);
1.34 millert 286:
1.17 millert 287: if (argc > 0) {
1.44 itojun 288: if (isurl(argv[0])) {
1.17 millert 289: anonftp = 1; /* Handle "automatic" transfers. */
1.38 millert 290: rval = auto_fetch(argc, argv, outfile);
1.17 millert 291: if (rval >= 0) /* -1 == connected and cd-ed */
292: exit(rval);
293: } else {
1.3 deraadt 294: char *xargv[5];
295:
296: if (setjmp(toplevel))
297: exit(0);
1.20 millert 298: (void)signal(SIGINT, (sig_t)intr);
299: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.3 deraadt 300: xargv[0] = __progname;
1.17 millert 301: xargv[1] = argv[0];
302: xargv[2] = argv[1];
303: xargv[3] = argv[2];
304: xargv[4] = NULL;
1.18 millert 305: do {
306: setpeer(argc+1, xargv);
307: if (!retry_connect)
308: break;
309: if (!connected) {
310: macnum = 0;
1.30 deraadt 311: fputs("Retrying...\n", ttyout);
1.18 millert 312: sleep(retry_connect);
313: }
314: } while (!connected);
1.35 mickey 315: retry_connect = 0; /* connected, stop hiding msgs */
1.3 deraadt 316: }
1.1 deraadt 317: }
1.28 millert 318: #ifndef SMALL
319: controlediting();
320: #endif /* !SMALL */
1.1 deraadt 321: top = setjmp(toplevel) == 0;
322: if (top) {
1.20 millert 323: (void)signal(SIGINT, (sig_t)intr);
324: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 325: }
326: for (;;) {
327: cmdscanner(top);
328: top = 1;
329: }
330: }
331:
332: void
333: intr()
334: {
335:
1.17 millert 336: alarmtimer(0);
1.1 deraadt 337: longjmp(toplevel, 1);
338: }
339:
340: void
341: lostpeer()
342: {
1.39 deraadt 343: int save_errno = errno;
1.1 deraadt 344:
1.17 millert 345: alarmtimer(0);
1.1 deraadt 346: if (connected) {
347: if (cout != NULL) {
1.18 millert 348: (void)shutdown(fileno(cout), 1+1);
349: (void)fclose(cout);
1.1 deraadt 350: cout = NULL;
351: }
352: if (data >= 0) {
1.18 millert 353: (void)shutdown(data, 1+1);
354: (void)close(data);
1.1 deraadt 355: data = -1;
356: }
357: connected = 0;
358: }
359: pswitch(1);
360: if (connected) {
361: if (cout != NULL) {
1.18 millert 362: (void)shutdown(fileno(cout), 1+1);
363: (void)fclose(cout);
1.1 deraadt 364: cout = NULL;
365: }
366: connected = 0;
367: }
368: proxflag = 0;
369: pswitch(0);
1.39 deraadt 370: errno = save_errno;
1.1 deraadt 371: }
372:
373: /*
1.17 millert 374: * Generate a prompt
375: */
1.1 deraadt 376: char *
1.17 millert 377: prompt()
1.1 deraadt 378: {
1.17 millert 379: return ("ftp> ");
1.1 deraadt 380: }
381:
382: /*
383: * Command parser.
384: */
385: void
386: cmdscanner(top)
387: int top;
388: {
389: struct cmd *c;
1.17 millert 390: int num;
1.1 deraadt 391:
1.17 millert 392: if (!top
1.25 millert 393: #ifndef SMALL
1.17 millert 394: && !editing
1.25 millert 395: #endif /* !SMALL */
1.17 millert 396: )
1.30 deraadt 397: (void)putc('\n', ttyout);
1.1 deraadt 398: for (;;) {
1.25 millert 399: #ifndef SMALL
1.17 millert 400: if (!editing) {
1.25 millert 401: #endif /* !SMALL */
1.17 millert 402: if (fromatty) {
1.30 deraadt 403: fputs(prompt(), ttyout);
404: (void)fflush(ttyout);
1.17 millert 405: }
406: if (fgets(line, sizeof(line), stdin) == NULL)
407: quit(0, 0);
408: num = strlen(line);
409: if (num == 0)
410: break;
411: if (line[--num] == '\n') {
412: if (num == 0)
413: break;
414: line[num] = '\0';
415: } else if (num == sizeof(line) - 2) {
1.30 deraadt 416: fputs("sorry, input line too long.\n", ttyout);
1.17 millert 417: while ((num = getchar()) != '\n' && num != EOF)
418: /* void */;
419: break;
420: } /* else it was a line without a newline */
1.25 millert 421: #ifndef SMALL
1.17 millert 422: } else {
423: const char *buf;
424: cursor_pos = NULL;
425:
426: if ((buf = el_gets(el, &num)) == NULL || num == 0)
427: quit(0, 0);
1.46 fgsch 428: if (buf[--num] == '\n') {
1.17 millert 429: if (num == 0)
430: break;
1.46 fgsch 431: }
432: if (num >= sizeof(line)) {
1.30 deraadt 433: fputs("sorry, input line too long.\n", ttyout);
1.1 deraadt 434: break;
1.17 millert 435: }
1.34 millert 436: memcpy(line, buf, (size_t)num);
1.17 millert 437: line[num] = '\0';
438: history(hist, H_ENTER, buf);
439: }
1.25 millert 440: #endif /* !SMALL */
1.17 millert 441:
1.1 deraadt 442: makeargv();
1.17 millert 443: if (margc == 0)
1.1 deraadt 444: continue;
445: c = getcmd(margv[0]);
446: if (c == (struct cmd *)-1) {
1.30 deraadt 447: fputs("?Ambiguous command.\n", ttyout);
1.1 deraadt 448: continue;
449: }
450: if (c == 0) {
1.25 millert 451: #ifndef SMALL
1.24 millert 452: /*
453: * Give editline(3) a shot at unknown commands.
454: * XXX - bogus commands with a colon in
455: * them will not elicit an error.
456: */
1.45 markus 457: if (editing &&
458: el_parse(el, margc, margv) != 0)
1.25 millert 459: #endif /* !SMALL */
1.30 deraadt 460: fputs("?Invalid command.\n", ttyout);
1.1 deraadt 461: continue;
462: }
463: if (c->c_conn && !connected) {
1.30 deraadt 464: fputs("Not connected.\n", ttyout);
1.1 deraadt 465: continue;
466: }
1.17 millert 467: confirmrest = 0;
1.1 deraadt 468: (*c->c_handler)(margc, margv);
469: if (bell && c->c_bell)
1.30 deraadt 470: (void)putc('\007', ttyout);
1.1 deraadt 471: if (c->c_handler != help)
472: break;
473: }
1.20 millert 474: (void)signal(SIGINT, (sig_t)intr);
475: (void)signal(SIGPIPE, (sig_t)lostpeer);
1.1 deraadt 476: }
477:
478: struct cmd *
479: getcmd(name)
1.17 millert 480: const char *name;
1.1 deraadt 481: {
1.17 millert 482: const char *p, *q;
1.1 deraadt 483: struct cmd *c, *found;
484: int nmatches, longest;
1.2 deraadt 485:
486: if (name == NULL)
487: return (0);
1.1 deraadt 488:
489: longest = 0;
490: nmatches = 0;
491: found = 0;
1.17 millert 492: for (c = cmdtab; (p = c->c_name) != NULL; c++) {
1.1 deraadt 493: for (q = name; *q == *p++; q++)
494: if (*q == 0) /* exact match? */
495: return (c);
496: if (!*q) { /* the name was a prefix */
497: if (q - name > longest) {
498: longest = q - name;
499: nmatches = 1;
500: found = c;
501: } else if (q - name == longest)
502: nmatches++;
503: }
504: }
505: if (nmatches > 1)
506: return ((struct cmd *)-1);
507: return (found);
508: }
509:
510: /*
511: * Slice a string up into argc/argv.
512: */
513:
514: int slrflag;
515:
516: void
517: makeargv()
518: {
1.17 millert 519: char *argp;
1.1 deraadt 520:
521: stringbase = line; /* scan from first of buffer */
522: argbase = argbuf; /* store from first of buffer */
523: slrflag = 0;
1.17 millert 524: marg_sl->sl_cur = 0; /* reset to start of marg_sl */
1.1 deraadt 525: for (margc = 0; ; margc++) {
1.17 millert 526: argp = slurpstring();
527: sl_add(marg_sl, argp);
528: if (argp == NULL)
1.1 deraadt 529: break;
530: }
1.25 millert 531: #ifndef SMALL
1.17 millert 532: if (cursor_pos == line) {
533: cursor_argc = 0;
534: cursor_argo = 0;
535: } else if (cursor_pos != NULL) {
536: cursor_argc = margc;
537: cursor_argo = strlen(margv[margc-1]);
538: }
1.25 millert 539: #endif /* !SMALL */
1.17 millert 540: }
1.1 deraadt 541:
1.25 millert 542: #ifdef SMALL
1.17 millert 543: #define INC_CHKCURSOR(x) (x)++
1.25 millert 544: #else /* !SMALL */
1.17 millert 545: #define INC_CHKCURSOR(x) { (x)++ ; \
546: if (x == cursor_pos) { \
547: cursor_argc = margc; \
548: cursor_argo = ap-argbase; \
549: cursor_pos = NULL; \
550: } }
551:
1.25 millert 552: #endif /* !SMALL */
1.1 deraadt 553:
554: /*
555: * Parse string into argbuf;
556: * implemented with FSM to
557: * handle quoting and strings
558: */
559: char *
560: slurpstring()
561: {
562: int got_one = 0;
563: char *sb = stringbase;
564: char *ap = argbase;
565: char *tmp = argbase; /* will return this if token found */
566:
567: if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
568: switch (slrflag) { /* and $ as token for macro invoke */
569: case 0:
570: slrflag++;
1.17 millert 571: INC_CHKCURSOR(stringbase);
1.1 deraadt 572: return ((*sb == '!') ? "!" : "$");
573: /* NOTREACHED */
574: case 1:
575: slrflag++;
576: altarg = stringbase;
577: break;
578: default:
579: break;
580: }
581: }
582:
583: S0:
584: switch (*sb) {
585:
586: case '\0':
587: goto OUT;
588:
589: case ' ':
590: case '\t':
1.17 millert 591: INC_CHKCURSOR(sb);
592: goto S0;
1.1 deraadt 593:
594: default:
595: switch (slrflag) {
596: case 0:
597: slrflag++;
598: break;
599: case 1:
600: slrflag++;
601: altarg = sb;
602: break;
603: default:
604: break;
605: }
606: goto S1;
607: }
608:
609: S1:
610: switch (*sb) {
611:
612: case ' ':
613: case '\t':
614: case '\0':
615: goto OUT; /* end of token */
616:
617: case '\\':
1.17 millert 618: INC_CHKCURSOR(sb);
619: goto S2; /* slurp next character */
1.1 deraadt 620:
621: case '"':
1.17 millert 622: INC_CHKCURSOR(sb);
623: goto S3; /* slurp quoted string */
1.1 deraadt 624:
625: default:
1.17 millert 626: *ap = *sb; /* add character to token */
627: ap++;
628: INC_CHKCURSOR(sb);
1.1 deraadt 629: got_one = 1;
630: goto S1;
631: }
632:
633: S2:
634: switch (*sb) {
635:
636: case '\0':
637: goto OUT;
638:
639: default:
1.17 millert 640: *ap = *sb;
641: ap++;
642: INC_CHKCURSOR(sb);
1.1 deraadt 643: got_one = 1;
644: goto S1;
645: }
646:
647: S3:
648: switch (*sb) {
649:
650: case '\0':
651: goto OUT;
652:
653: case '"':
1.17 millert 654: INC_CHKCURSOR(sb);
655: goto S1;
1.1 deraadt 656:
657: default:
1.17 millert 658: *ap = *sb;
659: ap++;
660: INC_CHKCURSOR(sb);
1.1 deraadt 661: got_one = 1;
662: goto S3;
663: }
664:
665: OUT:
666: if (got_one)
667: *ap++ = '\0';
668: argbase = ap; /* update storage pointer */
669: stringbase = sb; /* update scan pointer */
670: if (got_one) {
671: return (tmp);
672: }
673: switch (slrflag) {
674: case 0:
675: slrflag++;
676: break;
677: case 1:
678: slrflag++;
679: altarg = (char *) 0;
680: break;
681: default:
682: break;
683: }
684: return ((char *)0);
685: }
686:
687: /*
688: * Help command.
689: * Call each command handler with argc == 0 and argv[0] == name.
690: */
691: void
692: help(argc, argv)
693: int argc;
694: char *argv[];
695: {
696: struct cmd *c;
697:
698: if (argc == 1) {
1.17 millert 699: StringList *buf;
1.1 deraadt 700:
1.17 millert 701: buf = sl_init();
1.30 deraadt 702: fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n",
1.17 millert 703: proxy ? "Proxy c" : "C");
704: for (c = cmdtab; c < &cmdtab[NCMDS]; c++)
705: if (c->c_name && (!proxy || c->c_proxy))
706: sl_add(buf, c->c_name);
707: list_vertical(buf);
708: sl_free(buf, 0);
1.1 deraadt 709: return;
710: }
1.17 millert 711:
1.18 millert 712: #define HELPINDENT ((int) sizeof("disconnect"))
1.17 millert 713:
1.1 deraadt 714: while (--argc > 0) {
715: char *arg;
1.17 millert 716:
1.1 deraadt 717: arg = *++argv;
718: c = getcmd(arg);
719: if (c == (struct cmd *)-1)
1.30 deraadt 720: fprintf(ttyout, "?Ambiguous help command %s\n", arg);
1.1 deraadt 721: else if (c == (struct cmd *)0)
1.30 deraadt 722: fprintf(ttyout, "?Invalid help command %s\n", arg);
1.1 deraadt 723: else
1.30 deraadt 724: fprintf(ttyout, "%-*s\t%s\n", HELPINDENT,
1.1 deraadt 725: c->c_name, c->c_help);
726: }
1.17 millert 727: }
728:
729: void
730: usage()
731: {
732: (void)fprintf(stderr,
1.50 mpech 733: "usage: %s [-AVadegimnptv46] [-o output] [-P port] [-r <seconds>] [host [port]]\n"
734: " %s host:[/path/]file[/]\n"
735: " %s ftp://[user:password@]host[:port]/file[/]\n"
1.17 millert 736: " %s http://host[:port]/file\n",
737: __progname, __progname, __progname, __progname);
738: exit(1);
1.1 deraadt 739: }