Annotation of mandoc/html.c, Revision 1.87
1.87 ! kristaps 1: /* $Id: html.c,v 1.86 2009/11/14 12:00:24 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: return(t);
1.14 kristaps 310: }
311:
312:
313: /* ARGSUSED */
1.29 kristaps 314: static void
315: print_ctag(struct html *h, enum htmltag tag)
1.14 kristaps 316: {
317:
1.29 kristaps 318: printf("</%s>", htmltags[tag].name);
1.71 kristaps 319: if (HTML_CLRLINE & htmltags[tag].flags) {
1.29 kristaps 320: h->flags |= HTML_NOSPACE;
1.78 kristaps 321: putchar('\n');
1.87 ! kristaps 322: }
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.86 kristaps 368: assert(p);
369: if ( ! print_encode(h, p))
370: h->flags &= ~HTML_NOSPACE;
1.8 kristaps 371:
1.29 kristaps 372: if (*p && 0 == *(p + 1))
373: switch (*p) {
374: case('('):
375: /* FALLTHROUGH */
376: case('['):
377: /* FALLTHROUGH */
378: case('{'):
379: h->flags |= HTML_NOSPACE;
1.30 kristaps 380: break;
1.29 kristaps 381: default:
382: break;
383: }
1.1 kristaps 384: }
1.30 kristaps 385:
386:
1.51 kristaps 387: void
1.30 kristaps 388: print_tagq(struct html *h, const struct tag *until)
389: {
390: struct tag *tag;
391:
1.66 kristaps 392: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 393: print_ctag(h, tag->tag);
1.66 kristaps 394: h->tags.head = tag->next;
1.30 kristaps 395: free(tag);
396: if (until && tag == until)
397: return;
398: }
399: }
400:
401:
1.51 kristaps 402: void
1.30 kristaps 403: print_stagq(struct html *h, const struct tag *suntil)
404: {
405: struct tag *tag;
406:
1.66 kristaps 407: while ((tag = h->tags.head) != NULL) {
1.30 kristaps 408: if (suntil && tag == suntil)
409: return;
410: print_ctag(h, tag->tag);
1.66 kristaps 411: h->tags.head = tag->next;
1.30 kristaps 412: free(tag);
413: }
414: }
1.55 kristaps 415:
416:
417: void
418: bufinit(struct html *h)
419: {
420:
421: h->buf[0] = '\0';
422: h->buflen = 0;
423: }
424:
425:
426: void
1.58 kristaps 427: bufcat_style(struct html *h, const char *key, const char *val)
428: {
429:
430: bufcat(h, key);
431: bufncat(h, ":", 1);
432: bufcat(h, val);
433: bufncat(h, ";", 1);
434: }
435:
436:
437: void
1.55 kristaps 438: bufcat(struct html *h, const char *p)
439: {
440:
441: bufncat(h, p, strlen(p));
442: }
443:
444:
445: void
446: buffmt(struct html *h, const char *fmt, ...)
447: {
448: va_list ap;
449:
450: va_start(ap, fmt);
1.56 kristaps 451: (void)vsnprintf(h->buf + (int)h->buflen,
1.55 kristaps 452: BUFSIZ - h->buflen - 1, fmt, ap);
453: va_end(ap);
454: h->buflen = strlen(h->buf);
455: }
456:
457:
458: void
459: bufncat(struct html *h, const char *p, size_t sz)
460: {
461:
462: if (h->buflen + sz > BUFSIZ - 1)
463: sz = BUFSIZ - 1 - h->buflen;
464:
465: (void)strncat(h->buf, p, sz);
466: h->buflen += sz;
467: }
468:
469:
470: void
471: buffmt_includes(struct html *h, const char *name)
472: {
473: const char *p, *pp;
474:
475: pp = h->base_includes;
1.61 kristaps 476:
477: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 478: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 479: switch (*(p + 1)) {
480: case('I'):
481: bufcat(h, name);
482: break;
483: default:
484: bufncat(h, p, 2);
485: break;
486: }
487: pp = p + 2;
488: }
489: if (pp)
490: bufcat(h, pp);
491: }
492:
493:
494: void
495: buffmt_man(struct html *h,
496: const char *name, const char *sec)
497: {
498: const char *p, *pp;
499:
500: pp = h->base_man;
1.61 kristaps 501:
502: /* LINTED */
503: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 504: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 505: switch (*(p + 1)) {
506: case('S'):
1.58 kristaps 507: bufcat(h, sec ? sec : "1");
1.55 kristaps 508: break;
509: case('N'):
1.58 kristaps 510: buffmt(h, name);
1.55 kristaps 511: break;
512: default:
513: bufncat(h, p, 2);
514: break;
515: }
516: pp = p + 2;
517: }
518: if (pp)
519: bufcat(h, pp);
520: }
1.58 kristaps 521:
522:
523: void
524: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
525: {
1.62 kristaps 526: double v;
1.63 kristaps 527: const char *u;
1.58 kristaps 528:
529: v = su->scale;
530:
531: switch (su->unit) {
532: case (SCALE_CM):
533: u = "cm";
534: break;
535: case (SCALE_IN):
536: u = "in";
537: break;
538: case (SCALE_PC):
539: u = "pc";
540: break;
541: case (SCALE_PT):
542: u = "pt";
543: break;
1.59 kristaps 544: case (SCALE_EM):
545: u = "em";
546: break;
1.58 kristaps 547: case (SCALE_MM):
548: if (0 == (v /= 100))
549: v = 1;
550: u = "em";
551: break;
1.59 kristaps 552: case (SCALE_EN):
553: u = "ex";
554: break;
555: case (SCALE_BU):
556: u = "ex";
557: break;
1.58 kristaps 558: case (SCALE_VS):
559: u = "em";
560: break;
561: default:
562: u = "ex";
563: break;
564: }
565:
1.62 kristaps 566: if (su->pt)
567: buffmt(h, "%s: %f%s;", p, v, u);
568: else
569: /* LINTED */
570: buffmt(h, "%s: %d%s;", p, (int)v, u);
1.58 kristaps 571: }
1.65 kristaps 572:
1.68 kristaps 573:
574: void
1.70 kristaps 575: html_idcat(char *dst, const char *src, int sz)
1.68 kristaps 576: {
1.70 kristaps 577: int ssz;
1.68 kristaps 578:
579: assert(sz);
580:
581: /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */
582:
1.70 kristaps 583: for ( ; *dst != '\0' && sz; dst++, sz--)
1.68 kristaps 584: /* Jump to end. */ ;
585:
1.70 kristaps 586: assert(sz > 2);
1.68 kristaps 587:
1.70 kristaps 588: /* We can't start with a number (bah). */
1.68 kristaps 589:
1.70 kristaps 590: *dst++ = 'x';
1.68 kristaps 591: *dst = '\0';
1.70 kristaps 592: sz--;
593:
594: for ( ; *src != '\0' && sz > 1; src++) {
1.73 kristaps 595: ssz = snprintf(dst, (size_t)sz, "%.2x", *src);
1.70 kristaps 596: sz -= ssz;
597: dst += ssz;
598: }
1.68 kristaps 599: }
CVSweb