]> git.cameronkatri.com Git - mandoc.git/blob - man_html.c
Fill in more version notes.
[mandoc.git] / man_html.c
1 /* $Id: man_html.c,v 1.45 2010/07/23 12:27:28 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc.h"
30 #include "out.h"
31 #include "html.h"
32 #include "man.h"
33 #include "main.h"
34
35 /* TODO: preserve ident widths. */
36 /* FIXME: have PD set the default vspace width. */
37
38 #define INDENT 5
39 #define HALFINDENT 3
40
41 #define MAN_ARGS const struct man_meta *m, \
42 const struct man_node *n, \
43 struct mhtml *mh, \
44 struct html *h
45
46 struct mhtml {
47 int fl;
48 #define MANH_LITERAL (1 << 0) /* literal context */
49 };
50
51 struct htmlman {
52 int (*pre)(MAN_ARGS);
53 int (*post)(MAN_ARGS);
54 };
55
56 static void print_man(MAN_ARGS);
57 static void print_man_head(MAN_ARGS);
58 static void print_man_nodelist(MAN_ARGS);
59 static void print_man_node(MAN_ARGS);
60
61 static int a2width(const struct man_node *,
62 struct roffsu *);
63
64 static int man_alt_pre(MAN_ARGS);
65 static int man_br_pre(MAN_ARGS);
66 static int man_ign_pre(MAN_ARGS);
67 static int man_in_pre(MAN_ARGS);
68 static int man_literal_pre(MAN_ARGS);
69 static void man_root_post(MAN_ARGS);
70 static int man_root_pre(MAN_ARGS);
71 static int man_B_pre(MAN_ARGS);
72 static int man_HP_pre(MAN_ARGS);
73 static int man_I_pre(MAN_ARGS);
74 static int man_IP_pre(MAN_ARGS);
75 static int man_PP_pre(MAN_ARGS);
76 static int man_RS_pre(MAN_ARGS);
77 static int man_SB_pre(MAN_ARGS);
78 static int man_SH_pre(MAN_ARGS);
79 static int man_SM_pre(MAN_ARGS);
80 static int man_SS_pre(MAN_ARGS);
81
82 static const struct htmlman mans[MAN_MAX] = {
83 { man_br_pre, NULL }, /* br */
84 { NULL, NULL }, /* TH */
85 { man_SH_pre, NULL }, /* SH */
86 { man_SS_pre, NULL }, /* SS */
87 { man_IP_pre, NULL }, /* TP */
88 { man_PP_pre, NULL }, /* LP */
89 { man_PP_pre, NULL }, /* PP */
90 { man_PP_pre, NULL }, /* P */
91 { man_IP_pre, NULL }, /* IP */
92 { man_HP_pre, NULL }, /* HP */
93 { man_SM_pre, NULL }, /* SM */
94 { man_SB_pre, NULL }, /* SB */
95 { man_alt_pre, NULL }, /* BI */
96 { man_alt_pre, NULL }, /* IB */
97 { man_alt_pre, NULL }, /* BR */
98 { man_alt_pre, NULL }, /* RB */
99 { NULL, NULL }, /* R */
100 { man_B_pre, NULL }, /* B */
101 { man_I_pre, NULL }, /* I */
102 { man_alt_pre, NULL }, /* IR */
103 { man_alt_pre, NULL }, /* RI */
104 { NULL, NULL }, /* na */
105 { NULL, NULL }, /* i */
106 { man_br_pre, NULL }, /* sp */
107 { man_literal_pre, NULL }, /* nf */
108 { man_literal_pre, NULL }, /* fi */
109 { NULL, NULL }, /* r */
110 { NULL, NULL }, /* RE */
111 { man_RS_pre, NULL }, /* RS */
112 { man_ign_pre, NULL }, /* DT */
113 { man_ign_pre, NULL }, /* UC */
114 { man_ign_pre, NULL }, /* PD */
115 { man_br_pre, NULL }, /* Sp */
116 { man_literal_pre, NULL }, /* Vb */
117 { man_literal_pre, NULL }, /* Ve */
118 { man_ign_pre, NULL }, /* AT */
119 { man_in_pre, NULL }, /* in */
120 };
121
122
123 void
124 html_man(void *arg, const struct man *m)
125 {
126 struct html *h;
127 struct tag *t;
128 struct mhtml mh;
129
130 h = (struct html *)arg;
131
132 print_gen_decls(h);
133
134 memset(&mh, 0, sizeof(struct mhtml));
135
136 t = print_otag(h, TAG_HTML, 0, NULL);
137 print_man(man_meta(m), man_node(m), &mh, h);
138 print_tagq(h, t);
139
140 printf("\n");
141 }
142
143
144 static void
145 print_man(MAN_ARGS)
146 {
147 struct tag *t;
148 struct htmlpair tag;
149
150 t = print_otag(h, TAG_HEAD, 0, NULL);
151
152 print_man_head(m, n, mh, h);
153 print_tagq(h, t);
154 t = print_otag(h, TAG_BODY, 0, NULL);
155
156 tag.key = ATTR_CLASS;
157 tag.val = "body";
158 print_otag(h, TAG_DIV, 1, &tag);
159
160 print_man_nodelist(m, n, mh, h);
161
162 print_tagq(h, t);
163 }
164
165
166 /* ARGSUSED */
167 static void
168 print_man_head(MAN_ARGS)
169 {
170
171 print_gen_head(h);
172 bufinit(h);
173 buffmt(h, "%s(%s)", m->title, m->msec);
174
175 print_otag(h, TAG_TITLE, 0, NULL);
176 print_text(h, h->buf);
177 }
178
179
180 static void
181 print_man_nodelist(MAN_ARGS)
182 {
183
184 print_man_node(m, n, mh, h);
185 if (n->next)
186 print_man_nodelist(m, n->next, mh, h);
187 }
188
189
190 static void
191 print_man_node(MAN_ARGS)
192 {
193 int child;
194 struct tag *t;
195
196 child = 1;
197 t = h->tags.head;
198
199 bufinit(h);
200
201 /*
202 * FIXME: embedded elements within next-line scopes (e.g., `br'
203 * within an empty `B') will cause formatting to be forgotten
204 * due to scope closing out.
205 */
206
207 switch (n->type) {
208 case (MAN_ROOT):
209 child = man_root_pre(m, n, mh, h);
210 break;
211 case (MAN_TEXT):
212 print_text(h, n->string);
213
214 if (MANH_LITERAL & mh->fl)
215 print_otag(h, TAG_BR, 0, NULL);
216
217 return;
218 default:
219 /*
220 * Close out scope of font prior to opening a macro
221 * scope. Assert that the metafont is on the top of the
222 * stack (it's never nested).
223 */
224 if (h->metaf) {
225 assert(h->metaf == t);
226 print_tagq(h, h->metaf);
227 assert(NULL == h->metaf);
228 t = h->tags.head;
229 }
230 if (mans[n->tok].pre)
231 child = (*mans[n->tok].pre)(m, n, mh, h);
232 break;
233 }
234
235 if (child && n->child)
236 print_man_nodelist(m, n->child, mh, h);
237
238 /* This will automatically close out any font scope. */
239 print_stagq(h, t);
240
241 bufinit(h);
242
243 switch (n->type) {
244 case (MAN_ROOT):
245 man_root_post(m, n, mh, h);
246 break;
247 case (MAN_TEXT):
248 break;
249 default:
250 if (mans[n->tok].post)
251 (*mans[n->tok].post)(m, n, mh, h);
252 break;
253 }
254 }
255
256
257 static int
258 a2width(const struct man_node *n, struct roffsu *su)
259 {
260
261 if (MAN_TEXT != n->type)
262 return(0);
263 if (a2roffsu(n->string, su, SCALE_BU))
264 return(1);
265
266 return(0);
267 }
268
269
270 /* ARGSUSED */
271 static int
272 man_root_pre(MAN_ARGS)
273 {
274 struct htmlpair tag[3];
275 struct tag *t, *tt;
276 char b[BUFSIZ], title[BUFSIZ];
277
278 b[0] = 0;
279 if (m->vol)
280 (void)strlcat(b, m->vol, BUFSIZ);
281
282 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
283
284 PAIR_CLASS_INIT(&tag[0], "header");
285 bufcat_style(h, "width", "100%");
286 PAIR_STYLE_INIT(&tag[1], h);
287 PAIR_SUMMARY_INIT(&tag[2], "header");
288
289 t = print_otag(h, TAG_TABLE, 3, tag);
290 tt = print_otag(h, TAG_TR, 0, NULL);
291
292 bufinit(h);
293 bufcat_style(h, "width", "10%");
294 PAIR_STYLE_INIT(&tag[0], h);
295 print_otag(h, TAG_TD, 1, tag);
296 print_text(h, title);
297 print_stagq(h, tt);
298
299 bufinit(h);
300 bufcat_style(h, "width", "80%");
301 bufcat_style(h, "white-space", "nowrap");
302 bufcat_style(h, "text-align", "center");
303 PAIR_STYLE_INIT(&tag[0], h);
304 print_otag(h, TAG_TD, 1, tag);
305 print_text(h, b);
306 print_stagq(h, tt);
307
308 bufinit(h);
309 bufcat_style(h, "width", "10%");
310 bufcat_style(h, "text-align", "right");
311 PAIR_STYLE_INIT(&tag[0], h);
312 print_otag(h, TAG_TD, 1, tag);
313 print_text(h, title);
314 print_tagq(h, t);
315 return(1);
316 }
317
318
319 /* ARGSUSED */
320 static void
321 man_root_post(MAN_ARGS)
322 {
323 struct htmlpair tag[3];
324 struct tag *t, *tt;
325 char b[DATESIZ];
326
327 if (m->rawdate)
328 strlcpy(b, m->rawdate, DATESIZ);
329 else
330 time2a(m->date, b, DATESIZ);
331
332 PAIR_CLASS_INIT(&tag[0], "footer");
333 bufcat_style(h, "width", "100%");
334 PAIR_STYLE_INIT(&tag[1], h);
335 PAIR_SUMMARY_INIT(&tag[2], "footer");
336
337 t = print_otag(h, TAG_TABLE, 3, tag);
338 tt = print_otag(h, TAG_TR, 0, NULL);
339
340 bufinit(h);
341 bufcat_style(h, "width", "50%");
342 PAIR_STYLE_INIT(&tag[0], h);
343 print_otag(h, TAG_TD, 1, tag);
344 print_text(h, b);
345 print_stagq(h, tt);
346
347 bufinit(h);
348 bufcat_style(h, "width", "50%");
349 bufcat_style(h, "text-align", "right");
350 PAIR_STYLE_INIT(&tag[0], h);
351 print_otag(h, TAG_TD, 1, tag);
352 if (m->source)
353 print_text(h, m->source);
354 print_tagq(h, t);
355 }
356
357
358
359 /* ARGSUSED */
360 static int
361 man_br_pre(MAN_ARGS)
362 {
363 struct roffsu su;
364 struct htmlpair tag;
365
366 SCALE_VS_INIT(&su, 1);
367
368 switch (n->tok) {
369 case (MAN_Sp):
370 SCALE_VS_INIT(&su, 0.5);
371 break;
372 case (MAN_sp):
373 if (n->child)
374 a2roffsu(n->child->string, &su, SCALE_VS);
375 break;
376 default:
377 su.scale = 0;
378 break;
379 }
380
381 bufcat_su(h, "height", &su);
382 PAIR_STYLE_INIT(&tag, h);
383 print_otag(h, TAG_DIV, 1, &tag);
384
385 /* So the div isn't empty: */
386 print_text(h, "\\~");
387
388 return(0);
389 }
390
391
392 /* ARGSUSED */
393 static int
394 man_SH_pre(MAN_ARGS)
395 {
396 struct htmlpair tag[2];
397 struct roffsu su;
398
399 if (MAN_BODY == n->type) {
400 SCALE_HS_INIT(&su, INDENT);
401 bufcat_su(h, "margin-left", &su);
402 PAIR_CLASS_INIT(&tag[0], "sec-body");
403 PAIR_STYLE_INIT(&tag[1], h);
404 print_otag(h, TAG_DIV, 2, tag);
405 return(1);
406 } else if (MAN_BLOCK == n->type) {
407 PAIR_CLASS_INIT(&tag[0], "sec-block");
408 if (n->prev && MAN_SH == n->prev->tok)
409 if (NULL == n->prev->body->child) {
410 print_otag(h, TAG_DIV, 1, tag);
411 return(1);
412 }
413
414 SCALE_VS_INIT(&su, 1);
415 bufcat_su(h, "margin-top", &su);
416 if (NULL == n->next)
417 bufcat_su(h, "margin-bottom", &su);
418 PAIR_STYLE_INIT(&tag[1], h);
419 print_otag(h, TAG_DIV, 2, tag);
420 return(1);
421 }
422
423 PAIR_CLASS_INIT(&tag[0], "sec-head");
424 print_otag(h, TAG_DIV, 1, tag);
425 return(1);
426 }
427
428
429 /* ARGSUSED */
430 static int
431 man_alt_pre(MAN_ARGS)
432 {
433 const struct man_node *nn;
434 struct tag *t;
435 int i;
436 enum htmlfont fp;
437
438 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
439 switch (n->tok) {
440 case (MAN_BI):
441 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD;
442 break;
443 case (MAN_IB):
444 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC;
445 break;
446 case (MAN_RI):
447 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE;
448 break;
449 case (MAN_IR):
450 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC;
451 break;
452 case (MAN_BR):
453 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD;
454 break;
455 case (MAN_RB):
456 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE;
457 break;
458 default:
459 abort();
460 /* NOTREACHED */
461 }
462
463 if (i)
464 h->flags |= HTML_NOSPACE;
465
466 /*
467 * Open and close the scope with each argument, so that
468 * internal \f escapes, which are common, are also
469 * closed out with the scope.
470 */
471 t = print_ofont(h, fp);
472 print_man_node(m, nn, mh, h);
473 print_tagq(h, t);
474 }
475
476 return(0);
477 }
478
479
480 /* ARGSUSED */
481 static int
482 man_SB_pre(MAN_ARGS)
483 {
484 struct htmlpair tag;
485
486 /* FIXME: print_ofont(). */
487 PAIR_CLASS_INIT(&tag, "small bold");
488 print_otag(h, TAG_SPAN, 1, &tag);
489 return(1);
490 }
491
492
493 /* ARGSUSED */
494 static int
495 man_SM_pre(MAN_ARGS)
496 {
497 struct htmlpair tag;
498
499 PAIR_CLASS_INIT(&tag, "small");
500 print_otag(h, TAG_SPAN, 1, &tag);
501 return(1);
502 }
503
504
505 /* ARGSUSED */
506 static int
507 man_SS_pre(MAN_ARGS)
508 {
509 struct htmlpair tag[3];
510 struct roffsu su;
511
512 SCALE_VS_INIT(&su, 1);
513
514 if (MAN_BODY == n->type) {
515 PAIR_CLASS_INIT(&tag[0], "ssec-body");
516 if (n->parent->next && n->child) {
517 bufcat_su(h, "margin-bottom", &su);
518 PAIR_STYLE_INIT(&tag[1], h);
519 print_otag(h, TAG_DIV, 2, tag);
520 return(1);
521 }
522
523 print_otag(h, TAG_DIV, 1, tag);
524 return(1);
525 } else if (MAN_BLOCK == n->type) {
526 PAIR_CLASS_INIT(&tag[0], "ssec-block");
527 if (n->prev && MAN_SS == n->prev->tok)
528 if (n->prev->body->child) {
529 bufcat_su(h, "margin-top", &su);
530 PAIR_STYLE_INIT(&tag[1], h);
531 print_otag(h, TAG_DIV, 2, tag);
532 return(1);
533 }
534
535 print_otag(h, TAG_DIV, 1, tag);
536 return(1);
537 }
538
539 SCALE_HS_INIT(&su, INDENT - HALFINDENT);
540 bufcat_su(h, "margin-left", &su);
541 PAIR_CLASS_INIT(&tag[0], "ssec-head");
542 PAIR_STYLE_INIT(&tag[1], h);
543 print_otag(h, TAG_DIV, 2, tag);
544 return(1);
545 }
546
547
548 /* ARGSUSED */
549 static int
550 man_PP_pre(MAN_ARGS)
551 {
552 struct htmlpair tag;
553 struct roffsu su;
554 int i;
555
556 if (MAN_BLOCK != n->type)
557 return(1);
558
559 i = 0;
560
561 if (MAN_ROOT == n->parent->type) {
562 SCALE_HS_INIT(&su, INDENT);
563 bufcat_su(h, "margin-left", &su);
564 i = 1;
565 }
566 if (n->prev) {
567 SCALE_VS_INIT(&su, 1);
568 bufcat_su(h, "margin-top", &su);
569 i = 1;
570 }
571
572 PAIR_STYLE_INIT(&tag, h);
573 print_otag(h, TAG_DIV, i, &tag);
574 return(1);
575 }
576
577
578 /* ARGSUSED */
579 static int
580 man_IP_pre(MAN_ARGS)
581 {
582 struct roffsu su;
583 struct htmlpair tag;
584 const struct man_node *nn;
585 int width;
586
587 /*
588 * This scattering of 1-BU margins and pads is to make sure that
589 * when text overruns its box, the subsequent text isn't flush
590 * up against it. However, the rest of the right-hand box must
591 * also be adjusted in consideration of this 1-BU space.
592 */
593
594 if (MAN_BODY == n->type) {
595 SCALE_HS_INIT(&su, INDENT);
596 bufcat_su(h, "margin-left", &su);
597 PAIR_STYLE_INIT(&tag, h);
598 print_otag(h, TAG_DIV, 1, &tag);
599 return(1);
600 }
601
602 nn = MAN_BLOCK == n->type ?
603 n->head->child : n->parent->head->child;
604
605 SCALE_HS_INIT(&su, INDENT);
606 width = 0;
607
608 /* Width is the last token. */
609
610 if (MAN_IP == n->tok && NULL != nn)
611 if (NULL != (nn = nn->next)) {
612 for ( ; nn->next; nn = nn->next)
613 /* Do nothing. */ ;
614 width = a2width(nn, &su);
615 }
616
617 /* Width is the first token. */
618
619 if (MAN_TP == n->tok && NULL != nn) {
620 /* Skip past non-text children. */
621 while (nn && MAN_TEXT != nn->type)
622 nn = nn->next;
623 if (nn)
624 width = a2width(nn, &su);
625 }
626
627 if (MAN_BLOCK == n->type) {
628 bufcat_su(h, "margin-left", &su);
629 SCALE_VS_INIT(&su, 1);
630 bufcat_su(h, "margin-top", &su);
631 bufcat_style(h, "clear", "both");
632 PAIR_STYLE_INIT(&tag, h);
633 print_otag(h, TAG_DIV, 1, &tag);
634 return(1);
635 }
636
637 bufcat_su(h, "min-width", &su);
638 SCALE_INVERT(&su);
639 bufcat_su(h, "margin-left", &su);
640 SCALE_HS_INIT(&su, 1);
641 bufcat_su(h, "margin-right", &su);
642 bufcat_style(h, "clear", "left");
643
644 if (n->next && n->next->child)
645 bufcat_style(h, "float", "left");
646
647 PAIR_STYLE_INIT(&tag, h);
648 print_otag(h, TAG_DIV, 1, &tag);
649
650 /*
651 * Without a length string, we can print all of our children.
652 */
653
654 if ( ! width)
655 return(1);
656
657 /*
658 * When a length has been specified, we need to carefully print
659 * our child context: IP gets all children printed but the last
660 * (the width), while TP gets all children printed but the first
661 * (the width).
662 */
663
664 if (MAN_IP == n->tok)
665 for (nn = n->child; nn->next; nn = nn->next)
666 print_man_node(m, nn, mh, h);
667 if (MAN_TP == n->tok)
668 for (nn = n->child->next; nn; nn = nn->next)
669 print_man_node(m, nn, mh, h);
670
671 return(0);
672 }
673
674
675 /* ARGSUSED */
676 static int
677 man_HP_pre(MAN_ARGS)
678 {
679 const struct man_node *nn;
680 struct htmlpair tag;
681 struct roffsu su;
682
683 if (MAN_HEAD == n->type)
684 return(0);
685
686 nn = MAN_BLOCK == n->type ?
687 n->head->child : n->parent->head->child;
688
689 SCALE_HS_INIT(&su, INDENT);
690
691 if (NULL != nn)
692 (void)a2width(nn, &su);
693
694 if (MAN_BLOCK == n->type) {
695 bufcat_su(h, "margin-left", &su);
696 SCALE_VS_INIT(&su, 1);
697 bufcat_su(h, "margin-top", &su);
698 bufcat_style(h, "clear", "both");
699 PAIR_STYLE_INIT(&tag, h);
700 print_otag(h, TAG_DIV, 1, &tag);
701 return(1);
702 }
703
704 bufcat_su(h, "margin-left", &su);
705 SCALE_INVERT(&su);
706 bufcat_su(h, "text-indent", &su);
707
708 PAIR_STYLE_INIT(&tag, h);
709 print_otag(h, TAG_DIV, 1, &tag);
710 return(1);
711 }
712
713
714 /* ARGSUSED */
715 static int
716 man_B_pre(MAN_ARGS)
717 {
718
719 print_ofont(h, HTMLFONT_BOLD);
720 return(1);
721 }
722
723
724 /* ARGSUSED */
725 static int
726 man_I_pre(MAN_ARGS)
727 {
728
729 print_ofont(h, HTMLFONT_ITALIC);
730 return(1);
731 }
732
733
734 /* ARGSUSED */
735 static int
736 man_literal_pre(MAN_ARGS)
737 {
738
739 switch (n->tok) {
740 case (MAN_nf):
741 /* FALLTHROUGH */
742 case (MAN_Vb):
743 print_otag(h, TAG_BR, 0, NULL);
744 mh->fl |= MANH_LITERAL;
745 return(MAN_Vb != n->tok);
746 default:
747 mh->fl &= ~MANH_LITERAL;
748 break;
749 }
750
751 return(1);
752 }
753
754
755 /* ARGSUSED */
756 static int
757 man_in_pre(MAN_ARGS)
758 {
759
760 print_otag(h, TAG_BR, 0, NULL);
761 return(0);
762 }
763
764
765 /* ARGSUSED */
766 static int
767 man_ign_pre(MAN_ARGS)
768 {
769
770 return(0);
771 }
772
773
774 /* ARGSUSED */
775 static int
776 man_RS_pre(MAN_ARGS)
777 {
778 struct htmlpair tag;
779 struct roffsu su;
780
781 if (MAN_HEAD == n->type)
782 return(0);
783 else if (MAN_BODY == n->type)
784 return(1);
785
786 SCALE_HS_INIT(&su, INDENT);
787 bufcat_su(h, "margin-left", &su);
788
789 if (n->head->child) {
790 SCALE_VS_INIT(&su, 1);
791 a2width(n->head->child, &su);
792 bufcat_su(h, "margin-top", &su);
793 }
794
795 PAIR_STYLE_INIT(&tag, h);
796 print_otag(h, TAG_DIV, 1, &tag);
797 return(1);
798 }