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