xref: /plan9/sys/src/cmd/wikifs/io.c (revision c1e157924ccd61786fc081b2c6ae14a0a280680d)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * I/O for a Wiki document set.
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  * The files are kept in one flat directory.
59a747e4fSDavid du Colombier  * There are three files for each document:
69a747e4fSDavid du Colombier  *	nnn	- current version of the document
79a747e4fSDavid du Colombier  *	nnn.hist	- history (all old versions) of the document
89a747e4fSDavid du Colombier  *		append-only
99a747e4fSDavid du Colombier  *	L.nnn	- write lock file for the document
109a747e4fSDavid du Colombier  *
119a747e4fSDavid du Colombier  * At the moment, since we don't have read/write locks
129a747e4fSDavid du Colombier  * in the file system, we use the L.nnn file as a read lock too.
139a747e4fSDavid du Colombier  * It's a hack but there aren't supposed to be many readers
149a747e4fSDavid du Colombier  * anyway.
159a747e4fSDavid du Colombier  *
169a747e4fSDavid du Colombier  * The nnn.hist file is in the format read by Brdwhist.
179a747e4fSDavid du Colombier  * The nnn file is in that format too, but only contains the
189a747e4fSDavid du Colombier  * last entry of the nnn.hist file.
199a747e4fSDavid du Colombier  *
209a747e4fSDavid du Colombier  * In addition to this set of files, there is an append-only
219a747e4fSDavid du Colombier  * map file that provides a mapping between numbers and titles.
229a747e4fSDavid du Colombier  * The map file is a sequence of lines of the form
239a747e4fSDavid du Colombier  *	nnn Title Here
249a747e4fSDavid du Colombier  * The lock file L.map must be held to add to the end, to
259a747e4fSDavid du Colombier  * make sure that the numbers are allocated sequentially.
269a747e4fSDavid du Colombier  *
279a747e4fSDavid du Colombier  * We assume that writes to the map file will fit in one message,
289a747e4fSDavid du Colombier  * so that we don't have to read-lock the file.
299a747e4fSDavid du Colombier  */
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier #include <u.h>
329a747e4fSDavid du Colombier #include <libc.h>
339a747e4fSDavid du Colombier #include <bio.h>
349a747e4fSDavid du Colombier #include <String.h>
359a747e4fSDavid du Colombier #include <thread.h>
369a747e4fSDavid du Colombier #include "wiki.h"
379a747e4fSDavid du Colombier 
389a747e4fSDavid du Colombier enum {
399a747e4fSDavid du Colombier 	Nhash = 64,
409a747e4fSDavid du Colombier 	Mcache = 128,
419a747e4fSDavid du Colombier };
429a747e4fSDavid du Colombier 
439a747e4fSDavid du Colombier typedef struct Wcache Wcache;
449a747e4fSDavid du Colombier struct Wcache {
459a747e4fSDavid du Colombier 	int n;
469a747e4fSDavid du Colombier 	ulong use;
479a747e4fSDavid du Colombier 	RWLock;
489a747e4fSDavid du Colombier 	ulong tcurrent;
499a747e4fSDavid du Colombier 	ulong thist;
509a747e4fSDavid du Colombier 	Whist *hist;
519a747e4fSDavid du Colombier 	Whist *current;
529a747e4fSDavid du Colombier 	Qid qid;
539a747e4fSDavid du Colombier 	Qid qidhist;
549a747e4fSDavid du Colombier 	Wcache *hash;
559a747e4fSDavid du Colombier };
569a747e4fSDavid du Colombier 
579a747e4fSDavid du Colombier static RWLock cachelock;
589a747e4fSDavid du Colombier static Wcache *tab[Nhash];
599a747e4fSDavid du Colombier int ncache;
609a747e4fSDavid du Colombier 
619a747e4fSDavid du Colombier void
closewhist(Whist * wh)629a747e4fSDavid du Colombier closewhist(Whist *wh)
639a747e4fSDavid du Colombier {
649a747e4fSDavid du Colombier 	int i;
659a747e4fSDavid du Colombier 
669a747e4fSDavid du Colombier 	if(wh && decref(wh) == 0){
679a747e4fSDavid du Colombier 		free(wh->title);
689a747e4fSDavid du Colombier 		for(i=0; i<wh->ndoc; i++){
699a747e4fSDavid du Colombier 			free(wh->doc[i].author);
709a747e4fSDavid du Colombier 			free(wh->doc[i].comment);
719a747e4fSDavid du Colombier 			freepage(wh->doc[i].wtxt);
729a747e4fSDavid du Colombier 		}
739a747e4fSDavid du Colombier 		free(wh->doc);
749a747e4fSDavid du Colombier 		free(wh);
759a747e4fSDavid du Colombier 	}
769a747e4fSDavid du Colombier }
779a747e4fSDavid du Colombier 
789a747e4fSDavid du Colombier void
freepage(Wpage * p)799a747e4fSDavid du Colombier freepage(Wpage *p)
809a747e4fSDavid du Colombier {
819a747e4fSDavid du Colombier 	Wpage *next;
829a747e4fSDavid du Colombier 
839a747e4fSDavid du Colombier 	for(; p; p=next){
849a747e4fSDavid du Colombier 		next = p->next;
859a747e4fSDavid du Colombier 		free(p->text);
869a747e4fSDavid du Colombier 		free(p->url);
879a747e4fSDavid du Colombier 		free(p);
889a747e4fSDavid du Colombier 	}
899a747e4fSDavid du Colombier }
909a747e4fSDavid du Colombier 
919a747e4fSDavid du Colombier static Wcache*
findcache(int n)929a747e4fSDavid du Colombier findcache(int n)
939a747e4fSDavid du Colombier {
949a747e4fSDavid du Colombier 	Wcache *w;
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier 	for(w=tab[n%Nhash]; w; w=w->hash)
979a747e4fSDavid du Colombier 		if(w->n == n){
989a747e4fSDavid du Colombier 			w->use = time(0);
999a747e4fSDavid du Colombier 			return w;
1009a747e4fSDavid du Colombier 		}
1019a747e4fSDavid du Colombier 	return nil;
1029a747e4fSDavid du Colombier }
1039a747e4fSDavid du Colombier 
1049a747e4fSDavid du Colombier static int
getlock(char * lock)1059a747e4fSDavid du Colombier getlock(char *lock)
1069a747e4fSDavid du Colombier {
1079a747e4fSDavid du Colombier 	char buf[ERRMAX];
1089a747e4fSDavid du Colombier 	int i, fd;
1099a747e4fSDavid du Colombier 	enum { SECS = 200 };
1109a747e4fSDavid du Colombier 
1119a747e4fSDavid du Colombier 	for(i=0; i<SECS*10; i++){
1129a747e4fSDavid du Colombier 		fd = wcreate(lock, ORDWR, DMEXCL|0666);
1139a747e4fSDavid du Colombier 		if(fd >= 0)
1149a747e4fSDavid du Colombier 			return fd;
1159a747e4fSDavid du Colombier 		buf[0] = '\0';
1169a747e4fSDavid du Colombier 		rerrstr(buf, sizeof buf);
1179a747e4fSDavid du Colombier 		if(strstr(buf, "locked") == nil)
1189a747e4fSDavid du Colombier 			break;
1199a747e4fSDavid du Colombier 		sleep(1000/10);
1209a747e4fSDavid du Colombier 	}
121*c1e15792SDavid du Colombier 	werrstr("couldn't acquire lock %s: %r", lock);
1229a747e4fSDavid du Colombier 	return -1;
1239a747e4fSDavid du Colombier }
1249a747e4fSDavid du Colombier 
1259a747e4fSDavid du Colombier static Whist*
readwhist(char * file,char * lock,Qid * qid)1269a747e4fSDavid du Colombier readwhist(char *file, char *lock, Qid *qid)
1279a747e4fSDavid du Colombier {
1289a747e4fSDavid du Colombier 	int lfd;
1299a747e4fSDavid du Colombier 	Biobuf *b;
1309a747e4fSDavid du Colombier 	Dir *d;
1319a747e4fSDavid du Colombier 	Whist *wh;
1329a747e4fSDavid du Colombier 
1339a747e4fSDavid du Colombier 	if((lfd=getlock(lock)) < 0)	// LOG?
1349a747e4fSDavid du Colombier 		return nil;
1359a747e4fSDavid du Colombier 
1369a747e4fSDavid du Colombier 	if(qid){
1379a747e4fSDavid du Colombier 		if((d = wdirstat(file)) == nil){
1389a747e4fSDavid du Colombier 			close(lfd);
1399a747e4fSDavid du Colombier 			return nil;
1409a747e4fSDavid du Colombier 		}
1419a747e4fSDavid du Colombier 		*qid = d->qid;
1429a747e4fSDavid du Colombier 		free(d);
1439a747e4fSDavid du Colombier 	}
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier 	if((b = wBopen(file, OREAD)) == nil){	//LOG?
1469a747e4fSDavid du Colombier 		close(lfd);
1479a747e4fSDavid du Colombier 		return nil;
1489a747e4fSDavid du Colombier 	}
1499a747e4fSDavid du Colombier 
1509a747e4fSDavid du Colombier 	wh = Brdwhist(b);
1519a747e4fSDavid du Colombier 
1529a747e4fSDavid du Colombier 	Bterm(b);
1539a747e4fSDavid du Colombier 	close(lfd);
1549a747e4fSDavid du Colombier 	return wh;
1559a747e4fSDavid du Colombier }
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier static void
gencurrent(Wcache * w,Qid * q,char * file,char * lock,ulong * t,Whist ** wp,int n)1589a747e4fSDavid du Colombier gencurrent(Wcache *w, Qid *q, char *file, char *lock, ulong *t, Whist **wp, int n)
1599a747e4fSDavid du Colombier {
1609a747e4fSDavid du Colombier 	Dir *d;
1619a747e4fSDavid du Colombier 	Whist *wh;
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	if(*wp && *t+Tcache >= time(0))
1649a747e4fSDavid du Colombier 		return;
1659a747e4fSDavid du Colombier 
1669a747e4fSDavid du Colombier 	wlock(w);
1679a747e4fSDavid du Colombier 	if(*wp && *t+Tcache >= time(0)){
1689a747e4fSDavid du Colombier 		wunlock(w);
1699a747e4fSDavid du Colombier 		return;
1709a747e4fSDavid du Colombier 	}
1719a747e4fSDavid du Colombier 
1729a747e4fSDavid du Colombier 	if(((d = wdirstat(file)) == nil) || (d->qid.path==q->path && d->qid.vers==q->vers)){
1739a747e4fSDavid du Colombier 		*t = time(0);
1749a747e4fSDavid du Colombier 		wunlock(w);
1759a747e4fSDavid du Colombier 		free(d);
1769a747e4fSDavid du Colombier 		return;
1779a747e4fSDavid du Colombier 	}
1789a747e4fSDavid du Colombier 
1799a747e4fSDavid du Colombier 	free(d);
1809a747e4fSDavid du Colombier 	if(wh = readwhist(file, lock, q)){
1819a747e4fSDavid du Colombier 		wh->n = n;
1829a747e4fSDavid du Colombier 		*t = time(0);
1839a747e4fSDavid du Colombier 		closewhist(*wp);
1849a747e4fSDavid du Colombier 		*wp = wh;
1859a747e4fSDavid du Colombier 	}
1869a747e4fSDavid du Colombier else fprint(2, "error file=%s lock=%s %r\n", file, lock);
1879a747e4fSDavid du Colombier 	wunlock(w);
1889a747e4fSDavid du Colombier }
1899a747e4fSDavid du Colombier 
1909a747e4fSDavid du Colombier static void
current(Wcache * w)1919a747e4fSDavid du Colombier current(Wcache *w)
1929a747e4fSDavid du Colombier {
1939a747e4fSDavid du Colombier 	char tmp[40];
1949a747e4fSDavid du Colombier 	char tmplock[40];
1959a747e4fSDavid du Colombier 
1969a747e4fSDavid du Colombier 	sprint(tmplock, "d/L.%d", w->n);
1979a747e4fSDavid du Colombier 	sprint(tmp, "d/%d", w->n);
1989a747e4fSDavid du Colombier 	gencurrent(w, &w->qid, tmp, tmplock, &w->tcurrent, &w->current, w->n);
1999a747e4fSDavid du Colombier }
2009a747e4fSDavid du Colombier 
2019a747e4fSDavid du Colombier static void
currenthist(Wcache * w)2029a747e4fSDavid du Colombier currenthist(Wcache *w)
2039a747e4fSDavid du Colombier {
2049a747e4fSDavid du Colombier 	char hist[40], lock[40];
2059a747e4fSDavid du Colombier 
2069a747e4fSDavid du Colombier 	sprint(hist, "d/%d.hist", w->n);
2079a747e4fSDavid du Colombier 	sprint(lock, "d/L.%d", w->n);
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier 	gencurrent(w, &w->qidhist, hist, lock, &w->thist, &w->hist, w->n);
2109a747e4fSDavid du Colombier }
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier void
voidcache(int n)2139a747e4fSDavid du Colombier voidcache(int n)
2149a747e4fSDavid du Colombier {
2159a747e4fSDavid du Colombier 	Wcache *c;
2169a747e4fSDavid du Colombier 
2179a747e4fSDavid du Colombier 	rlock(&cachelock);
2189a747e4fSDavid du Colombier 	if(c = findcache(n)){
2199a747e4fSDavid du Colombier 		wlock(c);
2209a747e4fSDavid du Colombier 		c->tcurrent = 0;
2219a747e4fSDavid du Colombier 		c->thist = 0;
2221052a86aSDavid du Colombier 		/* aggressively free memory */
2231052a86aSDavid du Colombier 		closewhist(c->hist);
2241052a86aSDavid du Colombier 		c->hist = nil;
2251052a86aSDavid du Colombier 		closewhist(c->current);
2261052a86aSDavid du Colombier 		c->current = nil;
2279a747e4fSDavid du Colombier 		wunlock(c);
2289a747e4fSDavid du Colombier 	}
2299a747e4fSDavid du Colombier 	runlock(&cachelock);
2309a747e4fSDavid du Colombier }
2319a747e4fSDavid du Colombier 
2329a747e4fSDavid du Colombier static Whist*
getcache(int n,int hist)2339a747e4fSDavid du Colombier getcache(int n, int hist)
2349a747e4fSDavid du Colombier {
2359a747e4fSDavid du Colombier 	int i, isw;
2369a747e4fSDavid du Colombier 	ulong t;
2379a747e4fSDavid du Colombier 	Wcache *c, **cp, **evict;
2389a747e4fSDavid du Colombier 	Whist *wh;
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier 	isw = 0;
2419a747e4fSDavid du Colombier 	rlock(&cachelock);
2429a747e4fSDavid du Colombier 	if(c = findcache(n)){
2439a747e4fSDavid du Colombier 	Found:
2449a747e4fSDavid du Colombier 		current(c);
2459a747e4fSDavid du Colombier 		if(hist)
2469a747e4fSDavid du Colombier 			currenthist(c);
2479a747e4fSDavid du Colombier 		rlock(c);
2489a747e4fSDavid du Colombier 		if(hist)
2499a747e4fSDavid du Colombier 			wh = c->hist;
2509a747e4fSDavid du Colombier 		else
2519a747e4fSDavid du Colombier 			wh = c->current;
2529a747e4fSDavid du Colombier 		if(wh)
2539a747e4fSDavid du Colombier 			incref(wh);
2549a747e4fSDavid du Colombier 		runlock(c);
2559a747e4fSDavid du Colombier 		if(isw)
2569a747e4fSDavid du Colombier 			wunlock(&cachelock);
2579a747e4fSDavid du Colombier 		else
2589a747e4fSDavid du Colombier 			runlock(&cachelock);
2599a747e4fSDavid du Colombier 		return wh;
2609a747e4fSDavid du Colombier 	}
2619a747e4fSDavid du Colombier 	runlock(&cachelock);
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier 	wlock(&cachelock);
2649a747e4fSDavid du Colombier 	if(c = findcache(n)){
2659a747e4fSDavid du Colombier 		isw = 1;	/* better to downgrade lock but can't */
2669a747e4fSDavid du Colombier 		goto Found;
2679a747e4fSDavid du Colombier 	}
2689a747e4fSDavid du Colombier 
2699a747e4fSDavid du Colombier 	if(ncache < Mcache){
2709a747e4fSDavid du Colombier 	Alloc:
2719a747e4fSDavid du Colombier 		c = emalloc(sizeof *c);
2729a747e4fSDavid du Colombier 		ncache++;
2739a747e4fSDavid du Colombier 	}else{
2749a747e4fSDavid du Colombier 		/* find something to evict. */
2759a747e4fSDavid du Colombier 		t = ~0;
2769a747e4fSDavid du Colombier 		evict = nil;
2779a747e4fSDavid du Colombier 		for(i=0; i<Nhash; i++){
2789a747e4fSDavid du Colombier 			for(cp=&tab[i], c=*cp; c; cp=&c->hash, c=*cp){
2799a747e4fSDavid du Colombier 				if(c->use < t
2809a747e4fSDavid du Colombier 				&& (!c->hist || c->hist->ref==1)
2819a747e4fSDavid du Colombier 				&& (!c->current || c->current->ref==1)){
2829a747e4fSDavid du Colombier 					evict = cp;
2839a747e4fSDavid du Colombier 					t = c->use;
2849a747e4fSDavid du Colombier 				}
2859a747e4fSDavid du Colombier 			}
2869a747e4fSDavid du Colombier 		}
2879a747e4fSDavid du Colombier 
2889a747e4fSDavid du Colombier 		if(evict == nil){
2899a747e4fSDavid du Colombier 			fprint(2, "wikifs: nothing to evict\n");
2909a747e4fSDavid du Colombier 			goto Alloc;
2919a747e4fSDavid du Colombier 		}
2929a747e4fSDavid du Colombier 
2939a747e4fSDavid du Colombier 		c = *evict;
2949a747e4fSDavid du Colombier 		*evict = c->hash;
2959a747e4fSDavid du Colombier 
2969a747e4fSDavid du Colombier 		closewhist(c->current);
2979a747e4fSDavid du Colombier 		closewhist(c->hist);
2989a747e4fSDavid du Colombier 		memset(c, 0, sizeof *c);
2999a747e4fSDavid du Colombier 	}
3009a747e4fSDavid du Colombier 
3019a747e4fSDavid du Colombier 	c->n = n;
3029a747e4fSDavid du Colombier 	c->hash = tab[n%Nhash];
3039a747e4fSDavid du Colombier 	tab[n%Nhash] = c;
3049a747e4fSDavid du Colombier 	isw = 1;
3059a747e4fSDavid du Colombier 	goto Found;
3069a747e4fSDavid du Colombier }
3079a747e4fSDavid du Colombier 
3089a747e4fSDavid du Colombier Whist*
getcurrent(int n)3099a747e4fSDavid du Colombier getcurrent(int n)
3109a747e4fSDavid du Colombier {
3119a747e4fSDavid du Colombier 	return getcache(n, 0);
3129a747e4fSDavid du Colombier }
3139a747e4fSDavid du Colombier 
3149a747e4fSDavid du Colombier Whist*
gethistory(int n)3159a747e4fSDavid du Colombier gethistory(int n)
3169a747e4fSDavid du Colombier {
3179a747e4fSDavid du Colombier 	return getcache(n, 1);
3189a747e4fSDavid du Colombier }
3199a747e4fSDavid du Colombier 
3209a747e4fSDavid du Colombier RWLock maplock;
3219a747e4fSDavid du Colombier Map *map;
3229a747e4fSDavid du Colombier 
3239a747e4fSDavid du Colombier static int
mapcmp(const void * va,const void * vb)3249a747e4fSDavid du Colombier mapcmp(const void *va, const void *vb)
3259a747e4fSDavid du Colombier {
3269a747e4fSDavid du Colombier 	Mapel *a, *b;
3279a747e4fSDavid du Colombier 
3289a747e4fSDavid du Colombier 	a = (Mapel*)va;
3299a747e4fSDavid du Colombier 	b = (Mapel*)vb;
3309a747e4fSDavid du Colombier 
3319a747e4fSDavid du Colombier 	return strcmp(a->s, b->s);
3329a747e4fSDavid du Colombier }
3339a747e4fSDavid du Colombier 
3349a747e4fSDavid du Colombier void
closemap(Map * m)3359a747e4fSDavid du Colombier closemap(Map *m)
3369a747e4fSDavid du Colombier {
3379a747e4fSDavid du Colombier 	if(decref(m)==0){
3389a747e4fSDavid du Colombier 		free(m->buf);
3399a747e4fSDavid du Colombier 		free(m->el);
3409a747e4fSDavid du Colombier 		free(m);
3419a747e4fSDavid du Colombier 	}
3429a747e4fSDavid du Colombier }
3439a747e4fSDavid du Colombier 
3449a747e4fSDavid du Colombier void
currentmap(int force)3459a747e4fSDavid du Colombier currentmap(int force)
3469a747e4fSDavid du Colombier {
3479a747e4fSDavid du Colombier 	char *p, *q, *r;
3489a747e4fSDavid du Colombier 	int lfd, fd, m, n;
3499a747e4fSDavid du Colombier 	Dir *d;
3509a747e4fSDavid du Colombier 	Map *nmap;
351da51d93aSDavid du Colombier 	char *err = nil;
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier 	lfd = -1;
3549a747e4fSDavid du Colombier 	fd = -1;
3559a747e4fSDavid du Colombier 	d = nil;
3569a747e4fSDavid du Colombier 	nmap = nil;
3579a747e4fSDavid du Colombier 	if(!force && map && map->t+Tcache >= time(0))
3589a747e4fSDavid du Colombier 		return;
3599a747e4fSDavid du Colombier 
3609a747e4fSDavid du Colombier 	wlock(&maplock);
3619a747e4fSDavid du Colombier 	if(!force && map && map->t+Tcache >= time(0))
3629a747e4fSDavid du Colombier 		goto Return;
3639a747e4fSDavid du Colombier 
364da51d93aSDavid du Colombier 	if((lfd = getlock("d/L.map")) < 0){
365da51d93aSDavid du Colombier 		err = "can't lock";
3669a747e4fSDavid du Colombier 		goto Return;
367da51d93aSDavid du Colombier 	}
3689a747e4fSDavid du Colombier 
3699a747e4fSDavid du Colombier 	if((d = wdirstat("d/map")) == nil)
3709a747e4fSDavid du Colombier 		goto Return;
3719a747e4fSDavid du Colombier 
3729a747e4fSDavid du Colombier 	if(map && d->qid.path == map->qid.path && d->qid.vers == map->qid.vers){
3739a747e4fSDavid du Colombier 		map->t = time(0);
3749a747e4fSDavid du Colombier 		goto Return;
3759a747e4fSDavid du Colombier 	}
3769a747e4fSDavid du Colombier 
3779a747e4fSDavid du Colombier 	if(d->length > Maxmap){
3789a747e4fSDavid du Colombier 		//LOG
379da51d93aSDavid du Colombier 		err = "too long";
3809a747e4fSDavid du Colombier 		goto Return;
3819a747e4fSDavid du Colombier 	}
3829a747e4fSDavid du Colombier 
3839a747e4fSDavid du Colombier 	if((fd = wopen("d/map", OREAD)) < 0)
3849a747e4fSDavid du Colombier 		goto Return;
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 	nmap = emalloc(sizeof *nmap);
3879a747e4fSDavid du Colombier 	nmap->buf = emalloc(d->length+1);
3889a747e4fSDavid du Colombier 	n = readn(fd, nmap->buf, d->length);
389da51d93aSDavid du Colombier 	if(n != d->length){
390da51d93aSDavid du Colombier 		err = "bad length";
3919a747e4fSDavid du Colombier 		goto Return;
392da51d93aSDavid du Colombier 	}
3939a747e4fSDavid du Colombier 	nmap->buf[n] = '\0';
3949a747e4fSDavid du Colombier 
3959a747e4fSDavid du Colombier 	n = 0;
3969a747e4fSDavid du Colombier 	for(p=nmap->buf; p; p=strchr(p+1, '\n'))
3979a747e4fSDavid du Colombier 		n++;
3989a747e4fSDavid du Colombier 	nmap->el = emalloc(n*sizeof(nmap->el[0]));
3999a747e4fSDavid du Colombier 
4009a747e4fSDavid du Colombier 	m = 0;
4019a747e4fSDavid du Colombier 	for(p=nmap->buf; p && *p && m < n; p=q){
4029a747e4fSDavid du Colombier 		if(q = strchr(p+1, '\n'))
4039a747e4fSDavid du Colombier 			*q++ = '\0';
4049a747e4fSDavid du Colombier 		nmap->el[m].n = strtol(p, &r, 10);
4059a747e4fSDavid du Colombier 		if(*r == ' ')
4069a747e4fSDavid du Colombier 			r++;
4079a747e4fSDavid du Colombier 		else
4089a747e4fSDavid du Colombier 			{}//LOG?
4099a747e4fSDavid du Colombier 		nmap->el[m].s = strcondense(r, 1);
4109a747e4fSDavid du Colombier 		m++;
4119a747e4fSDavid du Colombier 	}
4129a747e4fSDavid du Colombier 	//LOG if m != n
4139a747e4fSDavid du Colombier 
4149a747e4fSDavid du Colombier 	nmap->qid = d->qid;
4159a747e4fSDavid du Colombier 	nmap->t = time(0);
4169a747e4fSDavid du Colombier 	nmap->nel = m;
4179a747e4fSDavid du Colombier 	qsort(nmap->el, nmap->nel, sizeof(nmap->el[0]), mapcmp);
4189a747e4fSDavid du Colombier 	if(map)
4199a747e4fSDavid du Colombier 		closemap(map);
4209a747e4fSDavid du Colombier 	map = nmap;
4219a747e4fSDavid du Colombier 	incref(map);
4229a747e4fSDavid du Colombier 	nmap = nil;
4239a747e4fSDavid du Colombier 
4249a747e4fSDavid du Colombier Return:
4259a747e4fSDavid du Colombier 	free(d);
4269a747e4fSDavid du Colombier 	if(nmap){
4279a747e4fSDavid du Colombier 		free(nmap->el);
4289a747e4fSDavid du Colombier 		free(nmap->buf);
4299a747e4fSDavid du Colombier 		free(nmap);
4309a747e4fSDavid du Colombier 	}
4319a747e4fSDavid du Colombier 	if(map == nil)
432da51d93aSDavid du Colombier 		sysfatal("cannot get map: %s: %r", err);
4339a747e4fSDavid du Colombier 
4349a747e4fSDavid du Colombier 	if(fd >= 0)
4359a747e4fSDavid du Colombier 		close(fd);
4369a747e4fSDavid du Colombier 	if(lfd >= 0)
4379a747e4fSDavid du Colombier 		close(lfd);
4389a747e4fSDavid du Colombier 	wunlock(&maplock);
4399a747e4fSDavid du Colombier }
4409a747e4fSDavid du Colombier 
4419a747e4fSDavid du Colombier int
allocnum(char * title,int mustbenew)4429a747e4fSDavid du Colombier allocnum(char *title, int mustbenew)
4439a747e4fSDavid du Colombier {
4449a747e4fSDavid du Colombier 	char *p, *q;
4459a747e4fSDavid du Colombier 	int lfd, fd, n;
4469a747e4fSDavid du Colombier 	Biobuf b;
4479a747e4fSDavid du Colombier 
4489a747e4fSDavid du Colombier 	if(strcmp(title, "map")==0 || strcmp(title, "new")==0){
4499a747e4fSDavid du Colombier 		werrstr("reserved title name");
4509a747e4fSDavid du Colombier 		return -1;
4519a747e4fSDavid du Colombier 	}
4529a747e4fSDavid du Colombier 
453f0ed0fb6SDavid du Colombier 	if(title[0]=='\0' || strpbrk(title, "/<>:?")){
4549a747e4fSDavid du Colombier 		werrstr("invalid character in name");
4559a747e4fSDavid du Colombier 		return -1;
4569a747e4fSDavid du Colombier 	}
4579a747e4fSDavid du Colombier 	if((n = nametonum(title)) >= 0){
4589a747e4fSDavid du Colombier 		if(mustbenew){
4599a747e4fSDavid du Colombier 			werrstr("duplicate title");
4609a747e4fSDavid du Colombier 			return -1;
4619a747e4fSDavid du Colombier 		}
4629a747e4fSDavid du Colombier 		return n;
4639a747e4fSDavid du Colombier 	}
4649a747e4fSDavid du Colombier 
4659a747e4fSDavid du Colombier 	title = estrdup(title);
4669a747e4fSDavid du Colombier 	strcondense(title, 1);
4679a747e4fSDavid du Colombier 	strlower(title);
4689a747e4fSDavid du Colombier 	if(strchr(title, '\n') || strlen(title) > 200){
4699a747e4fSDavid du Colombier 		werrstr("bad title");
4709a747e4fSDavid du Colombier 		free(title);
4719a747e4fSDavid du Colombier 		return -1;
4729a747e4fSDavid du Colombier 	}
4739a747e4fSDavid du Colombier 
4749a747e4fSDavid du Colombier 	if((lfd = getlock("d/L.map")) < 0){
4759a747e4fSDavid du Colombier 		free(title);
4769a747e4fSDavid du Colombier 		return -1;
4779a747e4fSDavid du Colombier 	}
4789a747e4fSDavid du Colombier 
4799a747e4fSDavid du Colombier 	if((fd = wopen("d/map", ORDWR)) < 0){	// LOG?
4809a747e4fSDavid du Colombier 		close(lfd);
4819a747e4fSDavid du Colombier 		free(title);
4829a747e4fSDavid du Colombier 		return -1;
4839a747e4fSDavid du Colombier 	}
4849a747e4fSDavid du Colombier 
4859a747e4fSDavid du Colombier 	/*
4869a747e4fSDavid du Colombier 	 * What we really need to do here is make sure the
4879a747e4fSDavid du Colombier 	 * map is up-to-date, then make sure the title isn't
4889a747e4fSDavid du Colombier 	 * taken, and then add it, all without dropping the locks.
4899a747e4fSDavid du Colombier 	 *
4909a747e4fSDavid du Colombier 	 * This turns out to be a mess when you start adding
4919a747e4fSDavid du Colombier 	 * all the necessary dolock flags, so instead we just
4929a747e4fSDavid du Colombier 	 * read through the file ourselves, and let our
4939a747e4fSDavid du Colombier 	 * map catch up on its own.
4949a747e4fSDavid du Colombier 	 */
4959a747e4fSDavid du Colombier 	Binit(&b, fd, OREAD);
4969a747e4fSDavid du Colombier 	n = 0;
4979a747e4fSDavid du Colombier 	while(p = Brdline(&b, '\n')){
4989a747e4fSDavid du Colombier 		p[Blinelen(&b)-1] = '\0';
4999a747e4fSDavid du Colombier 		n = atoi(p)+1;
5009a747e4fSDavid du Colombier 		q = strchr(p, ' ');
5019a747e4fSDavid du Colombier 		if(q == nil)
5029a747e4fSDavid du Colombier 			continue;
5039a747e4fSDavid du Colombier 		if(strcmp(q+1, title) == 0){
5049a747e4fSDavid du Colombier 			free(title);
5059a747e4fSDavid du Colombier 			close(fd);
5069a747e4fSDavid du Colombier 			close(lfd);
5079a747e4fSDavid du Colombier 			if(mustbenew){
5089a747e4fSDavid du Colombier 				werrstr("duplicate title");
5099a747e4fSDavid du Colombier 				return -1;
5109a747e4fSDavid du Colombier 			}else
5119a747e4fSDavid du Colombier 				return n;
5129a747e4fSDavid du Colombier 		}
5139a747e4fSDavid du Colombier 	}
5149a747e4fSDavid du Colombier 
5159a747e4fSDavid du Colombier 	seek(fd, 0, 2);	/* just in case it's not append only */
5169a747e4fSDavid du Colombier 	fprint(fd, "%d %s\n", n, title);
5179a747e4fSDavid du Colombier 	close(fd);
5189a747e4fSDavid du Colombier 	close(lfd);
5199a747e4fSDavid du Colombier 	free(title);
5209a747e4fSDavid du Colombier 	/* kick the map */
5219a747e4fSDavid du Colombier 	currentmap(1);
5229a747e4fSDavid du Colombier 	return n;
5239a747e4fSDavid du Colombier }
5249a747e4fSDavid du Colombier 
5259a747e4fSDavid du Colombier int
nametonum(char * s)5269a747e4fSDavid du Colombier nametonum(char *s)
5279a747e4fSDavid du Colombier {
5289a747e4fSDavid du Colombier 	char *p;
5299a747e4fSDavid du Colombier 	int i, lo, hi, m, rv;
5309a747e4fSDavid du Colombier 
5319a747e4fSDavid du Colombier 	s = estrdup(s);
5329a747e4fSDavid du Colombier 	strlower(s);
5339a747e4fSDavid du Colombier 	for(p=s; *p; p++)
5349a747e4fSDavid du Colombier 		if(*p=='_')
5359a747e4fSDavid du Colombier 			*p = ' ';
5369a747e4fSDavid du Colombier 
5379a747e4fSDavid du Colombier 	currentmap(0);
5389a747e4fSDavid du Colombier 	rlock(&maplock);
5399a747e4fSDavid du Colombier 	lo = 0;
5409a747e4fSDavid du Colombier 	hi = map->nel;
5419a747e4fSDavid du Colombier 	while(hi-lo > 1){
5429a747e4fSDavid du Colombier 		m = (lo+hi)/2;
5439a747e4fSDavid du Colombier 		i = strcmp(s, map->el[m].s);
5449a747e4fSDavid du Colombier 		if(i < 0)
5459a747e4fSDavid du Colombier 			hi = m;
5469a747e4fSDavid du Colombier 		else
5479a747e4fSDavid du Colombier 			lo = m;
5489a747e4fSDavid du Colombier 	}
5499a747e4fSDavid du Colombier 	if(hi-lo == 1 && strcmp(s, map->el[lo].s)==0)
5509a747e4fSDavid du Colombier 		rv = map->el[lo].n;
5519a747e4fSDavid du Colombier 	else
5529a747e4fSDavid du Colombier 		rv = -1;
5539a747e4fSDavid du Colombier 	runlock(&maplock);
5549a747e4fSDavid du Colombier 	free(s);
5559a747e4fSDavid du Colombier 	return rv;
5569a747e4fSDavid du Colombier }
5579a747e4fSDavid du Colombier 
5589a747e4fSDavid du Colombier char*
numtoname(int n)5599a747e4fSDavid du Colombier numtoname(int n)
5609a747e4fSDavid du Colombier {
5619a747e4fSDavid du Colombier 	int i;
5629a747e4fSDavid du Colombier 	char *s;
5639a747e4fSDavid du Colombier 
5649a747e4fSDavid du Colombier 	currentmap(0);
5659a747e4fSDavid du Colombier 	rlock(&maplock);
5669a747e4fSDavid du Colombier 	for(i=0; i<map->nel; i++){
5679a747e4fSDavid du Colombier 		if(map->el[i].n==n)
5689a747e4fSDavid du Colombier 			break;
5699a747e4fSDavid du Colombier 	}
5709a747e4fSDavid du Colombier 	if(i==map->nel){
5719a747e4fSDavid du Colombier 		runlock(&maplock);
5729a747e4fSDavid du Colombier 		return nil;
5739a747e4fSDavid du Colombier 	}
5749a747e4fSDavid du Colombier 	s = estrdup(map->el[i].s);
5759a747e4fSDavid du Colombier 	runlock(&maplock);
5769a747e4fSDavid du Colombier 	return s;
5779a747e4fSDavid du Colombier }
5789a747e4fSDavid du Colombier 
5799a747e4fSDavid du Colombier Whist*
getcurrentbyname(char * s)5809a747e4fSDavid du Colombier getcurrentbyname(char *s)
5819a747e4fSDavid du Colombier {
5829a747e4fSDavid du Colombier 	int n;
5839a747e4fSDavid du Colombier 
5849a747e4fSDavid du Colombier 	if((n = nametonum(s)) < 0)
5859a747e4fSDavid du Colombier 		return nil;
5869a747e4fSDavid du Colombier 	return getcache(n, 0);
5879a747e4fSDavid du Colombier }
5889a747e4fSDavid du Colombier 
5899a747e4fSDavid du Colombier static String*
Brdstring(Biobuf * b)5909a747e4fSDavid du Colombier Brdstring(Biobuf *b)
5919a747e4fSDavid du Colombier {
5921052a86aSDavid du Colombier 	long len;
5939a747e4fSDavid du Colombier 	String *s;
5941052a86aSDavid du Colombier 	Dir *d;
5959a747e4fSDavid du Colombier 
5961052a86aSDavid du Colombier 	d = dirfstat(Bfildes(b));
5971052a86aSDavid du Colombier 	if (d == nil)	/* shouldn't happen, we just opened it */
5981052a86aSDavid du Colombier 		len = 0;
5991052a86aSDavid du Colombier 	else
6001052a86aSDavid du Colombier 		len = d->length;
6011052a86aSDavid du Colombier 	free(d);
6021052a86aSDavid du Colombier 	s = s_newalloc(len);
6031052a86aSDavid du Colombier 	s_read(b, s, len);
6049a747e4fSDavid du Colombier 	return s;
6059a747e4fSDavid du Colombier }
6069a747e4fSDavid du Colombier 
6079a747e4fSDavid du Colombier /*
6089a747e4fSDavid du Colombier  * Attempt to install a new page.  If t==0 we are creating.
6099a747e4fSDavid du Colombier  * Otherwise, we are editing and t must be set to the current
6109a747e4fSDavid du Colombier  * version (t is the version we started with) to avoid conflicting
6119a747e4fSDavid du Colombier  * writes.
6129a747e4fSDavid du Colombier  *
6139a747e4fSDavid du Colombier  * If there is a conflicting write, we still write the page to
6149a747e4fSDavid du Colombier  * the history file, but mark it as a failed write.
6159a747e4fSDavid du Colombier  */
6169a747e4fSDavid du Colombier int
writepage(int num,ulong t,String * s,char * title)6179a747e4fSDavid du Colombier writepage(int num, ulong t, String *s, char *title)
6189a747e4fSDavid du Colombier {
6199a747e4fSDavid du Colombier 	char tmp[40], tmplock[40], err[ERRMAX], hist[40], *p;
6209a747e4fSDavid du Colombier 	int conflict, lfd, fd;
6219a747e4fSDavid du Colombier 	Biobuf *b;
6229a747e4fSDavid du Colombier 	String *os;
6239a747e4fSDavid du Colombier 
6249a747e4fSDavid du Colombier 	sprint(tmp, "d/%d", num);
6259a747e4fSDavid du Colombier 	sprint(tmplock, "d/L.%d", num);
6269a747e4fSDavid du Colombier 	sprint(hist, "d/%d.hist", num);
6279a747e4fSDavid du Colombier 	if((lfd = getlock(tmplock)) < 0)
6289a747e4fSDavid du Colombier 		return -1;
6299a747e4fSDavid du Colombier 
6309a747e4fSDavid du Colombier 	conflict = 0;
6319a747e4fSDavid du Colombier 	if(b = wBopen(tmp, OREAD)){
6329a747e4fSDavid du Colombier 		Brdline(b, '\n');	/* title */
6339a747e4fSDavid du Colombier 		if(p = Brdline(b, '\n'))		/* version */
6349a747e4fSDavid du Colombier 			p[Blinelen(b)-1] = '\0';
6359a747e4fSDavid du Colombier 		if(p==nil || p[0] != 'D'){
6369a747e4fSDavid du Colombier 			snprint(err, sizeof err, "bad format in extant file");
6379a747e4fSDavid du Colombier 			conflict = 1;
6389a747e4fSDavid du Colombier 		}else if(strtoul(p+1, 0, 0) != t){
6391052a86aSDavid du Colombier 			os = Brdstring(b);	/* why read the whole file? */
6409a747e4fSDavid du Colombier 			p = strchr(s_to_c(s), '\n');
6419a747e4fSDavid du Colombier 			if(p!=nil && strcmp(p+1, s_to_c(os))==0){	/* ignore dup write */
6429a747e4fSDavid du Colombier 				close(lfd);
6431052a86aSDavid du Colombier 				s_free(os);
6441052a86aSDavid du Colombier 				Bterm(b);
6459a747e4fSDavid du Colombier 				return 0;
6469a747e4fSDavid du Colombier 			}
6479a747e4fSDavid du Colombier 			s_free(os);
6489a747e4fSDavid du Colombier 			snprint(err, sizeof err, "update conflict %lud != %s", t, p+1);
6499a747e4fSDavid du Colombier 			conflict = 1;
6509a747e4fSDavid du Colombier 		}
6519a747e4fSDavid du Colombier 		Bterm(b);
6529a747e4fSDavid du Colombier 	}else{
6539a747e4fSDavid du Colombier 		if(t != 0){
6549a747e4fSDavid du Colombier 			close(lfd);
6559a747e4fSDavid du Colombier 			werrstr("did not expect to create");
6569a747e4fSDavid du Colombier 			return -1;
6579a747e4fSDavid du Colombier 		}
6589a747e4fSDavid du Colombier 	}
6599a747e4fSDavid du Colombier 
6609a747e4fSDavid du Colombier 	if((fd = wopen(hist, OWRITE)) < 0){
6619a747e4fSDavid du Colombier 		if((fd = wcreate(hist, OWRITE, 0666)) < 0){
6629a747e4fSDavid du Colombier 			close(lfd);
6639a747e4fSDavid du Colombier 			return -1;
6649a747e4fSDavid du Colombier 		}else
6659a747e4fSDavid du Colombier 			fprint(fd, "%s\n", title);
6669a747e4fSDavid du Colombier 	}
6679a747e4fSDavid du Colombier 	if(seek(fd, 0, 2) < 0
6689a747e4fSDavid du Colombier 	|| (conflict && write(fd, "X\n", 2) != 2)
6699a747e4fSDavid du Colombier 	|| write(fd, s_to_c(s), s_len(s)) != s_len(s)){
6709a747e4fSDavid du Colombier 		close(fd);
6719a747e4fSDavid du Colombier 		close(lfd);
6729a747e4fSDavid du Colombier 		return -1;
6739a747e4fSDavid du Colombier 	}
6749a747e4fSDavid du Colombier 	close(fd);
6759a747e4fSDavid du Colombier 
6769a747e4fSDavid du Colombier 	if(conflict){
6779a747e4fSDavid du Colombier 		close(lfd);
6789a747e4fSDavid du Colombier 		voidcache(num);
6799a747e4fSDavid du Colombier 		werrstr(err);
6809a747e4fSDavid du Colombier 		return -1;
6819a747e4fSDavid du Colombier 	}
6829a747e4fSDavid du Colombier 
6839a747e4fSDavid du Colombier 	if((fd = wcreate(tmp, OWRITE, 0666)) < 0){
6849a747e4fSDavid du Colombier 		close(lfd);
6859a747e4fSDavid du Colombier 		voidcache(num);
6869a747e4fSDavid du Colombier 		return -1;
6879a747e4fSDavid du Colombier 	}
6889a747e4fSDavid du Colombier 	if(write(fd, title, strlen(title)) != strlen(title)
6899a747e4fSDavid du Colombier 	|| write(fd, "\n", 1) != 1
6909a747e4fSDavid du Colombier 	|| write(fd, s_to_c(s), s_len(s)) != s_len(s)){
6919a747e4fSDavid du Colombier 		close(fd);
6929a747e4fSDavid du Colombier 		close(lfd);
6939a747e4fSDavid du Colombier 		voidcache(num);
6949a747e4fSDavid du Colombier 		return -1;
6959a747e4fSDavid du Colombier 	}
6969a747e4fSDavid du Colombier 	close(fd);
6979a747e4fSDavid du Colombier 	close(lfd);
6989a747e4fSDavid du Colombier 	voidcache(num);
6999a747e4fSDavid du Colombier 	return 0;
7009a747e4fSDavid du Colombier }
701