xref: /illumos-gate/usr/src/cmd/csh/sh.glob.c (revision 069e6b7e31ba5dcbc5441b98af272714d9a5455c)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved	*/
8 
9 /*
10  * Copyright (c) 1980 Regents of the University of California.
11  * All rights reserved.  The Berkeley Software License Agreement
12  * specifies the terms and conditions for redistribution.
13  */
14 
15 #include "sh.h"
16 #include "sh.tconst.h"
17 #include <dirent.h>
18 #include <strings.h>
19 #ifdef MBCHAR
20 #include <widec.h>	/* wcsetno() */
21 #include <fnmatch.h>	/* fnmatch() */
22 #endif /* MBCHAR */
23 
24 /*
25  * C Shell
26  */
27 
28 int	globcnt;
29 
30 tchar	*gpath, *gpathp, *lastgpathp;
31 int	globbed;
32 bool	noglob;
33 bool	nonomatch;
34 tchar	*entp;
35 tchar	**sortbas;
36 int	sortscmp(tchar **, tchar **);
37 void	ginit(tchar **);
38 void	collect(tchar *);
39 void	acollect(tchar *);
40 void	expand(tchar *);
41 void	matchdir_(tchar *);
42 void	Gcat(tchar *, tchar *);
43 void	addpath(tchar);
44 void	tglob(tchar **);
45 tchar	**dobackp(tchar *, bool);
46 void	backeval(tchar *, bool);
47 void	psave(tchar);
48 void	pword(void);
49 
50 extern	DIR *opendir_(tchar *);
51 
52 #define	sort()	qsort((char *)sortbas, &gargv[gargc] - sortbas, \
53 			sizeof (*sortbas), (int (*)(const void *, \
54 			const void *)) sortscmp), sortbas = &gargv[gargc]
55 
56 
57 tchar **
58 glob(tchar **v)
59 {
60 	tchar agpath[BUFSIZ];
61 	tchar *agargv[GAVSIZ];
62 
63 	gpath = agpath; gpathp = gpath; *gpathp = 0;
64 	lastgpathp = &gpath[BUFSIZ - 2];
65 	ginit(agargv); globcnt = 0;
66 #ifdef TRACE
67 	tprintf("TRACE- glob()\n");
68 #endif
69 #ifdef GDEBUG
70 	printf("glob entered: "); blkpr(v); printf("\n");
71 #endif
72 	noglob = adrof(S_noglob /* "noglob" */) != 0;
73 	nonomatch = adrof(S_nonomatch /* "nonomatch" */) != 0;
74 	globcnt = noglob | nonomatch;
75 	while (*v)
76 		collect(*v++);
77 #ifdef GDEBUG
78 	printf("glob done, globcnt=%d, gflag=%d: ", globcnt, gflag);
79 	blkpr(gargv); printf("\n");
80 #endif
81 	if (globcnt == 0 && (gflag&1)) {
82 		blkfree(gargv), gargv = 0;
83 		return (0);
84 	} else
85 		return (gargv = copyblk(gargv));
86 }
87 
88 void
89 ginit(tchar **agargv)
90 {
91 
92 	agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
93 	gnleft = NCARGS - 4;
94 }
95 
96 void
97 collect(tchar *as)
98 {
99 	int i;
100 
101 #ifdef TRACE
102 	tprintf("TRACE- collect()\n");
103 #endif
104 	if (any('`', as)) {
105 #ifdef GDEBUG
106 		printf("doing backp of %t\n", as);
107 #endif
108 		(void) dobackp(as, 0);
109 #ifdef GDEBUG
110 		printf("backp done, acollect'ing\n");
111 #endif
112 		/*
113 		 * dobackp has the side effect of messing with
114 		 * gflag, since it does more globbing, so check
115 		 * if the results is still globbable
116 		 */
117 		tglob(pargv);
118 
119 		for (i = 0; i < pargc; i++)
120 			if (noglob) {
121 				Gcat(pargv[i], S_ /* "" */);
122 				sortbas = &gargv[gargc];
123 			} else
124 				acollect(pargv[i]);
125 		if (pargv)
126 			blkfree(pargv), pargv = 0;
127 #ifdef GDEBUG
128 		printf("acollect done\n");
129 #endif
130 	} else if (noglob || eq(as, S_LBRA /* "{" */) ||
131 	    eq(as, S_BRABRA /* "{}" */)) {
132 		Gcat(as, S_ /* "" */);
133 		sort();
134 	} else
135 		acollect(as);
136 }
137 
138 void
139 acollect(tchar *as)
140 {
141 	long ogargc = gargc;
142 
143 #ifdef TRACE
144 	tprintf("TRACE- acollect()\n");
145 #endif
146 	gpathp = gpath; *gpathp = 0; globbed = 0;
147 	expand(as);
148 	if (gargc == ogargc) {
149 		if (nonomatch) {
150 			Gcat(as, S_ /* "" */);
151 			sort();
152 		}
153 	} else
154 		sort();
155 }
156 
157 /*
158  * String compare for qsort.  Also used by filec code in sh.file.c.
159  */
160 int
161 sortscmp(tchar **a1, tchar **a2)
162 {
163 
164 	return (strcoll_(*a1, *a2));
165 }
166 
167 void
168 expand(tchar *as)
169 {
170 	tchar *cs;
171 	tchar *sgpathp, *oldcs;
172 	struct stat stb;
173 
174 #ifdef TRACE
175 	tprintf("TRACE- expand()\n");
176 #endif
177 	sgpathp = gpathp;
178 	cs = as;
179 	if (*cs == '~' && gpathp == gpath) {
180 		addpath('~');
181 		for (cs++; alnum(*cs) || *cs == '-'; )
182 			addpath(*cs++);
183 		if (!*cs || *cs == '/') {
184 			if (gpathp != gpath + 1) {
185 				*gpathp = 0;
186 				if (gethdir(gpath + 1))
187 					/*
188 					 * modified from %s to %t
189 					 */
190 					error("Unknown user: %t", gpath + 1);
191 				(void) strcpy_(gpath, gpath + 1);
192 			} else
193 				(void) strcpy_(gpath,
194 				    value(S_home /* "home" */));
195 			gpathp = strend(gpath);
196 		}
197 	}
198 	while (!isglob(*cs)) {
199 		if (*cs == 0) {
200 			if (!globbed)
201 				Gcat(gpath, S_ /* "" */);
202 			else if (lstat_(gpath, &stb) >= 0) {
203 				Gcat(gpath, S_ /* "" */);
204 				globcnt++;
205 			}
206 			goto endit;
207 		}
208 		addpath(*cs++);
209 	}
210 	oldcs = cs;
211 	while (cs > as && *cs != '/')
212 		cs--, gpathp--;
213 	if (*cs == '/')
214 		cs++, gpathp++;
215 	*gpathp = 0;
216 	if (*oldcs == '{') {
217 		(void) execbrc(cs, NOSTR);
218 		return;
219 	}
220 	matchdir_(cs);
221 endit:
222 	gpathp = sgpathp;
223 	*gpathp = 0;
224 }
225 
226 void
227 matchdir_(tchar *pattern)
228 {
229 	struct stat stb;
230 	struct dirent *dp;
231 	DIR *dirp;
232 	tchar curdir_[MAXNAMLEN+1];
233 	int slproc = 0;
234 
235 #ifdef TRACE
236 	tprintf("TRACE- matchdir()\n");
237 #endif
238 	/*
239 	 * BSD's opendir would open "." if argument is NULL, but not S5
240 	 */
241 
242 	if (*gpath == '\0')
243 		dirp = opendir_(S_DOT /* "." */);
244 	else
245 		dirp = opendir_(gpath);
246 	if (dirp == NULL) {
247 		if (globbed)
248 			return;
249 		goto patherr2;
250 	}
251 	if (fstat(dirp->dd_fd, &stb) < 0)
252 		goto patherr1;
253 	if (!isdir(stb)) {
254 		errno = ENOTDIR;
255 		goto patherr1;
256 	}
257 	while ((dp = readdir(dirp)) != NULL) {
258 
259 		if (dp->d_ino == 0)
260 			continue;
261 		strtots(curdir_, dp->d_name);
262 		slproc = 0;
263 		if (match(curdir_, pattern, &slproc)) {
264 			Gcat(gpath, curdir_);
265 			globcnt++;
266 		}
267 	}
268 	unsetfd(dirp->dd_fd);
269 	closedir_(dirp);
270 	return;
271 
272 patherr1:
273 	unsetfd(dirp->dd_fd);
274 	closedir_(dirp);
275 patherr2:
276 	Perror(gpath);
277 }
278 
279 int
280 execbrc(tchar *p, tchar *s)
281 {
282 	tchar restbuf[BUFSIZ + 2];
283 	tchar *pe, *pm, *pl;
284 	int brclev = 0;
285 	tchar *lm, savec, *sgpathp;
286 	int slproc = 0;
287 
288 #ifdef TRACE
289 	tprintf("TRACE- execbrc()\n");
290 #endif
291 	for (lm = restbuf; *p != '{'; *lm++ = *p++)
292 		continue;
293 	for (pe = ++p; *pe; pe++)
294 	switch (*pe) {
295 
296 	case '{':
297 		brclev++;
298 		continue;
299 
300 	case '}':
301 		if (brclev == 0)
302 			goto pend;
303 		brclev--;
304 		continue;
305 
306 	case '[':
307 		for (pe++; *pe && *pe != ']'; pe++)
308 			continue;
309 		if (!*pe)
310 			error("Missing ]");
311 		continue;
312 	}
313 pend:
314 	if (brclev || !*pe)
315 		error("Missing }");
316 	for (pl = pm = p; pm <= pe; pm++)
317 	switch (*pm & (QUOTE|TRIM)) {
318 
319 	case '{':
320 		brclev++;
321 		continue;
322 
323 	case '}':
324 		if (brclev) {
325 			brclev--;
326 			continue;
327 		}
328 		goto doit;
329 
330 	case ',':
331 		if (brclev)
332 			continue;
333 doit:
334 		savec = *pm;
335 		*pm = 0;
336 		(void) strcpy_(lm, pl);
337 		(void) strcat_(restbuf, pe + 1);
338 		*pm = savec;
339 		if (s == 0) {
340 			sgpathp = gpathp;
341 			expand(restbuf);
342 			gpathp = sgpathp;
343 			*gpathp = 0;
344 		} else if (amatch(s, restbuf, &slproc))
345 			return (1);
346 		sort();
347 		pl = pm + 1;
348 		continue;
349 
350 	case '[':
351 		for (pm++; *pm && *pm != ']'; pm++)
352 			continue;
353 		if (!*pm)
354 			error("Missing ]");
355 		continue;
356 	}
357 	return (0);
358 }
359 
360 int
361 match(tchar *s, tchar *p, int *slproc)
362 {
363 	int c;
364 	tchar *sentp;
365 	tchar sglobbed = globbed;
366 
367 #ifdef TRACE
368 	tprintf("TRACE- match()\n");
369 #endif
370 	if (*s == '.' && *p != '.')
371 		return (0);
372 	sentp = entp;
373 	entp = s;
374 	c = amatch(s, p, slproc);
375 	entp = sentp;
376 	globbed = sglobbed;
377 	return (c);
378 }
379 
380 int
381 amatch(tchar *s, tchar *p, int *slproc)
382 {
383 	int scc;
384 	int ok, lc;
385 	tchar *sgpathp;
386 	struct stat stb;
387 	int c, cc;
388 
389 #ifdef TRACE
390 	tprintf("TRACE- amatch()\n");
391 #endif
392 	globbed = 1;
393 	for (;;) {
394 		scc = *s++ & TRIM;
395 		switch (c = *p++) {
396 
397 		case '{':
398 			return (execbrc(p - 1, s - 1));
399 
400 		case '[':
401 			ok = 0;
402 			lc = TRIM;
403 			while (cc = *p++) {
404 				if (cc == ']') {
405 					if (ok)
406 						break;
407 					return (0);
408 				}
409 				if (cc == '-') {
410 #ifdef MBCHAR
411 					wchar_t rc = *p++;
412 					if (rc == ']') {
413 						p--;
414 						continue;
415 					}
416 					/*
417 					 * Both ends of the char range
418 					 * must belong to the same codeset.
419 					 */
420 					if (sh_bracket_exp(scc, lc, rc))
421 						ok++;
422 #else /* !MBCHAR */
423 					if (lc <= scc && scc <= (int)*p++)
424 						ok++;
425 #endif /* !MBCHAR */
426 				} else
427 					if (scc == (lc = cc))
428 						ok++;
429 			}
430 			if (cc == 0)
431 				error("Missing ]");
432 			continue;
433 
434 		case '*':
435 			if (!*p)
436 				return (1);
437 			if (*p == '/') {
438 				p++;
439 				goto slash;
440 			} else if (*p == '*') {
441 				s--;
442 				continue;
443 			}
444 
445 			for (s--; *s; s++)
446 				if (amatch(s, p, slproc))
447 					return (1);
448 
449 			return (0);
450 
451 		case 0:
452 			return (scc == 0);
453 
454 		default:
455 			if ((c & TRIM) != scc)
456 				return (0);
457 			continue;
458 
459 		case '?':
460 			if (scc == 0)
461 				return (0);
462 			continue;
463 
464 		case '/':
465 			if (scc)
466 				return (0);
467 slash:
468 			if (*slproc)	/* Need to expand "/" only once */
469 				return (0);
470 			else
471 				*slproc = 1;
472 
473 			s = entp;
474 			sgpathp = gpathp;
475 			while (*s)
476 				addpath(*s++);
477 			addpath('/');
478 			if (stat_(gpath, &stb) == 0 && isdir(stb))
479 				if (*p == 0) {
480 					Gcat(gpath, S_ /* "" */);
481 					globcnt++;
482 				} else
483 					expand(p);
484 			gpathp = sgpathp;
485 			*gpathp = 0;
486 			return (0);
487 		}
488 	}
489 }
490 
491 int
492 Gmatch(tchar *s, tchar *p)
493 {
494 	int scc;
495 	int ok, lc;
496 	int c, cc;
497 
498 #ifdef TRACE
499 	tprintf("TRACE- Gmatch()\n");
500 #endif
501 	for (;;) {
502 		scc = *s++ & TRIM;
503 		switch (c = *p++) {
504 
505 		case '[':
506 			ok = 0;
507 			lc = TRIM;
508 			while (cc = *p++) {
509 				if (cc == ']') {
510 					if (ok)
511 						break;
512 					return (0);
513 				}
514 				if (cc == '-') {
515 #ifdef MBCHAR
516 					wchar_t rc = *p++;
517 					/*
518 					 * Both ends of the char range
519 					 * must belong to the same codeset...
520 					 */
521 					if (sh_bracket_exp(scc, lc, rc))
522 						ok++;
523 #else /* !MBCHAR */
524 					if (lc <= scc && scc <= (int)*p++)
525 						ok++;
526 #endif /* !MBCHAR */
527 				} else
528 					if (scc == (lc = cc))
529 						ok++;
530 			}
531 			if (cc == 0)
532 				bferr("Missing ]");
533 			continue;
534 
535 		case '*':
536 			if (!*p)
537 				return (1);
538 			for (s--; *s; s++)
539 				if (Gmatch(s, p))
540 					return (1);
541 			return (0);
542 
543 		case 0:
544 			return (scc == 0);
545 
546 		default:
547 			if ((c & TRIM) != scc)
548 				return (0);
549 			continue;
550 
551 		case '?':
552 			if (scc == 0)
553 				return (0);
554 			continue;
555 
556 		}
557 	}
558 }
559 
560 void
561 Gcat(tchar *s1, tchar *s2)
562 {
563 	tchar *p, *q;
564 	int n;
565 
566 #ifdef TRACE
567 	tprintf("TRACE- Gcat()\n");
568 #endif
569 	for (p = s1; *p++; )
570 		;
571 	for (q = s2; *q++; )
572 		;
573 	gnleft -= (n = (p - s1) + (q - s2) - 1);
574 	if (gnleft <= 0 || ++gargc >= GAVSIZ)
575 		error("Arguments too long");
576 	gargv[gargc] = 0;
577 	p = gargv[gargc - 1] = (tchar *) xalloc((unsigned)n*sizeof (tchar));
578 
579 	for (q = s1; *p++ = *q++; )
580 		;
581 	for (p--, q = s2; *p++ = *q++; )
582 		;
583 }
584 
585 void
586 addpath(tchar c)
587 {
588 
589 #ifdef TRACE
590 	tprintf("TRACE- addpath()\n");
591 #endif
592 	if (gpathp >= lastgpathp)
593 		error("Pathname too long");
594 	*gpathp++ = c & TRIM;
595 	*gpathp = 0;
596 }
597 
598 void
599 rscan(tchar **t, int (*f)(int))
600 {
601 	tchar *p;
602 
603 #ifdef TRACE
604 	tprintf("TRACE- rscan()\n");
605 #endif
606 	while (p = *t++)
607 		while (*p)
608 			(*f)(*p++);
609 }
610 
611 void
612 trim(tchar **t)
613 {
614 	tchar *p;
615 
616 #ifdef TRACE
617 	tprintf("TRACE- trim()\n");
618 #endif
619 	while (p = *t++)
620 		while (*p)
621 			*p++ &= TRIM;
622 }
623 
624 void
625 tglob(tchar **t)
626 {
627 	tchar *p, c;
628 
629 #ifdef TRACE
630 	tprintf("TRACE- tglob()\n");
631 #endif
632 	while (p = *t++) {
633 		if (*p == '~')
634 			gflag |= 2;
635 		else if (*p == '{' && (p[1] == '\0' ||
636 		    p[1] == '}' && p[2] == '\0'))
637 			continue;
638 		while (c = *p++)
639 			if (isglob(c))
640 				gflag |= c == '{' ? 2 : 1;
641 	}
642 }
643 
644 tchar *
645 globone(tchar *str)
646 {
647 	tchar *gv[2];
648 	tchar **gvp;
649 	tchar *cp;
650 
651 #ifdef TRACE
652 	tprintf("TRACE- globone()\n");
653 #endif
654 	gv[0] = str;
655 	gv[1] = 0;
656 	gflag = 0;
657 	tglob(gv);
658 	if (gflag) {
659 		gvp = glob(gv);
660 		if (gvp == 0) {
661 			setname(str);
662 			bferr("No match");
663 		}
664 		cp = *gvp++;
665 		if (cp == 0)
666 			cp = S_ /* "" */;
667 		else if (*gvp) {
668 			setname(str);
669 			bferr("Ambiguous");
670 		} else
671 			cp = strip(cp);
672 #if 0
673 		if (cp == 0 || *gvp) {
674 			setname(str);
675 			bferr(cp ? "Ambiguous" : "No output");
676 		}
677 #endif
678 		xfree((char *)gargv); gargv = 0;
679 	} else {
680 		trim(gv);
681 		cp = savestr(gv[0]);
682 	}
683 	return (cp);
684 }
685 
686 /*
687  * Command substitute cp.  If literal, then this is
688  * a substitution from a << redirection, and so we should
689  * not crunch blanks and tabs, separating words only at newlines.
690  */
691 tchar **
692 dobackp(tchar *cp, bool literal)
693 {
694 	tchar *lp, *rp;
695 	tchar *ep;
696 	tchar word[BUFSIZ];
697 	tchar *apargv[GAVSIZ + 2];
698 
699 #ifdef TRACE
700 	tprintf("TRACE- dobackp()\n");
701 #endif
702 	if (pargv) {
703 		blkfree(pargv);
704 	}
705 	pargv = apargv;
706 	pargv[0] = NOSTR;
707 	pargcp = pargs = word;
708 	pargc = 0;
709 	pnleft = BUFSIZ - 4;
710 	for (;;) {
711 		for (lp = cp; *lp != '`'; lp++) {
712 			if (*lp == 0) {
713 				if (pargcp != pargs)
714 					pword();
715 #ifdef GDEBUG
716 				printf("leaving dobackp\n");
717 #endif
718 				return (pargv = copyblk(pargv));
719 			}
720 			psave(*lp);
721 		}
722 		lp++;
723 		for (rp = lp; *rp && *rp != '`'; rp++)
724 			if (*rp == '\\') {
725 				rp++;
726 				if (!*rp)
727 					goto oops;
728 			}
729 		if (!*rp)
730 oops:
731 			error("Unmatched `");
732 		ep = savestr(lp);
733 		ep[rp - lp] = 0;
734 		backeval(ep, literal);
735 #ifdef GDEBUG
736 		printf("back from backeval\n");
737 #endif
738 		cp = rp + 1;
739 	}
740 }
741 
742 void
743 backeval(tchar *cp, bool literal)
744 {
745 	int pvec[2];
746 	int quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
747 	tchar ibuf[BUFSIZ + MB_LEN_MAX]; /* read_ can return extra bytes */
748 	int icnt = 0, c;
749 	tchar *ip;
750 	bool hadnl = 0;
751 	tchar *fakecom[2];
752 	struct command faket;
753 
754 #ifdef TRACE
755 	tprintf("TRACE- backeval()\n");
756 #endif
757 	faket.t_dtyp = TCOM;
758 	faket.t_dflg = 0;
759 	faket.t_dlef = 0;
760 	faket.t_drit = 0;
761 	faket.t_dspr = 0;
762 	faket.t_dcom = fakecom;
763 	fakecom[0] = S_QPPPQ; /* "` ... `" */;
764 	fakecom[1] = 0;
765 	/*
766 	 * We do the psave job to temporarily change the current job
767 	 * so that the following fork is considered a separate job.
768 	 * This is so that when backquotes are used in a
769 	 * builtin function that calls glob the "current job" is not corrupted.
770 	 * We only need one level of pushed jobs as long as we are sure to
771 	 * fork here.
772 	 */
773 	psavejob();
774 	/*
775 	 * It would be nicer if we could integrate this redirection more
776 	 * with the routines in sh.sem.c by doing a fake execute on a builtin
777 	 * function that was piped out.
778 	 */
779 	mypipe(pvec);
780 	if (pfork(&faket, -1) == 0) {
781 		struct wordent paraml;
782 		struct command *t;
783 		tchar oHIST;
784 
785 		new_process();
786 		(void) close(pvec[0]);
787 		unsetfd(pvec[0]);
788 		(void) dmove(pvec[1], 1);
789 		(void) dmove(SHDIAG, 2);
790 		reinitdesc(0, NULL);
791 		arginp = cp;
792 		while (*cp)
793 			*cp++ &= TRIM;
794 		/*
795 		 *	disable history subsitution in sub-shell
796 		 *  of `` evaluation prevents possible
797 		 *  infinite recursion of `` evaluation
798 		 */
799 		oHIST = HIST;
800 		HIST = 0;
801 		(void) lex(&paraml);
802 		HIST = oHIST;
803 		if (err)
804 			error("%s", gettext(err));
805 		alias(&paraml);
806 		t = syntax(paraml.next, &paraml, 0);
807 		if (err)
808 			error("%s", gettext(err));
809 		if (t)
810 			t->t_dflg |= FPAR;
811 		(void) signal(SIGTSTP, SIG_IGN);
812 		(void) signal(SIGTTIN, SIG_IGN);
813 		(void) signal(SIGTTOU, SIG_IGN);
814 		execute(t, -1);
815 		exitstat();
816 	}
817 	xfree(cp);
818 	(void) close(pvec[1]);
819 	unsetfd(pvec[1]);
820 	do {
821 		int cnt = 0;
822 		for (;;) {
823 			if (icnt == 0) {
824 				ip = ibuf;
825 				icnt = read_(pvec[0], ip, BUFSIZ);
826 				if (icnt <= 0) {
827 					c = -1;
828 					break;
829 				}
830 			}
831 			if (hadnl)
832 				break;
833 			--icnt;
834 			c = (*ip++ & TRIM);
835 			if (c == 0)
836 				break;
837 			if (c == '\n') {
838 				/*
839 				 * Continue around the loop one
840 				 * more time, so that we can eat
841 				 * the last newline without terminating
842 				 * this word.
843 				 */
844 				hadnl = 1;
845 				continue;
846 			}
847 			if (!quoted && issp(c))
848 				break;
849 			cnt++;
850 			psave(c | quoted);
851 		}
852 		/*
853 		 * Unless at end-of-file, we will form a new word
854 		 * here if there were characters in the word, or in
855 		 * any case when we take text literally.  If
856 		 * we didn't make empty words here when literal was
857 		 * set then we would lose blank lines.
858 		 */
859 		if (c != -1 && (cnt || literal)) {
860 			if (pargc == GAVSIZ)
861 				break;
862 			pword();
863 		}
864 		hadnl = 0;
865 	} while (c >= 0);
866 #ifdef GDEBUG
867 	printf("done in backeval, pvec: %d %d\n", pvec[0], pvec[1]);
868 	printf("also c = %c <%o>\n", (tchar) c, (tchar) c);
869 #endif
870 	(void) close(pvec[0]);
871 	unsetfd(pvec[0]);
872 	pwait();
873 	prestjob();
874 }
875 
876 void
877 psave(tchar c)
878 {
879 #ifdef TRACE
880 	tprintf("TRACE- psave()\n");
881 #endif
882 
883 	if (--pnleft <= 0)
884 		error("Word too long");
885 	*pargcp++ = c;
886 }
887 
888 void
889 pword(void)
890 {
891 #ifdef TRACE
892 	tprintf("TRACE- pword()\n");
893 #endif
894 
895 	psave(0);
896 	if (pargc == GAVSIZ)
897 		error("Too many words from ``");
898 	pargv[pargc++] = savestr(pargs);
899 	pargv[pargc] = NOSTR;
900 #ifdef GDEBUG
901 	printf("got word %t\n", pargv[pargc-1]);
902 #endif
903 	pargcp = pargs;
904 	pnleft = BUFSIZ - 4;
905 }
906 
907 
908 
909 /*
910  * returns pathname of the form dir/file;
911  *  dir is a null-terminated string;
912  */
913 char *
914 makename(char *dir, char *file)
915 {
916 	/*
917 	 *  Maximum length of a
918 	 *  file/dir name in ls-command;
919 	 *  dfile is static as this is returned
920 	 *  by makename();
921 	 */
922 	static char dfile[MAXNAMLEN];
923 
924 	char *dp, *fp;
925 
926 	dp = dfile;
927 	fp = dir;
928 	while (*fp)
929 		*dp++ = *fp++;
930 	if (dp > dfile && *(dp - 1) != '/')
931 		*dp++ = '/';
932 	fp = file;
933 	while (*fp)
934 		*dp++ = *fp++;
935 	*dp = '\0';
936 	/*
937 	 * dfile points to the absolute pathname. We are
938 	 * only interested in the last component.
939 	 */
940 	return (rindex(dfile, '/') + 1);
941 }
942 
943 int
944 sh_bracket_exp(tchar t_ch, tchar t_fch, tchar t_lch)
945 {
946 	char	t_char[MB_LEN_MAX + 1];
947 	char	t_patan[MB_LEN_MAX * 2 + 8];
948 	char	*p;
949 	int	i;
950 
951 	if ((t_ch == t_fch) || (t_ch == t_lch))
952 		return (1);
953 
954 	p = t_patan;
955 	if ((i = wctomb(t_char, (wchar_t)t_ch)) <= 0)
956 		return (0);
957 	t_char[i] = 0;
958 
959 	*p++ = '[';
960 	if ((i = wctomb(p, (wchar_t)t_fch)) <= 0)
961 		return (0);
962 	p += i;
963 	*p++ = '-';
964 	if ((i = wctomb(p, (wchar_t)t_lch)) <= 0)
965 		return (0);
966 	p += i;
967 	*p++ = ']';
968 	*p = 0;
969 
970 	if (fnmatch(t_patan, t_char, FNM_NOESCAPE))
971 		return (0);
972 	return (1);
973 }
974