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