9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

glob.c (4540B)


      1 #include "rc.h"
      2 #include "exec.h"
      3 #include "fns.h"
      4 char *globname;
      5 struct word *globv;
      6 /*
      7  * delete all the GLOB marks from s, in place
      8  */
      9 
     10 void
     11 deglob(char *s)
     12 {
     13 	char *t = s;
     14 	do{
     15 		if(*t==GLOB)
     16 			t++;
     17 		*s++=*t;
     18 	}while(*t++);
     19 }
     20 
     21 int
     22 globcmp(const void *s, const void *t)
     23 {
     24 	return strcmp(*(char**)s, *(char**)t);
     25 }
     26 
     27 void
     28 globsort(word *left, word *right)
     29 {
     30 	char **list;
     31 	word *a;
     32 	int n = 0;
     33 	for(a = left;a!=right;a = a->next) n++;
     34 	list = (char **)emalloc(n*sizeof(char *));
     35 	for(a = left,n = 0;a!=right;a = a->next,n++) list[n] = a->word;
     36 	qsort((void *)list, n, sizeof(void *), globcmp);
     37 	for(a = left,n = 0;a!=right;a = a->next,n++) a->word = list[n];
     38 	efree((char *)list);
     39 }
     40 /*
     41  * Push names prefixed by globname and suffixed by a match of p onto the astack.
     42  * namep points to the end of the prefix in globname.
     43  */
     44 
     45 void
     46 globdir(char *p, char *namep)
     47 {
     48 	char *t, *newp;
     49 	int f;
     50 	/* scan the pattern looking for a component with a metacharacter in it */
     51 	if(*p=='\0'){
     52 		globv = newword(globname, globv);
     53 		return;
     54 	}
     55 	t = namep;
     56 	newp = p;
     57 	while(*newp){
     58 		if(*newp==GLOB)
     59 			break;
     60 		*t=*newp++;
     61 		if(*t++=='/'){
     62 			namep = t;
     63 			p = newp;
     64 		}
     65 	}
     66 	/* If we ran out of pattern, append the name if accessible */
     67 	if(*newp=='\0'){
     68 		*t='\0';
     69 		if(access(globname, 0)==0)
     70 			globv = newword(globname, globv);
     71 		return;
     72 	}
     73 	/* read the directory and recur for any entry that matches */
     74 	*namep='\0';
     75 	if((f = Opendir(globname[0]?globname:"."))<0) return;
     76 	while(*newp!='/' && *newp!='\0') newp++;
     77 	while(Readdir(f, namep, *newp=='/')){
     78 		if(matchfn(namep, p)){
     79 			for(t = namep;*t;t++);
     80 			globdir(newp, t);
     81 		}
     82 	}
     83 	Closedir(f);
     84 }
     85 /*
     86  * Push all file names matched by p on the current thread's stack.
     87  * If there are no matches, the list consists of p.
     88  */
     89 
     90 void
     91 glob(char *p)
     92 {
     93 	word *svglobv = globv;
     94 	int globlen = Globsize(p);
     95 	if(!globlen){
     96 		deglob(p);
     97 		globv = newword(p, globv);
     98 		return;
     99 	}
    100 	globname = emalloc(globlen);
    101 	globname[0]='\0';
    102 	globdir(p, globname);
    103 	efree(globname);
    104 	if(svglobv==globv){
    105 		deglob(p);
    106 		globv = newword(p, globv);
    107 	}
    108 	else
    109 		globsort(globv, svglobv);
    110 }
    111 /*
    112  * Do p and q point at equal utf codes
    113  */
    114 
    115 int
    116 equtf(char *p, char *q)
    117 {
    118 	if(*p!=*q)
    119 		return 0;
    120 	if(twobyte(*p)) return p[1]==q[1];
    121 	if(threebyte(*p)){
    122 		if(p[1]!=q[1])
    123 			return 0;
    124 		if(p[1]=='\0')
    125 			return 1;	/* broken code at end of string! */
    126 		return p[2]==q[2];
    127 	}
    128 	return 1;
    129 }
    130 /*
    131  * Return a pointer to the next utf code in the string,
    132  * not jumping past nuls in broken utf codes!
    133  */
    134 
    135 char*
    136 nextutf(char *p)
    137 {
    138 	if(twobyte(*p)) return p[1]=='\0'?p+1:p+2;
    139 	if(threebyte(*p)) return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3;
    140 	return p+1;
    141 }
    142 /*
    143  * Convert the utf code at *p to a unicode value
    144  */
    145 
    146 int
    147 unicode(char *p)
    148 {
    149 	int u=*p&0xff;
    150 	if(twobyte(u)) return ((u&0x1f)<<6)|(p[1]&0x3f);
    151 	if(threebyte(u)) return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f);
    152 	return u;
    153 }
    154 /*
    155  * Does the string s match the pattern p
    156  * . and .. are only matched by patterns starting with .
    157  * * matches any sequence of characters
    158  * ? matches any single character
    159  * [...] matches the enclosed list of characters
    160  */
    161 
    162 int
    163 matchfn(char *s, char *p)
    164 {
    165 	if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.')
    166 		return 0;
    167 	return match(s, p, '/');
    168 }
    169 
    170 int
    171 match(char *s, char *p, int stop)
    172 {
    173 	int compl, hit, lo, hi, t, c;
    174 	for(;*p!=stop && *p!='\0';s = nextutf(s),p = nextutf(p)){
    175 		if(*p!=GLOB){
    176 			if(!equtf(p, s)) return 0;
    177 		}
    178 		else switch(*++p){
    179 		case GLOB:
    180 			if(*s!=GLOB)
    181 				return 0;
    182 			break;
    183 		case '*':
    184 			for(;;){
    185 				if(match(s, nextutf(p), stop)) return 1;
    186 				if(!*s)
    187 					break;
    188 				s = nextutf(s);
    189 			}
    190 			return 0;
    191 		case '?':
    192 			if(*s=='\0')
    193 				return 0;
    194 			break;
    195 		case '[':
    196 			if(*s=='\0')
    197 				return 0;
    198 			c = unicode(s);
    199 			p++;
    200 			compl=*p=='~';
    201 			if(compl)
    202 				p++;
    203 			hit = 0;
    204 			while(*p!=']'){
    205 				if(*p=='\0')
    206 					return 0;		/* syntax error */
    207 				lo = unicode(p);
    208 				p = nextutf(p);
    209 				if(*p!='-')
    210 					hi = lo;
    211 				else{
    212 					p++;
    213 					if(*p=='\0')
    214 						return 0;	/* syntax error */
    215 					hi = unicode(p);
    216 					p = nextutf(p);
    217 					if(hi<lo){ t = lo; lo = hi; hi = t; }
    218 				}
    219 				if(lo<=c && c<=hi)
    220 					hit = 1;
    221 			}
    222 			if(compl)
    223 				hit=!hit;
    224 			if(!hit)
    225 				return 0;
    226 			break;
    227 		}
    228 	}
    229 	return *s=='\0';
    230 }
    231 
    232 void
    233 globlist1(word *gl)
    234 {
    235 	if(gl){
    236 		globlist1(gl->next);
    237 		glob(gl->word);
    238 	}
    239 }
    240 
    241 void
    242 globlist(void)
    243 {
    244 	word *a;
    245 	globv = 0;
    246 	globlist1(runq->argv->words);
    247 	poplist();
    248 	pushlist();
    249 	if(globv){
    250 		for(a = globv;a->next;a = a->next);
    251 		a->next = runq->argv->words;
    252 		runq->argv->words = globv;
    253 	}
    254 }