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