xref: /titanic_41/usr/src/lib/libast/common/misc/glob.c (revision d4660949aa62dd6a963f4913b7120b383cf473c4)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 
24 /*
25  * file name expansion - posix.2 glob with gnu and ast extensions
26  *
27  *	David Korn
28  *	Glenn Fowler
29  *	AT&T Research
30  */
31 
32 #include <ast.h>
33 #include <ls.h>
34 #include <stak.h>
35 #include <ast_dir.h>
36 #include <error.h>
37 #include <ctype.h>
38 #include <regex.h>
39 
40 #define GLOB_MAGIC	0xaaaa0000
41 
42 #define MATCH_RAW	1
43 #define MATCH_MAKE	2
44 #define MATCH_META	4
45 
46 #define MATCHPATH(g)	(offsetof(globlist_t,gl_path)+(g)->gl_extra)
47 
48 typedef int (*GL_error_f)(const char*, int);
49 typedef void* (*GL_opendir_f)(const char*);
50 typedef struct dirent* (*GL_readdir_f)(void*);
51 typedef void (*GL_closedir_f)(void*);
52 typedef int (*GL_stat_f)(const char*, struct stat*);
53 
54 #define _GLOB_PRIVATE_ \
55 	GL_error_f	gl_errfn; \
56 	int		gl_error; \
57 	char*		gl_nextpath; \
58 	globlist_t*	gl_rescan; \
59 	globlist_t*	gl_match; \
60 	Stak_t*		gl_stak; \
61 	int		re_flags; \
62 	regex_t*	gl_ignore; \
63 	regex_t*	gl_ignorei; \
64 	regex_t		re_ignore; \
65 	regex_t		re_ignorei; \
66 	unsigned long	gl_starstar; \
67 	char*		gl_opt; \
68 	char*		gl_pat; \
69 	char*		gl_pad[4];
70 
71 #include <glob.h>
72 
73 /*
74  * default gl_diropen
75  */
76 
77 static void*
78 gl_diropen(glob_t* gp, const char* path)
79 {
80 	return (*gp->gl_opendir)(path);
81 }
82 
83 /*
84  * default gl_dirnext
85  */
86 
87 static char*
88 gl_dirnext(glob_t* gp, void* handle)
89 {
90 	struct dirent*	dp;
91 
92 	while (dp = (struct dirent*)(*gp->gl_readdir)(handle))
93 #ifdef D_FILENO
94 		if (D_FILENO(dp))
95 #endif
96 		{
97 #ifdef D_TYPE
98 			if (D_TYPE(dp) != DT_UNKNOWN && D_TYPE(dp) != DT_DIR && D_TYPE(dp) != DT_LNK)
99 				gp->gl_status |= GLOB_NOTDIR;
100 #endif
101 			return dp->d_name;
102 		}
103 	return 0;
104 }
105 
106 /*
107  * default gl_dirclose
108  */
109 
110 static void
111 gl_dirclose(glob_t* gp, void* handle)
112 {
113 	(gp->gl_closedir)(handle);
114 }
115 
116 /*
117  * default gl_type
118  */
119 
120 static int
121 gl_type(glob_t* gp, const char* path)
122 {
123 	register int	type;
124 	struct stat	st;
125 
126 	if ((*gp->gl_stat)(path, &st))
127 		type = 0;
128 	else if (S_ISDIR(st.st_mode))
129 		type = GLOB_DIR;
130 	else if (!S_ISREG(st.st_mode))
131 		type = GLOB_DEV;
132 	else if (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
133 		type = GLOB_EXE;
134 	else
135 		type = GLOB_REG;
136 	return type;
137 }
138 
139 /*
140  * default gl_attr
141  */
142 
143 static int
144 gl_attr(glob_t* gp, const char* path)
145 {
146 	return strchr(astconf("PATH_ATTRIBUTES", path, NiL), 'c') ? GLOB_ICASE : 0;
147 }
148 
149 /*
150  * default gl_nextdir
151  */
152 
153 static char*
154 gl_nextdir(glob_t* gp, char* dir)
155 {
156 	if (!(dir = gp->gl_nextpath))
157 		dir = gp->gl_nextpath = stakcopy(pathbin());
158 	switch (*gp->gl_nextpath)
159 	{
160 	case 0:
161 		dir = 0;
162 		break;
163 	case ':':
164 		while (*gp->gl_nextpath == ':')
165 			gp->gl_nextpath++;
166 		dir = ".";
167 		break;
168 	default:
169 		while (*gp->gl_nextpath)
170 			if (*gp->gl_nextpath++ == ':')
171 			{
172 				*(gp->gl_nextpath - 1) = 0;
173 				break;
174 			}
175 		break;
176 	}
177 	return dir;
178 }
179 
180 /*
181  * error intercept
182  */
183 
184 static int
185 errorcheck(register glob_t* gp, const char* path)
186 {
187 	int	r = 1;
188 
189 	if (gp->gl_errfn)
190 		r = (*gp->gl_errfn)(path, errno);
191 	if (gp->gl_flags & GLOB_ERR)
192 		r = 0;
193 	if (!r)
194 		gp->gl_error = GLOB_ABORTED;
195 	return r;
196 }
197 
198 /*
199  * remove backslashes
200  */
201 
202 static void
203 trim(register char* sp, register char* p1, int* n1, register char* p2, int* n2)
204 {
205 	register char*	dp = sp;
206 	register int	c;
207 	register int	n;
208 
209 	if (p1)
210 		*n1 = 0;
211 	if (p2)
212 		*n2 = 0;
213 	do
214 	{
215 		if ((c = *sp++) == '\\' && (c = *sp++))
216 			n++;
217 		if (sp == p1)
218 		{
219 			p1 = 0;
220 			*n1 = sp - dp - 1;
221 		}
222 		if (sp == p2)
223 		{
224 			p2 = 0;
225 			*n2 = sp - dp - 1;
226 		}
227 	} while (*dp++ = c);
228 }
229 
230 static void
231 addmatch(register glob_t* gp, const char* dir, const char* pat, register const char* rescan, char* endslash, int meta)
232 {
233 	register globlist_t*	ap;
234 	int			offset;
235 	int			type;
236 
237 	stakseek(MATCHPATH(gp));
238 	if (dir)
239 	{
240 		stakputs(dir);
241 		stakputc(gp->gl_delim);
242 	}
243 	if (endslash)
244 		*endslash = 0;
245 	stakputs(pat);
246 	if (rescan)
247 	{
248 		if ((*gp->gl_type)(gp, stakptr(MATCHPATH(gp))) != GLOB_DIR)
249 			return;
250 		stakputc(gp->gl_delim);
251 		offset = staktell();
252 		/* if null, reserve room for . */
253 		if (*rescan)
254 			stakputs(rescan);
255 		else
256 			stakputc(0);
257 		stakputc(0);
258 		rescan = stakptr(offset);
259 		ap = (globlist_t*)stakfreeze(0);
260 		ap->gl_begin = (char*)rescan;
261 		ap->gl_next = gp->gl_rescan;
262 		gp->gl_rescan = ap;
263 	}
264 	else
265 	{
266 		if (!endslash && (gp->gl_flags & GLOB_MARK) && (type = (*gp->gl_type)(gp, stakptr(MATCHPATH(gp)))))
267 		{
268 			if ((gp->gl_flags & GLOB_COMPLETE) && type != GLOB_EXE)
269 			{
270 				stakseek(0);
271 				return;
272 			}
273 			else if (type == GLOB_DIR && (gp->gl_flags & GLOB_MARK))
274 				stakputc(gp->gl_delim);
275 		}
276 		ap = (globlist_t*)stakfreeze(1);
277 		ap->gl_next = gp->gl_match;
278 		gp->gl_match = ap;
279 		gp->gl_pathc++;
280 	}
281 	ap->gl_flags = MATCH_RAW|meta;
282 	if (gp->gl_flags & GLOB_COMPLETE)
283 		ap->gl_flags |= MATCH_MAKE;
284 }
285 
286 /*
287  * this routine builds a list of files that match a given pathname
288  * uses REG_SHELL of <regex> to match each component
289  * a leading . must match explicitly
290  */
291 
292 static void
293 glob_dir(glob_t* gp, globlist_t* ap)
294 {
295 	register char*		rescan;
296 	register char*		prefix;
297 	register char*		pat;
298 	register char*		name;
299 	register int		c;
300 	char*			dirname;
301 	void*			dirf;
302 	char			first;
303 	regex_t*		ire;
304 	regex_t*		pre;
305 	regex_t			rec;
306 	regex_t			rei;
307 	int			notdir;
308 	int			t1;
309 	int			t2;
310 	int			bracket;
311 
312 	int			anymeta = ap->gl_flags & MATCH_META;
313 	int			complete = 0;
314 	int			err = 0;
315 	int			meta = ((gp->re_flags & REG_ICASE) && *ap->gl_begin != '/') ? MATCH_META : 0;
316 	int			quote = 0;
317 	int			savequote = 0;
318 	char*			restore1 = 0;
319 	char*			restore2 = 0;
320 	regex_t*		prec = 0;
321 	regex_t*		prei = 0;
322 	char*			matchdir = 0;
323 	int			starstar = 0;
324 
325 	if (*gp->gl_intr)
326 	{
327 		gp->gl_error = GLOB_INTR;
328 		return;
329 	}
330 	pat = rescan = ap->gl_begin;
331 	prefix = dirname = ap->gl_path + gp->gl_extra;
332 	first = (rescan == prefix);
333 again:
334 	bracket = 0;
335 	for (;;)
336 	{
337 		switch (c = *rescan++)
338 		{
339 		case 0:
340 			if (meta)
341 			{
342 				rescan = 0;
343 				break;
344 			}
345 			if (quote)
346 			{
347 				trim(ap->gl_begin, rescan, &t1, NiL, NiL);
348 				rescan -= t1;
349 			}
350 			if (!first && !*rescan && *(rescan - 2) == gp->gl_delim)
351 			{
352 				*(rescan - 2) = 0;
353 				c = (*gp->gl_type)(gp, prefix);
354 				*(rescan - 2) = gp->gl_delim;
355 				if (c == GLOB_DIR)
356 					addmatch(gp, NiL, prefix, NiL, rescan - 1, anymeta);
357 			}
358 			else if ((anymeta || !(gp->gl_flags & GLOB_NOCHECK)) && (*gp->gl_type)(gp, prefix))
359 				addmatch(gp, NiL, prefix, NiL, NiL, anymeta);
360 			return;
361 		case '[':
362 			if (!bracket)
363 			{
364 				bracket = MATCH_META;
365 				if (*rescan == '!' || *rescan == '^')
366 					rescan++;
367 				if (*rescan == ']')
368 					rescan++;
369 			}
370 			continue;
371 		case ']':
372 			meta |= bracket;
373 			continue;
374 		case '(':
375 			if (!(gp->gl_flags & GLOB_AUGMENTED))
376 				continue;
377 		case '*':
378 		case '?':
379 			meta = MATCH_META;
380 			continue;
381 		case '\\':
382 			if (!(gp->gl_flags & GLOB_NOESCAPE))
383 			{
384 				quote = 1;
385 				if (*rescan)
386 					rescan++;
387 			}
388 			continue;
389 		default:
390 			if (c == gp->gl_delim)
391 			{
392 				if (meta)
393 					break;
394 				pat = rescan;
395 				bracket = 0;
396 				savequote = quote;
397 			}
398 			continue;
399 		}
400 		break;
401 	}
402 	anymeta |= meta;
403 	if (matchdir)
404 		goto skip;
405 	if (pat == prefix)
406 	{
407 		prefix = 0;
408 		if (!rescan && (gp->gl_flags & GLOB_COMPLETE))
409 		{
410 			complete = 1;
411 			dirname = 0;
412 		}
413 		else
414 			dirname = ".";
415 	}
416 	else
417 	{
418 		if (pat == prefix + 1)
419 			dirname = "/";
420 		if (savequote)
421 		{
422 			quote = 0;
423 			trim(ap->gl_begin, pat, &t1, rescan, &t2);
424 			pat -= t1;
425 			if (rescan)
426 				rescan -= t2;
427 		}
428 		*(restore1 = pat - 1) = 0;
429 	}
430 	if (!complete && (gp->gl_flags & GLOB_STARSTAR))
431 		while (pat[0] == '*' && pat[1] == '*' && (pat[2] == '/'  || pat[2]==0))
432 		{
433 			matchdir = pat;
434 			if (pat[2])
435 			{
436 				pat += 3;
437 				while (*pat=='/') pat++;
438 				if (*pat)
439 					continue;
440 			}
441 			rescan = *pat?0:pat;
442 			pat = "*";
443 			goto skip;
444 		}
445 	if (matchdir)
446 	{
447 		rescan = pat;
448 		goto again;
449 	}
450 skip:
451 	if (rescan)
452 		*(restore2 = rescan - 1) = 0;
453 	if (rescan && !complete && (gp->gl_flags & GLOB_STARSTAR))
454 	{
455 		register char *p = rescan;
456 		while (p[0] == '*' && p[1] == '*' && (p[2] == '/'  || p[2]==0))
457 		{
458 			rescan = p;
459 			if (starstar = (p[2]==0))
460 				break;
461 			p += 3;
462 			while (*p=='/')
463 				p++;
464 			if (*p==0)
465 			{
466 				starstar = 2;
467 				break;
468 			}
469 		}
470 	}
471 	if (matchdir)
472 		gp->gl_starstar++;
473 	if (gp->gl_opt)
474 		pat = strcpy(gp->gl_opt, pat);
475 	for (;;)
476 	{
477 		if (complete)
478 		{
479 			if (!(dirname = (*gp->gl_nextdir)(gp, dirname)))
480 				break;
481 			prefix = streq(dirname, ".") ? (char*)0 : dirname;
482 		}
483 		if (dirf = (*gp->gl_diropen)(gp, dirname))
484 		{
485 			if (!(gp->re_flags & REG_ICASE) && ((*gp->gl_attr)(gp, dirname) & GLOB_ICASE))
486 			{
487 				if (!prei)
488 				{
489 					if (err = regcomp(&rei, pat, gp->re_flags|REG_ICASE))
490 						break;
491 					prei = &rei;
492 				}
493 				pre = prei;
494 				if (gp->gl_ignore)
495 				{
496 					if (!gp->gl_ignorei)
497 					{
498 						if (regcomp(&gp->re_ignorei, gp->gl_fignore, gp->re_flags|REG_ICASE))
499 						{
500 							gp->gl_error = GLOB_APPERR;
501 							break;
502 						}
503 						gp->gl_ignorei = &gp->re_ignorei;
504 					}
505 					ire = gp->gl_ignorei;
506 				}
507 				else
508 					ire = 0;
509 			}
510 			else
511 			{
512 				if (!prec)
513 				{
514 					if (err = regcomp(&rec, pat, gp->re_flags))
515 						break;
516 					prec = &rec;
517 				}
518 				pre = prec;
519 				ire = gp->gl_ignore;
520 			}
521 			if (restore2)
522 				*restore2 = gp->gl_delim;
523 			while ((name = (*gp->gl_dirnext)(gp, dirf)) && !*gp->gl_intr)
524 			{
525 				if (notdir = (gp->gl_status & GLOB_NOTDIR))
526 					gp->gl_status &= ~GLOB_NOTDIR;
527 				if (ire && !regexec(ire, name, 0, NiL, 0))
528 					continue;
529 				if (matchdir && (name[0] != '.' || name[1] && (name[1] != '.' || name[2])) && !notdir)
530 					addmatch(gp, prefix, name, matchdir, NiL, anymeta);
531 				if (!regexec(pre, name, 0, NiL, 0))
532 				{
533 					if (!rescan || !notdir)
534 						addmatch(gp, prefix, name, rescan, NiL, anymeta);
535 					if (starstar==1 || (starstar==2 && !notdir))
536 						addmatch(gp, prefix, name, starstar==2?"":NiL, NiL, anymeta);
537 				}
538 				errno = 0;
539 			}
540 			(*gp->gl_dirclose)(gp, dirf);
541 			if (err || errno && !errorcheck(gp, dirname))
542 				break;
543 		}
544 		else if (!complete && !errorcheck(gp, dirname))
545 			break;
546 		if (!complete)
547 			break;
548 		if (*gp->gl_intr)
549 		{
550 			gp->gl_error = GLOB_INTR;
551 			break;
552 		}
553 	}
554 	if (restore1)
555 		*restore1 = gp->gl_delim;
556 	if (restore2)
557 		*restore2 = gp->gl_delim;
558 	if (prec)
559 		regfree(prec);
560 	if (prei)
561 		regfree(prei);
562 	if (err == REG_ESPACE)
563 		gp->gl_error = GLOB_NOSPACE;
564 }
565 
566 int
567 glob(const char* pattern, int flags, int (*errfn)(const char*, int), register glob_t* gp)
568 {
569 	register globlist_t*	ap;
570 	register char*		pat;
571 	globlist_t*		top;
572 	Stak_t*			oldstak;
573 	char**			argv;
574 	char**			av;
575 	size_t			skip;
576 	unsigned long		f;
577 	int			n;
578 	int			x;
579 
580 	const char*		nocheck = pattern;
581 	int			optlen = 0;
582 	int			suflen = 0;
583 	int			extra = 1;
584 	unsigned char		intr = 0;
585 
586 	gp->gl_rescan = 0;
587 	gp->gl_error = 0;
588 	gp->gl_errfn = errfn;
589 	if (flags & GLOB_APPEND)
590 	{
591 		if ((gp->gl_flags |= GLOB_APPEND) ^ (flags|GLOB_MAGIC))
592 			return GLOB_APPERR;
593 		if (((gp->gl_flags & GLOB_STACK) == 0) == (gp->gl_stak == 0))
594 			return GLOB_APPERR;
595 		if (gp->gl_starstar > 1)
596 			gp->gl_flags |= GLOB_STARSTAR;
597 		else
598 			gp->gl_starstar = 0;
599 	}
600 	else
601 	{
602 		gp->gl_flags = (flags&0xffff)|GLOB_MAGIC;
603 		gp->re_flags = REG_SHELL|REG_NOSUB|REG_LEFT|REG_RIGHT|((flags&GLOB_AUGMENTED)?REG_AUGMENTED:0);
604 		gp->gl_pathc = 0;
605 		gp->gl_ignore = 0;
606 		gp->gl_ignorei = 0;
607 		gp->gl_starstar = 0;
608 		if (!(flags & GLOB_DISC))
609 		{
610 			gp->gl_fignore = 0;
611 			gp->gl_suffix = 0;
612 			gp->gl_intr = 0;
613 			gp->gl_delim = 0;
614 			gp->gl_handle = 0;
615 			gp->gl_diropen = 0;
616 			gp->gl_dirnext = 0;
617 			gp->gl_dirclose = 0;
618 			gp->gl_type = 0;
619 			gp->gl_attr = 0;
620 			gp->gl_nextdir = 0;
621 			gp->gl_stat = 0;
622 			gp->gl_extra = 0;
623 		}
624 		if (!(flags & GLOB_ALTDIRFUNC))
625 		{
626 			gp->gl_opendir = (GL_opendir_f)opendir;
627 			gp->gl_readdir = (GL_readdir_f)readdir;
628 			gp->gl_closedir = (GL_closedir_f)closedir;
629 			if (!gp->gl_stat)
630 				gp->gl_stat = (flags & GLOB_STARSTAR) ? (GL_stat_f)lstat : (GL_stat_f)pathstat;
631 		}
632 		if (!gp->gl_intr)
633 			gp->gl_intr = &intr;
634 		if (!gp->gl_delim)
635 			gp->gl_delim = '/';
636 		if (!gp->gl_diropen)
637 			gp->gl_diropen = gl_diropen;
638 		if (!gp->gl_dirnext)
639 			gp->gl_dirnext = gl_dirnext;
640 		if (!gp->gl_dirclose)
641 			gp->gl_dirclose = gl_dirclose;
642 		if (!gp->gl_type)
643 			gp->gl_type = gl_type;
644 		if (!gp->gl_attr)
645 			gp->gl_attr = gl_attr;
646 		if (flags & GLOB_ICASE)
647 			gp->re_flags |= REG_ICASE;
648 		if (!gp->gl_fignore)
649 			gp->re_flags |= REG_SHELL_DOT;
650 		else if (*gp->gl_fignore)
651 		{
652 			if (regcomp(&gp->re_ignore, gp->gl_fignore, gp->re_flags))
653 				return GLOB_APPERR;
654 			gp->gl_ignore = &gp->re_ignore;
655 		}
656 		if (gp->gl_flags & GLOB_STACK)
657 			gp->gl_stak = 0;
658 		else if (!(gp->gl_stak = stakcreate(0)))
659 			return GLOB_NOSPACE;
660 		if ((gp->gl_flags & GLOB_COMPLETE) && !gp->gl_nextdir)
661 			gp->gl_nextdir = gl_nextdir;
662 	}
663 	skip = gp->gl_pathc;
664 	if (gp->gl_stak)
665 		oldstak = stakinstall(gp->gl_stak, 0);
666 	if (flags & GLOB_DOOFFS)
667 		extra += gp->gl_offs;
668 	if (gp->gl_suffix)
669 		suflen =  strlen(gp->gl_suffix);
670 	if (*(pat = (char*)pattern) == '~' && *(pat + 1) == '(')
671 	{
672 		f = gp->gl_flags;
673 		n = 1;
674 		x = 1;
675 		pat += 2;
676 		for (;;)
677 		{
678 			switch (*pat++)
679 			{
680 			case 0:
681 			case ':':
682 				break;
683 			case '-':
684 				n = 0;
685 				continue;
686 			case '+':
687 				n = 1;
688 				continue;
689 			case 'i':
690 				if (n)
691 					f |= GLOB_ICASE;
692 				else
693 					f &= ~GLOB_ICASE;
694 				continue;
695 			case 'M':
696 				if (n)
697 					f |= GLOB_BRACE;
698 				else
699 					f &= ~GLOB_BRACE;
700 				continue;
701 			case 'N':
702 				if (n)
703 					f &= ~GLOB_NOCHECK;
704 				else
705 					f |= GLOB_NOCHECK;
706 				continue;
707 			case 'R':
708 				if (n)
709 					f |= GLOB_STARSTAR;
710 				else
711 					f &= ~GLOB_STARSTAR;
712 				continue;
713 			case ')':
714 				flags = (gp->gl_flags = f) & 0xffff;
715 				if (f & GLOB_ICASE)
716 					gp->re_flags |= REG_ICASE;
717 				else
718 					gp->re_flags &= ~REG_ICASE;
719 				if ((f & (GLOB_STARSTAR|GLOB_ALTDIRFUNC)) == GLOB_STARSTAR)
720 					gp->gl_stat = (GL_stat_f)lstat;
721 				if (x)
722 					optlen = pat - (char*)pattern;
723 				break;
724 			default:
725 				x = 0;
726 				continue;
727 			}
728 			break;
729 		}
730 	}
731 	top = ap = (globlist_t*)stakalloc((optlen ? 2 : 1) * strlen(pattern) + sizeof(globlist_t) + suflen + gp->gl_extra);
732 	ap->gl_next = 0;
733 	ap->gl_flags = 0;
734 	ap->gl_begin = ap->gl_path + gp->gl_extra;
735 	pat = strcopy(ap->gl_begin, pattern + optlen);
736 	if (suflen)
737 		pat = strcopy(pat, gp->gl_suffix);
738 	gp->gl_pat = optlen ? strncpy(gp->gl_opt = pat + 1, pattern, optlen) : (char*)0;
739 	suflen = 0;
740 	if (!(flags & GLOB_LIST))
741 		gp->gl_match = 0;
742 	do
743 	{
744 		gp->gl_rescan = ap->gl_next;
745 		glob_dir(gp, ap);
746 	} while (!gp->gl_error && (ap = gp->gl_rescan));
747 	if (gp->gl_pathc == skip)
748 	{
749 		if (flags & GLOB_NOCHECK)
750 		{
751 			gp->gl_pathc++;
752 			top->gl_next = gp->gl_match;
753 			gp->gl_match = top;
754 			strcopy(top->gl_path + gp->gl_extra, nocheck);
755 		}
756 		else
757 			gp->gl_error = GLOB_NOMATCH;
758 	}
759 	if (flags & GLOB_LIST)
760 		gp->gl_list = gp->gl_match;
761 	else
762 	{
763 		argv = (char**)stakalloc((gp->gl_pathc + extra) * sizeof(char*));
764 		if (gp->gl_flags & GLOB_APPEND)
765 		{
766 			skip += --extra;
767 			memcpy(argv, gp->gl_pathv, skip * sizeof(char*));
768 			av = argv + skip;
769 		}
770 		else
771 		{
772 			av = argv;
773 			while (--extra > 0)
774 				*av++ = 0;
775 		}
776 		gp->gl_pathv = argv;
777 		argv = av;
778 		ap = gp->gl_match;
779 		while (ap)
780 		{
781 			*argv++ = ap->gl_path + gp->gl_extra;
782 			ap = ap->gl_next;
783 		}
784 		*argv = 0;
785 		if (!(flags & GLOB_NOSORT) && (argv - av) > 1)
786 		{
787 			strsort(av, argv - av, strcoll);
788 			if (gp->gl_starstar > 1)
789 				av[gp->gl_pathc = struniq(av, argv - av)] = 0;
790 			gp->gl_starstar = 0;
791 		}
792 	}
793 	if (gp->gl_starstar > 1)
794 		gp->gl_flags &= ~GLOB_STARSTAR;
795 	if (gp->gl_stak)
796 		stakinstall(oldstak, 0);
797 	return gp->gl_error;
798 }
799 
800 void
801 globfree(glob_t* gp)
802 {
803 	if ((gp->gl_flags & GLOB_MAGIC) == GLOB_MAGIC)
804 	{
805 		gp->gl_flags &= ~GLOB_MAGIC;
806 		if (gp->gl_stak)
807 			stkclose(gp->gl_stak);
808 		if (gp->gl_ignore)
809 			regfree(gp->gl_ignore);
810 		if (gp->gl_ignorei)
811 			regfree(gp->gl_ignorei);
812 	}
813 }
814