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