xref: /plan9/sys/src/cmd/ratfs/misc.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1*9a747e4fSDavid du Colombier #include "ratfs.h"
2*9a747e4fSDavid du Colombier #include <ip.h>
3*9a747e4fSDavid du Colombier 
4*9a747e4fSDavid du Colombier enum {
5*9a747e4fSDavid du Colombier 	Maxdoms	=	10,	/* max domains in a path */
6*9a747e4fSDavid du Colombier 	Timeout =	15*60,	/* seconds until temporarily trusted addr times out */
7*9a747e4fSDavid du Colombier };
8*9a747e4fSDavid du Colombier 
9*9a747e4fSDavid du Colombier static	int	accountmatch(char*, char**, int, char*);
10*9a747e4fSDavid du Colombier static	Node*	acctwalk(char*,  Node*);
11*9a747e4fSDavid du Colombier static	int	dommatch(char*, char*);
12*9a747e4fSDavid du Colombier static	Address* ipsearch(ulong, Address*, int);
13*9a747e4fSDavid du Colombier static	Node*	ipwalk(char*,  Node*);
14*9a747e4fSDavid du Colombier static	Node*	trwalk(char*, Node*);
15*9a747e4fSDavid du Colombier static	int	usermatch(char*, char*);
16*9a747e4fSDavid du Colombier 
17*9a747e4fSDavid du Colombier /*
18*9a747e4fSDavid du Colombier  *	Do a walk
19*9a747e4fSDavid du Colombier  */
20*9a747e4fSDavid du Colombier char*
21*9a747e4fSDavid du Colombier walk(char *name, Fid *fidp)
22*9a747e4fSDavid du Colombier {
23*9a747e4fSDavid du Colombier 	Node *np;
24*9a747e4fSDavid du Colombier 
25*9a747e4fSDavid du Colombier 	if((fidp->node->d.mode & DMDIR) == 0)
26*9a747e4fSDavid du Colombier 		return "not a directory";
27*9a747e4fSDavid du Colombier 
28*9a747e4fSDavid du Colombier 	if(strcmp(name, ".") == 0)
29*9a747e4fSDavid du Colombier 		return 0;
30*9a747e4fSDavid du Colombier 	if(strcmp(name, "..") == 0){
31*9a747e4fSDavid du Colombier 		fidp->node = fidp->node->parent;
32*9a747e4fSDavid du Colombier 		fidp->name = 0;
33*9a747e4fSDavid du Colombier 		return 0;
34*9a747e4fSDavid du Colombier 	}
35*9a747e4fSDavid du Colombier 
36*9a747e4fSDavid du Colombier 	switch(fidp->node->d.type){
37*9a747e4fSDavid du Colombier 	case Directory:
38*9a747e4fSDavid du Colombier 	case Addrdir:
39*9a747e4fSDavid du Colombier 		np = dirwalk(name, fidp->node);
40*9a747e4fSDavid du Colombier 		break;
41*9a747e4fSDavid du Colombier 	case Trusted:
42*9a747e4fSDavid du Colombier 		np = trwalk(name, fidp->node);
43*9a747e4fSDavid du Colombier 		break;
44*9a747e4fSDavid du Colombier 	case IPaddr:
45*9a747e4fSDavid du Colombier 		np = ipwalk(name, fidp->node);
46*9a747e4fSDavid du Colombier 		break;
47*9a747e4fSDavid du Colombier 	case Acctaddr:
48*9a747e4fSDavid du Colombier 		np = acctwalk(name, fidp->node);
49*9a747e4fSDavid du Colombier 		break;
50*9a747e4fSDavid du Colombier 	default:
51*9a747e4fSDavid du Colombier 		return "directory botch in walk";
52*9a747e4fSDavid du Colombier 	}
53*9a747e4fSDavid du Colombier 	if(np) {
54*9a747e4fSDavid du Colombier 		fidp->node = np;
55*9a747e4fSDavid du Colombier 		fidp->name = np->d.name;
56*9a747e4fSDavid du Colombier 		return 0;
57*9a747e4fSDavid du Colombier 	}
58*9a747e4fSDavid du Colombier 	return "file does not exist";
59*9a747e4fSDavid du Colombier }
60*9a747e4fSDavid du Colombier 
61*9a747e4fSDavid du Colombier /*
62*9a747e4fSDavid du Colombier  *	Walk to a subdirectory
63*9a747e4fSDavid du Colombier  */
64*9a747e4fSDavid du Colombier Node*
65*9a747e4fSDavid du Colombier dirwalk(char *name, Node *np)
66*9a747e4fSDavid du Colombier {
67*9a747e4fSDavid du Colombier 	Node *p;
68*9a747e4fSDavid du Colombier 
69*9a747e4fSDavid du Colombier 	for(p = np->children; p; p = p->sibs)
70*9a747e4fSDavid du Colombier 		if(strcmp(name, p->d.name) == 0)
71*9a747e4fSDavid du Colombier 			break;
72*9a747e4fSDavid du Colombier 	return p;
73*9a747e4fSDavid du Colombier }
74*9a747e4fSDavid du Colombier 
75*9a747e4fSDavid du Colombier /*
76*9a747e4fSDavid du Colombier  *	Walk the directory of trusted files
77*9a747e4fSDavid du Colombier  */
78*9a747e4fSDavid du Colombier static Node*
79*9a747e4fSDavid du Colombier trwalk(char *name, Node *np)
80*9a747e4fSDavid du Colombier {
81*9a747e4fSDavid du Colombier 	Node *p;
82*9a747e4fSDavid du Colombier 	ulong peerip;
83*9a747e4fSDavid du Colombier 	uchar addr[IPv4addrlen];
84*9a747e4fSDavid du Colombier 
85*9a747e4fSDavid du Colombier 	v4parseip(addr, name);
86*9a747e4fSDavid du Colombier 	peerip = nhgetl(addr);
87*9a747e4fSDavid du Colombier 
88*9a747e4fSDavid du Colombier 	for(p = np->children; p; p = p->sibs)
89*9a747e4fSDavid du Colombier 		if((peerip&p->ip.mask) == p->ip.ipaddr)
90*9a747e4fSDavid du Colombier 			break;
91*9a747e4fSDavid du Colombier 	return p;
92*9a747e4fSDavid du Colombier }
93*9a747e4fSDavid du Colombier 
94*9a747e4fSDavid du Colombier /*
95*9a747e4fSDavid du Colombier  *	Walk a directory of IP addresses
96*9a747e4fSDavid du Colombier  */
97*9a747e4fSDavid du Colombier static Node*
98*9a747e4fSDavid du Colombier ipwalk(char *name,  Node *np)
99*9a747e4fSDavid du Colombier {
100*9a747e4fSDavid du Colombier 	Address *ap;
101*9a747e4fSDavid du Colombier 	ulong peerip;
102*9a747e4fSDavid du Colombier 	uchar addr[IPv4addrlen];
103*9a747e4fSDavid du Colombier 
104*9a747e4fSDavid du Colombier 	v4parseip(addr, name);
105*9a747e4fSDavid du Colombier 	peerip = nhgetl(addr);
106*9a747e4fSDavid du Colombier 
107*9a747e4fSDavid du Colombier 	if(debugfd >= 0)
108*9a747e4fSDavid du Colombier 		fprint(debugfd, "%d.%d.%d.%d - ", addr[0]&0xff, addr[1]&0xff,
109*9a747e4fSDavid du Colombier 				addr[2]&0xff, addr[3]&0xff);
110*9a747e4fSDavid du Colombier 	ap = ipsearch(peerip, np->addrs, np->count);
111*9a747e4fSDavid du Colombier 	if(ap == 0)
112*9a747e4fSDavid du Colombier 		return 0;
113*9a747e4fSDavid du Colombier 
114*9a747e4fSDavid du Colombier 	dummy.d.name = ap->name;
115*9a747e4fSDavid du Colombier 	return &dummy;
116*9a747e4fSDavid du Colombier }
117*9a747e4fSDavid du Colombier 
118*9a747e4fSDavid du Colombier /*
119*9a747e4fSDavid du Colombier  *	Walk a directory of account names
120*9a747e4fSDavid du Colombier  */
121*9a747e4fSDavid du Colombier static Node*
122*9a747e4fSDavid du Colombier acctwalk(char *name, Node *np)
123*9a747e4fSDavid du Colombier {
124*9a747e4fSDavid du Colombier 	int i, n;
125*9a747e4fSDavid du Colombier 	Address *ap;
126*9a747e4fSDavid du Colombier 	char *p, *cp, *user;
127*9a747e4fSDavid du Colombier 	char buf[512];
128*9a747e4fSDavid du Colombier 	char *doms[Maxdoms];
129*9a747e4fSDavid du Colombier 
130*9a747e4fSDavid du Colombier 	strecpy(buf, buf+sizeof buf, name);
131*9a747e4fSDavid du Colombier 	subslash(buf);
132*9a747e4fSDavid du Colombier 
133*9a747e4fSDavid du Colombier 	p = buf;
134*9a747e4fSDavid du Colombier 	for(n = 0; n < Maxdoms; n++) {
135*9a747e4fSDavid du Colombier 		cp = strchr(p, '!');
136*9a747e4fSDavid du Colombier 		if(cp == 0)
137*9a747e4fSDavid du Colombier 			break;
138*9a747e4fSDavid du Colombier 		*cp = 0;
139*9a747e4fSDavid du Colombier 		doms[n] = p;
140*9a747e4fSDavid du Colombier 		p = cp+1;
141*9a747e4fSDavid du Colombier 	}
142*9a747e4fSDavid du Colombier 	user = p;
143*9a747e4fSDavid du Colombier 
144*9a747e4fSDavid du Colombier 	for(i = 0; i < np->count; i++){
145*9a747e4fSDavid du Colombier 		ap = &np->addrs[i];
146*9a747e4fSDavid du Colombier 		if (accountmatch(ap->name, doms, n, user)) {
147*9a747e4fSDavid du Colombier 			dummy.d.name = ap->name;
148*9a747e4fSDavid du Colombier 			return &dummy;
149*9a747e4fSDavid du Colombier 		}
150*9a747e4fSDavid du Colombier 	}
151*9a747e4fSDavid du Colombier 	return 0;
152*9a747e4fSDavid du Colombier }
153*9a747e4fSDavid du Colombier 
154*9a747e4fSDavid du Colombier /*
155*9a747e4fSDavid du Colombier  * binary search sorted IP address list
156*9a747e4fSDavid du Colombier  */
157*9a747e4fSDavid du Colombier 
158*9a747e4fSDavid du Colombier static Address*
159*9a747e4fSDavid du Colombier ipsearch(ulong addr, Address *base, int n)
160*9a747e4fSDavid du Colombier {
161*9a747e4fSDavid du Colombier 	ulong top, bot, mid;
162*9a747e4fSDavid du Colombier 	Address *ap;
163*9a747e4fSDavid du Colombier 
164*9a747e4fSDavid du Colombier 	bot = 0;
165*9a747e4fSDavid du Colombier 	top = n;
166*9a747e4fSDavid du Colombier 	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
167*9a747e4fSDavid du Colombier 		ap = &base[mid];
168*9a747e4fSDavid du Colombier 		if((addr&ap->ip.mask) == ap->ip.ipaddr)
169*9a747e4fSDavid du Colombier 			return ap;
170*9a747e4fSDavid du Colombier 		if(addr < ap->ip.ipaddr)
171*9a747e4fSDavid du Colombier 			top = mid;
172*9a747e4fSDavid du Colombier 		else if(mid != n-1 && addr >= base[mid+1].ip.ipaddr)
173*9a747e4fSDavid du Colombier 			bot = mid;
174*9a747e4fSDavid du Colombier 		else
175*9a747e4fSDavid du Colombier 			break;
176*9a747e4fSDavid du Colombier 	}
177*9a747e4fSDavid du Colombier 	return 0;
178*9a747e4fSDavid du Colombier }
179*9a747e4fSDavid du Colombier 
180*9a747e4fSDavid du Colombier /*
181*9a747e4fSDavid du Colombier  *	Read a directory
182*9a747e4fSDavid du Colombier  */
183*9a747e4fSDavid du Colombier int
184*9a747e4fSDavid du Colombier dread(Fid *fidp, int cnt)
185*9a747e4fSDavid du Colombier {
186*9a747e4fSDavid du Colombier 	uchar *q, *eq, *oq;
187*9a747e4fSDavid du Colombier 	int n, skip;
188*9a747e4fSDavid du Colombier 	Node *np;
189*9a747e4fSDavid du Colombier 
190*9a747e4fSDavid du Colombier 	if(debugfd >= 0)
191*9a747e4fSDavid du Colombier 		fprint(debugfd, "dread %d\n", cnt);
192*9a747e4fSDavid du Colombier 
193*9a747e4fSDavid du Colombier 	np = fidp->node;
194*9a747e4fSDavid du Colombier 	oq = q = rbuf+IOHDRSZ;
195*9a747e4fSDavid du Colombier 	eq = q+cnt;
196*9a747e4fSDavid du Colombier 	if(fidp->dirindex >= np->count)
197*9a747e4fSDavid du Colombier 		return 0;
198*9a747e4fSDavid du Colombier 
199*9a747e4fSDavid du Colombier 	skip = fidp->dirindex;
200*9a747e4fSDavid du Colombier 	for(np = np->children; skip > 0 && np; np = np->sibs)
201*9a747e4fSDavid du Colombier 		skip--;
202*9a747e4fSDavid du Colombier 	if(np == 0)
203*9a747e4fSDavid du Colombier 		return 0;
204*9a747e4fSDavid du Colombier 
205*9a747e4fSDavid du Colombier 	for(; q < eq && np; np = np->sibs){
206*9a747e4fSDavid du Colombier 		if(debugfd >= 0)
207*9a747e4fSDavid du Colombier 			printnode(np);
208*9a747e4fSDavid du Colombier 		if((n=convD2M(&np->d, q, eq-q)) <= BIT16SZ)
209*9a747e4fSDavid du Colombier 			break;
210*9a747e4fSDavid du Colombier 		q += n;
211*9a747e4fSDavid du Colombier 		fidp->dirindex++;
212*9a747e4fSDavid du Colombier 	}
213*9a747e4fSDavid du Colombier 	return q - oq;
214*9a747e4fSDavid du Colombier }
215*9a747e4fSDavid du Colombier 
216*9a747e4fSDavid du Colombier /*
217*9a747e4fSDavid du Colombier  *	Read a directory of IP addresses or account names
218*9a747e4fSDavid du Colombier  */
219*9a747e4fSDavid du Colombier int
220*9a747e4fSDavid du Colombier hread(Fid *fidp, int cnt)
221*9a747e4fSDavid du Colombier {
222*9a747e4fSDavid du Colombier 	uchar *q, *eq, *oq;
223*9a747e4fSDavid du Colombier 	int i, n, path;
224*9a747e4fSDavid du Colombier 	Address *p;
225*9a747e4fSDavid du Colombier 	Node *np;
226*9a747e4fSDavid du Colombier 
227*9a747e4fSDavid du Colombier 	if(debugfd >= 0)
228*9a747e4fSDavid du Colombier 		fprint(debugfd, "hread %d\n", cnt);
229*9a747e4fSDavid du Colombier 
230*9a747e4fSDavid du Colombier 	np = fidp->node;
231*9a747e4fSDavid du Colombier 	oq = q = rbuf+IOHDRSZ;
232*9a747e4fSDavid du Colombier 	eq = q+cnt;
233*9a747e4fSDavid du Colombier 	if(fidp->dirindex >= np->count)
234*9a747e4fSDavid du Colombier 		return 0;
235*9a747e4fSDavid du Colombier 
236*9a747e4fSDavid du Colombier 	path = np->baseqid;
237*9a747e4fSDavid du Colombier 	for(i = fidp->dirindex; q < eq && i < np->count; i++){
238*9a747e4fSDavid du Colombier 		p = &np->addrs[i];
239*9a747e4fSDavid du Colombier 		dummy.d.name = p->name;
240*9a747e4fSDavid du Colombier 		dummy.d.qid.path = path++;
241*9a747e4fSDavid du Colombier 		if((n=convD2M(&dummy.d, q, eq-q)) <= BIT16SZ)
242*9a747e4fSDavid du Colombier 			break;
243*9a747e4fSDavid du Colombier 		q += n;
244*9a747e4fSDavid du Colombier 	}
245*9a747e4fSDavid du Colombier 	fidp->dirindex = i;
246*9a747e4fSDavid du Colombier 	return q - oq;
247*9a747e4fSDavid du Colombier }
248*9a747e4fSDavid du Colombier 
249*9a747e4fSDavid du Colombier /*
250*9a747e4fSDavid du Colombier  *	Find a directory node by type
251*9a747e4fSDavid du Colombier  */
252*9a747e4fSDavid du Colombier Node*
253*9a747e4fSDavid du Colombier finddir(int type)
254*9a747e4fSDavid du Colombier {
255*9a747e4fSDavid du Colombier 	Node *np;
256*9a747e4fSDavid du Colombier 
257*9a747e4fSDavid du Colombier 	for(np = root->children; np; np = np->sibs)
258*9a747e4fSDavid du Colombier 		if (np->d.type == type)
259*9a747e4fSDavid du Colombier 			return np;
260*9a747e4fSDavid du Colombier 	return 0;
261*9a747e4fSDavid du Colombier }
262*9a747e4fSDavid du Colombier 
263*9a747e4fSDavid du Colombier /*
264*9a747e4fSDavid du Colombier  *	Remove temporary pseudo-files that have timed-out
265*9a747e4fSDavid du Colombier  *	from the trusted directory
266*9a747e4fSDavid du Colombier  */
267*9a747e4fSDavid du Colombier void
268*9a747e4fSDavid du Colombier cleantrusted(void)
269*9a747e4fSDavid du Colombier {
270*9a747e4fSDavid du Colombier 	Node *np, **l;
271*9a747e4fSDavid du Colombier 	ulong t;
272*9a747e4fSDavid du Colombier 
273*9a747e4fSDavid du Colombier 	np = finddir(Trusted);
274*9a747e4fSDavid du Colombier 	if (np == 0)
275*9a747e4fSDavid du Colombier 		return;
276*9a747e4fSDavid du Colombier 
277*9a747e4fSDavid du Colombier 	t = time(0)-Timeout;
278*9a747e4fSDavid du Colombier 	l = &np->children;
279*9a747e4fSDavid du Colombier 	for (np = np->children; np; np = *l) {
280*9a747e4fSDavid du Colombier 		if(np->d.type == Trustedtemp && t >= np->d.mtime) {
281*9a747e4fSDavid du Colombier 			*l = np->sibs;
282*9a747e4fSDavid du Colombier 			if(debugfd >= 0)
283*9a747e4fSDavid du Colombier 				fprint(debugfd, "Deleting %s\n", np->d.name);
284*9a747e4fSDavid du Colombier 			np->parent->count--;
285*9a747e4fSDavid du Colombier 			free(np);
286*9a747e4fSDavid du Colombier 		} else
287*9a747e4fSDavid du Colombier 			l = &np->sibs;
288*9a747e4fSDavid du Colombier 	}
289*9a747e4fSDavid du Colombier }
290*9a747e4fSDavid du Colombier 
291*9a747e4fSDavid du Colombier /*
292*9a747e4fSDavid du Colombier  * match path components to prohibited domain & user specifications.  patterns include:
293*9a747e4fSDavid du Colombier  *	domain, domain! or domain!*	  - all users in domain
294*9a747e4fSDavid du Colombier  *	*.domain, *.domain! or *.domain!* - all users in domain and its subdomains
295*9a747e4fSDavid du Colombier  *	!user or *!user			  - user in all domains
296*9a747e4fSDavid du Colombier  *	domain!user			  - user in domain
297*9a747e4fSDavid du Colombier  *	*.domain!user			  - user in domain and its subdomains
298*9a747e4fSDavid du Colombier  *
299*9a747e4fSDavid du Colombier  *	if "user" has a trailing '*', it matches all user names beginning with "user"
300*9a747e4fSDavid du Colombier  *
301*9a747e4fSDavid du Colombier  * there are special semantics for the "domain, domain! or domain!*" specifications:
302*9a747e4fSDavid du Colombier  * the first two forms match when the domain is anywhere in at list of source-routed
303*9a747e4fSDavid du Colombier  * domains while the latter matches only when the domain is the last hop.  the same is
304*9a747e4fSDavid du Colombier  * true for the *.domain!* form of the pattern.
305*9a747e4fSDavid du Colombier  */
306*9a747e4fSDavid du Colombier static int
307*9a747e4fSDavid du Colombier accountmatch(char *spec, char **doms, int ndoms, char *user)
308*9a747e4fSDavid du Colombier {
309*9a747e4fSDavid du Colombier 	char *cp, *userp;
310*9a747e4fSDavid du Colombier 	int i, ret;
311*9a747e4fSDavid du Colombier 
312*9a747e4fSDavid du Colombier 	userp = 0;
313*9a747e4fSDavid du Colombier 	ret = 0;
314*9a747e4fSDavid du Colombier 	cp = strchr(spec, '!');
315*9a747e4fSDavid du Colombier 	if(cp){
316*9a747e4fSDavid du Colombier 		*cp++ = 0;		/* restored below */
317*9a747e4fSDavid du Colombier 		if(*cp)
318*9a747e4fSDavid du Colombier 		if(strcmp(cp, "*"))	/* "!*" is the same as no user field */
319*9a747e4fSDavid du Colombier 			userp = cp;	/* there is a user name */
320*9a747e4fSDavid du Colombier 	}
321*9a747e4fSDavid du Colombier 
322*9a747e4fSDavid du Colombier 	if(userp == 0){			/* no user field - domain match only */
323*9a747e4fSDavid du Colombier 		for(i = 0; i < ndoms && doms[i]; i++)
324*9a747e4fSDavid du Colombier 			if(dommatch(doms[i], spec) == 0)
325*9a747e4fSDavid du Colombier 				ret = 1;
326*9a747e4fSDavid du Colombier 	} else {
327*9a747e4fSDavid du Colombier 		/* check for "!user", "*!user" or "domain!user" */
328*9a747e4fSDavid du Colombier 		if(usermatch(user, userp) == 0){
329*9a747e4fSDavid du Colombier 			if(*spec == 0 || strcmp(spec, "*") == 0)
330*9a747e4fSDavid du Colombier 				ret = 1;
331*9a747e4fSDavid du Colombier 			else if(ndoms > 0  && dommatch(doms[ndoms-1], spec) == 0)
332*9a747e4fSDavid du Colombier 				ret = 1;
333*9a747e4fSDavid du Colombier 		}
334*9a747e4fSDavid du Colombier 	}
335*9a747e4fSDavid du Colombier 	if(cp)
336*9a747e4fSDavid du Colombier 		cp[-1] = '!';
337*9a747e4fSDavid du Colombier 	return ret;
338*9a747e4fSDavid du Colombier }
339*9a747e4fSDavid du Colombier 
340*9a747e4fSDavid du Colombier /*
341*9a747e4fSDavid du Colombier  *	match a user name.  the only meta-char is '*' which matches all
342*9a747e4fSDavid du Colombier  *	characters.  we only allow it as "*", which matches anything or
343*9a747e4fSDavid du Colombier  *	an * at the end of the name (e.g., "username*") which matches
344*9a747e4fSDavid du Colombier  *	trailing characters.
345*9a747e4fSDavid du Colombier  */
346*9a747e4fSDavid du Colombier static int
347*9a747e4fSDavid du Colombier usermatch(char *pathuser, char *specuser)
348*9a747e4fSDavid du Colombier {
349*9a747e4fSDavid du Colombier 	int n;
350*9a747e4fSDavid du Colombier 
351*9a747e4fSDavid du Colombier 	n = strlen(specuser)-1;
352*9a747e4fSDavid du Colombier 	if(specuser[n] == '*'){
353*9a747e4fSDavid du Colombier 		if(n == 0)		/* match everything */
354*9a747e4fSDavid du Colombier 			return 0;
355*9a747e4fSDavid du Colombier 		return strncmp(pathuser, specuser, n);
356*9a747e4fSDavid du Colombier 	}
357*9a747e4fSDavid du Colombier 	return strcmp(pathuser, specuser);
358*9a747e4fSDavid du Colombier }
359*9a747e4fSDavid du Colombier 
360*9a747e4fSDavid du Colombier /*
361*9a747e4fSDavid du Colombier  *	Match a domain specification
362*9a747e4fSDavid du Colombier  */
363*9a747e4fSDavid du Colombier static int
364*9a747e4fSDavid du Colombier dommatch(char *pathdom, char *specdom)
365*9a747e4fSDavid du Colombier {
366*9a747e4fSDavid du Colombier 	int n;
367*9a747e4fSDavid du Colombier 
368*9a747e4fSDavid du Colombier 	if (*specdom == '*'){
369*9a747e4fSDavid du Colombier 		if (specdom[1] == '.' && specdom[2]){
370*9a747e4fSDavid du Colombier 			specdom += 2;
371*9a747e4fSDavid du Colombier 			n = strlen(pathdom)-strlen(specdom);
372*9a747e4fSDavid du Colombier 			if(n == 0 || (n > 0 && pathdom[n-1] == '.'))
373*9a747e4fSDavid du Colombier 				return strcmp(pathdom+n, specdom);
374*9a747e4fSDavid du Colombier 			return n;
375*9a747e4fSDavid du Colombier 		}
376*9a747e4fSDavid du Colombier 	}
377*9a747e4fSDavid du Colombier 	return strcmp(pathdom, specdom);
378*9a747e4fSDavid du Colombier }
379*9a747e4fSDavid du Colombier 
380*9a747e4fSDavid du Colombier /*
381*9a747e4fSDavid du Colombier  *	Custom allocators to avoid malloc overheads on small objects.
382*9a747e4fSDavid du Colombier  * 	We never free these.  (See below.)
383*9a747e4fSDavid du Colombier  */
384*9a747e4fSDavid du Colombier typedef struct Stringtab	Stringtab;
385*9a747e4fSDavid du Colombier struct Stringtab {
386*9a747e4fSDavid du Colombier 	Stringtab *link;
387*9a747e4fSDavid du Colombier 	char *str;
388*9a747e4fSDavid du Colombier };
389*9a747e4fSDavid du Colombier static Stringtab*
390*9a747e4fSDavid du Colombier taballoc(void)
391*9a747e4fSDavid du Colombier {
392*9a747e4fSDavid du Colombier 	static Stringtab *t;
393*9a747e4fSDavid du Colombier 	static uint nt;
394*9a747e4fSDavid du Colombier 
395*9a747e4fSDavid du Colombier 	if(nt == 0){
396*9a747e4fSDavid du Colombier 		t = malloc(64*sizeof(Stringtab));
397*9a747e4fSDavid du Colombier 		if(t == 0)
398*9a747e4fSDavid du Colombier 			fatal("out of memory");
399*9a747e4fSDavid du Colombier 		nt = 64;
400*9a747e4fSDavid du Colombier 	}
401*9a747e4fSDavid du Colombier 	nt--;
402*9a747e4fSDavid du Colombier 	return t++;
403*9a747e4fSDavid du Colombier }
404*9a747e4fSDavid du Colombier 
405*9a747e4fSDavid du Colombier static char*
406*9a747e4fSDavid du Colombier xstrdup(char *s)
407*9a747e4fSDavid du Colombier {
408*9a747e4fSDavid du Colombier 	char *r;
409*9a747e4fSDavid du Colombier 	int len;
410*9a747e4fSDavid du Colombier 	static char *t;
411*9a747e4fSDavid du Colombier 	static int nt;
412*9a747e4fSDavid du Colombier 
413*9a747e4fSDavid du Colombier 	len = strlen(s)+1;
414*9a747e4fSDavid du Colombier 	if(len >= 8192)
415*9a747e4fSDavid du Colombier 		fatal("strdup big string");
416*9a747e4fSDavid du Colombier 
417*9a747e4fSDavid du Colombier 	if(nt < len){
418*9a747e4fSDavid du Colombier 		t = malloc(8192);
419*9a747e4fSDavid du Colombier 		if(t == 0)
420*9a747e4fSDavid du Colombier 			fatal("out of memory");
421*9a747e4fSDavid du Colombier 		nt = 8192;
422*9a747e4fSDavid du Colombier 	}
423*9a747e4fSDavid du Colombier 	r = t;
424*9a747e4fSDavid du Colombier 	t += len;
425*9a747e4fSDavid du Colombier 	nt -= len;
426*9a747e4fSDavid du Colombier 	strcpy(r, s);
427*9a747e4fSDavid du Colombier 	return r;
428*9a747e4fSDavid du Colombier }
429*9a747e4fSDavid du Colombier 
430*9a747e4fSDavid du Colombier /*
431*9a747e4fSDavid du Colombier  *	Return a uniquely allocated copy of a string.
432*9a747e4fSDavid du Colombier  *	Don't free these -- they stay in the table for the
433*9a747e4fSDavid du Colombier  *	next caller who wants that particular string.
434*9a747e4fSDavid du Colombier  *	String comparison can be done with pointer comparison
435*9a747e4fSDavid du Colombier  *	if you know both strings are atoms.
436*9a747e4fSDavid du Colombier  */
437*9a747e4fSDavid du Colombier static Stringtab *stab[1024];
438*9a747e4fSDavid du Colombier 
439*9a747e4fSDavid du Colombier static uint
440*9a747e4fSDavid du Colombier hash(char *s)
441*9a747e4fSDavid du Colombier {
442*9a747e4fSDavid du Colombier 	uint h;
443*9a747e4fSDavid du Colombier 	uchar *p;
444*9a747e4fSDavid du Colombier 
445*9a747e4fSDavid du Colombier 	h = 0;
446*9a747e4fSDavid du Colombier 	for(p=(uchar*)s; *p; p++)
447*9a747e4fSDavid du Colombier 		h = h*37 + *p;
448*9a747e4fSDavid du Colombier 	return h;
449*9a747e4fSDavid du Colombier }
450*9a747e4fSDavid du Colombier 
451*9a747e4fSDavid du Colombier char*
452*9a747e4fSDavid du Colombier atom(char *str)
453*9a747e4fSDavid du Colombier {
454*9a747e4fSDavid du Colombier 	uint h;
455*9a747e4fSDavid du Colombier 	Stringtab *tab;
456*9a747e4fSDavid du Colombier 
457*9a747e4fSDavid du Colombier 	h = hash(str) % nelem(stab);
458*9a747e4fSDavid du Colombier 	for(tab=stab[h]; tab; tab=tab->link)
459*9a747e4fSDavid du Colombier 		if(strcmp(str, tab->str) == 0)
460*9a747e4fSDavid du Colombier 			return tab->str;
461*9a747e4fSDavid du Colombier 
462*9a747e4fSDavid du Colombier 	tab = taballoc();
463*9a747e4fSDavid du Colombier 	tab->str = xstrdup(str);
464*9a747e4fSDavid du Colombier 	tab->link = stab[h];
465*9a747e4fSDavid du Colombier 	stab[h] = tab;
466*9a747e4fSDavid du Colombier 	return tab->str;
467*9a747e4fSDavid du Colombier }
468