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