GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_term.c Lines: 861 896 96.1 %
Date: 2017-11-07 Branches: 618 763 81.0 %

Line Branch Exec Source
1
/*	$OpenBSD: mdoc_term.c,v 1.263 2017/06/14 17:50:43 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
5
 * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
#include <sys/types.h>
20
21
#include <assert.h>
22
#include <ctype.h>
23
#include <limits.h>
24
#include <stdint.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "mandoc_aux.h"
30
#include "mandoc.h"
31
#include "roff.h"
32
#include "mdoc.h"
33
#include "out.h"
34
#include "term.h"
35
#include "tag.h"
36
#include "main.h"
37
38
struct	termpair {
39
	struct termpair	 *ppair;
40
	int		  count;
41
};
42
43
#define	DECL_ARGS struct termp *p, \
44
		  struct termpair *pair, \
45
		  const struct roff_meta *meta, \
46
		  struct roff_node *n
47
48
struct	termact {
49
	int	(*pre)(DECL_ARGS);
50
	void	(*post)(DECL_ARGS);
51
};
52
53
static	int	  a2width(const struct termp *, const char *);
54
55
static	void	  print_bvspace(struct termp *,
56
			const struct roff_node *,
57
			const struct roff_node *);
58
static	void	  print_mdoc_node(DECL_ARGS);
59
static	void	  print_mdoc_nodelist(DECL_ARGS);
60
static	void	  print_mdoc_head(struct termp *, const struct roff_meta *);
61
static	void	  print_mdoc_foot(struct termp *, const struct roff_meta *);
62
static	void	  synopsis_pre(struct termp *,
63
			const struct roff_node *);
64
65
static	void	  termp____post(DECL_ARGS);
66
static	void	  termp__t_post(DECL_ARGS);
67
static	void	  termp_bd_post(DECL_ARGS);
68
static	void	  termp_bk_post(DECL_ARGS);
69
static	void	  termp_bl_post(DECL_ARGS);
70
static	void	  termp_eo_post(DECL_ARGS);
71
static	void	  termp_fd_post(DECL_ARGS);
72
static	void	  termp_fo_post(DECL_ARGS);
73
static	void	  termp_in_post(DECL_ARGS);
74
static	void	  termp_it_post(DECL_ARGS);
75
static	void	  termp_lb_post(DECL_ARGS);
76
static	void	  termp_nm_post(DECL_ARGS);
77
static	void	  termp_pf_post(DECL_ARGS);
78
static	void	  termp_quote_post(DECL_ARGS);
79
static	void	  termp_sh_post(DECL_ARGS);
80
static	void	  termp_ss_post(DECL_ARGS);
81
static	void	  termp_xx_post(DECL_ARGS);
82
83
static	int	  termp__a_pre(DECL_ARGS);
84
static	int	  termp__t_pre(DECL_ARGS);
85
static	int	  termp_an_pre(DECL_ARGS);
86
static	int	  termp_ap_pre(DECL_ARGS);
87
static	int	  termp_bd_pre(DECL_ARGS);
88
static	int	  termp_bf_pre(DECL_ARGS);
89
static	int	  termp_bk_pre(DECL_ARGS);
90
static	int	  termp_bl_pre(DECL_ARGS);
91
static	int	  termp_bold_pre(DECL_ARGS);
92
static	int	  termp_cd_pre(DECL_ARGS);
93
static	int	  termp_d1_pre(DECL_ARGS);
94
static	int	  termp_eo_pre(DECL_ARGS);
95
static	int	  termp_em_pre(DECL_ARGS);
96
static	int	  termp_er_pre(DECL_ARGS);
97
static	int	  termp_ex_pre(DECL_ARGS);
98
static	int	  termp_fa_pre(DECL_ARGS);
99
static	int	  termp_fd_pre(DECL_ARGS);
100
static	int	  termp_fl_pre(DECL_ARGS);
101
static	int	  termp_fn_pre(DECL_ARGS);
102
static	int	  termp_fo_pre(DECL_ARGS);
103
static	int	  termp_ft_pre(DECL_ARGS);
104
static	int	  termp_in_pre(DECL_ARGS);
105
static	int	  termp_it_pre(DECL_ARGS);
106
static	int	  termp_li_pre(DECL_ARGS);
107
static	int	  termp_lk_pre(DECL_ARGS);
108
static	int	  termp_nd_pre(DECL_ARGS);
109
static	int	  termp_nm_pre(DECL_ARGS);
110
static	int	  termp_ns_pre(DECL_ARGS);
111
static	int	  termp_quote_pre(DECL_ARGS);
112
static	int	  termp_rs_pre(DECL_ARGS);
113
static	int	  termp_sh_pre(DECL_ARGS);
114
static	int	  termp_skip_pre(DECL_ARGS);
115
static	int	  termp_sm_pre(DECL_ARGS);
116
static	int	  termp_pp_pre(DECL_ARGS);
117
static	int	  termp_ss_pre(DECL_ARGS);
118
static	int	  termp_sy_pre(DECL_ARGS);
119
static	int	  termp_tag_pre(DECL_ARGS);
120
static	int	  termp_under_pre(DECL_ARGS);
121
static	int	  termp_vt_pre(DECL_ARGS);
122
static	int	  termp_xr_pre(DECL_ARGS);
123
static	int	  termp_xx_pre(DECL_ARGS);
124
125
static	const struct termact __termacts[MDOC_MAX - MDOC_Dd] = {
126
	{ NULL, NULL }, /* Dd */
127
	{ NULL, NULL }, /* Dt */
128
	{ NULL, NULL }, /* Os */
129
	{ termp_sh_pre, termp_sh_post }, /* Sh */
130
	{ termp_ss_pre, termp_ss_post }, /* Ss */
131
	{ termp_pp_pre, NULL }, /* Pp */
132
	{ termp_d1_pre, termp_bl_post }, /* D1 */
133
	{ termp_d1_pre, termp_bl_post }, /* Dl */
134
	{ termp_bd_pre, termp_bd_post }, /* Bd */
135
	{ NULL, NULL }, /* Ed */
136
	{ termp_bl_pre, termp_bl_post }, /* Bl */
137
	{ NULL, NULL }, /* El */
138
	{ termp_it_pre, termp_it_post }, /* It */
139
	{ termp_under_pre, NULL }, /* Ad */
140
	{ termp_an_pre, NULL }, /* An */
141
	{ termp_ap_pre, NULL }, /* Ap */
142
	{ termp_under_pre, NULL }, /* Ar */
143
	{ termp_cd_pre, NULL }, /* Cd */
144
	{ termp_bold_pre, NULL }, /* Cm */
145
	{ termp_li_pre, NULL }, /* Dv */
146
	{ termp_er_pre, NULL }, /* Er */
147
	{ termp_tag_pre, NULL }, /* Ev */
148
	{ termp_ex_pre, NULL }, /* Ex */
149
	{ termp_fa_pre, NULL }, /* Fa */
150
	{ termp_fd_pre, termp_fd_post }, /* Fd */
151
	{ termp_fl_pre, NULL }, /* Fl */
152
	{ termp_fn_pre, NULL }, /* Fn */
153
	{ termp_ft_pre, NULL }, /* Ft */
154
	{ termp_bold_pre, NULL }, /* Ic */
155
	{ termp_in_pre, termp_in_post }, /* In */
156
	{ termp_li_pre, NULL }, /* Li */
157
	{ termp_nd_pre, NULL }, /* Nd */
158
	{ termp_nm_pre, termp_nm_post }, /* Nm */
159
	{ termp_quote_pre, termp_quote_post }, /* Op */
160
	{ termp_ft_pre, NULL }, /* Ot */
161
	{ termp_under_pre, NULL }, /* Pa */
162
	{ termp_ex_pre, NULL }, /* Rv */
163
	{ NULL, NULL }, /* St */
164
	{ termp_under_pre, NULL }, /* Va */
165
	{ termp_vt_pre, NULL }, /* Vt */
166
	{ termp_xr_pre, NULL }, /* Xr */
167
	{ termp__a_pre, termp____post }, /* %A */
168
	{ termp_under_pre, termp____post }, /* %B */
169
	{ NULL, termp____post }, /* %D */
170
	{ termp_under_pre, termp____post }, /* %I */
171
	{ termp_under_pre, termp____post }, /* %J */
172
	{ NULL, termp____post }, /* %N */
173
	{ NULL, termp____post }, /* %O */
174
	{ NULL, termp____post }, /* %P */
175
	{ NULL, termp____post }, /* %R */
176
	{ termp__t_pre, termp__t_post }, /* %T */
177
	{ NULL, termp____post }, /* %V */
178
	{ NULL, NULL }, /* Ac */
179
	{ termp_quote_pre, termp_quote_post }, /* Ao */
180
	{ termp_quote_pre, termp_quote_post }, /* Aq */
181
	{ NULL, NULL }, /* At */
182
	{ NULL, NULL }, /* Bc */
183
	{ termp_bf_pre, NULL }, /* Bf */
184
	{ termp_quote_pre, termp_quote_post }, /* Bo */
185
	{ termp_quote_pre, termp_quote_post }, /* Bq */
186
	{ termp_xx_pre, termp_xx_post }, /* Bsx */
187
	{ NULL, NULL }, /* Bx */
188
	{ termp_skip_pre, NULL }, /* Db */
189
	{ NULL, NULL }, /* Dc */
190
	{ termp_quote_pre, termp_quote_post }, /* Do */
191
	{ termp_quote_pre, termp_quote_post }, /* Dq */
192
	{ NULL, NULL }, /* Ec */ /* FIXME: no space */
193
	{ NULL, NULL }, /* Ef */
194
	{ termp_em_pre, NULL }, /* Em */
195
	{ termp_eo_pre, termp_eo_post }, /* Eo */
196
	{ termp_xx_pre, termp_xx_post }, /* Fx */
197
	{ termp_bold_pre, NULL }, /* Ms */
198
	{ termp_li_pre, NULL }, /* No */
199
	{ termp_ns_pre, NULL }, /* Ns */
200
	{ termp_xx_pre, termp_xx_post }, /* Nx */
201
	{ termp_xx_pre, termp_xx_post }, /* Ox */
202
	{ NULL, NULL }, /* Pc */
203
	{ NULL, termp_pf_post }, /* Pf */
204
	{ termp_quote_pre, termp_quote_post }, /* Po */
205
	{ termp_quote_pre, termp_quote_post }, /* Pq */
206
	{ NULL, NULL }, /* Qc */
207
	{ termp_quote_pre, termp_quote_post }, /* Ql */
208
	{ termp_quote_pre, termp_quote_post }, /* Qo */
209
	{ termp_quote_pre, termp_quote_post }, /* Qq */
210
	{ NULL, NULL }, /* Re */
211
	{ termp_rs_pre, NULL }, /* Rs */
212
	{ NULL, NULL }, /* Sc */
213
	{ termp_quote_pre, termp_quote_post }, /* So */
214
	{ termp_quote_pre, termp_quote_post }, /* Sq */
215
	{ termp_sm_pre, NULL }, /* Sm */
216
	{ termp_under_pre, NULL }, /* Sx */
217
	{ termp_sy_pre, NULL }, /* Sy */
218
	{ NULL, NULL }, /* Tn */
219
	{ termp_xx_pre, termp_xx_post }, /* Ux */
220
	{ NULL, NULL }, /* Xc */
221
	{ NULL, NULL }, /* Xo */
222
	{ termp_fo_pre, termp_fo_post }, /* Fo */
223
	{ NULL, NULL }, /* Fc */
224
	{ termp_quote_pre, termp_quote_post }, /* Oo */
225
	{ NULL, NULL }, /* Oc */
226
	{ termp_bk_pre, termp_bk_post }, /* Bk */
227
	{ NULL, NULL }, /* Ek */
228
	{ NULL, NULL }, /* Bt */
229
	{ NULL, NULL }, /* Hf */
230
	{ termp_under_pre, NULL }, /* Fr */
231
	{ NULL, NULL }, /* Ud */
232
	{ NULL, termp_lb_post }, /* Lb */
233
	{ termp_pp_pre, NULL }, /* Lp */
234
	{ termp_lk_pre, NULL }, /* Lk */
235
	{ termp_under_pre, NULL }, /* Mt */
236
	{ termp_quote_pre, termp_quote_post }, /* Brq */
237
	{ termp_quote_pre, termp_quote_post }, /* Bro */
238
	{ NULL, NULL }, /* Brc */
239
	{ NULL, termp____post }, /* %C */
240
	{ termp_skip_pre, NULL }, /* Es */
241
	{ termp_quote_pre, termp_quote_post }, /* En */
242
	{ termp_xx_pre, termp_xx_post }, /* Dx */
243
	{ NULL, termp____post }, /* %Q */
244
	{ NULL, termp____post }, /* %U */
245
	{ NULL, NULL }, /* Ta */
246
};
247
static	const struct termact *const termacts = __termacts - MDOC_Dd;
248
249
static	int	 fn_prio;
250
251
252
void
253
terminal_mdoc(void *arg, const struct roff_man *mdoc)
254
{
255
	struct roff_node	*n;
256
	struct termp		*p;
257
	size_t			 save_defindent;
258
259
5950
	p = (struct termp *)arg;
260
2975
	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
261
2975
	term_tab_set(p, NULL);
262
2975
	term_tab_set(p, "T");
263
2975
	term_tab_set(p, ".5i");
264
265
2975
	n = mdoc->first->child;
266
2975
	if (p->synopsisonly) {
267
		while (n != NULL) {
268
			if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
269
				if (n->child->next->child != NULL)
270
					print_mdoc_nodelist(p, NULL,
271
					    &mdoc->meta,
272
					    n->child->next->child);
273
				term_newln(p);
274
				break;
275
			}
276
			n = n->next;
277
		}
278
	} else {
279
2975
		save_defindent = p->defindent;
280
2975
		if (p->defindent == 0)
281
2975
			p->defindent = 5;
282
2975
		term_begin(p, print_mdoc_head, print_mdoc_foot,
283
2975
		    &mdoc->meta);
284

47519
		while (n != NULL && n->flags & NODE_NOPRT)
285
8907
			n = n->next;
286
2975
		if (n != NULL) {
287
2966
			if (n->tok != MDOC_Sh)
288
27
				term_vspace(p);
289
2966
			print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
290
2966
		}
291
2975
		term_end(p);
292
2975
		p->defindent = save_defindent;
293
	}
294
2975
}
295
296
static void
297
print_mdoc_nodelist(DECL_ARGS)
298
{
299
300
560345
	while (n != NULL) {
301
177118
		print_mdoc_node(p, pair, meta, n);
302
177118
		n = n->next;
303
	}
304
68703
}
305
306
static void
307
print_mdoc_node(DECL_ARGS)
308
{
309
	int		 chld;
310
357820
	struct termpair	 npair;
311
	size_t		 offset, rmargin;
312
313
178910
	if (n->flags & NODE_NOPRT)
314
99
		return;
315
316
	chld = 1;
317
178811
	offset = p->tcol->offset;
318
178811
	rmargin = p->tcol->rmargin;
319
178811
	n->flags &= ~NODE_ENDED;
320
178811
	n->prev_font = p->fonti;
321
322
178811
	memset(&npair, 0, sizeof(struct termpair));
323
178811
	npair.ppair = pair;
324
325
	/*
326
	 * Keeps only work until the end of a line.  If a keep was
327
	 * invoked in a prior line, revert it to PREKEEP.
328
	 */
329
330

185908
	if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
331
1090
		p->flags &= ~TERMP_KEEP;
332
1090
		p->flags |= TERMP_PREKEEP;
333
1090
	}
334
335
	/*
336
	 * After the keep flags have been set up, we may now
337
	 * produce output.  Note that some pre-handlers do so.
338
	 */
339
340

178811
	switch (n->type) {
341
	case ROFFT_TEXT:
342

67814
		if (*n->string == ' ' && n->flags & NODE_LINE &&
343
54
		    (p->flags & TERMP_NONEWLINE) == 0)
344
54
			term_newln(p);
345
67697
		if (NODE_DELIMC & n->flags)
346
2631
			p->flags |= TERMP_NOSPACE;
347
67697
		term_word(p, n->string);
348
67697
		if (NODE_DELIMO & n->flags)
349
792
			p->flags |= TERMP_NOSPACE;
350
		break;
351
	case ROFFT_EQN:
352
306
		if ( ! (n->flags & NODE_LINE))
353
18
			p->flags |= TERMP_NOSPACE;
354
306
		term_eqn(p, n->eqn);
355

612
		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
356
18
			p->flags |= TERMP_NOSPACE;
357
		break;
358
	case ROFFT_TBL:
359
18738
		if (p->tbl.cols == NULL)
360
5544
			term_newln(p);
361
18738
		term_tbl(p, n->span);
362
18738
		break;
363
	default:
364
92070
		if (n->tok < ROFF_MAX) {
365
7974
			roff_term_pre(p, n);
366
7974
			return;
367
		}
368

168192
		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
369

84555
		if (termacts[n->tok].pre != NULL &&
370
83749
		    (n->end == ENDBODY_NOT || n->child != NULL))
371
82849
			chld = (*termacts[n->tok].pre)
372
				(p, &npair, meta, n);
373
		break;
374
	}
375
376

330833
	if (chld && n->child)
377
65737
		print_mdoc_nodelist(p, &npair, meta, n->child);
378
379
170837
	term_fontpopq(p,
380
342133
	    (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
381
382
170837
	switch (n->type) {
383
	case ROFFT_TEXT:
384
		break;
385
	case ROFFT_TBL:
386
		break;
387
	case ROFFT_EQN:
388
		break;
389
	default:
390

142533
		if (termacts[n->tok].post == NULL || n->flags & NODE_ENDED)
391
			break;
392
57987
		(void)(*termacts[n->tok].post)(p, &npair, meta, n);
393
394
		/*
395
		 * Explicit end tokens not only call the post
396
		 * handler, but also tell the respective block
397
		 * that it must not call the post handler again.
398
		 */
399
57987
		if (ENDBODY_NOT != n->end)
400
450
			n->body->flags |= NODE_ENDED;
401
		break;
402
	}
403
404
170837
	if (NODE_EOS & n->flags)
405
2773
		p->flags |= TERMP_SENTENCE;
406
407
170837
	if (n->type != ROFFT_TEXT)
408
103140
		p->tcol->offset = offset;
409
170837
	p->tcol->rmargin = rmargin;
410
349747
}
411
412
static void
413
print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
414
{
415
	size_t sz;
416
417
5950
	term_fontrepl(p, TERMFONT_NONE);
418
419
	/*
420
	 * Output the footer in new-groff style, that is, three columns
421
	 * with the middle being the manual date and flanking columns
422
	 * being the operating system:
423
	 *
424
	 * SYSTEM                  DATE                    SYSTEM
425
	 */
426
427
2975
	term_vspace(p);
428
429
2975
	p->tcol->offset = 0;
430
2975
	sz = term_strlen(p, meta->date);
431
8916
	p->tcol->rmargin = p->maxrmargin > sz ?
432
2966
	    (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
433
2975
	p->trailspace = 1;
434
2975
	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
435
436
2975
	term_word(p, meta->os);
437
2975
	term_flushln(p);
438
439
2975
	p->tcol->offset = p->tcol->rmargin;
440
2975
	sz = term_strlen(p, meta->os);
441
8916
	p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
442
2975
	p->flags |= TERMP_NOSPACE;
443
444
2975
	term_word(p, meta->date);
445
2975
	term_flushln(p);
446
447
2975
	p->tcol->offset = p->tcol->rmargin;
448
2975
	p->tcol->rmargin = p->maxrmargin;
449
2975
	p->trailspace = 0;
450
2975
	p->flags &= ~TERMP_NOBREAK;
451
2975
	p->flags |= TERMP_NOSPACE;
452
453
2975
	term_word(p, meta->os);
454
2975
	term_flushln(p);
455
456
2975
	p->tcol->offset = 0;
457
2975
	p->tcol->rmargin = p->maxrmargin;
458
2975
	p->flags = 0;
459
2975
}
460
461
static void
462
print_mdoc_head(struct termp *p, const struct roff_meta *meta)
463
{
464
5950
	char			*volume, *title;
465
	size_t			 vollen, titlen;
466
467
	/*
468
	 * The header is strange.  It has three components, which are
469
	 * really two with the first duplicated.  It goes like this:
470
	 *
471
	 * IDENTIFIER              TITLE                   IDENTIFIER
472
	 *
473
	 * The IDENTIFIER is NAME(SECTION), which is the command-name
474
	 * (if given, or "unknown" if not) followed by the manual page
475
	 * section.  These are given in `Dt'.  The TITLE is a free-form
476
	 * string depending on the manual volume.  If not specified, it
477
	 * switches on the manual section.
478
	 */
479
480
2975
	assert(meta->vol);
481
2975
	if (NULL == meta->arch)
482
2966
		volume = mandoc_strdup(meta->vol);
483
	else
484
9
		mandoc_asprintf(&volume, "%s (%s)",
485
		    meta->vol, meta->arch);
486
2975
	vollen = term_strlen(p, volume);
487
488
2975
	if (NULL == meta->msec)
489
36
		title = mandoc_strdup(meta->title);
490
	else
491
2939
		mandoc_asprintf(&title, "%s(%s)",
492
		    meta->title, meta->msec);
493
2975
	titlen = term_strlen(p, title);
494
495
2975
	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
496
2975
	p->trailspace = 1;
497
2975
	p->tcol->offset = 0;
498
8916
	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
499
2966
	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
500
18
	    vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
501
502
2975
	term_word(p, title);
503
2975
	term_flushln(p);
504
505
2975
	p->flags |= TERMP_NOSPACE;
506
2975
	p->tcol->offset = p->tcol->rmargin;
507
8925
	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
508
5950
	    p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
509
510
2975
	term_word(p, volume);
511
2975
	term_flushln(p);
512
513
2975
	p->flags &= ~TERMP_NOBREAK;
514
2975
	p->trailspace = 0;
515
2975
	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
516
2966
		p->flags |= TERMP_NOSPACE;
517
2966
		p->tcol->offset = p->tcol->rmargin;
518
2966
		p->tcol->rmargin = p->maxrmargin;
519
2966
		term_word(p, title);
520
2966
		term_flushln(p);
521
2966
	}
522
523
2975
	p->flags &= ~TERMP_NOSPACE;
524
2975
	p->tcol->offset = 0;
525
2975
	p->tcol->rmargin = p->maxrmargin;
526
2975
	free(title);
527
2975
	free(volume);
528
2975
}
529
530
static int
531
a2width(const struct termp *p, const char *v)
532
{
533
13054
	struct roffsu	 su;
534
	const char	*end;
535
536
6527
	end = a2roffsu(v, &su, SCALE_MAX);
537

11596
	if (end == NULL || *end != '\0') {
538
1458
		SCALE_HS_INIT(&su, term_strlen(p, v));
539
1458
		su.scale /= term_strlen(p, "0");
540
1458
	}
541
13054
	return term_hen(p, &su);
542
6527
}
543
544
/*
545
 * Determine how much space to print out before block elements of `It'
546
 * (and thus `Bl') and `Bd'.  And then go ahead and print that space,
547
 * too.
548
 */
549
static void
550
print_bvspace(struct termp *p,
551
	const struct roff_node *bl,
552
	const struct roff_node *n)
553
{
554
	const struct roff_node	*nn;
555
556
9504
	assert(n);
557
558
4752
	term_newln(p);
559
560

5372
	if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
561
54
		return;
562

8830
	if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
563
1093
		return;
564
565
	/* Do not vspace directly after Ss/Sh. */
566
567
	nn = n;
568

8673
	while (nn->prev != NULL && nn->prev->flags & NODE_NOPRT)
569
		nn = nn->prev;
570
5783
	while (nn->prev == NULL) {
571
		do {
572
4997
			nn = nn->parent;
573
4997
			if (nn->type == ROFFT_ROOT)
574
9
				return;
575
4988
		} while (nn->type != ROFFT_BLOCK);
576

4753
		if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
577
289
			return;
578

2241
		if (nn->tok == MDOC_It &&
579
36
		    nn->parent->parent->norm->Bl.type != LIST_item)
580
			break;
581
	}
582
583
	/* A `-column' does not assert vspace within the list. */
584
585

6120
	if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
586

810
		if (n->prev && MDOC_It == n->prev->tok)
587
324
			return;
588
589
	/* A `-diag' without body does not vspace. */
590
591

5472
	if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
592

117
		if (n->prev && MDOC_It == n->prev->tok) {
593
18
			assert(n->prev->body);
594
18
			if (NULL == n->prev->body->child)
595
				return;
596
		}
597
598
2983
	term_vspace(p);
599
7735
}
600
601
602
static int
603
termp_it_pre(DECL_ARGS)
604
{
605
26736
	struct roffsu		su;
606
13368
	char			buf[24];
607
	const struct roff_node *bl, *nn;
608
	size_t			ncols, dcol;
609
	int			i, offset, width;
610
	enum mdoc_list		type;
611
612
13368
	if (n->type == ROFFT_BLOCK) {
613
4132
		print_bvspace(p, n->parent->parent, n);
614
4132
		return 1;
615
	}
616
617
9236
	bl = n->parent->parent->parent;
618
9236
	type = bl->norm->Bl.type;
619
620
	/*
621
	 * Defaults for specific list types.
622
	 */
623
624


9236
	switch (type) {
625
	case LIST_bullet:
626
	case LIST_dash:
627
	case LIST_hyphen:
628
	case LIST_enum:
629
2583
		width = term_len(p, 2);
630
2583
		break;
631
	case LIST_hang:
632
	case LIST_tag:
633
2990
		width = term_len(p, 8);
634
2990
		break;
635
	case LIST_column:
636
2043
		width = term_len(p, 10);
637
2043
		break;
638
	default:
639
		width = 0;
640
1620
		break;
641
	}
642
	offset = 0;
643
644
	/*
645
	 * First calculate width and offset.  This is pretty easy unless
646
	 * we're a -column list, in which case all prior columns must
647
	 * be accounted for.
648
	 */
649
650
9236
	if (bl->norm->Bl.offs != NULL) {
651
873
		offset = a2width(p, bl->norm->Bl.offs);
652

909
		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
653
			offset = -p->tcol->offset;
654
873
		else if (offset > SHRT_MAX)
655
			offset = 0;
656
	}
657
658
9236
	switch (type) {
659
	case LIST_column:
660
2043
		if (n->type == ROFFT_HEAD)
661
			break;
662
663
		/*
664
		 * Imitate groff's column handling:
665
		 * - For each earlier column, add its width.
666
		 * - For less than 5 columns, add four more blanks per
667
		 *   column.
668
		 * - For exactly 5 columns, add three more blank per
669
		 *   column.
670
		 * - For more than 5 columns, add only one column.
671
		 */
672
1512
		ncols = bl->norm->Bl.ncols;
673
4536
		dcol = ncols < 5 ? term_len(p, 4) :
674
135
		    ncols == 5 ? term_len(p, 3) : term_len(p, 1);
675
676
		/*
677
		 * Calculate the offset by applying all prior ROFFT_BODY,
678
		 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
679
		 */
680
681
4869
		for (i = 0, nn = n->prev;
682
4869
		    nn->prev && i < (int)ncols;
683
1665
		    nn = nn->prev, i++) {
684
1665
			SCALE_HS_INIT(&su,
685
			    term_strlen(p, bl->norm->Bl.cols[i]));
686
1665
			su.scale /= term_strlen(p, "0");
687
1665
			offset += term_hen(p, &su) + dcol;
688
		}
689
690
		/*
691
		 * When exceeding the declared number of columns, leave
692
		 * the remaining widths at 0.  This will later be
693
		 * adjusted to the default width of 10, or, for the last
694
		 * column, stretched to the right margin.
695
		 */
696
1512
		if (i >= (int)ncols)
697
			break;
698
699
		/*
700
		 * Use the declared column widths, extended as explained
701
		 * in the preceding paragraph.
702
		 */
703
1368
		SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
704
1368
		su.scale /= term_strlen(p, "0");
705
1368
		width = term_hen(p, &su) + dcol;
706
1368
		break;
707
	default:
708
7193
		if (NULL == bl->norm->Bl.width)
709
			break;
710
711
		/*
712
		 * Note: buffer the width by 2, which is groff's magic
713
		 * number for buffering single arguments.  See the above
714
		 * handling for column for how this changes.
715
		 */
716
5483
		width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
717

5699
		if (width < 0 && (size_t)(-width) > p->tcol->offset)
718
			width = -p->tcol->offset;
719
5483
		else if (width > SHRT_MAX)
720
			width = 0;
721
		break;
722
	}
723
724
	/*
725
	 * Whitespace control.  Inset bodies need an initial space,
726
	 * while diagonal bodies need two.
727
	 */
728
729
10064
	p->flags |= TERMP_NOSPACE;
730
731
10064
	switch (type) {
732
	case LIST_diag:
733
270
		if (n->type == ROFFT_BODY)
734
135
			term_word(p, "\\ \\ ");
735
		break;
736
	case LIST_inset:
737

837
		if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
738
270
			term_word(p, "\\ ");
739
		break;
740
	default:
741
		break;
742
	}
743
744
9236
	p->flags |= TERMP_NOSPACE;
745
746
9236
	switch (type) {
747
	case LIST_diag:
748
270
		if (n->type == ROFFT_HEAD)
749
135
			term_fontpush(p, TERMFONT_BOLD);
750
		break;
751
	default:
752
		break;
753
	}
754
755
	/*
756
	 * Pad and break control.  This is the tricky part.  These flags
757
	 * are documented in term_flushln() in term.c.  Note that we're
758
	 * going to unset all of these flags in termp_it_post() when we
759
	 * exit.
760
	 */
761
762


17122
	switch (type) {
763
	case LIST_enum:
764
	case LIST_bullet:
765
	case LIST_dash:
766
	case LIST_hyphen:
767
2583
		if (n->type == ROFFT_HEAD) {
768
1296
			p->flags |= TERMP_NOBREAK | TERMP_HANG;
769
1296
			p->trailspace = 1;
770
2583
		} else if (width <= (int)term_len(p, 2))
771
207
			p->flags |= TERMP_NOPAD;
772
		break;
773
	case LIST_hang:
774
486
		if (n->type != ROFFT_HEAD)
775
			break;
776
243
		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
777
243
		p->trailspace = 1;
778
243
		break;
779
	case LIST_tag:
780
2504
		if (n->type != ROFFT_HEAD)
781
			break;
782
783
1252
		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
784
1252
		p->trailspace = 2;
785
786

2504
		if (NULL == n->next || NULL == n->next->child)
787
54
			p->flags |= TERMP_HANG;
788
		break;
789
	case LIST_column:
790
2043
		if (n->type == ROFFT_HEAD)
791
			break;
792
793
1512
		if (NULL == n->next) {
794
531
			p->flags &= ~TERMP_NOBREAK;
795
			p->trailspace = 0;
796
531
		} else {
797
981
			p->flags |= TERMP_NOBREAK;
798
			p->trailspace = 1;
799
		}
800
801
1512
		break;
802
	case LIST_diag:
803
270
		if (n->type != ROFFT_HEAD)
804
			break;
805
135
		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
806
135
		p->trailspace = 1;
807
135
		break;
808
	default:
809
		break;
810
	}
811
812
	/*
813
	 * Margin control.  Set-head-width lists have their right
814
	 * margins shortened.  The body for these lists has the offset
815
	 * necessarily lengthened.  Everybody gets the offset.
816
	 */
817
818
16852
	p->tcol->offset += offset;
819
820


16852
	switch (type) {
821
	case LIST_bullet:
822
	case LIST_dash:
823
	case LIST_enum:
824
	case LIST_hyphen:
825
	case LIST_hang:
826
	case LIST_tag:
827
5573
		if (n->type == ROFFT_HEAD)
828
2791
			p->tcol->rmargin = p->tcol->offset + width;
829
		else
830
2782
			p->tcol->offset += width;
831
		break;
832
	case LIST_column:
833
2043
		assert(width);
834
2043
		p->tcol->rmargin = p->tcol->offset + width;
835
		/*
836
		 * XXX - this behaviour is not documented: the
837
		 * right-most column is filled to the right margin.
838
		 */
839
2043
		if (n->type == ROFFT_HEAD)
840
			break;
841

2043
		if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
842
459
			p->tcol->rmargin = p->maxrmargin;
843
		break;
844
	default:
845
		break;
846
	}
847
848
	/*
849
	 * The dash, hyphen, bullet and enum lists all have a special
850
	 * HEAD character (temporarily bold, in some cases).
851
	 */
852
853
9236
	if (n->type == ROFFT_HEAD)
854

5428
		switch (type) {
855
		case LIST_bullet:
856
351
			term_fontpush(p, TERMFONT_BOLD);
857
351
			term_word(p, "\\[bu]");
858
351
			term_fontpop(p);
859
351
			break;
860
		case LIST_dash:
861
		case LIST_hyphen:
862
603
			term_fontpush(p, TERMFONT_BOLD);
863
603
			term_word(p, "-");
864
603
			term_fontpop(p);
865
603
			break;
866
		case LIST_enum:
867
342
			(pair->ppair->ppair->count)++;
868
684
			(void)snprintf(buf, sizeof(buf), "%d.",
869
342
			    pair->ppair->ppair->count);
870
342
			term_word(p, buf);
871
342
			break;
872
		default:
873
			break;
874
		}
875
876
	/*
877
	 * If we're not going to process our children, indicate so here.
878
	 */
879
880

12341
	switch (type) {
881
	case LIST_bullet:
882
	case LIST_item:
883
	case LIST_dash:
884
	case LIST_hyphen:
885
	case LIST_enum:
886
3195
		if (n->type == ROFFT_HEAD)
887
1602
			return 0;
888
		break;
889
	case LIST_column:
890
2043
		if (n->type == ROFFT_HEAD)
891
531
			return 0;
892
1512
		p->minbl = 0;
893
1512
		break;
894
	default:
895
		break;
896
	}
897
898
7103
	return 1;
899
13368
}
900
901
static void
902
termp_it_post(DECL_ARGS)
903
{
904
	enum mdoc_list	   type;
905
906
26736
	if (n->type == ROFFT_BLOCK)
907
4132
		return;
908
909
9236
	type = n->parent->parent->parent->norm->Bl.type;
910
911

9236
	switch (type) {
912
	case LIST_item:
913
	case LIST_diag:
914
	case LIST_inset:
915
1440
		if (n->type == ROFFT_BODY)
916
720
			term_newln(p);
917
		break;
918
	case LIST_column:
919
2043
		if (n->type == ROFFT_BODY)
920
1512
			term_flushln(p);
921
		break;
922
	default:
923
5753
		term_newln(p);
924
5753
		break;
925
	}
926
927
	/*
928
	 * Now that our output is flushed, we can reset our tags.  Since
929
	 * only `It' sets these flags, we're free to assume that nobody
930
	 * has munged them in the meanwhile.
931
	 */
932
933
9236
	p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
934
9236
	p->trailspace = 0;
935
22604
}
936
937
static int
938
termp_nm_pre(DECL_ARGS)
939
{
940
	const char	*cp;
941
942
8558
	if (n->type == ROFFT_BLOCK) {
943
303
		p->flags |= TERMP_PREKEEP;
944
303
		return 1;
945
	}
946
947
3976
	if (n->type == ROFFT_BODY) {
948
294
		if (n->child == NULL)
949
63
			return 0;
950
231
		p->flags |= TERMP_NOSPACE;
951
		cp = NULL;
952
231
		if (n->prev->child != NULL)
953
231
		    cp = n->prev->child->string;
954
231
		if (cp == NULL)
955
			cp = meta->name;
956
231
		if (cp == NULL)
957
			p->tcol->offset += term_len(p, 6);
958
		else
959
462
			p->tcol->offset += term_len(p, 1) +
960
231
			    term_strlen(p, cp);
961
231
		return 1;
962
	}
963
964
3682
	if (n->child == NULL)
965
54
		return 0;
966
967
3628
	if (n->type == ROFFT_HEAD)
968
303
		synopsis_pre(p, n->parent);
969
970

3922
	if (n->type == ROFFT_HEAD &&
971
597
	    n->next != NULL && n->next->child != NULL) {
972
231
		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
973
231
		p->trailspace = 1;
974
231
		p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
975
231
		if (n->child == NULL)
976
			p->tcol->rmargin += term_strlen(p, meta->name);
977
231
		else if (n->child->type == ROFFT_TEXT) {
978
231
			p->tcol->rmargin += term_strlen(p, n->child->string);
979
231
			if (n->child->next != NULL)
980
				p->flags |= TERMP_HANG;
981
		} else {
982
			p->tcol->rmargin += term_len(p, 5);
983
			p->flags |= TERMP_HANG;
984
		}
985
	}
986
987
3628
	term_fontpush(p, TERMFONT_BOLD);
988
3628
	return 1;
989
4279
}
990
991
static void
992
termp_nm_post(DECL_ARGS)
993
{
994
995
8558
	if (n->type == ROFFT_BLOCK) {
996
303
		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
997

4573
	} else if (n->type == ROFFT_HEAD &&
998
597
	    NULL != n->next && NULL != n->next->child) {
999
231
		term_flushln(p);
1000
231
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1001
231
		p->trailspace = 0;
1002

4270
	} else if (n->type == ROFFT_BODY && n->child != NULL)
1003
231
		term_flushln(p);
1004
4279
}
1005
1006
static int
1007
termp_fl_pre(DECL_ARGS)
1008
{
1009
1010
3226
	termp_tag_pre(p, pair, meta, n);
1011
1613
	term_fontpush(p, TERMFONT_BOLD);
1012
1613
	term_word(p, "\\-");
1013
1014

1649
	if (!(n->child == NULL &&
1015
171
	    (n->next == NULL ||
1016
171
	     n->next->type == ROFFT_TEXT ||
1017
36
	     n->next->flags & NODE_LINE)))
1018
1478
		p->flags |= TERMP_NOSPACE;
1019
1020
1613
	return 1;
1021
}
1022
1023
static int
1024
termp__a_pre(DECL_ARGS)
1025
{
1026
1027

400
	if (n->prev && MDOC__A == n->prev->tok)
1028

72
		if (NULL == n->next || MDOC__A != n->next->tok)
1029
18
			term_word(p, "and");
1030
1031
164
	return 1;
1032
}
1033
1034
static int
1035
termp_an_pre(DECL_ARGS)
1036
{
1037
1038
596
	if (n->norm->An.auth == AUTH_split) {
1039
9
		p->flags &= ~TERMP_NOSPLIT;
1040
9
		p->flags |= TERMP_SPLIT;
1041
9
		return 0;
1042
	}
1043
289
	if (n->norm->An.auth == AUTH_nosplit) {
1044
18
		p->flags &= ~TERMP_SPLIT;
1045
18
		p->flags |= TERMP_NOSPLIT;
1046
18
		return 0;
1047
	}
1048
1049
271
	if (p->flags & TERMP_SPLIT)
1050
99
		term_newln(p);
1051
1052

398
	if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
1053
82
		p->flags |= TERMP_SPLIT;
1054
1055
271
	return 1;
1056
298
}
1057
1058
static int
1059
termp_ns_pre(DECL_ARGS)
1060
{
1061
1062
534
	if ( ! (NODE_LINE & n->flags))
1063
258
		p->flags |= TERMP_NOSPACE;
1064
267
	return 1;
1065
}
1066
1067
static int
1068
termp_rs_pre(DECL_ARGS)
1069
{
1070
1071
984
	if (SEC_SEE_ALSO != n->sec)
1072
216
		return 1;
1073

368
	if (n->type == ROFFT_BLOCK && n->prev != NULL)
1074
92
		term_vspace(p);
1075
276
	return 1;
1076
492
}
1077
1078
static int
1079
termp_ex_pre(DECL_ARGS)
1080
{
1081
330
	term_newln(p);
1082
165
	return 1;
1083
}
1084
1085
static int
1086
termp_nd_pre(DECL_ARGS)
1087
{
1088
1089
17796
	if (n->type == ROFFT_BODY)
1090
2966
		term_word(p, "\\(en");
1091
8898
	return 1;
1092
}
1093
1094
static int
1095
termp_bl_pre(DECL_ARGS)
1096
{
1097
1098
15096
	return n->type != ROFFT_HEAD;
1099
}
1100
1101
static void
1102
termp_bl_post(DECL_ARGS)
1103
{
1104
1105
15546
	if (n->type != ROFFT_BLOCK)
1106
		return;
1107
2591
	term_newln(p);
1108

5107
	if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
1109
		return;
1110
216
	term_tab_set(p, NULL);
1111
216
	term_tab_set(p, "T");
1112
216
	term_tab_set(p, ".5i");
1113
7989
}
1114
1115
static int
1116
termp_xr_pre(DECL_ARGS)
1117
{
1118
1119
670
	if (NULL == (n = n->child))
1120
		return 0;
1121
1122
335
	assert(n->type == ROFFT_TEXT);
1123
335
	term_word(p, n->string);
1124
1125
335
	if (NULL == (n = n->next))
1126
18
		return 0;
1127
1128
317
	p->flags |= TERMP_NOSPACE;
1129
317
	term_word(p, "(");
1130
317
	p->flags |= TERMP_NOSPACE;
1131
1132
317
	assert(n->type == ROFFT_TEXT);
1133
317
	term_word(p, n->string);
1134
1135
317
	p->flags |= TERMP_NOSPACE;
1136
317
	term_word(p, ")");
1137
1138
317
	return 0;
1139
335
}
1140
1141
/*
1142
 * This decides how to assert whitespace before any of the SYNOPSIS set
1143
 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
1144
 * macro combos).
1145
 */
1146
static void
1147
synopsis_pre(struct termp *p, const struct roff_node *n)
1148
{
1149
	/*
1150
	 * Obviously, if we're not in a SYNOPSIS or no prior macros
1151
	 * exist, do nothing.
1152
	 */
1153

6522
	if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
1154
		return;
1155
1156
	/*
1157
	 * If we're the second in a pair of like elements, emit our
1158
	 * newline and return.  UNLESS we're `Fo', `Fn', `Fn', in which
1159
	 * case we soldier on.
1160
	 */
1161

717
	if (n->prev->tok == n->tok &&
1162
84
	    MDOC_Ft != n->tok &&
1163
84
	    MDOC_Fo != n->tok &&
1164
84
	    MDOC_Fn != n->tok) {
1165
84
		term_newln(p);
1166
84
		return;
1167
	}
1168
1169
	/*
1170
	 * If we're one of the SYNOPSIS set and non-like pair-wise after
1171
	 * another (or Fn/Fo, which we've let slip through) then assert
1172
	 * vertical space, else only newline and move on.
1173
	 */
1174

747
	switch (n->prev->tok) {
1175
	case MDOC_Fd:
1176
	case MDOC_Fn:
1177
	case MDOC_Fo:
1178
	case MDOC_In:
1179
	case MDOC_Vt:
1180
198
		term_vspace(p);
1181
198
		break;
1182
	case MDOC_Ft:
1183

261
		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1184
			term_vspace(p);
1185
			break;
1186
		}
1187
		/* FALLTHROUGH */
1188
	default:
1189
351
		term_newln(p);
1190
351
		break;
1191
	}
1192
2310
}
1193
1194
static int
1195
termp_vt_pre(DECL_ARGS)
1196
{
1197
1198
414
	if (n->type == ROFFT_ELEM) {
1199
126
		synopsis_pre(p, n);
1200
126
		return termp_under_pre(p, pair, meta, n);
1201
81
	} else if (n->type == ROFFT_BLOCK) {
1202
27
		synopsis_pre(p, n);
1203
27
		return 1;
1204
54
	} else if (n->type == ROFFT_HEAD)
1205
27
		return 0;
1206
1207
27
	return termp_under_pre(p, pair, meta, n);
1208
207
}
1209
1210
static int
1211
termp_bold_pre(DECL_ARGS)
1212
{
1213
1214
1272
	termp_tag_pre(p, pair, meta, n);
1215
636
	term_fontpush(p, TERMFONT_BOLD);
1216
636
	return 1;
1217
}
1218
1219
static int
1220
termp_fd_pre(DECL_ARGS)
1221
{
1222
1223
198
	synopsis_pre(p, n);
1224
99
	return termp_bold_pre(p, pair, meta, n);
1225
}
1226
1227
static void
1228
termp_fd_post(DECL_ARGS)
1229
{
1230
1231
198
	term_newln(p);
1232
99
}
1233
1234
static int
1235
termp_sh_pre(DECL_ARGS)
1236
{
1237
1238

51832
	switch (n->type) {
1239
	case ROFFT_BLOCK:
1240
		/*
1241
		 * Vertical space before sections, except
1242
		 * when the previous section was empty.
1243
		 */
1244

9992
		if (n->prev == NULL ||
1245
6479
		    n->prev->tok != MDOC_Sh ||
1246
3513
		    (n->prev->body != NULL &&
1247
3513
		     n->prev->body->child != NULL))
1248
6434
			term_vspace(p);
1249
		break;
1250
	case ROFFT_HEAD:
1251
6479
		term_fontpush(p, TERMFONT_BOLD);
1252
6479
		break;
1253
	case ROFFT_BODY:
1254
9311
		p->tcol->offset = term_len(p, p->defindent);
1255
9311
		term_tab_set(p, NULL);
1256
9311
		term_tab_set(p, "T");
1257
9311
		term_tab_set(p, ".5i");
1258
9311
		switch (n->sec) {
1259
		case SEC_DESCRIPTION:
1260
2777
			fn_prio = 0;
1261
2777
			break;
1262
		case SEC_AUTHORS:
1263
55
			p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1264
55
			break;
1265
		default:
1266
			break;
1267
		}
1268
		break;
1269
	default:
1270
		break;
1271
	}
1272
19437
	return 1;
1273
}
1274
1275
static void
1276
termp_sh_post(DECL_ARGS)
1277
{
1278
1279
51832
	switch (n->type) {
1280
	case ROFFT_HEAD:
1281
6479
		term_newln(p);
1282
6479
		break;
1283
	case ROFFT_BODY:
1284
6479
		term_newln(p);
1285
6479
		p->tcol->offset = 0;
1286
6479
		break;
1287
	default:
1288
		break;
1289
	}
1290
19437
}
1291
1292
static void
1293
termp_lb_post(DECL_ARGS)
1294
{
1295
1296

117
	if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
1297
27
		term_newln(p);
1298
45
}
1299
1300
static int
1301
termp_d1_pre(DECL_ARGS)
1302
{
1303
1304
450
	if (n->type != ROFFT_BLOCK)
1305
150
		return 1;
1306
75
	term_newln(p);
1307
75
	p->tcol->offset += term_len(p, p->defindent + 1);
1308
75
	term_tab_set(p, NULL);
1309
75
	term_tab_set(p, "T");
1310
75
	term_tab_set(p, ".5i");
1311
75
	return 1;
1312
225
}
1313
1314
static int
1315
termp_ft_pre(DECL_ARGS)
1316
{
1317
1318
	/* NB: NODE_LINE does not effect this! */
1319
1278
	synopsis_pre(p, n);
1320
639
	term_fontpush(p, TERMFONT_UNDER);
1321
639
	return 1;
1322
}
1323
1324
static int
1325
termp_fn_pre(DECL_ARGS)
1326
{
1327
	size_t		 rmargin = 0;
1328
	int		 pretty;
1329
1330
1224
	pretty = NODE_SYNPRETTY & n->flags;
1331
1332
612
	synopsis_pre(p, n);
1333
1334
612
	if (NULL == (n = n->child))
1335
		return 0;
1336
1337
612
	if (pretty) {
1338
144
		rmargin = p->tcol->rmargin;
1339
144
		p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1340
144
		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1341
144
	}
1342
1343
612
	assert(n->type == ROFFT_TEXT);
1344
612
	term_fontpush(p, TERMFONT_BOLD);
1345
612
	term_word(p, n->string);
1346
612
	term_fontpop(p);
1347
1348

1053
	if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
1349
189
		tag_put(n->string, ++fn_prio, p->line);
1350
1351
612
	if (pretty) {
1352
144
		term_flushln(p);
1353
144
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1354
144
		p->flags |= TERMP_NOPAD;
1355
144
		p->tcol->offset = p->tcol->rmargin;
1356
144
		p->tcol->rmargin = rmargin;
1357
144
	}
1358
1359
612
	p->flags |= TERMP_NOSPACE;
1360
612
	term_word(p, "(");
1361
612
	p->flags |= TERMP_NOSPACE;
1362
1363
2628
	for (n = n->next; n; n = n->next) {
1364
702
		assert(n->type == ROFFT_TEXT);
1365
702
		term_fontpush(p, TERMFONT_UNDER);
1366
702
		if (pretty)
1367
252
			p->flags |= TERMP_NBRWORD;
1368
702
		term_word(p, n->string);
1369
702
		term_fontpop(p);
1370
1371
702
		if (n->next) {
1372
198
			p->flags |= TERMP_NOSPACE;
1373
198
			term_word(p, ",");
1374
198
		}
1375
	}
1376
1377
612
	p->flags |= TERMP_NOSPACE;
1378
612
	term_word(p, ")");
1379
1380
612
	if (pretty) {
1381
144
		p->flags |= TERMP_NOSPACE;
1382
144
		term_word(p, ";");
1383
144
		term_flushln(p);
1384
144
	}
1385
1386
612
	return 0;
1387
612
}
1388
1389
static int
1390
termp_fa_pre(DECL_ARGS)
1391
{
1392
	const struct roff_node	*nn;
1393
1394
414
	if (n->parent->tok != MDOC_Fo) {
1395
18
		term_fontpush(p, TERMFONT_UNDER);
1396
18
		return 1;
1397
	}
1398
1399
882
	for (nn = n->child; nn; nn = nn->next) {
1400
252
		term_fontpush(p, TERMFONT_UNDER);
1401
252
		p->flags |= TERMP_NBRWORD;
1402
252
		term_word(p, nn->string);
1403
252
		term_fontpop(p);
1404
1405

477
		if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1406
99
			p->flags |= TERMP_NOSPACE;
1407
99
			term_word(p, ",");
1408
99
		}
1409
	}
1410
1411
189
	return 0;
1412
207
}
1413
1414
static int
1415
termp_bd_pre(DECL_ARGS)
1416
{
1417
	size_t			 lm, len;
1418
	struct roff_node	*nn;
1419
	int			 offset;
1420
1421
3720
	if (n->type == ROFFT_BLOCK) {
1422
620
		print_bvspace(p, n, n);
1423
620
		return 1;
1424
1240
	} else if (n->type == ROFFT_HEAD)
1425
620
		return 0;
1426
1427
	/* Handle the -offset argument. */
1428
1429

1015
	if (n->norm->Bd.offs == NULL ||
1430
395
	    ! strcmp(n->norm->Bd.offs, "left"))
1431
		/* nothing */;
1432
386
	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1433
206
		p->tcol->offset += term_len(p, p->defindent + 1);
1434
180
	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1435
9
		p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
1436
	else {
1437
171
		offset = a2width(p, n->norm->Bd.offs);
1438

189
		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
1439
			p->tcol->offset = 0;
1440
171
		else if (offset < SHRT_MAX)
1441
171
			p->tcol->offset += offset;
1442
	}
1443
1444
	/*
1445
	 * If -ragged or -filled are specified, the block does nothing
1446
	 * but change the indentation.  If -unfilled or -literal are
1447
	 * specified, text is printed exactly as entered in the display:
1448
	 * for macro lines, a newline is appended to the line.  Blank
1449
	 * lines are allowed.
1450
	 */
1451
1452

972
	if (n->norm->Bd.type != DISP_literal &&
1453
415
	    n->norm->Bd.type != DISP_unfilled &&
1454
352
	    n->norm->Bd.type != DISP_centered)
1455
334
		return 1;
1456
1457
286
	if (n->norm->Bd.type == DISP_literal) {
1458
205
		term_tab_set(p, NULL);
1459
205
		term_tab_set(p, "T");
1460
205
		term_tab_set(p, "8n");
1461
205
	}
1462
1463
286
	lm = p->tcol->offset;
1464
286
	p->flags |= TERMP_BRNEVER;
1465
4156
	for (nn = n->child; nn != NULL; nn = nn->next) {
1466
1792
		if (n->norm->Bd.type == DISP_centered) {
1467
72
			if (nn->type == ROFFT_TEXT) {
1468
45
				len = term_strlen(p, nn->string);
1469
90
				p->tcol->offset = len >= p->tcol->rmargin ?
1470
90
				    0 : lm + len >= p->tcol->rmargin ?
1471
				    p->tcol->rmargin - len :
1472
45
				    (lm + p->tcol->rmargin - len) / 2;
1473
45
			} else
1474
				p->tcol->offset = lm;
1475
72
		}
1476
1792
		print_mdoc_node(p, pair, meta, nn);
1477
		/*
1478
		 * If the printed node flushes its own line, then we
1479
		 * needn't do it here as well.  This is hacky, but the
1480
		 * notion of selective eoln whitespace is pretty dumb
1481
		 * anyway, so don't sweat it.
1482
		 */
1483
1792
		if (nn->tok < ROFF_MAX)
1484
			continue;
1485

3521
		switch (nn->tok) {
1486
		case MDOC_Sm:
1487
		case MDOC_Bl:
1488
		case MDOC_D1:
1489
		case MDOC_Dl:
1490
		case MDOC_Lp:
1491
		case MDOC_Pp:
1492
			continue;
1493
		default:
1494
			break;
1495
		}
1496

3172
		if (p->flags & TERMP_NONEWLINE ||
1497
3154
		    (nn->next && ! (nn->next->flags & NODE_LINE)))
1498
			continue;
1499
1711
		term_flushln(p);
1500
1711
		p->flags |= TERMP_NOSPACE;
1501
1711
	}
1502
286
	p->flags &= ~TERMP_BRNEVER;
1503
286
	return 0;
1504
1860
}
1505
1506
static void
1507
termp_bd_post(DECL_ARGS)
1508
{
1509
3720
	if (n->type != ROFFT_BODY)
1510
		return;
1511

1035
	if (DISP_literal == n->norm->Bd.type ||
1512
415
	    DISP_unfilled == n->norm->Bd.type)
1513
268
		p->flags |= TERMP_BRNEVER;
1514
620
	p->flags |= TERMP_NOSPACE;
1515
620
	term_newln(p);
1516
620
	p->flags &= ~TERMP_BRNEVER;
1517
2480
}
1518
1519
static int
1520
termp_xx_pre(DECL_ARGS)
1521
{
1522
836
	if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1523
409
		p->flags |= TERMP_PREKEEP;
1524
418
	return 1;
1525
}
1526
1527
static void
1528
termp_xx_post(DECL_ARGS)
1529
{
1530
836
	if (n->aux == 0)
1531
409
		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1532
418
}
1533
1534
static void
1535
termp_pf_post(DECL_ARGS)
1536
{
1537
1538

279
	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1539
63
		p->flags |= TERMP_NOSPACE;
1540
99
}
1541
1542
static int
1543
termp_ss_pre(DECL_ARGS)
1544
{
1545
	struct roff_node *nn;
1546
1547

1215
	switch (n->type) {
1548
	case ROFFT_BLOCK:
1549
135
		term_newln(p);
1550
324
		for (nn = n->prev; nn != NULL; nn = nn->prev)
1551
126
			if ((nn->flags & NODE_NOPRT) == 0)
1552
				break;
1553
135
		if (nn != NULL)
1554
99
			term_vspace(p);
1555
		break;
1556
	case ROFFT_HEAD:
1557
135
		term_fontpush(p, TERMFONT_BOLD);
1558
135
		p->tcol->offset = term_len(p, (p->defindent+1)/2);
1559
135
		break;
1560
	case ROFFT_BODY:
1561
135
		p->tcol->offset = term_len(p, p->defindent);
1562
135
		term_tab_set(p, NULL);
1563
135
		term_tab_set(p, "T");
1564
135
		term_tab_set(p, ".5i");
1565
135
		break;
1566
	default:
1567
		break;
1568
	}
1569
1570
405
	return 1;
1571
}
1572
1573
static void
1574
termp_ss_post(DECL_ARGS)
1575
{
1576
1577

1080
	if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1578
270
		term_newln(p);
1579
405
}
1580
1581
static int
1582
termp_cd_pre(DECL_ARGS)
1583
{
1584
1585
396
	synopsis_pre(p, n);
1586
198
	term_fontpush(p, TERMFONT_BOLD);
1587
198
	return 1;
1588
}
1589
1590
static int
1591
termp_in_pre(DECL_ARGS)
1592
{
1593
1594
216
	synopsis_pre(p, n);
1595
1596

162
	if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
1597
54
		term_fontpush(p, TERMFONT_BOLD);
1598
54
		term_word(p, "#include");
1599
54
		term_word(p, "<");
1600
54
	} else {
1601
54
		term_word(p, "<");
1602
54
		term_fontpush(p, TERMFONT_UNDER);
1603
	}
1604
1605
108
	p->flags |= TERMP_NOSPACE;
1606
108
	return 1;
1607
}
1608
1609
static void
1610
termp_in_post(DECL_ARGS)
1611
{
1612
1613
216
	if (NODE_SYNPRETTY & n->flags)
1614
54
		term_fontpush(p, TERMFONT_BOLD);
1615
1616
108
	p->flags |= TERMP_NOSPACE;
1617
108
	term_word(p, ">");
1618
1619
108
	if (NODE_SYNPRETTY & n->flags)
1620
54
		term_fontpop(p);
1621
108
}
1622
1623
static int
1624
termp_pp_pre(DECL_ARGS)
1625
{
1626
5596
	fn_prio = 0;
1627
2798
	term_vspace(p);
1628
2798
	return 0;
1629
}
1630
1631
static int
1632
termp_skip_pre(DECL_ARGS)
1633
{
1634
1635
90
	return 0;
1636
}
1637
1638
static int
1639
termp_quote_pre(DECL_ARGS)
1640
{
1641
1642

19732
	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1643
4906
		return 1;
1644
1645





2489
	switch (n->tok) {
1646
	case MDOC_Ao:
1647
	case MDOC_Aq:
1648

1149
		term_word(p, n->child != NULL && n->child->next == NULL &&
1649
200
		    n->child->tok == MDOC_Mt ? "<" : "\\(la");
1650
389
		break;
1651
	case MDOC_Bro:
1652
	case MDOC_Brq:
1653
90
		term_word(p, "{");
1654
90
		break;
1655
	case MDOC_Oo:
1656
	case MDOC_Op:
1657
	case MDOC_Bo:
1658
	case MDOC_Bq:
1659
1510
		term_word(p, "[");
1660
1510
		break;
1661
	case MDOC__T:
1662
		/* FALLTHROUGH */
1663
	case MDOC_Do:
1664
	case MDOC_Dq:
1665
120
		term_word(p, "\\(Lq");
1666
120
		break;
1667
	case MDOC_En:
1668

63
		if (NULL == n->norm->Es ||
1669
27
		    NULL == n->norm->Es->child)
1670
18
			return 1;
1671
18
		term_word(p, n->norm->Es->child->string);
1672
18
		break;
1673
	case MDOC_Po:
1674
	case MDOC_Pq:
1675
224
		term_word(p, "(");
1676
224
		break;
1677
	case MDOC_Qo:
1678
	case MDOC_Qq:
1679
45
		term_word(p, "\"");
1680
45
		break;
1681
	case MDOC_Ql:
1682
	case MDOC_So:
1683
	case MDOC_Sq:
1684
75
		term_word(p, "\\(oq");
1685
75
		break;
1686
	default:
1687
		abort();
1688
	}
1689
1690
2471
	p->flags |= TERMP_NOSPACE;
1691
2471
	return 1;
1692
7395
}
1693
1694
static void
1695
termp_quote_post(DECL_ARGS)
1696
{
1697
1698

19732
	if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1699
		return;
1700
1701
2489
	p->flags |= TERMP_NOSPACE;
1702
1703





2489
	switch (n->tok) {
1704
	case MDOC_Ao:
1705
	case MDOC_Aq:
1706

1041
		term_word(p, n->child != NULL && n->child->next == NULL &&
1707
200
		    n->child->tok == MDOC_Mt ? ">" : "\\(ra");
1708
389
		break;
1709
	case MDOC_Bro:
1710
	case MDOC_Brq:
1711
90
		term_word(p, "}");
1712
90
		break;
1713
	case MDOC_Oo:
1714
	case MDOC_Op:
1715
	case MDOC_Bo:
1716
	case MDOC_Bq:
1717
1510
		term_word(p, "]");
1718
1510
		break;
1719
	case MDOC__T:
1720
		/* FALLTHROUGH */
1721
	case MDOC_Do:
1722
	case MDOC_Dq:
1723
120
		term_word(p, "\\(Rq");
1724
120
		break;
1725
	case MDOC_En:
1726

54
		if (n->norm->Es == NULL ||
1727
27
		    n->norm->Es->child == NULL ||
1728
18
		    n->norm->Es->child->next == NULL)
1729
18
			p->flags &= ~TERMP_NOSPACE;
1730
		else
1731
18
			term_word(p, n->norm->Es->child->next->string);
1732
		break;
1733
	case MDOC_Po:
1734
	case MDOC_Pq:
1735
224
		term_word(p, ")");
1736
224
		break;
1737
	case MDOC_Qo:
1738
	case MDOC_Qq:
1739
45
		term_word(p, "\"");
1740
45
		break;
1741
	case MDOC_Ql:
1742
	case MDOC_So:
1743
	case MDOC_Sq:
1744
75
		term_word(p, "\\(cq");
1745
75
		break;
1746
	default:
1747
		abort();
1748
	}
1749
7395
}
1750
1751
static int
1752
termp_eo_pre(DECL_ARGS)
1753
{
1754
1755
1458
	if (n->type != ROFFT_BODY)
1756
522
		return 1;
1757
1758

252
	if (n->end == ENDBODY_NOT &&
1759
189
	    n->parent->head->child == NULL &&
1760
81
	    n->child != NULL &&
1761
45
	    n->child->end != ENDBODY_NOT)
1762
9
		term_word(p, "\\&");
1763

234
	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1764

288
	     n->parent->head->child != NULL && (n->child != NULL ||
1765
45
	     (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1766
108
		p->flags |= TERMP_NOSPACE;
1767
1768
207
	return 1;
1769
729
}
1770
1771
static void
1772
termp_eo_post(DECL_ARGS)
1773
{
1774
	int	 body, tail;
1775
1776
1422
	if (n->type != ROFFT_BODY)
1777
522
		return;
1778
1779
189
	if (n->end != ENDBODY_NOT) {
1780
36
		p->flags &= ~TERMP_NOSPACE;
1781
36
		return;
1782
	}
1783
1784
369
	body = n->child != NULL || n->parent->head->child != NULL;
1785
450
	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1786
1787
153
	if (body && tail)
1788
72
		p->flags |= TERMP_NOSPACE;
1789
81
	else if ( ! (body || tail))
1790
18
		term_word(p, "\\&");
1791
63
	else if ( ! tail)
1792
45
		p->flags &= ~TERMP_NOSPACE;
1793
864
}
1794
1795
static int
1796
termp_fo_pre(DECL_ARGS)
1797
{
1798
	size_t		 rmargin = 0;
1799
	int		 pretty;
1800
1801
1188
	pretty = NODE_SYNPRETTY & n->flags;
1802
1803
594
	if (n->type == ROFFT_BLOCK) {
1804
198
		synopsis_pre(p, n);
1805
198
		return 1;
1806
396
	} else if (n->type == ROFFT_BODY) {
1807
198
		if (pretty) {
1808
72
			rmargin = p->tcol->rmargin;
1809
72
			p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1810
72
			p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1811
					TERMP_HANG;
1812
72
		}
1813
198
		p->flags |= TERMP_NOSPACE;
1814
198
		term_word(p, "(");
1815
198
		p->flags |= TERMP_NOSPACE;
1816
198
		if (pretty) {
1817
72
			term_flushln(p);
1818
72
			p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1819
					TERMP_HANG);
1820
72
			p->flags |= TERMP_NOPAD;
1821
72
			p->tcol->offset = p->tcol->rmargin;
1822
72
			p->tcol->rmargin = rmargin;
1823
72
		}
1824
198
		return 1;
1825
	}
1826
1827
198
	if (NULL == n->child)
1828
9
		return 0;
1829
1830
	/* XXX: we drop non-initial arguments as per groff. */
1831
1832
189
	assert(n->child->string);
1833
189
	term_fontpush(p, TERMFONT_BOLD);
1834
189
	term_word(p, n->child->string);
1835
189
	return 0;
1836
594
}
1837
1838
static void
1839
termp_fo_post(DECL_ARGS)
1840
{
1841
1842
1188
	if (n->type != ROFFT_BODY)
1843
		return;
1844
1845
198
	p->flags |= TERMP_NOSPACE;
1846
198
	term_word(p, ")");
1847
1848
198
	if (NODE_SYNPRETTY & n->flags) {
1849
72
		p->flags |= TERMP_NOSPACE;
1850
72
		term_word(p, ";");
1851
72
		term_flushln(p);
1852
72
	}
1853
594
}
1854
1855
static int
1856
termp_bf_pre(DECL_ARGS)
1857
{
1858
1859
918
	if (n->type == ROFFT_HEAD)
1860
153
		return 0;
1861
306
	else if (n->type != ROFFT_BODY)
1862
153
		return 1;
1863
1864
153
	if (FONT_Em == n->norm->Bf.font)
1865
45
		term_fontpush(p, TERMFONT_UNDER);
1866
108
	else if (FONT_Sy == n->norm->Bf.font)
1867
72
		term_fontpush(p, TERMFONT_BOLD);
1868
	else
1869
36
		term_fontpush(p, TERMFONT_NONE);
1870
1871
153
	return 1;
1872
459
}
1873
1874
static int
1875
termp_sm_pre(DECL_ARGS)
1876
{
1877
1878
940
	if (NULL == n->child)
1879
45
		p->flags ^= TERMP_NONOSPACE;
1880
850
	else if (0 == strcmp("on", n->child->string))
1881
425
		p->flags &= ~TERMP_NONOSPACE;
1882
	else
1883
425
		p->flags |= TERMP_NONOSPACE;
1884
1885

894
	if (p->col && ! (TERMP_NONOSPACE & p->flags))
1886
216
		p->flags &= ~TERMP_NOSPACE;
1887
1888
470
	return 0;
1889
}
1890
1891
static int
1892
termp_ap_pre(DECL_ARGS)
1893
{
1894
1895
72
	p->flags |= TERMP_NOSPACE;
1896
36
	term_word(p, "'");
1897
36
	p->flags |= TERMP_NOSPACE;
1898
36
	return 1;
1899
}
1900
1901
static void
1902
termp____post(DECL_ARGS)
1903
{
1904
1905
	/*
1906
	 * Handle lists of authors.  In general, print each followed by
1907
	 * a comma.  Don't print the comma if there are only two
1908
	 * authors.
1909
	 */
1910

1896
	if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
1911

72
		if (NULL == n->next->next || MDOC__A != n->next->next->tok)
1912

36
			if (NULL == n->prev || MDOC__A != n->prev->tok)
1913
				return;
1914
1915
	/* TODO: %U. */
1916
1917

1568
	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
1918
		return;
1919
1920
784
	p->flags |= TERMP_NOSPACE;
1921
784
	if (NULL == n->next) {
1922
128
		term_word(p, ".");
1923
128
		p->flags |= TERMP_SENTENCE;
1924
128
	} else
1925
656
		term_word(p, ",");
1926
784
}
1927
1928
static int
1929
termp_li_pre(DECL_ARGS)
1930
{
1931
1932
5218
	termp_tag_pre(p, pair, meta, n);
1933
2609
	term_fontpush(p, TERMFONT_NONE);
1934
2609
	return 1;
1935
}
1936
1937
static int
1938
termp_lk_pre(DECL_ARGS)
1939
{
1940
	const struct roff_node *link, *descr, *punct;
1941
	int display;
1942
1943
198
	if ((link = n->child) == NULL)
1944
		return 0;
1945
1946
	/* Find beginning of trailing punctuation. */
1947
99
	punct = n->last;
1948

459
	while (punct != link && punct->flags & NODE_DELIMC)
1949
18
		punct = punct->prev;
1950
99
	punct = punct->next;
1951
1952
	/* Link text. */
1953

189
	if ((descr = link->next) != NULL && descr != punct) {
1954
90
		term_fontpush(p, TERMFONT_UNDER);
1955
414
		while (descr != punct) {
1956
117
			if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1957
				p->flags |= TERMP_NOSPACE;
1958
117
			term_word(p, descr->string);
1959
117
			descr = descr->next;
1960
		}
1961
90
		term_fontpop(p);
1962
90
		p->flags |= TERMP_NOSPACE;
1963
90
		term_word(p, ":");
1964
90
	}
1965
1966
	/* Link target. */
1967
99
	display = term_strlen(p, link->string) >= 26;
1968
99
	if (display) {
1969
9
		term_newln(p);
1970
9
		p->tcol->offset += term_len(p, p->defindent + 1);
1971
9
	}
1972
99
	term_fontpush(p, TERMFONT_BOLD);
1973
99
	term_word(p, link->string);
1974
99
	term_fontpop(p);
1975
1976
	/* Trailing punctuation. */
1977
234
	while (punct != NULL) {
1978
18
		p->flags |= TERMP_NOSPACE;
1979
18
		term_word(p, punct->string);
1980
18
		punct = punct->next;
1981
	}
1982
99
	if (display)
1983
9
		term_newln(p);
1984
99
	return 0;
1985
99
}
1986
1987
static int
1988
termp_bk_pre(DECL_ARGS)
1989
{
1990
1991

1512
	switch (n->type) {
1992
	case ROFFT_BLOCK:
1993
		break;
1994
	case ROFFT_HEAD:
1995
216
		return 0;
1996
	case ROFFT_BODY:
1997

261
		if (n->parent->args != NULL || n->prev->child == NULL)
1998
216
			p->flags |= TERMP_PREKEEP;
1999
		break;
2000
	default:
2001
		abort();
2002
	}
2003
2004
432
	return 1;
2005
648
}
2006
2007
static void
2008
termp_bk_post(DECL_ARGS)
2009
{
2010
2011
1296
	if (n->type == ROFFT_BODY)
2012
216
		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2013
648
}
2014
2015
static void
2016
termp__t_post(DECL_ARGS)
2017
{
2018
2019
	/*
2020
	 * If we're in an `Rs' and there's a journal present, then quote
2021
	 * us instead of underlining us (for disambiguation).
2022
	 */
2023

216
	if (n->parent && MDOC_Rs == n->parent->tok &&
2024
54
	    n->parent->norm->Rs.quote_T)
2025
36
		termp_quote_post(p, pair, meta, n);
2026
2027
54
	termp____post(p, pair, meta, n);
2028
54
}
2029
2030
static int
2031
termp__t_pre(DECL_ARGS)
2032
{
2033
2034
	/*
2035
	 * If we're in an `Rs' and there's a journal present, then quote
2036
	 * us instead of underlining us (for disambiguation).
2037
	 */
2038

216
	if (n->parent && MDOC_Rs == n->parent->tok &&
2039
54
	    n->parent->norm->Rs.quote_T)
2040
36
		return termp_quote_pre(p, pair, meta, n);
2041
2042
18
	term_fontpush(p, TERMFONT_UNDER);
2043
18
	return 1;
2044
54
}
2045
2046
static int
2047
termp_under_pre(DECL_ARGS)
2048
{
2049
2050
4710
	term_fontpush(p, TERMFONT_UNDER);
2051
2355
	return 1;
2052
}
2053
2054
static int
2055
termp_em_pre(DECL_ARGS)
2056
{
2057

3447
	if (n->child != NULL &&
2058
1149
	    n->child->type == ROFFT_TEXT)
2059
1149
		tag_put(n->child->string, 0, p->line);
2060
1149
	term_fontpush(p, TERMFONT_UNDER);
2061
1149
	return 1;
2062
}
2063
2064
static int
2065
termp_sy_pre(DECL_ARGS)
2066
{
2067

3492
	if (n->child != NULL &&
2068
1164
	    n->child->type == ROFFT_TEXT)
2069
1164
		tag_put(n->child->string, 0, p->line);
2070
1164
	term_fontpush(p, TERMFONT_BOLD);
2071
1164
	return 1;
2072
}
2073
2074
static int
2075
termp_er_pre(DECL_ARGS)
2076
{
2077
2078

54
	if (n->sec == SEC_ERRORS &&
2079
	    (n->parent->tok == MDOC_It ||
2080
	     (n->parent->tok == MDOC_Bq &&
2081
	      n->parent->parent->parent->tok == MDOC_It)))
2082
		tag_put(n->child->string, 1, p->line);
2083
27
	return 1;
2084
}
2085
2086
static int
2087
termp_tag_pre(DECL_ARGS)
2088
{
2089
2090

9772
	if (n->child != NULL &&
2091
4715
	    n->child->type == ROFFT_TEXT &&
2092
4715
	    (n->prev == NULL ||
2093
4262
	     (n->prev->type == ROFFT_TEXT &&
2094
2555
	      strcmp(n->prev->string, "|") == 0)) &&
2095
633
	    (n->parent->tok == MDOC_It ||
2096
569
	     (n->parent->tok == MDOC_Xo &&
2097
	      n->parent->parent->prev == NULL &&
2098
	      n->parent->parent->parent->tok == MDOC_It)))
2099
64
		tag_put(n->child->string, 1, p->line);
2100
4886
	return 1;
2101
}