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