xref: /plan9/sys/src/cmd/wikifs/parse.c (revision 1052a86abe4783012df9e7959032cbd3f59e6d9c)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <String.h>
59a747e4fSDavid du Colombier #include <ctype.h>
69a747e4fSDavid du Colombier #include <thread.h>
79a747e4fSDavid du Colombier #include "wiki.h"
89a747e4fSDavid du Colombier 
99a747e4fSDavid du Colombier static Wpage*
mkwtxt(int type,char * text)109a747e4fSDavid du Colombier mkwtxt(int type, char *text)
119a747e4fSDavid du Colombier {
129a747e4fSDavid du Colombier 	Wpage *w;
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier 	w = emalloc(sizeof(*w));
159a747e4fSDavid du Colombier 	w->type = type;
169a747e4fSDavid du Colombier 	w->text = text;
17af2e6ba6SDavid du Colombier 	setmalloctag(w, getcallerpc(&type));
189a747e4fSDavid du Colombier 	return w;
199a747e4fSDavid du Colombier }
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier /*
229a747e4fSDavid du Colombier  * turn runs of whitespace into single spaces,
239a747e4fSDavid du Colombier  * eliminate whitespace at beginning and end.
249a747e4fSDavid du Colombier  */
259a747e4fSDavid du Colombier char*
strcondense(char * s,int cutbegin)269a747e4fSDavid du Colombier strcondense(char *s, int cutbegin)
279a747e4fSDavid du Colombier {
289a747e4fSDavid du Colombier 	char *r, *w, *es;
299a747e4fSDavid du Colombier 	int inspace;
309a747e4fSDavid du Colombier 
319a747e4fSDavid du Colombier 	es = s+strlen(s);
329a747e4fSDavid du Colombier 	inspace = cutbegin;
339a747e4fSDavid du Colombier 	for(r=w=s; *r; r++){
349a747e4fSDavid du Colombier 		if(isspace(*r)){
359a747e4fSDavid du Colombier 			if(!inspace){
369a747e4fSDavid du Colombier 				inspace=1;
379a747e4fSDavid du Colombier 				*w++ = ' ';
389a747e4fSDavid du Colombier 			}
399a747e4fSDavid du Colombier 		}else{
409a747e4fSDavid du Colombier 			inspace=0;
419a747e4fSDavid du Colombier 			*w++ = *r;
429a747e4fSDavid du Colombier 		}
439a747e4fSDavid du Colombier 	}
449a747e4fSDavid du Colombier 	assert(w <= es);
459a747e4fSDavid du Colombier 	if(inspace && w>s){
469a747e4fSDavid du Colombier 		--w;
479a747e4fSDavid du Colombier 		*w = '\0';
489a747e4fSDavid du Colombier 	}
499a747e4fSDavid du Colombier 	else
509a747e4fSDavid du Colombier 		*w = '\0';
519a747e4fSDavid du Colombier 	return s;
529a747e4fSDavid du Colombier }
539a747e4fSDavid du Colombier 
549a747e4fSDavid du Colombier /*
559a747e4fSDavid du Colombier  * turn runs of Wplain into single Wplain.
569a747e4fSDavid du Colombier  */
579a747e4fSDavid du Colombier static Wpage*
wcondense(Wpage * wtxt)589a747e4fSDavid du Colombier wcondense(Wpage *wtxt)
599a747e4fSDavid du Colombier {
609a747e4fSDavid du Colombier 	Wpage *ow, *w;
619a747e4fSDavid du Colombier 
629a747e4fSDavid du Colombier 	for(w=wtxt; w; ){
639a747e4fSDavid du Colombier 		if(w->type == Wplain)
649a747e4fSDavid du Colombier 			strcondense(w->text, 1);
659a747e4fSDavid du Colombier 
669a747e4fSDavid du Colombier 		if(w->type != Wplain || w->next==nil
679a747e4fSDavid du Colombier 		|| w->next->type != Wplain){
689a747e4fSDavid du Colombier 			w=w->next;
699a747e4fSDavid du Colombier 			continue;
709a747e4fSDavid du Colombier 		}
719a747e4fSDavid du Colombier 
729a747e4fSDavid du Colombier 		w->text = erealloc(w->text, strlen(w->text)+1+strlen(w->next->text)+1);
739a747e4fSDavid du Colombier 		strcat(w->text, " ");
749a747e4fSDavid du Colombier 		strcat(w->text, w->next->text);
759a747e4fSDavid du Colombier 
769a747e4fSDavid du Colombier 		ow = w->next;
77*1052a86aSDavid du Colombier 		w->next = ow->next;
78*1052a86aSDavid du Colombier 		ow->next = nil;
79*1052a86aSDavid du Colombier 		freepage(ow);
809a747e4fSDavid du Colombier 	}
819a747e4fSDavid du Colombier 	return wtxt;
829a747e4fSDavid du Colombier }
839a747e4fSDavid du Colombier 
849a747e4fSDavid du Colombier /*
859a747e4fSDavid du Colombier  * Parse a link, without the brackets.
869a747e4fSDavid du Colombier  */
879a747e4fSDavid du Colombier static Wpage*
mklink(char * s)889a747e4fSDavid du Colombier mklink(char *s)
899a747e4fSDavid du Colombier {
909a747e4fSDavid du Colombier 	char *q;
919a747e4fSDavid du Colombier 	Wpage *w;
929a747e4fSDavid du Colombier 
939a747e4fSDavid du Colombier 	for(q=s; *q && *q != '|'; q++)
949a747e4fSDavid du Colombier 		;
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier 	if(*q == '\0'){
979a747e4fSDavid du Colombier 		w = mkwtxt(Wlink, estrdup(strcondense(s, 1)));
989a747e4fSDavid du Colombier 		w->url = nil;
999a747e4fSDavid du Colombier 	}else{
1009a747e4fSDavid du Colombier 		*q = '\0';
1019a747e4fSDavid du Colombier 		w = mkwtxt(Wlink, estrdup(strcondense(s, 1)));
1029a747e4fSDavid du Colombier 		w->url = estrdup(strcondense(q+1, 1));
1039a747e4fSDavid du Colombier 	}
104af2e6ba6SDavid du Colombier 	setmalloctag(w, getcallerpc(&s));
1059a747e4fSDavid du Colombier 	return w;
1069a747e4fSDavid du Colombier }
1079a747e4fSDavid du Colombier 
1089a747e4fSDavid du Colombier /*
1099a747e4fSDavid du Colombier  * Parse Wplains, inserting Wlink nodes where appropriate.
1109a747e4fSDavid du Colombier  */
1119a747e4fSDavid du Colombier static Wpage*
wlink(Wpage * wtxt)1129a747e4fSDavid du Colombier wlink(Wpage *wtxt)
1139a747e4fSDavid du Colombier {
1149a747e4fSDavid du Colombier 	char *p, *q, *r, *s;
1159a747e4fSDavid du Colombier 	Wpage *w, *nw;
1169a747e4fSDavid du Colombier 
1179a747e4fSDavid du Colombier 	for(w=wtxt; w; w=nw){
1189a747e4fSDavid du Colombier 		nw = w->next;
1199a747e4fSDavid du Colombier 		if(w->type != Wplain)
1209a747e4fSDavid du Colombier 			continue;
1219a747e4fSDavid du Colombier 		while(w->text[0]){
1229a747e4fSDavid du Colombier 			p = w->text;
1239a747e4fSDavid du Colombier 			for(q=p; *q && *q != '['; q++)
1249a747e4fSDavid du Colombier 				;
1259a747e4fSDavid du Colombier 			if(*q == '\0')
1269a747e4fSDavid du Colombier 				break;
1279a747e4fSDavid du Colombier 			for(r=q; *r && *r != ']'; r++)
1289a747e4fSDavid du Colombier 				;
1299a747e4fSDavid du Colombier 			if(*r == '\0')
1309a747e4fSDavid du Colombier 				break;
1319a747e4fSDavid du Colombier 			*q = '\0';
1329a747e4fSDavid du Colombier 			*r = '\0';
1339a747e4fSDavid du Colombier 			s = w->text;
1349a747e4fSDavid du Colombier 			w->text = estrdup(w->text);
1359a747e4fSDavid du Colombier 			w->next = mklink(q+1);
1369a747e4fSDavid du Colombier 			w = w->next;
1379a747e4fSDavid du Colombier 			w->next = mkwtxt(Wplain, estrdup(r+1));
1389a747e4fSDavid du Colombier 			free(s);
1399a747e4fSDavid du Colombier 			w = w->next;
1409a747e4fSDavid du Colombier 			w->next = nw;
1419a747e4fSDavid du Colombier 		}
1429a747e4fSDavid du Colombier 		assert(w->next == nw);
1439a747e4fSDavid du Colombier 	}
1449a747e4fSDavid du Colombier 	return wtxt;
1459a747e4fSDavid du Colombier }
1469a747e4fSDavid du Colombier 
1479a747e4fSDavid du Colombier static int
ismanchar(int c)1489a747e4fSDavid du Colombier ismanchar(int c)
1499a747e4fSDavid du Colombier {
1509a747e4fSDavid du Colombier 	return ('a' <= c && c <= 'z')
1519a747e4fSDavid du Colombier 		|| ('A' <= c && c <= 'Z')
1529a747e4fSDavid du Colombier 		|| ('0' <= c && c <= '9')
1539a747e4fSDavid du Colombier 		|| c=='_' || c=='-' || c=='.' || c=='/'
1549a747e4fSDavid du Colombier 		|| (c < 0);	/* UTF */
1559a747e4fSDavid du Colombier }
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier static Wpage*
findmanref(char * p,char ** beginp,char ** endp)1589a747e4fSDavid du Colombier findmanref(char *p, char **beginp, char **endp)
1599a747e4fSDavid du Colombier {
1609a747e4fSDavid du Colombier 	char *q, *r;
1619a747e4fSDavid du Colombier 	Wpage *w;
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	q=p;
1649a747e4fSDavid du Colombier 	for(;;){
1659a747e4fSDavid du Colombier 		for(; q[0] && (q[0] != '(' || !isdigit(q[1]) || q[2] != ')'); q++)
1669a747e4fSDavid du Colombier 			;
1679a747e4fSDavid du Colombier 		if(*q == '\0')
1689a747e4fSDavid du Colombier 			break;
1699a747e4fSDavid du Colombier 		for(r=q; r>p && ismanchar(r[-1]); r--)
1709a747e4fSDavid du Colombier 			;
1719a747e4fSDavid du Colombier 		if(r==q){
1729a747e4fSDavid du Colombier 			q += 3;
1739a747e4fSDavid du Colombier 			continue;
1749a747e4fSDavid du Colombier 		}
1759a747e4fSDavid du Colombier 		*q = '\0';
1769a747e4fSDavid du Colombier 		w = mkwtxt(Wman, estrdup(r));
1779a747e4fSDavid du Colombier 		*beginp = r;
1789a747e4fSDavid du Colombier 		*q = '(';
1799a747e4fSDavid du Colombier 		w->section = q[1]-'0';
1809a747e4fSDavid du Colombier 		*endp = q+3;
181af2e6ba6SDavid du Colombier 		setmalloctag(w, getcallerpc(&p));
1829a747e4fSDavid du Colombier 		return w;
1839a747e4fSDavid du Colombier 	}
1849a747e4fSDavid du Colombier 	return nil;
1859a747e4fSDavid du Colombier }
1869a747e4fSDavid du Colombier 
1879a747e4fSDavid du Colombier /*
1889a747e4fSDavid du Colombier  * Parse Wplains, looking for man page references.
1899a747e4fSDavid du Colombier  * This should be done by using a plumb(6)-style
1909a747e4fSDavid du Colombier  * control file rather than hard-coding things here.
1919a747e4fSDavid du Colombier  */
1929a747e4fSDavid du Colombier static Wpage*
wman(Wpage * wtxt)1939a747e4fSDavid du Colombier wman(Wpage *wtxt)
1949a747e4fSDavid du Colombier {
1959a747e4fSDavid du Colombier 	char *q, *r;
1969a747e4fSDavid du Colombier 	Wpage *w, *mw, *nw;
1979a747e4fSDavid du Colombier 
1989a747e4fSDavid du Colombier 	for(w=wtxt; w; w=nw){
1999a747e4fSDavid du Colombier 		nw = w->next;
2009a747e4fSDavid du Colombier 		if(w->type != Wplain)
2019a747e4fSDavid du Colombier 			continue;
2029a747e4fSDavid du Colombier 		while(w->text[0]){
2039a747e4fSDavid du Colombier 			if((mw = findmanref(w->text, &q, &r)) == nil)
2049a747e4fSDavid du Colombier 				break;
2059a747e4fSDavid du Colombier 			*q = '\0';
2069a747e4fSDavid du Colombier 			w->next = mw;
2079a747e4fSDavid du Colombier 			w = w->next;
2089a747e4fSDavid du Colombier 			w->next = mkwtxt(Wplain, estrdup(r));
2099a747e4fSDavid du Colombier 			w = w->next;
2109a747e4fSDavid du Colombier 			w->next = nw;
2119a747e4fSDavid du Colombier 		}
2129a747e4fSDavid du Colombier 		assert(w->next == nw);
2139a747e4fSDavid du Colombier 	}
2149a747e4fSDavid du Colombier 	return wtxt;
2159a747e4fSDavid du Colombier }
2169a747e4fSDavid du Colombier 
isheading(char * p)217fcdc259eSDavid du Colombier static int isheading(char *p) {
218fcdc259eSDavid du Colombier 	Rune r;
219fcdc259eSDavid du Colombier 	int hasupper=0;
220fcdc259eSDavid du Colombier 	while(*p) {
221fcdc259eSDavid du Colombier 		p+=chartorune(&r,p);
222fcdc259eSDavid du Colombier 		if(isupperrune(r))
223fcdc259eSDavid du Colombier 			hasupper=1;
224fcdc259eSDavid du Colombier 		else if(islowerrune(r))
225fcdc259eSDavid du Colombier 			return 0;
226fcdc259eSDavid du Colombier 	}
227fcdc259eSDavid du Colombier 	return hasupper;
228fcdc259eSDavid du Colombier }
229fcdc259eSDavid du Colombier 
2309a747e4fSDavid du Colombier Wpage*
Brdpage(char * (* rdline)(void *,int),void * b)2319a747e4fSDavid du Colombier Brdpage(char *(*rdline)(void*,int), void *b)
2329a747e4fSDavid du Colombier {
23313ec2712SDavid du Colombier 	char *p, *c;
2349a747e4fSDavid du Colombier 	int waspara;
2359a747e4fSDavid du Colombier 	Wpage *w, **pw;
2369a747e4fSDavid du Colombier 
2379a747e4fSDavid du Colombier 	w = nil;
2389a747e4fSDavid du Colombier 	pw = &w;
2399a747e4fSDavid du Colombier 	waspara = 1;
2409a747e4fSDavid du Colombier 	while((p = rdline(b, '\n')) != nil){
2419a747e4fSDavid du Colombier 		if(p[0] != '!')
2429a747e4fSDavid du Colombier 			p = strcondense(p, 1);
2439a747e4fSDavid du Colombier 		if(p[0] == '\0'){
2449a747e4fSDavid du Colombier 			if(waspara==0){
2459a747e4fSDavid du Colombier 				waspara=1;
2469a747e4fSDavid du Colombier 				*pw = mkwtxt(Wpara, nil);
2479a747e4fSDavid du Colombier 				pw = &(*pw)->next;
2489a747e4fSDavid du Colombier 			}
2499a747e4fSDavid du Colombier 			continue;
2509a747e4fSDavid du Colombier 		}
2519a747e4fSDavid du Colombier 		waspara = 0;
2529a747e4fSDavid du Colombier 		switch(p[0]){
2539a747e4fSDavid du Colombier 		case '*':
2549a747e4fSDavid du Colombier 			*pw = mkwtxt(Wbullet, nil);
2559a747e4fSDavid du Colombier 			pw = &(*pw)->next;
2569a747e4fSDavid du Colombier 			*pw = mkwtxt(Wplain, estrdup(p+1));
2579a747e4fSDavid du Colombier 			pw = &(*pw)->next;
2589a747e4fSDavid du Colombier 			break;
2599a747e4fSDavid du Colombier 		case '!':
2609a747e4fSDavid du Colombier 			*pw = mkwtxt(Wpre, estrdup(p[1]==' '?p+2:p+1));
2619a747e4fSDavid du Colombier 			pw = &(*pw)->next;
2629a747e4fSDavid du Colombier 			break;
26313ec2712SDavid du Colombier 		case '-':
26413ec2712SDavid du Colombier 			for(c = p; *c != '\0'; c++) {
26513ec2712SDavid du Colombier 				if(*c != '-') {
26613ec2712SDavid du Colombier 					c = p;
26713ec2712SDavid du Colombier 					break;
26813ec2712SDavid du Colombier 				}
26913ec2712SDavid du Colombier 			}
27013ec2712SDavid du Colombier 
27113ec2712SDavid du Colombier 			if( (c-p) > 4) {
27213ec2712SDavid du Colombier 				*pw = mkwtxt(Whr, nil);
27313ec2712SDavid du Colombier 				pw = &(*pw)->next;
27413ec2712SDavid du Colombier 				break;
27513ec2712SDavid du Colombier 			}
27613ec2712SDavid du Colombier 			/* else fall thru */
2779a747e4fSDavid du Colombier 		default:
278fcdc259eSDavid du Colombier 			if(isheading(p)){
2799a747e4fSDavid du Colombier 				*pw = mkwtxt(Wheading, estrdup(p));
2809a747e4fSDavid du Colombier 				pw = &(*pw)->next;
2819a747e4fSDavid du Colombier 				continue;
2829a747e4fSDavid du Colombier 			}
2839a747e4fSDavid du Colombier 			*pw = mkwtxt(Wplain, estrdup(p));
2849a747e4fSDavid du Colombier 			pw = &(*pw)->next;
2859a747e4fSDavid du Colombier 			break;
2869a747e4fSDavid du Colombier 		}
2879a747e4fSDavid du Colombier 	}
2889a747e4fSDavid du Colombier 	if(w == nil)
2899a747e4fSDavid du Colombier 		werrstr("empty page");
2909a747e4fSDavid du Colombier 
2919a747e4fSDavid du Colombier 	*pw = nil;
2929a747e4fSDavid du Colombier 	w = wcondense(w);
2939a747e4fSDavid du Colombier 	w = wlink(w);
2949a747e4fSDavid du Colombier 	w = wman(w);
295af2e6ba6SDavid du Colombier 	setmalloctag(w, getcallerpc(&rdline));
2969a747e4fSDavid du Colombier 
2979a747e4fSDavid du Colombier 	return w;
2989a747e4fSDavid du Colombier }
2999a747e4fSDavid du Colombier 
3009a747e4fSDavid du Colombier void
printpage(Wpage * w)3019a747e4fSDavid du Colombier printpage(Wpage *w)
3029a747e4fSDavid du Colombier {
3039a747e4fSDavid du Colombier 	for(; w; w=w->next){
3049a747e4fSDavid du Colombier 		switch(w->type){
3059a747e4fSDavid du Colombier 		case Wpara:
3069a747e4fSDavid du Colombier 			print("para\n");
3079a747e4fSDavid du Colombier 			break;
3089a747e4fSDavid du Colombier 		case Wheading:
3099a747e4fSDavid du Colombier 			print("heading '%s'\n", w->text);
3109a747e4fSDavid du Colombier 			break;
3119a747e4fSDavid du Colombier 		case Wbullet:
3129a747e4fSDavid du Colombier 			print("bullet\n");
3139a747e4fSDavid du Colombier 			break;
3149a747e4fSDavid du Colombier 		case Wlink:
3159a747e4fSDavid du Colombier 			print("link '%s' '%s'\n", w->text, w->url);
3169a747e4fSDavid du Colombier 			break;
3179a747e4fSDavid du Colombier 		case Wman:
3189a747e4fSDavid du Colombier 			print("man %d %s\n", w->section, w->text);
3199a747e4fSDavid du Colombier 			break;
3209a747e4fSDavid du Colombier 		case Wplain:
3219a747e4fSDavid du Colombier 			print("plain '%s'\n", w->text);
3229a747e4fSDavid du Colombier 			break;
32313ec2712SDavid du Colombier 		case Whr:
32413ec2712SDavid du Colombier 			print("hr\n");
32513ec2712SDavid du Colombier 			break;
3269a747e4fSDavid du Colombier 		case Wpre:
3279a747e4fSDavid du Colombier 			print("pre '%s'\n", w->text);
3289a747e4fSDavid du Colombier 			break;
3299a747e4fSDavid du Colombier 		}
3309a747e4fSDavid du Colombier 	}
3319a747e4fSDavid du Colombier }
332