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