| #include "rc.h" | 
 | #include "exec.h" | 
 | #include "fns.h" | 
 | char *globname; | 
 | struct word *globv; | 
 | /* | 
 |  * delete all the GLOB marks from s, in place | 
 |  */ | 
 |  | 
 | void | 
 | deglob(char *s) | 
 | { | 
 | 	char *t = s; | 
 | 	do{ | 
 | 		if(*t==GLOB) | 
 | 			t++; | 
 | 		*s++=*t; | 
 | 	}while(*t++); | 
 | } | 
 |  | 
 | int | 
 | globcmp(const void *s, const void *t) | 
 | { | 
 | 	return strcmp(*(char**)s, *(char**)t); | 
 | } | 
 |  | 
 | void | 
 | globsort(word *left, word *right) | 
 | { | 
 | 	char **list; | 
 | 	word *a; | 
 | 	int n = 0; | 
 | 	for(a = left;a!=right;a = a->next) n++; | 
 | 	list = (char **)emalloc(n*sizeof(char *)); | 
 | 	for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word; | 
 | 	qsort((void *)list, n, sizeof(void *), globcmp); | 
 | 	for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n]; | 
 | 	efree((char *)list); | 
 | } | 
 | /* | 
 |  * Push names prefixed by globname and suffixed by a match of p onto the astack. | 
 |  * namep points to the end of the prefix in globname. | 
 |  */ | 
 |  | 
 | void | 
 | globdir(char *p, char *namep) | 
 | { | 
 | 	char *t, *newp; | 
 | 	int f; | 
 | 	/* scan the pattern looking for a component with a metacharacter in it */ | 
 | 	if(*p=='\0'){ | 
 | 		globv = newword(globname, globv); | 
 | 		return; | 
 | 	} | 
 | 	t = namep; | 
 | 	newp = p; | 
 | 	while(*newp){ | 
 | 		if(*newp==GLOB) | 
 | 			break; | 
 | 		*t=*newp++; | 
 | 		if(*t++=='/'){ | 
 | 			namep = t; | 
 | 			p = newp; | 
 | 		} | 
 | 	} | 
 | 	/* If we ran out of pattern, append the name if accessible */ | 
 | 	if(*newp=='\0'){ | 
 | 		*t='\0'; | 
 | 		if(access(globname, 0)==0) | 
 | 			globv = newword(globname, globv); | 
 | 		return; | 
 | 	} | 
 | 	/* read the directory and recur for any entry that matches */ | 
 | 	*namep='\0'; | 
 | 	if((f = Opendir(globname[0]?globname:"."))<0) return; | 
 | 	while(*newp!='/' && *newp!='\0') newp++; | 
 | 	while(Readdir(f, namep, *newp=='/')){ | 
 | 		if(matchfn(namep, p)){ | 
 | 			for(t = namep;*t;t++); | 
 | 			globdir(newp, t); | 
 | 		} | 
 | 	} | 
 | 	Closedir(f); | 
 | } | 
 | /* | 
 |  * Push all file names matched by p on the current thread's stack. | 
 |  * If there are no matches, the list consists of p. | 
 |  */ | 
 |  | 
 | void | 
 | glob(char *p) | 
 | { | 
 | 	word *svglobv = globv; | 
 | 	int globlen = Globsize(p); | 
 | 	if(!globlen){ | 
 | 		deglob(p); | 
 | 		globv = newword(p, globv); | 
 | 		return; | 
 | 	} | 
 | 	globname = emalloc(globlen); | 
 | 	globname[0]='\0'; | 
 | 	globdir(p, globname); | 
 | 	efree(globname); | 
 | 	if(svglobv==globv){ | 
 | 		deglob(p); | 
 | 		globv = newword(p, globv); | 
 | 	} | 
 | 	else | 
 | 		globsort(globv, svglobv); | 
 | } | 
 | /* | 
 |  * Do p and q point at equal utf codes | 
 |  */ | 
 |  | 
 | int | 
 | equtf(char *p, char *q) | 
 | { | 
 | 	if(*p!=*q) | 
 | 		return 0; | 
 | 	if(twobyte(*p)) return p[1]==q[1]; | 
 | 	if(threebyte(*p)){ | 
 | 		if(p[1]!=q[1]) | 
 | 			return 0; | 
 | 		if(p[1]=='\0') | 
 | 			return 1;	/* broken code at end of string! */ | 
 | 		return p[2]==q[2]; | 
 | 	} | 
 | 	if(fourbyte(*p)){ | 
 | 		if(p[1]!=q[1]) | 
 | 			return 0; | 
 | 		if(p[1]=='\0') | 
 | 			return 1; | 
 | 		if(p[2]!=q[2]) | 
 | 			return 0; | 
 | 		if(p[2]=='\0') | 
 | 			return 1; | 
 | 		return p[3]==q[3]; | 
 | 	} | 
 | 	return 1; | 
 | } | 
 | /* | 
 |  * Return a pointer to the next utf code in the string, | 
 |  * not jumping past nuls in broken utf codes! | 
 |  */ | 
 |  | 
 | char* | 
 | nextutf(char *p) | 
 | { | 
 | 	if(twobyte(*p)) return p[1]=='\0'?p+1:p+2; | 
 | 	if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; | 
 | 	if(fourbyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p[3]=='\0'?p+3:p+4; | 
 | 	return p+1; | 
 | } | 
 | /* | 
 |  * Convert the utf code at *p to a unicode value | 
 |  */ | 
 |  | 
 | int | 
 | unicode(char *p) | 
 | { | 
 | 	int u=*p&0xff; | 
 | 	if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f); | 
 | 	if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); | 
 | 	if(fourbyte(u)) return (u<<18)|((p[1]&0x3f)<<12)|((p[2]&0x3f)<<6)|(p[3]&0x3f); | 
 | 	return u; | 
 | } | 
 | /* | 
 |  * Does the string s match the pattern p | 
 |  * . and .. are only matched by patterns starting with . | 
 |  * * matches any sequence of characters | 
 |  * ? matches any single character | 
 |  * [...] matches the enclosed list of characters | 
 |  */ | 
 |  | 
 | int | 
 | matchfn(char *s, char *p) | 
 | { | 
 | 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') | 
 | 		return 0; | 
 | 	return match(s, p, '/'); | 
 | } | 
 |  | 
 | int | 
 | match(char *s, char *p, int stop) | 
 | { | 
 | 	int compl, hit, lo, hi, t, c; | 
 | 	for(;*p!=stop && *p!='\0';s = nextutf(s),p = nextutf(p)){ | 
 | 		if(*p!=GLOB){ | 
 | 			if(!equtf(p, s)) return 0; | 
 | 		} | 
 | 		else switch(*++p){ | 
 | 		case GLOB: | 
 | 			if(*s!=GLOB) | 
 | 				return 0; | 
 | 			break; | 
 | 		case '*': | 
 | 			for(;;){ | 
 | 				if(match(s, nextutf(p), stop)) return 1; | 
 | 				if(!*s) | 
 | 					break; | 
 | 				s = nextutf(s); | 
 | 			} | 
 | 			return 0; | 
 | 		case '?': | 
 | 			if(*s=='\0') | 
 | 				return 0; | 
 | 			break; | 
 | 		case '[': | 
 | 			if(*s=='\0') | 
 | 				return 0; | 
 | 			c = unicode(s); | 
 | 			p++; | 
 | 			compl=*p=='~'; | 
 | 			if(compl) | 
 | 				p++; | 
 | 			hit = 0; | 
 | 			while(*p!=']'){ | 
 | 				if(*p=='\0') | 
 | 					return 0;		/* syntax error */ | 
 | 				lo = unicode(p); | 
 | 				p = nextutf(p); | 
 | 				if(*p!='-') | 
 | 					hi = lo; | 
 | 				else{ | 
 | 					p++; | 
 | 					if(*p=='\0') | 
 | 						return 0;	/* syntax error */ | 
 | 					hi = unicode(p); | 
 | 					p = nextutf(p); | 
 | 					if(hi<lo){ t = lo; lo = hi; hi = t; } | 
 | 				} | 
 | 				if(lo<=c && c<=hi) | 
 | 					hit = 1; | 
 | 			} | 
 | 			if(compl) | 
 | 				hit=!hit; | 
 | 			if(!hit) | 
 | 				return 0; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return *s=='\0'; | 
 | } | 
 |  | 
 | void | 
 | globlist1(word *gl) | 
 | { | 
 | 	if(gl){ | 
 | 		globlist1(gl->next); | 
 | 		glob(gl->word); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | globlist(void) | 
 | { | 
 | 	word *a; | 
 | 	globv = 0; | 
 | 	globlist1(runq->argv->words); | 
 | 	poplist(); | 
 | 	pushlist(); | 
 | 	if(globv){ | 
 | 		for(a = globv;a->next;a = a->next); | 
 | 		a->next = runq->argv->words; | 
 | 		runq->argv->words = globv; | 
 | 	} | 
 | } |