1241675Suqs/* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#ifdef HAVE_CONFIG_H 19241675Suqs#include "config.h" 20241675Suqs#endif 21241675Suqs 22241675Suqs#include <sys/types.h> 23241675Suqs 24241675Suqs#include <assert.h> 25241675Suqs#include <ctype.h> 26241675Suqs#include <stdarg.h> 27241675Suqs#include <stdio.h> 28241675Suqs#include <stdint.h> 29241675Suqs#include <stdlib.h> 30241675Suqs#include <string.h> 31241675Suqs#include <unistd.h> 32241675Suqs 33241675Suqs#include "mandoc.h" 34241675Suqs#include "libmandoc.h" 35241675Suqs#include "out.h" 36241675Suqs#include "html.h" 37241675Suqs#include "main.h" 38241675Suqs 39241675Suqsstruct htmldata { 40241675Suqs const char *name; 41241675Suqs int flags; 42241675Suqs#define HTML_CLRLINE (1 << 0) 43241675Suqs#define HTML_NOSTACK (1 << 1) 44241675Suqs#define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */ 45241675Suqs}; 46241675Suqs 47241675Suqsstatic const struct htmldata htmltags[TAG_MAX] = { 48241675Suqs {"html", HTML_CLRLINE}, /* TAG_HTML */ 49241675Suqs {"head", HTML_CLRLINE}, /* TAG_HEAD */ 50241675Suqs {"body", HTML_CLRLINE}, /* TAG_BODY */ 51241675Suqs {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */ 52241675Suqs {"title", HTML_CLRLINE}, /* TAG_TITLE */ 53241675Suqs {"div", HTML_CLRLINE}, /* TAG_DIV */ 54241675Suqs {"h1", 0}, /* TAG_H1 */ 55241675Suqs {"h2", 0}, /* TAG_H2 */ 56241675Suqs {"span", 0}, /* TAG_SPAN */ 57241675Suqs {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */ 58241675Suqs {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */ 59241675Suqs {"a", 0}, /* TAG_A */ 60241675Suqs {"table", HTML_CLRLINE}, /* TAG_TABLE */ 61241675Suqs {"tbody", HTML_CLRLINE}, /* TAG_TBODY */ 62241675Suqs {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */ 63241675Suqs {"tr", HTML_CLRLINE}, /* TAG_TR */ 64241675Suqs {"td", HTML_CLRLINE}, /* TAG_TD */ 65241675Suqs {"li", HTML_CLRLINE}, /* TAG_LI */ 66241675Suqs {"ul", HTML_CLRLINE}, /* TAG_UL */ 67241675Suqs {"ol", HTML_CLRLINE}, /* TAG_OL */ 68241675Suqs {"dl", HTML_CLRLINE}, /* TAG_DL */ 69241675Suqs {"dt", HTML_CLRLINE}, /* TAG_DT */ 70241675Suqs {"dd", HTML_CLRLINE}, /* TAG_DD */ 71241675Suqs {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */ 72241675Suqs {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */ 73241675Suqs {"pre", HTML_CLRLINE }, /* TAG_PRE */ 74241675Suqs {"b", 0 }, /* TAG_B */ 75241675Suqs {"i", 0 }, /* TAG_I */ 76241675Suqs {"code", 0 }, /* TAG_CODE */ 77241675Suqs {"small", 0 }, /* TAG_SMALL */ 78241675Suqs}; 79241675Suqs 80241675Suqsstatic const char *const htmlattrs[ATTR_MAX] = { 81241675Suqs "http-equiv", /* ATTR_HTTPEQUIV */ 82241675Suqs "content", /* ATTR_CONTENT */ 83241675Suqs "name", /* ATTR_NAME */ 84241675Suqs "rel", /* ATTR_REL */ 85241675Suqs "href", /* ATTR_HREF */ 86241675Suqs "type", /* ATTR_TYPE */ 87241675Suqs "media", /* ATTR_MEDIA */ 88241675Suqs "class", /* ATTR_CLASS */ 89241675Suqs "style", /* ATTR_STYLE */ 90241675Suqs "width", /* ATTR_WIDTH */ 91241675Suqs "id", /* ATTR_ID */ 92241675Suqs "summary", /* ATTR_SUMMARY */ 93241675Suqs "align", /* ATTR_ALIGN */ 94241675Suqs "colspan", /* ATTR_COLSPAN */ 95241675Suqs}; 96241675Suqs 97241675Suqsstatic const char *const roffscales[SCALE_MAX] = { 98241675Suqs "cm", /* SCALE_CM */ 99241675Suqs "in", /* SCALE_IN */ 100241675Suqs "pc", /* SCALE_PC */ 101241675Suqs "pt", /* SCALE_PT */ 102241675Suqs "em", /* SCALE_EM */ 103241675Suqs "em", /* SCALE_MM */ 104241675Suqs "ex", /* SCALE_EN */ 105241675Suqs "ex", /* SCALE_BU */ 106241675Suqs "em", /* SCALE_VS */ 107241675Suqs "ex", /* SCALE_FS */ 108241675Suqs}; 109241675Suqs 110241675Suqsstatic void bufncat(struct html *, const char *, size_t); 111241675Suqsstatic void print_ctag(struct html *, enum htmltag); 112241675Suqsstatic int print_encode(struct html *, const char *, int); 113241675Suqsstatic void print_metaf(struct html *, enum mandoc_esc); 114241675Suqsstatic void print_attr(struct html *, const char *, const char *); 115241675Suqsstatic void *ml_alloc(char *, enum htmltype); 116241675Suqs 117241675Suqsstatic void * 118241675Suqsml_alloc(char *outopts, enum htmltype type) 119241675Suqs{ 120241675Suqs struct html *h; 121241675Suqs const char *toks[5]; 122241675Suqs char *v; 123241675Suqs 124241675Suqs toks[0] = "style"; 125241675Suqs toks[1] = "man"; 126241675Suqs toks[2] = "includes"; 127241675Suqs toks[3] = "fragment"; 128241675Suqs toks[4] = NULL; 129241675Suqs 130241675Suqs h = mandoc_calloc(1, sizeof(struct html)); 131241675Suqs 132241675Suqs h->type = type; 133241675Suqs h->tags.head = NULL; 134241675Suqs h->symtab = mchars_alloc(); 135241675Suqs 136241675Suqs while (outopts && *outopts) 137241675Suqs switch (getsubopt(&outopts, UNCONST(toks), &v)) { 138241675Suqs case (0): 139241675Suqs h->style = v; 140241675Suqs break; 141241675Suqs case (1): 142241675Suqs h->base_man = v; 143241675Suqs break; 144241675Suqs case (2): 145241675Suqs h->base_includes = v; 146241675Suqs break; 147241675Suqs case (3): 148241675Suqs h->oflags |= HTML_FRAGMENT; 149241675Suqs break; 150241675Suqs default: 151241675Suqs break; 152241675Suqs } 153241675Suqs 154241675Suqs return(h); 155241675Suqs} 156241675Suqs 157241675Suqsvoid * 158241675Suqshtml_alloc(char *outopts) 159241675Suqs{ 160241675Suqs 161241675Suqs return(ml_alloc(outopts, HTML_HTML_4_01_STRICT)); 162241675Suqs} 163241675Suqs 164241675Suqs 165241675Suqsvoid * 166241675Suqsxhtml_alloc(char *outopts) 167241675Suqs{ 168241675Suqs 169241675Suqs return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT)); 170241675Suqs} 171241675Suqs 172241675Suqs 173241675Suqsvoid 174241675Suqshtml_free(void *p) 175241675Suqs{ 176241675Suqs struct tag *tag; 177241675Suqs struct html *h; 178241675Suqs 179241675Suqs h = (struct html *)p; 180241675Suqs 181241675Suqs while ((tag = h->tags.head) != NULL) { 182241675Suqs h->tags.head = tag->next; 183241675Suqs free(tag); 184241675Suqs } 185241675Suqs 186241675Suqs if (h->symtab) 187241675Suqs mchars_free(h->symtab); 188241675Suqs 189241675Suqs free(h); 190241675Suqs} 191241675Suqs 192241675Suqs 193241675Suqsvoid 194241675Suqsprint_gen_head(struct html *h) 195241675Suqs{ 196241675Suqs struct htmlpair tag[4]; 197241675Suqs 198241675Suqs tag[0].key = ATTR_HTTPEQUIV; 199241675Suqs tag[0].val = "Content-Type"; 200241675Suqs tag[1].key = ATTR_CONTENT; 201241675Suqs tag[1].val = "text/html; charset=utf-8"; 202241675Suqs print_otag(h, TAG_META, 2, tag); 203241675Suqs 204241675Suqs tag[0].key = ATTR_NAME; 205241675Suqs tag[0].val = "resource-type"; 206241675Suqs tag[1].key = ATTR_CONTENT; 207241675Suqs tag[1].val = "document"; 208241675Suqs print_otag(h, TAG_META, 2, tag); 209241675Suqs 210241675Suqs if (h->style) { 211241675Suqs tag[0].key = ATTR_REL; 212241675Suqs tag[0].val = "stylesheet"; 213241675Suqs tag[1].key = ATTR_HREF; 214241675Suqs tag[1].val = h->style; 215241675Suqs tag[2].key = ATTR_TYPE; 216241675Suqs tag[2].val = "text/css"; 217241675Suqs tag[3].key = ATTR_MEDIA; 218241675Suqs tag[3].val = "all"; 219241675Suqs print_otag(h, TAG_LINK, 4, tag); 220241675Suqs } 221241675Suqs} 222241675Suqs 223241675Suqsstatic void 224241675Suqsprint_metaf(struct html *h, enum mandoc_esc deco) 225241675Suqs{ 226241675Suqs enum htmlfont font; 227241675Suqs 228241675Suqs switch (deco) { 229241675Suqs case (ESCAPE_FONTPREV): 230241675Suqs font = h->metal; 231241675Suqs break; 232241675Suqs case (ESCAPE_FONTITALIC): 233241675Suqs font = HTMLFONT_ITALIC; 234241675Suqs break; 235241675Suqs case (ESCAPE_FONTBOLD): 236241675Suqs font = HTMLFONT_BOLD; 237241675Suqs break; 238241675Suqs case (ESCAPE_FONT): 239241675Suqs /* FALLTHROUGH */ 240241675Suqs case (ESCAPE_FONTROMAN): 241241675Suqs font = HTMLFONT_NONE; 242241675Suqs break; 243241675Suqs default: 244241675Suqs abort(); 245241675Suqs /* NOTREACHED */ 246241675Suqs } 247241675Suqs 248241675Suqs if (h->metaf) { 249241675Suqs print_tagq(h, h->metaf); 250241675Suqs h->metaf = NULL; 251241675Suqs } 252241675Suqs 253241675Suqs h->metal = h->metac; 254241675Suqs h->metac = font; 255241675Suqs 256241675Suqs if (HTMLFONT_NONE != font) 257241675Suqs h->metaf = HTMLFONT_BOLD == font ? 258241675Suqs print_otag(h, TAG_B, 0, NULL) : 259241675Suqs print_otag(h, TAG_I, 0, NULL); 260241675Suqs} 261241675Suqs 262241675Suqsint 263241675Suqshtml_strlen(const char *cp) 264241675Suqs{ 265241675Suqs int ssz, sz; 266241675Suqs const char *seq, *p; 267241675Suqs 268241675Suqs /* 269241675Suqs * Account for escaped sequences within string length 270241675Suqs * calculations. This follows the logic in term_strlen() as we 271241675Suqs * must calculate the width of produced strings. 272241675Suqs * Assume that characters are always width of "1". This is 273241675Suqs * hacky, but it gets the job done for approximation of widths. 274241675Suqs */ 275241675Suqs 276241675Suqs sz = 0; 277241675Suqs while (NULL != (p = strchr(cp, '\\'))) { 278241675Suqs sz += (int)(p - cp); 279241675Suqs ++cp; 280241675Suqs switch (mandoc_escape(&cp, &seq, &ssz)) { 281241675Suqs case (ESCAPE_ERROR): 282241675Suqs return(sz); 283241675Suqs case (ESCAPE_UNICODE): 284241675Suqs /* FALLTHROUGH */ 285241675Suqs case (ESCAPE_NUMBERED): 286241675Suqs /* FALLTHROUGH */ 287241675Suqs case (ESCAPE_SPECIAL): 288241675Suqs sz++; 289241675Suqs break; 290241675Suqs default: 291241675Suqs break; 292241675Suqs } 293241675Suqs } 294241675Suqs 295241675Suqs assert(sz >= 0); 296241675Suqs return(sz + strlen(cp)); 297241675Suqs} 298241675Suqs 299241675Suqsstatic int 300241675Suqsprint_encode(struct html *h, const char *p, int norecurse) 301241675Suqs{ 302241675Suqs size_t sz; 303241675Suqs int c, len, nospace; 304241675Suqs const char *seq; 305241675Suqs enum mandoc_esc esc; 306241675Suqs static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' }; 307241675Suqs 308241675Suqs nospace = 0; 309241675Suqs 310241675Suqs while ('\0' != *p) { 311241675Suqs sz = strcspn(p, rejs); 312241675Suqs 313241675Suqs fwrite(p, 1, sz, stdout); 314241675Suqs p += (int)sz; 315241675Suqs 316241675Suqs if ('\0' == *p) 317241675Suqs break; 318241675Suqs 319241675Suqs switch (*p++) { 320241675Suqs case ('<'): 321241675Suqs printf("<"); 322241675Suqs continue; 323241675Suqs case ('>'): 324241675Suqs printf(">"); 325241675Suqs continue; 326241675Suqs case ('&'): 327241675Suqs printf("&"); 328241675Suqs continue; 329241675Suqs case (ASCII_HYPH): 330241675Suqs putchar('-'); 331241675Suqs continue; 332241675Suqs default: 333241675Suqs break; 334241675Suqs } 335241675Suqs 336241675Suqs esc = mandoc_escape(&p, &seq, &len); 337241675Suqs if (ESCAPE_ERROR == esc) 338241675Suqs break; 339241675Suqs 340241675Suqs switch (esc) { 341241675Suqs case (ESCAPE_UNICODE): 342241675Suqs /* Skip passed "u" header. */ 343241675Suqs c = mchars_num2uc(seq + 1, len - 1); 344241675Suqs if ('\0' != c) 345241675Suqs printf("&#x%x;", c); 346241675Suqs break; 347241675Suqs case (ESCAPE_NUMBERED): 348241675Suqs c = mchars_num2char(seq, len); 349241675Suqs if ('\0' != c) 350241675Suqs putchar(c); 351241675Suqs break; 352241675Suqs case (ESCAPE_SPECIAL): 353241675Suqs c = mchars_spec2cp(h->symtab, seq, len); 354241675Suqs if (c > 0) 355241675Suqs printf("&#%d;", c); 356241675Suqs else if (-1 == c && 1 == len) 357241675Suqs putchar((int)*seq); 358241675Suqs break; 359241675Suqs case (ESCAPE_FONT): 360241675Suqs /* FALLTHROUGH */ 361241675Suqs case (ESCAPE_FONTPREV): 362241675Suqs /* FALLTHROUGH */ 363241675Suqs case (ESCAPE_FONTBOLD): 364241675Suqs /* FALLTHROUGH */ 365241675Suqs case (ESCAPE_FONTITALIC): 366241675Suqs /* FALLTHROUGH */ 367241675Suqs case (ESCAPE_FONTROMAN): 368241675Suqs if (norecurse) 369241675Suqs break; 370241675Suqs print_metaf(h, esc); 371241675Suqs break; 372241675Suqs case (ESCAPE_NOSPACE): 373241675Suqs if ('\0' == *p) 374241675Suqs nospace = 1; 375241675Suqs break; 376241675Suqs default: 377241675Suqs break; 378241675Suqs } 379241675Suqs } 380241675Suqs 381241675Suqs return(nospace); 382241675Suqs} 383241675Suqs 384241675Suqs 385241675Suqsstatic void 386241675Suqsprint_attr(struct html *h, const char *key, const char *val) 387241675Suqs{ 388241675Suqs printf(" %s=\"", key); 389241675Suqs (void)print_encode(h, val, 1); 390241675Suqs putchar('\"'); 391241675Suqs} 392241675Suqs 393241675Suqs 394241675Suqsstruct tag * 395241675Suqsprint_otag(struct html *h, enum htmltag tag, 396241675Suqs int sz, const struct htmlpair *p) 397241675Suqs{ 398241675Suqs int i; 399241675Suqs struct tag *t; 400241675Suqs 401241675Suqs /* Push this tags onto the stack of open scopes. */ 402241675Suqs 403241675Suqs if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { 404241675Suqs t = mandoc_malloc(sizeof(struct tag)); 405241675Suqs t->tag = tag; 406241675Suqs t->next = h->tags.head; 407241675Suqs h->tags.head = t; 408241675Suqs } else 409241675Suqs t = NULL; 410241675Suqs 411241675Suqs if ( ! (HTML_NOSPACE & h->flags)) 412241675Suqs if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { 413241675Suqs /* Manage keeps! */ 414241675Suqs if ( ! (HTML_KEEP & h->flags)) { 415241675Suqs if (HTML_PREKEEP & h->flags) 416241675Suqs h->flags |= HTML_KEEP; 417241675Suqs putchar(' '); 418241675Suqs } else 419241675Suqs printf(" "); 420241675Suqs } 421241675Suqs 422241675Suqs if ( ! (h->flags & HTML_NONOSPACE)) 423241675Suqs h->flags &= ~HTML_NOSPACE; 424241675Suqs else 425241675Suqs h->flags |= HTML_NOSPACE; 426241675Suqs 427241675Suqs /* Print out the tag name and attributes. */ 428241675Suqs 429241675Suqs printf("<%s", htmltags[tag].name); 430241675Suqs for (i = 0; i < sz; i++) 431241675Suqs print_attr(h, htmlattrs[p[i].key], p[i].val); 432241675Suqs 433241675Suqs /* Add non-overridable attributes. */ 434241675Suqs 435241675Suqs if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) { 436241675Suqs print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml"); 437241675Suqs print_attr(h, "xml:lang", "en"); 438241675Suqs print_attr(h, "lang", "en"); 439241675Suqs } 440241675Suqs 441241675Suqs /* Accommodate for XML "well-formed" singleton escaping. */ 442241675Suqs 443241675Suqs if (HTML_AUTOCLOSE & htmltags[tag].flags) 444241675Suqs switch (h->type) { 445241675Suqs case (HTML_XHTML_1_0_STRICT): 446241675Suqs putchar('/'); 447241675Suqs break; 448241675Suqs default: 449241675Suqs break; 450241675Suqs } 451241675Suqs 452241675Suqs putchar('>'); 453241675Suqs 454241675Suqs h->flags |= HTML_NOSPACE; 455241675Suqs 456241675Suqs if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) 457241675Suqs putchar('\n'); 458241675Suqs 459241675Suqs return(t); 460241675Suqs} 461241675Suqs 462241675Suqs 463241675Suqsstatic void 464241675Suqsprint_ctag(struct html *h, enum htmltag tag) 465241675Suqs{ 466241675Suqs 467241675Suqs printf("</%s>", htmltags[tag].name); 468241675Suqs if (HTML_CLRLINE & htmltags[tag].flags) { 469241675Suqs h->flags |= HTML_NOSPACE; 470241675Suqs putchar('\n'); 471241675Suqs } 472241675Suqs} 473241675Suqs 474241675Suqsvoid 475241675Suqsprint_gen_decls(struct html *h) 476241675Suqs{ 477241675Suqs const char *doctype; 478241675Suqs const char *dtd; 479241675Suqs const char *name; 480241675Suqs 481241675Suqs switch (h->type) { 482241675Suqs case (HTML_HTML_4_01_STRICT): 483241675Suqs name = "HTML"; 484241675Suqs doctype = "-//W3C//DTD HTML 4.01//EN"; 485241675Suqs dtd = "http://www.w3.org/TR/html4/strict.dtd"; 486241675Suqs break; 487241675Suqs default: 488241675Suqs puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 489241675Suqs name = "html"; 490241675Suqs doctype = "-//W3C//DTD XHTML 1.0 Strict//EN"; 491241675Suqs dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; 492241675Suqs break; 493241675Suqs } 494241675Suqs 495241675Suqs printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", 496241675Suqs name, doctype, dtd); 497241675Suqs} 498241675Suqs 499241675Suqsvoid 500241675Suqsprint_text(struct html *h, const char *word) 501241675Suqs{ 502241675Suqs 503241675Suqs if ( ! (HTML_NOSPACE & h->flags)) { 504241675Suqs /* Manage keeps! */ 505241675Suqs if ( ! (HTML_KEEP & h->flags)) { 506241675Suqs if (HTML_PREKEEP & h->flags) 507241675Suqs h->flags |= HTML_KEEP; 508241675Suqs putchar(' '); 509241675Suqs } else 510241675Suqs printf(" "); 511241675Suqs } 512241675Suqs 513241675Suqs assert(NULL == h->metaf); 514241675Suqs if (HTMLFONT_NONE != h->metac) 515241675Suqs h->metaf = HTMLFONT_BOLD == h->metac ? 516241675Suqs print_otag(h, TAG_B, 0, NULL) : 517241675Suqs print_otag(h, TAG_I, 0, NULL); 518241675Suqs 519241675Suqs assert(word); 520241675Suqs if ( ! print_encode(h, word, 0)) { 521241675Suqs if ( ! (h->flags & HTML_NONOSPACE)) 522241675Suqs h->flags &= ~HTML_NOSPACE; 523241675Suqs } else 524241675Suqs h->flags |= HTML_NOSPACE; 525241675Suqs 526241675Suqs if (h->metaf) { 527241675Suqs print_tagq(h, h->metaf); 528241675Suqs h->metaf = NULL; 529241675Suqs } 530241675Suqs 531241675Suqs h->flags &= ~HTML_IGNDELIM; 532241675Suqs} 533241675Suqs 534241675Suqs 535241675Suqsvoid 536241675Suqsprint_tagq(struct html *h, const struct tag *until) 537241675Suqs{ 538241675Suqs struct tag *tag; 539241675Suqs 540241675Suqs while ((tag = h->tags.head) != NULL) { 541241675Suqs /* 542241675Suqs * Remember to close out and nullify the current 543241675Suqs * meta-font and table, if applicable. 544241675Suqs */ 545241675Suqs if (tag == h->metaf) 546241675Suqs h->metaf = NULL; 547241675Suqs if (tag == h->tblt) 548241675Suqs h->tblt = NULL; 549241675Suqs print_ctag(h, tag->tag); 550241675Suqs h->tags.head = tag->next; 551241675Suqs free(tag); 552241675Suqs if (until && tag == until) 553241675Suqs return; 554241675Suqs } 555241675Suqs} 556241675Suqs 557241675Suqs 558241675Suqsvoid 559241675Suqsprint_stagq(struct html *h, const struct tag *suntil) 560241675Suqs{ 561241675Suqs struct tag *tag; 562241675Suqs 563241675Suqs while ((tag = h->tags.head) != NULL) { 564241675Suqs if (suntil && tag == suntil) 565241675Suqs return; 566241675Suqs /* 567241675Suqs * Remember to close out and nullify the current 568241675Suqs * meta-font and table, if applicable. 569241675Suqs */ 570241675Suqs if (tag == h->metaf) 571241675Suqs h->metaf = NULL; 572241675Suqs if (tag == h->tblt) 573241675Suqs h->tblt = NULL; 574241675Suqs print_ctag(h, tag->tag); 575241675Suqs h->tags.head = tag->next; 576241675Suqs free(tag); 577241675Suqs } 578241675Suqs} 579241675Suqs 580241675Suqsvoid 581241675Suqsbufinit(struct html *h) 582241675Suqs{ 583241675Suqs 584241675Suqs h->buf[0] = '\0'; 585241675Suqs h->buflen = 0; 586241675Suqs} 587241675Suqs 588241675Suqsvoid 589241675Suqsbufcat_style(struct html *h, const char *key, const char *val) 590241675Suqs{ 591241675Suqs 592241675Suqs bufcat(h, key); 593241675Suqs bufcat(h, ":"); 594241675Suqs bufcat(h, val); 595241675Suqs bufcat(h, ";"); 596241675Suqs} 597241675Suqs 598241675Suqsvoid 599241675Suqsbufcat(struct html *h, const char *p) 600241675Suqs{ 601241675Suqs 602241675Suqs h->buflen = strlcat(h->buf, p, BUFSIZ); 603241675Suqs assert(h->buflen < BUFSIZ); 604241675Suqs} 605241675Suqs 606241675Suqsvoid 607241675Suqsbufcat_fmt(struct html *h, const char *fmt, ...) 608241675Suqs{ 609241675Suqs va_list ap; 610241675Suqs 611241675Suqs va_start(ap, fmt); 612241675Suqs (void)vsnprintf(h->buf + (int)h->buflen, 613241675Suqs BUFSIZ - h->buflen - 1, fmt, ap); 614241675Suqs va_end(ap); 615241675Suqs h->buflen = strlen(h->buf); 616241675Suqs} 617241675Suqs 618241675Suqsstatic void 619241675Suqsbufncat(struct html *h, const char *p, size_t sz) 620241675Suqs{ 621241675Suqs 622241675Suqs assert(h->buflen + sz + 1 < BUFSIZ); 623241675Suqs strncat(h->buf, p, sz); 624241675Suqs h->buflen += sz; 625241675Suqs} 626241675Suqs 627241675Suqsvoid 628241675Suqsbuffmt_includes(struct html *h, const char *name) 629241675Suqs{ 630241675Suqs const char *p, *pp; 631241675Suqs 632241675Suqs pp = h->base_includes; 633241675Suqs 634241675Suqs bufinit(h); 635241675Suqs while (NULL != (p = strchr(pp, '%'))) { 636241675Suqs bufncat(h, pp, (size_t)(p - pp)); 637241675Suqs switch (*(p + 1)) { 638241675Suqs case('I'): 639241675Suqs bufcat(h, name); 640241675Suqs break; 641241675Suqs default: 642241675Suqs bufncat(h, p, 2); 643241675Suqs break; 644241675Suqs } 645241675Suqs pp = p + 2; 646241675Suqs } 647241675Suqs if (pp) 648241675Suqs bufcat(h, pp); 649241675Suqs} 650241675Suqs 651241675Suqsvoid 652241675Suqsbuffmt_man(struct html *h, 653241675Suqs const char *name, const char *sec) 654241675Suqs{ 655241675Suqs const char *p, *pp; 656241675Suqs 657241675Suqs pp = h->base_man; 658241675Suqs 659241675Suqs bufinit(h); 660241675Suqs while (NULL != (p = strchr(pp, '%'))) { 661241675Suqs bufncat(h, pp, (size_t)(p - pp)); 662241675Suqs switch (*(p + 1)) { 663241675Suqs case('S'): 664241675Suqs bufcat(h, sec ? sec : "1"); 665241675Suqs break; 666241675Suqs case('N'): 667241675Suqs bufcat_fmt(h, name); 668241675Suqs break; 669241675Suqs default: 670241675Suqs bufncat(h, p, 2); 671241675Suqs break; 672241675Suqs } 673241675Suqs pp = p + 2; 674241675Suqs } 675241675Suqs if (pp) 676241675Suqs bufcat(h, pp); 677241675Suqs} 678241675Suqs 679241675Suqsvoid 680241675Suqsbufcat_su(struct html *h, const char *p, const struct roffsu *su) 681241675Suqs{ 682241675Suqs double v; 683241675Suqs 684241675Suqs v = su->scale; 685241675Suqs if (SCALE_MM == su->unit && 0.0 == (v /= 100.0)) 686241675Suqs v = 1.0; 687241675Suqs 688241675Suqs bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]); 689241675Suqs} 690241675Suqs 691241675Suqsvoid 692241675Suqsbufcat_id(struct html *h, const char *src) 693241675Suqs{ 694241675Suqs 695241675Suqs /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */ 696241675Suqs 697241675Suqs while ('\0' != *src) 698241675Suqs bufcat_fmt(h, "%.2x", *src++); 699241675Suqs} 700