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