GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_html.c Lines: 0 873 0.0 %
Date: 2016-12-06 Branches: 0 570 0.0 %

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