ba93749b549c3940aca15ea6224f557fc5851617
[dragonfly.git] / contrib / mdocml / mdoc_html.c
1 /*      $Id: mdoc_html.c,v 1.184 2012/11/17 00:26:33 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 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 #include <unistd.h>
29
30 #include "mandoc.h"
31 #include "out.h"
32 #include "html.h"
33 #include "mdoc.h"
34 #include "main.h"
35
36 #define INDENT           5
37
38 #define MDOC_ARGS         const struct mdoc_meta *meta, \
39                           const struct mdoc_node *n, \
40                           struct html *h
41
42 #ifndef MIN
43 #define MIN(a,b)        ((/*CONSTCOND*/(a)<(b))?(a):(b))
44 #endif
45
46 struct  htmlmdoc {
47         int             (*pre)(MDOC_ARGS);
48         void            (*post)(MDOC_ARGS);
49 };
50
51 static  void              print_mdoc(MDOC_ARGS);
52 static  void              print_mdoc_head(MDOC_ARGS);
53 static  void              print_mdoc_node(MDOC_ARGS);
54 static  void              print_mdoc_nodelist(MDOC_ARGS);
55 static  void              synopsis_pre(struct html *, 
56                                 const struct mdoc_node *);
57
58 static  void              a2width(const char *, struct roffsu *);
59 static  void              a2offs(const char *, struct roffsu *);
60
61 static  void              mdoc_root_post(MDOC_ARGS);
62 static  int               mdoc_root_pre(MDOC_ARGS);
63
64 static  void              mdoc__x_post(MDOC_ARGS);
65 static  int               mdoc__x_pre(MDOC_ARGS);
66 static  int               mdoc_ad_pre(MDOC_ARGS);
67 static  int               mdoc_an_pre(MDOC_ARGS);
68 static  int               mdoc_ap_pre(MDOC_ARGS);
69 static  int               mdoc_ar_pre(MDOC_ARGS);
70 static  int               mdoc_bd_pre(MDOC_ARGS);
71 static  int               mdoc_bf_pre(MDOC_ARGS);
72 static  void              mdoc_bk_post(MDOC_ARGS);
73 static  int               mdoc_bk_pre(MDOC_ARGS);
74 static  int               mdoc_bl_pre(MDOC_ARGS);
75 static  int               mdoc_bt_pre(MDOC_ARGS);
76 static  int               mdoc_bx_pre(MDOC_ARGS);
77 static  int               mdoc_cd_pre(MDOC_ARGS);
78 static  int               mdoc_d1_pre(MDOC_ARGS);
79 static  int               mdoc_dv_pre(MDOC_ARGS);
80 static  int               mdoc_fa_pre(MDOC_ARGS);
81 static  int               mdoc_fd_pre(MDOC_ARGS);
82 static  int               mdoc_fl_pre(MDOC_ARGS);
83 static  int               mdoc_fn_pre(MDOC_ARGS);
84 static  int               mdoc_ft_pre(MDOC_ARGS);
85 static  int               mdoc_em_pre(MDOC_ARGS);
86 static  int               mdoc_er_pre(MDOC_ARGS);
87 static  int               mdoc_ev_pre(MDOC_ARGS);
88 static  int               mdoc_ex_pre(MDOC_ARGS);
89 static  void              mdoc_fo_post(MDOC_ARGS);
90 static  int               mdoc_fo_pre(MDOC_ARGS);
91 static  int               mdoc_ic_pre(MDOC_ARGS);
92 static  int               mdoc_igndelim_pre(MDOC_ARGS);
93 static  int               mdoc_in_pre(MDOC_ARGS);
94 static  int               mdoc_it_pre(MDOC_ARGS);
95 static  int               mdoc_lb_pre(MDOC_ARGS);
96 static  int               mdoc_li_pre(MDOC_ARGS);
97 static  int               mdoc_lk_pre(MDOC_ARGS);
98 static  int               mdoc_mt_pre(MDOC_ARGS);
99 static  int               mdoc_ms_pre(MDOC_ARGS);
100 static  int               mdoc_nd_pre(MDOC_ARGS);
101 static  int               mdoc_nm_pre(MDOC_ARGS);
102 static  int               mdoc_ns_pre(MDOC_ARGS);
103 static  int               mdoc_pa_pre(MDOC_ARGS);
104 static  void              mdoc_pf_post(MDOC_ARGS);
105 static  int               mdoc_pp_pre(MDOC_ARGS);
106 static  void              mdoc_quote_post(MDOC_ARGS);
107 static  int               mdoc_quote_pre(MDOC_ARGS);
108 static  int               mdoc_rs_pre(MDOC_ARGS);
109 static  int               mdoc_rv_pre(MDOC_ARGS);
110 static  int               mdoc_sh_pre(MDOC_ARGS);
111 static  int               mdoc_sm_pre(MDOC_ARGS);
112 static  int               mdoc_sp_pre(MDOC_ARGS);
113 static  int               mdoc_ss_pre(MDOC_ARGS);
114 static  int               mdoc_sx_pre(MDOC_ARGS);
115 static  int               mdoc_sy_pre(MDOC_ARGS);
116 static  int               mdoc_ud_pre(MDOC_ARGS);
117 static  int               mdoc_va_pre(MDOC_ARGS);
118 static  int               mdoc_vt_pre(MDOC_ARGS);
119 static  int               mdoc_xr_pre(MDOC_ARGS);
120 static  int               mdoc_xx_pre(MDOC_ARGS);
121
122 static  const struct htmlmdoc mdocs[MDOC_MAX] = {
123         {mdoc_ap_pre, NULL}, /* Ap */
124         {NULL, NULL}, /* Dd */
125         {NULL, NULL}, /* Dt */
126         {NULL, NULL}, /* Os */
127         {mdoc_sh_pre, NULL }, /* Sh */
128         {mdoc_ss_pre, NULL }, /* Ss */ 
129         {mdoc_pp_pre, NULL}, /* Pp */ 
130         {mdoc_d1_pre, NULL}, /* D1 */
131         {mdoc_d1_pre, NULL}, /* Dl */
132         {mdoc_bd_pre, NULL}, /* Bd */
133         {NULL, NULL}, /* Ed */
134         {mdoc_bl_pre, NULL}, /* Bl */
135         {NULL, NULL}, /* El */
136         {mdoc_it_pre, NULL}, /* It */
137         {mdoc_ad_pre, NULL}, /* Ad */ 
138         {mdoc_an_pre, NULL}, /* An */
139         {mdoc_ar_pre, NULL}, /* Ar */
140         {mdoc_cd_pre, NULL}, /* Cd */
141         {mdoc_fl_pre, NULL}, /* Cm */
142         {mdoc_dv_pre, NULL}, /* Dv */ 
143         {mdoc_er_pre, NULL}, /* Er */ 
144         {mdoc_ev_pre, NULL}, /* Ev */ 
145         {mdoc_ex_pre, NULL}, /* Ex */
146         {mdoc_fa_pre, NULL}, /* Fa */ 
147         {mdoc_fd_pre, NULL}, /* Fd */ 
148         {mdoc_fl_pre, NULL}, /* Fl */
149         {mdoc_fn_pre, NULL}, /* Fn */ 
150         {mdoc_ft_pre, NULL}, /* Ft */ 
151         {mdoc_ic_pre, NULL}, /* Ic */ 
152         {mdoc_in_pre, NULL}, /* In */ 
153         {mdoc_li_pre, NULL}, /* Li */
154         {mdoc_nd_pre, NULL}, /* Nd */ 
155         {mdoc_nm_pre, NULL}, /* Nm */ 
156         {mdoc_quote_pre, mdoc_quote_post}, /* Op */
157         {NULL, NULL}, /* Ot */
158         {mdoc_pa_pre, NULL}, /* Pa */
159         {mdoc_rv_pre, NULL}, /* Rv */
160         {NULL, NULL}, /* St */ 
161         {mdoc_va_pre, NULL}, /* Va */
162         {mdoc_vt_pre, NULL}, /* Vt */ 
163         {mdoc_xr_pre, NULL}, /* Xr */
164         {mdoc__x_pre, mdoc__x_post}, /* %A */
165         {mdoc__x_pre, mdoc__x_post}, /* %B */
166         {mdoc__x_pre, mdoc__x_post}, /* %D */
167         {mdoc__x_pre, mdoc__x_post}, /* %I */
168         {mdoc__x_pre, mdoc__x_post}, /* %J */
169         {mdoc__x_pre, mdoc__x_post}, /* %N */
170         {mdoc__x_pre, mdoc__x_post}, /* %O */
171         {mdoc__x_pre, mdoc__x_post}, /* %P */
172         {mdoc__x_pre, mdoc__x_post}, /* %R */
173         {mdoc__x_pre, mdoc__x_post}, /* %T */
174         {mdoc__x_pre, mdoc__x_post}, /* %V */
175         {NULL, NULL}, /* Ac */
176         {mdoc_quote_pre, mdoc_quote_post}, /* Ao */
177         {mdoc_quote_pre, mdoc_quote_post}, /* Aq */
178         {NULL, NULL}, /* At */
179         {NULL, NULL}, /* Bc */
180         {mdoc_bf_pre, NULL}, /* Bf */ 
181         {mdoc_quote_pre, mdoc_quote_post}, /* Bo */
182         {mdoc_quote_pre, mdoc_quote_post}, /* Bq */
183         {mdoc_xx_pre, NULL}, /* Bsx */
184         {mdoc_bx_pre, NULL}, /* Bx */
185         {NULL, NULL}, /* Db */
186         {NULL, NULL}, /* Dc */
187         {mdoc_quote_pre, mdoc_quote_post}, /* Do */
188         {mdoc_quote_pre, mdoc_quote_post}, /* Dq */
189         {NULL, NULL}, /* Ec */ /* FIXME: no space */
190         {NULL, NULL}, /* Ef */
191         {mdoc_em_pre, NULL}, /* Em */ 
192         {mdoc_quote_pre, mdoc_quote_post}, /* Eo */
193         {mdoc_xx_pre, NULL}, /* Fx */
194         {mdoc_ms_pre, NULL}, /* Ms */
195         {mdoc_igndelim_pre, NULL}, /* No */
196         {mdoc_ns_pre, NULL}, /* Ns */
197         {mdoc_xx_pre, NULL}, /* Nx */
198         {mdoc_xx_pre, NULL}, /* Ox */
199         {NULL, NULL}, /* Pc */
200         {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */
201         {mdoc_quote_pre, mdoc_quote_post}, /* Po */
202         {mdoc_quote_pre, mdoc_quote_post}, /* Pq */
203         {NULL, NULL}, /* Qc */
204         {mdoc_quote_pre, mdoc_quote_post}, /* Ql */
205         {mdoc_quote_pre, mdoc_quote_post}, /* Qo */
206         {mdoc_quote_pre, mdoc_quote_post}, /* Qq */
207         {NULL, NULL}, /* Re */
208         {mdoc_rs_pre, NULL}, /* Rs */
209         {NULL, NULL}, /* Sc */
210         {mdoc_quote_pre, mdoc_quote_post}, /* So */
211         {mdoc_quote_pre, mdoc_quote_post}, /* Sq */
212         {mdoc_sm_pre, NULL}, /* Sm */ 
213         {mdoc_sx_pre, NULL}, /* Sx */
214         {mdoc_sy_pre, NULL}, /* Sy */
215         {NULL, NULL}, /* Tn */
216         {mdoc_xx_pre, NULL}, /* Ux */
217         {NULL, NULL}, /* Xc */
218         {NULL, NULL}, /* Xo */
219         {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ 
220         {NULL, NULL}, /* Fc */ 
221         {mdoc_quote_pre, mdoc_quote_post}, /* Oo */
222         {NULL, NULL}, /* Oc */
223         {mdoc_bk_pre, mdoc_bk_post}, /* Bk */
224         {NULL, NULL}, /* Ek */
225         {mdoc_bt_pre, NULL}, /* Bt */
226         {NULL, NULL}, /* Hf */
227         {NULL, NULL}, /* Fr */
228         {mdoc_ud_pre, NULL}, /* Ud */
229         {mdoc_lb_pre, NULL}, /* Lb */
230         {mdoc_pp_pre, NULL}, /* Lp */ 
231         {mdoc_lk_pre, NULL}, /* Lk */ 
232         {mdoc_mt_pre, NULL}, /* Mt */ 
233         {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ 
234         {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ 
235         {NULL, NULL}, /* Brc */ 
236         {mdoc__x_pre, mdoc__x_post}, /* %C */ 
237         {NULL, NULL}, /* Es */  /* TODO */
238         {NULL, NULL}, /* En */  /* TODO */
239         {mdoc_xx_pre, NULL}, /* Dx */ 
240         {mdoc__x_pre, mdoc__x_post}, /* %Q */ 
241         {mdoc_sp_pre, NULL}, /* br */
242         {mdoc_sp_pre, NULL}, /* sp */ 
243         {mdoc__x_pre, mdoc__x_post}, /* %U */ 
244         {NULL, NULL}, /* Ta */ 
245 };
246
247 static  const char * const lists[LIST_MAX] = {
248         NULL,
249         "list-bul",
250         "list-col",
251         "list-dash",
252         "list-diag",
253         "list-enum",
254         "list-hang",
255         "list-hyph",
256         "list-inset",
257         "list-item",
258         "list-ohang",
259         "list-tag"
260 };
261
262 void
263 html_mdoc(void *arg, const struct mdoc *mdoc)
264 {
265
266         print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc),
267                         (struct html *)arg);
268         putchar('\n');
269 }
270
271
272 /*
273  * Calculate the scaling unit passed in a `-width' argument.  This uses
274  * either a native scaling unit (e.g., 1i, 2m) or the string length of
275  * the value.
276  */
277 static void
278 a2width(const char *p, struct roffsu *su)
279 {
280
281         if ( ! a2roffsu(p, su, SCALE_MAX)) {
282                 su->unit = SCALE_BU;
283                 su->scale = html_strlen(p);
284         }
285 }
286
287
288 /*
289  * See the same function in mdoc_term.c for documentation.
290  */
291 static void
292 synopsis_pre(struct html *h, const struct mdoc_node *n)
293 {
294
295         if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
296                 return;
297
298         if (n->prev->tok == n->tok && 
299                         MDOC_Fo != n->tok && 
300                         MDOC_Ft != n->tok && 
301                         MDOC_Fn != n->tok) {
302                 print_otag(h, TAG_BR, 0, NULL);
303                 return;
304         }
305
306         switch (n->prev->tok) {
307         case (MDOC_Fd):
308                 /* FALLTHROUGH */
309         case (MDOC_Fn):
310                 /* FALLTHROUGH */
311         case (MDOC_Fo):
312                 /* FALLTHROUGH */
313         case (MDOC_In):
314                 /* FALLTHROUGH */
315         case (MDOC_Vt):
316                 print_otag(h, TAG_P, 0, NULL);
317                 break;
318         case (MDOC_Ft):
319                 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
320                         print_otag(h, TAG_P, 0, NULL);
321                         break;
322                 }
323                 /* FALLTHROUGH */
324         default:
325                 print_otag(h, TAG_BR, 0, NULL);
326                 break;
327         }
328 }
329
330
331 /*
332  * Calculate the scaling unit passed in an `-offset' argument.  This
333  * uses either a native scaling unit (e.g., 1i, 2m), one of a set of
334  * predefined strings (indent, etc.), or the string length of the value.
335  */
336 static void
337 a2offs(const char *p, struct roffsu *su)
338 {
339
340         /* FIXME: "right"? */
341
342         if (0 == strcmp(p, "left"))
343                 SCALE_HS_INIT(su, 0);
344         else if (0 == strcmp(p, "indent"))
345                 SCALE_HS_INIT(su, INDENT);
346         else if (0 == strcmp(p, "indent-two"))
347                 SCALE_HS_INIT(su, INDENT * 2);
348         else if ( ! a2roffsu(p, su, SCALE_MAX))
349                 SCALE_HS_INIT(su, html_strlen(p));
350 }
351
352
353 static void
354 print_mdoc(MDOC_ARGS)
355 {
356         struct tag      *t, *tt;
357         struct htmlpair  tag;
358
359         PAIR_CLASS_INIT(&tag, "mandoc");
360
361         if ( ! (HTML_FRAGMENT & h->oflags)) {
362                 print_gen_decls(h);
363                 t = print_otag(h, TAG_HTML, 0, NULL);
364                 tt = print_otag(h, TAG_HEAD, 0, NULL);
365                 print_mdoc_head(meta, n, h);
366                 print_tagq(h, tt);
367                 print_otag(h, TAG_BODY, 0, NULL);
368                 print_otag(h, TAG_DIV, 1, &tag);
369         } else 
370                 t = print_otag(h, TAG_DIV, 1, &tag);
371
372         print_mdoc_nodelist(meta, n, h);
373         print_tagq(h, t);
374 }
375
376
377 /* ARGSUSED */
378 static void
379 print_mdoc_head(MDOC_ARGS)
380 {
381
382         print_gen_head(h);
383         bufinit(h);
384         bufcat_fmt(h, "%s(%s)", meta->title, meta->msec);
385
386         if (meta->arch)
387                 bufcat_fmt(h, " (%s)", meta->arch);
388
389         print_otag(h, TAG_TITLE, 0, NULL);
390         print_text(h, h->buf);
391 }
392
393
394 static void
395 print_mdoc_nodelist(MDOC_ARGS)
396 {
397
398         print_mdoc_node(meta, n, h);
399         if (n->next)
400                 print_mdoc_nodelist(meta, n->next, h);
401 }
402
403
404 static void
405 print_mdoc_node(MDOC_ARGS)
406 {
407         int              child;
408         struct tag      *t;
409
410         child = 1;
411         t = h->tags.head;
412
413         switch (n->type) {
414         case (MDOC_ROOT):
415                 child = mdoc_root_pre(meta, n, h);
416                 break;
417         case (MDOC_TEXT):
418                 /* No tables in this mode... */
419                 assert(NULL == h->tblt);
420
421                 /*
422                  * Make sure that if we're in a literal mode already
423                  * (i.e., within a <PRE>) don't print the newline.
424                  */
425                 if (' ' == *n->string && MDOC_LINE & n->flags)
426                         if ( ! (HTML_LITERAL & h->flags))
427                                 print_otag(h, TAG_BR, 0, NULL);
428                 if (MDOC_DELIMC & n->flags)
429                         h->flags |= HTML_NOSPACE;
430                 print_text(h, n->string);
431                 if (MDOC_DELIMO & n->flags)
432                         h->flags |= HTML_NOSPACE;
433                 return;
434         case (MDOC_EQN):
435                 print_eqn(h, n->eqn);
436                 break;
437         case (MDOC_TBL):
438                 /*
439                  * This will take care of initialising all of the table
440                  * state data for the first table, then tearing it down
441                  * for the last one.
442                  */
443                 print_tbl(h, n->span);
444                 return;
445         default:
446                 /*
447                  * Close out the current table, if it's open, and unset
448                  * the "meta" table state.  This will be reopened on the
449                  * next table element.
450                  */
451                 if (h->tblt) {
452                         print_tblclose(h);
453                         t = h->tags.head;
454                 }
455
456                 assert(NULL == h->tblt);
457                 if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
458                         child = (*mdocs[n->tok].pre)(meta, n, h);
459                 break;
460         }
461
462         if (HTML_KEEP & h->flags) {
463                 if (n->prev && n->prev->line != n->line) {
464                         h->flags &= ~HTML_KEEP;
465                         h->flags |= HTML_PREKEEP;
466                 } else if (NULL == n->prev) {
467                         if (n->parent && n->parent->line != n->line) {
468                                 h->flags &= ~HTML_KEEP;
469                                 h->flags |= HTML_PREKEEP;
470                         }
471                 }
472         }
473
474         if (child && n->child)
475                 print_mdoc_nodelist(meta, n->child, h);
476
477         print_stagq(h, t);
478
479         switch (n->type) {
480         case (MDOC_ROOT):
481                 mdoc_root_post(meta, n, h);
482                 break;
483         case (MDOC_EQN):
484                 break;
485         default:
486                 if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
487                         (*mdocs[n->tok].post)(meta, n, h);
488                 break;
489         }
490 }
491
492 /* ARGSUSED */
493 static void
494 mdoc_root_post(MDOC_ARGS)
495 {
496         struct htmlpair  tag[3];
497         struct tag      *t, *tt;
498
499         PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
500         PAIR_CLASS_INIT(&tag[1], "foot");
501         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
502         t = print_otag(h, TAG_TABLE, 3, tag);
503         PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
504         print_otag(h, TAG_COL, 1, tag);
505         print_otag(h, TAG_COL, 1, tag);
506
507         print_otag(h, TAG_TBODY, 0, NULL);
508
509         tt = print_otag(h, TAG_TR, 0, NULL);
510
511         PAIR_CLASS_INIT(&tag[0], "foot-date");
512         print_otag(h, TAG_TD, 1, tag);
513         print_text(h, meta->date);
514         print_stagq(h, tt);
515
516         PAIR_CLASS_INIT(&tag[0], "foot-os");
517         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
518         print_otag(h, TAG_TD, 2, tag);
519         print_text(h, meta->os);
520         print_tagq(h, t);
521 }
522
523
524 /* ARGSUSED */
525 static int
526 mdoc_root_pre(MDOC_ARGS)
527 {
528         struct htmlpair  tag[3];
529         struct tag      *t, *tt;
530         char             b[BUFSIZ], title[BUFSIZ];
531
532         strlcpy(b, meta->vol, BUFSIZ);
533
534         if (meta->arch) {
535                 strlcat(b, " (", BUFSIZ);
536                 strlcat(b, meta->arch, BUFSIZ);
537                 strlcat(b, ")", BUFSIZ);
538         }
539
540         snprintf(title, BUFSIZ - 1, "%s(%s)", meta->title, meta->msec);
541
542         PAIR_SUMMARY_INIT(&tag[0], "Document Header");
543         PAIR_CLASS_INIT(&tag[1], "head");
544         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
545         t = print_otag(h, TAG_TABLE, 3, tag);
546         PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
547         print_otag(h, TAG_COL, 1, tag);
548         print_otag(h, TAG_COL, 1, tag);
549         print_otag(h, TAG_COL, 1, tag);
550
551         print_otag(h, TAG_TBODY, 0, NULL);
552
553         tt = print_otag(h, TAG_TR, 0, NULL);
554
555         PAIR_CLASS_INIT(&tag[0], "head-ltitle");
556         print_otag(h, TAG_TD, 1, tag);
557         print_text(h, title);
558         print_stagq(h, tt);
559
560         PAIR_CLASS_INIT(&tag[0], "head-vol");
561         PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
562         print_otag(h, TAG_TD, 2, tag);
563         print_text(h, b);
564         print_stagq(h, tt);
565
566         PAIR_CLASS_INIT(&tag[0], "head-rtitle");
567         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
568         print_otag(h, TAG_TD, 2, tag);
569         print_text(h, title);
570         print_tagq(h, t);
571         return(1);
572 }
573
574
575 /* ARGSUSED */
576 static int
577 mdoc_sh_pre(MDOC_ARGS)
578 {
579         struct htmlpair  tag;
580
581         if (MDOC_BLOCK == n->type) {
582                 PAIR_CLASS_INIT(&tag, "section");
583                 print_otag(h, TAG_DIV, 1, &tag);
584                 return(1);
585         } else if (MDOC_BODY == n->type)
586                 return(1);
587
588         bufinit(h);
589         bufcat(h, "x");
590
591         for (n = n->child; n && MDOC_TEXT == n->type; ) {
592                 bufcat_id(h, n->string);
593                 if (NULL != (n = n->next))
594                         bufcat_id(h, " ");
595         }
596
597         if (NULL == n) {
598                 PAIR_ID_INIT(&tag, h->buf);
599                 print_otag(h, TAG_H1, 1, &tag);
600         } else
601                 print_otag(h, TAG_H1, 0, NULL);
602
603         return(1);
604 }
605
606 /* ARGSUSED */
607 static int
608 mdoc_ss_pre(MDOC_ARGS)
609 {
610         struct htmlpair  tag;
611
612         if (MDOC_BLOCK == n->type) {
613                 PAIR_CLASS_INIT(&tag, "subsection");
614                 print_otag(h, TAG_DIV, 1, &tag);
615                 return(1);
616         } else if (MDOC_BODY == n->type)
617                 return(1);
618
619         bufinit(h);
620         bufcat(h, "x");
621
622         for (n = n->child; n && MDOC_TEXT == n->type; ) {
623                 bufcat_id(h, n->string);
624                 if (NULL != (n = n->next))
625                         bufcat_id(h, " ");
626         }
627
628         if (NULL == n) {
629                 PAIR_ID_INIT(&tag, h->buf);
630                 print_otag(h, TAG_H2, 1, &tag);
631         } else
632                 print_otag(h, TAG_H2, 0, NULL);
633
634         return(1);
635 }
636
637
638 /* ARGSUSED */
639 static int
640 mdoc_fl_pre(MDOC_ARGS)
641 {
642         struct htmlpair  tag;
643
644         PAIR_CLASS_INIT(&tag, "flag");
645         print_otag(h, TAG_B, 1, &tag);
646
647         /* `Cm' has no leading hyphen. */
648
649         if (MDOC_Cm == n->tok)
650                 return(1);
651
652         print_text(h, "\\-");
653
654         if (n->child)
655                 h->flags |= HTML_NOSPACE;
656         else if (n->next && n->next->line == n->line)
657                 h->flags |= HTML_NOSPACE;
658
659         return(1);
660 }
661
662
663 /* ARGSUSED */
664 static int
665 mdoc_nd_pre(MDOC_ARGS)
666 {
667         struct htmlpair  tag;
668
669         if (MDOC_BODY != n->type)
670                 return(1);
671
672         /* XXX: this tag in theory can contain block elements. */
673
674         print_text(h, "\\(em");
675         PAIR_CLASS_INIT(&tag, "desc");
676         print_otag(h, TAG_SPAN, 1, &tag);
677         return(1);
678 }
679
680
681 static int
682 mdoc_nm_pre(MDOC_ARGS)
683 {
684         struct htmlpair  tag;
685         struct roffsu    su;
686         int              len;
687
688         switch (n->type) {
689         case (MDOC_ELEM):
690                 synopsis_pre(h, n);
691                 PAIR_CLASS_INIT(&tag, "name");
692                 print_otag(h, TAG_B, 1, &tag);
693                 if (NULL == n->child && meta->name)
694                         print_text(h, meta->name);
695                 return(1);
696         case (MDOC_HEAD):
697                 print_otag(h, TAG_TD, 0, NULL);
698                 if (NULL == n->child && meta->name)
699                         print_text(h, meta->name);
700                 return(1);
701         case (MDOC_BODY):
702                 print_otag(h, TAG_TD, 0, NULL);
703                 return(1);
704         default:
705                 break;
706         }
707
708         synopsis_pre(h, n);
709         PAIR_CLASS_INIT(&tag, "synopsis");
710         print_otag(h, TAG_TABLE, 1, &tag);
711
712         for (len = 0, n = n->child; n; n = n->next)
713                 if (MDOC_TEXT == n->type)
714                         len += html_strlen(n->string);
715
716         if (0 == len && meta->name)
717                 len = html_strlen(meta->name);
718
719         SCALE_HS_INIT(&su, (double)len);
720         bufinit(h);
721         bufcat_su(h, "width", &su);
722         PAIR_STYLE_INIT(&tag, h);
723         print_otag(h, TAG_COL, 1, &tag);
724         print_otag(h, TAG_COL, 0, NULL);
725         print_otag(h, TAG_TBODY, 0, NULL);
726         print_otag(h, TAG_TR, 0, NULL);
727         return(1);
728 }
729
730
731 /* ARGSUSED */
732 static int
733 mdoc_xr_pre(MDOC_ARGS)
734 {
735         struct htmlpair  tag[2];
736
737         if (NULL == n->child)
738                 return(0);
739
740         PAIR_CLASS_INIT(&tag[0], "link-man");
741
742         if (h->base_man) {
743                 buffmt_man(h, n->child->string, 
744                                 n->child->next ? 
745                                 n->child->next->string : NULL);
746                 PAIR_HREF_INIT(&tag[1], h->buf);
747                 print_otag(h, TAG_A, 2, tag);
748         } else
749                 print_otag(h, TAG_A, 1, tag);
750
751         n = n->child;
752         print_text(h, n->string);
753
754         if (NULL == (n = n->next))
755                 return(0);
756
757         h->flags |= HTML_NOSPACE;
758         print_text(h, "(");
759         h->flags |= HTML_NOSPACE;
760         print_text(h, n->string);
761         h->flags |= HTML_NOSPACE;
762         print_text(h, ")");
763         return(0);
764 }
765
766
767 /* ARGSUSED */
768 static int
769 mdoc_ns_pre(MDOC_ARGS)
770 {
771
772         if ( ! (MDOC_LINE & n->flags))
773                 h->flags |= HTML_NOSPACE;
774         return(1);
775 }
776
777
778 /* ARGSUSED */
779 static int
780 mdoc_ar_pre(MDOC_ARGS)
781 {
782         struct htmlpair tag;
783
784         PAIR_CLASS_INIT(&tag, "arg");
785         print_otag(h, TAG_I, 1, &tag);
786         return(1);
787 }
788
789
790 /* ARGSUSED */
791 static int
792 mdoc_xx_pre(MDOC_ARGS)
793 {
794         const char      *pp;
795         struct htmlpair  tag;
796         int              flags;
797
798         switch (n->tok) {
799         case (MDOC_Bsx):
800                 pp = "BSD/OS";
801                 break;
802         case (MDOC_Dx):
803                 pp = "DragonFly";
804                 break;
805         case (MDOC_Fx):
806                 pp = "FreeBSD";
807                 break;
808         case (MDOC_Nx):
809                 pp = "NetBSD";
810                 break;
811         case (MDOC_Ox):
812                 pp = "OpenBSD";
813                 break;
814         case (MDOC_Ux):
815                 pp = "UNIX";
816                 break;
817         default:
818                 return(1);
819         }
820
821         PAIR_CLASS_INIT(&tag, "unix");
822         print_otag(h, TAG_SPAN, 1, &tag);
823
824         print_text(h, pp);
825         if (n->child) {
826                 flags = h->flags;
827                 h->flags |= HTML_KEEP;
828                 print_text(h, n->child->string);
829                 h->flags = flags;
830         }
831         return(0);
832 }
833
834
835 /* ARGSUSED */
836 static int
837 mdoc_bx_pre(MDOC_ARGS)
838 {
839         struct htmlpair  tag;
840
841         PAIR_CLASS_INIT(&tag, "unix");
842         print_otag(h, TAG_SPAN, 1, &tag);
843
844         if (NULL != (n = n->child)) {
845                 print_text(h, n->string);
846                 h->flags |= HTML_NOSPACE;
847                 print_text(h, "BSD");
848         } else {
849                 print_text(h, "BSD");
850                 return(0);
851         }
852
853         if (NULL != (n = n->next)) {
854                 h->flags |= HTML_NOSPACE;
855                 print_text(h, "-");
856                 h->flags |= HTML_NOSPACE;
857                 print_text(h, n->string);
858         }
859
860         return(0);
861 }
862
863 /* ARGSUSED */
864 static int
865 mdoc_it_pre(MDOC_ARGS)
866 {
867         struct roffsu    su;
868         enum mdoc_list   type;
869         struct htmlpair  tag[2];
870         const struct mdoc_node *bl;
871
872         bl = n->parent;
873         while (bl && MDOC_Bl != bl->tok)
874                 bl = bl->parent;
875
876         assert(bl);
877
878         type = bl->norm->Bl.type;
879
880         assert(lists[type]);
881         PAIR_CLASS_INIT(&tag[0], lists[type]);
882
883         bufinit(h);
884
885         if (MDOC_HEAD == n->type) {
886                 switch (type) {
887                 case(LIST_bullet):
888                         /* FALLTHROUGH */
889                 case(LIST_dash):
890                         /* FALLTHROUGH */
891                 case(LIST_item):
892                         /* FALLTHROUGH */
893                 case(LIST_hyphen):
894                         /* FALLTHROUGH */
895                 case(LIST_enum):
896                         return(0);
897                 case(LIST_diag):
898                         /* FALLTHROUGH */
899                 case(LIST_hang):
900                         /* FALLTHROUGH */
901                 case(LIST_inset):
902                         /* FALLTHROUGH */
903                 case(LIST_ohang):
904                         /* FALLTHROUGH */
905                 case(LIST_tag):
906                         SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
907                         bufcat_su(h, "margin-top", &su);
908                         PAIR_STYLE_INIT(&tag[1], h);
909                         print_otag(h, TAG_DT, 2, tag);
910                         if (LIST_diag != type)
911                                 break;
912                         PAIR_CLASS_INIT(&tag[0], "diag");
913                         print_otag(h, TAG_B, 1, tag);
914                         break;
915                 case(LIST_column):
916                         break;
917                 default:
918                         break;
919                 }
920         } else if (MDOC_BODY == n->type) {
921                 switch (type) {
922                 case(LIST_bullet):
923                         /* FALLTHROUGH */
924                 case(LIST_hyphen):
925                         /* FALLTHROUGH */
926                 case(LIST_dash):
927                         /* FALLTHROUGH */
928                 case(LIST_enum):
929                         /* FALLTHROUGH */
930                 case(LIST_item):
931                         SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
932                         bufcat_su(h, "margin-top", &su);
933                         PAIR_STYLE_INIT(&tag[1], h);
934                         print_otag(h, TAG_LI, 2, tag);
935                         break;
936                 case(LIST_diag):
937                         /* FALLTHROUGH */
938                 case(LIST_hang):
939                         /* FALLTHROUGH */
940                 case(LIST_inset):
941                         /* FALLTHROUGH */
942                 case(LIST_ohang):
943                         /* FALLTHROUGH */
944                 case(LIST_tag):
945                         if (NULL == bl->norm->Bl.width) {
946                                 print_otag(h, TAG_DD, 1, tag);
947                                 break;
948                         }
949                         a2width(bl->norm->Bl.width, &su);
950                         bufcat_su(h, "margin-left", &su);
951                         PAIR_STYLE_INIT(&tag[1], h);
952                         print_otag(h, TAG_DD, 2, tag);
953                         break;
954                 case(LIST_column):
955                         SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
956                         bufcat_su(h, "margin-top", &su);
957                         PAIR_STYLE_INIT(&tag[1], h);
958                         print_otag(h, TAG_TD, 2, tag);
959                         break;
960                 default:
961                         break;
962                 }
963         } else {
964                 switch (type) {
965                 case (LIST_column):
966                         print_otag(h, TAG_TR, 1, tag);
967                         break;
968                 default:
969                         break;
970                 }
971         }
972
973         return(1);
974 }
975
976 /* ARGSUSED */
977 static int
978 mdoc_bl_pre(MDOC_ARGS)
979 {
980         int              i;
981         struct htmlpair  tag[3];
982         struct roffsu    su;
983         char             buf[BUFSIZ];
984
985         if (MDOC_BODY == n->type) {
986                 if (LIST_column == n->norm->Bl.type)
987                         print_otag(h, TAG_TBODY, 0, NULL);
988                 return(1);
989         }
990
991         if (MDOC_HEAD == n->type) {
992                 if (LIST_column != n->norm->Bl.type)
993                         return(0);
994
995                 /*
996                  * For each column, print out the <COL> tag with our
997                  * suggested width.  The last column gets min-width, as
998                  * in terminal mode it auto-sizes to the width of the
999                  * screen and we want to preserve that behaviour.
1000                  */
1001
1002                 for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
1003                         bufinit(h);
1004                         a2width(n->norm->Bl.cols[i], &su);
1005                         if (i < (int)n->norm->Bl.ncols - 1)
1006                                 bufcat_su(h, "width", &su);
1007                         else
1008                                 bufcat_su(h, "min-width", &su);
1009                         PAIR_STYLE_INIT(&tag[0], h);
1010                         print_otag(h, TAG_COL, 1, tag);
1011                 }
1012
1013                 return(0);
1014         }
1015
1016         SCALE_VS_INIT(&su, 0);
1017         bufinit(h);
1018         bufcat_su(h, "margin-top", &su);
1019         bufcat_su(h, "margin-bottom", &su);
1020         PAIR_STYLE_INIT(&tag[0], h);
1021
1022         assert(lists[n->norm->Bl.type]);
1023         strlcpy(buf, "list ", BUFSIZ);
1024         strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
1025         PAIR_INIT(&tag[1], ATTR_CLASS, buf);
1026
1027         /* Set the block's left-hand margin. */
1028
1029         if (n->norm->Bl.offs) {
1030                 a2offs(n->norm->Bl.offs, &su);
1031                 bufcat_su(h, "margin-left", &su);
1032         }
1033
1034         switch (n->norm->Bl.type) {
1035         case(LIST_bullet):
1036                 /* FALLTHROUGH */
1037         case(LIST_dash):
1038                 /* FALLTHROUGH */
1039         case(LIST_hyphen):
1040                 /* FALLTHROUGH */
1041         case(LIST_item):
1042                 print_otag(h, TAG_UL, 2, tag);
1043                 break;
1044         case(LIST_enum):
1045                 print_otag(h, TAG_OL, 2, tag);
1046                 break;
1047         case(LIST_diag):
1048                 /* FALLTHROUGH */
1049         case(LIST_hang):
1050                 /* FALLTHROUGH */
1051         case(LIST_inset):
1052                 /* FALLTHROUGH */
1053         case(LIST_ohang):
1054                 /* FALLTHROUGH */
1055         case(LIST_tag):
1056                 print_otag(h, TAG_DL, 2, tag);
1057                 break;
1058         case(LIST_column):
1059                 print_otag(h, TAG_TABLE, 2, tag);
1060                 break;
1061         default:
1062                 abort();
1063                 /* NOTREACHED */
1064         }
1065
1066         return(1);
1067 }
1068
1069 /* ARGSUSED */
1070 static int
1071 mdoc_ex_pre(MDOC_ARGS)
1072 {
1073         struct tag      *t;
1074         struct htmlpair  tag;
1075         int              nchild;
1076
1077         if (n->prev)
1078                 print_otag(h, TAG_BR, 0, NULL);
1079
1080         PAIR_CLASS_INIT(&tag, "utility");
1081
1082         print_text(h, "The");
1083
1084         nchild = n->nchild;
1085         for (n = n->child; n; n = n->next) {
1086                 assert(MDOC_TEXT == n->type);
1087
1088                 t = print_otag(h, TAG_B, 1, &tag);
1089                 print_text(h, n->string);
1090                 print_tagq(h, t);
1091
1092                 if (nchild > 2 && n->next) {
1093                         h->flags |= HTML_NOSPACE;
1094                         print_text(h, ",");
1095                 }
1096
1097                 if (n->next && NULL == n->next->next)
1098                         print_text(h, "and");
1099         }
1100
1101         if (nchild > 1)
1102                 print_text(h, "utilities exit");
1103         else
1104                 print_text(h, "utility exits");
1105
1106         print_text(h, "0 on success, and >0 if an error occurs.");
1107         return(0);
1108 }
1109
1110
1111 /* ARGSUSED */
1112 static int
1113 mdoc_em_pre(MDOC_ARGS)
1114 {
1115         struct htmlpair tag;
1116
1117         PAIR_CLASS_INIT(&tag, "emph");
1118         print_otag(h, TAG_SPAN, 1, &tag);
1119         return(1);
1120 }
1121
1122
1123 /* ARGSUSED */
1124 static int
1125 mdoc_d1_pre(MDOC_ARGS)
1126 {
1127         struct htmlpair  tag[2];
1128         struct roffsu    su;
1129
1130         if (MDOC_BLOCK != n->type)
1131                 return(1);
1132
1133         SCALE_VS_INIT(&su, 0);
1134         bufinit(h);
1135         bufcat_su(h, "margin-top", &su);
1136         bufcat_su(h, "margin-bottom", &su);
1137         PAIR_STYLE_INIT(&tag[0], h);
1138         print_otag(h, TAG_BLOCKQUOTE, 1, tag);
1139
1140         /* BLOCKQUOTE needs a block body. */
1141
1142         PAIR_CLASS_INIT(&tag[0], "display");
1143         print_otag(h, TAG_DIV, 1, tag);
1144
1145         if (MDOC_Dl == n->tok) {
1146                 PAIR_CLASS_INIT(&tag[0], "lit");
1147                 print_otag(h, TAG_CODE, 1, tag);
1148         } 
1149
1150         return(1);
1151 }
1152
1153
1154 /* ARGSUSED */
1155 static int
1156 mdoc_sx_pre(MDOC_ARGS)
1157 {
1158         struct htmlpair  tag[2];
1159
1160         bufinit(h);
1161         bufcat(h, "#x");
1162
1163         for (n = n->child; n; ) {
1164                 bufcat_id(h, n->string);
1165                 if (NULL != (n = n->next))
1166                         bufcat_id(h, " ");
1167         }
1168
1169         PAIR_CLASS_INIT(&tag[0], "link-sec");
1170         PAIR_HREF_INIT(&tag[1], h->buf);
1171
1172         print_otag(h, TAG_I, 1, tag);
1173         print_otag(h, TAG_A, 2, tag);
1174         return(1);
1175 }
1176
1177
1178 /* ARGSUSED */
1179 static int
1180 mdoc_bd_pre(MDOC_ARGS)
1181 {
1182         struct htmlpair          tag[2];
1183         int                      comp, sv;
1184         const struct mdoc_node  *nn;
1185         struct roffsu            su;
1186
1187         if (MDOC_HEAD == n->type)
1188                 return(0);
1189
1190         if (MDOC_BLOCK == n->type) {
1191                 comp = n->norm->Bd.comp;
1192                 for (nn = n; nn && ! comp; nn = nn->parent) {
1193                         if (MDOC_BLOCK != nn->type)
1194                                 continue;
1195                         if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
1196                                 comp = 1;
1197                         if (nn->prev)
1198                                 break;
1199                 }
1200                 if ( ! comp)
1201                         print_otag(h, TAG_P, 0, NULL);
1202                 return(1);
1203         }
1204
1205         SCALE_HS_INIT(&su, 0);
1206         if (n->norm->Bd.offs)
1207                 a2offs(n->norm->Bd.offs, &su);
1208         
1209         bufinit(h);
1210         bufcat_su(h, "margin-left", &su);
1211         PAIR_STYLE_INIT(&tag[0], h);
1212
1213         if (DISP_unfilled != n->norm->Bd.type && 
1214                         DISP_literal != n->norm->Bd.type) {
1215                 PAIR_CLASS_INIT(&tag[1], "display");
1216                 print_otag(h, TAG_DIV, 2, tag);
1217                 return(1);
1218         }
1219
1220         PAIR_CLASS_INIT(&tag[1], "lit display");
1221         print_otag(h, TAG_PRE, 2, tag);
1222
1223         /* This can be recursive: save & set our literal state. */
1224
1225         sv = h->flags & HTML_LITERAL;
1226         h->flags |= HTML_LITERAL;
1227
1228         for (nn = n->child; nn; nn = nn->next) {
1229                 print_mdoc_node(meta, nn, h);
1230                 /*
1231                  * If the printed node flushes its own line, then we
1232                  * needn't do it here as well.  This is hacky, but the
1233                  * notion of selective eoln whitespace is pretty dumb
1234                  * anyway, so don't sweat it.
1235                  */
1236                 switch (nn->tok) {
1237                 case (MDOC_Sm):
1238                         /* FALLTHROUGH */
1239                 case (MDOC_br):
1240                         /* FALLTHROUGH */
1241                 case (MDOC_sp):
1242                         /* FALLTHROUGH */
1243                 case (MDOC_Bl):
1244                         /* FALLTHROUGH */
1245                 case (MDOC_D1):
1246                         /* FALLTHROUGH */
1247                 case (MDOC_Dl):
1248                         /* FALLTHROUGH */
1249                 case (MDOC_Lp):
1250                         /* FALLTHROUGH */
1251                 case (MDOC_Pp):
1252                         continue;
1253                 default:
1254                         break;
1255                 }
1256                 if (nn->next && nn->next->line == nn->line)
1257                         continue;
1258                 else if (nn->next)
1259                         print_text(h, "\n");
1260
1261                 h->flags |= HTML_NOSPACE;
1262         }
1263
1264         if (0 == sv)
1265                 h->flags &= ~HTML_LITERAL;
1266
1267         return(0);
1268 }
1269
1270
1271 /* ARGSUSED */
1272 static int
1273 mdoc_pa_pre(MDOC_ARGS)
1274 {
1275         struct htmlpair tag;
1276
1277         PAIR_CLASS_INIT(&tag, "file");
1278         print_otag(h, TAG_I, 1, &tag);
1279         return(1);
1280 }
1281
1282
1283 /* ARGSUSED */
1284 static int
1285 mdoc_ad_pre(MDOC_ARGS)
1286 {
1287         struct htmlpair tag;
1288
1289         PAIR_CLASS_INIT(&tag, "addr");
1290         print_otag(h, TAG_I, 1, &tag);
1291         return(1);
1292 }
1293
1294
1295 /* ARGSUSED */
1296 static int
1297 mdoc_an_pre(MDOC_ARGS)
1298 {
1299         struct htmlpair tag;
1300
1301         /* TODO: -split and -nosplit (see termp_an_pre()). */
1302
1303         PAIR_CLASS_INIT(&tag, "author");
1304         print_otag(h, TAG_SPAN, 1, &tag);
1305         return(1);
1306 }
1307
1308
1309 /* ARGSUSED */
1310 static int
1311 mdoc_cd_pre(MDOC_ARGS)
1312 {
1313         struct htmlpair tag;
1314
1315         synopsis_pre(h, n);
1316         PAIR_CLASS_INIT(&tag, "config");
1317         print_otag(h, TAG_B, 1, &tag);
1318         return(1);
1319 }
1320
1321
1322 /* ARGSUSED */
1323 static int
1324 mdoc_dv_pre(MDOC_ARGS)
1325 {
1326         struct htmlpair tag;
1327
1328         PAIR_CLASS_INIT(&tag, "define");
1329         print_otag(h, TAG_SPAN, 1, &tag);
1330         return(1);
1331 }
1332
1333
1334 /* ARGSUSED */
1335 static int
1336 mdoc_ev_pre(MDOC_ARGS)
1337 {
1338         struct htmlpair tag;
1339
1340         PAIR_CLASS_INIT(&tag, "env");
1341         print_otag(h, TAG_SPAN, 1, &tag);
1342         return(1);
1343 }
1344
1345
1346 /* ARGSUSED */
1347 static int
1348 mdoc_er_pre(MDOC_ARGS)
1349 {
1350         struct htmlpair tag;
1351
1352         PAIR_CLASS_INIT(&tag, "errno");
1353         print_otag(h, TAG_SPAN, 1, &tag);
1354         return(1);
1355 }
1356
1357
1358 /* ARGSUSED */
1359 static int
1360 mdoc_fa_pre(MDOC_ARGS)
1361 {
1362         const struct mdoc_node  *nn;
1363         struct htmlpair          tag;
1364         struct tag              *t;
1365
1366         PAIR_CLASS_INIT(&tag, "farg");
1367         if (n->parent->tok != MDOC_Fo) {
1368                 print_otag(h, TAG_I, 1, &tag);
1369                 return(1);
1370         }
1371
1372         for (nn = n->child; nn; nn = nn->next) {
1373                 t = print_otag(h, TAG_I, 1, &tag);
1374                 print_text(h, nn->string);
1375                 print_tagq(h, t);
1376                 if (nn->next) {
1377                         h->flags |= HTML_NOSPACE;
1378                         print_text(h, ",");
1379                 }
1380         }
1381
1382         if (n->child && n->next && n->next->tok == MDOC_Fa) {
1383                 h->flags |= HTML_NOSPACE;
1384                 print_text(h, ",");
1385         }
1386
1387         return(0);
1388 }
1389
1390
1391 /* ARGSUSED */
1392 static int
1393 mdoc_fd_pre(MDOC_ARGS)
1394 {
1395         struct htmlpair  tag[2];
1396         char             buf[BUFSIZ];
1397         size_t           sz;
1398         int              i;
1399         struct tag      *t;
1400
1401         synopsis_pre(h, n);
1402
1403         if (NULL == (n = n->child))
1404                 return(0);
1405
1406         assert(MDOC_TEXT == n->type);
1407
1408         if (strcmp(n->string, "#include")) {
1409                 PAIR_CLASS_INIT(&tag[0], "macro");
1410                 print_otag(h, TAG_B, 1, tag);
1411                 return(1);
1412         }
1413
1414         PAIR_CLASS_INIT(&tag[0], "includes");
1415         print_otag(h, TAG_B, 1, tag);
1416         print_text(h, n->string);
1417
1418         if (NULL != (n = n->next)) {
1419                 assert(MDOC_TEXT == n->type);
1420                 strlcpy(buf, '<' == *n->string || '"' == *n->string ? 
1421                                 n->string + 1 : n->string, BUFSIZ);
1422
1423                 sz = strlen(buf);
1424                 if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
1425                         buf[sz - 1] = '\0';
1426
1427                 PAIR_CLASS_INIT(&tag[0], "link-includes");
1428                 
1429                 i = 1;
1430                 if (h->base_includes) {
1431                         buffmt_includes(h, buf);
1432                         PAIR_HREF_INIT(&tag[i], h->buf);
1433                         i++;
1434                 } 
1435
1436                 t = print_otag(h, TAG_A, i, tag);
1437                 print_text(h, n->string);
1438                 print_tagq(h, t);
1439
1440                 n = n->next;
1441         }
1442
1443         for ( ; n; n = n->next) {
1444                 assert(MDOC_TEXT == n->type);
1445                 print_text(h, n->string);
1446         }
1447
1448         return(0);
1449 }
1450
1451
1452 /* ARGSUSED */
1453 static int
1454 mdoc_vt_pre(MDOC_ARGS)
1455 {
1456         struct htmlpair  tag;
1457
1458         if (MDOC_BLOCK == n->type) {
1459                 synopsis_pre(h, n);
1460                 return(1);
1461         } else if (MDOC_ELEM == n->type) {
1462                 synopsis_pre(h, n);
1463         } else if (MDOC_HEAD == n->type)
1464                 return(0);
1465
1466         PAIR_CLASS_INIT(&tag, "type");
1467         print_otag(h, TAG_SPAN, 1, &tag);
1468         return(1);
1469 }
1470
1471
1472 /* ARGSUSED */
1473 static int
1474 mdoc_ft_pre(MDOC_ARGS)
1475 {
1476         struct htmlpair  tag;
1477
1478         synopsis_pre(h, n);
1479         PAIR_CLASS_INIT(&tag, "ftype");
1480         print_otag(h, TAG_I, 1, &tag);
1481         return(1);
1482 }
1483
1484
1485 /* ARGSUSED */
1486 static int
1487 mdoc_fn_pre(MDOC_ARGS)
1488 {
1489         struct tag      *t;
1490         struct htmlpair  tag[2];
1491         char             nbuf[BUFSIZ];
1492         const char      *sp, *ep;
1493         int              sz, i, pretty;
1494
1495         pretty = MDOC_SYNPRETTY & n->flags;
1496         synopsis_pre(h, n);
1497
1498         /* Split apart into type and name. */
1499         assert(n->child->string);
1500         sp = n->child->string;
1501
1502         ep = strchr(sp, ' ');
1503         if (NULL != ep) {
1504                 PAIR_CLASS_INIT(&tag[0], "ftype");
1505                 t = print_otag(h, TAG_I, 1, tag);
1506         
1507                 while (ep) {
1508                         sz = MIN((int)(ep - sp), BUFSIZ - 1);
1509                         (void)memcpy(nbuf, sp, (size_t)sz);
1510                         nbuf[sz] = '\0';
1511                         print_text(h, nbuf);
1512                         sp = ++ep;
1513                         ep = strchr(sp, ' ');
1514                 }
1515                 print_tagq(h, t);
1516         }
1517
1518         PAIR_CLASS_INIT(&tag[0], "fname");
1519
1520         /*
1521          * FIXME: only refer to IDs that we know exist.
1522          */
1523
1524 #if 0
1525         if (MDOC_SYNPRETTY & n->flags) {
1526                 nbuf[0] = '\0';
1527                 html_idcat(nbuf, sp, BUFSIZ);
1528                 PAIR_ID_INIT(&tag[1], nbuf);
1529         } else {
1530                 strlcpy(nbuf, "#", BUFSIZ);
1531                 html_idcat(nbuf, sp, BUFSIZ);
1532                 PAIR_HREF_INIT(&tag[1], nbuf);
1533         }
1534 #endif
1535
1536         t = print_otag(h, TAG_B, 1, tag);
1537
1538         if (sp) {
1539                 strlcpy(nbuf, sp, BUFSIZ);
1540                 print_text(h, nbuf);
1541         }
1542
1543         print_tagq(h, t);
1544
1545         h->flags |= HTML_NOSPACE;
1546         print_text(h, "(");
1547         h->flags |= HTML_NOSPACE;
1548
1549         PAIR_CLASS_INIT(&tag[0], "farg");
1550         bufinit(h);
1551         bufcat_style(h, "white-space", "nowrap");
1552         PAIR_STYLE_INIT(&tag[1], h);
1553
1554         for (n = n->child->next; n; n = n->next) {
1555                 i = 1;
1556                 if (MDOC_SYNPRETTY & n->flags)
1557                         i = 2;
1558                 t = print_otag(h, TAG_I, i, tag);
1559                 print_text(h, n->string);
1560                 print_tagq(h, t);
1561                 if (n->next) {
1562                         h->flags |= HTML_NOSPACE;
1563                         print_text(h, ",");
1564                 }
1565         }
1566
1567         h->flags |= HTML_NOSPACE;
1568         print_text(h, ")");
1569
1570         if (pretty) {
1571                 h->flags |= HTML_NOSPACE;
1572                 print_text(h, ";");
1573         }
1574
1575         return(0);
1576 }
1577
1578
1579 /* ARGSUSED */
1580 static int
1581 mdoc_sm_pre(MDOC_ARGS)
1582 {
1583
1584         assert(n->child && MDOC_TEXT == n->child->type);
1585         if (0 == strcmp("on", n->child->string)) {
1586                 /* 
1587                  * FIXME: no p->col to check.  Thus, if we have
1588                  *  .Bd -literal
1589                  *  .Sm off
1590                  *  1 2
1591                  *  .Sm on
1592                  *  3
1593                  *  .Ed
1594                  * the "3" is preceded by a space.
1595                  */
1596                 h->flags &= ~HTML_NOSPACE;
1597                 h->flags &= ~HTML_NONOSPACE;
1598         } else
1599                 h->flags |= HTML_NONOSPACE;
1600
1601         return(0);
1602 }
1603
1604 /* ARGSUSED */
1605 static int
1606 mdoc_pp_pre(MDOC_ARGS)
1607 {
1608
1609         print_otag(h, TAG_P, 0, NULL);
1610         return(0);
1611
1612 }
1613
1614 /* ARGSUSED */
1615 static int
1616 mdoc_sp_pre(MDOC_ARGS)
1617 {
1618         struct roffsu    su;
1619         struct htmlpair  tag;
1620
1621         SCALE_VS_INIT(&su, 1);
1622
1623         if (MDOC_sp == n->tok) {
1624                 if (NULL != (n = n->child))
1625                         if ( ! a2roffsu(n->string, &su, SCALE_VS))
1626                                 SCALE_VS_INIT(&su, atoi(n->string));
1627         } else
1628                 su.scale = 0;
1629
1630         bufinit(h);
1631         bufcat_su(h, "height", &su);
1632         PAIR_STYLE_INIT(&tag, h);
1633         print_otag(h, TAG_DIV, 1, &tag);
1634
1635         /* So the div isn't empty: */
1636         print_text(h, "\\~");
1637
1638         return(0);
1639
1640 }
1641
1642 /* ARGSUSED */
1643 static int
1644 mdoc_lk_pre(MDOC_ARGS)
1645 {
1646         struct htmlpair  tag[2];
1647
1648         if (NULL == (n = n->child))
1649                 return(0);
1650
1651         assert(MDOC_TEXT == n->type);
1652
1653         PAIR_CLASS_INIT(&tag[0], "link-ext");
1654         PAIR_HREF_INIT(&tag[1], n->string);
1655
1656         print_otag(h, TAG_A, 2, tag);
1657
1658         if (NULL == n->next)
1659                 print_text(h, n->string);
1660
1661         for (n = n->next; n; n = n->next)
1662                 print_text(h, n->string);
1663
1664         return(0);
1665 }
1666
1667
1668 /* ARGSUSED */
1669 static int
1670 mdoc_mt_pre(MDOC_ARGS)
1671 {
1672         struct htmlpair  tag[2];
1673         struct tag      *t;
1674
1675         PAIR_CLASS_INIT(&tag[0], "link-mail");
1676
1677         for (n = n->child; n; n = n->next) {
1678                 assert(MDOC_TEXT == n->type);
1679
1680                 bufinit(h);
1681                 bufcat(h, "mailto:");
1682                 bufcat(h, n->string);
1683
1684                 PAIR_HREF_INIT(&tag[1], h->buf);
1685                 t = print_otag(h, TAG_A, 2, tag);
1686                 print_text(h, n->string);
1687                 print_tagq(h, t);
1688         }
1689         
1690         return(0);
1691 }
1692
1693
1694 /* ARGSUSED */
1695 static int
1696 mdoc_fo_pre(MDOC_ARGS)
1697 {
1698         struct htmlpair  tag;
1699         struct tag      *t;
1700
1701         if (MDOC_BODY == n->type) {
1702                 h->flags |= HTML_NOSPACE;
1703                 print_text(h, "(");
1704                 h->flags |= HTML_NOSPACE;
1705                 return(1);
1706         } else if (MDOC_BLOCK == n->type) {
1707                 synopsis_pre(h, n);
1708                 return(1);
1709         }
1710
1711         /* XXX: we drop non-initial arguments as per groff. */
1712
1713         assert(n->child);
1714         assert(n->child->string);
1715
1716         PAIR_CLASS_INIT(&tag, "fname");
1717         t = print_otag(h, TAG_B, 1, &tag);
1718         print_text(h, n->child->string);
1719         print_tagq(h, t);
1720         return(0);
1721 }
1722
1723
1724 /* ARGSUSED */
1725 static void
1726 mdoc_fo_post(MDOC_ARGS)
1727 {
1728
1729         if (MDOC_BODY != n->type)
1730                 return;
1731         h->flags |= HTML_NOSPACE;
1732         print_text(h, ")");
1733         h->flags |= HTML_NOSPACE;
1734         print_text(h, ";");
1735 }
1736
1737
1738 /* ARGSUSED */
1739 static int
1740 mdoc_in_pre(MDOC_ARGS)
1741 {
1742         struct tag      *t;
1743         struct htmlpair  tag[2];
1744         int              i;
1745
1746         synopsis_pre(h, n);
1747
1748         PAIR_CLASS_INIT(&tag[0], "includes");
1749         print_otag(h, TAG_B, 1, tag);
1750
1751         /*
1752          * The first argument of the `In' gets special treatment as
1753          * being a linked value.  Subsequent values are printed
1754          * afterward.  groff does similarly.  This also handles the case
1755          * of no children.
1756          */
1757
1758         if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
1759                 print_text(h, "#include");
1760
1761         print_text(h, "<");
1762         h->flags |= HTML_NOSPACE;
1763
1764         if (NULL != (n = n->child)) {
1765                 assert(MDOC_TEXT == n->type);
1766
1767                 PAIR_CLASS_INIT(&tag[0], "link-includes");
1768
1769                 i = 1;
1770                 if (h->base_includes) {
1771                         buffmt_includes(h, n->string);
1772                         PAIR_HREF_INIT(&tag[i], h->buf);
1773                         i++;
1774                 } 
1775
1776                 t = print_otag(h, TAG_A, i, tag);
1777                 print_text(h, n->string);
1778                 print_tagq(h, t);
1779
1780                 n = n->next;
1781         }
1782
1783         h->flags |= HTML_NOSPACE;
1784         print_text(h, ">");
1785
1786         for ( ; n; n = n->next) {
1787                 assert(MDOC_TEXT == n->type);
1788                 print_text(h, n->string);
1789         }
1790
1791         return(0);
1792 }
1793
1794
1795 /* ARGSUSED */
1796 static int
1797 mdoc_ic_pre(MDOC_ARGS)
1798 {
1799         struct htmlpair tag;
1800
1801         PAIR_CLASS_INIT(&tag, "cmd");
1802         print_otag(h, TAG_B, 1, &tag);
1803         return(1);
1804 }
1805
1806
1807 /* ARGSUSED */
1808 static int
1809 mdoc_rv_pre(MDOC_ARGS)
1810 {
1811         struct htmlpair  tag;
1812         struct tag      *t;
1813         int              nchild;
1814
1815         if (n->prev)
1816                 print_otag(h, TAG_BR, 0, NULL);
1817
1818         PAIR_CLASS_INIT(&tag, "fname");
1819
1820         print_text(h, "The");
1821
1822         nchild = n->nchild;
1823         for (n = n->child; n; n = n->next) {
1824                 assert(MDOC_TEXT == n->type);
1825
1826                 t = print_otag(h, TAG_B, 1, &tag);
1827                 print_text(h, n->string);
1828                 print_tagq(h, t);
1829
1830                 h->flags |= HTML_NOSPACE;
1831                 print_text(h, "()");
1832
1833                 if (nchild > 2 && n->next) {
1834                         h->flags |= HTML_NOSPACE;
1835                         print_text(h, ",");
1836                 }
1837
1838                 if (n->next && NULL == n->next->next)
1839                         print_text(h, "and");
1840         }
1841
1842         if (nchild > 1)
1843                 print_text(h, "functions return");
1844         else
1845                 print_text(h, "function returns");
1846
1847         print_text(h, "the value 0 if successful; otherwise the value "
1848                         "-1 is returned and the global variable");
1849
1850         PAIR_CLASS_INIT(&tag, "var");
1851         t = print_otag(h, TAG_B, 1, &tag);
1852         print_text(h, "errno");
1853         print_tagq(h, t);
1854         print_text(h, "is set to indicate the error.");
1855         return(0);
1856 }
1857
1858
1859 /* ARGSUSED */
1860 static int
1861 mdoc_va_pre(MDOC_ARGS)
1862 {
1863         struct htmlpair tag;
1864
1865         PAIR_CLASS_INIT(&tag, "var");
1866         print_otag(h, TAG_B, 1, &tag);
1867         return(1);
1868 }
1869
1870
1871 /* ARGSUSED */
1872 static int
1873 mdoc_ap_pre(MDOC_ARGS)
1874 {
1875         
1876         h->flags |= HTML_NOSPACE;
1877         print_text(h, "\\(aq");
1878         h->flags |= HTML_NOSPACE;
1879         return(1);
1880 }
1881
1882
1883 /* ARGSUSED */
1884 static int
1885 mdoc_bf_pre(MDOC_ARGS)
1886 {
1887         struct htmlpair  tag[2];
1888         struct roffsu    su;
1889
1890         if (MDOC_HEAD == n->type)
1891                 return(0);
1892         else if (MDOC_BODY != n->type)
1893                 return(1);
1894
1895         if (FONT_Em == n->norm->Bf.font) 
1896                 PAIR_CLASS_INIT(&tag[0], "emph");
1897         else if (FONT_Sy == n->norm->Bf.font) 
1898                 PAIR_CLASS_INIT(&tag[0], "symb");
1899         else if (FONT_Li == n->norm->Bf.font) 
1900                 PAIR_CLASS_INIT(&tag[0], "lit");
1901         else
1902                 PAIR_CLASS_INIT(&tag[0], "none");
1903
1904         /* 
1905          * We want this to be inline-formatted, but needs to be div to
1906          * accept block children. 
1907          */
1908         bufinit(h);
1909         bufcat_style(h, "display", "inline");
1910         SCALE_HS_INIT(&su, 1);
1911         /* Needs a left-margin for spacing. */
1912         bufcat_su(h, "margin-left", &su);
1913         PAIR_STYLE_INIT(&tag[1], h);
1914         print_otag(h, TAG_DIV, 2, tag);
1915         return(1);
1916 }
1917
1918
1919 /* ARGSUSED */
1920 static int
1921 mdoc_ms_pre(MDOC_ARGS)
1922 {
1923         struct htmlpair tag;
1924
1925         PAIR_CLASS_INIT(&tag, "symb");
1926         print_otag(h, TAG_SPAN, 1, &tag);
1927         return(1);
1928 }
1929
1930
1931 /* ARGSUSED */
1932 static int
1933 mdoc_igndelim_pre(MDOC_ARGS)
1934 {
1935
1936         h->flags |= HTML_IGNDELIM;
1937         return(1);
1938 }
1939
1940
1941 /* ARGSUSED */
1942 static void
1943 mdoc_pf_post(MDOC_ARGS)
1944 {
1945
1946         h->flags |= HTML_NOSPACE;
1947 }
1948
1949
1950 /* ARGSUSED */
1951 static int
1952 mdoc_rs_pre(MDOC_ARGS)
1953 {
1954         struct htmlpair  tag;
1955
1956         if (MDOC_BLOCK != n->type)
1957                 return(1);
1958
1959         if (n->prev && SEC_SEE_ALSO == n->sec)
1960                 print_otag(h, TAG_P, 0, NULL);
1961
1962         PAIR_CLASS_INIT(&tag, "ref");
1963         print_otag(h, TAG_SPAN, 1, &tag);
1964         return(1);
1965 }
1966
1967
1968
1969 /* ARGSUSED */
1970 static int
1971 mdoc_li_pre(MDOC_ARGS)
1972 {
1973         struct htmlpair tag;
1974
1975         PAIR_CLASS_INIT(&tag, "lit");
1976         print_otag(h, TAG_CODE, 1, &tag);
1977         return(1);
1978 }
1979
1980
1981 /* ARGSUSED */
1982 static int
1983 mdoc_sy_pre(MDOC_ARGS)
1984 {
1985         struct htmlpair tag;
1986
1987         PAIR_CLASS_INIT(&tag, "symb");
1988         print_otag(h, TAG_SPAN, 1, &tag);
1989         return(1);
1990 }
1991
1992
1993 /* ARGSUSED */
1994 static int
1995 mdoc_bt_pre(MDOC_ARGS)
1996 {
1997
1998         print_text(h, "is currently in beta test.");
1999         return(0);
2000 }
2001
2002
2003 /* ARGSUSED */
2004 static int
2005 mdoc_ud_pre(MDOC_ARGS)
2006 {
2007
2008         print_text(h, "currently under development.");
2009         return(0);
2010 }
2011
2012
2013 /* ARGSUSED */
2014 static int
2015 mdoc_lb_pre(MDOC_ARGS)
2016 {
2017         struct htmlpair tag;
2018
2019         if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
2020                 print_otag(h, TAG_BR, 0, NULL);
2021
2022         PAIR_CLASS_INIT(&tag, "lib");
2023         print_otag(h, TAG_SPAN, 1, &tag);
2024         return(1);
2025 }
2026
2027
2028 /* ARGSUSED */
2029 static int
2030 mdoc__x_pre(MDOC_ARGS)
2031 {
2032         struct htmlpair tag[2];
2033         enum htmltag    t;
2034
2035         t = TAG_SPAN;
2036
2037         switch (n->tok) {
2038         case(MDOC__A):
2039                 PAIR_CLASS_INIT(&tag[0], "ref-auth");
2040                 if (n->prev && MDOC__A == n->prev->tok)
2041                         if (NULL == n->next || MDOC__A != n->next->tok)
2042                                 print_text(h, "and");
2043                 break;
2044         case(MDOC__B):
2045                 PAIR_CLASS_INIT(&tag[0], "ref-book");
2046                 t = TAG_I;
2047                 break;
2048         case(MDOC__C):
2049                 PAIR_CLASS_INIT(&tag[0], "ref-city");
2050                 break;
2051         case(MDOC__D):
2052                 PAIR_CLASS_INIT(&tag[0], "ref-date");
2053                 break;
2054         case(MDOC__I):
2055                 PAIR_CLASS_INIT(&tag[0], "ref-issue");
2056                 t = TAG_I;
2057                 break;
2058         case(MDOC__J):
2059                 PAIR_CLASS_INIT(&tag[0], "ref-jrnl");
2060                 t = TAG_I;
2061                 break;
2062         case(MDOC__N):
2063                 PAIR_CLASS_INIT(&tag[0], "ref-num");
2064                 break;
2065         case(MDOC__O):
2066                 PAIR_CLASS_INIT(&tag[0], "ref-opt");
2067                 break;
2068         case(MDOC__P):
2069                 PAIR_CLASS_INIT(&tag[0], "ref-page");
2070                 break;
2071         case(MDOC__Q):
2072                 PAIR_CLASS_INIT(&tag[0], "ref-corp");
2073                 break;
2074         case(MDOC__R):
2075                 PAIR_CLASS_INIT(&tag[0], "ref-rep");
2076                 break;
2077         case(MDOC__T):
2078                 PAIR_CLASS_INIT(&tag[0], "ref-title");
2079                 break;
2080         case(MDOC__U):
2081                 PAIR_CLASS_INIT(&tag[0], "link-ref");
2082                 break;
2083         case(MDOC__V):
2084                 PAIR_CLASS_INIT(&tag[0], "ref-vol");
2085                 break;
2086         default:
2087                 abort();
2088                 /* NOTREACHED */
2089         }
2090
2091         if (MDOC__U != n->tok) {
2092                 print_otag(h, t, 1, tag);
2093                 return(1);
2094         }
2095
2096         PAIR_HREF_INIT(&tag[1], n->child->string);
2097         print_otag(h, TAG_A, 2, tag);
2098
2099         return(1);
2100 }
2101
2102
2103 /* ARGSUSED */
2104 static void
2105 mdoc__x_post(MDOC_ARGS)
2106 {
2107
2108         if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
2109                 if (NULL == n->next->next || MDOC__A != n->next->next->tok)
2110                         if (NULL == n->prev || MDOC__A != n->prev->tok)
2111                                 return;
2112
2113         /* TODO: %U */
2114
2115         if (NULL == n->parent || MDOC_Rs != n->parent->tok)
2116                 return;
2117
2118         h->flags |= HTML_NOSPACE;
2119         print_text(h, n->next ? "," : ".");
2120 }
2121
2122
2123 /* ARGSUSED */
2124 static int
2125 mdoc_bk_pre(MDOC_ARGS)
2126 {
2127
2128         switch (n->type) {
2129         case (MDOC_BLOCK):
2130                 break;
2131         case (MDOC_HEAD):
2132                 return(0);
2133         case (MDOC_BODY):
2134                 if (n->parent->args || 0 == n->prev->nchild)
2135                         h->flags |= HTML_PREKEEP;
2136                 break;
2137         default:
2138                 abort();
2139                 /* NOTREACHED */
2140         }
2141
2142         return(1);
2143 }
2144
2145
2146 /* ARGSUSED */
2147 static void
2148 mdoc_bk_post(MDOC_ARGS)
2149 {
2150
2151         if (MDOC_BODY == n->type)
2152                 h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
2153 }
2154
2155
2156 /* ARGSUSED */
2157 static int
2158 mdoc_quote_pre(MDOC_ARGS)
2159 {
2160         struct htmlpair tag;
2161
2162         if (MDOC_BODY != n->type)
2163                 return(1);
2164
2165         switch (n->tok) {
2166         case (MDOC_Ao):
2167                 /* FALLTHROUGH */
2168         case (MDOC_Aq):
2169                 print_text(h, "\\(la");
2170                 break;
2171         case (MDOC_Bro):
2172                 /* FALLTHROUGH */
2173         case (MDOC_Brq):
2174                 print_text(h, "\\(lC");
2175                 break;
2176         case (MDOC_Bo):
2177                 /* FALLTHROUGH */
2178         case (MDOC_Bq):
2179                 print_text(h, "\\(lB");
2180                 break;
2181         case (MDOC_Oo):
2182                 /* FALLTHROUGH */
2183         case (MDOC_Op):
2184                 print_text(h, "\\(lB");
2185                 h->flags |= HTML_NOSPACE;
2186                 PAIR_CLASS_INIT(&tag, "opt");
2187                 print_otag(h, TAG_SPAN, 1, &tag);
2188                 break;
2189         case (MDOC_Eo):
2190                 break;
2191         case (MDOC_Do):
2192                 /* FALLTHROUGH */
2193         case (MDOC_Dq):
2194                 /* FALLTHROUGH */
2195         case (MDOC_Qo):
2196                 /* FALLTHROUGH */
2197         case (MDOC_Qq):
2198                 print_text(h, "\\(lq");
2199                 break;
2200         case (MDOC_Po):
2201                 /* FALLTHROUGH */
2202         case (MDOC_Pq):
2203                 print_text(h, "(");
2204                 break;
2205         case (MDOC_Ql):
2206                 print_text(h, "\\(oq");
2207                 h->flags |= HTML_NOSPACE;
2208                 PAIR_CLASS_INIT(&tag, "lit");
2209                 print_otag(h, TAG_CODE, 1, &tag);
2210                 break;
2211         case (MDOC_So):
2212                 /* FALLTHROUGH */
2213         case (MDOC_Sq):
2214                 print_text(h, "\\(oq");
2215                 break;
2216         default:
2217                 abort();
2218                 /* NOTREACHED */
2219         }
2220
2221         h->flags |= HTML_NOSPACE;
2222         return(1);
2223 }
2224
2225
2226 /* ARGSUSED */
2227 static void
2228 mdoc_quote_post(MDOC_ARGS)
2229 {
2230
2231         if (MDOC_BODY != n->type)
2232                 return;
2233
2234         h->flags |= HTML_NOSPACE;
2235
2236         switch (n->tok) {
2237         case (MDOC_Ao):
2238                 /* FALLTHROUGH */
2239         case (MDOC_Aq):
2240                 print_text(h, "\\(ra");
2241                 break;
2242         case (MDOC_Bro):
2243                 /* FALLTHROUGH */
2244         case (MDOC_Brq):
2245                 print_text(h, "\\(rC");
2246                 break;
2247         case (MDOC_Oo):
2248                 /* FALLTHROUGH */
2249         case (MDOC_Op):
2250                 /* FALLTHROUGH */
2251         case (MDOC_Bo):
2252                 /* FALLTHROUGH */
2253         case (MDOC_Bq):
2254                 print_text(h, "\\(rB");
2255                 break;
2256         case (MDOC_Eo):
2257                 break;
2258         case (MDOC_Qo):
2259                 /* FALLTHROUGH */
2260         case (MDOC_Qq):
2261                 /* FALLTHROUGH */
2262         case (MDOC_Do):
2263                 /* FALLTHROUGH */
2264         case (MDOC_Dq):
2265                 print_text(h, "\\(rq");
2266                 break;
2267         case (MDOC_Po):
2268                 /* FALLTHROUGH */
2269         case (MDOC_Pq):
2270                 print_text(h, ")");
2271                 break;
2272         case (MDOC_Ql):
2273                 /* FALLTHROUGH */
2274         case (MDOC_So):
2275                 /* FALLTHROUGH */
2276         case (MDOC_Sq):
2277                 print_text(h, "\\(cq");
2278                 break;
2279         default:
2280                 abort();
2281                 /* NOTREACHED */
2282         }
2283 }
2284
2285