6 typedef struct Tag Tag;
7 typedef struct Attr Attr;
8 typedef struct Text Text;
23 void (*close)(Text *, Tag *);
43 void parsetext(Text *, Tag *);
45 int parseattr(Attr *);
46 void flushtext(Text *);
47 char* getattr(Tag *, char *);
48 int gotattr(Tag *, char *, char *);
49 int gotstyle(Tag *, char *, char *);
50 void reparent(Text *, Tag *, Tag *);
51 void debugtag(Tag *, char *);
56 emitbuf(Text *text, char *buf, int nbuf)
60 nw = text->wp - text->bp;
61 if((text->nb - nw) < nbuf){
66 text->bp = realloc(text->bp, text->nb);
67 text->wp = text->bp + nw;
69 memmove(text->wp, buf, nbuf);
74 emitrune(Text *text, Rune r)
78 if(r == '\r' || r =='\n'){
83 emitbuf(text, buf, runetochar(buf, &r));
87 emit(Text *text, char *fmt, ...)
93 if(fmt[0] == '.' && text->pos)
96 runevsnprint(buf, nelem(buf), fmt, a);
99 emitrune(text, buf[i]);
103 restoreoutput(Text *text, Tag *)
109 ongarbage(Text *text, Tag *tag)
111 if(text->output == 0)
113 tag->close = restoreoutput;
118 onmeta(Text *, Tag *tag)
124 onp(Text *text, Tag *)
130 restorepre(Text *text, Tag *)
137 onpre(Text *text, Tag *tag)
141 tag->close = restorepre;
143 emit(text, ".DS L\n");
147 onli(Text *text, Tag *tag)
149 if(tag->up && cistrcmp(tag->up->tag, "ol") == 0)
152 emit(text, ".IP \\(bu\n");
154 tag->up->close = onp;
158 onh(Text *text, Tag *tag)
165 onbr(Text *text, Tag *tag)
169 if(cistrcmp(tag->tag, "hr") == 0)
170 emit(text, "\\l'5i'\n.br\n");
174 fontstyle(Text *text, char *style)
176 if(strcmp(text->fontstyle, style) == 0)
178 text->fontstyle = style;
179 emit(text, "\\f%s", style);
183 fontsize(Text *text, char *size)
185 if(strcmp(text->fontsize, size) == 0)
187 text->fontsize = size;
188 emit(text, ".%s\n", size);
192 restorefontstyle(Text *text, Tag *tag)
194 fontstyle(text, tag->aux);
198 restorefontsize(Text *text, Tag *tag)
200 fontsize(text, tag->aux);
204 oni(Text *text, Tag *tag)
206 tag->aux = text->fontstyle;
207 tag->close = restorefontstyle;
208 fontstyle(text, "I");
212 onb(Text *text, Tag *tag)
214 tag->aux = text->fontstyle;
215 tag->close = restorefontstyle;
216 fontstyle(text, "B");
219 void onsmall(Text *text, Tag *tag);
220 void onsup(Text *text, Tag *tag);
223 onsub(Text *text, Tag *tag)
225 emit(text, "\\v\'0.5\'");
226 if(cistrcmp(tag->tag, "sub") == 0){
227 emit(text, "\\x\'0.5\'");
230 restorefontsize(text, tag);
235 onsup(Text *text, Tag *tag)
237 emit(text, "\\v\'-0.5\'");
238 if(cistrcmp(tag->tag, "sup") == 0){
239 emit(text, "\\x\'-0.5\'");
242 restorefontsize(text, tag);
247 * this is poor mans CSS handler.
250 onspan(Text *text, Tag *tag)
257 for(a=tag->attr; a < tag->attr+tag->nattr; a++){
258 if(cistrcmp(a->attr, "class") != 0)
261 if(cistrcmp(a->val, "bold") == 0){
265 if(cistrcmp(a->val, "italic") == 0){
269 if(cistrcmp(a->val, "subscript") == 0){
270 strcpy(tag->tag, "sub");
272 strcpy(tag->tag, "span");
275 if(cistrcmp(a->val, "superscript") == 0){
276 strcpy(tag->tag, "sup");
278 strcpy(tag->tag, "span");
285 ontt(Text *text, Tag *tag)
287 tag->aux = text->fontstyle;
288 tag->close = restorefontstyle;
289 fontstyle(text, "C");
293 onsmall(Text *text, Tag *tag)
295 tag->aux = text->fontsize;
296 tag->close = restorefontsize;
297 fontsize(text, "SM");
301 onbig(Text *text, Tag *tag)
303 tag->aux = text->fontsize;
304 tag->close = restorefontsize;
305 fontsize(text, "LG");
309 endquote(Text *text, Tag *tag)
311 if(cistrcmp(tag->tag, "q") == 0)
317 onquote(Text *text, Tag *tag)
319 tag->close = endquote;
320 if(cistrcmp(tag->tag, "q") == 0)
321 emit(text, ".QS\n\"");
326 typedef struct Table Table;
347 if(cistrcmp(tag->tag, "table") == 0)
349 return tabletag(tag->up);
353 dumprows(Text *text, Table *s, Table *e)
356 for(; s != e; s = s->next){
362 emitbuf(text, s->bp, s->nb);
365 emitrune(text, s->brk ? '\n' : '\t');
370 endtable(Text *text, Tag *tag)
379 for(; t; t = t->prev){
385 * nested table case, add our cells to the next table up.
386 * this is the best we can do, tbl doesnt support nesting
388 if(tt = tabletag(tag->up)){
400 for(i = 0, t = h; t; t = t->next){
411 for(t = h; t; t = t->next){
415 s = mallocz(sizeof(Table), 1);
430 if(gotattr(tag, "align", "center"))
431 emit(text, "center ;\n");
433 for(t = s; t; t = t->next){
434 emit(text, "%s", t->fmt);
436 emitrune(text, '\n');
445 dumprows(text, s, t);
458 ontable(Text *, Tag *tag)
461 tag->close = endtable;
465 endcell(Text *text, Tag *tag)
471 if((tt = tabletag(tag)) == nil)
473 if(cistrcmp(tag->tag, "tr") == 0){
479 t->nb = text->wp - text->bp;
481 for(i=0; i<t->nb; i++)
482 if(strchr(" \t\r\n", t->bp[i]) == nil)
485 memmove(t->bp, t->bp+i, t->nb - i);
488 while(t->nb > 0 && strchr(" \t\r\n", t->bp[t->nb-1]))
491 for(i=0; i<t->nb; i++)
492 if(strchr("\t\r\n", t->bp[i]))
494 t->enclose = i < t->nb;
498 if(gotstyle(tag, "text-align", "center") || gotstyle(tt, "text-align", "center"))
502 if(strcmp(tag->tag, "th") == 0)
503 strcpy(t->fmt+1, "B");
511 oncell(Text *text, Tag *tag)
515 if((tt = tabletag(tag)) == nil)
517 if(cistrcmp(tag->tag, "tr")){
521 while(tt && cistrcmp(tt->tag, "tr"))
525 reparent(text, tag, tt);
527 t = mallocz(sizeof(*t), 1);
537 reparent(text, tag, tt);
538 tag->close = endcell;
543 void (*open)(Text *, Tag *);
547 "blockquote", onquote,
591 while((c = Bgetc(&in)) > 0){
592 if(strchr("\n\r\t ", c) == nil){
607 while((c = Bgetc(&in)) > 0){
610 if(n == 0 && c == '-'){
611 while((c = Bgetc(&in)) > 0){
613 if(Bgetc(&in) == '-')
614 if(Bgetc(&in) == '>')
618 if(n+1 < sizeof(buf)){
620 if(n != 7 || cistrncmp(buf, "[CDATA[", 7))
622 while((c = Bgetc(&in)) > 0){
624 if(Bgetc(&in) == ']'){
625 if(Bgetc(&in) != '>')
642 while((c = Bgetc(&in)) > 0){
643 if(strchr("</>=?!", c)){
647 if(strchr("\n\r\t ", c))
649 if(n < sizeof(a->attr)-1)
657 if(Bgetc(&in) == '='){
660 if(strchr("'\"", c)){
662 while((c = Bgetc(&in)) > 0){
665 if(n < sizeof(a->val)-1)
670 while((c = Bgetc(&in)) > 0){
671 if(strchr("\n\r\t </>?!", c)){
675 if(n < sizeof(a->val)-1)
696 while((c = Bgetc(&in)) > 0){
699 if(strchr("\n\r\t ", c)){
700 if(parseattr(t->attr + t->nattr))
701 if(t->nattr < nelem(t->attr)-1)
705 if(n == 0 && strchr("?!", c)){
717 if(n < sizeof(t->tag)-1)
733 while((c = Bgetc(&in)) > 0){
734 if(strchr(";&</>\n\r\t ", c)){
741 if(n == sizeof(buf)-1)
746 if(strcmp(buf, "lt") == 0)
748 if(strcmp(buf, "gt") == 0)
750 if(strcmp(buf, "quot") == 0)
752 if(strcmp(buf, "apos") == 0)
754 if(strcmp(buf, "amp") == 0)
756 /* use tcs -f html to handle the rest. */
760 if(fullrune(buf, n)){
766 } while((c = Bgetc(&in)) > 0);
787 debugtag(Tag *tag, char *dbg)
797 debugtag(tag->up, nil);
798 fprint(2, "%s %s%s", tag->tag, dbg ? dbg : " > ", dbg ? "\n" : "");
802 getattr(Tag *tag, char *attr)
806 for(i=0; i<tag->nattr; i++)
807 if(cistrcmp(tag->attr[i].attr, attr) == 0)
808 return tag->attr[i].val;
813 gotattr(Tag *tag, char *attr, char *val)
817 if((v = getattr(tag, attr)) == nil)
819 return cistrstr(v, val) != 0;
823 gotstyle(Tag *tag, char *style, char *val)
827 if((v = getattr(tag, "style")) == nil)
829 if((v = cistrstr(v, style)) == nil)
832 while(*v && *v != ':')
837 while(*v && strchr("\t ", *v))
839 if(cistrncmp(v, val, strlen(val)))
845 reparent(Text *text, Tag *tag, Tag *up)
851 debugtag(old, "reparent");
853 old->close(text, old);
863 parsetext(Text *text, Tag *tag)
871 debugtag(tag, "open");
872 for(c = 0; c < nelem(ontag); c++){
873 if(cistrcmp(tag->tag, ontag[c].tag) == 0){
874 ontag[c].open(text, tag);
878 hidden = getattr(tag, "hidden") || gotstyle(tag, "display", "none");
883 if(tag == nil || tag->closing == 0){
884 while((c = Bgetc(&in)) > 0){
886 memset(&t, 0, sizeof(t));
892 debugtag(tag, "skip");
896 debugtag(tag, "back");
897 } else if(t.closing){
899 while(up && cistrcmp(up->tag, t.tag))
909 if(hidden || !text->output)
911 r = substrune(parserune(c));
924 emitrune(text, '\n');
925 else if(text->pos > 0)
928 if((text->pos == 0 && r == '.') || r == '\\')
930 if(r == '\\' || r == 0xA0)
931 emitrune(text, '\\');
940 debugtag(tag, "close");
942 tag->close(text, tag);
953 memset(text, 0, sizeof(Text));
954 text->fontstyle = "R";
955 text->fontsize = "NL";
963 Binit(&in, 0, OREAD);
965 parsetext(&text, nil);
967 write(1, text.bp, text.wp - text.bp);