1 /* @(#)bsh.c	1.81 21/08/20 Copyright 1984,1985,1988,1989,1991,1994-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)bsh.c	1.81 21/08/20 Copyright 1982,1984,1985,1988,1989,1991,1994-2021 J. Schilling";
6 #endif
7 /*
8  *	bsh command interpreter - main Program
9  *
10  *	Copyright (c) 1982,1984,1985,1988,1989,1991,1994-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 #include <schily/stdio.h>
27 #include <schily/ctype.h>
28 #include <schily/signal.h>
29 #include <schily/setjmp.h>
30 #include <schily/sigblk.h>
31 #include <schily/pwd.h>
32 #include "bsh.h"
33 #include "node.h"
34 #include "abbrev.h"
35 #include "str.h"
36 #include "strsubs.h"
37 #include <schily/varargs.h>
38 #include <schily/stdlib.h>
39 #include <schily/unistd.h>
40 #include <schily/string.h>
41 #include <schily/fcntl.h>
42 #include <schily/getargs.h>
43 #include <schily/locale.h>
44 #include <schily/nlsdefs.h>
45 
46 #ifdef	SIGUSR1
47 #	define	PROTSIG	SIGUSR1
48 #else
49 #	define	PROTSIG	31
50 #endif
51 
52 char *prompts[2]	= { NULL, NULL};
53 
54 /*
55  * Some saved old signal values...
56  */
57 sigtype osig2		= (sigtype) SIG_DFL;
58 sigtype osig3		= (sigtype) SIG_DFL;
59 sigtype osig15		= (sigtype) SIG_DFL;
60 sigtype osig18		= (sigtype) SIG_DFL;
61 sigtype osig21		= (sigtype) SIG_DFL;
62 sigtype osig22		= (sigtype) SIG_DFL;
63 
64 int	batch		= 0;
65 int	verbose		= FALSE;
66 int	cflg		= FALSE;
67 int	eflg		= FALSE;
68 int	vflg		= FALSE;
69 int	iflg		= FALSE;
70 int	nflg		= FALSE;
71 int	sflg		= FALSE;
72 int	tflg		= FALSE;
73 int	no_closeflg	= FALSE;
74 int	no_histflg	= FALSE;
75 int	mailcheck	= 600;		/* Mail check interval		    */
76 int	qflg		= FALSE;
77 int	pfshell		= FALSE;
78 
79 int	prflg		= FALSE;
80 int	ttyflg		= FALSE;
81 
82 int	parseflg	= FALSE;
83 int	ctlc		= 0;
84 int	ex_status	= 0;
85 int	do_status	= 0;		/* for recognition of readaccess error */
86 pid_t	mypid		= 0;
87 pid_t	opgrp		= 0;
88 pid_t	mypgrp		= 0;
89 Tnode	*lastcmd	= (Tnode *)NULL; /* Used by ancient #e command */
90 BOOL	noslash		= FALSE;	/* Used for restrictions */
91 char	*user		= NULL;		/* Used for ~ Expansion  */
92 char	*hostname	= NULL;
93 char	*cmdfname	= NULL;		/* Used for #! commands */
94 LOCAL	int sig_err_cnt	= 0;		/* abort on 10.th error */
95 
96 #ifdef	INTERACTIVE
97 int	prompt		= 0;
98 #else
99 Tnode	**cur_base	= 0;		/* for use by history */
100 int	history		= 0;
101 #endif
102 
103 int	vac		= 0;
104 /*char	* const *vav	= (char * const *)NULL;*/
105 char	**vav		= (char **)NULL;
106 FILE	*cmdfp		= (FILE *) NULL;
107 FILE	*gstd[3];
108 char	**evarray	= (char **) NULL;
109 unsigned evasize	= 0;
110 int	evaent		= 0;
111 char	*initav0	= NULL;
112 
113 BOOL	firstsh = FALSE;
114 char	*inithome = NULL;
115 FILE	*protfile = (FILE *)NULL;	/* output file for consprot */
116 
117 LOCAL	int	prompterrs	= 0;	/* no errors on stderr jet */
118 
119 LOCAL	jmp_buf	jmpblk;
120 LOCAL	SIGBLK	sb;
121 
122 extern	abidx_t	deftab;			/* Default tab for Abbrev/Alias */
123 
124 extern	int	delim;
125 extern	int	nerrors;
126 
127 EXPORT	sigret	intr		__PR((int sig));
128 LOCAL	BOOL	clearferr	__PR((void));
129 LOCAL	int	sigjmp		__PR((const char *signalstr, long j, long arg));
130 LOCAL	void	printsig	__PR((const char *sig, const char *arg));
131 LOCAL	sigret	proton_off	__PR((int sig));
132 EXPORT	int	main		__PR((int ac, char **av, char **ev));
133 EXPORT	BOOL	dofile		__PR((char *s, abidx_t tab, int flag, FILE ** std, BOOL jump));
134 EXPORT	void	doopen		__PR((FILE * fd, char *s, abidx_t tab, int flag, FILE ** std, BOOL jump));
135 EXPORT	void	process		__PR((FILE * f, int flag, FILE ** std, BOOL jump));
136 EXPORT	int	berror		__PR((const char *s, ...));
137 EXPORT	char	*errstr		__PR((int err));
138 EXPORT	void	close_other_files	__PR((FILE ** std));
139 LOCAL	char	*getgfile	__PR((void));
140 EXPORT	char	*getuname	__PR((int uid));
141 EXPORT	char	*getpwdir	__PR((char *name));
142 EXPORT	char	*mypwhome	__PR((void));
143 EXPORT	char	*myhome		__PR((void));
144 LOCAL	int	get_oopt	__PR((const char *arg, void *valp, int *pac, char *const **pav, const char *opt));
145 LOCAL	void	gargs		__PR((int ac, char *const* av, char *opts, int *no_i2flg, int *no_gaflg, int *no_laflg));
146 EXPORT	void	exitbsh		__PR((int excode));
147 LOCAL	void	bshusage	__PR((int flag, char *name, char *s));
148 
149 /* ARGSUSED */
150 EXPORT sigret
intr(sig)151 intr(sig)
152 	int	sig;
153 {
154 	extern int sigcount[];
155 
156 	signal(SIGINT, intr);
157 	ctlc++;
158 	sigcount[SIGINT]++;
159 #ifdef	DEBUG
160 	fprintf(stderr,
161 		"ctlc: pid: %ld sigcount[%d] %d parseflg: %d jmpblk.ret: 0x%x\n",
162 			(long)mypid, SIGINT, sigcount[SIGINT], parseflg, jmpblk[0]);
163 	fflush(stderr);
164 #endif
165 /*
166  * XXXY Vielleicht doch wieder lonjmp solange keine ueberall funktionierende
167  * XXXY raisecond() Implementierung vorliegt.
168  * XXXY raisecond geht nur wenn wir USE_SCANSTACK bei der Kompilation von
169  * XXXY raisecond.c definieren.
170  */
171 	if (parseflg && !cflg) {		/* Kein Jump, wenn bsh -c '' */
172 		raisecond(sn_ctlc, (long)NULL);	/* raise the condition */
173 	}
174 }
175 
176 #	ifndef	_NFILE
177 #		define	_MAXFILES	20	/* XXX nicht ok */
178 #	else
179 #		define	_MAXFILES	_NFILE
180 #	endif
181 LOCAL BOOL
clearferr()182 clearferr()
183 {
184 	register int	i;
185 
186 	/*
187 	 * Wenn ein Pseudotty geschloszen wird (quit bei shelltool)
188 	 * gibt es fortgesetzte write-errors und der bsh beginnt zu
189 	 * onanieren, wenn auch hier ferror geloescht wird.
190 	 */
191 	if (ferror(stderr)) {
192 		if (++prompterrs >= 10) {
193 			exitbsh(ex_status);
194 			/* NOTREACHED */
195 		}
196 	}
197 
198 	for (i = 0; i < 3; i++) {
199 		if (ferror(gstd[i])) {
200 			clearerr(gstd[i]);
201 			return (TRUE);
202 		}
203 	}
204 	return (FALSE);
205 }
206 
207 /*
208  * XXXY long j kann erst geaendert werden, wenn raisecond() ein void *
209  * XXXY Argument hat.
210  */
211 LOCAL int
sigjmp(signalstr,j,arg)212 sigjmp(signalstr, j, arg)
213 	const char	*signalstr;
214 	long	j;		/* (jmp_buf*)j: stack frame information */
215 	long	arg;
216 {
217 	jmp_buf	*jp = (jmp_buf *)j;
218 
219 	printsig(signalstr, (char *)arg);
220 
221 	if (!(strindex("file", (char *)signalstr) && clearferr()) &&
222 	    !streql(signalstr, sn_ctlc) && (++sig_err_cnt >= 10)) {
223 #ifdef	INTERACTIVE
224 		reset_tty_modes();
225 		reset_line_disc();
226 		reset_tty_pgrp();
227 #endif
228 		abort();
229 	}
230 	longjmp(*jp, TRUE);
231 	return (FALSE);
232 }
233 
234 LOCAL void
printsig(sig,arg)235 printsig(sig, arg)
236 	const char	*sig;
237 	const char	*arg;
238 {
239 	char	str[80];
240 
241 	sprintf(str, "Caught %.20s Signal", sig);
242 	write(STDERR_FILENO, str, strlen(str));
243 	if (arg) {
244 		sprintf(str, " from '%.20s'", arg);
245 		write(STDERR_FILENO, str, strlen(str));
246 	}
247 	write(STDERR_FILENO, ".\r\n", 3);
248 }
249 
250 /* ARGSUSED */
251 LOCAL sigret
proton_off(sig)252 proton_off(sig)
253 	int	sig;
254 {
255 	char	protfname[25];
256 
257 	signal(PROTSIG, proton_off);
258 	if (protfile) {
259 		fclose(protfile);
260 		protfile = (FILE *) NULL;
261 	} else {
262 		sprintf(protfname, "%s.%ld", tmpname, (long)mypid);
263 		protfile = fileopen(protfname, for_wca);
264 #ifdef	F_SETFD
265 		fcntl(fdown(protfile), F_SETFD, FD_CLOEXEC);
266 #endif
267 	}
268 }
269 
270 EXPORT int
main(ac,av,ev)271 main(ac, av, ev)
272 	int ac;
273 	char *av[];
274 	char *ev[];
275 {
276 	char	*gabbrevs;
277 	char	*initfname;
278 	int	no_i2flg	= 0;
279 	int	no_gaflg	= 0;
280 	int	no_laflg	= 0;
281 
282 	save_args(ac, av);
283 	initav0 = av[0];
284 	vac = ac;
285 	vav = av;
286 	firstsh = ac > 0 && av[0][0] == '-';	/* see if its a login shell */
287 
288 	/*
289 	 * Cygwin32 makes stdin/stdout/stderr non constant expressions
290 	 * so we cannot do loader initialization.
291 	 *
292 	 * XXX May this be a problem?
293 	 */
294 	gstd[0]	= stdin;
295 	gstd[1]	= stdout;
296 	gstd[2]	= stderr;
297 
298 	inittime();
299 
300 /*error("euid: %d ruid: %d", geteuid(), getuid());*/
301 
302 #ifdef	HAVE_SETEUID
303 	/*
304 	 * setreuid() ist POSIX aber alte *BSD BS haben kein saved uid.
305 	 * Hier sollte noch ein Test auf _POSIX_SAVED_IDS hinein.
306 	 */
307 	seteuid(getuid());	/* Ungef�hrlich auf Systemen ohne saved uid */
308 #else
309 #ifdef	HAVE_SETREUID		/* BSD & POSIX */
310 	setreuid(-1, getuid());
311 #else
312 #	ifdef	HAVE_SETRESUID
313 	setresuid(-1, getuid(), -1);	/* HP-UX setresuid(ruid, euid, suid)*/
314 #	else
315 	/*
316 	 * Hier sollte nur dann eine Warnung/Abbruch kommen, wenn
317 	 * der bsh tats�chlich suid root installiert ist.
318 	 */
319 #if	!defined(__EMX__) && !defined(__DJGPP__) && \
320 	!defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BEOS__)
321 error  No function to set uid available
322 #endif
323 
324 #	endif
325 #endif
326 #endif	/* HAVE_SETEUID */
327 
328 #if	defined(HAVE_SIGPROCMASK)
329 	{
330 		sigset_t set;
331 
332 		sigemptyset(&set);	/* csh macht login falsch */
333 		sigprocmask(SIG_SETMASK, &set, 0);
334 	}
335 #else
336 #	if defined(HAVE_SIGSETMASK)
337 	sigsetmask(0);			/* csh macht login falsch */
338 #	endif
339 #endif
340 	signal(PROTSIG, proton_off);
341 	mypid = getpid();
342 	mypgrp = opgrp = getpgid(0);
343 #ifdef	WRONG
344 #ifdef	JOBCONTROL
345 	setpgid(0, mypid);
346 	mypgrp = getpgid(0);
347 #endif
348 #endif	/* WRONG */
349 	ev_init(ev);
350 	if (setlocale(LC_ALL, "") == NULL)
351 		error("Bad locale in inital environment.\n");
352 	ev_insert(concat(ignoreeofname, eql, off, (char *)NULL));
353 
354 #ifdef  USE_NLS
355 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
356 #define	TEXT_DOMAIN "bsh"	/* Use this only if it weren't */
357 #endif
358 	{ char	*dir;
359 	dir = searchfileinpath("share/locale", F_OK,
360 					SIP_ANY_FILE|SIP_NO_PATH, NULL);
361 	if (dir)
362 		(void) bindtextdomain(TEXT_DOMAIN, dir);
363 	else
364 #if defined(PROTOTYPES) && defined(INS_BASE)
365 	(void) bindtextdomain(TEXT_DOMAIN, INS_BASE "/share/locale");
366 #else
367 	(void) bindtextdomain(TEXT_DOMAIN, "/usr/share/locale");
368 #endif
369 	(void) textdomain(TEXT_DOMAIN);
370 	}
371 #endif 	/* USE_NLS */
372 
373 
374 	inituser();
375 	inithostname();
376 	initprompt();
377 #ifdef INTERACTIVE
378 	init_input();
379 #else
380 	inithistory();
381 #endif
382 	osig2 = signal(SIGINT, (sigtype) SIG_IGN);
383 	if (osig2 != (sigtype) SIG_IGN)
384 		signal(SIGINT, intr);
385 #ifdef	SIGQUIT
386 	osig3 = signal(SIGQUIT, (sigtype) SIG_IGN);
387 #endif
388 	osig15 = signal(SIGTERM, (sigtype) SIG_IGN);
389 #ifdef	SIGTSTP
390 	osig18 = signal(SIGTSTP, (sigtype) SIG_IGN);
391 #endif
392 #ifdef	SIGTTIN
393 	osig21 = signal(SIGTTIN, (sigtype) SIG_IGN);
394 #endif
395 #ifdef	SIGTTOU
396 	osig22 = signal(SIGTTOU, (sigtype) SIG_IGN);
397 #endif
398 	if (firstsh) {
399 		signal(SIGINT, intr);
400 		osig2 = (sigtype) SIG_DFL;
401 		osig3 = (sigtype) SIG_DFL;
402 		osig15 = (sigtype) SIG_DFL;
403 		osig18 = (sigtype) SIG_DFL;
404 		osig21 = (sigtype) SIG_DFL;
405 		osig22 = (sigtype) SIG_DFL;
406 	}
407 
408 #ifdef	EXECATTR_FILENAME
409 	if (ac > 0) {
410 		const char	*fn = fbasename(av[0]);
411 
412 		if (streql(fn, "pfbsh") || streql(fn, "-pfbsh")) {
413 			pfshell = TRUE;
414 			pfinit();
415 		}
416 	}
417 #endif
418 
419 	gargs(ac, av, bshopts, &no_i2flg, &no_gaflg, &no_laflg);
420 
421 #ifdef	SIGQUIT
422 	if (qflg)
423 		(void) signal(SIGQUIT, SIG_DFL);
424 #endif
425 
426 	if (batch) {
427 		vac -= ac - batch;
428 		vav += ac - batch;
429 		if (vac <= 1)
430 			sflg++;
431 	}
432 #ifdef	XXX
433 	/* XXX solange setreuid() am Anfang steht ungefaehrlich !! */
434 	else if (getuid() != geteuid() || getgid() != getegid()) {
435 		berror("%s: %s", fbasename(initav0), errstr(EACCES));
436 		exit(EACCES);
437 	}
438 #endif
439 
440 
441 	if (!no_closeflg)
442 		close_other_files(gstd);
443 
444 	setinput((FILE *) NULL);		/* Create input stream */
445 	gabbrevs = getgfile();			/* Get .globals file name */
446 	inithome = concat(getcurenv(homename), (char *)NULL);
447 
448 	if (firstsh) {
449 						/* Run /etc/initbsh  file */
450 		dofile(sysinitname, GLOBAL_AB, 0, gstd, TRUE);
451 
452 						/* Run /etc/initrbsh file */
453 		if (strchr(fbasename(initav0), 'r'))
454 			dofile(sysrinitname, GLOBAL_AB, 0, gstd, TRUE);
455 	}
456 
457 	if (!no_gaflg) {			/* Read in .globals */
458 		ab_use(GLOBAL_AB, gabbrevs);
459 	}
460 
461 	if (!no_laflg) {			/* Read in .locals */
462 		ab_use(LOCAL_AB, localname);
463 	}
464 
465 	if (firstsh) {
466 						/* Run .init script */
467 		initfname = concat(inithome, slash, initname, (char *)NULL);
468 		dofile(initfname, GLOBAL_AB, 0, gstd, TRUE);
469 	} else {
470 						/* Run .init2 script */
471 		initfname = concat(inithome, slash, init2name, (char *)NULL);
472 		if (!no_i2flg)
473 			dofile(initfname, GLOBAL_AB, NOTMS, gstd, TRUE);
474 	}
475 	free(initfname);
476 	if (verbose)
477 		vflg++;
478 
479 	vav++;
480 	vac--;
481 	if (!sflg && !batch &&
482 			getfiles(&vac, (char * const **)&vav, bshopts) <= 0)
483 		sflg++;
484 
485 	if (!sflg) {
486 		if (cflg) {
487 			/*
488 			 * -c Option: force "one line" command.
489 			 */
490 			pushline(vav[0]);
491 			do {
492 				freetree(cmdline(0, gstd, FALSE));
493 			} while (delim != EOF);
494 		} else if (!dofile(vav[0], GLOBAL_AB, 0, gstd, TRUE)) {
495 			berror(ecantopen, vav[0], errstr(ex_status = do_status));
496 		}
497 	} else {
498 		cflg = FALSE;
499 #ifdef	INTERACTIVE
500 		if (!no_histflg)
501 			read_init_history();
502 #endif
503 #ifdef	JOBCONTROL
504 		setpgid(0, mypid);
505 		mypgrp = getpgid(0);
506 #endif
507 		do
508 			process(stdin, 0, gstd, TRUE);
509 		while (delim != EOF && !tflg);
510 	}
511 	exitbsh(ex_status);
512 	return (ex_status);	/* Keep lint happy */
513 }
514 
515 EXPORT BOOL
dofile(s,tab,flag,std,jump)516 dofile(s, tab, flag, std, jump)
517 	char	*s;
518 	abidx_t	tab;
519 	int	flag;
520 	FILE	*std[];
521 	BOOL	jump;
522 {
523 	FILE	*fd;
524 
525 #ifdef DEBUG
526 	fprintf(stderr, "dofile(%s,%d,..%sjump) ", s, tab, jump?nullstr:"no");
527 #endif
528 	if ((fd = fileopen(s, for_read)) != (FILE *) NULL) {
529 #ifdef DEBUG
530 		fprintf(stderr, "(ok)\n");
531 		fflush(stderr);
532 #endif
533 #ifdef	F_SETFD
534 		fcntl(fdown(fd), F_SETFD, FD_CLOEXEC);
535 #endif
536 		doopen(fd, s, tab, flag, std, jump);
537 		fclose(fd);
538 		return (TRUE);
539 	} else {
540 		do_status = geterrno();
541 #ifdef DEBUG
542 		fprintf(stderr, "(error) %s\n", errstr(do_status));
543 		fflush(stderr);
544 #endif
545 		return (FALSE);
546 	}
547 }
548 
549 EXPORT void
doopen(fd,s,tab,flag,std,jump)550 doopen(fd, s, tab, flag, std, jump)
551 	FILE	*fd;
552 	char	*s;
553 	abidx_t	tab;
554 	int	flag;
555 	FILE	*std[];
556 	BOOL	jump;
557 {
558 	abidx_t	savetab;	/* abbrev tab */
559 	int	prsave;		/* Promptflag */
560 	int	ttysave;	/* ttyflag */
561 	int	ssave;		/* sflag */
562 
563 #ifdef DEBUG
564 	fprintf(stderr, "doopen(%d,%s,%d,..%sjump) ",
565 			fdown(fd), s, tab, jump?nullstr:"no");
566 #endif
567 	do_status = 0;
568 	cmdfname = s;
569 	cmdfp = fd;
570 	savetab = deftab;
571 	deftab = tab;
572 	prsave = prflg;
573 	ttysave = ttyflg;
574 	ssave = sflg;
575 	sflg = FALSE;
576 	process(fd, flag, std, jump);
577 	cmdfp = (FILE *)NULL;
578 	cmdfname = NULL;
579 	deftab = savetab;
580 	prflg = prsave;		/* process setzt prflg  um */
581 	ttyflg = ttysave;	/* process setzt ttyflg um */
582 	sflg = ssave;
583 }
584 
585 EXPORT void
process(f,flag,std,jump)586 process(f, flag, std, jump)
587 	FILE	*f;
588 	int	flag;
589 	FILE	*std[];
590 	BOOL	jump;		/* Damit dofile und process auch von hoeherer*/
591 				/* Ebene aufgerufen werden kann		    */
592 {
593 	FILE	*old;
594 	int	save = delim;
595 	Tnode	*cmd;
596 	SIGBLK	sigfirst;
597 #ifndef	INTERACTIVE
598 	int	i, found, max;
599 #endif
600 
601 	ttyflg = isatty(fdown(f));
602 	prflg = ttyflg || iflg;
603 
604 	if (ttyflg) {
605 		setbuf(std[0], NULL);	/* XXX ist das die richtige Stelle ? */
606 		osig18 = (sigtype) SIG_DFL;
607 		osig21 = (sigtype) SIG_DFL;
608 		osig22 = (sigtype) SIG_DFL;
609 	}
610 	old = setinput(f);
611 	starthandlecond(&sigfirst);
612 	if (jump) {
613 		if (setjmp(jmpblk)) {
614 			/*
615 			 * A longjmp() from the SIGINT handler may leave
616 			 * SIGINT blocked.
617 			 */
618 			unblock_sigs();
619 			eatline();
620 			if (!prflg || delim == EOF) {
621 				setinput(old);
622 				unhandlecond(&sigfirst);
623 				delim = save;
624 				return;
625 			}
626 		} else {
627 			handlecond(sn_any_other, &sb, sigjmp, (long)jmpblk);
628 		}
629 	}
630 	do {
631 		ctlc = 0;
632 		if (prflg)
633 			testmail();
634 #ifdef INTERACTIVE
635 		prompt = 0;
636 #else
637 		if (prflg) {
638 #ifdef	JOBCONTROL
639 			tty_setpgrp(fdown(f), mypgrp);
640 #endif
641 			fprintf(stderr, prompts[0]);
642 			prompterrs = 0;		/* diesmal kein write-error */
643 		}
644 #endif
645 		cmd = cmdline(flag, std, FALSE); /* Parse / execute next line */
646 #ifndef INTERACTIVE
647 		if (cmd && history > 0) {
648 			max = high_hist();
649 			found = -1;
650 			for (i = max; i >= 0 && found == -1; i--)
651 				if (treeequal(cmd, cur_base[i]))
652 					found = i;
653 			if (found == -1)
654 				h_append(cmd);
655 			else
656 				lr_used(found);
657 			/* no freetree(lastcmd) h_append() free's it */
658 			lastcmd = cmd;
659 		}
660 		if (cmd && history == 0) {
661 			freetree(lastcmd);
662 			lastcmd = cmd;
663 		}
664 #else
665 		if (cmd) {
666 			freetree(lastcmd);
667 			lastcmd = cmd;
668 		}
669 #endif
670 	} while (delim != EOF &&
671 			((nerrors == 0 && !ctlc) || prflg) &&
672 						!(ttyflg && tflg));
673 	setinput(old);
674 	unhandlecond(&sigfirst);
675 	delim = save;
676 }
677 
678 #ifdef	PROTOTYPES
679 EXPORT int
berror(const char * s,...)680 berror(const char *s, ...)
681 #else
682 /* VARARGS1 */
683 EXPORT int
684 berror(s, va_alist)
685 	char	*s;
686 	va_dcl
687 #endif
688 {
689 	va_list	args;
690 	int	ret;
691 
692 #ifdef	PROTOTYPES
693 	va_start(args, s);
694 #else
695 	va_start(args);
696 #endif
697 	ret = fprintf(stderr, "%r\n", s, args);
698 	va_end(args);
699 	fflush(stderr);
700 	return (ret);
701 }
702 
703 /*
704  * Return system error message string for arg 'err'.
705  */
706 
707 #if defined(__BEOS__) || defined(__HAIKU__)
708 #define	silent_error(e)		((e) < 0 && (e) >= -1024)
709 #else
710 #define	silent_error(e)		((e) < 0)
711 #endif
712 
713 EXPORT char *
errstr(err)714 errstr(err)
715 	int	err;
716 {
717 	static	char	errbuf[12];
718 	char	*estr;
719 
720 	if (silent_error(err)) {
721 		return (nullstr);
722 	} else {
723 		estr = errmsgstr(err);
724 		if (estr == NULL) {
725 			sprintf(errbuf, "%d", err);
726 			estr = errbuf;
727 		}
728 		return (estr);
729 	}
730 }
731 
732 EXPORT void
close_other_files(std)733 close_other_files(std)
734 	FILE	*std[3];
735 {
736 #ifdef	HAVE_CLOSEFROM
737 	closefrom(STDERR_FILENO+1);
738 #else
739 	register int s0 = fdown(std[0]);
740 	register int s1 = fdown(std[1]);
741 	register int s2 = fdown(std[2]);
742 	register int i;
743 #ifdef	_SC_OPEN_MAX
744 	register int max = sysconf(_SC_OPEN_MAX);
745 #else
746 #ifdef	HAVE_GETDTABLESIZE
747 	register int max = getdtablesize();
748 #else
749 	register int max = _MAXFILES;
750 #endif
751 #endif
752 
753 	for (i = 0; i++ < max; ) {
754 		if (i == STDIN_FILENO || i == STDOUT_FILENO ||
755 						i == STDERR_FILENO) {
756 			continue;
757 		}
758 		if (i != s0 && i != s1 && i != s2)
759 			close(i);
760 	}
761 #endif	/* HAVE_CLOSEFROM */
762 }
763 
764 LOCAL char *
getgfile()765 getgfile()
766 {
767 	char	*gname;
768 	char	*hdir;
769 	char	buf[10];
770 	struct passwd *pw;
771 /*	extern struct passwd *getpwuid();*/
772 
773 	hdir = getcurenv(homename);
774 	if (hdir) {
775 		gname = concat(hdir, slash, globalname, (char *)NULL);
776 	} else {
777 		sprintf(buf, "%ld", (long)geteuid());
778 		/*
779 		 * First search for user in passwd file. If user can be found,
780 		 * look in his home directory for .globals file.
781 		 * If the user is not in the passwd file then search
782 		 * current directory for .globals file.
783 		 */
784 		pw = getpwuid(geteuid());
785 		endpwent();
786 		if (!pw)
787 			return (concat(globalname, (char *)NULL));
788 		gname = concat(pw->pw_dir, slash, globalname, (char *)NULL);
789 		ev_insert(concat(homename, eql, pw->pw_dir, (char *)NULL));
790 	}
791 	return (gname);
792 }
793 
794 EXPORT char *
getuname(uid)795 getuname(uid)
796 	int	uid;
797 {
798 	char	buf[12];
799 	register struct passwd *pw;
800 
801 	pw = getpwuid(uid);
802 	endpwent();
803 	if (pw)
804 		return (makestr(pw->pw_name));
805 	sprintf(buf, "%d", uid);
806 	return (makestr(buf));
807 }
808 
809 EXPORT char *
getpwdir(name)810 getpwdir(name)
811 	char *name;
812 {
813 	register struct passwd *pw;
814 /*	extern struct passwd *getpwnam();*/
815 
816 	pw = getpwnam(name);
817 	endpwent();
818 
819 	if (!pw)
820 		return (NULL);
821 	return (makestr(pw->pw_dir));
822 }
823 
824 EXPORT char *
mypwhome()825 mypwhome()
826 {
827 	static char *my_pwhome = 0;
828 
829 	if (!my_pwhome)
830 		my_pwhome = getpwdir(user);
831 	if (!my_pwhome) {
832 		my_pwhome = getcurenv(homename);
833 		if (my_pwhome)
834 			my_pwhome = makestr(my_pwhome);
835 	}
836 	if (!my_pwhome)
837 		return (NULL);
838 	return (makestr(my_pwhome));
839 }
840 
841 EXPORT char *
myhome()842 myhome()
843 {
844 	char *my_home;
845 
846 	my_home = getcurenv(homename);
847 	if (!my_home)
848 		return (NULL);
849 	return (makestr(my_home));
850 }
851 
852 /* ARGSUSED */
853 LOCAL int
get_oopt(arg,valp,pac,pav,opt)854 get_oopt(arg, valp, pac, pav, opt)
855 	const char	*arg;
856 	void		*valp;
857 	int		*pac;
858 	char	*const	**pav;
859 	const char	 *opt;
860 {
861 	if (arg[0] == '-' || arg[0] == '+') {
862 		/*
863 		 * Strange Korn Shell rules:
864 		 * If next arg is an option, -o was called without parameter.
865 		 */
866 		if (arg == &opt[2])		/* arg concatenated with -o */
867 			return (BADFLAG);
868 		(*pav)--;
869 		(*pac)++;
870 		return (1);
871 	}
872 	if (streql(arg, "aliasowner")) {
873 		ab_setaltowner(GLOBAL_AB, "");
874 		ab_setaltowner(LOCAL_AB, "");
875 		return (1);
876 	} else if (strncmp(arg, "aliasowner=", 11) == 0) {
877 		ab_setaltowner(GLOBAL_AB, (char *)&arg[11]);
878 		ab_setaltowner(LOCAL_AB, (char *)&arg[11]);
879 		return (1);
880 	}
881 	return (BADFLAG);
882 }
883 
884 LOCAL void
gargs(ac,av,opts,no_i2flg,no_gaflg,no_laflg)885 gargs(ac, av, opts, no_i2flg, no_gaflg, no_laflg)
886 	int	ac;
887 	char	*const *av;
888 	char	*opts;
889 	int 	*no_i2flg, *no_gaflg, *no_laflg;
890 {
891 	BOOL	hflg = FALSE;
892 	BOOL	be_fast = FALSE;
893 	BOOL	be_xfast = FALSE;
894 	BOOL	prversion = FALSE;
895 	char	*aliasowner = NULL;
896 /*	char	bshopts[]	= "v,V,i,c,e,h,2,g,l,n,s,t,f,F,o&,q,alias-owner*,noclose,help,version";*/
897 
898 	av++;
899 	ac--;
900 	if (getargs(&ac, &av, opts,
901 			&verbose,
902 			&vflg,
903 			&iflg,
904 			&cflg,
905 			&eflg,
906 			&no_histflg,
907 			no_i2flg,
908 			no_gaflg,
909 			no_laflg,
910 			&nflg,
911 			&sflg,
912 			&tflg,
913 			&be_fast,
914 			&be_xfast,
915 			get_oopt, NULL,
916 			&qflg,		/* Undoc' d .. don't ignore SIGQUIT */
917 			&aliasowner,
918 			&no_closeflg,
919 			&hflg, &prversion) < 0) {
920 		if (av[0][0] != '-') {	/* Be careful, cmd args may have '=' */
921 			batch = ac+1;
922 		} else {
923 			bshusage(TRUE, fbasename(initav0), av[0]);
924 		}
925 	}
926 	if (av[0] != NULL && streql(av[0], "-"))
927 		batch = ac;
928 #ifdef	ODEBUG
929 	error("-v%d -V%d -i%d -c%d -e%d -h%d -2%d -g%d -l%d -n%d -s%d -t%d -f%d -F%d -o%d -q%d -help%d -version%d\n",
930 		verbose, vflg, iflg, cflg, eflg, no_histflg,
931 		*no_i2flg, *no_gaflg, *no_laflg, nflg, sflg, tflg,
932 		be_fast, be_xfast, no_closeflg, qflg, hflg, prversion);
933 #endif
934 	if (hflg)
935 		bshusage(FALSE, fbasename(initav0), (char *) NULL);
936 	if (prversion) {
937 		extern	int	MVERSION;
938 		extern	int	mVERSION;
939 		extern	char	dVERSION[];
940 
941 		printf("bsh %d.%02d %s (%s-%s-%s)\n\n", MVERSION, mVERSION, dVERSION,
942 						HOST_CPU, HOST_VENDOR, HOST_OS);
943 		printf("Copyright (C) 1982, 1984, 1985, 1988-1989, 1991, 1994-2021 %s\n",
944 			_("J�rg Schilling"));
945 		printf("This is free software; see the source for copying conditions.  There is NO\n");
946 		printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
947 		exit(0);
948 	}
949 	if (be_fast)
950 		(*no_i2flg)++, no_histflg++;
951 	if (be_xfast)
952 		(*no_i2flg)++, no_histflg++, (*no_gaflg)++, (*no_laflg)++;
953 	if (cflg && streql(fbasename(initav0), commandname))
954 		(*no_i2flg)++;
955 	if (aliasowner) {
956 		ab_setaltowner(GLOBAL_AB, aliasowner);
957 		ab_setaltowner(LOCAL_AB, aliasowner);
958 	}
959 }
960 
961 EXPORT void
exitbsh(excode)962 exitbsh(excode)
963 	int	excode;
964 {
965 	if (sflg) {
966 						/* see if its a top level */
967 						/* run final file */
968 #ifdef	INTERACTIVE
969 		if (!no_histflg)
970 			save_history(HI_NOINTR);
971 #endif
972 		if (firstsh)
973 			dofile(concat(inithome, slash, finalname, (char *)NULL),
974 						GLOBAL_AB, 0, gstd, TRUE);
975 	}
976 
977 #ifdef	INTERACTIVE
978 	reset_tty_modes();
979 	reset_line_disc();		/* Line discipline */
980 	reset_tty_pgrp();
981 #endif
982 	exit(excode);
983 }
984 
985 LOCAL void
bshusage(flag,name,s)986 bshusage(flag, name, s)
987 	int	flag;
988 	char	*name;
989 	char	*s;
990 {
991 	if (flag)
992 		berror(ebadopt, name, s);
993 	berror("%s%s %s", usage, name, ubsh);
994 	if (flag)
995 		exit(1);
996 	else
997 		exit(0);
998 }
999