6 typedef struct Tag Tag;
7 typedef struct Attr Attr;
8 typedef struct Text Text;
23 void (*close)(Text *, Tag *);
44 void parsetext(Text *, Tag *);
46 int parseattr(Attr *);
47 void flushtext(Text *);
48 char* getattr(Tag *, char *);
49 int gotattr(Tag *, char *, char *);
50 int gotstyle(Tag *, char *, char *);
51 void reparent(Text *, Tag *, Tag *);
52 void debugtag(Tag *, char *);
57 emitbuf(Text *text, char *buf, int nbuf)
61 nw = text->wp - text->bp;
62 if((text->nb - nw) < nbuf){
67 text->bp = realloc(text->bp, text->nb);
68 text->wp = text->bp + nw;
70 memmove(text->wp, buf, nbuf);
75 emitrune(Text *text, Rune r)
79 if(r == '\r' || r =='\n'){
84 emitbuf(text, buf, runetochar(buf, &r));
88 emit(Text *text, char *fmt, ...)
94 if(fmt[0] == '.' && text->pos)
97 runevsnprint(buf, nelem(buf), fmt, a);
100 emitrune(text, buf[i]);
104 restoreoutput(Text *text, Tag *)
110 ongarbage(Text *text, Tag *tag)
112 if(text->output == 0)
114 tag->close = restoreoutput;
119 onmeta(Text *, Tag *tag)
125 onp(Text *text, Tag *)
131 restorepre(Text *text, Tag *)
138 onpre(Text *text, Tag *tag)
142 tag->close = restorepre;
144 emit(text, ".DS L\n");
148 onli(Text *text, Tag *tag)
150 if(tag->up && cistrcmp(tag->up->tag, "ol") == 0)
153 emit(text, ".IP \\(bu\n");
155 tag->up->close = onp;
159 onh(Text *text, Tag *tag)
166 onbr(Text *text, Tag *tag)
170 if(cistrcmp(tag->tag, "hr") == 0)
171 emit(text, "\\l'5i'\n.br\n");
175 fontstyle(Text *text, char *style)
177 if(strcmp(text->fontstyle, style) == 0)
179 text->fontstyle = style;
180 emit(text, "\\f%s", style);
184 fontsize(Text *text, char *size)
186 if(strcmp(text->fontsize, size) == 0)
188 text->fontsize = size;
189 emit(text, ".%s\n", size);
193 restorefontstyle(Text *text, Tag *tag)
195 fontstyle(text, tag->aux);
199 restorefontsize(Text *text, Tag *tag)
201 fontsize(text, tag->aux);
205 oni(Text *text, Tag *tag)
207 tag->aux = text->fontstyle;
208 tag->close = restorefontstyle;
209 fontstyle(text, "I");
213 onb(Text *text, Tag *tag)
215 tag->aux = text->fontstyle;
216 tag->close = restorefontstyle;
217 fontstyle(text, "B");
221 ontt(Text *text, Tag *tag)
223 tag->aux = text->fontstyle;
224 tag->close = restorefontstyle;
225 fontstyle(text, "C");
229 onsmall(Text *text, Tag *tag)
231 tag->aux = text->fontsize;
232 tag->close = restorefontsize;
233 fontsize(text, "SM");
237 onbig(Text *text, Tag *tag)
239 tag->aux = text->fontsize;
240 tag->close = restorefontsize;
241 fontsize(text, "LG");
245 endquote(Text *text, Tag *tag)
247 if(cistrcmp(tag->tag, "q") == 0)
253 onquote(Text *text, Tag *tag)
255 tag->close = endquote;
256 if(cistrcmp(tag->tag, "q") == 0)
257 emit(text, ".QS\n\"");
262 typedef struct Table Table;
283 if(cistrcmp(tag->tag, "table") == 0)
285 return tabletag(tag->up);
289 dumprows(Text *text, Table *s, Table *e)
292 for(; s != e; s = s->next){
298 emitbuf(text, s->bp, s->nb);
301 emitrune(text, s->brk ? '\n' : '\t');
306 endtable(Text *text, Tag *tag)
315 for(; t; t = t->prev){
321 * nested table case, add our cells to the next table up.
322 * this is the best we can do, tbl doesnt support nesting
324 if(tt = tabletag(tag->up)){
336 for(i = 0, t = h; t; t = t->next){
347 for(t = h; t; t = t->next){
351 s = mallocz(sizeof(Table), 1);
366 if(gotattr(tag, "align", "center"))
367 emit(text, "center ;\n");
369 for(t = s; t; t = t->next){
370 emit(text, "%s", t->fmt);
372 emitrune(text, '\n');
381 dumprows(text, s, t);
394 ontable(Text *, Tag *tag)
397 tag->close = endtable;
401 endcell(Text *text, Tag *tag)
407 if((tt = tabletag(tag)) == nil)
409 if(cistrcmp(tag->tag, "tr") == 0){
415 t->nb = text->wp - text->bp;
417 for(i=0; i<t->nb; i++)
418 if(strchr(" \t\r\n", t->bp[i]) == nil)
421 memmove(t->bp, t->bp+i, t->nb - i);
424 while(t->nb > 0 && strchr(" \t\r\n", t->bp[t->nb-1]))
427 for(i=0; i<t->nb; i++)
428 if(strchr("\t\r\n", t->bp[i]))
430 t->enclose = i < t->nb;
434 if(gotstyle(tag, "text-align", "center") || gotstyle(tt, "text-align", "center"))
445 oncell(Text *text, Tag *tag)
449 if((tt = tabletag(tag)) == nil)
451 if(cistrcmp(tag->tag, "tr")){
455 while(tt && cistrcmp(tt->tag, "tr"))
459 reparent(text, tag, tt);
461 t = mallocz(sizeof(*t), 1);
471 reparent(text, tag, tt);
472 tag->close = endcell;
477 void (*open)(Text *, Tag *);
481 "blockquote", onquote,
522 while((c = Bgetc(&in)) > 0){
523 if(strchr("\n\r\t ", c) == nil){
538 while((c = Bgetc(&in)) > 0){
541 if(n == 0 && c == '-'){
542 while((c = Bgetc(&in)) > 0){
544 if(Bgetc(&in) == '-')
545 if(Bgetc(&in) == '>')
549 if(n+1 < sizeof(buf)){
551 if(n != 7 || cistrncmp(buf, "[CDATA[", 7))
553 while((c = Bgetc(&in)) > 0){
555 if(Bgetc(&in) == ']'){
556 if(Bgetc(&in) != '>')
573 while((c = Bgetc(&in)) > 0){
574 if(strchr("</>=?!", c)){
578 if(strchr("\n\r\t ", c))
580 if(n < sizeof(a->attr)-1)
588 if(Bgetc(&in) == '='){
591 if(strchr("'\"", c)){
593 while((c = Bgetc(&in)) > 0){
596 if(n < sizeof(a->val)-1)
601 while((c = Bgetc(&in)) > 0){
602 if(strchr("\n\r\t </>?!", c)){
606 if(n < sizeof(a->val)-1)
627 while((c = Bgetc(&in)) > 0){
630 if(strchr("\n\r\t ", c)){
631 if(parseattr(t->attr + t->nattr))
632 if(t->nattr < nelem(t->attr)-1)
636 if(n == 0 && strchr("?!", c)){
648 if(n < sizeof(t->tag)-1)
664 while((c = Bgetc(&in)) > 0){
665 if(strchr(";&</>\n\r\t ", c)){
672 if(n == sizeof(buf)-1)
677 if(strcmp(buf, "lt") == 0)
679 if(strcmp(buf, "gt") == 0)
681 if(strcmp(buf, "quot") == 0)
683 if(strcmp(buf, "apos") == 0)
685 if(strcmp(buf, "amp") == 0)
687 /* use tcs -f html to handle the rest. */
691 if(fullrune(buf, n)){
697 } while((c = Bgetc(&in)) > 0);
718 debugtag(Tag *tag, char *dbg)
724 debugtag(tag->up, nil);
725 fprint(2, "%s %s%s", tag->tag, dbg ? dbg : " > ", dbg ? "\n" : "");
729 getattr(Tag *tag, char *attr)
733 for(i=0; i<tag->nattr; i++)
734 if(cistrcmp(tag->attr[i].attr, attr) == 0)
735 return tag->attr[i].val;
740 gotattr(Tag *tag, char *attr, char *val)
744 if((v = getattr(tag, attr)) == nil)
746 return cistrstr(v, val) != 0;
750 gotstyle(Tag *tag, char *style, char *val)
754 if((v = getattr(tag, "style")) == nil)
756 if((v = cistrstr(v, style)) == nil)
759 while(*v && *v != ':')
764 while(*v && strchr("\t ", *v))
766 if(cistrncmp(v, val, strlen(val)))
772 reparent(Text *text, Tag *tag, Tag *up)
778 debugtag(old, "reparent");
780 old->close(text, old);
790 parsetext(Text *text, Tag *tag)
798 debugtag(tag, "open");
799 for(c = 0; c < nelem(ontag); c++){
800 if(cistrcmp(tag->tag, ontag[c].tag) == 0){
801 ontag[c].open(text, tag);
805 hidden = getattr(tag, "hidden") || gotstyle(tag, "display", "none");
810 if(tag == nil || tag->closing == 0){
811 while((c = Bgetc(&in)) > 0){
813 memset(&t, 0, sizeof(t));
820 debugtag(tag, "skip");
824 debugtag(tag, "back");
825 } else if(t.closing){
827 while(up && cistrcmp(up->tag, t.tag))
837 if(hidden || !text->output)
839 r = substrune(parserune(c));
843 if(text->pre == 0 && text->aftertag)
854 emitrune(text, '\n');
855 else if(text->pos > 0)
858 if((text->pos == 0 && r == '.') || r == '\\')
860 if(r == '\\' || r == 0xA0)
861 emitrune(text, '\\');
871 debugtag(tag, "close");
873 tag->close(text, tag);
884 memset(text, 0, sizeof(Text));
885 text->fontstyle = "R";
886 text->fontsize = "NL";
894 Binit(&in, 0, OREAD);
896 parsetext(&text, nil);
898 write(1, text.bp, text.wp - text.bp);