GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/man_term.c Lines: 459 494 92.9 %
Date: 2017-11-07 Branches: 265 322 82.3 %

Line Branch Exec Source
1
/*	$OpenBSD: man_term.c,v 1.162 2017/07/31 15:18:59 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
#include <sys/types.h>
19
20
#include <assert.h>
21
#include <ctype.h>
22
#include <limits.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "mandoc_aux.h"
28
#include "mandoc.h"
29
#include "roff.h"
30
#include "man.h"
31
#include "out.h"
32
#include "term.h"
33
#include "main.h"
34
35
#define	MAXMARGINS	  64 /* maximum number of indented scopes */
36
37
struct	mtermp {
38
	int		  fl;
39
#define	MANT_LITERAL	 (1 << 0)
40
	int		  lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
41
	int		  lmargincur; /* index of current margin */
42
	int		  lmarginsz; /* actual number of nested margins */
43
	size_t		  offset; /* default offset to visible page */
44
	int		  pardist; /* vert. space before par., unit: [v] */
45
};
46
47
#define	DECL_ARGS	  struct termp *p, \
48
			  struct mtermp *mt, \
49
			  struct roff_node *n, \
50
			  const struct roff_meta *meta
51
52
struct	termact {
53
	int		(*pre)(DECL_ARGS);
54
	void		(*post)(DECL_ARGS);
55
	int		  flags;
56
#define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
57
};
58
59
static	void		  print_man_nodelist(DECL_ARGS);
60
static	void		  print_man_node(DECL_ARGS);
61
static	void		  print_man_head(struct termp *,
62
				const struct roff_meta *);
63
static	void		  print_man_foot(struct termp *,
64
				const struct roff_meta *);
65
static	void		  print_bvspace(struct termp *,
66
				const struct roff_node *, int);
67
68
static	int		  pre_B(DECL_ARGS);
69
static	int		  pre_DT(DECL_ARGS);
70
static	int		  pre_HP(DECL_ARGS);
71
static	int		  pre_I(DECL_ARGS);
72
static	int		  pre_IP(DECL_ARGS);
73
static	int		  pre_OP(DECL_ARGS);
74
static	int		  pre_PD(DECL_ARGS);
75
static	int		  pre_PP(DECL_ARGS);
76
static	int		  pre_RS(DECL_ARGS);
77
static	int		  pre_SH(DECL_ARGS);
78
static	int		  pre_SS(DECL_ARGS);
79
static	int		  pre_TP(DECL_ARGS);
80
static	int		  pre_UR(DECL_ARGS);
81
static	int		  pre_alternate(DECL_ARGS);
82
static	int		  pre_ign(DECL_ARGS);
83
static	int		  pre_in(DECL_ARGS);
84
static	int		  pre_literal(DECL_ARGS);
85
86
static	void		  post_IP(DECL_ARGS);
87
static	void		  post_HP(DECL_ARGS);
88
static	void		  post_RS(DECL_ARGS);
89
static	void		  post_SH(DECL_ARGS);
90
static	void		  post_SS(DECL_ARGS);
91
static	void		  post_TP(DECL_ARGS);
92
static	void		  post_UR(DECL_ARGS);
93
94
static	const struct termact __termacts[MAN_MAX - MAN_TH] = {
95
	{ NULL, NULL, 0 }, /* TH */
96
	{ pre_SH, post_SH, 0 }, /* SH */
97
	{ pre_SS, post_SS, 0 }, /* SS */
98
	{ pre_TP, post_TP, 0 }, /* TP */
99
	{ pre_PP, NULL, 0 }, /* LP */
100
	{ pre_PP, NULL, 0 }, /* PP */
101
	{ pre_PP, NULL, 0 }, /* P */
102
	{ pre_IP, post_IP, 0 }, /* IP */
103
	{ pre_HP, post_HP, 0 }, /* HP */
104
	{ NULL, NULL, 0 }, /* SM */
105
	{ pre_B, NULL, 0 }, /* SB */
106
	{ pre_alternate, NULL, 0 }, /* BI */
107
	{ pre_alternate, NULL, 0 }, /* IB */
108
	{ pre_alternate, NULL, 0 }, /* BR */
109
	{ pre_alternate, NULL, 0 }, /* RB */
110
	{ NULL, NULL, 0 }, /* R */
111
	{ pre_B, NULL, 0 }, /* B */
112
	{ pre_I, NULL, 0 }, /* I */
113
	{ pre_alternate, NULL, 0 }, /* IR */
114
	{ pre_alternate, NULL, 0 }, /* RI */
115
	{ pre_literal, NULL, 0 }, /* nf */
116
	{ pre_literal, NULL, 0 }, /* fi */
117
	{ NULL, NULL, 0 }, /* RE */
118
	{ pre_RS, post_RS, 0 }, /* RS */
119
	{ pre_DT, NULL, 0 }, /* DT */
120
	{ pre_ign, NULL, MAN_NOTEXT }, /* UC */
121
	{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
122
	{ pre_ign, NULL, 0 }, /* AT */
123
	{ pre_in, NULL, MAN_NOTEXT }, /* in */
124
	{ pre_OP, NULL, 0 }, /* OP */
125
	{ pre_literal, NULL, 0 }, /* EX */
126
	{ pre_literal, NULL, 0 }, /* EE */
127
	{ pre_UR, post_UR, 0 }, /* UR */
128
	{ NULL, NULL, 0 }, /* UE */
129
	{ pre_UR, post_UR, 0 }, /* MT */
130
	{ NULL, NULL, 0 }, /* ME */
131
};
132
static	const struct termact *termacts = __termacts - MAN_TH;
133
134
135
void
136
terminal_man(void *arg, const struct roff_man *man)
137
{
138
	struct termp		*p;
139
	struct roff_node	*n;
140
6876
	struct mtermp		 mt;
141
	size_t			 save_defindent;
142
143
3438
	p = (struct termp *)arg;
144
3438
	save_defindent = p->defindent;
145

6876
	if (p->synopsisonly == 0 && p->defindent == 0)
146
1539
		p->defindent = 7;
147
3438
	p->tcol->rmargin = p->maxrmargin = p->defrmargin;
148
3438
	term_tab_set(p, NULL);
149
3438
	term_tab_set(p, "T");
150
3438
	term_tab_set(p, ".5i");
151
152
3438
	memset(&mt, 0, sizeof(struct mtermp));
153
3438
	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
154
3438
	mt.offset = term_len(p, p->defindent);
155
3438
	mt.pardist = 1;
156
157
3438
	n = man->first->child;
158
3438
	if (p->synopsisonly) {
159
		while (n != NULL) {
160
			if (n->tok == MAN_SH &&
161
			    n->child->child->type == ROFFT_TEXT &&
162
			    !strcmp(n->child->child->string, "SYNOPSIS")) {
163
				if (n->child->next->child != NULL)
164
					print_man_nodelist(p, &mt,
165
					    n->child->next->child,
166
					    &man->meta);
167
				term_newln(p);
168
				break;
169
			}
170
			n = n->next;
171
		}
172
	} else {
173
3438
		term_begin(p, print_man_head, print_man_foot, &man->meta);
174
3438
		p->flags |= TERMP_NOSPACE;
175
3438
		if (n != NULL)
176
3420
			print_man_nodelist(p, &mt, n, &man->meta);
177
3438
		term_end(p);
178
	}
179
3438
	p->defindent = save_defindent;
180
3438
}
181
182
/*
183
 * Printing leading vertical space before a block.
184
 * This is used for the paragraph macros.
185
 * The rules are pretty simple, since there's very little nesting going
186
 * on here.  Basically, if we're the first within another block (SS/SH),
187
 * then don't emit vertical space.  If we are (RS), then do.  If not the
188
 * first, print it.
189
 */
190
static void
191
print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
192
{
193
	int	 i;
194
195
15066
	term_newln(p);
196
197

15066
	if (n->body && n->body->child)
198
7434
		if (n->body->child->type == ROFFT_TBL)
199
			return;
200
201

15030
	if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
202
7110
		if (NULL == n->prev)
203
378
			return;
204
205
26352
	for (i = 0; i < pardist; i++)
206
6021
		term_vspace(p);
207
14688
}
208
209
210
static int
211
pre_ign(DECL_ARGS)
212
{
213
214
	return 0;
215
}
216
217
static int
218
pre_I(DECL_ARGS)
219
{
220
221
	term_fontrepl(p, TERMFONT_UNDER);
222
	return 1;
223
}
224
225
static int
226
pre_literal(DECL_ARGS)
227
{
228
229
2358
	term_newln(p);
230
231

1800
	if (n->tok == MAN_nf || n->tok == MAN_EX)
232
621
		mt->fl |= MANT_LITERAL;
233
	else
234
558
		mt->fl &= ~MANT_LITERAL;
235
236
	/*
237
	 * Unlike .IP and .TP, .HP does not have a HEAD.
238
	 * So in case a second call to term_flushln() is needed,
239
	 * indentation has to be set up explicitly.
240
	 */
241

1197
	if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
242
18
		p->tcol->offset = p->tcol->rmargin;
243
18
		p->tcol->rmargin = p->maxrmargin;
244
18
		p->trailspace = 0;
245
18
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
246
18
		p->flags |= TERMP_NOSPACE;
247
18
	}
248
249
1179
	return 0;
250
}
251
252
static int
253
pre_PD(DECL_ARGS)
254
{
255
2052
	struct roffsu	 su;
256
257
1026
	n = n->child;
258
1026
	if (n == NULL) {
259
378
		mt->pardist = 1;
260
378
		return 0;
261
	}
262
648
	assert(n->type == ROFFT_TEXT);
263
648
	if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
264
639
		mt->pardist = term_vspan(p, &su);
265
648
	return 0;
266
1026
}
267
268
static int
269
pre_alternate(DECL_ARGS)
270
{
271
666
	enum termfont		 font[2];
272
	struct roff_node	*nn;
273
	int			 savelit, i;
274
275

999
	switch (n->tok) {
276
	case MAN_RB:
277
		font[0] = TERMFONT_NONE;
278
		font[1] = TERMFONT_BOLD;
279
		break;
280
	case MAN_RI:
281
		font[0] = TERMFONT_NONE;
282
		font[1] = TERMFONT_UNDER;
283
		break;
284
	case MAN_BR:
285
		font[0] = TERMFONT_BOLD;
286
		font[1] = TERMFONT_NONE;
287
		break;
288
	case MAN_BI:
289
		font[0] = TERMFONT_BOLD;
290
		font[1] = TERMFONT_UNDER;
291
		break;
292
	case MAN_IR:
293
		font[0] = TERMFONT_UNDER;
294
		font[1] = TERMFONT_NONE;
295
		break;
296
	case MAN_IB:
297
		font[0] = TERMFONT_UNDER;
298
		font[1] = TERMFONT_BOLD;
299
		break;
300
	default:
301
		abort();
302
	}
303
304
333
	savelit = MANT_LITERAL & mt->fl;
305
333
	mt->fl &= ~MANT_LITERAL;
306
307
2232
	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
308
783
		term_fontrepl(p, font[i]);
309

927
		if (savelit && NULL == nn->next)
310
36
			mt->fl |= MANT_LITERAL;
311
783
		assert(nn->type == ROFFT_TEXT);
312
783
		term_word(p, nn->string);
313
783
		if (nn->flags & NODE_EOS)
314
                	p->flags |= TERMP_SENTENCE;
315
783
		if (nn->next)
316
450
			p->flags |= TERMP_NOSPACE;
317
	}
318
319
333
	return 0;
320
333
}
321
322
static int
323
pre_B(DECL_ARGS)
324
{
325
326
486
	term_fontrepl(p, TERMFONT_BOLD);
327
243
	return 1;
328
}
329
330
static int
331
pre_OP(DECL_ARGS)
332
{
333
334
72
	term_word(p, "[");
335
36
	p->flags |= TERMP_NOSPACE;
336
337
36
	if (NULL != (n = n->child)) {
338
27
		term_fontrepl(p, TERMFONT_BOLD);
339
27
		term_word(p, n->string);
340
27
	}
341

63
	if (NULL != n && NULL != n->next) {
342
18
		term_fontrepl(p, TERMFONT_UNDER);
343
18
		term_word(p, n->next->string);
344
18
	}
345
346
36
	term_fontrepl(p, TERMFONT_NONE);
347
36
	p->flags |= TERMP_NOSPACE;
348
36
	term_word(p, "]");
349
36
	return 0;
350
}
351
352
static int
353
pre_in(DECL_ARGS)
354
{
355
234
	struct roffsu	 su;
356
	const char	*cp;
357
	size_t		 v;
358
	int		 less;
359
360
117
	term_newln(p);
361
362
117
	if (n->child == NULL) {
363
		p->tcol->offset = mt->offset;
364
		return 0;
365
	}
366
367
117
	cp = n->child->string;
368
	less = 0;
369
370
117
	if ('-' == *cp)
371
9
		less = -1;
372
108
	else if ('+' == *cp)
373
9
		less = 1;
374
	else
375
99
		cp--;
376
377
117
	if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
378
		return 0;
379
380
117
	v = term_hen(p, &su);
381
382
117
	if (less < 0)
383
27
		p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
384
108
	else if (less > 0)
385
9
		p->tcol->offset += v;
386
	else
387
		p->tcol->offset = v;
388
117
	if (p->tcol->offset > SHRT_MAX)
389
		p->tcol->offset = term_len(p, p->defindent);
390
391
117
	return 0;
392
117
}
393
394
static int
395
pre_DT(DECL_ARGS)
396
{
397
	term_tab_set(p, NULL);
398
	term_tab_set(p, "T");
399
	term_tab_set(p, ".5i");
400
	return 0;
401
}
402
403
static int
404
pre_HP(DECL_ARGS)
405
{
406
5778
	struct roffsu		 su;
407
	const struct roff_node	*nn;
408
	int			 len;
409
410
2889
	switch (n->type) {
411
	case ROFFT_BLOCK:
412
963
		print_bvspace(p, n, mt->pardist);
413
963
		return 1;
414
	case ROFFT_BODY:
415
		break;
416
	default:
417
963
		return 0;
418
	}
419
420
963
	if ( ! (MANT_LITERAL & mt->fl)) {
421
927
		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
422
927
		p->trailspace = 2;
423
927
	}
424
425
	/* Calculate offset. */
426
427

1836
	if ((nn = n->parent->head->child) != NULL &&
428
873
	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
429
873
		len = term_hen(p, &su);
430

963
		if (len < 0 && (size_t)(-len) > mt->offset)
431
9
			len = -mt->offset;
432
864
		else if (len > SHRT_MAX)
433
			len = term_len(p, p->defindent);
434
873
		mt->lmargin[mt->lmargincur] = len;
435
873
	} else
436
90
		len = mt->lmargin[mt->lmargincur];
437
438
963
	p->tcol->offset = mt->offset;
439
963
	p->tcol->rmargin = mt->offset + len;
440
963
	return 1;
441
2889
}
442
443
static void
444
post_HP(DECL_ARGS)
445
{
446
447
5778
	switch (n->type) {
448
	case ROFFT_BODY:
449
963
		term_newln(p);
450
451
		/*
452
		 * Compatibility with a groff bug.
453
		 * The .HP macro uses the undocumented .tag request
454
		 * which causes a line break and cancels no-space
455
		 * mode even if there isn't any output.
456
		 */
457
458
963
		if (n->child == NULL)
459
9
			term_vspace(p);
460
461
963
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
462
963
		p->trailspace = 0;
463
963
		p->tcol->offset = mt->offset;
464
963
		p->tcol->rmargin = p->maxrmargin;
465
963
		break;
466
	default:
467
		break;
468
	}
469
2889
}
470
471
static int
472
pre_PP(DECL_ARGS)
473
{
474
475
24300
	switch (n->type) {
476
	case ROFFT_BLOCK:
477
4050
		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
478
4050
		print_bvspace(p, n, mt->pardist);
479
4050
		break;
480
	default:
481
8100
		p->tcol->offset = mt->offset;
482
8100
		break;
483
	}
484
485
12150
	return n->type != ROFFT_HEAD;
486
}
487
488
static int
489
pre_IP(DECL_ARGS)
490
{
491
4032
	struct roffsu		 su;
492
	const struct roff_node	*nn;
493
	int			 len, savelit;
494
495

2304
	switch (n->type) {
496
	case ROFFT_BODY:
497
576
		p->flags |= TERMP_NOSPACE;
498
576
		break;
499
	case ROFFT_HEAD:
500
576
		p->flags |= TERMP_NOBREAK;
501
576
		p->trailspace = 1;
502
576
		break;
503
	case ROFFT_BLOCK:
504
576
		print_bvspace(p, n, mt->pardist);
505
		/* FALLTHROUGH */
506
	default:
507
576
		return 1;
508
	}
509
510
	/* Calculate the offset from the optional second argument. */
511

1890
	if ((nn = n->parent->head->child) != NULL &&
512
1134
	    (nn = nn->next) != NULL &&
513
738
	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
514
702
		len = term_hen(p, &su);
515

774
		if (len < 0 && (size_t)(-len) > mt->offset)
516
36
			len = -mt->offset;
517
666
		else if (len > SHRT_MAX)
518
			len = term_len(p, p->defindent);
519
702
		mt->lmargin[mt->lmargincur] = len;
520
702
	} else
521
450
		len = mt->lmargin[mt->lmargincur];
522
523
1728
	switch (n->type) {
524
	case ROFFT_HEAD:
525
576
		p->tcol->offset = mt->offset;
526
576
		p->tcol->rmargin = mt->offset + len;
527
528
576
		savelit = MANT_LITERAL & mt->fl;
529
576
		mt->fl &= ~MANT_LITERAL;
530
531
576
		if (n->child)
532
567
			print_man_node(p, mt, n->child, meta);
533
534
576
		if (savelit)
535
135
			mt->fl |= MANT_LITERAL;
536
537
576
		return 0;
538
	case ROFFT_BODY:
539
576
		p->tcol->offset = mt->offset + len;
540
576
		p->tcol->rmargin = p->maxrmargin;
541
576
		break;
542
	default:
543
		break;
544
	}
545
546
576
	return 1;
547
1728
}
548
549
static void
550
post_IP(DECL_ARGS)
551
{
552
553
4608
	switch (n->type) {
554
	case ROFFT_HEAD:
555
576
		term_flushln(p);
556
576
		p->flags &= ~TERMP_NOBREAK;
557
576
		p->trailspace = 0;
558
576
		p->tcol->rmargin = p->maxrmargin;
559
576
		break;
560
	case ROFFT_BODY:
561
576
		term_newln(p);
562
576
		p->tcol->offset = mt->offset;
563
576
		break;
564
	default:
565
		break;
566
	}
567
1728
}
568
569
static int
570
pre_TP(DECL_ARGS)
571
{
572
13608
	struct roffsu		 su;
573
	struct roff_node	*nn;
574
	int			 len, savelit;
575
576

7776
	switch (n->type) {
577
	case ROFFT_HEAD:
578
1944
		p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
579
1944
		p->trailspace = 1;
580
1944
		break;
581
	case ROFFT_BODY:
582
1944
		p->flags |= TERMP_NOSPACE;
583
1944
		break;
584
	case ROFFT_BLOCK:
585
1944
		print_bvspace(p, n, mt->pardist);
586
		/* FALLTHROUGH */
587
	default:
588
1944
		return 1;
589
	}
590
591
	/* Calculate offset. */
592
593

7290
	if ((nn = n->parent->head->child) != NULL &&
594

7740
	    nn->string != NULL && ! (NODE_LINE & nn->flags) &&
595
3402
	    a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
596
3348
		len = term_hen(p, &su);
597

3420
		if (len < 0 && (size_t)(-len) > mt->offset)
598
36
			len = -mt->offset;
599
3312
		else if (len > SHRT_MAX)
600
			len = term_len(p, p->defindent);
601
3348
		mt->lmargin[mt->lmargincur] = len;
602
3348
	} else
603
540
		len = mt->lmargin[mt->lmargincur];
604
605
5832
	switch (n->type) {
606
	case ROFFT_HEAD:
607
1944
		p->tcol->offset = mt->offset;
608
1944
		p->tcol->rmargin = mt->offset + len;
609
610
1944
		savelit = MANT_LITERAL & mt->fl;
611
1944
		mt->fl &= ~MANT_LITERAL;
612
613
		/* Don't print same-line elements. */
614
1944
		nn = n->child;
615

11097
		while (NULL != nn && 0 == (NODE_LINE & nn->flags))
616
1755
			nn = nn->next;
617
618
5886
		while (NULL != nn) {
619
1971
			print_man_node(p, mt, nn, meta);
620
1971
			nn = nn->next;
621
		}
622
623
1944
		if (savelit)
624
117
			mt->fl |= MANT_LITERAL;
625
1944
		return 0;
626
	case ROFFT_BODY:
627
1944
		p->tcol->offset = mt->offset + len;
628
1944
		p->tcol->rmargin = p->maxrmargin;
629
1944
		p->trailspace = 0;
630
1944
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
631
1944
		break;
632
	default:
633
		break;
634
	}
635
636
1944
	return 1;
637
5832
}
638
639
static void
640
post_TP(DECL_ARGS)
641
{
642
643
15552
	switch (n->type) {
644
	case ROFFT_HEAD:
645
1944
		term_flushln(p);
646
1944
		break;
647
	case ROFFT_BODY:
648
1944
		term_newln(p);
649
1944
		p->tcol->offset = mt->offset;
650
1944
		break;
651
	default:
652
		break;
653
	}
654
5832
}
655
656
static int
657
pre_SS(DECL_ARGS)
658
{
659
	int	 i;
660
661

4779
	switch (n->type) {
662
	case ROFFT_BLOCK:
663
531
		mt->fl &= ~MANT_LITERAL;
664
531
		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
665
531
		mt->offset = term_len(p, p->defindent);
666
667
		/*
668
		 * No vertical space before the first subsection
669
		 * and after an empty subsection.
670
		 */
671
672
531
		do {
673
531
			n = n->prev;
674

1377
		} while (n != NULL && n->tok >= MAN_TH &&
675
378
		    termacts[n->tok].flags & MAN_NOTEXT);
676

1305
		if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL))
677
			break;
678
679
1872
		for (i = 0; i < mt->pardist; i++)
680
468
			term_vspace(p);
681
		break;
682
	case ROFFT_HEAD:
683
531
		term_fontrepl(p, TERMFONT_BOLD);
684
531
		p->tcol->offset = term_len(p, 3);
685
531
		p->tcol->rmargin = mt->offset;
686
531
		p->trailspace = mt->offset;
687
531
		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
688
531
		break;
689
	case ROFFT_BODY:
690
531
		p->tcol->offset = mt->offset;
691
531
		p->tcol->rmargin = p->maxrmargin;
692
531
		p->trailspace = 0;
693
531
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
694
531
		break;
695
	default:
696
		break;
697
	}
698
699
1593
	return 1;
700
}
701
702
static void
703
post_SS(DECL_ARGS)
704
{
705
706
4248
	switch (n->type) {
707
	case ROFFT_HEAD:
708
531
		term_newln(p);
709
531
		break;
710
	case ROFFT_BODY:
711
531
		term_newln(p);
712
531
		break;
713
	default:
714
		break;
715
	}
716
1593
}
717
718
static int
719
pre_SH(DECL_ARGS)
720
{
721
	int	 i;
722
723

67068
	switch (n->type) {
724
	case ROFFT_BLOCK:
725
7452
		mt->fl &= ~MANT_LITERAL;
726
7452
		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
727
7452
		mt->offset = term_len(p, p->defindent);
728
729
		/*
730
		 * No vertical space before the first section
731
		 * and after an empty section.
732
		 */
733
734
7452
		do {
735
7452
			n = n->prev;
736

15534
		} while (n != NULL && n->tok >= MAN_TH &&
737
4032
		    termacts[n->tok].flags & MAN_NOTEXT);
738

15525
		if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL))
739
			break;
740
741
16020
		for (i = 0; i < mt->pardist; i++)
742
4005
			term_vspace(p);
743
		break;
744
	case ROFFT_HEAD:
745
7452
		term_fontrepl(p, TERMFONT_BOLD);
746
7452
		p->tcol->offset = 0;
747
7452
		p->tcol->rmargin = mt->offset;
748
7452
		p->trailspace = mt->offset;
749
7452
		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
750
7452
		break;
751
	case ROFFT_BODY:
752
7452
		p->tcol->offset = mt->offset;
753
7452
		p->tcol->rmargin = p->maxrmargin;
754
7452
		p->trailspace = 0;
755
7452
		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
756
7452
		break;
757
	default:
758
		break;
759
	}
760
761
22356
	return 1;
762
}
763
764
static void
765
post_SH(DECL_ARGS)
766
{
767
768
59616
	switch (n->type) {
769
	case ROFFT_HEAD:
770
7452
		term_newln(p);
771
7452
		break;
772
	case ROFFT_BODY:
773
7452
		term_newln(p);
774
7452
		break;
775
	default:
776
		break;
777
	}
778
22356
}
779
780
static int
781
pre_RS(DECL_ARGS)
782
{
783
6696
	struct roffsu	 su;
784
785
3348
	switch (n->type) {
786
	case ROFFT_BLOCK:
787
1116
		term_newln(p);
788
1116
		return 1;
789
	case ROFFT_HEAD:
790
1116
		return 0;
791
	default:
792
		break;
793
	}
794
795
1116
	n = n->parent->head;
796
1116
	n->aux = SHRT_MAX + 1;
797
1116
	if (n->child == NULL)
798
135
		n->aux = mt->lmargin[mt->lmargincur];
799
981
	else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
800
981
		n->aux = term_hen(p, &su);
801

2286
	if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
802
9
		n->aux = -mt->offset;
803
1107
	else if (n->aux > SHRT_MAX)
804
		n->aux = term_len(p, p->defindent);
805
806
1125
	mt->offset += n->aux;
807
1116
	p->tcol->offset = mt->offset;
808
1116
	p->tcol->rmargin = p->maxrmargin;
809
810
1116
	if (++mt->lmarginsz < MAXMARGINS)
811
1116
		mt->lmargincur = mt->lmarginsz;
812
813
1116
	mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
814
1116
	return 1;
815
3348
}
816
817
static void
818
post_RS(DECL_ARGS)
819
{
820
821
6696
	switch (n->type) {
822
	case ROFFT_BLOCK:
823
		return;
824
	case ROFFT_HEAD:
825
		return;
826
	default:
827
1116
		term_newln(p);
828
		break;
829
	}
830
831
1116
	mt->offset -= n->parent->head->aux;
832
1116
	p->tcol->offset = mt->offset;
833
834
1116
	if (--mt->lmarginsz < MAXMARGINS)
835
1116
		mt->lmargincur = mt->lmarginsz;
836
3348
}
837
838
static int
839
pre_UR(DECL_ARGS)
840
{
841
842
756
	return n->type != ROFFT_HEAD;
843
}
844
845
static void
846
post_UR(DECL_ARGS)
847
{
848
849
756
	if (n->type != ROFFT_BLOCK)
850
		return;
851
852
126
	term_word(p, "<");
853
126
	p->flags |= TERMP_NOSPACE;
854
855
126
	if (NULL != n->child->child)
856
108
		print_man_node(p, mt, n->child->child, meta);
857
858
126
	p->flags |= TERMP_NOSPACE;
859
126
	term_word(p, ">");
860
504
}
861
862
static void
863
print_man_node(DECL_ARGS)
864
{
865
	int		 c;
866
867

234522
	switch (n->type) {
868
	case ROFFT_TEXT:
869
		/*
870
		 * If we have a blank line, output a vertical space.
871
		 * If we have a space as the first character, break
872
		 * before printing the line's data.
873
		 */
874
57069
		if (*n->string == '\0') {
875
63
			if (p->flags & TERMP_NONEWLINE)
876
9
				term_newln(p);
877
			else
878
54
				term_vspace(p);
879
63
			return;
880

57096
		} else if (*n->string == ' ' && n->flags & NODE_LINE &&
881
45
		    (p->flags & TERMP_NONEWLINE) == 0)
882
45
			term_newln(p);
883
884
57006
		term_word(p, n->string);
885
57006
		goto out;
886
887
	case ROFFT_EQN:
888
		if ( ! (n->flags & NODE_LINE))
889
			p->flags |= TERMP_NOSPACE;
890
		term_eqn(p, n->eqn);
891
		if (n->next != NULL && ! (n->next->flags & NODE_LINE))
892
			p->flags |= TERMP_NOSPACE;
893
		return;
894
	case ROFFT_TBL:
895
2511
		if (p->tbl.cols == NULL)
896
1044
			term_vspace(p);
897
2511
		term_tbl(p, n->span);
898
2511
		return;
899
	default:
900
		break;
901
	}
902
903
57681
	if (n->tok < ROFF_MAX) {
904
4248
		roff_term_pre(p, n);
905
4248
		return;
906
	}
907
908

106866
	assert(n->tok >= MAN_TH && n->tok <= MAN_MAX);
909
53433
	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
910
52290
		term_fontrepl(p, TERMFONT_NONE);
911
912
	c = 1;
913
53433
	if (termacts[n->tok].pre)
914
53208
		c = (*termacts[n->tok].pre)(p, mt, n, meta);
915
916

95400
	if (c && n->child)
917
41679
		print_man_nodelist(p, mt, n->child, meta);
918
919
53433
	if (termacts[n->tok].post)
920
38124
		(*termacts[n->tok].post)(p, mt, n, meta);
921
53433
	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
922
52290
		term_fontrepl(p, TERMFONT_NONE);
923
924
out:
925
	/*
926
	 * If we're in a literal context, make sure that words
927
	 * together on the same line stay together.  This is a
928
	 * POST-printing call, so we check the NEXT word.  Since
929
	 * -man doesn't have nested macros, we don't need to be
930
	 * more specific than this.
931
	 */
932

120708
	if (mt->fl & MANT_LITERAL &&
933
11790
	    ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
934
21897
	    (n->next == NULL || n->next->flags & NODE_LINE)) {
935
11214
		p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
936

20466
		if (n->string != NULL && *n->string != '\0')
937
9252
			term_flushln(p);
938
		else
939
1962
			term_newln(p);
940
11214
		p->flags &= ~TERMP_BRNEVER;
941

11250
		if (p->tcol->rmargin < p->maxrmargin &&
942
36
		    n->parent->tok == MAN_HP) {
943
36
			p->tcol->offset = p->tcol->rmargin;
944
36
			p->tcol->rmargin = p->maxrmargin;
945
36
		}
946
	}
947
110439
	if (NODE_EOS & n->flags)
948
3087
		p->flags |= TERMP_SENTENCE;
949
227700
}
950
951
952
static void
953
print_man_nodelist(DECL_ARGS)
954
{
955
956
364527
	while (n != NULL) {
957
114615
		print_man_node(p, mt, n, meta);
958
114615
		n = n->next;
959
	}
960
45099
}
961
962
static void
963
print_man_foot(struct termp *p, const struct roff_meta *meta)
964
{
965
6876
	char			*title;
966
	size_t			 datelen, titlen;
967
968
3438
	assert(meta->title);
969
3438
	assert(meta->msec);
970
3438
	assert(meta->date);
971
972
3438
	term_fontrepl(p, TERMFONT_NONE);
973
974
3438
	if (meta->hasbody)
975
3420
		term_vspace(p);
976
977
	/*
978
	 * Temporary, undocumented option to imitate mdoc(7) output.
979
	 * In the bottom right corner, use the operating system
980
	 * instead of the title.
981
	 */
982
983
3438
	if ( ! p->mdocstyle) {
984
1539
		if (meta->hasbody) {
985
1530
			term_vspace(p);
986
1530
			term_vspace(p);
987
1530
		}
988
1539
		mandoc_asprintf(&title, "%s(%s)",
989
1539
		    meta->title, meta->msec);
990
3438
	} else if (meta->os) {
991
1899
		title = mandoc_strdup(meta->os);
992
1899
	} else {
993
		title = mandoc_strdup("");
994
	}
995
3438
	datelen = term_strlen(p, meta->date);
996
997
	/* Bottom left corner: operating system. */
998
999
3438
	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1000
3438
	p->trailspace = 1;
1001
3438
	p->tcol->offset = 0;
1002
10296
	p->tcol->rmargin = p->maxrmargin > datelen ?
1003
3420
	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1004
1005
3438
	if (meta->os)
1006
3438
		term_word(p, meta->os);
1007
3438
	term_flushln(p);
1008
1009
	/* At the bottom in the middle: manual date. */
1010
1011
3438
	p->tcol->offset = p->tcol->rmargin;
1012
3438
	titlen = term_strlen(p, title);
1013
10296
	p->tcol->rmargin = p->maxrmargin > titlen ?
1014
3420
	    p->maxrmargin - titlen : 0;
1015
3438
	p->flags |= TERMP_NOSPACE;
1016
1017
3438
	term_word(p, meta->date);
1018
3438
	term_flushln(p);
1019
1020
	/* Bottom right corner: manual title and section. */
1021
1022
3438
	p->flags &= ~TERMP_NOBREAK;
1023
3438
	p->flags |= TERMP_NOSPACE;
1024
3438
	p->trailspace = 0;
1025
3438
	p->tcol->offset = p->tcol->rmargin;
1026
3438
	p->tcol->rmargin = p->maxrmargin;
1027
1028
3438
	term_word(p, title);
1029
3438
	term_flushln(p);
1030
3438
	free(title);
1031
3438
}
1032
1033
static void
1034
print_man_head(struct termp *p, const struct roff_meta *meta)
1035
{
1036
	const char		*volume;
1037
6876
	char			*title;
1038
	size_t			 vollen, titlen;
1039
1040
3438
	assert(meta->title);
1041
3438
	assert(meta->msec);
1042
1043
10287
	volume = NULL == meta->vol ? "" : meta->vol;
1044
3438
	vollen = term_strlen(p, volume);
1045
1046
	/* Top left corner: manual title and section. */
1047
1048
3438
	mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1049
3438
	titlen = term_strlen(p, title);
1050
1051
3438
	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1052
3438
	p->trailspace = 1;
1053
3438
	p->tcol->offset = 0;
1054
10296
	p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1055
3420
	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1056
27
	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1057
1058
3438
	term_word(p, title);
1059
3438
	term_flushln(p);
1060
1061
	/* At the top in the middle: manual volume. */
1062
1063
3438
	p->flags |= TERMP_NOSPACE;
1064
3438
	p->tcol->offset = p->tcol->rmargin;
1065
10314
	p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1066
6876
	    p->maxrmargin ?  p->maxrmargin - titlen : p->maxrmargin;
1067
1068
3438
	term_word(p, volume);
1069
3438
	term_flushln(p);
1070
1071
	/* Top right corner: title and section, again. */
1072
1073
3438
	p->flags &= ~TERMP_NOBREAK;
1074
3438
	p->trailspace = 0;
1075
3438
	if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1076
3420
		p->flags |= TERMP_NOSPACE;
1077
3420
		p->tcol->offset = p->tcol->rmargin;
1078
3420
		p->tcol->rmargin = p->maxrmargin;
1079
3420
		term_word(p, title);
1080
3420
		term_flushln(p);
1081
3420
	}
1082
1083
3438
	p->flags &= ~TERMP_NOSPACE;
1084
3438
	p->tcol->offset = 0;
1085
3438
	p->tcol->rmargin = p->maxrmargin;
1086
1087
	/*
1088
	 * Groff prints three blank lines before the content.
1089
	 * Do the same, except in the temporary, undocumented
1090
	 * mode imitating mdoc(7) output.
1091
	 */
1092
1093
3438
	term_vspace(p);
1094
3438
	if ( ! p->mdocstyle) {
1095
1539
		term_vspace(p);
1096
1539
		term_vspace(p);
1097
1539
	}
1098
3438
	free(title);
1099
3438
}