1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright 2000 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #if defined(sun)
33 #pragma ident	"@(#)word.c	1.22	05/09/13 SMI"
34 #endif
35 
36 #include "defs.h"
37 
38 /*
39  * Copyright 2008-2021 J. Schilling
40  *
41  * @(#)word.c	1.109 21/02/27 2008-2021 J. Schilling
42  */
43 #ifndef lint
44 static	UConst char sccsid[] =
45 	"@(#)word.c	1.109 21/02/27 2008-2021 J. Schilling";
46 #endif
47 
48 /*
49  * UNIX shell
50  */
51 
52 #include	"sym.h"
53 #ifdef	DO_SYSALIAS
54 #include	"abbrev.h"
55 #endif
56 #ifdef	SCHILY_INCLUDES
57 #include	<schily/errno.h>
58 #include	<schily/fcntl.h>
59 #ifdef	INTERACTIVE
60 #include	<schily/shedit.h>
61 #endif
62 #else
63 #include	<errno.h>
64 #include	<fcntl.h>
65 #endif
66 #ifdef	DO_TILDE
67 #include	<schily/pwd.h>
68 #endif
69 
70 	int		word	__PR((void));
71 static	unsigned char	*match_word __PR((unsigned char *argp,
72 						unsigned int c,
73 						unsigned int d,
74 						unsigned int *wordcp));
75 #ifdef	DO_DOL_PAREN
76 static	unsigned char	*dolparen	__PR((unsigned char *argp));
77 static	unsigned char	*match_cmd	__PR((unsigned char *argp));
78 	unsigned char	*match_arith	__PR((unsigned char *argp));
79 #endif
80 static	unsigned char	*match_literal	__PR((unsigned char *argp));
81 static	unsigned char	*match_block __PR((unsigned char *argp,
82 						unsigned int c,
83 						unsigned int d));
84 	unsigned int	skipwc	__PR((void));
85 	unsigned int	nextwc	__PR((void));
86 	unsigned char	*readw	__PR((wchar_t d));
87 	unsigned int	readwc	__PR((void));
88 static int		readb	__PR((struct fileblk *, int, int));
89 	int		isbinary __PR((struct fileblk *f));
90 #ifdef	INTERACTIVE
91 static	BOOL		chk_igneof __PR((void));
92 static	int		xread	__PR((int f, char *buf, int n));
93 #endif
94 #ifdef	DO_TILDE
95 	unsigned char	*do_tilde __PR((unsigned char *arg));
96 #endif
97 
98 /* ========	character handling for command lines	======== */
99 
100 
101 int
word()102 word()
103 {
104 	unsigned int	c, d;
105 #ifdef	DO_SYSALIAS
106 	void		*seen;
107 #endif
108 
109 	wdnum = 0;
110 	wdset &= ~KEYFLAG;
111 
112 	/*
113 	 * We first call readwc() in order to make sure that the history editor
114 	 * was called already and malloc() will not be called while we are
115 	 * working on a "local stack". We asume that after readwc() was called,
116 	 * no further edit related malloc() call will happen and it is safe to
117 	 * call locstak() to create a local stack.
118 	 */
119 	/* CONSTCOND */
120 	while (1) {
121 		while (c = nextwc(), space(c))		/* skipc() */
122 			/* LINTED */
123 			;
124 
125 		if (c == COMCHAR) {			/* Skip comment */
126 			while ((c = readwc()) != NL && c != EOF)
127 				/* LINTED */
128 				;
129 			peekc = c;			/* NL or EOF */
130 		} else {
131 			break;	/* out of comment - white space loop */
132 		}
133 	}
134 
135 #ifdef	DO_SYSALIAS
136 	/*
137 	 * We skipped the white space...
138 	 * Now remember the alias state from the beginning of this word as we
139 	 * later need to check whether there might be a loop for this word. We
140 	 * need to do it here since parsing the word may cause the pushed macro
141 	 * replacement to be popped already at the end of the word.
142 	 */
143 	seen = standin->alias;
144 #endif
145 
146 	if (!eofmeta(c) || (c == '^' && (flags2 & posixflg))) {
147 		struct argnod	*arg = (struct argnod *)locstak();
148 		unsigned char	*argp = arg->argval;
149 		unsigned int	wordc;	/* To restore c from  match_word() */
150 
151 		/*
152 		 * As eofmeta(c) includes NL and EOF, we will not be here in
153 		 * case that peekc was set.
154 		 */
155 		argp = match_word(argp, c, MARK, &wordc);
156 		arg = (struct argnod *)endstak(argp);
157 		if (!letter(arg->argval[0]))
158 			wdset &= ~KEYFLAG;
159 
160 		c = wordc;		/* Last c from inside match_word() */
161 		if (arg->argval[1] == 0 &&
162 		    (d = arg->argval[0], digit(d)) &&
163 #ifdef	DO_FDPIPE
164 		    (c == '>' || c == '<' ||
165 		    ((flags2 & fdpipeflg) && c == '|'))) {
166 #else
167 		    /* CSTYLED */
168 		    (c == '>' || c == '<')) {
169 #endif
170 			word();
171 			/*
172 			 * wdnum is cleared when entering word() but may
173 			 * already contain IOSTRIP here. Keep the bug for
174 			 * the old Bourne Shell variant.
175 			 */
176 #ifndef	DO_IOSTRIP_FIX
177 			wdnum = d - '0';
178 #else
179 			wdnum |= d - '0';
180 #endif
181 		} else { /* check for reserved words */
182 			if (reserv == FALSE ||
183 			    (wdset & IN_CASE) ||
184 			    (wdval = syslook(arg->argval,
185 			    reserved, no_reserved)) == 0) {
186 				wdval = 0;
187 			}
188 #ifdef	DO_TIME
189 			else if (wdval == TIMSYM) {
190 				/*
191 				 * POSIX requires to support "time -p command",
192 				 * so check for "time -" and disable the
193 				 * "time" reserved word if needed.
194 				 */
195 				while (c = nextwc(), space(c))	/* skipc() */
196 					/* LINTED */
197 					;
198 				if (c)
199 					peekn = c;
200 				if (c == '-')
201 					wdval = 0;
202 			}
203 #endif
204 			/* set arg for reserved words too */
205 			wdarg = arg;
206 		}
207 	} else if (dipchar(c)) {
208 		if ((d = nextwc()) == c) {
209 			wdval = c | SYMREP;
210 			if (c == '<') {
211 				if ((d = nextwc()) == '-')
212 					wdnum |= IOSTRIP;
213 				else
214 					peekn = d | MARK;
215 			}
216 #ifdef	DO_FALLTHR_CASE
217 			else if (wdval == ECSYM) {		/* ;;  */
218 				if ((d = nextwc()) == '&')	/* ;;& */
219 					wdval = ECARSYM;	/* ;;& */
220 				else
221 					peekn = d | MARK;
222 			}
223 		} else if (c == ';' && d == '&') {		/* ;& */
224 			wdval = ECASYM;				/* ;& */
225 #endif
226 		} else {
227 			peekn = d | MARK;
228 			wdval = c;
229 		}
230 	} else {
231 		if ((wdval = c) == EOF)
232 			wdval = EOFSYM;
233 		if (iopend && eolchar(c)) {
234 			struct ionod *tmp_iopend;
235 			tmp_iopend = iopend;
236 			iopend = 0;
237 			copy(tmp_iopend);
238 		}
239 	}
240 
241 #ifdef	DO_SYSALIAS
242 	/*
243 	 * We previously did not expand aliases while in an eval(1) call.
244 	 * Since all other shells expand aliases inside an eval call, we
245 	 * now do it as well.
246 	 */
247 	if (wdval == 0) {
248 			char	*val;
249 		extern	int	abegin;
250 			int	aflags = abegin > 0 ? AB_BEGIN:0;
251 
252 		if ((val = ab_value(LOCAL_AB, (char *)wdarg->argval,
253 		    &seen, aflags)) == NULL) {
254 			val = ab_value(GLOBAL_AB, (char *)wdarg->argval,
255 			    &seen, aflags);
256 		}
257 		if (val) {
258 			struct filehdr *fb = alloc(sizeof (struct filehdr));
259 
260 			push((struct fileblk *)fb);	/* Push tmp filehdr */
261 			estabf(UC val);			/* Install value    */
262 			standin->fdes = -2;		/* Make it auto-pop */
263 			standin->peekn = peekn;		/* Remember peekn   */
264 			standin->alias = seen;		/* Curr. alias list */
265 			peekn = 0;			/* for later use    */
266 
267 			if (abegin > 0) {		/* Was a begin alias */
268 				size_t	len = strlen(val);
269 
270 				if (len > 0 &&
271 				    (val[len-1] == ' ' || val[len-1] == '\t'))
272 					standin->fdes = -3; /* begin alias */
273 			}
274 
275 			return (word());		/* Parse replacement */
276 		}
277 	}
278 #endif
279 	reserv = FALSE;
280 	return (wdval);
281 }
282 
283 /*
284  * Match and copy the next word from the input stream.
285  */
286 static unsigned char *
287 match_word(argp, c, d, wordcp)
288 	unsigned char	*argp;		/* Output pointer	*/
289 	unsigned int	c;		/* Last read character	*/
290 	unsigned int	d;		/* Delimiter or MARK	*/
291 	unsigned int	*wordcp;	/* Pointer to return c	*/
292 {
293 	unsigned int	cc;
294 	unsigned char	*pc;
295 	int		alpha = 1;
296 	int		parm = 0;
297 #ifdef	DO_TILDE
298 	int		iskey = 0;
299 	int		tilde = -1;
300 extern	int		abegin;
301 
302 	if (c == '~')
303 		tilde = argp - stakbot;
304 #endif
305 
306 	do {
307 		if (c == LITERAL) {	/* '\'' */
308 			argp = match_literal(argp);
309 		} else {
310 			if (c == 0) {
311 				GROWSTAK(argp);
312 				*argp++ = 0;
313 				parm = 0;	/* EOF -> abort ${..} scan */
314 			} else {
315 				pc = readw(c);
316 				while (*pc) {
317 					GROWSTAK(argp);
318 					*argp++ = *pc++;
319 				}
320 			}
321 			if (d != MARK) {
322 				if (c == 0 || c == d)
323 					break;
324 				if (c == NL)
325 					chkpr();
326 			}
327 			if (c == '\\') {
328 				if ((cc = readwc()) == 0) {
329 					GROWSTAK(argp);
330 					*argp++ = 0;
331 				} else {
332 					pc = readw(cc);
333 					while (*pc) {
334 						GROWSTAK(argp);
335 						*argp++ = *pc++;
336 					}
337 				}
338 			}
339 			if (d == MARK) {
340 				if (c == '=') {
341 					wdset |= alpha;
342 #ifdef	DO_TILDE
343 					if (abegin > 0 || flags & keyflg) {
344 						tilde = argp - stakbot;
345 						iskey++;
346 					}
347 #endif
348 				}
349 				if (!alphanum(c))
350 					alpha = 0;
351 			}
352 #ifdef	DO_DOL_PAREN
353 			if (c == DOLLAR) {
354 				argp = dolparen(argp);
355 				if (peekn == ('{' | MARK))
356 					parm++;
357 			} else if (c == '}' && parm) {
358 				parm--;
359 			} else
360 #endif
361 			if (qotchar(c)) {	/* '`' or '"' */
362 				argp = match_block(argp, c, c);
363 			}
364 
365 #ifdef	DO_TILDE
366 			if (tilde >= 0) {
367 				c = readwc();
368 				peekc = c | MARK;
369 				if (c == '/' || c == ':' || eofmeta(c)) {
370 					unsigned char	*val;
371 
372 					GROWSTAK(argp);
373 					*argp = '\0';
374 					val = do_tilde(stakbot+tilde);
375 					if (val)
376 						argp = movstrstak(val,
377 								stakbot+tilde);
378 					tilde = -1;
379 				}
380 			} else if (c == ':' && iskey)
381 				tilde = argp - stakbot;
382 #endif
383 		}
384 	} while ((c = nextwc(), d != MARK || !eofmeta(c)) || parm > 0 ||
385 				(c == '^' && (flags2 & posixflg)));
386 
387 	if (d == MARK) {
388 		/*
389 		 * We need to remember c for word() as c may have MARK set.
390 		 */
391 		*wordcp = c;
392 		peekn = c | MARK;
393 	}
394 	return (argp);
395 }
396 
397 #ifdef	DO_DOL_PAREN
398 static unsigned char *
399 dolparen(argp)
400 	unsigned char	*argp;		/* Output pointer	*/
401 {
402 	unsigned int	c;		/* Last read character	*/
403 
404 	/*
405 	 * Check for '$('
406 	 */
407 	if ((c = nextwc()) == '(') {
408 		/*
409 		 * Check for '$(('
410 		 */
411 		if ((c = nextwc()) == '(') {
412 			argp = match_arith(argp);
413 		} else {
414 			peekn = c | MARK;
415 			argp = match_cmd(argp);
416 		}
417 	} else {
418 		peekn = c | MARK;
419 	}
420 	return (argp);
421 }
422 
423 static unsigned char *
424 match_cmd(argp)
425 	unsigned char	*argp;		/* Output pointer	*/
426 {
427 	struct argnod	*arg;
428 	struct trenod	*tc;
429 	int		save_fd;
430 	struct ionod	*oiopend = iopend;
431 	int		owdnum = wdnum;
432 	int		owdset = wdset;
433 
434 	/*
435 	 * Add "( " and make the string null terminated semi permanent.
436 	 * Note that the space is needed to avoid confusion with "$((".
437 	 */
438 	argp += 3;
439 	GROWSTAK(argp);
440 	argp -= 3;
441 	*argp++ = '(';
442 	*argp++ = ' ';
443 	*argp++ = 0;
444 	arg = (struct argnod *)endstak(argp);
445 
446 	iopend = 0;
447 	tc = cmd(')', MTFLG | NLFLG | SEMIFLG);	/* Tell parser to stop at ) */
448 	iopend = oiopend;
449 	wdset = owdset;
450 	wdnum = owdnum;
451 
452 	/*
453 	 * Convert the syntax tree back into a command line.
454 	 * Use prf() to get a command line with new-lines instead of ';'
455 	 * since our current parser does not walways grok ';' where a
456 	 * new line is ok.
457 	 */
458 	save_fd = setb(-1);
459 	prs_buff(arg->argval);		/* Copy begin of argument */
460 	prf(tc);			/* Convert cmd to string  */
461 	prs_buff(UC ")");		/* Add closing ) from $() */
462 	argp = stakbot;
463 	(void) setb(save_fd);
464 	argp = endb();
465 #ifdef	DOL_PAREN_DEBUG
466 	fprintf(stderr, "DO_PAREN parse->prf() '%s'\n", argp); fflush(stderr);
467 #endif
468 
469 	/*
470 	 * Create new growable local stack and copy over the current text.
471 	 */
472 	arg = (struct argnod *)locstak();
473 	argp = movstrstak(argp, arg->argval);
474 	return (argp);
475 }
476 
477 unsigned char *
478 match_arith(argp)
479 	unsigned char	*argp;		/* Output pointer	*/
480 {
481 	int		nest = 2;
482 	unsigned int	c;
483 	unsigned char	*pc;
484 	UIntptr_t	p = relstakp(argp);
485 
486 	/*
487 	 * Add the "((".
488 	 */
489 	argp += 3;
490 	GROWSTAK(argp);
491 	argp -= 3;
492 	*argp++ = '(';
493 	*argp++ = '(';
494 	*argp = 0;
495 	while ((c = nextwc()) != '\0') {
496 		/*
497 		 * quote each character within
498 		 * single quotes
499 		 */
500 		pc = readw(c);
501 		while (*pc) {
502 			GROWSTAK(argp);
503 			*argp++ = *pc++;
504 		}
505 		if (c == '`') {
506 			argp = match_block(argp, c, c);
507 			continue;
508 		}
509 		if (c == NL) {
510 			chkpr();
511 		} else if (c == '(') {
512 			nest++;
513 		} else if (c == ')') {
514 			if (--nest == 0)
515 				break;
516 		} else if (c == DOLLAR) {
517 			argp = dolparen(argp);
518 			continue;
519 		}
520 	}
521 	GROWSTAK(argp);
522 	*argp = 0;
523 	if (nest != 0)		/* Need a generalized syntax error function */
524 		failed(absstak(p), synmsg); /* instead if calling failed()  */
525 	return (argp);
526 }
527 #endif
528 
529 /*
530  * Match and copy the next literal block (surrounded by '\'') from the
531  * input stream.
532  */
533 static unsigned char *
534 match_literal(argp)
535 	unsigned char	*argp;		/* Output pointer	*/
536 {
537 	unsigned int	c;
538 	unsigned char	*pc;
539 	unsigned char	*oldargp = argp;
540 
541 	while ((c = readwc()) != '\0' && c != LITERAL) {
542 		/*
543 		 * quote each character within
544 		 * single quotes.
545 		 * If we implement $(), the strings need to pass the parser
546 		 * more than once, so we need to surround \n by "".
547 		 */
548 		pc = readw(c);
549 		GROWSTAK(argp);
550 #ifdef	DO_DOL_PAREN
551 		if (c == NL)
552 			*argp++ = '"';
553 		else
554 #endif
555 			*argp++ = '\\';
556 		/* Pick up rest of multibyte character */
557 		while (*pc != 0) {
558 			GROWSTAK(argp);
559 			*argp++ = *pc++;
560 		}
561 		if (c == NL) {
562 #ifdef	DO_DOL_PAREN
563 			GROWSTAK(argp);
564 			*argp++ = '"';
565 #endif
566 			chkpr();
567 		}
568 	}
569 	if (argp == oldargp) { /* null argument - '' */
570 		/*
571 		 * Word will be represented by quoted null
572 		 * in macro.c if necessary
573 		 */
574 		GROWSTAK(argp);
575 		*argp++ = '"';
576 		GROWSTAK(argp);
577 		*argp++ = '"';
578 	}
579 	GROWSTAK(argp);
580 	*argp = '\0';
581 	return (argp);
582 }
583 
584 /*
585  * Match and copy the next quoted block (surrounded by e.g. '`' or '"') from the
586  * input stream.
587  */
588 static unsigned char *
589 match_block(argp, c, d)
590 	unsigned char	*argp;		/* Output pointer	*/
591 	unsigned int	c;		/* Last read character	*/
592 	unsigned int	d;		/* Delimiter		*/
593 {
594 	unsigned int	cc;
595 	unsigned char	*pc;
596 	int		parm = 0;
597 #ifdef	MATCH_BLOCK_DEBUG
598 	UIntptr_t	p = relstakp(argp);
599 #endif
600 
601 	for (;;) {
602 		if ((c = nextwc()) == 0) {
603 			GROWSTAK(argp);
604 			*argp++ = 0;
605 		} else {
606 			pc = readw(c);
607 			while (*pc) {
608 				GROWSTAK(argp);
609 				*argp++ = *pc++;
610 			}
611 		}
612 		if (c == 0 || ((c == d) && parm <= 0))
613 			break;
614 		if (c == NL)
615 			chkpr();
616 		/*
617 		 * don't interpret quoted
618 		 * characters
619 		 */
620 		if (c == '\\') {
621 			/*
622 			 * This is the quoted character:
623 			 */
624 			if ((cc = readwc()) == 0) {
625 				GROWSTAK(argp);
626 				*argp++ = 0;
627 			} else {
628 				pc = readw(cc);
629 				while (*pc) {
630 					GROWSTAK(argp);
631 					*argp++ = *pc++;
632 				}
633 			}
634 		}
635 #ifdef	DO_DOL_PAREN
636 		if (c == DOLLAR) {
637 			argp = dolparen(argp);
638 			if (peekn == ('{' | MARK))
639 				parm++;
640 		} else if (c == '}' && parm) {
641 			parm--;
642 		}
643 #endif
644 	}
645 	GROWSTAK(argp);
646 	*argp = '\0';
647 #ifdef	MATCH_BLOCK_DEBUG
648 	fprintf(stderr, "match_block(%c) '%s'\n", d, absstak(p));
649 	fflush(stderr);
650 #endif
651 	return (argp);
652 }
653 
654 unsigned int
655 skipwc()
656 {
657 	unsigned int c;
658 
659 	while (c = nextwc(), space(c))
660 		/* LINTED */
661 		;
662 	return (c);
663 }
664 
665 unsigned int
666 nextwc()
667 {
668 	register unsigned int	c, d;
669 
670 retry:
671 	if ((d = readwc()) == ESCAPE) {
672 		if ((c = readwc()) == NL) {
673 			chkpr();
674 			goto retry;
675 		}
676 		peekc = c | MARK;
677 	}
678 	return (d);
679 }
680 
681 unsigned char *
682 readw(d)
683 	wchar_t	d;
684 {
685 	static unsigned char c[MULTI_BYTE_MAX + 1];
686 	int clength;
687 
688 	if (isascii(d)) {
689 		c[0] = d;
690 		c[1] = '\0';
691 		return (c);
692 	}
693 	if (d == standin->lastwc)		/* d == last EILSEQ   */
694 		return (standin->mbs);		/* use original input */
695 
696 	clength = wctomb((char *)c, d);
697 	if (clength <= 0) {
698 		c[0] = (unsigned char)d;
699 		clength = 1;
700 	}
701 	c[clength] = '\0';
702 	return (c);
703 }
704 
705 unsigned int
706 readwc()
707 {
708 	register wchar_t	c;
709 	int	len;
710 	register struct fileblk	*f;
711 	int	mbmax;
712 	int	i, mlen;
713 
714 	standin->lastwc = 0;
715 top:
716 	if (peekn) {
717 		c = peekn & 0x7fffffff;
718 		peekn = 0;
719 		return (c);
720 	}
721 	if (peekc) {
722 		c = peekc & 0x7fffffff;
723 		peekc = 0;
724 		return (c);
725 	}
726 
727 	f = standin;
728 retry:
729 	if (f->fend > f->fnxt) {
730 		/*
731 		 * something in buffer
732 		 */
733 		c = (unsigned char)*f->fnxt;
734 		if (c == 0) {
735 			f->fnxt++;
736 			f->nxtoff++;
737 			if (f->feval == 0)
738 				goto retry;	/* = c = readc(); */
739 			if (estabf(*f->feval++))
740 				c = EOF;
741 			else
742 				c = SPACE;
743 			if ((flags & readpr) && standin->fstak == 0)
744 				prc(c);
745 			return (c);
746 		}
747 
748 		if (isascii(c)) {
749 			f->fnxt++;
750 			f->nxtoff++;
751 			if ((flags & readpr) && standin->fstak == 0)
752 				prc(c);
753 			if (c == NL)
754 				f->flin++;
755 			return (c);
756 		}
757 
758 		(void) mbtowc(NULL, NULL, 0);
759 		mbmax = MB_CUR_MAX;
760 		mlen = 0;
761 		for (i = 1; i <= mbmax; i++) {
762 			int	rest;
763 			wchar_t	cc;
764 
765 			if ((rest = f->fend - f->fnxt) < i) {
766 				/*
767 				 * not enough bytes available
768 				 * f->fsiz could be BUFFERSIZE or 1 since
769 				 * mbmax is enough smaller than BUFFERSIZE,
770 				 * this loop won't overrun the f->fbuf buffer.
771 				 */
772 				len = readb(f,
773 				    (f->fsiz == 1) ? 1 : (f->fsiz - rest),
774 				    rest);
775 				if (len <= 0)
776 					break;
777 			}
778 			mlen = mbtowc(&cc, (char *)f->fnxt, i);
779 			c = cc;
780 			if (mlen > 0)
781 				break;
782 			(void) mbtowc(NULL, NULL, 0);
783 		}
784 
785 		if (i > mbmax) {
786 			/*
787 			 * enough bytes available but cannot be converted to
788 			 * a valid wchar.
789 			 */
790 			c = (unsigned char)*f->fnxt;
791 			f->lastwc = c;
792 			f->mbs[0] = c;
793 			f->mbs[1] = '\0';	/* paranoia */
794 			mlen = 1;
795 		}
796 
797 		if ((flags & readpr) && standin->fstak == 0) {
798 			unsigned char	*p;
799 
800 			for(p = f->fnxt, i = mlen; --i >= 0; )
801 				prc(*p++);
802 		}
803 		f->fnxt += mlen;
804 		f->nxtoff += mlen;
805 		if (c == NL)
806 			f->flin++;
807 		return (c);
808 	}
809 
810 	if (f->feof || f->fdes < 0) {
811 		if (f->fdes <= -2) {	/* Auto-pop() fileblk to remove */
812 		extern	int	abegin;
813 
814 			if (f->fdes == -3) /* Continue with begin alias */
815 				if (abegin == 0)
816 					abegin++;
817 			peekn = f->peekn;
818 			pop();
819 			free(f);
820 			f = standin;
821 			if (peekn)
822 				goto top;
823 			goto retry;
824 		}
825 		c = EOF;
826 		f->feof++;
827 		return (c);
828 	}
829 
830 	if (readb(f, f->fsiz, 0) <= 0) {
831 		if (f->fdes != input || !isatty(input)) {
832 			close(f->fdes);
833 			f->fdes = -1;
834 		}
835 		f->feof++;
836 		c = EOF;
837 		return (c);
838 	}
839 	goto retry;
840 }
841 
842 static int
843 readb(f, toread, rest)
844 	struct fileblk	*f;
845 	int		toread;
846 	int		rest;
847 {
848 	int	len = 0;
849 	int	fflags;
850 
851 	if (rest) {
852 		/*
853 		 * copies the remaining 'rest' bytes from f->fnxt
854 		 * to f->fbuf
855 		 */
856 		(void) memmove(f->fbuf, f->fnxt, rest);
857 		f->fnxt = f->fbuf;
858 		f->fend = f->fnxt + rest;
859 		f->nxtoff = 0;
860 		f->endoff = rest;
861 		if (f->fbuf[rest - 1] == '\n') {
862 			/*
863 			 * if '\n' found, it should be
864 			 * a boundary of multibyte char.
865 			 */
866 			return (rest);
867 		}
868 	}
869 
870 retry:
871 	errno = 0;
872 	do {
873 		if (len < 0 && errno != EINTR) {
874 			/*
875 			 * Avoid a loop on EIO and similar read errors.
876 			 * This may happen afte a shut down TCP/IP connection.
877 			 */
878 			break;
879 		}
880 		if (trapnote & SIGSET) {
881 			newline();
882 			sigchk();
883 		} else if ((trapnote & SIGINP) ||
884 			    ((trapnote & TRAPSET) && (rwait > 0))) {
885 #ifdef	INTERACTIVE
886 			int	inp = trapnote & SIGINP; /* Reset by chktrap */
887 #endif
888 
889 			newline();
890 			chktrap();
891 			clearup();
892 #ifdef	INTERACTIVE
893 			if (inp) {
894 				/*
895 				 * Do a longjmp() to the next prompt, similar
896 				 * to sigchk().
897 				 */
898 				exval_sig();
899 				exitsh(exitval ? exitval : SIGFAIL);
900 			}
901 #endif
902 		}
903 #ifdef	INTERACTIVE
904 	} while ((len = xread(f->fdes,
905 			    (char *)f->fbuf + rest, toread)) < 0 && trapnote);
906 #else
907 	} while ((len = read(f->fdes,
908 			    (char *)f->fbuf + rest, toread)) < 0 && trapnote);
909 #endif
910 
911 	/*
912 	 * if child sets O_NDELAY or O_NONBLOCK on stdin
913 	 * and exited then turn the modes off and retry
914 	 */
915 	if (len == 0) {
916 		if (((flags & intflg) ||
917 		    ((flags & oneflg) == 0 && isatty(input) &&
918 		    (flags & stdflg))) &&
919 		    ((fflags = fcntl(f->fdes, F_GETFL, 0)) & O_NDELAY)) {
920 			fflags &= ~O_NDELAY;
921 			(void) fcntl(f->fdes, F_SETFL, fflags);
922 			goto retry;
923 		}
924 	} else if (len < 0) {
925 		if (errno == EAGAIN) {
926 			fflags = fcntl(f->fdes, F_GETFL, 0);
927 			fflags &= ~O_NONBLOCK;
928 			(void) fcntl(f->fdes, F_SETFL, fflags);
929 			goto retry;
930 		}
931 		len = 0;
932 	}
933 	f->fnxt = f->fbuf;
934 	f->fend = f->fnxt + (len + rest);
935 	f->nxtoff = 0;
936 	f->endoff = len + rest;
937 	return (len + rest);
938 }
939 
940 #ifdef	DO_CHECKBINARY
941 /*
942  * Check wether a script may be a binary file, e.g. from a different
943  * architecture and caused a ENOEXEC error.
944  * We only check for a binary file if the fileblk seems to be just
945  * initialized as we do not want to repeat the test in case that
946  * exfile() is called again with the same file. Since a shut down TCP/IP
947  * conection first sends a SIGTERM and then causes EIO on stdin, we need
948  * to be careful not to go into a longjmp() loop from inside readb() to
949  * exfile().
950  */
951 int
isbinary(f)952 isbinary(f)
953 	struct fileblk	*f;
954 {
955 	unsigned char	*p;
956 	unsigned char	c;
957 	BOOL		trapsav;
958 
959 	if (f->fend > f->fnxt)		/* The buffer is not empty */
960 		return (FALSE);		/* so this is not the first read */
961 	if (f->feof || f->fdes < 0)	/* Not a fresh new fileblk */
962 		return (FALSE);
963 	if (isatty(f->fdes))		/* Not a plain file */
964 		return (FALSE);
965 
966 	trapsav = trapnote;
967 	trapnote = 0;			/* Avoid endless longjmp() loop */
968 	readb(f, f->fsiz, 0);		/* Fill buffer */
969 	trapnote |= trapsav;		/* Keep signals just seen */
970 
971 	/*
972 	 * Scan the buffer but keep it intcact.
973 	 */
974 	for (p = f->fnxt; p < f->fend; p++) {
975 		c = *p;
976 		if (c == '\0')
977 			return (TRUE);
978 		if (c == '\n')
979 			return (FALSE);
980 	}
981 	return (FALSE);
982 }
983 #endif	/* DO_CHECKBINARY */
984 
985 #ifdef	INTERACTIVE
986 static BOOL
chk_igneof()987 chk_igneof()
988 {
989 	return (flags2 & ignoreeofflg);
990 }
991 
992 static int
xread(f,buf,n)993 xread(f, buf, n)
994 	int	f;
995 	char	*buf;
996 	int	n;
997 {
998 	/*
999 	 * If we like to call shedit_egetc() for files other than STDIN_FILENO
1000 	 * we would need to set up the file descriptor for libshedit.
1001 	 */
1002 	if ((f == STDIN_FILENO) &&
1003 	    (flags2 & vedflg)) {
1004 		static	int	init = 0;
1005 			int	c;
1006 			int	amt = 0;
1007 
1008 		if (!init) {
1009 			init = 1;
1010 			shedit_getenv(getcurenv);
1011 			shedit_putenv(ev_insert);
1012 			shedit_igneof(chk_igneof);
1013 		}
1014 		/*
1015 		 * In order to be able to flush the last line data from the
1016 		 * input when synbad() and others cause a longjmp() to the next
1017 		 * prompt, we need to get the whole line from the editor here.
1018 		 *
1019 		 * Note: calling shedit_egetc() with an empty libshedit buffer
1020 		 * triggers the history editor, so we need to be careful not
1021 		 * to buffer too much.
1022 		 */
1023 		while (--n >= 0) {
1024 			c = shedit_egetc();
1025 			if (c == -1 && shedit_getdelim() == -1) { /* EOF */
1026 				shedit_treset();	/* Writes ~/.history */
1027 				return (0);
1028 			}
1029 			if (c == CTLC && shedit_getdelim() == CTLC) {
1030 				fault(SIGINT);
1031 				trapnote |= SIGINP;
1032 				errno = EINTR;		/* Mark for readb() */
1033 				return (-1);
1034 			}
1035 			*buf++ = c;
1036 			if (amt++ == 0) {
1037 				size_t	l = shedit_getlen();
1038 
1039 				/*
1040 				 * Copy no more than what's currently in the
1041 				 * "line" returned from the history editor.
1042 				 */
1043 				if (l < n)
1044 					n = l;
1045 			}
1046 		}
1047 		return (amt);
1048 	}
1049 	return (read(f, buf, n));
1050 }
1051 #endif
1052 
1053 #ifdef	DO_TILDE
1054 unsigned char *
do_tilde(arg)1055 do_tilde(arg)
1056 	unsigned char	*arg;
1057 {
1058 	unsigned char	*val = NULL;
1059 	unsigned char	*u = arg;
1060 	unsigned char	*p;
1061 
1062 	if (*u++ != '~')
1063 		return (NULL);
1064 	for (p = u; *p && *p != '/' && *p != ':'; p++)
1065 		;
1066 	if (p == u) {
1067 		val = homenod.namval;
1068 	} else if ((p - u) == 1) {
1069 		if (*u == '+')
1070 			val = pwdnod.namval;
1071 		else if (*u == '-')
1072 			val = opwdnod.namval;
1073 	}
1074 	if (val == NULL) {
1075 		struct passwd	*pw;
1076 		unsigned int	c;
1077 
1078 		c = *p;
1079 		*p = '\0';
1080 		pw = getpwnam((char *)u);
1081 		endpwent();
1082 		*p = c;
1083 		if (pw)
1084 			val = UC pw->pw_dir;
1085 	}
1086 	return (val);
1087 }
1088 #endif
1089