html.c revision 256281
1193323Sed/* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */ 2193323Sed/* 3193323Sed * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4193323Sed * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> 5193323Sed * 6193323Sed * Permission to use, copy, modify, and distribute this software for any 7193323Sed * purpose with or without fee is hereby granted, provided that the above 8193323Sed * copyright notice and this permission notice appear in all copies. 9193323Sed * 10193323Sed * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11193323Sed * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12193323Sed * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13193323Sed * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14193323Sed * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15193323Sed * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16193323Sed * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17193323Sed */ 18193323Sed#ifdef HAVE_CONFIG_H 19193323Sed#include "config.h" 20193323Sed#endif 21193323Sed 22193323Sed#include <sys/types.h> 23193323Sed 24193323Sed#include <assert.h> 25193323Sed#include <ctype.h> 26193323Sed#include <stdarg.h> 27193323Sed#include <stdio.h> 28193323Sed#include <stdint.h> 29193323Sed#include <stdlib.h> 30193323Sed#include <string.h> 31193323Sed#include <unistd.h> 32193323Sed 33193323Sed#include "mandoc.h" 34193323Sed#include "libmandoc.h" 35193323Sed#include "out.h" 36207618Srdivacky#include "html.h" 37193323Sed#include "main.h" 38193323Sed 39193323Sedstruct htmldata { 40193323Sed const char *name; 41193323Sed int flags; 42193323Sed#define HTML_CLRLINE (1 << 0) 43193323Sed#define HTML_NOSTACK (1 << 1) 44193323Sed#define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */ 45193323Sed}; 46193323Sed 47193323Sedstatic const struct htmldata htmltags[TAG_MAX] = { 48193323Sed {"html", HTML_CLRLINE}, /* TAG_HTML */ 49193323Sed {"head", HTML_CLRLINE}, /* TAG_HEAD */ 50193323Sed {"body", HTML_CLRLINE}, /* TAG_BODY */ 51193323Sed {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */ 52193323Sed {"title", HTML_CLRLINE}, /* TAG_TITLE */ 53193323Sed {"div", HTML_CLRLINE}, /* TAG_DIV */ 54193323Sed {"h1", 0}, /* TAG_H1 */ 55193323Sed {"h2", 0}, /* TAG_H2 */ 56193323Sed {"span", 0}, /* TAG_SPAN */ 57193323Sed {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */ 58193323Sed {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */ 59193323Sed {"a", 0}, /* TAG_A */ 60193323Sed {"table", HTML_CLRLINE}, /* TAG_TABLE */ 61193323Sed {"tbody", HTML_CLRLINE}, /* TAG_TBODY */ 62193323Sed {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */ 63193323Sed {"tr", HTML_CLRLINE}, /* TAG_TR */ 64193323Sed {"td", HTML_CLRLINE}, /* TAG_TD */ 65193323Sed {"li", HTML_CLRLINE}, /* TAG_LI */ 66193323Sed {"ul", HTML_CLRLINE}, /* TAG_UL */ 67198090Srdivacky {"ol", HTML_CLRLINE}, /* TAG_OL */ 68193323Sed {"dl", HTML_CLRLINE}, /* TAG_DL */ 69193323Sed {"dt", HTML_CLRLINE}, /* TAG_DT */ 70193323Sed {"dd", HTML_CLRLINE}, /* TAG_DD */ 71193323Sed {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */ 72193323Sed {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */ 73193323Sed {"pre", HTML_CLRLINE }, /* TAG_PRE */ 74193323Sed {"b", 0 }, /* TAG_B */ 75193323Sed {"i", 0 }, /* TAG_I */ 76193323Sed {"code", 0 }, /* TAG_CODE */ 77198090Srdivacky {"small", 0 }, /* TAG_SMALL */ 78198090Srdivacky}; 79193323Sed 80193323Sedstatic const char *const htmlattrs[ATTR_MAX] = { 81193323Sed "http-equiv", /* ATTR_HTTPEQUIV */ 82193323Sed "content", /* ATTR_CONTENT */ 83193323Sed "name", /* ATTR_NAME */ 84193323Sed "rel", /* ATTR_REL */ 85193323Sed "href", /* ATTR_HREF */ 86193323Sed "type", /* ATTR_TYPE */ 87193323Sed "media", /* ATTR_MEDIA */ 88193323Sed "class", /* ATTR_CLASS */ 89193323Sed "style", /* ATTR_STYLE */ 90193323Sed "width", /* ATTR_WIDTH */ 91193323Sed "id", /* ATTR_ID */ 92193323Sed "summary", /* ATTR_SUMMARY */ 93193323Sed "align", /* ATTR_ALIGN */ 94193323Sed "colspan", /* ATTR_COLSPAN */ 95193323Sed}; 96193323Sed 97193323Sedstatic const char *const roffscales[SCALE_MAX] = { 98193323Sed "cm", /* SCALE_CM */ 99198090Srdivacky "in", /* SCALE_IN */ 100193323Sed "pc", /* SCALE_PC */ 101193323Sed "pt", /* SCALE_PT */ 102193323Sed "em", /* SCALE_EM */ 103193323Sed "em", /* SCALE_MM */ 104193323Sed "ex", /* SCALE_EN */ 105193323Sed "ex", /* SCALE_BU */ 106193323Sed "em", /* SCALE_VS */ 107193323Sed "ex", /* SCALE_FS */ 108193323Sed}; 109193323Sed 110193323Sedstatic void bufncat(struct html *, const char *, size_t); 111193323Sedstatic void print_ctag(struct html *, enum htmltag); 112198090Srdivackystatic int print_encode(struct html *, const char *, int); 113198090Srdivackystatic void print_metaf(struct html *, enum mandoc_esc); 114198090Srdivackystatic void print_attr(struct html *, const char *, const char *); 115193323Sedstatic void *ml_alloc(char *, enum htmltype); 116193323Sed 117193323Sedstatic void * 118193323Sedml_alloc(char *outopts, enum htmltype type) 119193323Sed{ 120193323Sed struct html *h; 121193323Sed const char *toks[5]; 122193323Sed char *v; 123193323Sed 124193323Sed toks[0] = "style"; 125193323Sed toks[1] = "man"; 126193323Sed toks[2] = "includes"; 127193323Sed toks[3] = "fragment"; 128193323Sed toks[4] = NULL; 129193323Sed 130193323Sed h = mandoc_calloc(1, sizeof(struct html)); 131193323Sed 132193323Sed h->type = type; 133193323Sed h->tags.head = NULL; 134193323Sed h->symtab = mchars_alloc(); 135193323Sed 136193323Sed while (outopts && *outopts) 137193323Sed switch (getsubopt(&outopts, UNCONST(toks), &v)) { 138193323Sed case (0): 139193323Sed h->style = v; 140193323Sed break; 141193323Sed case (1): 142193323Sed h->base_man = v; 143193323Sed break; 144193323Sed case (2): 145193323Sed h->base_includes = v; 146193323Sed break; 147193323Sed case (3): 148193323Sed h->oflags |= HTML_FRAGMENT; 149193323Sed break; 150193323Sed default: 151193323Sed break; 152193323Sed } 153193323Sed 154193323Sed return(h); 155193323Sed} 156193323Sed 157193323Sedvoid * 158193323Sedhtml_alloc(char *outopts) 159193323Sed{ 160193323Sed 161193323Sed return(ml_alloc(outopts, HTML_HTML_4_01_STRICT)); 162193323Sed} 163195098Sed 164193323Sed 165193323Sedvoid * 166193323Sedxhtml_alloc(char *outopts) 167193323Sed{ 168193323Sed 169193323Sed return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT)); 170193323Sed} 171193323Sed 172193323Sed 173193323Sedvoid 174193323Sedhtml_free(void *p) 175193323Sed{ 176193323Sed struct tag *tag; 177193323Sed struct html *h; 178193323Sed 179193323Sed h = (struct html *)p; 180193323Sed 181193323Sed while ((tag = h->tags.head) != NULL) { 182193323Sed h->tags.head = tag->next; 183193323Sed free(tag); 184193323Sed } 185193323Sed 186193323Sed if (h->symtab) 187193323Sed mchars_free(h->symtab); 188193323Sed 189198090Srdivacky free(h); 190198090Srdivacky} 191199989Srdivacky 192198090Srdivacky 193193323Sedvoid 194193323Sedprint_gen_head(struct html *h) 195198090Srdivacky{ 196193323Sed struct htmlpair tag[4]; 197193323Sed 198198090Srdivacky tag[0].key = ATTR_HTTPEQUIV; 199193323Sed tag[0].val = "Content-Type"; 200193323Sed tag[1].key = ATTR_CONTENT; 201198090Srdivacky tag[1].val = "text/html; charset=utf-8"; 202193323Sed print_otag(h, TAG_META, 2, tag); 203193323Sed 204193323Sed tag[0].key = ATTR_NAME; 205193323Sed tag[0].val = "resource-type"; 206193323Sed tag[1].key = ATTR_CONTENT; 207193323Sed tag[1].val = "document"; 208193323Sed print_otag(h, TAG_META, 2, tag); 209193323Sed 210193323Sed if (h->style) { 211193323Sed tag[0].key = ATTR_REL; 212193323Sed tag[0].val = "stylesheet"; 213193323Sed tag[1].key = ATTR_HREF; 214193323Sed tag[1].val = h->style; 215193323Sed tag[2].key = ATTR_TYPE; 216193323Sed tag[2].val = "text/css"; 217193323Sed tag[3].key = ATTR_MEDIA; 218193323Sed tag[3].val = "all"; 219193323Sed print_otag(h, TAG_LINK, 4, tag); 220193323Sed } 221193323Sed} 222193323Sed 223193323Sedstatic void 224193323Sedprint_metaf(struct html *h, enum mandoc_esc deco) 225193323Sed{ 226193323Sed enum htmlfont font; 227193323Sed 228198090Srdivacky switch (deco) { 229193323Sed case (ESCAPE_FONTPREV): 230193323Sed font = h->metal; 231193323Sed break; 232193323Sed case (ESCAPE_FONTITALIC): 233193323Sed font = HTMLFONT_ITALIC; 234193323Sed break; 235193323Sed case (ESCAPE_FONTBOLD): 236193323Sed font = HTMLFONT_BOLD; 237193323Sed break; 238198090Srdivacky case (ESCAPE_FONT): 239193323Sed /* FALLTHROUGH */ 240193323Sed case (ESCAPE_FONTROMAN): 241193323Sed font = HTMLFONT_NONE; 242193323Sed break; 243193323Sed default: 244193323Sed abort(); 245193323Sed /* NOTREACHED */ 246193323Sed } 247193323Sed 248193323Sed if (h->metaf) { 249193323Sed print_tagq(h, h->metaf); 250193323Sed h->metaf = NULL; 251193323Sed } 252193323Sed 253193323Sed h->metal = h->metac; 254193323Sed h->metac = font; 255193323Sed 256193323Sed if (HTMLFONT_NONE != font) 257193323Sed h->metaf = HTMLFONT_BOLD == font ? 258193323Sed print_otag(h, TAG_B, 0, NULL) : 259193323Sed print_otag(h, TAG_I, 0, NULL); 260205218Srdivacky} 261193323Sed 262193323Sedint 263193323Sedhtml_strlen(const char *cp) 264193323Sed{ 265193323Sed int ssz, sz; 266193323Sed const char *seq, *p; 267193323Sed 268193323Sed /* 269193323Sed * Account for escaped sequences within string length 270193323Sed * calculations. This follows the logic in term_strlen() as we 271193323Sed * must calculate the width of produced strings. 272193323Sed * Assume that characters are always width of "1". This is 273193323Sed * hacky, but it gets the job done for approximation of widths. 274193323Sed */ 275193323Sed 276193323Sed sz = 0; 277193323Sed while (NULL != (p = strchr(cp, '\\'))) { 278193323Sed sz += (int)(p - cp); 279193323Sed ++cp; 280193323Sed switch (mandoc_escape(&cp, &seq, &ssz)) { 281193323Sed case (ESCAPE_ERROR): 282193323Sed return(sz); 283193323Sed case (ESCAPE_UNICODE): 284193323Sed /* FALLTHROUGH */ 285193323Sed case (ESCAPE_NUMBERED): 286193323Sed /* FALLTHROUGH */ 287193323Sed case (ESCAPE_SPECIAL): 288193323Sed sz++; 289193323Sed break; 290193323Sed default: 291193323Sed break; 292193323Sed } 293193323Sed } 294193323Sed 295193323Sed assert(sz >= 0); 296193323Sed return(sz + strlen(cp)); 297193323Sed} 298193323Sed 299193323Sedstatic int 300193323Sedprint_encode(struct html *h, const char *p, int norecurse) 301193323Sed{ 302193323Sed size_t sz; 303193323Sed int c, len, nospace; 304193323Sed const char *seq; 305193323Sed enum mandoc_esc esc; 306193323Sed static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' }; 307193323Sed 308193323Sed nospace = 0; 309193323Sed 310193323Sed while ('\0' != *p) { 311193323Sed sz = strcspn(p, rejs); 312193323Sed 313193323Sed fwrite(p, 1, sz, stdout); 314193323Sed p += (int)sz; 315193323Sed 316193323Sed if ('\0' == *p) 317193323Sed break; 318193323Sed 319193323Sed switch (*p++) { 320193323Sed case ('<'): 321193323Sed printf("<"); 322193323Sed continue; 323193323Sed case ('>'): 324193323Sed printf(">"); 325193323Sed continue; 326193323Sed case ('&'): 327193323Sed printf("&"); 328193323Sed continue; 329193323Sed case (ASCII_HYPH): 330193323Sed putchar('-'); 331193323Sed continue; 332193323Sed default: 333193323Sed break; 334193323Sed } 335193323Sed 336193323Sed esc = mandoc_escape(&p, &seq, &len); 337193323Sed if (ESCAPE_ERROR == esc) 338193323Sed break; 339193323Sed 340193323Sed switch (esc) { 341193323Sed case (ESCAPE_UNICODE): 342193323Sed /* Skip passed "u" header. */ 343193323Sed c = mchars_num2uc(seq + 1, len - 1); 344193323Sed if ('\0' != c) 345193323Sed printf("&#x%x;", c); 346193323Sed break; 347193323Sed case (ESCAPE_NUMBERED): 348193323Sed c = mchars_num2char(seq, len); 349193323Sed if ('\0' != c) 350193323Sed putchar(c); 351193323Sed break; 352193323Sed case (ESCAPE_SPECIAL): 353193323Sed c = mchars_spec2cp(h->symtab, seq, len); 354193323Sed if (c > 0) 355193323Sed printf("&#%d;", c); 356193323Sed else if (-1 == c && 1 == len) 357193323Sed putchar((int)*seq); 358193323Sed break; 359193323Sed case (ESCAPE_FONT): 360193323Sed /* FALLTHROUGH */ 361193323Sed case (ESCAPE_FONTPREV): 362193323Sed /* FALLTHROUGH */ 363193323Sed case (ESCAPE_FONTBOLD): 364193323Sed /* FALLTHROUGH */ 365193323Sed case (ESCAPE_FONTITALIC): 366200581Srdivacky /* FALLTHROUGH */ 367193323Sed case (ESCAPE_FONTROMAN): 368193323Sed if (norecurse) 369193323Sed break; 370193323Sed print_metaf(h, esc); 371193323Sed break; 372193323Sed case (ESCAPE_NOSPACE): 373193323Sed if ('\0' == *p) 374193323Sed nospace = 1; 375193323Sed break; 376193323Sed default: 377193323Sed break; 378193323Sed } 379193323Sed } 380193323Sed 381193323Sed return(nospace); 382193323Sed} 383193323Sed 384193323Sed 385193323Sedstatic void 386193323Sedprint_attr(struct html *h, const char *key, const char *val) 387193323Sed{ 388193323Sed printf(" %s=\"", key); 389193323Sed (void)print_encode(h, val, 1); 390193323Sed putchar('\"'); 391193323Sed} 392193323Sed 393193323Sed 394193323Sedstruct tag * 395193323Sedprint_otag(struct html *h, enum htmltag tag, 396193323Sed int sz, const struct htmlpair *p) 397193323Sed{ 398193323Sed int i; 399193323Sed struct tag *t; 400193323Sed 401193323Sed /* Push this tags onto the stack of open scopes. */ 402193323Sed 403193323Sed if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { 404193323Sed t = mandoc_malloc(sizeof(struct tag)); 405193323Sed t->tag = tag; 406193323Sed t->next = h->tags.head; 407193323Sed h->tags.head = t; 408193323Sed } else 409193323Sed t = NULL; 410205218Srdivacky 411193323Sed if ( ! (HTML_NOSPACE & h->flags)) 412193323Sed if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { 413193323Sed /* Manage keeps! */ 414193323Sed if ( ! (HTML_KEEP & h->flags)) { 415193323Sed if (HTML_PREKEEP & h->flags) 416193323Sed h->flags |= HTML_KEEP; 417193323Sed putchar(' '); 418193323Sed } else 419193323Sed printf(" "); 420193323Sed } 421193323Sed 422193323Sed if ( ! (h->flags & HTML_NONOSPACE)) 423193323Sed h->flags &= ~HTML_NOSPACE; 424193323Sed else 425193323Sed h->flags |= HTML_NOSPACE; 426193323Sed 427193323Sed /* Print out the tag name and attributes. */ 428193323Sed 429193323Sed printf("<%s", htmltags[tag].name); 430193323Sed for (i = 0; i < sz; i++) 431193323Sed print_attr(h, htmlattrs[p[i].key], p[i].val); 432193323Sed 433193323Sed /* Add non-overridable attributes. */ 434205218Srdivacky 435193323Sed if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) { 436193323Sed print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml"); 437193323Sed print_attr(h, "xml:lang", "en"); 438193323Sed print_attr(h, "lang", "en"); 439193323Sed } 440193323Sed 441193323Sed /* Accommodate for XML "well-formed" singleton escaping. */ 442193323Sed 443193323Sed if (HTML_AUTOCLOSE & htmltags[tag].flags) 444193323Sed switch (h->type) { 445193323Sed case (HTML_XHTML_1_0_STRICT): 446193323Sed putchar('/'); 447193323Sed break; 448193323Sed default: 449193323Sed break; 450193323Sed } 451193323Sed 452193323Sed putchar('>'); 453193323Sed 454193323Sed h->flags |= HTML_NOSPACE; 455193323Sed 456193323Sed if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) 457193323Sed putchar('\n'); 458193323Sed 459193323Sed return(t); 460193323Sed} 461205218Srdivacky 462193323Sed 463193323Sedstatic void 464193323Sedprint_ctag(struct html *h, enum htmltag tag) 465193323Sed{ 466193323Sed 467193323Sed printf("</%s>", htmltags[tag].name); 468193323Sed if (HTML_CLRLINE & htmltags[tag].flags) { 469193323Sed h->flags |= HTML_NOSPACE; 470193323Sed putchar('\n'); 471193323Sed } 472193323Sed} 473193323Sed 474193323Sedvoid 475193323Sedprint_gen_decls(struct html *h) 476193323Sed{ 477193323Sed const char *doctype; 478193323Sed const char *dtd; 479193323Sed const char *name; 480193323Sed 481193323Sed switch (h->type) { 482193323Sed case (HTML_HTML_4_01_STRICT): 483193323Sed name = "HTML"; 484193323Sed doctype = "-//W3C//DTD HTML 4.01//EN"; 485193323Sed dtd = "http://www.w3.org/TR/html4/strict.dtd"; 486193323Sed break; 487193323Sed default: 488193323Sed puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 489193323Sed name = "html"; 490193323Sed doctype = "-//W3C//DTD XHTML 1.0 Strict//EN"; 491193323Sed dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; 492193323Sed break; 493193323Sed } 494193323Sed 495193323Sed printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", 496193323Sed name, doctype, dtd); 497193323Sed} 498193323Sed 499193323Sedvoid 500193323Sedprint_text(struct html *h, const char *word) 501193323Sed{ 502193323Sed 503193323Sed if ( ! (HTML_NOSPACE & h->flags)) { 504193323Sed /* Manage keeps! */ 505193323Sed if ( ! (HTML_KEEP & h->flags)) { 506193323Sed if (HTML_PREKEEP & h->flags) 507193323Sed h->flags |= HTML_KEEP; 508193323Sed putchar(' '); 509193323Sed } else 510193323Sed printf(" "); 511193323Sed } 512193323Sed 513193323Sed assert(NULL == h->metaf); 514193323Sed if (HTMLFONT_NONE != h->metac) 515193323Sed h->metaf = HTMLFONT_BOLD == h->metac ? 516202375Srdivacky print_otag(h, TAG_B, 0, NULL) : 517193323Sed print_otag(h, TAG_I, 0, NULL); 518193323Sed 519193323Sed assert(word); 520193323Sed if ( ! print_encode(h, word, 0)) { 521193323Sed if ( ! (h->flags & HTML_NONOSPACE)) 522193323Sed h->flags &= ~HTML_NOSPACE; 523193323Sed } else 524193323Sed h->flags |= HTML_NOSPACE; 525200581Srdivacky 526193323Sed if (h->metaf) { 527193323Sed print_tagq(h, h->metaf); 528198090Srdivacky h->metaf = NULL; 529193323Sed } 530193323Sed 531193323Sed h->flags &= ~HTML_IGNDELIM; 532193323Sed} 533193323Sed 534193323Sed 535193323Sedvoid 536193323Sedprint_tagq(struct html *h, const struct tag *until) 537193323Sed{ 538193323Sed struct tag *tag; 539193323Sed 540193323Sed while ((tag = h->tags.head) != NULL) { 541193323Sed /* 542193323Sed * Remember to close out and nullify the current 543193323Sed * meta-font and table, if applicable. 544198090Srdivacky */ 545198090Srdivacky if (tag == h->metaf) 546193323Sed h->metaf = NULL; 547193323Sed if (tag == h->tblt) 548193323Sed h->tblt = NULL; 549193323Sed print_ctag(h, tag->tag); 550193323Sed h->tags.head = tag->next; 551193323Sed free(tag); 552193323Sed if (until && tag == until) 553193323Sed return; 554193323Sed } 555193323Sed} 556193323Sed 557193323Sed 558202375Srdivackyvoid 559193323Sedprint_stagq(struct html *h, const struct tag *suntil) 560193323Sed{ 561193323Sed struct tag *tag; 562193323Sed 563193323Sed while ((tag = h->tags.head) != NULL) { 564193323Sed if (suntil && tag == suntil) 565193323Sed return; 566193323Sed /* 567193323Sed * Remember to close out and nullify the current 568193323Sed * meta-font and table, if applicable. 569193323Sed */ 570200581Srdivacky if (tag == h->metaf) 571198090Srdivacky h->metaf = NULL; 572193323Sed if (tag == h->tblt) 573198090Srdivacky h->tblt = NULL; 574193323Sed print_ctag(h, tag->tag); 575193323Sed h->tags.head = tag->next; 576193323Sed free(tag); 577193323Sed } 578193323Sed} 579193323Sed 580193323Sedvoid 581193323Sedbufinit(struct html *h) 582193323Sed{ 583193323Sed 584193323Sed h->buf[0] = '\0'; 585193323Sed h->buflen = 0; 586193323Sed} 587193323Sed 588193323Sedvoid 589193323Sedbufcat_style(struct html *h, const char *key, const char *val) 590193323Sed{ 591193323Sed 592193323Sed bufcat(h, key); 593193323Sed bufcat(h, ":"); 594193323Sed bufcat(h, val); 595193323Sed bufcat(h, ";"); 596193323Sed} 597193323Sed 598193323Sedvoid 599193323Sedbufcat(struct html *h, const char *p) 600193323Sed{ 601193323Sed 602193323Sed h->buflen = strlcat(h->buf, p, BUFSIZ); 603193323Sed assert(h->buflen < BUFSIZ); 604193323Sed} 605193323Sed 606193323Sedvoid 607193323Sedbufcat_fmt(struct html *h, const char *fmt, ...) 608193323Sed{ 609193323Sed va_list ap; 610193323Sed 611193323Sed va_start(ap, fmt); 612193323Sed (void)vsnprintf(h->buf + (int)h->buflen, 613200581Srdivacky BUFSIZ - h->buflen - 1, fmt, ap); 614193323Sed va_end(ap); 615193323Sed h->buflen = strlen(h->buf); 616203954Srdivacky} 617193323Sed 618193323Sedstatic void 619193323Sedbufncat(struct html *h, const char *p, size_t sz) 620193323Sed{ 621193323Sed 622193323Sed assert(h->buflen + sz + 1 < BUFSIZ); 623193323Sed strncat(h->buf, p, sz); 624193323Sed h->buflen += sz; 625202375Srdivacky} 626193323Sed 627193323Sedvoid 628193323Sedbuffmt_includes(struct html *h, const char *name) 629193323Sed{ 630193323Sed const char *p, *pp; 631193323Sed 632198396Srdivacky pp = h->base_includes; 633193323Sed 634193323Sed bufinit(h); 635193323Sed while (NULL != (p = strchr(pp, '%'))) { 636193323Sed bufncat(h, pp, (size_t)(p - pp)); 637193323Sed switch (*(p + 1)) { 638193323Sed case('I'): 639193323Sed bufcat(h, name); 640193323Sed break; 641203954Srdivacky default: 642193323Sed bufncat(h, p, 2); 643203954Srdivacky break; 644203954Srdivacky } 645203954Srdivacky pp = p + 2; 646203954Srdivacky } 647193323Sed if (pp) 648203954Srdivacky bufcat(h, pp); 649203954Srdivacky} 650203954Srdivacky 651203954Srdivackyvoid 652203954Srdivackybuffmt_man(struct html *h, 653203954Srdivacky const char *name, const char *sec) 654203954Srdivacky{ 655203954Srdivacky const char *p, *pp; 656193323Sed 657193323Sed pp = h->base_man; 658193323Sed 659203954Srdivacky bufinit(h); 660203954Srdivacky while (NULL != (p = strchr(pp, '%'))) { 661193323Sed bufncat(h, pp, (size_t)(p - pp)); 662203954Srdivacky switch (*(p + 1)) { 663203954Srdivacky case('S'): 664203954Srdivacky bufcat(h, sec ? sec : "1"); 665203954Srdivacky break; 666203954Srdivacky case('N'): 667203954Srdivacky bufcat_fmt(h, name); 668203954Srdivacky break; 669193323Sed default: 670193323Sed bufncat(h, p, 2); 671198090Srdivacky break; 672193323Sed } 673193323Sed pp = p + 2; 674193323Sed } 675193323Sed if (pp) 676193323Sed bufcat(h, pp); 677193323Sed} 678193323Sed 679193323Sedvoid 680193323Sedbufcat_su(struct html *h, const char *p, const struct roffsu *su) 681193323Sed{ 682193323Sed double v; 683193323Sed 684193323Sed v = su->scale; 685193323Sed if (SCALE_MM == su->unit && 0.0 == (v /= 100.0)) 686193323Sed v = 1.0; 687193323Sed 688193323Sed bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]); 689193323Sed} 690193323Sed 691193323Sedvoid 692193323Sedbufcat_id(struct html *h, const char *src) 693198090Srdivacky{ 694193323Sed 695193323Sed /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */ 696193323Sed 697193323Sed while ('\0' != *src) 698193323Sed bufcat_fmt(h, "%.2x", *src++); 699193323Sed} 700193323Sed