Annotation of mandoc/html.c, Revision 1.61
1.61 ! kristaps 1: /* $Id: html.c,v 1.60 2009/10/07 15:06:03 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: #include <sys/queue.h>
19:
1.1 kristaps 20: #include <assert.h>
1.4 kristaps 21: #include <err.h>
1.29 kristaps 22: #include <stdio.h>
1.55 kristaps 23: #include <stdarg.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.2 kristaps 31:
1.29 kristaps 32: #define DOCTYPE "-//W3C//DTD HTML 4.01//EN"
33: #define DTD "http://www.w3.org/TR/html4/strict.dtd"
1.8 kristaps 34:
1.29 kristaps 35: struct htmldata {
36: char *name;
37: int flags;
1.30 kristaps 38: #define HTML_CLRLINE (1 << 0)
39: #define HTML_NOSTACK (1 << 1)
1.29 kristaps 40: };
1.7 kristaps 41:
1.29 kristaps 42: static const struct htmldata htmltags[TAG_MAX] = {
1.30 kristaps 43: {"html", HTML_CLRLINE}, /* TAG_HTML */
44: {"head", HTML_CLRLINE}, /* TAG_HEAD */
45: {"body", HTML_CLRLINE}, /* TAG_BODY */
46: {"meta", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_META */
1.33 kristaps 47: {"title", HTML_CLRLINE}, /* TAG_TITLE */
1.30 kristaps 48: {"div", HTML_CLRLINE}, /* TAG_DIV */
1.29 kristaps 49: {"h1", 0}, /* TAG_H1 */
50: {"h2", 0}, /* TAG_H2 */
1.30 kristaps 51: {"p", HTML_CLRLINE}, /* TAG_P */
1.29 kristaps 52: {"span", 0}, /* TAG_SPAN */
1.30 kristaps 53: {"link", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
54: {"br", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
55: {"a", 0}, /* TAG_A */
1.33 kristaps 56: {"table", HTML_CLRLINE}, /* TAG_TABLE */
57: {"col", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_COL */
58: {"tr", HTML_CLRLINE}, /* TAG_TR */
59: {"td", HTML_CLRLINE}, /* TAG_TD */
1.34 kristaps 60: {"li", HTML_CLRLINE}, /* TAG_LI */
61: {"ul", HTML_CLRLINE}, /* TAG_UL */
62: {"ol", HTML_CLRLINE}, /* TAG_OL */
1.41 kristaps 63: {"base", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
1.29 kristaps 64: };
1.10 kristaps 65:
1.29 kristaps 66: static const char *const htmlattrs[ATTR_MAX] = {
67: "http-equiv",
68: "content",
69: "name",
70: "rel",
71: "href",
72: "type",
73: "media",
1.33 kristaps 74: "class",
75: "style",
76: "width",
77: "valign",
1.54 kristaps 78: "target",
1.57 kristaps 79: "id",
1.29 kristaps 80: };
1.10 kristaps 81:
1.33 kristaps 82: #ifdef __linux__
1.43 kristaps 83: extern int getsubopt(char **, char * const *, char **);
1.33 kristaps 84: #endif
1.29 kristaps 85:
86: void *
1.43 kristaps 87: html_alloc(char *outopts)
1.10 kristaps 88: {
1.30 kristaps 89: struct html *h;
1.53 kristaps 90: char *toks[4], *v;
1.43 kristaps 91:
92: toks[0] = "style";
1.53 kristaps 93: toks[1] = "man";
1.54 kristaps 94: toks[2] = "includes";
95: toks[3] = NULL;
1.30 kristaps 96:
97: if (NULL == (h = calloc(1, sizeof(struct html))))
98: return(NULL);
1.10 kristaps 99:
1.37 kristaps 100: SLIST_INIT(&h->tags);
101: SLIST_INIT(&h->ords);
102:
1.32 kristaps 103: if (NULL == (h->symtab = chars_init(CHARS_HTML))) {
104: free(h);
105: return(NULL);
106: }
1.41 kristaps 107:
1.47 kristaps 108: while (outopts && *outopts)
1.43 kristaps 109: switch (getsubopt(&outopts, toks, &v)) {
110: case (0):
111: h->style = v;
112: break;
113: case (1):
1.53 kristaps 114: h->base_man = v;
1.43 kristaps 115: break;
1.54 kristaps 116: case (2):
117: h->base_includes = v;
118: break;
1.43 kristaps 119: default:
120: break;
121: }
122:
1.30 kristaps 123: return(h);
1.29 kristaps 124: }
1.10 kristaps 125:
1.33 kristaps 126:
1.29 kristaps 127: void
128: html_free(void *p)
129: {
1.30 kristaps 130: struct tag *tag;
1.37 kristaps 131: struct ord *ord;
1.30 kristaps 132: struct html *h;
133:
134: h = (struct html *)p;
1.10 kristaps 135:
1.37 kristaps 136: while ( ! SLIST_EMPTY(&h->ords)) {
137: ord = SLIST_FIRST(&h->ords);
138: SLIST_REMOVE_HEAD(&h->ords, entry);
139: free(ord);
140: }
141:
142: while ( ! SLIST_EMPTY(&h->tags)) {
143: tag = SLIST_FIRST(&h->tags);
144: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 145: free(tag);
146: }
1.36 kristaps 147:
148: if (h->symtab)
149: chars_free(h->symtab);
1.53 kristaps 150:
1.30 kristaps 151: free(h);
1.10 kristaps 152: }
1.2 kristaps 153:
1.33 kristaps 154:
1.51 kristaps 155: void
1.29 kristaps 156: print_gen_head(struct html *h)
157: {
1.41 kristaps 158: struct htmlpair tag[4];
159:
160: tag[0].key = ATTR_HTTPEQUIV;
161: tag[0].val = "Content-Type";
162: tag[1].key = ATTR_CONTENT;
163: tag[1].val = "text/html; charset=utf-8";
164: print_otag(h, TAG_META, 2, tag);
165:
166: tag[0].key = ATTR_NAME;
167: tag[0].val = "resource-type";
168: tag[1].key = ATTR_CONTENT;
169: tag[1].val = "document";
170: print_otag(h, TAG_META, 2, tag);
171:
172: if (h->style) {
173: tag[0].key = ATTR_REL;
174: tag[0].val = "stylesheet";
175: tag[1].key = ATTR_HREF;
176: tag[1].val = h->style;
177: tag[2].key = ATTR_TYPE;
178: tag[2].val = "text/css";
179: tag[3].key = ATTR_MEDIA;
180: tag[3].val = "all";
181: print_otag(h, TAG_LINK, 4, tag);
182: }
1.4 kristaps 183: }
184:
1.33 kristaps 185:
1.29 kristaps 186: static void
1.32 kristaps 187: print_spec(struct html *h, const char *p, int len)
188: {
189: const char *rhs;
190: int i;
191: size_t sz;
192:
193: rhs = chars_a2ascii(h->symtab, p, (size_t)len, &sz);
194:
195: if (NULL == rhs)
196: return;
197: for (i = 0; i < (int)sz; i++)
198: putchar(rhs[i]);
199: }
200:
1.33 kristaps 201:
1.32 kristaps 202: static void
203: print_res(struct html *h, const char *p, int len)
204: {
205: const char *rhs;
206: int i;
207: size_t sz;
208:
209: rhs = chars_a2res(h->symtab, p, (size_t)len, &sz);
210:
211: if (NULL == rhs)
212: return;
213: for (i = 0; i < (int)sz; i++)
214: putchar(rhs[i]);
215: }
216:
1.33 kristaps 217:
1.32 kristaps 218: static void
219: print_escape(struct html *h, const char **p)
220: {
221: int j, type;
222: const char *wp;
223:
224: wp = *p;
225: type = 1;
226:
227: if (0 == *(++wp)) {
228: *p = wp;
229: return;
230: }
231:
232: if ('(' == *wp) {
233: wp++;
234: if (0 == *wp || 0 == *(wp + 1)) {
235: *p = 0 == *wp ? wp : wp + 1;
236: return;
237: }
238:
239: print_spec(h, wp, 2);
240: *p = ++wp;
241: return;
242:
243: } else if ('*' == *wp) {
244: if (0 == *(++wp)) {
245: *p = wp;
246: return;
247: }
248:
249: switch (*wp) {
250: case ('('):
251: wp++;
252: if (0 == *wp || 0 == *(wp + 1)) {
253: *p = 0 == *wp ? wp : wp + 1;
254: return;
255: }
256:
257: print_res(h, wp, 2);
258: *p = ++wp;
259: return;
260: case ('['):
261: type = 0;
262: break;
263: default:
264: print_res(h, wp, 1);
265: *p = wp;
266: return;
267: }
268:
269: } else if ('f' == *wp) {
270: if (0 == *(++wp)) {
271: *p = wp;
272: return;
273: }
274:
275: switch (*wp) {
276: case ('B'):
277: /* TODO */
278: break;
279: case ('I'):
280: /* TODO */
281: break;
282: case ('P'):
283: /* FALLTHROUGH */
284: case ('R'):
285: /* TODO */
286: break;
287: default:
288: break;
289: }
290:
291: *p = wp;
292: return;
293:
294: } else if ('[' != *wp) {
295: print_spec(h, wp, 1);
296: *p = wp;
297: return;
298: }
299:
300: wp++;
301: for (j = 0; *wp && ']' != *wp; wp++, j++)
302: /* Loop... */ ;
303:
304: if (0 == *wp) {
305: *p = wp;
306: return;
307: }
308:
309: if (type)
310: print_spec(h, wp - j, j);
311: else
312: print_res(h, wp - j, j);
313:
314: *p = wp;
315: }
316:
1.9 kristaps 317:
1.29 kristaps 318: static void
1.32 kristaps 319: print_encode(struct html *h, const char *p)
1.29 kristaps 320: {
1.14 kristaps 321:
1.32 kristaps 322: for (; *p; p++) {
1.34 kristaps 323: if ('\\' == *p) {
324: print_escape(h, &p);
325: continue;
326: }
327: switch (*p) {
328: case ('<'):
329: printf("<");
330: break;
331: case ('>'):
332: printf(">");
333: break;
334: case ('&'):
335: printf("&");
336: break;
337: default:
1.32 kristaps 338: putchar(*p);
1.34 kristaps 339: break;
1.32 kristaps 340: }
341: }
1.14 kristaps 342: }
343:
344:
1.51 kristaps 345: struct tag *
1.29 kristaps 346: print_otag(struct html *h, enum htmltag tag,
347: int sz, const struct htmlpair *p)
1.14 kristaps 348: {
1.29 kristaps 349: int i;
1.30 kristaps 350: struct tag *t;
351:
352: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
353: if (NULL == (t = malloc(sizeof(struct tag))))
354: err(EXIT_FAILURE, "malloc");
355: t->tag = tag;
1.37 kristaps 356: SLIST_INSERT_HEAD(&h->tags, t, entry);
1.30 kristaps 357: } else
358: t = NULL;
1.29 kristaps 359:
360: if ( ! (HTML_NOSPACE & h->flags))
1.30 kristaps 361: if ( ! (HTML_CLRLINE & htmltags[tag].flags))
1.29 kristaps 362: printf(" ");
363:
364: printf("<%s", htmltags[tag].name);
365: for (i = 0; i < sz; i++) {
366: printf(" %s=\"", htmlattrs[p[i].key]);
367: assert(p->val);
1.32 kristaps 368: print_encode(h, p[i].val);
1.29 kristaps 369: printf("\"");
370: }
371: printf(">");
1.14 kristaps 372:
1.29 kristaps 373: h->flags |= HTML_NOSPACE;
1.30 kristaps 374: if (HTML_CLRLINE & htmltags[tag].flags)
375: h->flags |= HTML_NEWLINE;
376: else
377: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 378:
1.30 kristaps 379: return(t);
1.14 kristaps 380: }
381:
382:
383: /* ARGSUSED */
1.29 kristaps 384: static void
385: print_ctag(struct html *h, enum htmltag tag)
1.14 kristaps 386: {
387:
1.29 kristaps 388: printf("</%s>", htmltags[tag].name);
1.30 kristaps 389: if (HTML_CLRLINE & htmltags[tag].flags)
1.29 kristaps 390: h->flags |= HTML_NOSPACE;
1.30 kristaps 391: if (HTML_CLRLINE & htmltags[tag].flags)
392: h->flags |= HTML_NEWLINE;
393: else
394: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 395: }
396:
397:
1.29 kristaps 398: /* ARGSUSED */
1.51 kristaps 399: void
1.29 kristaps 400: print_gen_doctype(struct html *h)
1.1 kristaps 401: {
1.29 kristaps 402:
1.46 kristaps 403: printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
1.1 kristaps 404: }
405:
406:
1.51 kristaps 407: void
1.29 kristaps 408: print_text(struct html *h, const char *p)
1.1 kristaps 409: {
410:
1.29 kristaps 411: if (*p && 0 == *(p + 1))
412: switch (*p) {
413: case('.'):
414: /* FALLTHROUGH */
415: case(','):
416: /* FALLTHROUGH */
417: case(';'):
418: /* FALLTHROUGH */
419: case(':'):
420: /* FALLTHROUGH */
421: case('?'):
422: /* FALLTHROUGH */
423: case('!'):
424: /* FALLTHROUGH */
425: case(')'):
426: /* FALLTHROUGH */
427: case(']'):
428: /* FALLTHROUGH */
429: case('}'):
1.52 kristaps 430: if ( ! (HTML_IGNDELIM & h->flags))
431: h->flags |= HTML_NOSPACE;
1.30 kristaps 432: break;
1.29 kristaps 433: default:
434: break;
435: }
1.1 kristaps 436:
1.29 kristaps 437: if ( ! (h->flags & HTML_NOSPACE))
438: printf(" ");
1.30 kristaps 439:
1.29 kristaps 440: h->flags &= ~HTML_NOSPACE;
1.30 kristaps 441: h->flags &= ~HTML_NEWLINE;
1.1 kristaps 442:
1.29 kristaps 443: if (p)
1.32 kristaps 444: print_encode(h, p);
1.8 kristaps 445:
1.29 kristaps 446: if (*p && 0 == *(p + 1))
447: switch (*p) {
448: case('('):
449: /* FALLTHROUGH */
450: case('['):
451: /* FALLTHROUGH */
452: case('{'):
453: h->flags |= HTML_NOSPACE;
1.30 kristaps 454: break;
1.29 kristaps 455: default:
456: break;
457: }
1.1 kristaps 458: }
1.30 kristaps 459:
460:
1.51 kristaps 461: void
1.30 kristaps 462: print_tagq(struct html *h, const struct tag *until)
463: {
464: struct tag *tag;
465:
1.37 kristaps 466: while ( ! SLIST_EMPTY(&h->tags)) {
467: tag = SLIST_FIRST(&h->tags);
1.30 kristaps 468: print_ctag(h, tag->tag);
1.37 kristaps 469: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 470: free(tag);
471: if (until && tag == until)
472: return;
473: }
474: }
475:
476:
1.51 kristaps 477: void
1.30 kristaps 478: print_stagq(struct html *h, const struct tag *suntil)
479: {
480: struct tag *tag;
481:
1.37 kristaps 482: while ( ! SLIST_EMPTY(&h->tags)) {
483: tag = SLIST_FIRST(&h->tags);
1.30 kristaps 484: if (suntil && tag == suntil)
485: return;
486: print_ctag(h, tag->tag);
1.37 kristaps 487: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 488: free(tag);
489: }
490: }
1.55 kristaps 491:
492:
493: void
494: bufinit(struct html *h)
495: {
496:
497: h->buf[0] = '\0';
498: h->buflen = 0;
499: }
500:
501:
502: void
1.58 kristaps 503: bufcat_style(struct html *h, const char *key, const char *val)
504: {
505:
506: bufcat(h, key);
507: bufncat(h, ":", 1);
508: bufcat(h, val);
509: bufncat(h, ";", 1);
510: }
511:
512:
513: void
1.55 kristaps 514: bufcat(struct html *h, const char *p)
515: {
516:
517: bufncat(h, p, strlen(p));
518: }
519:
520:
521: void
522: buffmt(struct html *h, const char *fmt, ...)
523: {
524: va_list ap;
525:
526: va_start(ap, fmt);
1.56 kristaps 527: (void)vsnprintf(h->buf + (int)h->buflen,
1.55 kristaps 528: BUFSIZ - h->buflen - 1, fmt, ap);
529: va_end(ap);
530: h->buflen = strlen(h->buf);
531: }
532:
533:
534: void
535: bufncat(struct html *h, const char *p, size_t sz)
536: {
537:
538: if (h->buflen + sz > BUFSIZ - 1)
539: sz = BUFSIZ - 1 - h->buflen;
540:
541: (void)strncat(h->buf, p, sz);
542: h->buflen += sz;
543: }
544:
545:
546: void
547: buffmt_includes(struct html *h, const char *name)
548: {
549: const char *p, *pp;
550:
551: pp = h->base_includes;
1.61 ! kristaps 552:
! 553: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 554: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 555: switch (*(p + 1)) {
556: case('I'):
557: bufcat(h, name);
558: break;
559: default:
560: bufncat(h, p, 2);
561: break;
562: }
563: pp = p + 2;
564: }
565: if (pp)
566: bufcat(h, pp);
567: }
568:
569:
570: void
571: buffmt_man(struct html *h,
572: const char *name, const char *sec)
573: {
574: const char *p, *pp;
575:
576: pp = h->base_man;
1.61 ! kristaps 577:
! 578: /* LINTED */
! 579: while (NULL != (p = strchr(pp, '%'))) {
1.56 kristaps 580: bufncat(h, pp, (size_t)(p - pp));
1.55 kristaps 581: switch (*(p + 1)) {
582: case('S'):
1.58 kristaps 583: bufcat(h, sec ? sec : "1");
1.55 kristaps 584: break;
585: case('N'):
1.58 kristaps 586: buffmt(h, name);
1.55 kristaps 587: break;
588: default:
589: bufncat(h, p, 2);
590: break;
591: }
592: pp = p + 2;
593: }
594: if (pp)
595: bufcat(h, pp);
596: }
1.58 kristaps 597:
598:
599: void
600: bufcat_su(struct html *h, const char *p, const struct roffsu *su)
601: {
602: int v;
603: char *u;
604:
605: v = su->scale;
606:
607: switch (su->unit) {
608: case (SCALE_CM):
609: u = "cm";
610: break;
611: case (SCALE_IN):
612: u = "in";
613: break;
614: case (SCALE_PC):
615: u = "pc";
616: break;
617: case (SCALE_PT):
618: u = "pt";
619: break;
1.59 kristaps 620: case (SCALE_EM):
621: u = "em";
622: break;
1.58 kristaps 623: case (SCALE_MM):
624: if (0 == (v /= 100))
625: v = 1;
626: u = "em";
627: break;
1.59 kristaps 628: case (SCALE_EN):
629: u = "ex";
630: break;
631: case (SCALE_BU):
632: u = "ex";
633: break;
1.58 kristaps 634: case (SCALE_VS):
635: u = "em";
636: break;
637: default:
638: u = "ex";
639: break;
640: }
641:
642: buffmt(h, "%s: %d%s;", p, v, u);
643: }
CVSweb