xref: /dragonfly/contrib/mdocml/mdoc_html.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1 /* $Id: mdoc_html.c,v 1.342 2021/03/30 19:26:20 schwarze Exp $ */
2 /*
3  * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
4  * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * HTML formatter for mdoc(7) used by mandoc(1).
19  */
20 #include "config.h"
21 
22 #include <sys/types.h>
23 
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "mandoc_aux.h"
32 #include "mandoc.h"
33 #include "roff.h"
34 #include "mdoc.h"
35 #include "out.h"
36 #include "html.h"
37 #include "main.h"
38 
39 #define   MDOC_ARGS   const struct roff_meta *meta, \
40                                 struct roff_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    mdoc_html_act {
48           int                 (*pre)(MDOC_ARGS);
49           void                (*post)(MDOC_ARGS);
50 };
51 
52 static    void                  print_mdoc_head(const struct roff_meta *,
53                                         struct html *);
54 static    void                  print_mdoc_node(MDOC_ARGS);
55 static    void                  print_mdoc_nodelist(MDOC_ARGS);
56 static    void                  synopsis_pre(struct html *, struct roff_node *);
57 
58 static    void                  mdoc_root_post(const struct roff_meta *,
59                                         struct html *);
60 static    int                   mdoc_root_pre(const struct roff_meta *,
61                                         struct html *);
62 
63 static    void                  mdoc__x_post(MDOC_ARGS);
64 static    int                   mdoc__x_pre(MDOC_ARGS);
65 static    int                   mdoc_abort_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_cd_pre(MDOC_ARGS);
76 static    int                   mdoc_code_pre(MDOC_ARGS);
77 static    int                   mdoc_d1_pre(MDOC_ARGS);
78 static    int                   mdoc_fa_pre(MDOC_ARGS);
79 static    int                   mdoc_fd_pre(MDOC_ARGS);
80 static    int                   mdoc_fl_pre(MDOC_ARGS);
81 static    int                   mdoc_fn_pre(MDOC_ARGS);
82 static    int                   mdoc_ft_pre(MDOC_ARGS);
83 static    int                   mdoc_em_pre(MDOC_ARGS);
84 static    void                  mdoc_eo_post(MDOC_ARGS);
85 static    int                   mdoc_eo_pre(MDOC_ARGS);
86 static    int                   mdoc_ex_pre(MDOC_ARGS);
87 static    void                  mdoc_fo_post(MDOC_ARGS);
88 static    int                   mdoc_fo_pre(MDOC_ARGS);
89 static    int                   mdoc_igndelim_pre(MDOC_ARGS);
90 static    int                   mdoc_in_pre(MDOC_ARGS);
91 static    int                   mdoc_it_pre(MDOC_ARGS);
92 static    int                   mdoc_lb_pre(MDOC_ARGS);
93 static    int                   mdoc_lk_pre(MDOC_ARGS);
94 static    int                   mdoc_mt_pre(MDOC_ARGS);
95 static    int                   mdoc_nd_pre(MDOC_ARGS);
96 static    int                   mdoc_nm_pre(MDOC_ARGS);
97 static    int                   mdoc_no_pre(MDOC_ARGS);
98 static    int                   mdoc_ns_pre(MDOC_ARGS);
99 static    int                   mdoc_pa_pre(MDOC_ARGS);
100 static    void                  mdoc_pf_post(MDOC_ARGS);
101 static    int                   mdoc_pp_pre(MDOC_ARGS);
102 static    void                  mdoc_quote_post(MDOC_ARGS);
103 static    int                   mdoc_quote_pre(MDOC_ARGS);
104 static    int                   mdoc_rs_pre(MDOC_ARGS);
105 static    int                   mdoc_sh_pre(MDOC_ARGS);
106 static    int                   mdoc_skip_pre(MDOC_ARGS);
107 static    int                   mdoc_sm_pre(MDOC_ARGS);
108 static    int                   mdoc_ss_pre(MDOC_ARGS);
109 static    int                   mdoc_st_pre(MDOC_ARGS);
110 static    int                   mdoc_sx_pre(MDOC_ARGS);
111 static    int                   mdoc_sy_pre(MDOC_ARGS);
112 static    int                   mdoc_tg_pre(MDOC_ARGS);
113 static    int                   mdoc_va_pre(MDOC_ARGS);
114 static    int                   mdoc_vt_pre(MDOC_ARGS);
115 static    int                   mdoc_xr_pre(MDOC_ARGS);
116 static    int                   mdoc_xx_pre(MDOC_ARGS);
117 
118 static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = {
119           {NULL, NULL}, /* Dd */
120           {NULL, NULL}, /* Dt */
121           {NULL, NULL}, /* Os */
122           {mdoc_sh_pre, NULL }, /* Sh */
123           {mdoc_ss_pre, NULL }, /* Ss */
124           {mdoc_pp_pre, NULL}, /* Pp */
125           {mdoc_d1_pre, NULL}, /* D1 */
126           {mdoc_d1_pre, NULL}, /* Dl */
127           {mdoc_bd_pre, NULL}, /* Bd */
128           {NULL, NULL}, /* Ed */
129           {mdoc_bl_pre, NULL}, /* Bl */
130           {NULL, NULL}, /* El */
131           {mdoc_it_pre, NULL}, /* It */
132           {mdoc_ad_pre, NULL}, /* Ad */
133           {mdoc_an_pre, NULL}, /* An */
134           {mdoc_ap_pre, NULL}, /* Ap */
135           {mdoc_ar_pre, NULL}, /* Ar */
136           {mdoc_cd_pre, NULL}, /* Cd */
137           {mdoc_code_pre, NULL}, /* Cm */
138           {mdoc_code_pre, NULL}, /* Dv */
139           {mdoc_code_pre, NULL}, /* Er */
140           {mdoc_code_pre, NULL}, /* Ev */
141           {mdoc_ex_pre, NULL}, /* Ex */
142           {mdoc_fa_pre, NULL}, /* Fa */
143           {mdoc_fd_pre, NULL}, /* Fd */
144           {mdoc_fl_pre, NULL}, /* Fl */
145           {mdoc_fn_pre, NULL}, /* Fn */
146           {mdoc_ft_pre, NULL}, /* Ft */
147           {mdoc_code_pre, NULL}, /* Ic */
148           {mdoc_in_pre, NULL}, /* In */
149           {mdoc_code_pre, NULL}, /* Li */
150           {mdoc_nd_pre, NULL}, /* Nd */
151           {mdoc_nm_pre, NULL}, /* Nm */
152           {mdoc_quote_pre, mdoc_quote_post}, /* Op */
153           {mdoc_abort_pre, NULL}, /* Ot */
154           {mdoc_pa_pre, NULL}, /* Pa */
155           {mdoc_ex_pre, NULL}, /* Rv */
156           {mdoc_st_pre, NULL}, /* St */
157           {mdoc_va_pre, NULL}, /* Va */
158           {mdoc_vt_pre, NULL}, /* Vt */
159           {mdoc_xr_pre, NULL}, /* Xr */
160           {mdoc__x_pre, mdoc__x_post}, /* %A */
161           {mdoc__x_pre, mdoc__x_post}, /* %B */
162           {mdoc__x_pre, mdoc__x_post}, /* %D */
163           {mdoc__x_pre, mdoc__x_post}, /* %I */
164           {mdoc__x_pre, mdoc__x_post}, /* %J */
165           {mdoc__x_pre, mdoc__x_post}, /* %N */
166           {mdoc__x_pre, mdoc__x_post}, /* %O */
167           {mdoc__x_pre, mdoc__x_post}, /* %P */
168           {mdoc__x_pre, mdoc__x_post}, /* %R */
169           {mdoc__x_pre, mdoc__x_post}, /* %T */
170           {mdoc__x_pre, mdoc__x_post}, /* %V */
171           {NULL, NULL}, /* Ac */
172           {mdoc_quote_pre, mdoc_quote_post}, /* Ao */
173           {mdoc_quote_pre, mdoc_quote_post}, /* Aq */
174           {mdoc_xx_pre, NULL}, /* At */
175           {NULL, NULL}, /* Bc */
176           {mdoc_bf_pre, NULL}, /* Bf */
177           {mdoc_quote_pre, mdoc_quote_post}, /* Bo */
178           {mdoc_quote_pre, mdoc_quote_post}, /* Bq */
179           {mdoc_xx_pre, NULL}, /* Bsx */
180           {mdoc_xx_pre, NULL}, /* Bx */
181           {mdoc_skip_pre, NULL}, /* Db */
182           {NULL, NULL}, /* Dc */
183           {mdoc_quote_pre, mdoc_quote_post}, /* Do */
184           {mdoc_quote_pre, mdoc_quote_post}, /* Dq */
185           {NULL, NULL}, /* Ec */ /* FIXME: no space */
186           {NULL, NULL}, /* Ef */
187           {mdoc_em_pre, NULL}, /* Em */
188           {mdoc_eo_pre, mdoc_eo_post}, /* Eo */
189           {mdoc_xx_pre, NULL}, /* Fx */
190           {mdoc_no_pre, NULL}, /* Ms */
191           {mdoc_no_pre, NULL}, /* No */
192           {mdoc_ns_pre, NULL}, /* Ns */
193           {mdoc_xx_pre, NULL}, /* Nx */
194           {mdoc_xx_pre, NULL}, /* Ox */
195           {NULL, NULL}, /* Pc */
196           {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */
197           {mdoc_quote_pre, mdoc_quote_post}, /* Po */
198           {mdoc_quote_pre, mdoc_quote_post}, /* Pq */
199           {NULL, NULL}, /* Qc */
200           {mdoc_quote_pre, mdoc_quote_post}, /* Ql */
201           {mdoc_quote_pre, mdoc_quote_post}, /* Qo */
202           {mdoc_quote_pre, mdoc_quote_post}, /* Qq */
203           {NULL, NULL}, /* Re */
204           {mdoc_rs_pre, NULL}, /* Rs */
205           {NULL, NULL}, /* Sc */
206           {mdoc_quote_pre, mdoc_quote_post}, /* So */
207           {mdoc_quote_pre, mdoc_quote_post}, /* Sq */
208           {mdoc_sm_pre, NULL}, /* Sm */
209           {mdoc_sx_pre, NULL}, /* Sx */
210           {mdoc_sy_pre, NULL}, /* Sy */
211           {NULL, NULL}, /* Tn */
212           {mdoc_xx_pre, NULL}, /* Ux */
213           {NULL, NULL}, /* Xc */
214           {NULL, NULL}, /* Xo */
215           {mdoc_fo_pre, mdoc_fo_post}, /* Fo */
216           {NULL, NULL}, /* Fc */
217           {mdoc_quote_pre, mdoc_quote_post}, /* Oo */
218           {NULL, NULL}, /* Oc */
219           {mdoc_bk_pre, mdoc_bk_post}, /* Bk */
220           {NULL, NULL}, /* Ek */
221           {NULL, NULL}, /* Bt */
222           {NULL, NULL}, /* Hf */
223           {mdoc_em_pre, NULL}, /* Fr */
224           {NULL, NULL}, /* Ud */
225           {mdoc_lb_pre, NULL}, /* Lb */
226           {mdoc_abort_pre, NULL}, /* Lp */
227           {mdoc_lk_pre, NULL}, /* Lk */
228           {mdoc_mt_pre, NULL}, /* Mt */
229           {mdoc_quote_pre, mdoc_quote_post}, /* Brq */
230           {mdoc_quote_pre, mdoc_quote_post}, /* Bro */
231           {NULL, NULL}, /* Brc */
232           {mdoc__x_pre, mdoc__x_post}, /* %C */
233           {mdoc_skip_pre, NULL}, /* Es */
234           {mdoc_quote_pre, mdoc_quote_post}, /* En */
235           {mdoc_xx_pre, NULL}, /* Dx */
236           {mdoc__x_pre, mdoc__x_post}, /* %Q */
237           {mdoc__x_pre, mdoc__x_post}, /* %U */
238           {NULL, NULL}, /* Ta */
239           {mdoc_tg_pre, NULL}, /* Tg */
240 };
241 
242 
243 /*
244  * See the same function in mdoc_term.c for documentation.
245  */
246 static void
synopsis_pre(struct html * h,struct roff_node * n)247 synopsis_pre(struct html *h, struct roff_node *n)
248 {
249           struct roff_node *np;
250 
251           if ((n->flags & NODE_SYNPRETTY) == 0 ||
252               (np = roff_node_prev(n)) == NULL)
253                     return;
254 
255           if (np->tok == n->tok &&
256               MDOC_Fo != n->tok &&
257               MDOC_Ft != n->tok &&
258               MDOC_Fn != n->tok) {
259                     print_otag(h, TAG_BR, "");
260                     return;
261           }
262 
263           switch (np->tok) {
264           case MDOC_Fd:
265           case MDOC_Fn:
266           case MDOC_Fo:
267           case MDOC_In:
268           case MDOC_Vt:
269                     break;
270           case MDOC_Ft:
271                     if (n->tok != MDOC_Fn && n->tok != MDOC_Fo)
272                               break;
273                     /* FALLTHROUGH */
274           default:
275                     print_otag(h, TAG_BR, "");
276                     return;
277           }
278           html_close_paragraph(h);
279           print_otag(h, TAG_P, "c", "Pp");
280 }
281 
282 void
html_mdoc(void * arg,const struct roff_meta * mdoc)283 html_mdoc(void *arg, const struct roff_meta *mdoc)
284 {
285           struct html                   *h;
286           struct roff_node    *n;
287           struct tag                    *t;
288 
289           h = (struct html *)arg;
290           n = mdoc->first->child;
291 
292           if ((h->oflags & HTML_FRAGMENT) == 0) {
293                     print_gen_decls(h);
294                     print_otag(h, TAG_HTML, "");
295                     if (n != NULL && n->type == ROFFT_COMMENT)
296                               print_gen_comment(h, n);
297                     t = print_otag(h, TAG_HEAD, "");
298                     print_mdoc_head(mdoc, h);
299                     print_tagq(h, t);
300                     print_otag(h, TAG_BODY, "");
301           }
302 
303           mdoc_root_pre(mdoc, h);
304           t = print_otag(h, TAG_DIV, "c", "manual-text");
305           print_mdoc_nodelist(mdoc, n, h);
306           print_tagq(h, t);
307           mdoc_root_post(mdoc, h);
308           print_tagq(h, NULL);
309 }
310 
311 static void
print_mdoc_head(const struct roff_meta * meta,struct html * h)312 print_mdoc_head(const struct roff_meta *meta, struct html *h)
313 {
314           char      *cp;
315 
316           print_gen_head(h);
317 
318           if (meta->arch != NULL && meta->msec != NULL)
319                     mandoc_asprintf(&cp, "%s(%s) (%s)", meta->title,
320                         meta->msec, meta->arch);
321           else if (meta->msec != NULL)
322                     mandoc_asprintf(&cp, "%s(%s)", meta->title, meta->msec);
323           else if (meta->arch != NULL)
324                     mandoc_asprintf(&cp, "%s (%s)", meta->title, meta->arch);
325           else
326                     cp = mandoc_strdup(meta->title);
327 
328           print_otag(h, TAG_TITLE, "");
329           print_text(h, cp);
330           free(cp);
331 }
332 
333 static void
print_mdoc_nodelist(MDOC_ARGS)334 print_mdoc_nodelist(MDOC_ARGS)
335 {
336 
337           while (n != NULL) {
338                     print_mdoc_node(meta, n, h);
339                     n = n->next;
340           }
341 }
342 
343 static void
print_mdoc_node(MDOC_ARGS)344 print_mdoc_node(MDOC_ARGS)
345 {
346           struct tag          *t;
347           int                  child;
348 
349           if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
350                     return;
351 
352           if ((n->flags & NODE_NOFILL) == 0)
353                     html_fillmode(h, ROFF_fi);
354           else if (html_fillmode(h, ROFF_nf) == ROFF_nf &&
355               n->tok != ROFF_fi && n->flags & NODE_LINE)
356                     print_endline(h);
357 
358           child = 1;
359           n->flags &= ~NODE_ENDED;
360           switch (n->type) {
361           case ROFFT_TEXT:
362                     if (n->flags & NODE_LINE) {
363                               switch (*n->string) {
364                               case '\0':
365                                         h->col = 1;
366                                         print_endline(h);
367                                         return;
368                               case ' ':
369                                         if ((h->flags & HTML_NONEWLINE) == 0 &&
370                                             (n->flags & NODE_NOFILL) == 0)
371                                                   print_otag(h, TAG_BR, "");
372                                         break;
373                               default:
374                                         break;
375                               }
376                     }
377                     t = h->tag;
378                     t->refcnt++;
379                     if (n->flags & NODE_DELIMC)
380                               h->flags |= HTML_NOSPACE;
381                     if (n->flags & NODE_HREF)
382                               print_tagged_text(h, n->string, n);
383                     else
384                               print_text(h, n->string);
385                     if (n->flags & NODE_DELIMO)
386                               h->flags |= HTML_NOSPACE;
387                     break;
388           case ROFFT_EQN:
389                     t = h->tag;
390                     t->refcnt++;
391                     print_eqn(h, n->eqn);
392                     break;
393           case ROFFT_TBL:
394                     /*
395                      * This will take care of initialising all of the table
396                      * state data for the first table, then tearing it down
397                      * for the last one.
398                      */
399                     print_tbl(h, n->span);
400                     return;
401           default:
402                     /*
403                      * Close out the current table, if it's open, and unset
404                      * the "meta" table state.  This will be reopened on the
405                      * next table element.
406                      */
407                     if (h->tblt != NULL)
408                               print_tblclose(h);
409                     assert(h->tblt == NULL);
410                     t = h->tag;
411                     t->refcnt++;
412                     if (n->tok < ROFF_MAX) {
413                               roff_html_pre(h, n);
414                               t->refcnt--;
415                               print_stagq(h, t);
416                               return;
417                     }
418                     assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
419                     if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL &&
420                         (n->end == ENDBODY_NOT || n->child != NULL))
421                               child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta,
422                                   n, h);
423                     break;
424           }
425 
426           if (h->flags & HTML_KEEP && n->flags & NODE_LINE) {
427                     h->flags &= ~HTML_KEEP;
428                     h->flags |= HTML_PREKEEP;
429           }
430 
431           if (child && n->child != NULL)
432                     print_mdoc_nodelist(meta, n->child, h);
433 
434           t->refcnt--;
435           print_stagq(h, t);
436 
437           switch (n->type) {
438           case ROFFT_TEXT:
439           case ROFFT_EQN:
440                     break;
441           default:
442                     if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL ||
443                         n->flags & NODE_ENDED)
444                               break;
445                     (*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h);
446                     if (n->end != ENDBODY_NOT)
447                               n->body->flags |= NODE_ENDED;
448                     break;
449           }
450 }
451 
452 static void
mdoc_root_post(const struct roff_meta * meta,struct html * h)453 mdoc_root_post(const struct roff_meta *meta, struct html *h)
454 {
455           struct tag          *t, *tt;
456 
457           t = print_otag(h, TAG_TABLE, "c", "foot");
458           tt = print_otag(h, TAG_TR, "");
459 
460           print_otag(h, TAG_TD, "c", "foot-date");
461           print_text(h, meta->date);
462           print_stagq(h, tt);
463 
464           print_otag(h, TAG_TD, "c", "foot-os");
465           print_text(h, meta->os);
466           print_tagq(h, t);
467 }
468 
469 static int
mdoc_root_pre(const struct roff_meta * meta,struct html * h)470 mdoc_root_pre(const struct roff_meta *meta, struct html *h)
471 {
472           struct tag          *t, *tt;
473           char                *volume, *title;
474 
475           if (NULL == meta->arch)
476                     volume = mandoc_strdup(meta->vol);
477           else
478                     mandoc_asprintf(&volume, "%s (%s)",
479                         meta->vol, meta->arch);
480 
481           if (NULL == meta->msec)
482                     title = mandoc_strdup(meta->title);
483           else
484                     mandoc_asprintf(&title, "%s(%s)",
485                         meta->title, meta->msec);
486 
487           t = print_otag(h, TAG_TABLE, "c", "head");
488           tt = print_otag(h, TAG_TR, "");
489 
490           print_otag(h, TAG_TD, "c", "head-ltitle");
491           print_text(h, title);
492           print_stagq(h, tt);
493 
494           print_otag(h, TAG_TD, "c", "head-vol");
495           print_text(h, volume);
496           print_stagq(h, tt);
497 
498           print_otag(h, TAG_TD, "c", "head-rtitle");
499           print_text(h, title);
500           print_tagq(h, t);
501 
502           free(title);
503           free(volume);
504           return 1;
505 }
506 
507 static int
mdoc_code_pre(MDOC_ARGS)508 mdoc_code_pre(MDOC_ARGS)
509 {
510           print_otag_id(h, TAG_CODE, roff_name[n->tok], n);
511           return 1;
512 }
513 
514 static int
mdoc_sh_pre(MDOC_ARGS)515 mdoc_sh_pre(MDOC_ARGS)
516 {
517           struct roff_node    *sn, *subn;
518           struct tag                    *t, *tsec, *tsub;
519           char                          *id;
520           int                            sc;
521 
522           switch (n->type) {
523           case ROFFT_BLOCK:
524                     html_close_paragraph(h);
525                     if ((h->oflags & HTML_TOC) == 0 ||
526                         h->flags & HTML_TOCDONE ||
527                         n->sec <= SEC_SYNOPSIS) {
528                               print_otag(h, TAG_SECTION, "c", "Sh");
529                               break;
530                     }
531                     h->flags |= HTML_TOCDONE;
532                     sc = 0;
533                     for (sn = n->next; sn != NULL; sn = sn->next)
534                               if (sn->sec == SEC_CUSTOM)
535                                         if (++sc == 2)
536                                                   break;
537                     if (sc < 2)
538                               break;
539                     t = print_otag(h, TAG_H1, "c", "Sh");
540                     print_text(h, "TABLE OF CONTENTS");
541                     print_tagq(h, t);
542                     t = print_otag(h, TAG_UL, "c", "Bl-compact");
543                     for (sn = n; sn != NULL; sn = sn->next) {
544                               tsec = print_otag(h, TAG_LI, "");
545                               id = html_make_id(sn->head, 0);
546                               tsub = print_otag(h, TAG_A, "hR", id);
547                               free(id);
548                               print_mdoc_nodelist(meta, sn->head->child, h);
549                               print_tagq(h, tsub);
550                               tsub = NULL;
551                               for (subn = sn->body->child; subn != NULL;
552                                   subn = subn->next) {
553                                         if (subn->tok != MDOC_Ss)
554                                                   continue;
555                                         id = html_make_id(subn->head, 0);
556                                         if (id == NULL)
557                                                   continue;
558                                         if (tsub == NULL)
559                                                   print_otag(h, TAG_UL,
560                                                       "c", "Bl-compact");
561                                         tsub = print_otag(h, TAG_LI, "");
562                                         print_otag(h, TAG_A, "hR", id);
563                                         free(id);
564                                         print_mdoc_nodelist(meta,
565                                             subn->head->child, h);
566                                         print_tagq(h, tsub);
567                               }
568                               print_tagq(h, tsec);
569                     }
570                     print_tagq(h, t);
571                     print_otag(h, TAG_SECTION, "c", "Sh");
572                     break;
573           case ROFFT_HEAD:
574                     print_otag_id(h, TAG_H1, "Sh", n);
575                     break;
576           case ROFFT_BODY:
577                     if (n->sec == SEC_AUTHORS)
578                               h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT);
579                     break;
580           default:
581                     break;
582           }
583           return 1;
584 }
585 
586 static int
mdoc_ss_pre(MDOC_ARGS)587 mdoc_ss_pre(MDOC_ARGS)
588 {
589           switch (n->type) {
590           case ROFFT_BLOCK:
591                     html_close_paragraph(h);
592                     print_otag(h, TAG_SECTION, "c", "Ss");
593                     break;
594           case ROFFT_HEAD:
595                     print_otag_id(h, TAG_H2, "Ss", n);
596                     break;
597           case ROFFT_BODY:
598                     break;
599           default:
600                     abort();
601           }
602           return 1;
603 }
604 
605 static int
mdoc_fl_pre(MDOC_ARGS)606 mdoc_fl_pre(MDOC_ARGS)
607 {
608           struct roff_node    *nn;
609 
610           print_otag_id(h, TAG_CODE, "Fl", n);
611           print_text(h, "\\-");
612           if (n->child != NULL ||
613               ((nn = roff_node_next(n)) != NULL &&
614                nn->type != ROFFT_TEXT &&
615                (nn->flags & NODE_LINE) == 0))
616                     h->flags |= HTML_NOSPACE;
617 
618           return 1;
619 }
620 
621 static int
mdoc_nd_pre(MDOC_ARGS)622 mdoc_nd_pre(MDOC_ARGS)
623 {
624           switch (n->type) {
625           case ROFFT_BLOCK:
626                     return 1;
627           case ROFFT_HEAD:
628                     return 0;
629           case ROFFT_BODY:
630                     break;
631           default:
632                     abort();
633           }
634           print_text(h, "\\(em");
635           print_otag(h, TAG_SPAN, "c", "Nd");
636           return 1;
637 }
638 
639 static int
mdoc_nm_pre(MDOC_ARGS)640 mdoc_nm_pre(MDOC_ARGS)
641 {
642           switch (n->type) {
643           case ROFFT_BLOCK:
644                     break;
645           case ROFFT_HEAD:
646                     print_otag(h, TAG_TD, "");
647                     /* FALLTHROUGH */
648           case ROFFT_ELEM:
649                     print_otag(h, TAG_CODE, "c", "Nm");
650                     return 1;
651           case ROFFT_BODY:
652                     print_otag(h, TAG_TD, "");
653                     return 1;
654           default:
655                     abort();
656           }
657           html_close_paragraph(h);
658           synopsis_pre(h, n);
659           print_otag(h, TAG_TABLE, "c", "Nm");
660           print_otag(h, TAG_TR, "");
661           return 1;
662 }
663 
664 static int
mdoc_xr_pre(MDOC_ARGS)665 mdoc_xr_pre(MDOC_ARGS)
666 {
667           if (NULL == n->child)
668                     return 0;
669 
670           if (h->base_man1)
671                     print_otag(h, TAG_A, "chM", "Xr",
672                         n->child->string, n->child->next == NULL ?
673                         NULL : n->child->next->string);
674           else
675                     print_otag(h, TAG_A, "c", "Xr");
676 
677           n = n->child;
678           print_text(h, n->string);
679 
680           if (NULL == (n = n->next))
681                     return 0;
682 
683           h->flags |= HTML_NOSPACE;
684           print_text(h, "(");
685           h->flags |= HTML_NOSPACE;
686           print_text(h, n->string);
687           h->flags |= HTML_NOSPACE;
688           print_text(h, ")");
689           return 0;
690 }
691 
692 static int
mdoc_tg_pre(MDOC_ARGS)693 mdoc_tg_pre(MDOC_ARGS)
694 {
695           char      *id;
696 
697           if ((id = html_make_id(n, 1)) != NULL) {
698                     print_tagq(h, print_otag(h, TAG_MARK, "i", id));
699                     free(id);
700           }
701           return 0;
702 }
703 
704 static int
mdoc_ns_pre(MDOC_ARGS)705 mdoc_ns_pre(MDOC_ARGS)
706 {
707 
708           if ( ! (NODE_LINE & n->flags))
709                     h->flags |= HTML_NOSPACE;
710           return 1;
711 }
712 
713 static int
mdoc_ar_pre(MDOC_ARGS)714 mdoc_ar_pre(MDOC_ARGS)
715 {
716           print_otag(h, TAG_VAR, "c", "Ar");
717           return 1;
718 }
719 
720 static int
mdoc_xx_pre(MDOC_ARGS)721 mdoc_xx_pre(MDOC_ARGS)
722 {
723           print_otag(h, TAG_SPAN, "c", "Ux");
724           return 1;
725 }
726 
727 static int
mdoc_it_pre(MDOC_ARGS)728 mdoc_it_pre(MDOC_ARGS)
729 {
730           const struct roff_node        *bl;
731           enum mdoc_list                 type;
732 
733           bl = n->parent;
734           while (bl->tok != MDOC_Bl)
735                     bl = bl->parent;
736           type = bl->norm->Bl.type;
737 
738           switch (type) {
739           case LIST_bullet:
740           case LIST_dash:
741           case LIST_hyphen:
742           case LIST_item:
743           case LIST_enum:
744                     switch (n->type) {
745                     case ROFFT_HEAD:
746                               return 0;
747                     case ROFFT_BODY:
748                               print_otag_id(h, TAG_LI, NULL, n);
749                               break;
750                     default:
751                               break;
752                     }
753                     break;
754           case LIST_diag:
755           case LIST_hang:
756           case LIST_inset:
757           case LIST_ohang:
758                     switch (n->type) {
759                     case ROFFT_HEAD:
760                               print_otag_id(h, TAG_DT, NULL, n);
761                               break;
762                     case ROFFT_BODY:
763                               print_otag(h, TAG_DD, "");
764                               break;
765                     default:
766                               break;
767                     }
768                     break;
769           case LIST_tag:
770                     switch (n->type) {
771                     case ROFFT_HEAD:
772                               print_otag_id(h, TAG_DT, NULL, n);
773                               break;
774                     case ROFFT_BODY:
775                               if (n->child == NULL) {
776                                         print_otag(h, TAG_DD, "s", "width", "auto");
777                                         print_text(h, "\\ ");
778                               } else
779                                         print_otag(h, TAG_DD, "");
780                               break;
781                     default:
782                               break;
783                     }
784                     break;
785           case LIST_column:
786                     switch (n->type) {
787                     case ROFFT_HEAD:
788                               break;
789                     case ROFFT_BODY:
790                               print_otag(h, TAG_TD, "");
791                               break;
792                     default:
793                               print_otag_id(h, TAG_TR, NULL, n);
794                     }
795           default:
796                     break;
797           }
798 
799           return 1;
800 }
801 
802 static int
mdoc_bl_pre(MDOC_ARGS)803 mdoc_bl_pre(MDOC_ARGS)
804 {
805           char                 cattr[32];
806           struct mdoc_bl      *bl;
807           enum htmltag         elemtype;
808 
809           switch (n->type) {
810           case ROFFT_BLOCK:
811                     html_close_paragraph(h);
812                     break;
813           case ROFFT_HEAD:
814                     return 0;
815           case ROFFT_BODY:
816                     return 1;
817           default:
818                     abort();
819           }
820 
821           bl = &n->norm->Bl;
822           switch (bl->type) {
823           case LIST_bullet:
824                     elemtype = TAG_UL;
825                     (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr));
826                     break;
827           case LIST_dash:
828           case LIST_hyphen:
829                     elemtype = TAG_UL;
830                     (void)strlcpy(cattr, "Bl-dash", sizeof(cattr));
831                     break;
832           case LIST_item:
833                     elemtype = TAG_UL;
834                     (void)strlcpy(cattr, "Bl-item", sizeof(cattr));
835                     break;
836           case LIST_enum:
837                     elemtype = TAG_OL;
838                     (void)strlcpy(cattr, "Bl-enum", sizeof(cattr));
839                     break;
840           case LIST_diag:
841                     elemtype = TAG_DL;
842                     (void)strlcpy(cattr, "Bl-diag", sizeof(cattr));
843                     break;
844           case LIST_hang:
845                     elemtype = TAG_DL;
846                     (void)strlcpy(cattr, "Bl-hang", sizeof(cattr));
847                     break;
848           case LIST_inset:
849                     elemtype = TAG_DL;
850                     (void)strlcpy(cattr, "Bl-inset", sizeof(cattr));
851                     break;
852           case LIST_ohang:
853                     elemtype = TAG_DL;
854                     (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr));
855                     break;
856           case LIST_tag:
857                     if (bl->offs)
858                               print_otag(h, TAG_DIV, "c", "Bd-indent");
859                     print_otag_id(h, TAG_DL,
860                         bl->comp ? "Bl-tag Bl-compact" : "Bl-tag", n->body);
861                     return 1;
862           case LIST_column:
863                     elemtype = TAG_TABLE;
864                     (void)strlcpy(cattr, "Bl-column", sizeof(cattr));
865                     break;
866           default:
867                     abort();
868           }
869           if (bl->offs != NULL)
870                     (void)strlcat(cattr, " Bd-indent", sizeof(cattr));
871           if (bl->comp)
872                     (void)strlcat(cattr, " Bl-compact", sizeof(cattr));
873           print_otag_id(h, elemtype, cattr, n->body);
874           return 1;
875 }
876 
877 static int
mdoc_ex_pre(MDOC_ARGS)878 mdoc_ex_pre(MDOC_ARGS)
879 {
880           if (roff_node_prev(n) != NULL)
881                     print_otag(h, TAG_BR, "");
882           return 1;
883 }
884 
885 static int
mdoc_st_pre(MDOC_ARGS)886 mdoc_st_pre(MDOC_ARGS)
887 {
888           print_otag(h, TAG_SPAN, "c", "St");
889           return 1;
890 }
891 
892 static int
mdoc_em_pre(MDOC_ARGS)893 mdoc_em_pre(MDOC_ARGS)
894 {
895           print_otag_id(h, TAG_I, "Em", n);
896           return 1;
897 }
898 
899 static int
mdoc_d1_pre(MDOC_ARGS)900 mdoc_d1_pre(MDOC_ARGS)
901 {
902           switch (n->type) {
903           case ROFFT_BLOCK:
904                     html_close_paragraph(h);
905                     return 1;
906           case ROFFT_HEAD:
907                     return 0;
908           case ROFFT_BODY:
909                     break;
910           default:
911                     abort();
912           }
913           print_otag_id(h, TAG_DIV, "Bd Bd-indent", n);
914           if (n->tok == MDOC_Dl)
915                     print_otag(h, TAG_CODE, "c", "Li");
916           return 1;
917 }
918 
919 static int
mdoc_sx_pre(MDOC_ARGS)920 mdoc_sx_pre(MDOC_ARGS)
921 {
922           char      *id;
923 
924           id = html_make_id(n, 0);
925           print_otag(h, TAG_A, "chR", "Sx", id);
926           free(id);
927           return 1;
928 }
929 
930 static int
mdoc_bd_pre(MDOC_ARGS)931 mdoc_bd_pre(MDOC_ARGS)
932 {
933           char                           buf[20];
934           struct roff_node    *nn;
935           int                            comp;
936 
937           switch (n->type) {
938           case ROFFT_BLOCK:
939                     html_close_paragraph(h);
940                     return 1;
941           case ROFFT_HEAD:
942                     return 0;
943           case ROFFT_BODY:
944                     break;
945           default:
946                     abort();
947           }
948 
949           /* Handle preceding whitespace. */
950 
951           comp = n->norm->Bd.comp;
952           for (nn = n; nn != NULL && comp == 0; nn = nn->parent) {
953                     if (nn->type != ROFFT_BLOCK)
954                               continue;
955                     if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
956                               comp = 1;
957                     if (roff_node_prev(nn) != NULL)
958                               break;
959           }
960           (void)strlcpy(buf, "Bd", sizeof(buf));
961           if (comp == 0)
962                     (void)strlcat(buf, " Pp", sizeof(buf));
963 
964           /* Handle the -offset argument. */
965 
966           if (n->norm->Bd.offs != NULL &&
967               strcmp(n->norm->Bd.offs, "left") != 0)
968                     (void)strlcat(buf, " Bd-indent", sizeof(buf));
969 
970           if (n->norm->Bd.type == DISP_literal)
971                     (void)strlcat(buf, " Li", sizeof(buf));
972 
973           print_otag_id(h, TAG_DIV, buf, n);
974           return 1;
975 }
976 
977 static int
mdoc_pa_pre(MDOC_ARGS)978 mdoc_pa_pre(MDOC_ARGS)
979 {
980           print_otag(h, TAG_SPAN, "c", "Pa");
981           return 1;
982 }
983 
984 static int
mdoc_ad_pre(MDOC_ARGS)985 mdoc_ad_pre(MDOC_ARGS)
986 {
987           print_otag(h, TAG_SPAN, "c", "Ad");
988           return 1;
989 }
990 
991 static int
mdoc_an_pre(MDOC_ARGS)992 mdoc_an_pre(MDOC_ARGS)
993 {
994           if (n->norm->An.auth == AUTH_split) {
995                     h->flags &= ~HTML_NOSPLIT;
996                     h->flags |= HTML_SPLIT;
997                     return 0;
998           }
999           if (n->norm->An.auth == AUTH_nosplit) {
1000                     h->flags &= ~HTML_SPLIT;
1001                     h->flags |= HTML_NOSPLIT;
1002                     return 0;
1003           }
1004 
1005           if (h->flags & HTML_SPLIT)
1006                     print_otag(h, TAG_BR, "");
1007 
1008           if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
1009                     h->flags |= HTML_SPLIT;
1010 
1011           print_otag(h, TAG_SPAN, "c", "An");
1012           return 1;
1013 }
1014 
1015 static int
mdoc_cd_pre(MDOC_ARGS)1016 mdoc_cd_pre(MDOC_ARGS)
1017 {
1018           synopsis_pre(h, n);
1019           print_otag(h, TAG_CODE, "c", "Cd");
1020           return 1;
1021 }
1022 
1023 static int
mdoc_fa_pre(MDOC_ARGS)1024 mdoc_fa_pre(MDOC_ARGS)
1025 {
1026           const struct roff_node        *nn;
1027           struct tag                    *t;
1028 
1029           if (n->parent->tok != MDOC_Fo) {
1030                     print_otag(h, TAG_VAR, "c", "Fa");
1031                     return 1;
1032           }
1033           for (nn = n->child; nn != NULL; nn = nn->next) {
1034                     t = print_otag(h, TAG_VAR, "c", "Fa");
1035                     print_text(h, nn->string);
1036                     print_tagq(h, t);
1037                     if (nn->next != NULL) {
1038                               h->flags |= HTML_NOSPACE;
1039                               print_text(h, ",");
1040                     }
1041           }
1042           if (n->child != NULL &&
1043               (nn = roff_node_next(n)) != NULL &&
1044               nn->tok == MDOC_Fa) {
1045                     h->flags |= HTML_NOSPACE;
1046                     print_text(h, ",");
1047           }
1048           return 0;
1049 }
1050 
1051 static int
mdoc_fd_pre(MDOC_ARGS)1052 mdoc_fd_pre(MDOC_ARGS)
1053 {
1054           struct tag          *t;
1055           char                *buf, *cp;
1056 
1057           synopsis_pre(h, n);
1058 
1059           if (NULL == (n = n->child))
1060                     return 0;
1061 
1062           assert(n->type == ROFFT_TEXT);
1063 
1064           if (strcmp(n->string, "#include")) {
1065                     print_otag(h, TAG_CODE, "c", "Fd");
1066                     return 1;
1067           }
1068 
1069           print_otag(h, TAG_CODE, "c", "In");
1070           print_text(h, n->string);
1071 
1072           if (NULL != (n = n->next)) {
1073                     assert(n->type == ROFFT_TEXT);
1074 
1075                     if (h->base_includes) {
1076                               cp = n->string;
1077                               if (*cp == '<' || *cp == '"')
1078                                         cp++;
1079                               buf = mandoc_strdup(cp);
1080                               cp = strchr(buf, '\0') - 1;
1081                               if (cp >= buf && (*cp == '>' || *cp == '"'))
1082                                         *cp = '\0';
1083                               t = print_otag(h, TAG_A, "chI", "In", buf);
1084                               free(buf);
1085                     } else
1086                               t = print_otag(h, TAG_A, "c", "In");
1087 
1088                     print_text(h, n->string);
1089                     print_tagq(h, t);
1090 
1091                     n = n->next;
1092           }
1093 
1094           for ( ; n; n = n->next) {
1095                     assert(n->type == ROFFT_TEXT);
1096                     print_text(h, n->string);
1097           }
1098 
1099           return 0;
1100 }
1101 
1102 static int
mdoc_vt_pre(MDOC_ARGS)1103 mdoc_vt_pre(MDOC_ARGS)
1104 {
1105           if (n->type == ROFFT_BLOCK) {
1106                     synopsis_pre(h, n);
1107                     return 1;
1108           } else if (n->type == ROFFT_ELEM) {
1109                     synopsis_pre(h, n);
1110           } else if (n->type == ROFFT_HEAD)
1111                     return 0;
1112 
1113           print_otag(h, TAG_VAR, "c", "Vt");
1114           return 1;
1115 }
1116 
1117 static int
mdoc_ft_pre(MDOC_ARGS)1118 mdoc_ft_pre(MDOC_ARGS)
1119 {
1120           synopsis_pre(h, n);
1121           print_otag(h, TAG_VAR, "c", "Ft");
1122           return 1;
1123 }
1124 
1125 static int
mdoc_fn_pre(MDOC_ARGS)1126 mdoc_fn_pre(MDOC_ARGS)
1127 {
1128           struct tag          *t;
1129           char                 nbuf[BUFSIZ];
1130           const char          *sp, *ep;
1131           int                  sz, pretty;
1132 
1133           pretty = NODE_SYNPRETTY & n->flags;
1134           synopsis_pre(h, n);
1135 
1136           /* Split apart into type and name. */
1137           assert(n->child->string);
1138           sp = n->child->string;
1139 
1140           ep = strchr(sp, ' ');
1141           if (NULL != ep) {
1142                     t = print_otag(h, TAG_VAR, "c", "Ft");
1143 
1144                     while (ep) {
1145                               sz = MIN((int)(ep - sp), BUFSIZ - 1);
1146                               (void)memcpy(nbuf, sp, (size_t)sz);
1147                               nbuf[sz] = '\0';
1148                               print_text(h, nbuf);
1149                               sp = ++ep;
1150                               ep = strchr(sp, ' ');
1151                     }
1152                     print_tagq(h, t);
1153           }
1154 
1155           t = print_otag_id(h, TAG_CODE, "Fn", n);
1156 
1157           if (sp)
1158                     print_text(h, sp);
1159 
1160           print_tagq(h, t);
1161 
1162           h->flags |= HTML_NOSPACE;
1163           print_text(h, "(");
1164           h->flags |= HTML_NOSPACE;
1165 
1166           for (n = n->child->next; n; n = n->next) {
1167                     if (NODE_SYNPRETTY & n->flags)
1168                               t = print_otag(h, TAG_VAR, "cs", "Fa",
1169                                   "white-space", "nowrap");
1170                     else
1171                               t = print_otag(h, TAG_VAR, "c", "Fa");
1172                     print_text(h, n->string);
1173                     print_tagq(h, t);
1174                     if (n->next) {
1175                               h->flags |= HTML_NOSPACE;
1176                               print_text(h, ",");
1177                     }
1178           }
1179 
1180           h->flags |= HTML_NOSPACE;
1181           print_text(h, ")");
1182 
1183           if (pretty) {
1184                     h->flags |= HTML_NOSPACE;
1185                     print_text(h, ";");
1186           }
1187 
1188           return 0;
1189 }
1190 
1191 static int
mdoc_sm_pre(MDOC_ARGS)1192 mdoc_sm_pre(MDOC_ARGS)
1193 {
1194 
1195           if (NULL == n->child)
1196                     h->flags ^= HTML_NONOSPACE;
1197           else if (0 == strcmp("on", n->child->string))
1198                     h->flags &= ~HTML_NONOSPACE;
1199           else
1200                     h->flags |= HTML_NONOSPACE;
1201 
1202           if ( ! (HTML_NONOSPACE & h->flags))
1203                     h->flags &= ~HTML_NOSPACE;
1204 
1205           return 0;
1206 }
1207 
1208 static int
mdoc_skip_pre(MDOC_ARGS)1209 mdoc_skip_pre(MDOC_ARGS)
1210 {
1211 
1212           return 0;
1213 }
1214 
1215 static int
mdoc_pp_pre(MDOC_ARGS)1216 mdoc_pp_pre(MDOC_ARGS)
1217 {
1218           char      *id;
1219 
1220           if (n->flags & NODE_NOFILL) {
1221                     print_endline(h);
1222                     if (n->flags & NODE_ID)
1223                               mdoc_tg_pre(meta, n, h);
1224                     else {
1225                               h->col = 1;
1226                               print_endline(h);
1227                     }
1228           } else {
1229                     html_close_paragraph(h);
1230                     id = n->flags & NODE_ID ? html_make_id(n, 1) : NULL;
1231                     print_otag(h, TAG_P, "ci", "Pp", id);
1232                     free(id);
1233           }
1234           return 0;
1235 }
1236 
1237 static int
mdoc_lk_pre(MDOC_ARGS)1238 mdoc_lk_pre(MDOC_ARGS)
1239 {
1240           const struct roff_node *link, *descr, *punct;
1241           struct tag          *t;
1242 
1243           if ((link = n->child) == NULL)
1244                     return 0;
1245 
1246           /* Find beginning of trailing punctuation. */
1247           punct = n->last;
1248           while (punct != link && punct->flags & NODE_DELIMC)
1249                     punct = punct->prev;
1250           punct = punct->next;
1251 
1252           /* Link target and link text. */
1253           descr = link->next;
1254           if (descr == punct)
1255                     descr = link;  /* no text */
1256           t = print_otag(h, TAG_A, "ch", "Lk", link->string);
1257           do {
1258                     if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1259                               h->flags |= HTML_NOSPACE;
1260                     print_text(h, descr->string);
1261                     descr = descr->next;
1262           } while (descr != punct);
1263           print_tagq(h, t);
1264 
1265           /* Trailing punctuation. */
1266           while (punct != NULL) {
1267                     h->flags |= HTML_NOSPACE;
1268                     print_text(h, punct->string);
1269                     punct = punct->next;
1270           }
1271           return 0;
1272 }
1273 
1274 static int
mdoc_mt_pre(MDOC_ARGS)1275 mdoc_mt_pre(MDOC_ARGS)
1276 {
1277           struct tag          *t;
1278           char                *cp;
1279 
1280           for (n = n->child; n; n = n->next) {
1281                     assert(n->type == ROFFT_TEXT);
1282                     mandoc_asprintf(&cp, "mailto:%s", n->string);
1283                     t = print_otag(h, TAG_A, "ch", "Mt", cp);
1284                     print_text(h, n->string);
1285                     print_tagq(h, t);
1286                     free(cp);
1287           }
1288           return 0;
1289 }
1290 
1291 static int
mdoc_fo_pre(MDOC_ARGS)1292 mdoc_fo_pre(MDOC_ARGS)
1293 {
1294           struct tag          *t;
1295 
1296           switch (n->type) {
1297           case ROFFT_BLOCK:
1298                     synopsis_pre(h, n);
1299                     return 1;
1300           case ROFFT_HEAD:
1301                     if (n->child != NULL) {
1302                               t = print_otag_id(h, TAG_CODE, "Fn", n);
1303                               print_text(h, n->child->string);
1304                               print_tagq(h, t);
1305                     }
1306                     return 0;
1307           case ROFFT_BODY:
1308                     h->flags |= HTML_NOSPACE;
1309                     print_text(h, "(");
1310                     h->flags |= HTML_NOSPACE;
1311                     return 1;
1312           default:
1313                     abort();
1314           }
1315 }
1316 
1317 static void
mdoc_fo_post(MDOC_ARGS)1318 mdoc_fo_post(MDOC_ARGS)
1319 {
1320           if (n->type != ROFFT_BODY)
1321                     return;
1322           h->flags |= HTML_NOSPACE;
1323           print_text(h, ")");
1324           h->flags |= HTML_NOSPACE;
1325           print_text(h, ";");
1326 }
1327 
1328 static int
mdoc_in_pre(MDOC_ARGS)1329 mdoc_in_pre(MDOC_ARGS)
1330 {
1331           struct tag          *t;
1332 
1333           synopsis_pre(h, n);
1334           print_otag(h, TAG_CODE, "c", "In");
1335 
1336           /*
1337            * The first argument of the `In' gets special treatment as
1338            * being a linked value.  Subsequent values are printed
1339            * afterward.  groff does similarly.  This also handles the case
1340            * of no children.
1341            */
1342 
1343           if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags)
1344                     print_text(h, "#include");
1345 
1346           print_text(h, "<");
1347           h->flags |= HTML_NOSPACE;
1348 
1349           if (NULL != (n = n->child)) {
1350                     assert(n->type == ROFFT_TEXT);
1351 
1352                     if (h->base_includes)
1353                               t = print_otag(h, TAG_A, "chI", "In", n->string);
1354                     else
1355                               t = print_otag(h, TAG_A, "c", "In");
1356                     print_text(h, n->string);
1357                     print_tagq(h, t);
1358 
1359                     n = n->next;
1360           }
1361 
1362           h->flags |= HTML_NOSPACE;
1363           print_text(h, ">");
1364 
1365           for ( ; n; n = n->next) {
1366                     assert(n->type == ROFFT_TEXT);
1367                     print_text(h, n->string);
1368           }
1369           return 0;
1370 }
1371 
1372 static int
mdoc_va_pre(MDOC_ARGS)1373 mdoc_va_pre(MDOC_ARGS)
1374 {
1375           print_otag(h, TAG_VAR, "c", "Va");
1376           return 1;
1377 }
1378 
1379 static int
mdoc_ap_pre(MDOC_ARGS)1380 mdoc_ap_pre(MDOC_ARGS)
1381 {
1382           h->flags |= HTML_NOSPACE;
1383           print_text(h, "\\(aq");
1384           h->flags |= HTML_NOSPACE;
1385           return 1;
1386 }
1387 
1388 static int
mdoc_bf_pre(MDOC_ARGS)1389 mdoc_bf_pre(MDOC_ARGS)
1390 {
1391           const char          *cattr;
1392 
1393           switch (n->type) {
1394           case ROFFT_BLOCK:
1395                     html_close_paragraph(h);
1396                     return 1;
1397           case ROFFT_HEAD:
1398                     return 0;
1399           case ROFFT_BODY:
1400                     break;
1401           default:
1402                     abort();
1403           }
1404 
1405           if (FONT_Em == n->norm->Bf.font)
1406                     cattr = "Bf Em";
1407           else if (FONT_Sy == n->norm->Bf.font)
1408                     cattr = "Bf Sy";
1409           else if (FONT_Li == n->norm->Bf.font)
1410                     cattr = "Bf Li";
1411           else
1412                     cattr = "Bf No";
1413 
1414           /* Cannot use TAG_SPAN because it may contain blocks. */
1415           print_otag(h, TAG_DIV, "c", cattr);
1416           return 1;
1417 }
1418 
1419 static int
mdoc_igndelim_pre(MDOC_ARGS)1420 mdoc_igndelim_pre(MDOC_ARGS)
1421 {
1422           h->flags |= HTML_IGNDELIM;
1423           return 1;
1424 }
1425 
1426 static void
mdoc_pf_post(MDOC_ARGS)1427 mdoc_pf_post(MDOC_ARGS)
1428 {
1429           if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1430                     h->flags |= HTML_NOSPACE;
1431 }
1432 
1433 static int
mdoc_rs_pre(MDOC_ARGS)1434 mdoc_rs_pre(MDOC_ARGS)
1435 {
1436           switch (n->type) {
1437           case ROFFT_BLOCK:
1438                     if (n->sec == SEC_SEE_ALSO)
1439                               html_close_paragraph(h);
1440                     break;
1441           case ROFFT_HEAD:
1442                     return 0;
1443           case ROFFT_BODY:
1444                     if (n->sec == SEC_SEE_ALSO)
1445                               print_otag(h, TAG_P, "c", "Pp");
1446                     print_otag(h, TAG_CITE, "c", "Rs");
1447                     break;
1448           default:
1449                     abort();
1450           }
1451           return 1;
1452 }
1453 
1454 static int
mdoc_no_pre(MDOC_ARGS)1455 mdoc_no_pre(MDOC_ARGS)
1456 {
1457           print_otag_id(h, TAG_SPAN, roff_name[n->tok], n);
1458           return 1;
1459 }
1460 
1461 static int
mdoc_sy_pre(MDOC_ARGS)1462 mdoc_sy_pre(MDOC_ARGS)
1463 {
1464           print_otag_id(h, TAG_B, "Sy", n);
1465           return 1;
1466 }
1467 
1468 static int
mdoc_lb_pre(MDOC_ARGS)1469 mdoc_lb_pre(MDOC_ARGS)
1470 {
1471           if (n->sec == SEC_LIBRARY &&
1472               n->flags & NODE_LINE &&
1473               roff_node_prev(n) != NULL)
1474                     print_otag(h, TAG_BR, "");
1475 
1476           print_otag(h, TAG_SPAN, "c", "Lb");
1477           return 1;
1478 }
1479 
1480 static int
mdoc__x_pre(MDOC_ARGS)1481 mdoc__x_pre(MDOC_ARGS)
1482 {
1483           struct roff_node    *nn;
1484           const char                    *cattr;
1485           enum htmltag                   t;
1486 
1487           t = TAG_SPAN;
1488 
1489           switch (n->tok) {
1490           case MDOC__A:
1491                     cattr = "RsA";
1492                     if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A &&
1493                         ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A))
1494                               print_text(h, "and");
1495                     break;
1496           case MDOC__B:
1497                     t = TAG_I;
1498                     cattr = "RsB";
1499                     break;
1500           case MDOC__C:
1501                     cattr = "RsC";
1502                     break;
1503           case MDOC__D:
1504                     cattr = "RsD";
1505                     break;
1506           case MDOC__I:
1507                     t = TAG_I;
1508                     cattr = "RsI";
1509                     break;
1510           case MDOC__J:
1511                     t = TAG_I;
1512                     cattr = "RsJ";
1513                     break;
1514           case MDOC__N:
1515                     cattr = "RsN";
1516                     break;
1517           case MDOC__O:
1518                     cattr = "RsO";
1519                     break;
1520           case MDOC__P:
1521                     cattr = "RsP";
1522                     break;
1523           case MDOC__Q:
1524                     cattr = "RsQ";
1525                     break;
1526           case MDOC__R:
1527                     cattr = "RsR";
1528                     break;
1529           case MDOC__T:
1530                     cattr = "RsT";
1531                     break;
1532           case MDOC__U:
1533                     print_otag(h, TAG_A, "ch", "RsU", n->child->string);
1534                     return 1;
1535           case MDOC__V:
1536                     cattr = "RsV";
1537                     break;
1538           default:
1539                     abort();
1540           }
1541 
1542           print_otag(h, t, "c", cattr);
1543           return 1;
1544 }
1545 
1546 static void
mdoc__x_post(MDOC_ARGS)1547 mdoc__x_post(MDOC_ARGS)
1548 {
1549           struct roff_node *nn;
1550 
1551           if (n->tok == MDOC__A &&
1552               (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A &&
1553               ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) &&
1554               ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A))
1555                     return;
1556 
1557           /* TODO: %U */
1558 
1559           if (n->parent == NULL || n->parent->tok != MDOC_Rs)
1560                     return;
1561 
1562           h->flags |= HTML_NOSPACE;
1563           print_text(h, roff_node_next(n) ? "," : ".");
1564 }
1565 
1566 static int
mdoc_bk_pre(MDOC_ARGS)1567 mdoc_bk_pre(MDOC_ARGS)
1568 {
1569 
1570           switch (n->type) {
1571           case ROFFT_BLOCK:
1572                     break;
1573           case ROFFT_HEAD:
1574                     return 0;
1575           case ROFFT_BODY:
1576                     if (n->parent->args != NULL || n->prev->child == NULL)
1577                               h->flags |= HTML_PREKEEP;
1578                     break;
1579           default:
1580                     abort();
1581           }
1582 
1583           return 1;
1584 }
1585 
1586 static void
mdoc_bk_post(MDOC_ARGS)1587 mdoc_bk_post(MDOC_ARGS)
1588 {
1589 
1590           if (n->type == ROFFT_BODY)
1591                     h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
1592 }
1593 
1594 static int
mdoc_quote_pre(MDOC_ARGS)1595 mdoc_quote_pre(MDOC_ARGS)
1596 {
1597           if (n->type != ROFFT_BODY)
1598                     return 1;
1599 
1600           switch (n->tok) {
1601           case MDOC_Ao:
1602           case MDOC_Aq:
1603                     print_text(h, n->child != NULL && n->child->next == NULL &&
1604                         n->child->tok == MDOC_Mt ?  "<" : "\\(la");
1605                     break;
1606           case MDOC_Bro:
1607           case MDOC_Brq:
1608                     print_text(h, "\\(lC");
1609                     break;
1610           case MDOC_Bo:
1611           case MDOC_Bq:
1612                     print_text(h, "\\(lB");
1613                     break;
1614           case MDOC_Oo:
1615           case MDOC_Op:
1616                     print_text(h, "\\(lB");
1617                     /*
1618                      * Give up on semantic markup for now.
1619                      * We cannot use TAG_SPAN because .Oo may contain blocks.
1620                      * We cannot use TAG_DIV because we might be in a
1621                      * phrasing context (like .Dl or .Pp); we cannot
1622                      * close out a .Pp at this point either because
1623                      * that would break the line.
1624                      */
1625                     /* XXX print_otag(h, TAG_???, "c", "Op"); */
1626                     break;
1627           case MDOC_En:
1628                     if (NULL == n->norm->Es ||
1629                         NULL == n->norm->Es->child)
1630                               return 1;
1631                     print_text(h, n->norm->Es->child->string);
1632                     break;
1633           case MDOC_Do:
1634           case MDOC_Dq:
1635                     print_text(h, "\\(lq");
1636                     break;
1637           case MDOC_Qo:
1638           case MDOC_Qq:
1639                     print_text(h, "\"");
1640                     break;
1641           case MDOC_Po:
1642           case MDOC_Pq:
1643                     print_text(h, "(");
1644                     break;
1645           case MDOC_Ql:
1646                     print_text(h, "\\(oq");
1647                     h->flags |= HTML_NOSPACE;
1648                     print_otag(h, TAG_CODE, "c", "Li");
1649                     break;
1650           case MDOC_So:
1651           case MDOC_Sq:
1652                     print_text(h, "\\(oq");
1653                     break;
1654           default:
1655                     abort();
1656           }
1657 
1658           h->flags |= HTML_NOSPACE;
1659           return 1;
1660 }
1661 
1662 static void
mdoc_quote_post(MDOC_ARGS)1663 mdoc_quote_post(MDOC_ARGS)
1664 {
1665 
1666           if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1667                     return;
1668 
1669           h->flags |= HTML_NOSPACE;
1670 
1671           switch (n->tok) {
1672           case MDOC_Ao:
1673           case MDOC_Aq:
1674                     print_text(h, n->child != NULL && n->child->next == NULL &&
1675                         n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
1676                     break;
1677           case MDOC_Bro:
1678           case MDOC_Brq:
1679                     print_text(h, "\\(rC");
1680                     break;
1681           case MDOC_Oo:
1682           case MDOC_Op:
1683           case MDOC_Bo:
1684           case MDOC_Bq:
1685                     print_text(h, "\\(rB");
1686                     break;
1687           case MDOC_En:
1688                     if (n->norm->Es == NULL ||
1689                         n->norm->Es->child == NULL ||
1690                         n->norm->Es->child->next == NULL)
1691                               h->flags &= ~HTML_NOSPACE;
1692                     else
1693                               print_text(h, n->norm->Es->child->next->string);
1694                     break;
1695           case MDOC_Do:
1696           case MDOC_Dq:
1697                     print_text(h, "\\(rq");
1698                     break;
1699           case MDOC_Qo:
1700           case MDOC_Qq:
1701                     print_text(h, "\"");
1702                     break;
1703           case MDOC_Po:
1704           case MDOC_Pq:
1705                     print_text(h, ")");
1706                     break;
1707           case MDOC_Ql:
1708           case MDOC_So:
1709           case MDOC_Sq:
1710                     print_text(h, "\\(cq");
1711                     break;
1712           default:
1713                     abort();
1714           }
1715 }
1716 
1717 static int
mdoc_eo_pre(MDOC_ARGS)1718 mdoc_eo_pre(MDOC_ARGS)
1719 {
1720 
1721           if (n->type != ROFFT_BODY)
1722                     return 1;
1723 
1724           if (n->end == ENDBODY_NOT &&
1725               n->parent->head->child == NULL &&
1726               n->child != NULL &&
1727               n->child->end != ENDBODY_NOT)
1728                     print_text(h, "\\&");
1729           else if (n->end != ENDBODY_NOT ? n->child != NULL :
1730               n->parent->head->child != NULL && (n->child != NULL ||
1731               (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1732                     h->flags |= HTML_NOSPACE;
1733           return 1;
1734 }
1735 
1736 static void
mdoc_eo_post(MDOC_ARGS)1737 mdoc_eo_post(MDOC_ARGS)
1738 {
1739           int        body, tail;
1740 
1741           if (n->type != ROFFT_BODY)
1742                     return;
1743 
1744           if (n->end != ENDBODY_NOT) {
1745                     h->flags &= ~HTML_NOSPACE;
1746                     return;
1747           }
1748 
1749           body = n->child != NULL || n->parent->head->child != NULL;
1750           tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1751 
1752           if (body && tail)
1753                     h->flags |= HTML_NOSPACE;
1754           else if ( ! tail)
1755                     h->flags &= ~HTML_NOSPACE;
1756 }
1757 
1758 static int
mdoc_abort_pre(MDOC_ARGS)1759 mdoc_abort_pre(MDOC_ARGS)
1760 {
1761           abort();
1762 }
1763