Annotation of mandoc/html.c, Revision 1.52
1.52 ! kristaps 1: /* $Id: html.c,v 1.51 2009/09/21 14:56:56 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.1 kristaps 23: #include <stdlib.h>
1.33 kristaps 24: #include <string.h>
1.45 kristaps 25: #include <unistd.h>
1.1 kristaps 26:
1.32 kristaps 27: #include "chars.h"
1.51 kristaps 28: #include "html.h"
1.2 kristaps 29:
1.29 kristaps 30: #define DOCTYPE "-//W3C//DTD HTML 4.01//EN"
31: #define DTD "http://www.w3.org/TR/html4/strict.dtd"
1.8 kristaps 32:
1.29 kristaps 33: struct htmldata {
34: char *name;
35: int flags;
1.30 kristaps 36: #define HTML_CLRLINE (1 << 0)
37: #define HTML_NOSTACK (1 << 1)
1.29 kristaps 38: };
1.7 kristaps 39:
1.29 kristaps 40: static const struct htmldata htmltags[TAG_MAX] = {
1.30 kristaps 41: {"html", HTML_CLRLINE}, /* TAG_HTML */
42: {"head", HTML_CLRLINE}, /* TAG_HEAD */
43: {"body", HTML_CLRLINE}, /* TAG_BODY */
44: {"meta", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_META */
1.33 kristaps 45: {"title", HTML_CLRLINE}, /* TAG_TITLE */
1.30 kristaps 46: {"div", HTML_CLRLINE}, /* TAG_DIV */
1.29 kristaps 47: {"h1", 0}, /* TAG_H1 */
48: {"h2", 0}, /* TAG_H2 */
1.30 kristaps 49: {"p", HTML_CLRLINE}, /* TAG_P */
1.29 kristaps 50: {"span", 0}, /* TAG_SPAN */
1.30 kristaps 51: {"link", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
52: {"br", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
53: {"a", 0}, /* TAG_A */
1.33 kristaps 54: {"table", HTML_CLRLINE}, /* TAG_TABLE */
55: {"col", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_COL */
56: {"tr", HTML_CLRLINE}, /* TAG_TR */
57: {"td", HTML_CLRLINE}, /* TAG_TD */
1.34 kristaps 58: {"li", HTML_CLRLINE}, /* TAG_LI */
59: {"ul", HTML_CLRLINE}, /* TAG_UL */
60: {"ol", HTML_CLRLINE}, /* TAG_OL */
1.41 kristaps 61: {"base", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
1.29 kristaps 62: };
1.10 kristaps 63:
1.29 kristaps 64: static const char *const htmlattrs[ATTR_MAX] = {
65: "http-equiv",
66: "content",
67: "name",
68: "rel",
69: "href",
70: "type",
71: "media",
1.33 kristaps 72: "class",
73: "style",
74: "width",
75: "valign",
1.29 kristaps 76: };
1.10 kristaps 77:
1.33 kristaps 78: #ifdef __linux__
1.43 kristaps 79: extern int getsubopt(char **, char * const *, char **);
1.33 kristaps 80: #endif
1.29 kristaps 81:
82: void *
1.43 kristaps 83: html_alloc(char *outopts)
1.10 kristaps 84: {
1.30 kristaps 85: struct html *h;
1.44 kristaps 86: char *toks[3], *v;
1.43 kristaps 87:
88: toks[0] = "style";
89: toks[1] = "base";
90: toks[2] = NULL;
1.30 kristaps 91:
92: if (NULL == (h = calloc(1, sizeof(struct html))))
93: return(NULL);
1.10 kristaps 94:
1.37 kristaps 95: SLIST_INIT(&h->tags);
96: SLIST_INIT(&h->ords);
97:
1.32 kristaps 98: if (NULL == (h->symtab = chars_init(CHARS_HTML))) {
99: free(h);
100: return(NULL);
101: }
1.41 kristaps 102:
1.47 kristaps 103: while (outopts && *outopts)
1.43 kristaps 104: switch (getsubopt(&outopts, toks, &v)) {
105: case (0):
106: h->style = v;
107: break;
108: case (1):
109: h->base = v;
110: break;
111: default:
112: break;
113: }
114:
1.30 kristaps 115: return(h);
1.29 kristaps 116: }
1.10 kristaps 117:
1.33 kristaps 118:
1.29 kristaps 119: void
120: html_free(void *p)
121: {
1.30 kristaps 122: struct tag *tag;
1.37 kristaps 123: struct ord *ord;
1.30 kristaps 124: struct html *h;
125:
126: h = (struct html *)p;
1.10 kristaps 127:
1.37 kristaps 128: while ( ! SLIST_EMPTY(&h->ords)) {
129: ord = SLIST_FIRST(&h->ords);
130: SLIST_REMOVE_HEAD(&h->ords, entry);
131: free(ord);
132: }
133:
134: while ( ! SLIST_EMPTY(&h->tags)) {
135: tag = SLIST_FIRST(&h->tags);
136: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 137: free(tag);
138: }
1.36 kristaps 139:
140: if (h->symtab)
141: chars_free(h->symtab);
1.30 kristaps 142: free(h);
1.10 kristaps 143: }
1.2 kristaps 144:
1.33 kristaps 145:
1.51 kristaps 146: void
1.29 kristaps 147: print_gen_head(struct html *h)
148: {
1.41 kristaps 149: struct htmlpair tag[4];
150:
151: tag[0].key = ATTR_HTTPEQUIV;
152: tag[0].val = "Content-Type";
153: tag[1].key = ATTR_CONTENT;
154: tag[1].val = "text/html; charset=utf-8";
155: print_otag(h, TAG_META, 2, tag);
156:
157: tag[0].key = ATTR_NAME;
158: tag[0].val = "resource-type";
159: tag[1].key = ATTR_CONTENT;
160: tag[1].val = "document";
161: print_otag(h, TAG_META, 2, tag);
162:
163: if (h->style) {
164: tag[0].key = ATTR_REL;
165: tag[0].val = "stylesheet";
166: tag[1].key = ATTR_HREF;
167: tag[1].val = h->style;
168: tag[2].key = ATTR_TYPE;
169: tag[2].val = "text/css";
170: tag[3].key = ATTR_MEDIA;
171: tag[3].val = "all";
172: print_otag(h, TAG_LINK, 4, tag);
173: }
174:
175: if (h->base) {
176: tag[0].key = ATTR_HREF;
177: tag[1].val = h->base;
178: print_otag(h, TAG_BASE, 1, tag);
179: }
1.4 kristaps 180: }
181:
1.33 kristaps 182:
1.29 kristaps 183: static void
1.32 kristaps 184: print_spec(struct html *h, const char *p, int len)
185: {
186: const char *rhs;
187: int i;
188: size_t sz;
189:
190: rhs = chars_a2ascii(h->symtab, p, (size_t)len, &sz);
191:
192: if (NULL == rhs)
193: return;
194: for (i = 0; i < (int)sz; i++)
195: putchar(rhs[i]);
196: }
197:
1.33 kristaps 198:
1.32 kristaps 199: static void
200: print_res(struct html *h, const char *p, int len)
201: {
202: const char *rhs;
203: int i;
204: size_t sz;
205:
206: rhs = chars_a2res(h->symtab, p, (size_t)len, &sz);
207:
208: if (NULL == rhs)
209: return;
210: for (i = 0; i < (int)sz; i++)
211: putchar(rhs[i]);
212: }
213:
1.33 kristaps 214:
1.32 kristaps 215: static void
216: print_escape(struct html *h, const char **p)
217: {
218: int j, type;
219: const char *wp;
220:
221: wp = *p;
222: type = 1;
223:
224: if (0 == *(++wp)) {
225: *p = wp;
226: return;
227: }
228:
229: if ('(' == *wp) {
230: wp++;
231: if (0 == *wp || 0 == *(wp + 1)) {
232: *p = 0 == *wp ? wp : wp + 1;
233: return;
234: }
235:
236: print_spec(h, wp, 2);
237: *p = ++wp;
238: return;
239:
240: } else if ('*' == *wp) {
241: if (0 == *(++wp)) {
242: *p = wp;
243: return;
244: }
245:
246: switch (*wp) {
247: case ('('):
248: wp++;
249: if (0 == *wp || 0 == *(wp + 1)) {
250: *p = 0 == *wp ? wp : wp + 1;
251: return;
252: }
253:
254: print_res(h, wp, 2);
255: *p = ++wp;
256: return;
257: case ('['):
258: type = 0;
259: break;
260: default:
261: print_res(h, wp, 1);
262: *p = wp;
263: return;
264: }
265:
266: } else if ('f' == *wp) {
267: if (0 == *(++wp)) {
268: *p = wp;
269: return;
270: }
271:
272: switch (*wp) {
273: case ('B'):
274: /* TODO */
275: break;
276: case ('I'):
277: /* TODO */
278: break;
279: case ('P'):
280: /* FALLTHROUGH */
281: case ('R'):
282: /* TODO */
283: break;
284: default:
285: break;
286: }
287:
288: *p = wp;
289: return;
290:
291: } else if ('[' != *wp) {
292: print_spec(h, wp, 1);
293: *p = wp;
294: return;
295: }
296:
297: wp++;
298: for (j = 0; *wp && ']' != *wp; wp++, j++)
299: /* Loop... */ ;
300:
301: if (0 == *wp) {
302: *p = wp;
303: return;
304: }
305:
306: if (type)
307: print_spec(h, wp - j, j);
308: else
309: print_res(h, wp - j, j);
310:
311: *p = wp;
312: }
313:
1.9 kristaps 314:
1.29 kristaps 315: static void
1.32 kristaps 316: print_encode(struct html *h, const char *p)
1.29 kristaps 317: {
1.14 kristaps 318:
1.32 kristaps 319: for (; *p; p++) {
1.34 kristaps 320: if ('\\' == *p) {
321: print_escape(h, &p);
322: continue;
323: }
324: switch (*p) {
325: case ('<'):
326: printf("<");
327: break;
328: case ('>'):
329: printf(">");
330: break;
331: case ('&'):
332: printf("&");
333: break;
334: default:
1.32 kristaps 335: putchar(*p);
1.34 kristaps 336: break;
1.32 kristaps 337: }
338: }
1.14 kristaps 339: }
340:
341:
1.51 kristaps 342: struct tag *
1.29 kristaps 343: print_otag(struct html *h, enum htmltag tag,
344: int sz, const struct htmlpair *p)
1.14 kristaps 345: {
1.29 kristaps 346: int i;
1.30 kristaps 347: struct tag *t;
348:
349: if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
350: if (NULL == (t = malloc(sizeof(struct tag))))
351: err(EXIT_FAILURE, "malloc");
352: t->tag = tag;
1.37 kristaps 353: SLIST_INSERT_HEAD(&h->tags, t, entry);
1.30 kristaps 354: } else
355: t = NULL;
1.29 kristaps 356:
357: if ( ! (HTML_NOSPACE & h->flags))
1.30 kristaps 358: if ( ! (HTML_CLRLINE & htmltags[tag].flags))
1.29 kristaps 359: printf(" ");
360:
361: printf("<%s", htmltags[tag].name);
362: for (i = 0; i < sz; i++) {
363: printf(" %s=\"", htmlattrs[p[i].key]);
364: assert(p->val);
1.32 kristaps 365: print_encode(h, p[i].val);
1.29 kristaps 366: printf("\"");
367: }
368: printf(">");
1.14 kristaps 369:
1.29 kristaps 370: h->flags |= HTML_NOSPACE;
1.30 kristaps 371: if (HTML_CLRLINE & htmltags[tag].flags)
372: h->flags |= HTML_NEWLINE;
373: else
374: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 375:
1.30 kristaps 376: return(t);
1.14 kristaps 377: }
378:
379:
380: /* ARGSUSED */
1.29 kristaps 381: static void
382: print_ctag(struct html *h, enum htmltag tag)
1.14 kristaps 383: {
384:
1.29 kristaps 385: printf("</%s>", htmltags[tag].name);
1.30 kristaps 386: if (HTML_CLRLINE & htmltags[tag].flags)
1.29 kristaps 387: h->flags |= HTML_NOSPACE;
1.30 kristaps 388: if (HTML_CLRLINE & htmltags[tag].flags)
389: h->flags |= HTML_NEWLINE;
390: else
391: h->flags &= ~HTML_NEWLINE;
1.14 kristaps 392: }
393:
394:
1.29 kristaps 395: /* ARGSUSED */
1.51 kristaps 396: void
1.29 kristaps 397: print_gen_doctype(struct html *h)
1.1 kristaps 398: {
1.29 kristaps 399:
1.46 kristaps 400: printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
1.1 kristaps 401: }
402:
403:
1.51 kristaps 404: void
1.29 kristaps 405: print_text(struct html *h, const char *p)
1.1 kristaps 406: {
407:
1.29 kristaps 408: if (*p && 0 == *(p + 1))
409: switch (*p) {
410: case('.'):
411: /* FALLTHROUGH */
412: case(','):
413: /* FALLTHROUGH */
414: case(';'):
415: /* FALLTHROUGH */
416: case(':'):
417: /* FALLTHROUGH */
418: case('?'):
419: /* FALLTHROUGH */
420: case('!'):
421: /* FALLTHROUGH */
422: case(')'):
423: /* FALLTHROUGH */
424: case(']'):
425: /* FALLTHROUGH */
426: case('}'):
1.52 ! kristaps 427: if ( ! (HTML_IGNDELIM & h->flags))
! 428: h->flags |= HTML_NOSPACE;
1.30 kristaps 429: break;
1.29 kristaps 430: default:
431: break;
432: }
1.1 kristaps 433:
1.29 kristaps 434: if ( ! (h->flags & HTML_NOSPACE))
435: printf(" ");
1.30 kristaps 436:
1.29 kristaps 437: h->flags &= ~HTML_NOSPACE;
1.30 kristaps 438: h->flags &= ~HTML_NEWLINE;
1.1 kristaps 439:
1.29 kristaps 440: if (p)
1.32 kristaps 441: print_encode(h, p);
1.8 kristaps 442:
1.29 kristaps 443: if (*p && 0 == *(p + 1))
444: switch (*p) {
445: case('('):
446: /* FALLTHROUGH */
447: case('['):
448: /* FALLTHROUGH */
449: case('{'):
450: h->flags |= HTML_NOSPACE;
1.30 kristaps 451: break;
1.29 kristaps 452: default:
453: break;
454: }
1.1 kristaps 455: }
1.30 kristaps 456:
457:
1.51 kristaps 458: void
1.30 kristaps 459: print_tagq(struct html *h, const struct tag *until)
460: {
461: struct tag *tag;
462:
1.37 kristaps 463: while ( ! SLIST_EMPTY(&h->tags)) {
464: tag = SLIST_FIRST(&h->tags);
1.30 kristaps 465: print_ctag(h, tag->tag);
1.37 kristaps 466: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 467: free(tag);
468: if (until && tag == until)
469: return;
470: }
471: }
472:
473:
1.51 kristaps 474: void
1.30 kristaps 475: print_stagq(struct html *h, const struct tag *suntil)
476: {
477: struct tag *tag;
478:
1.37 kristaps 479: while ( ! SLIST_EMPTY(&h->tags)) {
480: tag = SLIST_FIRST(&h->tags);
1.30 kristaps 481: if (suntil && tag == suntil)
482: return;
483: print_ctag(h, tag->tag);
1.37 kristaps 484: SLIST_REMOVE_HEAD(&h->tags, entry);
1.30 kristaps 485: free(tag);
486: }
487: }
CVSweb