Annotation of src/usr.bin/mandoc/html.c, Revision 1.26
1.26 ! schwarze 1: /* $Id: html.c,v 1.25 2011/04/24 16:22:02 schwarze Exp $ */
1.1 schwarze 2: /*
1.22 schwarze 3: * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.23 schwarze 4: * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18: #include <sys/types.h>
19:
20: #include <assert.h>
1.3 schwarze 21: #include <ctype.h>
1.4 schwarze 22: #include <stdarg.h>
1.1 schwarze 23: #include <stdio.h>
24: #include <stdint.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
1.9 schwarze 29: #include "mandoc.h"
1.26 ! schwarze 30: #include "libmandoc.h"
1.1 schwarze 31: #include "out.h"
32: #include "html.h"
33: #include "main.h"
34:
35: struct htmldata {
36: const char *name;
37: int flags;
38: #define HTML_CLRLINE (1 << 0)
39: #define HTML_NOSTACK (1 << 1)
1.6 schwarze 40: #define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */
1.1 schwarze 41: };
42:
43: static const struct htmldata htmltags[TAG_MAX] = {
44: {"html", HTML_CLRLINE}, /* TAG_HTML */
45: {"head", HTML_CLRLINE}, /* TAG_HEAD */
46: {"body", HTML_CLRLINE}, /* TAG_BODY */
1.6 schwarze 47: {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
1.1 schwarze 48: {"title", HTML_CLRLINE}, /* TAG_TITLE */
49: {"div", HTML_CLRLINE}, /* TAG_DIV */
50: {"h1", 0}, /* TAG_H1 */
51: {"h2", 0}, /* TAG_H2 */
52: {"span", 0}, /* TAG_SPAN */
1.8 schwarze 53: {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
1.6 schwarze 54: {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
1.1 schwarze 55: {"a", 0}, /* TAG_A */
56: {"table", HTML_CLRLINE}, /* TAG_TABLE */
1.18 schwarze 57: {"tbody", HTML_CLRLINE}, /* TAG_TBODY */
1.6 schwarze 58: {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
1.1 schwarze 59: {"tr", HTML_CLRLINE}, /* TAG_TR */
60: {"td", HTML_CLRLINE}, /* TAG_TD */
61: {"li", HTML_CLRLINE}, /* TAG_LI */
62: {"ul", HTML_CLRLINE}, /* TAG_UL */
63: {"ol", HTML_CLRLINE}, /* TAG_OL */
1.18 schwarze 64: {"dl", HTML_CLRLINE}, /* TAG_DL */
65: {"dt", HTML_CLRLINE}, /* TAG_DT */
66: {"dd", HTML_CLRLINE}, /* TAG_DD */
67: {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */
68: {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */
69: {"pre", HTML_CLRLINE }, /* TAG_PRE */
1.19 schwarze 70: {"b", 0 }, /* TAG_B */
71: {"i", 0 }, /* TAG_I */
1.20 schwarze 72: {"code", 0 }, /* TAG_CODE */
73: {"small", 0 }, /* TAG_SMALL */
1.5 schwarze 74: };
75:
76: static const char *const htmlattrs[ATTR_MAX] = {
1.19 schwarze 77: "http-equiv", /* ATTR_HTTPEQUIV */
78: "content", /* ATTR_CONTENT */
79: "name", /* ATTR_NAME */
80: "rel", /* ATTR_REL */
81: "href", /* ATTR_HREF */
82: "type", /* ATTR_TYPE */
83: "media", /* ATTR_MEDIA */
84: "class", /* ATTR_CLASS */
85: "style", /* ATTR_STYLE */
86: "width", /* ATTR_WIDTH */
87: "id", /* ATTR_ID */
88: "summary", /* ATTR_SUMMARY */
89: "align", /* ATTR_ALIGN */
1.22 schwarze 90: "colspan", /* ATTR_COLSPAN */
1.1 schwarze 91: };
92:
1.26 ! schwarze 93: static const char *const roffscales[SCALE_MAX] = {
! 94: "cm", /* SCALE_CM */
! 95: "in", /* SCALE_IN */
! 96: "pc", /* SCALE_PC */
! 97: "pt", /* SCALE_PT */
! 98: "em", /* SCALE_EM */
! 99: "em", /* SCALE_MM */
! 100: "ex", /* SCALE_EN */
! 101: "ex", /* SCALE_BU */
! 102: "em", /* SCALE_VS */
! 103: "ex", /* SCALE_FS */
! 104: };
1.5 schwarze 105:
1.26 ! schwarze 106: static void bufncat(struct html *, const char *, size_t);
! 107: static void print_ctag(struct html *, enum htmltag);
! 108: static int print_encode(struct html *, const char *, int);
! 109: static void print_metaf(struct html *, enum mandoc_esc);
! 110: static void print_attr(struct html *, const char *, const char *);
! 111: static void *ml_alloc(char *, enum htmltype);
1.5 schwarze 112:
1.6 schwarze 113: static void *
114: ml_alloc(char *outopts, enum htmltype type)
1.1 schwarze 115: {
116: struct html *h;
117: const char *toks[4];
118: char *v;
119:
120: toks[0] = "style";
121: toks[1] = "man";
122: toks[2] = "includes";
123: toks[3] = NULL;
124:
1.24 schwarze 125: h = mandoc_calloc(1, sizeof(struct html));
1.1 schwarze 126:
1.6 schwarze 127: h->type = type;
1.2 schwarze 128: h->tags.head = NULL;
1.26 ! schwarze 129: h->symtab = mchars_alloc();
1.1 schwarze 130:
131: while (outopts && *outopts)
132: switch (getsubopt(&outopts, UNCONST(toks), &v)) {
133: case (0):
134: h->style = v;
135: break;
136: case (1):
137: h->base_man = v;
138: break;
139: case (2):
140: h->base_includes = v;
141: break;
142: default:
143: break;
144: }
145:
146: return(h);
147: }
148:
1.6 schwarze 149: void *
150: html_alloc(char *outopts)
151: {
152:
153: return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
154: }
155:
156:
157: void *
158: xhtml_alloc(char *outopts)
159: {
160:
161: return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
162: }
163:
1.1 schwarze 164:
165: void
166: html_free(void *p)
167: {
168: struct tag *tag;
169: struct html *h;
170:
171: h = (struct html *)p;
172:
1.2 schwarze 173: while ((tag = h->tags.head) != NULL) {
174: h->tags.head = tag->next;
1.1 schwarze 175: free(tag);
176: }
177:
178: if (h->symtab)
1.26 ! schwarze 179: mchars_free(h->symtab);
1.1 schwarze 180:
181: free(h);
182: }
183:
184:
185: void
186: print_gen_head(struct html *h)
187: {
188: struct htmlpair tag[4];
189:
190: tag[0].key = ATTR_HTTPEQUIV;
191: tag[0].val = "Content-Type";
192: tag[1].key = ATTR_CONTENT;
193: tag[1].val = "text/html; charset=utf-8";
194: print_otag(h, TAG_META, 2, tag);
195:
196: tag[0].key = ATTR_NAME;
197: tag[0].val = "resource-type";
198: tag[1].key = ATTR_CONTENT;
199: tag[1].val = "document";
200: print_otag(h, TAG_META, 2, tag);
201:
202: if (h->style) {
203: tag[0].key = ATTR_REL;
204: tag[0].val = "stylesheet";
205: tag[1].key = ATTR_HREF;
206: tag[1].val = h->style;
207: tag[2].key = ATTR_TYPE;
208: tag[2].val = "text/css";
209: tag[3].key = ATTR_MEDIA;
210: tag[3].val = "all";
211: print_otag(h, TAG_LINK, 4, tag);
212: }
213: }
214:
1.5 schwarze 215: static void
1.26 ! schwarze 216: print_metaf(struct html *h, enum mandoc_esc deco)
1.5 schwarze 217: {
218: enum htmlfont font;
1.1 schwarze 219:
1.5 schwarze 220: switch (deco) {
1.26 ! schwarze 221: case (ESCAPE_FONTPREV):
1.5 schwarze 222: font = h->metal;
223: break;
1.26 ! schwarze 224: case (ESCAPE_FONTITALIC):
1.5 schwarze 225: font = HTMLFONT_ITALIC;
226: break;
1.26 ! schwarze 227: case (ESCAPE_FONTBOLD):
1.5 schwarze 228: font = HTMLFONT_BOLD;
229: break;
1.26 ! schwarze 230: case (ESCAPE_FONT):
! 231: /* FALLTHROUGH */
! 232: case (ESCAPE_FONTROMAN):
1.5 schwarze 233: font = HTMLFONT_NONE;
234: break;
235: default:
236: abort();
237: /* NOTREACHED */
1.1 schwarze 238: }
239:
1.20 schwarze 240: if (h->metaf) {
241: print_tagq(h, h->metaf);
242: h->metaf = NULL;
243: }
244:
245: h->metal = h->metac;
246: h->metac = font;
247:
248: if (HTMLFONT_NONE != font)
249: h->metaf = HTMLFONT_BOLD == font ?
250: print_otag(h, TAG_B, 0, NULL) :
251: print_otag(h, TAG_I, 0, NULL);
1.1 schwarze 252: }
253:
1.26 ! schwarze 254: int
! 255: html_strlen(const char *cp)
! 256: {
! 257: int ssz, sz;
! 258: const char *seq, *p;
! 259:
! 260: /*
! 261: * Account for escaped sequences within string length
! 262: * calculations. This follows the logic in term_strlen() as we
! 263: * must calculate the width of produced strings.
! 264: * Assume that characters are always width of "1". This is
! 265: * hacky, but it gets the job done for approximation of widths.
! 266: */
! 267:
! 268: sz = 0;
! 269: while (NULL != (p = strchr(cp, '\\'))) {
! 270: sz += (int)(p - cp);
! 271: ++cp;
! 272: switch (mandoc_escape(&cp, &seq, &ssz)) {
! 273: case (ESCAPE_ERROR):
! 274: return(sz);
! 275: case (ESCAPE_UNICODE):
! 276: /* FALLTHROUGH */
! 277: case (ESCAPE_NUMBERED):
! 278: /* FALLTHROUGH */
! 279: case (ESCAPE_SPECIAL):
! 280: sz++;
! 281: break;
! 282: default:
! 283: break;
! 284: }
! 285: }
! 286:
! 287: assert(sz >= 0);
! 288: return(sz + strlen(cp));
! 289: }
1.1 schwarze 290:
1.5 schwarze 291: static int
292: print_encode(struct html *h, const char *p, int norecurse)
1.1 schwarze 293: {
1.4 schwarze 294: size_t sz;
1.26 ! schwarze 295: int c, len, nospace;
1.5 schwarze 296: const char *seq;
1.26 ! schwarze 297: enum mandoc_esc esc;
1.9 schwarze 298: static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
1.5 schwarze 299:
300: nospace = 0;
1.1 schwarze 301:
1.26 ! schwarze 302: while ('\0' != *p) {
1.9 schwarze 303: sz = strcspn(p, rejs);
1.4 schwarze 304:
305: fwrite(p, 1, sz, stdout);
1.26 ! schwarze 306: p += (int)sz;
1.4 schwarze 307:
1.26 ! schwarze 308: if ('\0' == *p)
! 309: break;
! 310:
! 311: switch (*p++) {
! 312: case ('<'):
1.5 schwarze 313: printf("<");
314: continue;
1.26 ! schwarze 315: case ('>'):
1.5 schwarze 316: printf(">");
317: continue;
1.26 ! schwarze 318: case ('&'):
1.5 schwarze 319: printf("&");
1.1 schwarze 320: continue;
1.26 ! schwarze 321: case (ASCII_HYPH):
1.9 schwarze 322: putchar('-');
323: continue;
1.26 ! schwarze 324: default:
1.4 schwarze 325: break;
1.26 ! schwarze 326: }
1.4 schwarze 327:
1.26 ! schwarze 328: esc = mandoc_escape(&p, &seq, &len);
! 329: if (ESCAPE_ERROR == esc)
! 330: break;
1.5 schwarze 331:
1.26 ! schwarze 332: switch (esc) {
! 333: case (ESCAPE_UNICODE):
! 334: /* Skip passed "u" header. */
! 335: c = mchars_num2uc(seq + 1, len - 1);
! 336: if ('\0' != c)
! 337: printf("&#x%x;", c);
! 338: break;
! 339: case (ESCAPE_NUMBERED):
! 340: c = mchars_num2char(seq, len);
! 341: if ('\0' != c)
! 342: putchar(c);
! 343: break;
! 344: case (ESCAPE_SPECIAL):
! 345: c = mchars_spec2cp(h->symtab, seq, len);
! 346: if (c > 0)
! 347: printf("&#%d;", c);
! 348: else if (-1 == c && 1 == len)
! 349: putchar((int)*seq);
1.5 schwarze 350: break;
1.26 ! schwarze 351: case (ESCAPE_FONT):
1.13 schwarze 352: /* FALLTHROUGH */
1.26 ! schwarze 353: case (ESCAPE_FONTPREV):
1.5 schwarze 354: /* FALLTHROUGH */
1.26 ! schwarze 355: case (ESCAPE_FONTBOLD):
1.5 schwarze 356: /* FALLTHROUGH */
1.26 ! schwarze 357: case (ESCAPE_FONTITALIC):
1.5 schwarze 358: /* FALLTHROUGH */
1.26 ! schwarze 359: case (ESCAPE_FONTROMAN):
1.5 schwarze 360: if (norecurse)
361: break;
1.26 ! schwarze 362: print_metaf(h, esc);
! 363: break;
! 364: case (ESCAPE_NOSPACE):
! 365: if ('\0' == *p)
! 366: nospace = 1;
1.5 schwarze 367: break;
368: default:
369: break;
370: }
1.1 schwarze 371: }
1.5 schwarze 372:
373: return(nospace);
1.1 schwarze 374: }
375:
376:
1.6 schwarze 377: static void
378: print_attr(struct html *h, const char *key, const char *val)
379: {
380: printf(" %s=\"", key);
381: (void)print_encode(h, val, 1);
382: putchar('\"');
383: }
384:
385:
1.1 schwarze 386: struct tag *
387: print_otag(struct html *h, enum htmltag tag,
388: int sz, const struct htmlpair *p)
389: {
390: int i;
391: struct tag *t;
392:
1.6 schwarze 393: /* Push this tags onto the stack of open scopes. */
394:
1.1 schwarze 395: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
1.24 schwarze 396: t = mandoc_malloc(sizeof(struct tag));
1.1 schwarze 397: t->tag = tag;
1.2 schwarze 398: t->next = h->tags.head;
399: h->tags.head = t;
1.1 schwarze 400: } else
401: t = NULL;
402:
403: if ( ! (HTML_NOSPACE & h->flags))
1.12 schwarze 404: if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
405: /* Manage keeps! */
406: if ( ! (HTML_KEEP & h->flags)) {
407: if (HTML_PREKEEP & h->flags)
408: h->flags |= HTML_KEEP;
409: putchar(' ');
410: } else
411: printf(" ");
412: }
1.1 schwarze 413:
1.13 schwarze 414: if ( ! (h->flags & HTML_NONOSPACE))
415: h->flags &= ~HTML_NOSPACE;
1.14 schwarze 416: else
417: h->flags |= HTML_NOSPACE;
1.13 schwarze 418:
1.6 schwarze 419: /* Print out the tag name and attributes. */
420:
1.1 schwarze 421: printf("<%s", htmltags[tag].name);
1.6 schwarze 422: for (i = 0; i < sz; i++)
423: print_attr(h, htmlattrs[p[i].key], p[i].val);
424:
425: /* Add non-overridable attributes. */
426:
427: if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
428: print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
429: print_attr(h, "xml:lang", "en");
430: print_attr(h, "lang", "en");
1.1 schwarze 431: }
1.6 schwarze 432:
1.26 ! schwarze 433: /* Accommodate for XML "well-formed" singleton escaping. */
1.6 schwarze 434:
435: if (HTML_AUTOCLOSE & htmltags[tag].flags)
436: switch (h->type) {
437: case (HTML_XHTML_1_0_STRICT):
438: putchar('/');
439: break;
440: default:
441: break;
442: }
443:
1.4 schwarze 444: putchar('>');
1.1 schwarze 445:
446: h->flags |= HTML_NOSPACE;
1.18 schwarze 447:
448: if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags)
449: putchar('\n');
450:
1.1 schwarze 451: return(t);
452: }
453:
454:
455: static void
456: print_ctag(struct html *h, enum htmltag tag)
457: {
458:
459: printf("</%s>", htmltags[tag].name);
1.3 schwarze 460: if (HTML_CLRLINE & htmltags[tag].flags) {
1.1 schwarze 461: h->flags |= HTML_NOSPACE;
1.4 schwarze 462: putchar('\n');
1.5 schwarze 463: }
1.1 schwarze 464: }
465:
466: void
1.6 schwarze 467: print_gen_decls(struct html *h)
468: {
469: const char *doctype;
470: const char *dtd;
471: const char *name;
472:
473: switch (h->type) {
474: case (HTML_HTML_4_01_STRICT):
475: name = "HTML";
476: doctype = "-//W3C//DTD HTML 4.01//EN";
477: dtd = "http://www.w3.org/TR/html4/strict.dtd";
478: break;
479: default:
1.26 ! schwarze 480: puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
1.6 schwarze 481: name = "html";
482: doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
483: dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
484: break;
485: }
486:
487: printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n",
488: name, doctype, dtd);
1.1 schwarze 489: }
490:
491: void
1.12 schwarze 492: print_text(struct html *h, const char *word)
1.1 schwarze 493: {
494:
1.12 schwarze 495: if ( ! (HTML_NOSPACE & h->flags)) {
496: /* Manage keeps! */
497: if ( ! (HTML_KEEP & h->flags)) {
498: if (HTML_PREKEEP & h->flags)
499: h->flags |= HTML_KEEP;
500: putchar(' ');
501: } else
502: printf(" ");
503: }
1.1 schwarze 504:
1.20 schwarze 505: assert(NULL == h->metaf);
506: if (HTMLFONT_NONE != h->metac)
507: h->metaf = HTMLFONT_BOLD == h->metac ?
508: print_otag(h, TAG_B, 0, NULL) :
509: print_otag(h, TAG_I, 0, NULL);
510:
1.12 schwarze 511: assert(word);
512: if ( ! print_encode(h, word, 0))
1.13 schwarze 513: if ( ! (h->flags & HTML_NONOSPACE))
514: h->flags &= ~HTML_NOSPACE;
1.20 schwarze 515:
516: if (h->metaf) {
517: print_tagq(h, h->metaf);
518: h->metaf = NULL;
519: }
1.17 schwarze 520:
521: h->flags &= ~HTML_IGNDELIM;
1.1 schwarze 522: }
523:
524:
525: void
526: print_tagq(struct html *h, const struct tag *until)
527: {
528: struct tag *tag;
529:
1.2 schwarze 530: while ((tag = h->tags.head) != NULL) {
1.22 schwarze 531: /*
532: * Remember to close out and nullify the current
533: * meta-font and table, if applicable.
534: */
1.5 schwarze 535: if (tag == h->metaf)
536: h->metaf = NULL;
1.22 schwarze 537: if (tag == h->tblt)
538: h->tblt = NULL;
1.1 schwarze 539: print_ctag(h, tag->tag);
1.2 schwarze 540: h->tags.head = tag->next;
1.1 schwarze 541: free(tag);
542: if (until && tag == until)
543: return;
544: }
545: }
546:
547:
548: void
549: print_stagq(struct html *h, const struct tag *suntil)
550: {
551: struct tag *tag;
552:
1.2 schwarze 553: while ((tag = h->tags.head) != NULL) {
1.1 schwarze 554: if (suntil && tag == suntil)
555: return;
1.22 schwarze 556: /*
557: * Remember to close out and nullify the current
558: * meta-font and table, if applicable.
559: */
1.5 schwarze 560: if (tag == h->metaf)
561: h->metaf = NULL;
1.22 schwarze 562: if (tag == h->tblt)
563: h->tblt = NULL;
1.1 schwarze 564: print_ctag(h, tag->tag);
1.2 schwarze 565: h->tags.head = tag->next;
1.1 schwarze 566: free(tag);
567: }
568: }
569:
570: void
571: bufinit(struct html *h)
572: {
573:
574: h->buf[0] = '\0';
575: h->buflen = 0;
576: }
577:
578: void
579: bufcat_style(struct html *h, const char *key, const char *val)
580: {
581:
582: bufcat(h, key);
1.26 ! schwarze 583: bufcat(h, ":");
1.1 schwarze 584: bufcat(h, val);
1.26 ! schwarze 585: bufcat(h, ";");
1.1 schwarze 586: }
587:
588: void
589: bufcat(struct html *h, const char *p)
590: {
591:
1.26 ! schwarze 592: h->buflen = strlcat(h->buf, p, BUFSIZ);
! 593: assert(h->buflen < BUFSIZ);
! 594: h->buflen--;
1.1 schwarze 595: }
596:
597: void
1.26 ! schwarze 598: bufcat_fmt(struct html *h, const char *fmt, ...)
1.1 schwarze 599: {
600: va_list ap;
601:
602: va_start(ap, fmt);
603: (void)vsnprintf(h->buf + (int)h->buflen,
604: BUFSIZ - h->buflen - 1, fmt, ap);
605: va_end(ap);
606: h->buflen = strlen(h->buf);
607: }
608:
1.26 ! schwarze 609: static void
1.1 schwarze 610: bufncat(struct html *h, const char *p, size_t sz)
611: {
612:
1.26 ! schwarze 613: assert(h->buflen + sz + 1 < BUFSIZ);
! 614: strncat(h->buf, p, sz);
1.1 schwarze 615: h->buflen += sz;
616: }
617:
618: void
619: buffmt_includes(struct html *h, const char *name)
620: {
621: const char *p, *pp;
622:
623: pp = h->base_includes;
624:
1.26 ! schwarze 625: bufinit(h);
1.1 schwarze 626: while (NULL != (p = strchr(pp, '%'))) {
627: bufncat(h, pp, (size_t)(p - pp));
628: switch (*(p + 1)) {
629: case('I'):
630: bufcat(h, name);
631: break;
632: default:
633: bufncat(h, p, 2);
634: break;
635: }
636: pp = p + 2;
637: }
638: if (pp)
639: bufcat(h, pp);
640: }
641:
642: void
643: buffmt_man(struct html *h,
644: const char *name, const char *sec)
645: {
646: const char *p, *pp;
647:
648: pp = h->base_man;
649:
1.26 ! schwarze 650: bufinit(h);
1.1 schwarze 651: while (NULL != (p = strchr(pp, '%'))) {
652: bufncat(h, pp, (size_t)(p - pp));
653: switch (*(p + 1)) {
654: case('S'):
655: bufcat(h, sec ? sec : "1");
656: break;
657: case('N'):
1.26 ! schwarze 658: bufcat_fmt(h, name);
1.1 schwarze 659: break;
660: default:
661: bufncat(h, p, 2);
662: break;
663: }
664: pp = p + 2;
665: }
666: if (pp)
667: bufcat(h, pp);
668: }
669:
670: void
671: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
672: {
673: double v;
674:
675: v = su->scale;
1.26 ! schwarze 676: if (SCALE_MM == su->unit && 0.0 == (v /= 100.0))
! 677: v = 1.0;
1.1 schwarze 678:
1.26 ! schwarze 679: bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]);
1.1 schwarze 680: }
681:
1.3 schwarze 682: void
1.26 ! schwarze 683: bufcat_id(struct html *h, const char *src)
1.3 schwarze 684: {
685:
686: /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
687:
1.26 ! schwarze 688: while ('\0' != *src)
! 689: bufcat_fmt(h, "%.2x", *src++);
1.3 schwarze 690: }