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