]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
.SY causes a one-column indentation even without any argument
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.219 2018/08/18 17:07:23 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015, 2017, 2018 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 "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <limits.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 "man.h"
33 #include "out.h"
34 #include "term.h"
35 #include "main.h"
36
37 #define MAXMARGINS 64 /* maximum number of indented scopes */
38
39 struct mtermp {
40 int fl;
41 #define MANT_LITERAL (1 << 0)
42 int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
43 int lmargincur; /* index of current margin */
44 int lmarginsz; /* actual number of nested margins */
45 size_t offset; /* default offset to visible page */
46 int pardist; /* vert. space before par., unit: [v] */
47 };
48
49 #define DECL_ARGS struct termp *p, \
50 struct mtermp *mt, \
51 struct roff_node *n, \
52 const struct roff_meta *meta
53
54 struct man_term_act {
55 int (*pre)(DECL_ARGS);
56 void (*post)(DECL_ARGS);
57 int flags;
58 #define MAN_NOTEXT (1 << 0) /* Never has text children. */
59 };
60
61 static void print_man_nodelist(DECL_ARGS);
62 static void print_man_node(DECL_ARGS);
63 static void print_man_head(struct termp *,
64 const struct roff_meta *);
65 static void print_man_foot(struct termp *,
66 const struct roff_meta *);
67 static void print_bvspace(struct termp *,
68 const struct roff_node *, int);
69
70 static int pre_B(DECL_ARGS);
71 static int pre_DT(DECL_ARGS);
72 static int pre_HP(DECL_ARGS);
73 static int pre_I(DECL_ARGS);
74 static int pre_IP(DECL_ARGS);
75 static int pre_OP(DECL_ARGS);
76 static int pre_PD(DECL_ARGS);
77 static int pre_PP(DECL_ARGS);
78 static int pre_RS(DECL_ARGS);
79 static int pre_SH(DECL_ARGS);
80 static int pre_SS(DECL_ARGS);
81 static int pre_SY(DECL_ARGS);
82 static int pre_TP(DECL_ARGS);
83 static int pre_UR(DECL_ARGS);
84 static int pre_alternate(DECL_ARGS);
85 static int pre_ign(DECL_ARGS);
86 static int pre_in(DECL_ARGS);
87 static int pre_literal(DECL_ARGS);
88
89 static void post_IP(DECL_ARGS);
90 static void post_HP(DECL_ARGS);
91 static void post_RS(DECL_ARGS);
92 static void post_SH(DECL_ARGS);
93 static void post_SS(DECL_ARGS);
94 static void post_SY(DECL_ARGS);
95 static void post_TP(DECL_ARGS);
96 static void post_UR(DECL_ARGS);
97
98 static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = {
99 { NULL, NULL, 0 }, /* TH */
100 { pre_SH, post_SH, 0 }, /* SH */
101 { pre_SS, post_SS, 0 }, /* SS */
102 { pre_TP, post_TP, 0 }, /* TP */
103 { pre_TP, post_TP, 0 }, /* TQ */
104 { pre_PP, NULL, 0 }, /* LP */
105 { pre_PP, NULL, 0 }, /* PP */
106 { pre_PP, NULL, 0 }, /* P */
107 { pre_IP, post_IP, 0 }, /* IP */
108 { pre_HP, post_HP, 0 }, /* HP */
109 { NULL, NULL, 0 }, /* SM */
110 { pre_B, NULL, 0 }, /* SB */
111 { pre_alternate, NULL, 0 }, /* BI */
112 { pre_alternate, NULL, 0 }, /* IB */
113 { pre_alternate, NULL, 0 }, /* BR */
114 { pre_alternate, NULL, 0 }, /* RB */
115 { NULL, NULL, 0 }, /* R */
116 { pre_B, NULL, 0 }, /* B */
117 { pre_I, NULL, 0 }, /* I */
118 { pre_alternate, NULL, 0 }, /* IR */
119 { pre_alternate, NULL, 0 }, /* RI */
120 { pre_literal, NULL, MAN_NOTEXT }, /* nf */
121 { pre_literal, NULL, MAN_NOTEXT }, /* fi */
122 { NULL, NULL, 0 }, /* RE */
123 { pre_RS, post_RS, 0 }, /* RS */
124 { pre_DT, NULL, 0 }, /* DT */
125 { pre_ign, NULL, MAN_NOTEXT }, /* UC */
126 { pre_PD, NULL, MAN_NOTEXT }, /* PD */
127 { pre_ign, NULL, 0 }, /* AT */
128 { pre_in, NULL, MAN_NOTEXT }, /* in */
129 { pre_SY, post_SY, 0 }, /* SY */
130 { NULL, NULL, 0 }, /* YS */
131 { pre_OP, NULL, 0 }, /* OP */
132 { pre_literal, NULL, 0 }, /* EX */
133 { pre_literal, NULL, 0 }, /* EE */
134 { pre_UR, post_UR, 0 }, /* UR */
135 { NULL, NULL, 0 }, /* UE */
136 { pre_UR, post_UR, 0 }, /* MT */
137 { NULL, NULL, 0 }, /* ME */
138 };
139 static const struct man_term_act *man_term_act(enum roff_tok);
140
141
142 static const struct man_term_act *
143 man_term_act(enum roff_tok tok)
144 {
145 assert(tok >= MAN_TH && tok <= MAN_MAX);
146 return man_term_acts + (tok - MAN_TH);
147 }
148
149 void
150 terminal_man(void *arg, const struct roff_man *man)
151 {
152 struct termp *p;
153 struct roff_node *n;
154 struct mtermp mt;
155 size_t save_defindent;
156
157 p = (struct termp *)arg;
158 save_defindent = p->defindent;
159 if (p->synopsisonly == 0 && p->defindent == 0)
160 p->defindent = 7;
161 p->tcol->rmargin = p->maxrmargin = p->defrmargin;
162 term_tab_set(p, NULL);
163 term_tab_set(p, "T");
164 term_tab_set(p, ".5i");
165
166 memset(&mt, 0, sizeof(struct mtermp));
167 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
168 mt.offset = term_len(p, p->defindent);
169 mt.pardist = 1;
170
171 n = man->first->child;
172 if (p->synopsisonly) {
173 while (n != NULL) {
174 if (n->tok == MAN_SH &&
175 n->child->child->type == ROFFT_TEXT &&
176 !strcmp(n->child->child->string, "SYNOPSIS")) {
177 if (n->child->next->child != NULL)
178 print_man_nodelist(p, &mt,
179 n->child->next->child,
180 &man->meta);
181 term_newln(p);
182 break;
183 }
184 n = n->next;
185 }
186 } else {
187 term_begin(p, print_man_head, print_man_foot, &man->meta);
188 p->flags |= TERMP_NOSPACE;
189 if (n != NULL)
190 print_man_nodelist(p, &mt, n, &man->meta);
191 term_end(p);
192 }
193 p->defindent = save_defindent;
194 }
195
196 /*
197 * Printing leading vertical space before a block.
198 * This is used for the paragraph macros.
199 * The rules are pretty simple, since there's very little nesting going
200 * on here. Basically, if we're the first within another block (SS/SH),
201 * then don't emit vertical space. If we are (RS), then do. If not the
202 * first, print it.
203 */
204 static void
205 print_bvspace(struct termp *p, const struct roff_node *n, int pardist)
206 {
207 int i;
208
209 term_newln(p);
210
211 if (n->body && n->body->child)
212 if (n->body->child->type == ROFFT_TBL)
213 return;
214
215 if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
216 if (NULL == n->prev)
217 return;
218
219 for (i = 0; i < pardist; i++)
220 term_vspace(p);
221 }
222
223
224 static int
225 pre_ign(DECL_ARGS)
226 {
227
228 return 0;
229 }
230
231 static int
232 pre_I(DECL_ARGS)
233 {
234
235 term_fontrepl(p, TERMFONT_UNDER);
236 return 1;
237 }
238
239 static int
240 pre_literal(DECL_ARGS)
241 {
242
243 term_newln(p);
244
245 if (n->tok == MAN_nf || n->tok == MAN_EX)
246 mt->fl |= MANT_LITERAL;
247 else
248 mt->fl &= ~MANT_LITERAL;
249
250 /*
251 * Unlike .IP and .TP, .HP does not have a HEAD.
252 * So in case a second call to term_flushln() is needed,
253 * indentation has to be set up explicitly.
254 */
255 if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) {
256 p->tcol->offset = p->tcol->rmargin;
257 p->tcol->rmargin = p->maxrmargin;
258 p->trailspace = 0;
259 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
260 p->flags |= TERMP_NOSPACE;
261 }
262
263 return 0;
264 }
265
266 static int
267 pre_PD(DECL_ARGS)
268 {
269 struct roffsu su;
270
271 n = n->child;
272 if (n == NULL) {
273 mt->pardist = 1;
274 return 0;
275 }
276 assert(n->type == ROFFT_TEXT);
277 if (a2roffsu(n->string, &su, SCALE_VS) != NULL)
278 mt->pardist = term_vspan(p, &su);
279 return 0;
280 }
281
282 static int
283 pre_alternate(DECL_ARGS)
284 {
285 enum termfont font[2];
286 struct roff_node *nn;
287 int savelit, i;
288
289 switch (n->tok) {
290 case MAN_RB:
291 font[0] = TERMFONT_NONE;
292 font[1] = TERMFONT_BOLD;
293 break;
294 case MAN_RI:
295 font[0] = TERMFONT_NONE;
296 font[1] = TERMFONT_UNDER;
297 break;
298 case MAN_BR:
299 font[0] = TERMFONT_BOLD;
300 font[1] = TERMFONT_NONE;
301 break;
302 case MAN_BI:
303 font[0] = TERMFONT_BOLD;
304 font[1] = TERMFONT_UNDER;
305 break;
306 case MAN_IR:
307 font[0] = TERMFONT_UNDER;
308 font[1] = TERMFONT_NONE;
309 break;
310 case MAN_IB:
311 font[0] = TERMFONT_UNDER;
312 font[1] = TERMFONT_BOLD;
313 break;
314 default:
315 abort();
316 }
317
318 savelit = MANT_LITERAL & mt->fl;
319 mt->fl &= ~MANT_LITERAL;
320
321 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
322 term_fontrepl(p, font[i]);
323 if (savelit && NULL == nn->next)
324 mt->fl |= MANT_LITERAL;
325 assert(nn->type == ROFFT_TEXT);
326 term_word(p, nn->string);
327 if (nn->flags & NODE_EOS)
328 p->flags |= TERMP_SENTENCE;
329 if (nn->next)
330 p->flags |= TERMP_NOSPACE;
331 }
332
333 return 0;
334 }
335
336 static int
337 pre_B(DECL_ARGS)
338 {
339
340 term_fontrepl(p, TERMFONT_BOLD);
341 return 1;
342 }
343
344 static int
345 pre_OP(DECL_ARGS)
346 {
347
348 term_word(p, "[");
349 p->flags |= TERMP_KEEP | TERMP_NOSPACE;
350
351 if (NULL != (n = n->child)) {
352 term_fontrepl(p, TERMFONT_BOLD);
353 term_word(p, n->string);
354 }
355 if (NULL != n && NULL != n->next) {
356 term_fontrepl(p, TERMFONT_UNDER);
357 term_word(p, n->next->string);
358 }
359
360 term_fontrepl(p, TERMFONT_NONE);
361 p->flags &= ~TERMP_KEEP;
362 p->flags |= TERMP_NOSPACE;
363 term_word(p, "]");
364 return 0;
365 }
366
367 static int
368 pre_in(DECL_ARGS)
369 {
370 struct roffsu su;
371 const char *cp;
372 size_t v;
373 int less;
374
375 term_newln(p);
376
377 if (n->child == NULL) {
378 p->tcol->offset = mt->offset;
379 return 0;
380 }
381
382 cp = n->child->string;
383 less = 0;
384
385 if ('-' == *cp)
386 less = -1;
387 else if ('+' == *cp)
388 less = 1;
389 else
390 cp--;
391
392 if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
393 return 0;
394
395 v = term_hen(p, &su);
396
397 if (less < 0)
398 p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
399 else if (less > 0)
400 p->tcol->offset += v;
401 else
402 p->tcol->offset = v;
403 if (p->tcol->offset > SHRT_MAX)
404 p->tcol->offset = term_len(p, p->defindent);
405
406 return 0;
407 }
408
409 static int
410 pre_DT(DECL_ARGS)
411 {
412 term_tab_set(p, NULL);
413 term_tab_set(p, "T");
414 term_tab_set(p, ".5i");
415 return 0;
416 }
417
418 static int
419 pre_HP(DECL_ARGS)
420 {
421 struct roffsu su;
422 const struct roff_node *nn;
423 int len;
424
425 switch (n->type) {
426 case ROFFT_BLOCK:
427 print_bvspace(p, n, mt->pardist);
428 return 1;
429 case ROFFT_BODY:
430 break;
431 default:
432 return 0;
433 }
434
435 if ( ! (MANT_LITERAL & mt->fl)) {
436 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
437 p->trailspace = 2;
438 }
439
440 /* Calculate offset. */
441
442 if ((nn = n->parent->head->child) != NULL &&
443 a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
444 len = term_hen(p, &su);
445 if (len < 0 && (size_t)(-len) > mt->offset)
446 len = -mt->offset;
447 else if (len > SHRT_MAX)
448 len = term_len(p, p->defindent);
449 mt->lmargin[mt->lmargincur] = len;
450 } else
451 len = mt->lmargin[mt->lmargincur];
452
453 p->tcol->offset = mt->offset;
454 p->tcol->rmargin = mt->offset + len;
455 return 1;
456 }
457
458 static void
459 post_HP(DECL_ARGS)
460 {
461
462 switch (n->type) {
463 case ROFFT_BODY:
464 term_newln(p);
465
466 /*
467 * Compatibility with a groff bug.
468 * The .HP macro uses the undocumented .tag request
469 * which causes a line break and cancels no-space
470 * mode even if there isn't any output.
471 */
472
473 if (n->child == NULL)
474 term_vspace(p);
475
476 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
477 p->trailspace = 0;
478 p->tcol->offset = mt->offset;
479 p->tcol->rmargin = p->maxrmargin;
480 break;
481 default:
482 break;
483 }
484 }
485
486 static int
487 pre_PP(DECL_ARGS)
488 {
489
490 switch (n->type) {
491 case ROFFT_BLOCK:
492 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
493 print_bvspace(p, n, mt->pardist);
494 break;
495 default:
496 p->tcol->offset = mt->offset;
497 break;
498 }
499
500 return n->type != ROFFT_HEAD;
501 }
502
503 static int
504 pre_IP(DECL_ARGS)
505 {
506 struct roffsu su;
507 const struct roff_node *nn;
508 int len, savelit;
509
510 switch (n->type) {
511 case ROFFT_BODY:
512 p->flags |= TERMP_NOSPACE;
513 break;
514 case ROFFT_HEAD:
515 p->flags |= TERMP_NOBREAK;
516 p->trailspace = 1;
517 break;
518 case ROFFT_BLOCK:
519 print_bvspace(p, n, mt->pardist);
520 /* FALLTHROUGH */
521 default:
522 return 1;
523 }
524
525 /* Calculate the offset from the optional second argument. */
526 if ((nn = n->parent->head->child) != NULL &&
527 (nn = nn->next) != NULL &&
528 a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
529 len = term_hen(p, &su);
530 if (len < 0 && (size_t)(-len) > mt->offset)
531 len = -mt->offset;
532 else if (len > SHRT_MAX)
533 len = term_len(p, p->defindent);
534 mt->lmargin[mt->lmargincur] = len;
535 } else
536 len = mt->lmargin[mt->lmargincur];
537
538 switch (n->type) {
539 case ROFFT_HEAD:
540 p->tcol->offset = mt->offset;
541 p->tcol->rmargin = mt->offset + len;
542
543 savelit = MANT_LITERAL & mt->fl;
544 mt->fl &= ~MANT_LITERAL;
545
546 if (n->child)
547 print_man_node(p, mt, n->child, meta);
548
549 if (savelit)
550 mt->fl |= MANT_LITERAL;
551
552 return 0;
553 case ROFFT_BODY:
554 p->tcol->offset = mt->offset + len;
555 p->tcol->rmargin = p->maxrmargin;
556 break;
557 default:
558 break;
559 }
560
561 return 1;
562 }
563
564 static void
565 post_IP(DECL_ARGS)
566 {
567
568 switch (n->type) {
569 case ROFFT_HEAD:
570 term_flushln(p);
571 p->flags &= ~TERMP_NOBREAK;
572 p->trailspace = 0;
573 p->tcol->rmargin = p->maxrmargin;
574 break;
575 case ROFFT_BODY:
576 term_newln(p);
577 p->tcol->offset = mt->offset;
578 break;
579 default:
580 break;
581 }
582 }
583
584 static int
585 pre_TP(DECL_ARGS)
586 {
587 struct roffsu su;
588 struct roff_node *nn;
589 int len, savelit;
590
591 switch (n->type) {
592 case ROFFT_HEAD:
593 p->flags |= TERMP_NOBREAK | TERMP_BRTRSP;
594 p->trailspace = 1;
595 break;
596 case ROFFT_BODY:
597 p->flags |= TERMP_NOSPACE;
598 break;
599 case ROFFT_BLOCK:
600 if (n->tok == MAN_TP)
601 print_bvspace(p, n, mt->pardist);
602 /* FALLTHROUGH */
603 default:
604 return 1;
605 }
606
607 /* Calculate offset. */
608
609 if ((nn = n->parent->head->child) != NULL &&
610 nn->string != NULL && ! (NODE_LINE & nn->flags) &&
611 a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
612 len = term_hen(p, &su);
613 if (len < 0 && (size_t)(-len) > mt->offset)
614 len = -mt->offset;
615 else if (len > SHRT_MAX)
616 len = term_len(p, p->defindent);
617 mt->lmargin[mt->lmargincur] = len;
618 } else
619 len = mt->lmargin[mt->lmargincur];
620
621 switch (n->type) {
622 case ROFFT_HEAD:
623 p->tcol->offset = mt->offset;
624 p->tcol->rmargin = mt->offset + len;
625
626 savelit = MANT_LITERAL & mt->fl;
627 mt->fl &= ~MANT_LITERAL;
628
629 /* Don't print same-line elements. */
630 nn = n->child;
631 while (NULL != nn && 0 == (NODE_LINE & nn->flags))
632 nn = nn->next;
633
634 while (NULL != nn) {
635 print_man_node(p, mt, nn, meta);
636 nn = nn->next;
637 }
638
639 if (savelit)
640 mt->fl |= MANT_LITERAL;
641 return 0;
642 case ROFFT_BODY:
643 p->tcol->offset = mt->offset + len;
644 p->tcol->rmargin = p->maxrmargin;
645 p->trailspace = 0;
646 p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP);
647 break;
648 default:
649 break;
650 }
651
652 return 1;
653 }
654
655 static void
656 post_TP(DECL_ARGS)
657 {
658
659 switch (n->type) {
660 case ROFFT_HEAD:
661 term_flushln(p);
662 break;
663 case ROFFT_BODY:
664 term_newln(p);
665 p->tcol->offset = mt->offset;
666 break;
667 default:
668 break;
669 }
670 }
671
672 static int
673 pre_SS(DECL_ARGS)
674 {
675 int i;
676
677 switch (n->type) {
678 case ROFFT_BLOCK:
679 mt->fl &= ~MANT_LITERAL;
680 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
681 mt->offset = term_len(p, p->defindent);
682
683 /*
684 * No vertical space before the first subsection
685 * and after an empty subsection.
686 */
687
688 do {
689 n = n->prev;
690 } while (n != NULL && n->tok >= MAN_TH &&
691 man_term_act(n->tok)->flags & MAN_NOTEXT);
692 if (n == NULL || n->type == ROFFT_COMMENT ||
693 (n->tok == MAN_SS && n->body->child == NULL))
694 break;
695
696 for (i = 0; i < mt->pardist; i++)
697 term_vspace(p);
698 break;
699 case ROFFT_HEAD:
700 term_fontrepl(p, TERMFONT_BOLD);
701 p->tcol->offset = term_len(p, 3);
702 p->tcol->rmargin = mt->offset;
703 p->trailspace = mt->offset;
704 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
705 break;
706 case ROFFT_BODY:
707 p->tcol->offset = mt->offset;
708 p->tcol->rmargin = p->maxrmargin;
709 p->trailspace = 0;
710 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
711 break;
712 default:
713 break;
714 }
715
716 return 1;
717 }
718
719 static void
720 post_SS(DECL_ARGS)
721 {
722
723 switch (n->type) {
724 case ROFFT_HEAD:
725 term_newln(p);
726 break;
727 case ROFFT_BODY:
728 term_newln(p);
729 break;
730 default:
731 break;
732 }
733 }
734
735 static int
736 pre_SH(DECL_ARGS)
737 {
738 int i;
739
740 switch (n->type) {
741 case ROFFT_BLOCK:
742 mt->fl &= ~MANT_LITERAL;
743 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
744 mt->offset = term_len(p, p->defindent);
745
746 /*
747 * No vertical space before the first section
748 * and after an empty section.
749 */
750
751 do {
752 n = n->prev;
753 } while (n != NULL && n->tok >= MAN_TH &&
754 man_term_act(n->tok)->flags & MAN_NOTEXT);
755 if (n == NULL || n->type == ROFFT_COMMENT ||
756 (n->tok == MAN_SH && n->body->child == NULL))
757 break;
758
759 for (i = 0; i < mt->pardist; i++)
760 term_vspace(p);
761 break;
762 case ROFFT_HEAD:
763 term_fontrepl(p, TERMFONT_BOLD);
764 p->tcol->offset = 0;
765 p->tcol->rmargin = mt->offset;
766 p->trailspace = mt->offset;
767 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
768 break;
769 case ROFFT_BODY:
770 p->tcol->offset = mt->offset;
771 p->tcol->rmargin = p->maxrmargin;
772 p->trailspace = 0;
773 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
774 break;
775 default:
776 break;
777 }
778
779 return 1;
780 }
781
782 static void
783 post_SH(DECL_ARGS)
784 {
785
786 switch (n->type) {
787 case ROFFT_HEAD:
788 term_newln(p);
789 break;
790 case ROFFT_BODY:
791 term_newln(p);
792 break;
793 default:
794 break;
795 }
796 }
797
798 static int
799 pre_RS(DECL_ARGS)
800 {
801 struct roffsu su;
802
803 switch (n->type) {
804 case ROFFT_BLOCK:
805 term_newln(p);
806 return 1;
807 case ROFFT_HEAD:
808 return 0;
809 default:
810 break;
811 }
812
813 n = n->parent->head;
814 n->aux = SHRT_MAX + 1;
815 if (n->child == NULL)
816 n->aux = mt->lmargin[mt->lmargincur];
817 else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
818 n->aux = term_hen(p, &su);
819 if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
820 n->aux = -mt->offset;
821 else if (n->aux > SHRT_MAX)
822 n->aux = term_len(p, p->defindent);
823
824 mt->offset += n->aux;
825 p->tcol->offset = mt->offset;
826 p->tcol->rmargin = p->maxrmargin;
827
828 if (++mt->lmarginsz < MAXMARGINS)
829 mt->lmargincur = mt->lmarginsz;
830
831 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
832 return 1;
833 }
834
835 static void
836 post_RS(DECL_ARGS)
837 {
838
839 switch (n->type) {
840 case ROFFT_BLOCK:
841 return;
842 case ROFFT_HEAD:
843 return;
844 default:
845 term_newln(p);
846 break;
847 }
848
849 mt->offset -= n->parent->head->aux;
850 p->tcol->offset = mt->offset;
851
852 if (--mt->lmarginsz < MAXMARGINS)
853 mt->lmargincur = mt->lmarginsz;
854 }
855
856 static int
857 pre_SY(DECL_ARGS)
858 {
859 const struct roff_node *nn;
860 int len;
861
862 switch (n->type) {
863 case ROFFT_BLOCK:
864 if (n->prev == NULL || n->prev->tok != MAN_SY)
865 print_bvspace(p, n, mt->pardist);
866 return 1;
867 case ROFFT_HEAD:
868 case ROFFT_BODY:
869 break;
870 default:
871 abort();
872 }
873
874 nn = n->parent->head->child;
875 len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
876
877 switch (n->type) {
878 case ROFFT_HEAD:
879 p->tcol->offset = mt->offset;
880 p->tcol->rmargin = mt->offset + len;
881 p->flags |= TERMP_NOBREAK;
882 term_fontrepl(p, TERMFONT_BOLD);
883 break;
884 case ROFFT_BODY:
885 mt->lmargin[mt->lmargincur] = len;
886 p->tcol->offset = mt->offset + len;
887 p->tcol->rmargin = p->maxrmargin;
888 p->flags |= TERMP_NOSPACE;
889 break;
890 default:
891 abort();
892 }
893 return 1;
894 }
895
896 static void
897 post_SY(DECL_ARGS)
898 {
899 switch (n->type) {
900 case ROFFT_HEAD:
901 term_flushln(p);
902 p->flags &= ~TERMP_NOBREAK;
903 break;
904 case ROFFT_BODY:
905 term_newln(p);
906 p->tcol->offset = mt->offset;
907 break;
908 default:
909 break;
910 }
911 }
912
913 static int
914 pre_UR(DECL_ARGS)
915 {
916
917 return n->type != ROFFT_HEAD;
918 }
919
920 static void
921 post_UR(DECL_ARGS)
922 {
923
924 if (n->type != ROFFT_BLOCK)
925 return;
926
927 term_word(p, "<");
928 p->flags |= TERMP_NOSPACE;
929
930 if (NULL != n->child->child)
931 print_man_node(p, mt, n->child->child, meta);
932
933 p->flags |= TERMP_NOSPACE;
934 term_word(p, ">");
935 }
936
937 static void
938 print_man_node(DECL_ARGS)
939 {
940 const struct man_term_act *act;
941 int c;
942
943 switch (n->type) {
944 case ROFFT_TEXT:
945 /*
946 * If we have a blank line, output a vertical space.
947 * If we have a space as the first character, break
948 * before printing the line's data.
949 */
950 if (*n->string == '\0') {
951 if (p->flags & TERMP_NONEWLINE)
952 term_newln(p);
953 else
954 term_vspace(p);
955 return;
956 } else if (*n->string == ' ' && n->flags & NODE_LINE &&
957 (p->flags & TERMP_NONEWLINE) == 0)
958 term_newln(p);
959 else if (n->flags & NODE_DELIMC)
960 p->flags |= TERMP_NOSPACE;
961
962 term_word(p, n->string);
963 goto out;
964 case ROFFT_COMMENT:
965 return;
966 case ROFFT_EQN:
967 if ( ! (n->flags & NODE_LINE))
968 p->flags |= TERMP_NOSPACE;
969 term_eqn(p, n->eqn);
970 if (n->next != NULL && ! (n->next->flags & NODE_LINE))
971 p->flags |= TERMP_NOSPACE;
972 return;
973 case ROFFT_TBL:
974 if (p->tbl.cols == NULL)
975 term_vspace(p);
976 term_tbl(p, n->span);
977 return;
978 default:
979 break;
980 }
981
982 if (n->tok < ROFF_MAX) {
983 roff_term_pre(p, n);
984 return;
985 }
986
987 act = man_term_act(n->tok);
988 if ((act->flags & MAN_NOTEXT) == 0)
989 term_fontrepl(p, TERMFONT_NONE);
990
991 c = 1;
992 if (act->pre != NULL)
993 c = (*act->pre)(p, mt, n, meta);
994
995 if (c && n->child)
996 print_man_nodelist(p, mt, n->child, meta);
997
998 if (act->post != NULL)
999 (*act->post)(p, mt, n, meta);
1000 if ((act->flags & MAN_NOTEXT) == 0)
1001 term_fontrepl(p, TERMFONT_NONE);
1002
1003 out:
1004 /*
1005 * If we're in a literal context, make sure that words
1006 * together on the same line stay together. This is a
1007 * POST-printing call, so we check the NEXT word. Since
1008 * -man doesn't have nested macros, we don't need to be
1009 * more specific than this.
1010 */
1011 if (mt->fl & MANT_LITERAL &&
1012 ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) &&
1013 (n->next == NULL || n->next->flags & NODE_LINE)) {
1014 p->flags |= TERMP_BRNEVER | TERMP_NOSPACE;
1015 if (n->string != NULL && *n->string != '\0')
1016 term_flushln(p);
1017 else
1018 term_newln(p);
1019 p->flags &= ~TERMP_BRNEVER;
1020 if (p->tcol->rmargin < p->maxrmargin &&
1021 n->parent->tok == MAN_HP) {
1022 p->tcol->offset = p->tcol->rmargin;
1023 p->tcol->rmargin = p->maxrmargin;
1024 }
1025 }
1026 if (NODE_EOS & n->flags)
1027 p->flags |= TERMP_SENTENCE;
1028 }
1029
1030
1031 static void
1032 print_man_nodelist(DECL_ARGS)
1033 {
1034
1035 while (n != NULL) {
1036 print_man_node(p, mt, n, meta);
1037 n = n->next;
1038 }
1039 }
1040
1041 static void
1042 print_man_foot(struct termp *p, const struct roff_meta *meta)
1043 {
1044 char *title;
1045 size_t datelen, titlen;
1046
1047 assert(meta->title);
1048 assert(meta->msec);
1049 assert(meta->date);
1050
1051 term_fontrepl(p, TERMFONT_NONE);
1052
1053 if (meta->hasbody)
1054 term_vspace(p);
1055
1056 /*
1057 * Temporary, undocumented option to imitate mdoc(7) output.
1058 * In the bottom right corner, use the operating system
1059 * instead of the title.
1060 */
1061
1062 if ( ! p->mdocstyle) {
1063 if (meta->hasbody) {
1064 term_vspace(p);
1065 term_vspace(p);
1066 }
1067 mandoc_asprintf(&title, "%s(%s)",
1068 meta->title, meta->msec);
1069 } else if (meta->os) {
1070 title = mandoc_strdup(meta->os);
1071 } else {
1072 title = mandoc_strdup("");
1073 }
1074 datelen = term_strlen(p, meta->date);
1075
1076 /* Bottom left corner: operating system. */
1077
1078 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1079 p->trailspace = 1;
1080 p->tcol->offset = 0;
1081 p->tcol->rmargin = p->maxrmargin > datelen ?
1082 (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
1083
1084 if (meta->os)
1085 term_word(p, meta->os);
1086 term_flushln(p);
1087
1088 /* At the bottom in the middle: manual date. */
1089
1090 p->tcol->offset = p->tcol->rmargin;
1091 titlen = term_strlen(p, title);
1092 p->tcol->rmargin = p->maxrmargin > titlen ?
1093 p->maxrmargin - titlen : 0;
1094 p->flags |= TERMP_NOSPACE;
1095
1096 term_word(p, meta->date);
1097 term_flushln(p);
1098
1099 /* Bottom right corner: manual title and section. */
1100
1101 p->flags &= ~TERMP_NOBREAK;
1102 p->flags |= TERMP_NOSPACE;
1103 p->trailspace = 0;
1104 p->tcol->offset = p->tcol->rmargin;
1105 p->tcol->rmargin = p->maxrmargin;
1106
1107 term_word(p, title);
1108 term_flushln(p);
1109
1110 /*
1111 * Reset the terminal state for more output after the footer:
1112 * Some output modes, in particular PostScript and PDF, print
1113 * the header and the footer into a buffer such that it can be
1114 * reused for multiple output pages, then go on to format the
1115 * main text.
1116 */
1117
1118 p->tcol->offset = 0;
1119 p->flags = 0;
1120
1121 free(title);
1122 }
1123
1124 static void
1125 print_man_head(struct termp *p, const struct roff_meta *meta)
1126 {
1127 const char *volume;
1128 char *title;
1129 size_t vollen, titlen;
1130
1131 assert(meta->title);
1132 assert(meta->msec);
1133
1134 volume = NULL == meta->vol ? "" : meta->vol;
1135 vollen = term_strlen(p, volume);
1136
1137 /* Top left corner: manual title and section. */
1138
1139 mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1140 titlen = term_strlen(p, title);
1141
1142 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1143 p->trailspace = 1;
1144 p->tcol->offset = 0;
1145 p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1146 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1147 vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
1148
1149 term_word(p, title);
1150 term_flushln(p);
1151
1152 /* At the top in the middle: manual volume. */
1153
1154 p->flags |= TERMP_NOSPACE;
1155 p->tcol->offset = p->tcol->rmargin;
1156 p->tcol->rmargin = p->tcol->offset + vollen + titlen <
1157 p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
1158
1159 term_word(p, volume);
1160 term_flushln(p);
1161
1162 /* Top right corner: title and section, again. */
1163
1164 p->flags &= ~TERMP_NOBREAK;
1165 p->trailspace = 0;
1166 if (p->tcol->rmargin + titlen <= p->maxrmargin) {
1167 p->flags |= TERMP_NOSPACE;
1168 p->tcol->offset = p->tcol->rmargin;
1169 p->tcol->rmargin = p->maxrmargin;
1170 term_word(p, title);
1171 term_flushln(p);
1172 }
1173
1174 p->flags &= ~TERMP_NOSPACE;
1175 p->tcol->offset = 0;
1176 p->tcol->rmargin = p->maxrmargin;
1177
1178 /*
1179 * Groff prints three blank lines before the content.
1180 * Do the same, except in the temporary, undocumented
1181 * mode imitating mdoc(7) output.
1182 */
1183
1184 term_vspace(p);
1185 if ( ! p->mdocstyle) {
1186 term_vspace(p);
1187 term_vspace(p);
1188 }
1189 free(title);
1190 }