#include <u.h>#include <libc.h>#include <bio.h>#include <String.h>#include <ctype.h>#include <thread.h>#include "wiki.h" static Wpage*mkwtxt(int type, char *text){ Wpage *w; w = emalloc(sizeof(*w)); w->type = type; w->text = text; setmalloctag(w, getcallerpc(&type)); return w;} /* * turn runs of whitespace into single spaces, * eliminate whitespace at beginning and end. */char*strcondense(char *s, int cutbegin){ char *r, *w, *es; int inspace; es = s+strlen(s); inspace = cutbegin; for(r=w=s; *r; r++){ if(isspace(*r)){ if(!inspace){ inspace=1; *w++ = ' '; } }else{ inspace=0; *w++ = *r; } } assert(w <= es); if(inspace && w>s){ --w; *w = '\0'; } else *w = '\0'; return s;} /* * turn runs of Wplain into single Wplain. */static Wpage*wcondense(Wpage *wtxt){ Wpage *ow, *w; for(w=wtxt; w; ){ if(w->type == Wplain) strcondense(w->text, 1); if(w->type != Wplain || w->next==nil || w->next->type != Wplain){ w=w->next; continue; } w->text = erealloc(w->text, strlen(w->text)+1+strlen(w->next->text)+1); strcat(w->text, " "); strcat(w->text, w->next->text); ow = w->next; w->next = ow->next; ow->next = nil; freepage(ow); } return wtxt;} /* * Parse a link, without the brackets. */static Wpage*mklink(char *s){ char *q; Wpage *w; for(q=s; *q && *q != '|'; q++) ; if(*q == '\0'){ w = mkwtxt(Wlink, estrdup(strcondense(s, 1))); w->url = nil; }else{ *q = '\0'; w = mkwtxt(Wlink, estrdup(strcondense(s, 1))); w->url = estrdup(strcondense(q+1, 1)); } setmalloctag(w, getcallerpc(&s)); return w;} /* * Parse Wplains, inserting Wlink nodes where appropriate. */static Wpage*wlink(Wpage *wtxt){ char *p, *q, *r, *s; Wpage *w, *nw; for(w=wtxt; w; w=nw){ nw = w->next; if(w->type != Wplain) continue; while(w->text[0]){ p = w->text; for(q=p; *q && *q != '['; q++) ; if(*q == '\0') break; for(r=q; *r && *r != ']'; r++) ; if(*r == '\0') break; *q = '\0'; *r = '\0'; s = w->text; w->text = estrdup(w->text); w->next = mklink(q+1); w = w->next; w->next = mkwtxt(Wplain, estrdup(r+1)); free(s); w = w->next; w->next = nw; } assert(w->next == nw); } return wtxt; } static intismanchar(int c){ return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c=='_' || c=='-' || c=='.' || c=='/' || (c < 0); /* UTF */} static Wpage*findmanref(char *p, char **beginp, char **endp){ char *q, *r; Wpage *w; q=p; for(;;){ for(; q[0] && (q[0] != '(' || !isdigit(q[1]) || q[2] != ')'); q++) ; if(*q == '\0') break; for(r=q; r>p && ismanchar(r[-1]); r--) ; if(r==q){ q += 3; continue; } *q = '\0'; w = mkwtxt(Wman, estrdup(r)); *beginp = r; *q = '('; w->section = q[1]-'0'; *endp = q+3; setmalloctag(w, getcallerpc(&p)); return w; } return nil;} /* * Parse Wplains, looking for man page references. * This should be done by using a plumb(6)-style * control file rather than hard-coding things here. */static Wpage*wman(Wpage *wtxt){ char *q, *r; Wpage *w, *mw, *nw; for(w=wtxt; w; w=nw){ nw = w->next; if(w->type != Wplain) continue; while(w->text[0]){ if((mw = findmanref(w->text, &q, &r)) == nil) break; *q = '\0'; w->next = mw; w = w->next; w->next = mkwtxt(Wplain, estrdup(r)); w = w->next; w->next = nw; } assert(w->next == nw); } return wtxt; } static int isheading(char *p) { Rune r; int hasupper=0; while(*p) { p+=chartorune(&r,p); if(isupperrune(r)) hasupper=1; else if(islowerrune(r)) return 0; } return hasupper;} Wpage*Brdpage(char *(*rdline)(void*,int), void *b){ char *p, *c; int waspara; Wpage *w, **pw; w = nil; pw = &w; waspara = 1; while((p = rdline(b, '\n')) != nil){ if(p[0] != '!') p = strcondense(p, 1); if(p[0] == '\0'){ if(waspara==0){ waspara=1; *pw = mkwtxt(Wpara, nil); pw = &(*pw)->next; } continue; } waspara = 0; switch(p[0]){ case '*': *pw = mkwtxt(Wbullet, nil); pw = &(*pw)->next; *pw = mkwtxt(Wplain, estrdup(p+1)); pw = &(*pw)->next; break; case '!': *pw = mkwtxt(Wpre, estrdup(p[1]==' '?p+2:p+1)); pw = &(*pw)->next; break; case '-': for(c = p; *c != '\0'; c++) { if(*c != '-') { c = p; break; } } if( (c-p) > 4) { *pw = mkwtxt(Whr, nil); pw = &(*pw)->next; break; } /* else fall thru */ default: if(isheading(p)){ *pw = mkwtxt(Wheading, estrdup(p)); pw = &(*pw)->next; continue; } *pw = mkwtxt(Wplain, estrdup(p)); pw = &(*pw)->next; break; } } if(w == nil) werrstr("empty page"); *pw = nil; w = wcondense(w); w = wlink(w); w = wman(w); setmalloctag(w, getcallerpc(&rdline)); return w; } voidprintpage(Wpage *w){ for(; w; w=w->next){ switch(w->type){ case Wpara: print("para\n"); break; case Wheading: print("heading '%s'\n", w->text); break; case Wbullet: print("bullet\n"); break; case Wlink: print("link '%s' '%s'\n", w->text, w->url); break; case Wman: print("man %d %s\n", w->section, w->text); break; case Wplain: print("plain '%s'\n", w->text); break; case Whr: print("hr\n"); break; case Wpre: print("pre '%s'\n", w->text); break; } }}
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <String.h>
#include <ctype.h>
#include <thread.h>
#include "wiki.h"
static Wpage*
mkwtxt(int type, char *text)
{
Wpage *w;
w = emalloc(sizeof(*w));
w->type = type;
w->text = text;
setmalloctag(w, getcallerpc(&type));
return w;
}
/*
* turn runs of whitespace into single spaces,
* eliminate whitespace at beginning and end.
*/
char*
strcondense(char *s, int cutbegin)
{
char *r, *w, *es;
int inspace;
es = s+strlen(s);
inspace = cutbegin;
for(r=w=s; *r; r++){
if(isspace(*r)){
if(!inspace){
inspace=1;
*w++ = ' ';
}
}else{
inspace=0;
*w++ = *r;
}
}
assert(w <= es);
if(inspace && w>s){
--w;
*w = '\0';
}
else
*w = '\0';
return s;
}
/*
* turn runs of Wplain into single Wplain.
*/
static Wpage*
wcondense(Wpage *wtxt)
{
Wpage *ow, *w;
for(w=wtxt; w; ){
if(w->type == Wplain)
strcondense(w->text, 1);
if(w->type != Wplain || w->next==nil
|| w->next->type != Wplain){
w=w->next;
continue;
}
w->text = erealloc(w->text, strlen(w->text)+1+strlen(w->next->text)+1);
strcat(w->text, " ");
strcat(w->text, w->next->text);
ow = w->next;
w->next = ow->next;
ow->next = nil;
freepage(ow);
}
return wtxt;
}
/*
* Parse a link, without the brackets.
*/
static Wpage*
mklink(char *s)
{
char *q;
Wpage *w;
for(q=s; *q && *q != '|'; q++)
;
if(*q == '\0'){
w = mkwtxt(Wlink, estrdup(strcondense(s, 1)));
w->url = nil;
}else{
*q = '\0';
w = mkwtxt(Wlink, estrdup(strcondense(s, 1)));
w->url = estrdup(strcondense(q+1, 1));
}
setmalloctag(w, getcallerpc(&s));
return w;
}
/*
* Parse Wplains, inserting Wlink nodes where appropriate.
*/
static Wpage*
wlink(Wpage *wtxt)
{
char *p, *q, *r, *s;
Wpage *w, *nw;
for(w=wtxt; w; w=nw){
nw = w->next;
if(w->type != Wplain)
continue;
while(w->text[0]){
p = w->text;
for(q=p; *q && *q != '['; q++)
;
if(*q == '\0')
break;
for(r=q; *r && *r != ']'; r++)
;
if(*r == '\0')
break;
*q = '\0';
*r = '\0';
s = w->text;
w->text = estrdup(w->text);
w->next = mklink(q+1);
w = w->next;
w->next = mkwtxt(Wplain, estrdup(r+1));
free(s);
w = w->next;
w->next = nw;
}
assert(w->next == nw);
}
return wtxt;
}
static int
ismanchar(int c)
{
return ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c=='_' || c=='-' || c=='.' || c=='/'
|| (c < 0); /* UTF */
}
static Wpage*
findmanref(char *p, char **beginp, char **endp)
{
char *q, *r;
Wpage *w;
q=p;
for(;;){
for(; q[0] && (q[0] != '(' || !isdigit(q[1]) || q[2] != ')'); q++)
;
if(*q == '\0')
break;
for(r=q; r>p && ismanchar(r[-1]); r--)
;
if(r==q){
q += 3;
continue;
}
*q = '\0';
w = mkwtxt(Wman, estrdup(r));
*beginp = r;
*q = '(';
w->section = q[1]-'0';
*endp = q+3;
setmalloctag(w, getcallerpc(&p));
return w;
}
return nil;
}
/*
* Parse Wplains, looking for man page references.
* This should be done by using a plumb(6)-style
* control file rather than hard-coding things here.
*/
static Wpage*
wman(Wpage *wtxt)
{
char *q, *r;
Wpage *w, *mw, *nw;
for(w=wtxt; w; w=nw){
nw = w->next;
if(w->type != Wplain)
continue;
while(w->text[0]){
if((mw = findmanref(w->text, &q, &r)) == nil)
break;
*q = '\0';
w->next = mw;
w = w->next;
w->next = mkwtxt(Wplain, estrdup(r));
w = w->next;
w->next = nw;
}
assert(w->next == nw);
}
return wtxt;
}
static int isheading(char *p) {
Rune r;
int hasupper=0;
while(*p) {
p+=chartorune(&r,p);
if(isupperrune(r))
hasupper=1;
else if(islowerrune(r))
return 0;
}
return hasupper;
}
Wpage*
Brdpage(char *(*rdline)(void*,int), void *b)
{
char *p, *c;
int waspara;
Wpage *w, **pw;
w = nil;
pw = &w;
waspara = 1;
while((p = rdline(b, '\n')) != nil){
if(p[0] != '!')
p = strcondense(p, 1);
if(p[0] == '\0'){
if(waspara==0){
waspara=1;
*pw = mkwtxt(Wpara, nil);
pw = &(*pw)->next;
}
continue;
}
waspara = 0;
switch(p[0]){
case '*':
*pw = mkwtxt(Wbullet, nil);
pw = &(*pw)->next;
*pw = mkwtxt(Wplain, estrdup(p+1));
pw = &(*pw)->next;
break;
case '!':
*pw = mkwtxt(Wpre, estrdup(p[1]==' '?p+2:p+1));
pw = &(*pw)->next;
break;
case '-':
for(c = p; *c != '\0'; c++) {
if(*c != '-') {
c = p;
break;
}
}
if( (c-p) > 4) {
*pw = mkwtxt(Whr, nil);
pw = &(*pw)->next;
break;
}
/* else fall thru */
default:
if(isheading(p)){
*pw = mkwtxt(Wheading, estrdup(p));
pw = &(*pw)->next;
continue;
}
*pw = mkwtxt(Wplain, estrdup(p));
pw = &(*pw)->next;
break;
}
}
if(w == nil)
werrstr("empty page");
*pw = nil;
w = wcondense(w);
w = wlink(w);
w = wman(w);
setmalloctag(w, getcallerpc(&rdline));
return w;
}
void
printpage(Wpage *w)
{
for(; w; w=w->next){
switch(w->type){
case Wpara:
print("para\n");
break;
case Wheading:
print("heading '%s'\n", w->text);
break;
case Wbullet:
print("bullet\n");
break;
case Wlink:
print("link '%s' '%s'\n", w->text, w->url);
break;
case Wman:
print("man %d %s\n", w->section, w->text);
break;
case Wplain:
print("plain '%s'\n", w->text);
break;
case Whr:
print("hr\n");
break;
case Wpre:
print("pre '%s'\n", w->text);
break;
}
}
}