Annotation of mandoc/html.c, Revision 1.86
1.86 ! kristaps 1: /* $Id: html.c,v 1.85 2009/11/14 11:58:36 kristaps Exp $ */
1.1 kristaps 2: /*
1.29 kristaps 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.29 kristaps 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.29 kristaps 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
1.41 kristaps 17: #include <sys/types.h>
1.30 kristaps 18:
1.1 kristaps 19: #include <assert.h>
1.68 kristaps 20: #include <ctype.h>
1.76 kristaps 21: #include <stdarg.h>
1.29 kristaps 22: #include <stdio.h>
1.63 kristaps 23: #include <stdint.h>
1.1 kristaps 24: #include <stdlib.h>
1.33 kristaps 25: #include <string.h>
1.45 kristaps 26: #include <unistd.h>
1.1 kristaps 27:
1.58 kristaps 28: #include "out.h"
1.32 kristaps 29: #include "chars.h"
1.51 kristaps 30: #include "html.h"
1.64 kristaps 31: #include "main.h"
1.2 kristaps 32:
1.63 kristaps 33: #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
34:
1.29 kristaps 35: #define DOCTYPE "-//W3C//DTD HTML 4.01//EN"
36: #define DTD "http://www.w3.org/TR/html4/strict.dtd"
1.8 kristaps 37:
1.29 kristaps 38: struct htmldata {
1.63 kristaps 39: const char *name;
1.29 kristaps 40: int flags;
1.30 kristaps 41: #define HTML_CLRLINE (1 << 0)
42: #define HTML_NOSTACK (1 << 1)
1.29 kristaps 43: };
1.7 kristaps 44:
1.29 kristaps 45: static const struct htmldata htmltags[TAG_MAX] = {
1.30 kristaps 46: {"html", HTML_CLRLINE}, /* TAG_HTML */
47: {"head", HTML_CLRLINE}, /* TAG_HEAD */
48: {"body", HTML_CLRLINE}, /* TAG_BODY */
49: {"meta", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_META */
1.33 kristaps 50: {"title", HTML_CLRLINE}, /* TAG_TITLE */
1.30 kristaps 51: {"div", HTML_CLRLINE}, /* TAG_DIV */
1.29 kristaps 52: {"h1", 0}, /* TAG_H1 */
53: {"h2", 0}, /* TAG_H2 */
1.30 kristaps 54: {"p", HTML_CLRLINE}, /* TAG_P */
1.29 kristaps 55: {"span", 0}, /* TAG_SPAN */
1.30 kristaps 56: {"link", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
57: {"br", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
58: {"a", 0}, /* TAG_A */
1.33 kristaps 59: {"table", HTML_CLRLINE}, /* TAG_TABLE */
60: {"col", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_COL */
61: {"tr", HTML_CLRLINE}, /* TAG_TR */
62: {"td", HTML_CLRLINE}, /* TAG_TD */
1.34 kristaps 63: {"li", HTML_CLRLINE}, /* TAG_LI */
64: {"ul", HTML_CLRLINE}, /* TAG_UL */
65: {"ol", HTML_CLRLINE}, /* TAG_OL */
1.41 kristaps 66: {"base", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
1.29 kristaps 67: };
1.10 kristaps 68:
1.82 kristaps 69: static const char *const htmlattrs[ATTR_MAX] = {
1.29 kristaps 70: "http-equiv",
71: "content",
72: "name",
73: "rel",
74: "href",
75: "type",
76: "media",
1.33 kristaps 77: "class",
78: "style",
79: "width",
80: "valign",
1.54 kristaps 81: "target",
1.57 kristaps 82: "id",
1.67 kristaps 83: "summary",
1.29 kristaps 84: };
1.10 kristaps 85:
1.33 kristaps 86: #ifdef __linux__
1.43 kristaps 87: extern int getsubopt(char **, char * const *, char **);
1.33 kristaps 88: #endif
1.29 kristaps 89:
1.82 kristaps 90:
1.83 kristaps 91: static void print_spec(struct html *, const char *, size_t);
92: static void print_res(struct html *, const char *, size_t);
1.82 kristaps 93: static void print_ctag(struct html *, enum htmltag);
1.85 kristaps 94: static int print_encode(struct html *, const char *);
1.82 kristaps 95:
96:
1.29 kristaps 97: void *
1.43 kristaps 98: html_alloc(char *outopts)
1.10 kristaps 99: {
1.30 kristaps 100: struct html *h;
1.63 kristaps 101: const char *toks[4];
102: char *v;
1.43 kristaps 103:
104: toks[0] = "style";
1.53 kristaps 105: toks[1] = "man";
1.54 kristaps 106: toks[2] = "includes";
107: toks[3] = NULL;
1.30 kristaps 108:
1.72 kristaps 109: h = calloc(1, sizeof(struct html));
110: if (NULL == h) {
1.75 kristaps 111: perror(NULL);
1.72 kristaps 112: exit(EXIT_FAILURE);
113: }
1.10 kristaps 114:
1.66 kristaps 115: h->tags.head = NULL;
116: h->ords.head = NULL;
1.72 kristaps 117: h->symtab = chars_init(CHARS_HTML);
1.41 kristaps 118:
1.47 kristaps 119: while (outopts && *outopts)
1.63 kristaps 120: switch (getsubopt(&outopts, UNCONST(toks), &v)) {
1.43 kristaps 121: case (0):
122: h->style = v;
123: break;
124: case (1):
1.53 kristaps 125: h->base_man = v;
1.43 kristaps 126: break;
1.54 kristaps 127: case (2):
128: h->base_includes = v;
129: break;
1.43 kristaps 130: default:
131: break;
132: }
133:
1.30 kristaps 134: return(h);
1.29 kristaps 135: }
1.10 kristaps 136:
1.33 kristaps 137:
1.29 kristaps 138: void
139: html_free(void *p)
140: {
1.30 kristaps 141: struct tag *tag;
1.37 kristaps 142: struct ord *ord;
1.30 kristaps 143: struct html *h;
144:
145: h = (struct html *)p;
1.10 kristaps 146:
1.66 kristaps 147: while ((ord = h->ords.head) != NULL) {
148: h->ords.head = ord->next;
1.37 kristaps 149: free(ord);
150: }
151:
1.66 kristaps 152: while ((tag = h->tags.head) != NULL) {
153: h->tags.head = tag->next;
1.30 kristaps 154: free(tag);
155: }
1.36 kristaps 156:
157: if (h->symtab)
158: chars_free(h->symtab);
1.53 kristaps 159:
1.30 kristaps 160: free(h);
1.10 kristaps 161: }
1.2 kristaps 162:
1.33 kristaps 163:
1.51 kristaps 164: void
1.29 kristaps 165: print_gen_head(struct html *h)
166: {
1.41 kristaps 167: struct htmlpair tag[4];
168:
169: tag[0].key = ATTR_HTTPEQUIV;
170: tag[0].val = "Content-Type";
171: tag[1].key = ATTR_CONTENT;
172: tag[1].val = "text/html; charset=utf-8";
173: print_otag(h, TAG_META, 2, tag);
174:
175: tag[0].key = ATTR_NAME;
176: tag[0].val = "resource-type";
177: tag[1].key = ATTR_CONTENT;
178: tag[1].val = "document";
179: print_otag(h, TAG_META, 2, tag);
180:
181: if (h->style) {
182: tag[0].key = ATTR_REL;
183: tag[0].val = "stylesheet";
184: tag[1].key = ATTR_HREF;
185: tag[1].val = h->style;
186: tag[2].key = ATTR_TYPE;
187: tag[2].val = "text/css";
188: tag[3].key = ATTR_MEDIA;
189: tag[3].val = "all";
190: print_otag(h, TAG_LINK, 4, tag);
191: }
1.4 kristaps 192: }
193:
1.33 kristaps 194:
1.29 kristaps 195: static void
1.83 kristaps 196: print_spec(struct html *h, const char *p, size_t len)
1.32 kristaps 197: {
198: const char *rhs;
199: size_t sz;
200:
1.83 kristaps 201: rhs = chars_a2ascii(h->symtab, p, len, &sz);
1.32 kristaps 202:
203: if (NULL == rhs)
204: return;
1.76 kristaps 205: fwrite(rhs, 1, sz, stdout);
1.32 kristaps 206: }
207:
1.33 kristaps 208:
1.32 kristaps 209: static void
1.83 kristaps 210: print_res(struct html *h, const char *p, size_t len)
1.32 kristaps 211: {
212: const char *rhs;
213: size_t sz;
214:
1.83 kristaps 215: rhs = chars_a2res(h->symtab, p, len, &sz);
1.32 kristaps 216:
217: if (NULL == rhs)
218: return;
1.76 kristaps 219: fwrite(rhs, 1, sz, stdout);
1.32 kristaps 220: }
221:
1.33 kristaps 222:
1.85 kristaps 223: static int
1.32 kristaps 224: print_encode(struct html *h, const char *p)
1.29 kristaps 225: {
1.77 kristaps 226: size_t sz;
1.85 kristaps 227: int len, nospace;
1.82 kristaps 228: const char *seq;
229: enum roffdeco deco;
1.14 kristaps 230:
1.85 kristaps 231: nospace = 0;
232:
1.32 kristaps 233: for (; *p; p++) {
1.77 kristaps 234: sz = strcspn(p, "\\<>&");
235:
236: fwrite(p, 1, sz, stdout);
1.80 kristaps 237: p += /* LINTED */
238: sz;
1.77 kristaps 239:
1.82 kristaps 240: if ('<' == *p) {
241: printf("<");
242: continue;
243: } else if ('>' == *p) {
244: printf(">");
245: continue;
246: } else if ('&' == *p) {
247: printf("&");
1.34 kristaps 248: continue;
1.77 kristaps 249: } else if ('\0' == *p)
250: break;
251:
1.82 kristaps 252: seq = ++p;
253: len = a2roffdeco(&deco, &seq, &sz);
254:
255: switch (deco) {
256: case (DECO_RESERVED):
257: print_res(h, seq, sz);
258: break;
259: case (DECO_SPECIAL):
260: print_spec(h, seq, sz);
261: break;
262: default:
263: break;
264: }
265:
266: p += len - 1;
1.84 kristaps 267:
268: if (DECO_NOSPACE == deco && '\0' == *(p + 1))
1.85 kristaps 269: nospace = 1;
1.32 kristaps 270: }
1.85 kristaps 271:
272: return(nospace);
1.14 kristaps 273: }
274:
275:
1.51 kristaps 276: struct tag *
1.29 kristaps 277: print_otag(struct html *h, enum htmltag tag,
278: int sz, const struct htmlpair *p)
1.14 kristaps 279: {
1.29 kristaps 280: int i;
1.30 kristaps 281: struct tag *t;
282:
283: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
1.72 kristaps 284: t = malloc(sizeof(struct tag));
285: if (NULL == t) {
1.75 kristaps 286: perror(NULL);
1.72 kristaps 287: exit(EXIT_FAILURE);
288: }
1.30 kristaps 289: t->tag = tag;
1.66 kristaps 290: t->next = h->tags.head;
291: h->tags.head = t;
1.30 kristaps 292: } else
293: t = NULL;
1.29 kristaps 294:
295: if ( ! (HTML_NOSPACE & h->flags))
1.30 kristaps 296: if ( ! (HTML_CLRLINE & htmltags[tag].flags))
1.78 kristaps 297: putchar(' ');
1.29 kristaps 298:
299: printf("<%s", htmltags[tag].name);
300: for (i = 0; i < sz; i++) {
301: printf(" %s=\"", htmlattrs[p[i].key]);
302: assert(p->val);
1.85 kristaps 303: (void)print_encode(h, p[i].val);
1.78 kristaps 304: putchar('\"');
1.29 kristaps 305: }
1.78 kristaps 306: putchar('>');
1.14 kristaps 307:
1.29 kristaps 308: h->flags |= HTML_NOSPACE;
1.30 kristaps 309: if (HTML_CLRLINE & htmltags[tag].flags)
310: h->flags |= HTML_NEWLINE;
311: else
312: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 313:
1.30 kristaps 314: return(t);
1.14 kristaps 315: }
316:
317:
318: /* ARGSUSED */
1.29 kristaps 319: static void
320: print_ctag(struct html *h, enum htmltag tag)
1.14 kristaps 321: {
322:
1.29 kristaps 323: printf("</%s>", htmltags[tag].name);
1.71 kristaps 324: if (HTML_CLRLINE & htmltags[tag].flags) {
1.29 kristaps 325: h->flags |= HTML_NOSPACE;
1.30 kristaps 326: h->flags |= HTML_NEWLINE;
1.78 kristaps 327: putchar('\n');
1.71 kristaps 328: } else
1.30 kristaps 329: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 330: }
331:
332:
1.29 kristaps 333: /* ARGSUSED */
1.51 kristaps 334: void
1.29 kristaps 335: print_gen_doctype(struct html *h)
1.1 kristaps 336: {
1.29 kristaps 337:
1.46 kristaps 338: printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
1.1 kristaps 339: }
340:
341:
1.51 kristaps 342: void
1.29 kristaps 343: print_text(struct html *h, const char *p)
1.1 kristaps 344: {
345:
1.29 kristaps 346: if (*p && 0 == *(p + 1))
347: switch (*p) {
348: case('.'):
349: /* FALLTHROUGH */
350: case(','):
351: /* FALLTHROUGH */
352: case(';'):
353: /* FALLTHROUGH */
354: case(':'):
355: /* FALLTHROUGH */
356: case('?'):
357: /* FALLTHROUGH */
358: case('!'):
359: /* FALLTHROUGH */
360: case(')'):
361: /* FALLTHROUGH */
362: case(']'):
363: /* FALLTHROUGH */
364: case('}'):
1.52 kristaps 365: if ( ! (HTML_IGNDELIM & h->flags))
366: h->flags |= HTML_NOSPACE;
1.30 kristaps 367: break;
1.29 kristaps 368: default:
369: break;
370: }
1.1 kristaps 371:
1.29 kristaps 372: if ( ! (h->flags & HTML_NOSPACE))
1.78 kristaps 373: putchar(' ');
1.30 kristaps 374:
375: h->flags &= ~HTML_NEWLINE;
1.1 kristaps 376:
1.86 ! kristaps 377: assert(p);
! 378: if ( ! print_encode(h, p))
! 379: h->flags &= ~HTML_NOSPACE;
1.8 kristaps 380:
1.29 kristaps 381: if (*p && 0 == *(p + 1))
382: switch (*p) {
383: case('('):
384: /* FALLTHROUGH */
385: case('['):
386: /* FALLTHROUGH */
387: case('{'):
388: h->flags |= HTML_NOSPACE;
1.30 kristaps 389: break;
1.29 kristaps 390: default:
391: break;
392: }
1.1 kristaps 393: }
1.30 kristaps 394:
395:
1.51 kristaps 396: void
1.30 kristaps 397: print_tagq(struct html *h, const struct tag *until)
398: {
399: struct tag *tag;
400:
1.66 kristaps 401: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 402: print_ctag(h, tag->tag);
1.66 kristaps 403: h->tags.head = tag->next;
1.30 kristaps 404: free(tag);
405: if (until && tag == until)
406: return;
407: }
408: }
409:
410:
1.51 kristaps 411: void
1.30 kristaps 412: print_stagq(struct html *h, const struct tag *suntil)
413: {
414: struct tag *tag;
415:
1.66 kristaps 416: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 417: if (suntil && tag == suntil)
418: return;
419: print_ctag(h, tag->tag);
1.66 kristaps 420: h->tags.head = tag->next;
1.30 kristaps 421: free(tag);
422: }
423: }
1.55 kristaps 424:
425:
426: void
427: bufinit(struct html *h)
428: {
429:
430: h->buf[0] = '\0';
431: h->buflen = 0;
432: }
433:
434:
435: void
1.58 kristaps 436: bufcat_style(struct html *h, const char *key, const char *val)
437: {
438:
439: bufcat(h, key);
440: bufncat(h, ":", 1);
441: bufcat(h, val);
442: bufncat(h, ";", 1);
443: }
444:
445:
446: void
1.55 kristaps 447: bufcat(struct html *h, const char *p)
448: {
449:
450: bufncat(h, p, strlen(p));
451: }
452:
453:
454: void
455: buffmt(struct html *h, const char *fmt, ...)
456: {
457: va_list ap;
458:
459: va_start(ap, fmt);
1.56 kristaps 460: (void)vsnprintf(h->buf + (int)h->buflen,
1.55 kristaps 461: BUFSIZ - h->buflen - 1, fmt, ap);
462: va_end(ap);
463: h->buflen = strlen(h->buf);
464: }
465:
466:
467: void
468: bufncat(struct html *h, const char *p, size_t sz)
469: {
470:
471: if (h->buflen + sz > BUFSIZ - 1)
472: sz = BUFSIZ - 1 - h->buflen;
473:
474: (void)strncat(h->buf, p, sz);
475: h->buflen += sz;
476: }
477:
478:
479: void
480: buffmt_includes(struct html *h, const char *name)
481: {
482: const char *p, *pp;
483:
484: pp = h->base_includes;
1.61 kristaps 485:
486: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 487: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 488: switch (*(p + 1)) {
489: case('I'):
490: bufcat(h, name);
491: break;
492: default:
493: bufncat(h, p, 2);
494: break;
495: }
496: pp = p + 2;
497: }
498: if (pp)
499: bufcat(h, pp);
500: }
501:
502:
503: void
504: buffmt_man(struct html *h,
505: const char *name, const char *sec)
506: {
507: const char *p, *pp;
508:
509: pp = h->base_man;
1.61 kristaps 510:
511: /* LINTED */
512: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 513: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 514: switch (*(p + 1)) {
515: case('S'):
1.58 kristaps 516: bufcat(h, sec ? sec : "1");
1.55 kristaps 517: break;
518: case('N'):
1.58 kristaps 519: buffmt(h, name);
1.55 kristaps 520: break;
521: default:
522: bufncat(h, p, 2);
523: break;
524: }
525: pp = p + 2;
526: }
527: if (pp)
528: bufcat(h, pp);
529: }
1.58 kristaps 530:
531:
532: void
533: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
534: {
1.62 kristaps 535: double v;
1.63 kristaps 536: const char *u;
1.58 kristaps 537:
538: v = su->scale;
539:
540: switch (su->unit) {
541: case (SCALE_CM):
542: u = "cm";
543: break;
544: case (SCALE_IN):
545: u = "in";
546: break;
547: case (SCALE_PC):
548: u = "pc";
549: break;
550: case (SCALE_PT):
551: u = "pt";
552: break;
1.59 kristaps 553: case (SCALE_EM):
554: u = "em";
555: break;
1.58 kristaps 556: case (SCALE_MM):
557: if (0 == (v /= 100))
558: v = 1;
559: u = "em";
560: break;
1.59 kristaps 561: case (SCALE_EN):
562: u = "ex";
563: break;
564: case (SCALE_BU):
565: u = "ex";
566: break;
1.58 kristaps 567: case (SCALE_VS):
568: u = "em";
569: break;
570: default:
571: u = "ex";
572: break;
573: }
574:
1.62 kristaps 575: if (su->pt)
576: buffmt(h, "%s: %f%s;", p, v, u);
577: else
578: /* LINTED */
579: buffmt(h, "%s: %d%s;", p, (int)v, u);
1.58 kristaps 580: }
1.65 kristaps 581:
1.68 kristaps 582:
583: void
1.70 kristaps 584: html_idcat(char *dst, const char *src, int sz)
1.68 kristaps 585: {
1.70 kristaps 586: int ssz;
1.68 kristaps 587:
588: assert(sz);
589:
590: /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
591:
1.70 kristaps 592: for ( ; *dst != '\0' && sz; dst++, sz--)
1.68 kristaps 593: /* Jump to end. */ ;
594:
1.70 kristaps 595: assert(sz > 2);
1.68 kristaps 596:
1.70 kristaps 597: /* We can't start with a number (bah). */
1.68 kristaps 598:
1.70 kristaps 599: *dst++ = 'x';
1.68 kristaps 600: *dst = '\0';
1.70 kristaps 601: sz--;
602:
603: for ( ; *src != '\0' && sz > 1; src++) {
1.73 kristaps 604: ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
1.70 kristaps 605: sz -= ssz;
606: dst += ssz;
607: }
1.68 kristaps 608: }
CVSweb