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