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