GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/mdoc_term.c Lines: 861 897 96.0 %
Date: 2017-11-13 Branches: 617 763 80.9 %

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
1982
	p = (struct termp *)arg;
260
991
	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
261
991
	term_tab_set(p, NULL);
262
991
	term_tab_set(p, "T");
263
991
	term_tab_set(p, ".5i");
264
265
991
	n = mdoc->first->child;
266
991
	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
991
		save_defindent = p->defindent;
280
991
		if (p->defindent == 0)
281
991
			p->defindent = 5;
282
991
		term_begin(p, print_mdoc_head, print_mdoc_foot,
283
991
		    &mdoc->meta);
284

15829
		while (n != NULL && n->flags & NODE_NOPRT)
285
2967
			n = n->next;
286
991
		if (n != NULL) {
287
988
			if (n->tok != MDOC_Sh)
288
9
				term_vspace(p);
289
988
			print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
290
988
		}
291
991
		term_end(p);
292
991
		p->defindent = save_defindent;
293
	}
294
991
}
295
296
static void
297
print_mdoc_nodelist(DECL_ARGS)
298
{
299
300
181100
	while (n != NULL) {
301
57322
		print_mdoc_node(p, pair, meta, n);
302
57322
		n = n->next;
303
	}
304
22152
}
305
306
static void
307
print_mdoc_node(DECL_ARGS)
308
{
309
	int		 chld;
310
115846
	struct termpair	 npair;
311
	size_t		 offset, rmargin;
312
313
57923
	if (n->flags & NODE_NOPRT)
314
30
		return;
315
316
	chld = 1;
317
57893
	offset = p->tcol->offset;
318
57893
	rmargin = p->tcol->rmargin;
319
57893
	n->flags &= ~NODE_ENDED;
320
57893
	n->prev_font = p->fonti;
321
322
57893
	memset(&npair, 0, sizeof(struct termpair));
323
57893
	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

60191
	if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
331
348
		p->flags &= ~TERMP_KEEP;
332
348
		p->flags |= TERMP_PREKEEP;
333
348
	}
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

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

21802
		if (*n->string == ' ' && n->flags & NODE_LINE &&
343
18
		    (p->flags & TERMP_NONEWLINE) == 0)
344
18
			term_newln(p);
345
21763
		if (NODE_DELIMC & n->flags)
346
780
			p->flags |= TERMP_NOSPACE;
347
21763
		term_word(p, n->string);
348
21763
		if (NODE_DELIMO & n->flags)
349
264
			p->flags |= TERMP_NOSPACE;
350
		break;
351
	case ROFFT_EQN:
352
102
		if ( ! (n->flags & NODE_LINE))
353
6
			p->flags |= TERMP_NOSPACE;
354
102
		term_eqn(p, n->eqn);
355

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

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

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

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

45959
		if (termacts[n->tok].post == NULL || n->flags & NODE_ENDED)
391
			break;
392
18685
		(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
18685
		if (ENDBODY_NOT != n->end)
400
150
			n->body->flags |= NODE_ENDED;
401
		break;
402
	}
403
404
55235
	if (NODE_EOS & n->flags)
405
705
		p->flags |= TERMP_SENTENCE;
406
407
55235
	if (n->type != ROFFT_TEXT)
408
33472
		p->tcol->offset = offset;
409
55235
	p->tcol->rmargin = rmargin;
410
113158
}
411
412
static void
413
print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
414
{
415
	size_t sz;
416
417
1982
	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
991
	term_vspace(p);
428
429
991
	p->tcol->offset = 0;
430
991
	sz = term_strlen(p, meta->date);
431
2970
	p->tcol->rmargin = p->maxrmargin > sz ?
432
988
	    (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
433
991
	p->trailspace = 1;
434
991
	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
435
436
991
	term_word(p, meta->os);
437
991
	term_flushln(p);
438
439
991
	p->tcol->offset = p->tcol->rmargin;
440
991
	sz = term_strlen(p, meta->os);
441
2970
	p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
442
991
	p->flags |= TERMP_NOSPACE;
443
444
991
	term_word(p, meta->date);
445
991
	term_flushln(p);
446
447
991
	p->tcol->offset = p->tcol->rmargin;
448
991
	p->tcol->rmargin = p->maxrmargin;
449
991
	p->trailspace = 0;
450
991
	p->flags &= ~TERMP_NOBREAK;
451
991
	p->flags |= TERMP_NOSPACE;
452
453
991
	term_word(p, meta->os);
454
991
	term_flushln(p);
455
456
991
	p->tcol->offset = 0;
457
991
	p->tcol->rmargin = p->maxrmargin;
458
991
	p->flags = 0;
459
991
}
460
461
static void
462
print_mdoc_head(struct termp *p, const struct roff_meta *meta)
463
{
464
1982
	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
991
	assert(meta->vol);
481
991
	if (NULL == meta->arch)
482
988
		volume = mandoc_strdup(meta->vol);
483
	else
484
3
		mandoc_asprintf(&volume, "%s (%s)",
485
		    meta->vol, meta->arch);
486
991
	vollen = term_strlen(p, volume);
487
488
991
	if (NULL == meta->msec)
489
12
		title = mandoc_strdup(meta->title);
490
	else
491
979
		mandoc_asprintf(&title, "%s(%s)",
492
		    meta->title, meta->msec);
493
991
	titlen = term_strlen(p, title);
494
495
991
	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
496
991
	p->trailspace = 1;
497
991
	p->tcol->offset = 0;
498
2970
	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
499
988
	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
500
6
	    vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
501
502
991
	term_word(p, title);
503
991
	term_flushln(p);
504
505
991
	p->flags |= TERMP_NOSPACE;
506
991
	p->tcol->offset = p->tcol->rmargin;
507
2973
	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
508
1982
	    p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
509
510
991
	term_word(p, volume);
511
991
	term_flushln(p);
512
513
991
	p->flags &= ~TERMP_NOBREAK;
514
991
	p->trailspace = 0;
515
991
	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
516
988
		p->flags |= TERMP_NOSPACE;
517
988
		p->tcol->offset = p->tcol->rmargin;
518
988
		p->tcol->rmargin = p->maxrmargin;
519
988
		term_word(p, title);
520
988
		term_flushln(p);
521
988
	}
522
523
991
	p->flags &= ~TERMP_NOSPACE;
524
991
	p->tcol->offset = 0;
525
991
	p->tcol->rmargin = p->maxrmargin;
526
991
	free(title);
527
991
	free(volume);
528
991
}
529
530
static int
531
a2width(const struct termp *p, const char *v)
532
{
533
3774
	struct roffsu	 su;
534
	const char	*end;
535
536
1887
	end = a2roffsu(v, &su, SCALE_MAX);
537

3528
	if (end == NULL || *end != '\0') {
538
246
		SCALE_HS_INIT(&su, term_strlen(p, v));
539
246
		su.scale /= term_strlen(p, "0");
540
246
	}
541
3774
	return term_hen(p, &su);
542
1887
}
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
2878
	assert(n);
557
558
1439
	term_newln(p);
559
560

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

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

2794
	while (nn->prev != NULL && nn->prev->flags & NODE_NOPRT)
569
		nn = nn->prev;
570
3784
	while (nn->prev == NULL) {
571
833
		do {
572
1663
			nn = nn->parent;
573
1663
			if (nn->type == ROFFT_ROOT)
574
3
				return;
575
1660
		} while (nn->type != ROFFT_BLOCK);
576

1580
		if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
577
98
			return;
578

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

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

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

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

39
		if (n->prev && MDOC_It == n->prev->tok) {
593
6
			assert(n->prev->body);
594
6
			if (NULL == n->prev->body->child)
595
				return;
596
		}
597
598
960
	term_vspace(p);
599
2399
}
600
601
602
static int
603
termp_it_pre(DECL_ARGS)
604
{
605
8046
	struct roffsu		su;
606
4023
	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
4023
	if (n->type == ROFFT_BLOCK) {
613
1233
		print_bvspace(p, n->parent->parent, n);
614
1233
		return 1;
615
	}
616
617
2790
	bl = n->parent->parent->parent;
618
2790
	type = bl->norm->Bl.type;
619
620
	/*
621
	 * Defaults for specific list types.
622
	 */
623
624


2790
	switch (type) {
625
	case LIST_bullet:
626
	case LIST_dash:
627
	case LIST_hyphen:
628
	case LIST_enum:
629
861
		width = term_len(p, 2);
630
861
		break;
631
	case LIST_hang:
632
	case LIST_tag:
633
708
		width = term_len(p, 8);
634
708
		break;
635
	case LIST_column:
636
681
		width = term_len(p, 10);
637
681
		break;
638
	default:
639
		width = 0;
640
540
		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
2790
	if (bl->norm->Bl.offs != NULL) {
651
291
		offset = a2width(p, bl->norm->Bl.offs);
652

303
		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
653
			offset = -p->tcol->offset;
654
291
		else if (offset > SHRT_MAX)
655
			offset = 0;
656
	}
657
658
2790
	switch (type) {
659
	case LIST_column:
660
681
		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
504
		ncols = bl->norm->Bl.ncols;
673
1512
		dcol = ncols < 5 ? term_len(p, 4) :
674
45
		    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
1623
		for (i = 0, nn = n->prev;
682
1623
		    nn->prev && i < (int)ncols;
683
555
		    nn = nn->prev, i++) {
684
555
			SCALE_HS_INIT(&su,
685
			    term_strlen(p, bl->norm->Bl.cols[i]));
686
555
			su.scale /= term_strlen(p, "0");
687
555
			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
504
		if (i >= (int)ncols)
697
			break;
698
699
		/*
700
		 * Use the declared column widths, extended as explained
701
		 * in the preceding paragraph.
702
		 */
703
456
		SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
704
456
		su.scale /= term_strlen(p, "0");
705
456
		width = term_hen(p, &su) + dcol;
706
456
		break;
707
	default:
708
2109
		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
1539
		width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
717

1611
		if (width < 0 && (size_t)(-width) > p->tcol->offset)
718
			width = -p->tcol->offset;
719
1539
		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
3066
	p->flags |= TERMP_NOSPACE;
730
731
3066
	switch (type) {
732
	case LIST_diag:
733
90
		if (n->type == ROFFT_BODY)
734
45
			term_word(p, "\\ \\ ");
735
		break;
736
	case LIST_inset:
737

279
		if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
738
90
			term_word(p, "\\ ");
739
		break;
740
	default:
741
		break;
742
	}
743
744
2790
	p->flags |= TERMP_NOSPACE;
745
746
2790
	switch (type) {
747
	case LIST_diag:
748
90
		if (n->type == ROFFT_HEAD)
749
45
			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


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

546
		if (NULL == n->next || NULL == n->next->child)
787
18
			p->flags |= TERMP_HANG;
788
		break;
789
	case LIST_column:
790
681
		if (n->type == ROFFT_HEAD)
791
			break;
792
793
504
		if (NULL == n->next) {
794
177
			p->flags &= ~TERMP_NOBREAK;
795
			p->trailspace = 0;
796
177
		} else {
797
327
			p->flags |= TERMP_NOBREAK;
798
			p->trailspace = 1;
799
		}
800
801
504
		break;
802
	case LIST_diag:
803
90
		if (n->type != ROFFT_HEAD)
804
			break;
805
45
		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
806
45
		p->trailspace = 1;
807
45
		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
5040
	p->tcol->offset += offset;
819
820


5040
	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
1569
		if (n->type == ROFFT_HEAD)
828
786
			p->tcol->rmargin = p->tcol->offset + width;
829
		else
830
783
			p->tcol->offset += width;
831
		break;
832
	case LIST_column:
833
681
		assert(width);
834
681
		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
681
		if (n->type == ROFFT_HEAD)
840
			break;
841

681
		if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
842
153
			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
2790
	if (n->type == ROFFT_HEAD)
854

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

3825
	switch (type) {
881
	case LIST_bullet:
882
	case LIST_item:
883
	case LIST_dash:
884
	case LIST_hyphen:
885
	case LIST_enum:
886
1065
		if (n->type == ROFFT_HEAD)
887
534
			return 0;
888
		break;
889
	case LIST_column:
890
681
		if (n->type == ROFFT_HEAD)
891
177
			return 0;
892
504
		p->minbl = 0;
893
504
		break;
894
	default:
895
		break;
896
	}
897
898
2079
	return 1;
899
4023
}
900
901
static void
902
termp_it_post(DECL_ARGS)
903
{
904
	enum mdoc_list	   type;
905
906
8046
	if (n->type == ROFFT_BLOCK)
907
1233
		return;
908
909
2790
	type = n->parent->parent->parent->norm->Bl.type;
910
911

2790
	switch (type) {
912
	case LIST_item:
913
	case LIST_diag:
914
	case LIST_inset:
915
480
		if (n->type == ROFFT_BODY)
916
240
			term_newln(p);
917
		break;
918
	case LIST_column:
919
681
		if (n->type == ROFFT_BODY)
920
504
			term_flushln(p);
921
		break;
922
	default:
923
1629
		term_newln(p);
924
1629
		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
2790
	p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
934
2790
	p->trailspace = 0;
935
6813
}
936
937
static int
938
termp_nm_pre(DECL_ARGS)
939
{
940
	const char	*cp;
941
942
2798
	if (n->type == ROFFT_BLOCK) {
943
99
		p->flags |= TERMP_PREKEEP;
944
99
		return 1;
945
	}
946
947
1300
	if (n->type == ROFFT_BODY) {
948
96
		if (n->child == NULL)
949
21
			return 0;
950
75
		p->flags |= TERMP_NOSPACE;
951
		cp = NULL;
952
75
		if (n->prev->child != NULL)
953
75
		    cp = n->prev->child->string;
954
75
		if (cp == NULL)
955
			cp = meta->name;
956
75
		if (cp == NULL)
957
			p->tcol->offset += term_len(p, 6);
958
		else
959
150
			p->tcol->offset += term_len(p, 1) +
960
75
			    term_strlen(p, cp);
961
75
		return 1;
962
	}
963
964
1204
	if (n->child == NULL)
965
18
		return 0;
966
967
1186
	if (n->type == ROFFT_HEAD)
968
99
		synopsis_pre(p, n->parent);
969
970

1282
	if (n->type == ROFFT_HEAD &&
971
195
	    n->next != NULL && n->next->child != NULL) {
972
75
		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
973
75
		p->trailspace = 1;
974
75
		p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
975
75
		if (n->child == NULL)
976
			p->tcol->rmargin += term_strlen(p, meta->name);
977
75
		else if (n->child->type == ROFFT_TEXT) {
978
75
			p->tcol->rmargin += term_strlen(p, n->child->string);
979
75
			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
1186
	term_fontpush(p, TERMFONT_BOLD);
988
1186
	return 1;
989
1399
}
990
991
static void
992
termp_nm_post(DECL_ARGS)
993
{
994
995
2798
	if (n->type == ROFFT_BLOCK) {
996
99
		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
997

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

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

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

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

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

132
	if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
1053
27
		p->flags |= TERMP_SPLIT;
1054
1055
90
	return 1;
1056
99
}
1057
1058
static int
1059
termp_ns_pre(DECL_ARGS)
1060
{
1061
1062
174
	if ( ! (NODE_LINE & n->flags))
1063
84
		p->flags |= TERMP_NOSPACE;
1064
87
	return 1;
1065
}
1066
1067
static int
1068
termp_rs_pre(DECL_ARGS)
1069
{
1070
1071
324
	if (SEC_SEE_ALSO != n->sec)
1072
72
		return 1;
1073

120
	if (n->type == ROFFT_BLOCK && n->prev != NULL)
1074
30
		term_vspace(p);
1075
90
	return 1;
1076
162
}
1077
1078
static int
1079
termp_ex_pre(DECL_ARGS)
1080
{
1081
108
	term_newln(p);
1082
54
	return 1;
1083
}
1084
1085
static int
1086
termp_nd_pre(DECL_ARGS)
1087
{
1088
1089
5928
	if (n->type == ROFFT_BODY)
1090
988
		term_word(p, "\\(en");
1091
2964
	return 1;
1092
}
1093
1094
static int
1095
termp_bl_pre(DECL_ARGS)
1096
{
1097
1098
4932
	return n->type != ROFFT_HEAD;
1099
}
1100
1101
static void
1102
termp_bl_post(DECL_ARGS)
1103
{
1104
1105
5058
	if (n->type != ROFFT_BLOCK)
1106
		return;
1107
843
	term_newln(p);
1108

1665
	if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
1109
		return;
1110
72
	term_tab_set(p, NULL);
1111
72
	term_tab_set(p, "T");
1112
72
	term_tab_set(p, ".5i");
1113
2601
}
1114
1115
static int
1116
termp_xr_pre(DECL_ARGS)
1117
{
1118
1119
108
	if (NULL == (n = n->child))
1120
		return 0;
1121
1122
54
	assert(n->type == ROFFT_TEXT);
1123
54
	term_word(p, n->string);
1124
1125
54
	if (NULL == (n = n->next))
1126
6
		return 0;
1127
1128
48
	p->flags |= TERMP_NOSPACE;
1129
48
	term_word(p, "(");
1130
48
	p->flags |= TERMP_NOSPACE;
1131
1132
48
	assert(n->type == ROFFT_TEXT);
1133
48
	term_word(p, n->string);
1134
1135
48
	p->flags |= TERMP_NOSPACE;
1136
48
	term_word(p, ")");
1137
1138
48
	return 0;
1139
54
}
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

2169
	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

237
	if (n->prev->tok == n->tok &&
1162
27
	    MDOC_Ft != n->tok &&
1163
27
	    MDOC_Fo != n->tok &&
1164
27
	    MDOC_Fn != n->tok) {
1165
27
		term_newln(p);
1166
27
		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

249
	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
66
		term_vspace(p);
1181
66
		break;
1182
	case MDOC_Ft:
1183

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

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

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

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

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

159
		if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1406
33
			p->flags |= TERMP_NOSPACE;
1407
33
			term_word(p, ",");
1408
33
		}
1409
	}
1410
1411
63
	return 0;
1412
69
}
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
1236
	if (n->type == ROFFT_BLOCK) {
1422
206
		print_bvspace(p, n, n);
1423
206
		return 1;
1424
412
	} else if (n->type == ROFFT_HEAD)
1425
206
		return 0;
1426
1427
	/* Handle the -offset argument. */
1428
1429

335
	if (n->norm->Bd.offs == NULL ||
1430
129
	    ! strcmp(n->norm->Bd.offs, "left"))
1431
		/* nothing */;
1432
126
	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1433
66
		p->tcol->offset += term_len(p, p->defindent + 1);
1434
60
	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1435
3
		p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
1436
	else {
1437
57
		offset = a2width(p, n->norm->Bd.offs);
1438

63
		if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
1439
			p->tcol->offset = 0;
1440
57
		else if (offset < SHRT_MAX)
1441
57
			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

323
	if (n->norm->Bd.type != DISP_literal &&
1453
140
	    n->norm->Bd.type != DISP_unfilled &&
1454
117
	    n->norm->Bd.type != DISP_centered)
1455
111
		return 1;
1456
1457
95
	if (n->norm->Bd.type == DISP_literal) {
1458
66
		term_tab_set(p, NULL);
1459
66
		term_tab_set(p, "T");
1460
66
		term_tab_set(p, "8n");
1461
66
	}
1462
1463
95
	lm = p->tcol->offset;
1464
95
	p->flags |= TERMP_BRNEVER;
1465
1392
	for (nn = n->child; nn != NULL; nn = nn->next) {
1466
601
		if (n->norm->Bd.type == DISP_centered) {
1467
24
			if (nn->type == ROFFT_TEXT) {
1468
15
				len = term_strlen(p, nn->string);
1469
30
				p->tcol->offset = len >= p->tcol->rmargin ?
1470
30
				    0 : lm + len >= p->tcol->rmargin ?
1471
				    p->tcol->rmargin - len :
1472
15
				    (lm + p->tcol->rmargin - len) / 2;
1473
15
			} else
1474
				p->tcol->offset = lm;
1475
24
		}
1476
601
		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
601
		if (nn->tok < ROFF_MAX)
1484
			continue;
1485

1181
		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

1065
		if (p->flags & TERMP_NONEWLINE ||
1497
1059
		    (nn->next && ! (nn->next->flags & NODE_LINE)))
1498
			continue;
1499
574
		term_flushln(p);
1500
574
		p->flags |= TERMP_NOSPACE;
1501
574
	}
1502
95
	p->flags &= ~TERMP_BRNEVER;
1503
95
	return 0;
1504
618
}
1505
1506
static void
1507
termp_bd_post(DECL_ARGS)
1508
{
1509
1236
	if (n->type != ROFFT_BODY)
1510
		return;
1511

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

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

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

360
	if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1578
90
		term_newln(p);
1579
135
}
1580
1581
static int
1582
termp_cd_pre(DECL_ARGS)
1583
{
1584
1585
132
	synopsis_pre(p, n);
1586
66
	term_fontpush(p, TERMFONT_BOLD);
1587
66
	return 1;
1588
}
1589
1590
static int
1591
termp_in_pre(DECL_ARGS)
1592
{
1593
1594
72
	synopsis_pre(p, n);
1595
1596

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

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





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

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

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

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





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

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

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

84
	if (n->end == ENDBODY_NOT &&
1759
63
	    n->parent->head->child == NULL &&
1760
27
	    n->child != NULL &&
1761
15
	    n->child->end != ENDBODY_NOT)
1762
3
		term_word(p, "\\&");
1763

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

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

297
	if (p->col && ! (TERMP_NONOSPACE & p->flags))
1886
72
		p->flags &= ~TERMP_NOSPACE;
1887
1888
156
	return 0;
1889
}
1890
1891
static int
1892
termp_ap_pre(DECL_ARGS)
1893
{
1894
1895
24
	p->flags |= TERMP_NOSPACE;
1896
12
	term_word(p, "'");
1897
12
	p->flags |= TERMP_NOSPACE;
1898
12
	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

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

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

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

516
	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
1918
		return;
1919
1920
258
	p->flags |= TERMP_NOSPACE;
1921
258
	if (NULL == n->next) {
1922
42
		term_word(p, ".");
1923
42
		p->flags |= TERMP_SENTENCE;
1924
42
	} else
1925
216
		term_word(p, ",");
1926
258
}
1927
1928
static int
1929
termp_li_pre(DECL_ARGS)
1930
{
1931
1932
1728
	termp_tag_pre(p, pair, meta, n);
1933
864
	term_fontpush(p, TERMFONT_NONE);
1934
864
	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
66
	if ((link = n->child) == NULL)
1944
		return 0;
1945
1946
	/* Find beginning of trailing punctuation. */
1947
33
	punct = n->last;
1948

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

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

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

87
		if (n->parent->args != NULL || n->prev->child == NULL)
1998
72
			p->flags |= TERMP_PREKEEP;
1999
		break;
2000
	default:
2001
		abort();
2002
	}
2003
2004
144
	return 1;
2005
216
}
2006
2007
static void
2008
termp_bk_post(DECL_ARGS)
2009
{
2010
2011
432
	if (n->type == ROFFT_BODY)
2012
72
		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2013
216
}
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

72
	if (n->parent && MDOC_Rs == n->parent->tok &&
2024
18
	    n->parent->norm->Rs.quote_T)
2025
12
		termp_quote_post(p, pair, meta, n);
2026
2027
18
	termp____post(p, pair, meta, n);
2028
18
}
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

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

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

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

18
	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
9
	return 1;
2084
}
2085
2086
static int
2087
termp_tag_pre(DECL_ARGS)
2088
{
2089
2090

3102
	if (n->child != NULL &&
2091
1494
	    n->child->type == ROFFT_TEXT &&
2092
1494
	    (n->prev == NULL ||
2093
1383
	     (n->prev->type == ROFFT_TEXT &&
2094
816
	      strcmp(n->prev->string, "|") == 0)) &&
2095
171
	    (n->parent->tok == MDOC_It ||
2096
171
	     (n->parent->tok == MDOC_Xo &&
2097
	      n->parent->parent->prev == NULL &&
2098
	      n->parent->parent->parent->tok == MDOC_It)))
2099
		tag_put(n->child->string, 1, p->line);
2100
1551
	return 1;
2101
}