Annotation of mandoc/html.c, Revision 1.82
1.82 ! kristaps 1: /* $Id: html.c,v 1.81 2009/11/05 10:16:01 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:
! 91: static void print_spec(struct html *, const char *, int);
! 92: static void print_res(struct html *, const char *, int);
! 93: static void print_ctag(struct html *, enum htmltag);
! 94: static void print_encode(struct html *, const char *);
! 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.32 kristaps 196: print_spec(struct html *h, const char *p, int len)
197: {
198: const char *rhs;
199: size_t sz;
200:
201: rhs = chars_a2ascii(h->symtab, p, (size_t)len, &sz);
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
210: print_res(struct html *h, const char *p, int len)
211: {
212: const char *rhs;
213: size_t sz;
214:
215: rhs = chars_a2res(h->symtab, p, (size_t)len, &sz);
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.32 kristaps 223: static void
224: print_encode(struct html *h, const char *p)
1.29 kristaps 225: {
1.77 kristaps 226: size_t sz;
1.82 ! kristaps 227: int len;
! 228: const char *seq;
! 229: enum roffdeco deco;
1.14 kristaps 230:
1.32 kristaps 231: for (; *p; p++) {
1.77 kristaps 232: sz = strcspn(p, "\\<>&");
233:
234: fwrite(p, 1, sz, stdout);
1.80 kristaps 235: p += /* LINTED */
236: sz;
1.77 kristaps 237:
1.82 ! kristaps 238: if ('<' == *p) {
! 239: printf("<");
! 240: continue;
! 241: } else if ('>' == *p) {
! 242: printf(">");
! 243: continue;
! 244: } else if ('&' == *p) {
! 245: printf("&");
1.34 kristaps 246: continue;
1.77 kristaps 247: } else if ('\0' == *p)
248: break;
249:
1.82 ! kristaps 250: seq = ++p;
! 251: len = a2roffdeco(&deco, &seq, &sz);
! 252:
! 253: switch (deco) {
! 254: case (DECO_RESERVED):
! 255: print_res(h, seq, sz);
! 256: break;
! 257: case (DECO_SPECIAL):
! 258: print_spec(h, seq, sz);
! 259: break;
! 260: default:
! 261: break;
! 262: }
! 263:
! 264: p += len - 1;
1.32 kristaps 265: }
1.14 kristaps 266: }
267:
268:
1.51 kristaps 269: struct tag *
1.29 kristaps 270: print_otag(struct html *h, enum htmltag tag,
271: int sz, const struct htmlpair *p)
1.14 kristaps 272: {
1.29 kristaps 273: int i;
1.30 kristaps 274: struct tag *t;
275:
276: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
1.72 kristaps 277: t = malloc(sizeof(struct tag));
278: if (NULL == t) {
1.75 kristaps 279: perror(NULL);
1.72 kristaps 280: exit(EXIT_FAILURE);
281: }
1.30 kristaps 282: t->tag = tag;
1.66 kristaps 283: t->next = h->tags.head;
284: h->tags.head = t;
1.30 kristaps 285: } else
286: t = NULL;
1.29 kristaps 287:
288: if ( ! (HTML_NOSPACE & h->flags))
1.30 kristaps 289: if ( ! (HTML_CLRLINE & htmltags[tag].flags))
1.78 kristaps 290: putchar(' ');
1.29 kristaps 291:
292: printf("<%s", htmltags[tag].name);
293: for (i = 0; i < sz; i++) {
294: printf(" %s=\"", htmlattrs[p[i].key]);
295: assert(p->val);
1.32 kristaps 296: print_encode(h, p[i].val);
1.78 kristaps 297: putchar('\"');
1.29 kristaps 298: }
1.78 kristaps 299: putchar('>');
1.14 kristaps 300:
1.29 kristaps 301: h->flags |= HTML_NOSPACE;
1.30 kristaps 302: if (HTML_CLRLINE & htmltags[tag].flags)
303: h->flags |= HTML_NEWLINE;
304: else
305: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 306:
1.30 kristaps 307: return(t);
1.14 kristaps 308: }
309:
310:
311: /* ARGSUSED */
1.29 kristaps 312: static void
313: print_ctag(struct html *h, enum htmltag tag)
1.14 kristaps 314: {
315:
1.29 kristaps 316: printf("</%s>", htmltags[tag].name);
1.71 kristaps 317: if (HTML_CLRLINE & htmltags[tag].flags) {
1.29 kristaps 318: h->flags |= HTML_NOSPACE;
1.30 kristaps 319: h->flags |= HTML_NEWLINE;
1.78 kristaps 320: putchar('\n');
1.71 kristaps 321: } else
1.30 kristaps 322: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 323: }
324:
325:
1.29 kristaps 326: /* ARGSUSED */
1.51 kristaps 327: void
1.29 kristaps 328: print_gen_doctype(struct html *h)
1.1 kristaps 329: {
1.29 kristaps 330:
1.46 kristaps 331: printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
1.1 kristaps 332: }
333:
334:
1.51 kristaps 335: void
1.29 kristaps 336: print_text(struct html *h, const char *p)
1.1 kristaps 337: {
338:
1.29 kristaps 339: if (*p && 0 == *(p + 1))
340: switch (*p) {
341: case('.'):
342: /* FALLTHROUGH */
343: case(','):
344: /* FALLTHROUGH */
345: case(';'):
346: /* FALLTHROUGH */
347: case(':'):
348: /* FALLTHROUGH */
349: case('?'):
350: /* FALLTHROUGH */
351: case('!'):
352: /* FALLTHROUGH */
353: case(')'):
354: /* FALLTHROUGH */
355: case(']'):
356: /* FALLTHROUGH */
357: case('}'):
1.52 kristaps 358: if ( ! (HTML_IGNDELIM & h->flags))
359: h->flags |= HTML_NOSPACE;
1.30 kristaps 360: break;
1.29 kristaps 361: default:
362: break;
363: }
1.1 kristaps 364:
1.29 kristaps 365: if ( ! (h->flags & HTML_NOSPACE))
1.78 kristaps 366: putchar(' ');
1.30 kristaps 367:
1.29 kristaps 368: h->flags &= ~HTML_NOSPACE;
1.30 kristaps 369: h->flags &= ~HTML_NEWLINE;
1.1 kristaps 370:
1.29 kristaps 371: if (p)
1.32 kristaps 372: print_encode(h, p);
1.8 kristaps 373:
1.29 kristaps 374: if (*p && 0 == *(p + 1))
375: switch (*p) {
376: case('('):
377: /* FALLTHROUGH */
378: case('['):
379: /* FALLTHROUGH */
380: case('{'):
381: h->flags |= HTML_NOSPACE;
1.30 kristaps 382: break;
1.29 kristaps 383: default:
384: break;
385: }
1.1 kristaps 386: }
1.30 kristaps 387:
388:
1.51 kristaps 389: void
1.30 kristaps 390: print_tagq(struct html *h, const struct tag *until)
391: {
392: struct tag *tag;
393:
1.66 kristaps 394: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 395: print_ctag(h, tag->tag);
1.66 kristaps 396: h->tags.head = tag->next;
1.30 kristaps 397: free(tag);
398: if (until && tag == until)
399: return;
400: }
401: }
402:
403:
1.51 kristaps 404: void
1.30 kristaps 405: print_stagq(struct html *h, const struct tag *suntil)
406: {
407: struct tag *tag;
408:
1.66 kristaps 409: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 410: if (suntil && tag == suntil)
411: return;
412: print_ctag(h, tag->tag);
1.66 kristaps 413: h->tags.head = tag->next;
1.30 kristaps 414: free(tag);
415: }
416: }
1.55 kristaps 417:
418:
419: void
420: bufinit(struct html *h)
421: {
422:
423: h->buf[0] = '\0';
424: h->buflen = 0;
425: }
426:
427:
428: void
1.58 kristaps 429: bufcat_style(struct html *h, const char *key, const char *val)
430: {
431:
432: bufcat(h, key);
433: bufncat(h, ":", 1);
434: bufcat(h, val);
435: bufncat(h, ";", 1);
436: }
437:
438:
439: void
1.55 kristaps 440: bufcat(struct html *h, const char *p)
441: {
442:
443: bufncat(h, p, strlen(p));
444: }
445:
446:
447: void
448: buffmt(struct html *h, const char *fmt, ...)
449: {
450: va_list ap;
451:
452: va_start(ap, fmt);
1.56 kristaps 453: (void)vsnprintf(h->buf + (int)h->buflen,
1.55 kristaps 454: BUFSIZ - h->buflen - 1, fmt, ap);
455: va_end(ap);
456: h->buflen = strlen(h->buf);
457: }
458:
459:
460: void
461: bufncat(struct html *h, const char *p, size_t sz)
462: {
463:
464: if (h->buflen + sz > BUFSIZ - 1)
465: sz = BUFSIZ - 1 - h->buflen;
466:
467: (void)strncat(h->buf, p, sz);
468: h->buflen += sz;
469: }
470:
471:
472: void
473: buffmt_includes(struct html *h, const char *name)
474: {
475: const char *p, *pp;
476:
477: pp = h->base_includes;
1.61 kristaps 478:
479: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 480: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 481: switch (*(p + 1)) {
482: case('I'):
483: bufcat(h, name);
484: break;
485: default:
486: bufncat(h, p, 2);
487: break;
488: }
489: pp = p + 2;
490: }
491: if (pp)
492: bufcat(h, pp);
493: }
494:
495:
496: void
497: buffmt_man(struct html *h,
498: const char *name, const char *sec)
499: {
500: const char *p, *pp;
501:
502: pp = h->base_man;
1.61 kristaps 503:
504: /* LINTED */
505: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 506: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 507: switch (*(p + 1)) {
508: case('S'):
1.58 kristaps 509: bufcat(h, sec ? sec : "1");
1.55 kristaps 510: break;
511: case('N'):
1.58 kristaps 512: buffmt(h, name);
1.55 kristaps 513: break;
514: default:
515: bufncat(h, p, 2);
516: break;
517: }
518: pp = p + 2;
519: }
520: if (pp)
521: bufcat(h, pp);
522: }
1.58 kristaps 523:
524:
525: void
526: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
527: {
1.62 kristaps 528: double v;
1.63 kristaps 529: const char *u;
1.58 kristaps 530:
531: v = su->scale;
532:
533: switch (su->unit) {
534: case (SCALE_CM):
535: u = "cm";
536: break;
537: case (SCALE_IN):
538: u = "in";
539: break;
540: case (SCALE_PC):
541: u = "pc";
542: break;
543: case (SCALE_PT):
544: u = "pt";
545: break;
1.59 kristaps 546: case (SCALE_EM):
547: u = "em";
548: break;
1.58 kristaps 549: case (SCALE_MM):
550: if (0 == (v /= 100))
551: v = 1;
552: u = "em";
553: break;
1.59 kristaps 554: case (SCALE_EN):
555: u = "ex";
556: break;
557: case (SCALE_BU):
558: u = "ex";
559: break;
1.58 kristaps 560: case (SCALE_VS):
561: u = "em";
562: break;
563: default:
564: u = "ex";
565: break;
566: }
567:
1.62 kristaps 568: if (su->pt)
569: buffmt(h, "%s: %f%s;", p, v, u);
570: else
571: /* LINTED */
572: buffmt(h, "%s: %d%s;", p, (int)v, u);
1.58 kristaps 573: }
1.65 kristaps 574:
1.68 kristaps 575:
576: void
1.70 kristaps 577: html_idcat(char *dst, const char *src, int sz)
1.68 kristaps 578: {
1.70 kristaps 579: int ssz;
1.68 kristaps 580:
581: assert(sz);
582:
583: /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
584:
1.70 kristaps 585: for ( ; *dst != '\0' && sz; dst++, sz--)
1.68 kristaps 586: /* Jump to end. */ ;
587:
1.70 kristaps 588: assert(sz > 2);
1.68 kristaps 589:
1.70 kristaps 590: /* We can't start with a number (bah). */
1.68 kristaps 591:
1.70 kristaps 592: *dst++ = 'x';
1.68 kristaps 593: *dst = '\0';
1.70 kristaps 594: sz--;
595:
596: for ( ; *src != '\0' && sz > 1; src++) {
1.73 kristaps 597: ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
1.70 kristaps 598: sz -= ssz;
599: dst += ssz;
600: }
1.68 kristaps 601: }
CVSweb