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