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