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