]> git.cameronkatri.com Git - mandoc.git/blob - html.c
`Cd' in -Thtml -mdoc correctly breaks lines.
[mandoc.git] / html.c
1 /* $Id: html.c,v 1.62 2009/10/09 06:54:11 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
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.
16 */
17 #include <sys/types.h>
18 #include <sys/queue.h>
19
20 #include <assert.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "out.h"
29 #include "chars.h"
30 #include "html.h"
31
32 #define DOCTYPE "-//W3C//DTD HTML 4.01//EN"
33 #define DTD "http://www.w3.org/TR/html4/strict.dtd"
34
35 struct htmldata {
36 char *name;
37 int flags;
38 #define HTML_CLRLINE (1 << 0)
39 #define HTML_NOSTACK (1 << 1)
40 };
41
42 static const struct htmldata htmltags[TAG_MAX] = {
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 */
47 {"title", HTML_CLRLINE}, /* TAG_TITLE */
48 {"div", HTML_CLRLINE}, /* TAG_DIV */
49 {"h1", 0}, /* TAG_H1 */
50 {"h2", 0}, /* TAG_H2 */
51 {"p", HTML_CLRLINE}, /* TAG_P */
52 {"span", 0}, /* TAG_SPAN */
53 {"link", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
54 {"br", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
55 {"a", 0}, /* TAG_A */
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 */
60 {"li", HTML_CLRLINE}, /* TAG_LI */
61 {"ul", HTML_CLRLINE}, /* TAG_UL */
62 {"ol", HTML_CLRLINE}, /* TAG_OL */
63 {"base", HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
64 };
65
66 static const char *const htmlattrs[ATTR_MAX] = {
67 "http-equiv",
68 "content",
69 "name",
70 "rel",
71 "href",
72 "type",
73 "media",
74 "class",
75 "style",
76 "width",
77 "valign",
78 "target",
79 "id",
80 };
81
82 #ifdef __linux__
83 extern int getsubopt(char **, char * const *, char **);
84 #endif
85
86 void *
87 html_alloc(char *outopts)
88 {
89 struct html *h;
90 char *toks[4], *v;
91
92 toks[0] = "style";
93 toks[1] = "man";
94 toks[2] = "includes";
95 toks[3] = NULL;
96
97 if (NULL == (h = calloc(1, sizeof(struct html))))
98 return(NULL);
99
100 SLIST_INIT(&h->tags);
101 SLIST_INIT(&h->ords);
102
103 if (NULL == (h->symtab = chars_init(CHARS_HTML))) {
104 free(h);
105 return(NULL);
106 }
107
108 while (outopts && *outopts)
109 switch (getsubopt(&outopts, toks, &v)) {
110 case (0):
111 h->style = v;
112 break;
113 case (1):
114 h->base_man = v;
115 break;
116 case (2):
117 h->base_includes = v;
118 break;
119 default:
120 break;
121 }
122
123 return(h);
124 }
125
126
127 void
128 html_free(void *p)
129 {
130 struct tag *tag;
131 struct ord *ord;
132 struct html *h;
133
134 h = (struct html *)p;
135
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);
145 free(tag);
146 }
147
148 if (h->symtab)
149 chars_free(h->symtab);
150
151 free(h);
152 }
153
154
155 void
156 print_gen_head(struct html *h)
157 {
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 }
183 }
184
185
186 static void
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
201
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
217
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
317
318 static void
319 print_encode(struct html *h, const char *p)
320 {
321
322 for (; *p; p++) {
323 if ('\\' == *p) {
324 print_escape(h, &p);
325 continue;
326 }
327 switch (*p) {
328 case ('<'):
329 printf("&lt;");
330 break;
331 case ('>'):
332 printf("&gt;");
333 break;
334 case ('&'):
335 printf("&amp;");
336 break;
337 default:
338 putchar(*p);
339 break;
340 }
341 }
342 }
343
344
345 struct tag *
346 print_otag(struct html *h, enum htmltag tag,
347 int sz, const struct htmlpair *p)
348 {
349 int i;
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;
356 SLIST_INSERT_HEAD(&h->tags, t, entry);
357 } else
358 t = NULL;
359
360 if ( ! (HTML_NOSPACE & h->flags))
361 if ( ! (HTML_CLRLINE & htmltags[tag].flags))
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);
368 print_encode(h, p[i].val);
369 printf("\"");
370 }
371 printf(">");
372
373 h->flags |= HTML_NOSPACE;
374 if (HTML_CLRLINE & htmltags[tag].flags)
375 h->flags |= HTML_NEWLINE;
376 else
377 h->flags &= ~HTML_NEWLINE;
378
379 return(t);
380 }
381
382
383 /* ARGSUSED */
384 static void
385 print_ctag(struct html *h, enum htmltag tag)
386 {
387
388 printf("</%s>", htmltags[tag].name);
389 if (HTML_CLRLINE & htmltags[tag].flags)
390 h->flags |= HTML_NOSPACE;
391 if (HTML_CLRLINE & htmltags[tag].flags)
392 h->flags |= HTML_NEWLINE;
393 else
394 h->flags &= ~HTML_NEWLINE;
395 }
396
397
398 /* ARGSUSED */
399 void
400 print_gen_doctype(struct html *h)
401 {
402
403 printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
404 }
405
406
407 void
408 print_text(struct html *h, const char *p)
409 {
410
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('}'):
430 if ( ! (HTML_IGNDELIM & h->flags))
431 h->flags |= HTML_NOSPACE;
432 break;
433 default:
434 break;
435 }
436
437 if ( ! (h->flags & HTML_NOSPACE))
438 printf(" ");
439
440 h->flags &= ~HTML_NOSPACE;
441 h->flags &= ~HTML_NEWLINE;
442
443 if (p)
444 print_encode(h, p);
445
446 if (*p && 0 == *(p + 1))
447 switch (*p) {
448 case('('):
449 /* FALLTHROUGH */
450 case('['):
451 /* FALLTHROUGH */
452 case('{'):
453 h->flags |= HTML_NOSPACE;
454 break;
455 default:
456 break;
457 }
458 }
459
460
461 void
462 print_tagq(struct html *h, const struct tag *until)
463 {
464 struct tag *tag;
465
466 while ( ! SLIST_EMPTY(&h->tags)) {
467 tag = SLIST_FIRST(&h->tags);
468 print_ctag(h, tag->tag);
469 SLIST_REMOVE_HEAD(&h->tags, entry);
470 free(tag);
471 if (until && tag == until)
472 return;
473 }
474 }
475
476
477 void
478 print_stagq(struct html *h, const struct tag *suntil)
479 {
480 struct tag *tag;
481
482 while ( ! SLIST_EMPTY(&h->tags)) {
483 tag = SLIST_FIRST(&h->tags);
484 if (suntil && tag == suntil)
485 return;
486 print_ctag(h, tag->tag);
487 SLIST_REMOVE_HEAD(&h->tags, entry);
488 free(tag);
489 }
490 }
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
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
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);
527 (void)vsnprintf(h->buf + (int)h->buflen,
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;
552
553 while (NULL != (p = strchr(pp, '%'))) {
554 bufncat(h, pp, (size_t)(p - pp));
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;
577
578 /* LINTED */
579 while (NULL != (p = strchr(pp, '%'))) {
580 bufncat(h, pp, (size_t)(p - pp));
581 switch (*(p + 1)) {
582 case('S'):
583 bufcat(h, sec ? sec : "1");
584 break;
585 case('N'):
586 buffmt(h, name);
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 }
597
598
599 void
600 bufcat_su(struct html *h, const char *p, const struct roffsu *su)
601 {
602 double 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;
620 case (SCALE_EM):
621 u = "em";
622 break;
623 case (SCALE_MM):
624 if (0 == (v /= 100))
625 v = 1;
626 u = "em";
627 break;
628 case (SCALE_EN):
629 u = "ex";
630 break;
631 case (SCALE_BU):
632 u = "ex";
633 break;
634 case (SCALE_VS):
635 u = "em";
636 break;
637 default:
638 u = "ex";
639 break;
640 }
641
642 if (su->pt)
643 buffmt(h, "%s: %f%s;", p, v, u);
644 else
645 /* LINTED */
646 buffmt(h, "%s: %d%s;", p, (int)v, u);
647 }