]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Support nesting of indented blocks.
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.116 2011/09/18 21:08:34 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011 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 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <sys/types.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "mandoc.h"
31 #include "out.h"
32 #include "man.h"
33 #include "term.h"
34 #include "main.h"
35
36 #define INDENT 7 /* fixed-width char full-indent */
37 #define HALFINDENT 3 /* fixed-width char half-indent */
38 #define MAXMARGINS 64 /* maximum number of indented scopes */
39
40 /* FIXME: have PD set the default vspace width. */
41
42 struct mtermp {
43 int fl;
44 #define MANT_LITERAL (1 << 0)
45 size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */
46 int lmargincur; /* index of current margin */
47 int lmarginsz; /* actual number of nested margins */
48 size_t offset; /* default offset to visible page */
49 };
50
51 #define DECL_ARGS struct termp *p, \
52 struct mtermp *mt, \
53 const struct man_node *n, \
54 const struct man_meta *m
55
56 struct termact {
57 int (*pre)(DECL_ARGS);
58 void (*post)(DECL_ARGS);
59 int flags;
60 #define MAN_NOTEXT (1 << 0) /* Never has text children. */
61 };
62
63 static int a2width(const struct termp *, const char *);
64 static size_t a2height(const struct termp *, const char *);
65
66 static void print_man_nodelist(DECL_ARGS);
67 static void print_man_node(DECL_ARGS);
68 static void print_man_head(struct termp *, const void *);
69 static void print_man_foot(struct termp *, const void *);
70 static void print_bvspace(struct termp *,
71 const struct man_node *);
72
73 static int pre_alternate(DECL_ARGS);
74 static int pre_B(DECL_ARGS);
75 static int pre_HP(DECL_ARGS);
76 static int pre_I(DECL_ARGS);
77 static int pre_IP(DECL_ARGS);
78 static int pre_PP(DECL_ARGS);
79 static int pre_RS(DECL_ARGS);
80 static int pre_SH(DECL_ARGS);
81 static int pre_SS(DECL_ARGS);
82 static int pre_TP(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_sp(DECL_ARGS);
87 static int pre_ft(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
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_ign, NULL, MAN_NOTEXT }, /* na */
119 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
120 { pre_literal, NULL, 0 }, /* nf */
121 { pre_literal, NULL, 0 }, /* fi */
122 { NULL, NULL, 0 }, /* RE */
123 { pre_RS, post_RS, 0 }, /* RS */
124 { pre_ign, NULL, 0 }, /* DT */
125 { pre_ign, NULL, 0 }, /* UC */
126 { pre_ign, NULL, 0 }, /* PD */
127 { pre_ign, NULL, 0 }, /* AT */
128 { pre_in, NULL, MAN_NOTEXT }, /* in */
129 { pre_ft, NULL, MAN_NOTEXT }, /* ft */
130 };
131
132
133
134 void
135 terminal_man(void *arg, const struct man *man)
136 {
137 struct termp *p;
138 const struct man_node *n;
139 const struct man_meta *m;
140 struct mtermp mt;
141
142 p = (struct termp *)arg;
143
144 p->overstep = 0;
145 p->maxrmargin = p->defrmargin;
146 p->tabwidth = term_len(p, 5);
147
148 if (NULL == p->symtab)
149 p->symtab = mchars_alloc();
150
151 n = man_node(man);
152 m = man_meta(man);
153
154 term_begin(p, print_man_head, print_man_foot, m);
155 p->flags |= TERMP_NOSPACE;
156
157 memset(&mt, 0, sizeof(struct mtermp));
158
159 mt.lmargin[mt.lmargincur] = term_len(p, INDENT);
160 mt.offset = term_len(p, INDENT);
161
162 if (n->child)
163 print_man_nodelist(p, &mt, n->child, m);
164
165 term_end(p);
166 }
167
168
169 static size_t
170 a2height(const struct termp *p, const char *cp)
171 {
172 struct roffsu su;
173
174 if ( ! a2roffsu(cp, &su, SCALE_VS))
175 SCALE_VS_INIT(&su, atoi(cp));
176
177 return(term_vspan(p, &su));
178 }
179
180
181 static int
182 a2width(const struct termp *p, const char *cp)
183 {
184 struct roffsu su;
185
186 if ( ! a2roffsu(cp, &su, SCALE_BU))
187 return(-1);
188
189 return((int)term_hspan(p, &su));
190 }
191
192 /*
193 * Printing leading vertical space before a block.
194 * This is used for the paragraph macros.
195 * The rules are pretty simple, since there's very little nesting going
196 * on here. Basically, if we're the first within another block (SS/SH),
197 * then don't emit vertical space. If we are (RS), then do. If not the
198 * first, print it.
199 */
200 static void
201 print_bvspace(struct termp *p, const struct man_node *n)
202 {
203
204 term_newln(p);
205
206 if (n->body && n->body->child)
207 if (MAN_TBL == n->body->child->type)
208 return;
209
210 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
211 if (NULL == n->prev)
212 return;
213
214 term_vspace(p);
215 }
216
217 /* ARGSUSED */
218 static int
219 pre_ign(DECL_ARGS)
220 {
221
222 return(0);
223 }
224
225
226 /* ARGSUSED */
227 static int
228 pre_I(DECL_ARGS)
229 {
230
231 term_fontrepl(p, TERMFONT_UNDER);
232 return(1);
233 }
234
235
236 /* ARGSUSED */
237 static int
238 pre_literal(DECL_ARGS)
239 {
240
241 term_newln(p);
242
243 if (MAN_nf == n->tok)
244 mt->fl |= MANT_LITERAL;
245 else
246 mt->fl &= ~MANT_LITERAL;
247
248 return(0);
249 }
250
251 /* ARGSUSED */
252 static int
253 pre_alternate(DECL_ARGS)
254 {
255 enum termfont font[2];
256 const struct man_node *nn;
257 int savelit, i;
258
259 switch (n->tok) {
260 case (MAN_RB):
261 font[0] = TERMFONT_NONE;
262 font[1] = TERMFONT_BOLD;
263 break;
264 case (MAN_RI):
265 font[0] = TERMFONT_NONE;
266 font[1] = TERMFONT_UNDER;
267 break;
268 case (MAN_BR):
269 font[0] = TERMFONT_BOLD;
270 font[1] = TERMFONT_NONE;
271 break;
272 case (MAN_BI):
273 font[0] = TERMFONT_BOLD;
274 font[1] = TERMFONT_UNDER;
275 break;
276 case (MAN_IR):
277 font[0] = TERMFONT_UNDER;
278 font[1] = TERMFONT_NONE;
279 break;
280 case (MAN_IB):
281 font[0] = TERMFONT_UNDER;
282 font[1] = TERMFONT_BOLD;
283 break;
284 default:
285 abort();
286 }
287
288 savelit = MANT_LITERAL & mt->fl;
289 mt->fl &= ~MANT_LITERAL;
290
291 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
292 term_fontrepl(p, font[i]);
293 if (savelit && NULL == nn->next)
294 mt->fl |= MANT_LITERAL;
295 print_man_node(p, mt, nn, m);
296 if (nn->next)
297 p->flags |= TERMP_NOSPACE;
298 }
299
300 return(0);
301 }
302
303 /* ARGSUSED */
304 static int
305 pre_B(DECL_ARGS)
306 {
307
308 term_fontrepl(p, TERMFONT_BOLD);
309 return(1);
310 }
311
312 /* ARGSUSED */
313 static int
314 pre_ft(DECL_ARGS)
315 {
316 const char *cp;
317
318 if (NULL == n->child) {
319 term_fontlast(p);
320 return(0);
321 }
322
323 cp = n->child->string;
324 switch (*cp) {
325 case ('4'):
326 /* FALLTHROUGH */
327 case ('3'):
328 /* FALLTHROUGH */
329 case ('B'):
330 term_fontrepl(p, TERMFONT_BOLD);
331 break;
332 case ('2'):
333 /* FALLTHROUGH */
334 case ('I'):
335 term_fontrepl(p, TERMFONT_UNDER);
336 break;
337 case ('P'):
338 term_fontlast(p);
339 break;
340 case ('1'):
341 /* FALLTHROUGH */
342 case ('C'):
343 /* FALLTHROUGH */
344 case ('R'):
345 term_fontrepl(p, TERMFONT_NONE);
346 break;
347 default:
348 break;
349 }
350 return(0);
351 }
352
353 /* ARGSUSED */
354 static int
355 pre_in(DECL_ARGS)
356 {
357 int len, less;
358 size_t v;
359 const char *cp;
360
361 term_newln(p);
362
363 if (NULL == n->child) {
364 p->offset = mt->offset;
365 return(0);
366 }
367
368 cp = n->child->string;
369 less = 0;
370
371 if ('-' == *cp)
372 less = -1;
373 else if ('+' == *cp)
374 less = 1;
375 else
376 cp--;
377
378 if ((len = a2width(p, ++cp)) < 0)
379 return(0);
380
381 v = (size_t)len;
382
383 if (less < 0)
384 p->offset -= p->offset > v ? v : p->offset;
385 else if (less > 0)
386 p->offset += v;
387 else
388 p->offset = v;
389
390 /* Don't let this creep beyond the right margin. */
391
392 if (p->offset > p->rmargin)
393 p->offset = p->rmargin;
394
395 return(0);
396 }
397
398
399 /* ARGSUSED */
400 static int
401 pre_sp(DECL_ARGS)
402 {
403 size_t i, len;
404
405 if ((NULL == n->prev && n->parent)) {
406 if (MAN_SS == n->parent->tok)
407 return(0);
408 if (MAN_SH == n->parent->tok)
409 return(0);
410 }
411
412 switch (n->tok) {
413 case (MAN_br):
414 len = 0;
415 break;
416 default:
417 len = n->child ? a2height(p, n->child->string) : 1;
418 break;
419 }
420
421 if (0 == len)
422 term_newln(p);
423 for (i = 0; i < len; i++)
424 term_vspace(p);
425
426 return(0);
427 }
428
429
430 /* ARGSUSED */
431 static int
432 pre_HP(DECL_ARGS)
433 {
434 size_t len;
435 int ival;
436 const struct man_node *nn;
437
438 switch (n->type) {
439 case (MAN_BLOCK):
440 print_bvspace(p, n);
441 return(1);
442 case (MAN_BODY):
443 p->flags |= TERMP_NOBREAK;
444 p->flags |= TERMP_TWOSPACE;
445 break;
446 default:
447 return(0);
448 }
449
450 len = mt->lmargin[mt->lmargincur];
451 ival = -1;
452
453 /* Calculate offset. */
454
455 if (NULL != (nn = n->parent->head->child))
456 if ((ival = a2width(p, nn->string)) >= 0)
457 len = (size_t)ival;
458
459 if (0 == len)
460 len = term_len(p, 1);
461
462 p->offset = mt->offset;
463 p->rmargin = mt->offset + len;
464
465 if (ival >= 0)
466 mt->lmargin[mt->lmargincur] = (size_t)ival;
467
468 return(1);
469 }
470
471
472 /* ARGSUSED */
473 static void
474 post_HP(DECL_ARGS)
475 {
476
477 switch (n->type) {
478 case (MAN_BLOCK):
479 term_flushln(p);
480 break;
481 case (MAN_BODY):
482 term_flushln(p);
483 p->flags &= ~TERMP_NOBREAK;
484 p->flags &= ~TERMP_TWOSPACE;
485 p->offset = mt->offset;
486 p->rmargin = p->maxrmargin;
487 break;
488 default:
489 break;
490 }
491 }
492
493
494 /* ARGSUSED */
495 static int
496 pre_PP(DECL_ARGS)
497 {
498
499 switch (n->type) {
500 case (MAN_BLOCK):
501 mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
502 print_bvspace(p, n);
503 break;
504 default:
505 p->offset = mt->offset;
506 break;
507 }
508
509 return(MAN_HEAD != n->type);
510 }
511
512
513 /* ARGSUSED */
514 static int
515 pre_IP(DECL_ARGS)
516 {
517 const struct man_node *nn;
518 size_t len;
519 int savelit, ival;
520
521 switch (n->type) {
522 case (MAN_BODY):
523 p->flags |= TERMP_NOLPAD;
524 p->flags |= TERMP_NOSPACE;
525 break;
526 case (MAN_HEAD):
527 p->flags |= TERMP_NOBREAK;
528 break;
529 case (MAN_BLOCK):
530 print_bvspace(p, n);
531 /* FALLTHROUGH */
532 default:
533 return(1);
534 }
535
536 len = mt->lmargin[mt->lmargincur];
537 ival = -1;
538
539 /* Calculate the offset from the optional second argument. */
540 if (NULL != (nn = n->parent->head->child))
541 if (NULL != (nn = nn->next))
542 if ((ival = a2width(p, nn->string)) >= 0)
543 len = (size_t)ival;
544
545 switch (n->type) {
546 case (MAN_HEAD):
547 /* Handle zero-width lengths. */
548 if (0 == len)
549 len = term_len(p, 1);
550
551 p->offset = mt->offset;
552 p->rmargin = mt->offset + len;
553 if (ival < 0)
554 break;
555
556 /* Set the saved left-margin. */
557 mt->lmargin[mt->lmargincur] = (size_t)ival;
558
559 savelit = MANT_LITERAL & mt->fl;
560 mt->fl &= ~MANT_LITERAL;
561
562 if (n->child)
563 print_man_node(p, mt, n->child, m);
564
565 if (savelit)
566 mt->fl |= MANT_LITERAL;
567
568 return(0);
569 case (MAN_BODY):
570 p->offset = mt->offset + len;
571 p->rmargin = p->maxrmargin;
572 break;
573 default:
574 break;
575 }
576
577 return(1);
578 }
579
580
581 /* ARGSUSED */
582 static void
583 post_IP(DECL_ARGS)
584 {
585
586 switch (n->type) {
587 case (MAN_HEAD):
588 term_flushln(p);
589 p->flags &= ~TERMP_NOBREAK;
590 p->rmargin = p->maxrmargin;
591 break;
592 case (MAN_BODY):
593 term_newln(p);
594 p->flags &= ~TERMP_NOLPAD;
595 break;
596 default:
597 break;
598 }
599 }
600
601
602 /* ARGSUSED */
603 static int
604 pre_TP(DECL_ARGS)
605 {
606 const struct man_node *nn;
607 size_t len;
608 int savelit, ival;
609
610 switch (n->type) {
611 case (MAN_HEAD):
612 p->flags |= TERMP_NOBREAK;
613 break;
614 case (MAN_BODY):
615 p->flags |= TERMP_NOLPAD;
616 p->flags |= TERMP_NOSPACE;
617 break;
618 case (MAN_BLOCK):
619 print_bvspace(p, n);
620 /* FALLTHROUGH */
621 default:
622 return(1);
623 }
624
625 len = (size_t)mt->lmargin[mt->lmargincur];
626 ival = -1;
627
628 /* Calculate offset. */
629
630 if (NULL != (nn = n->parent->head->child))
631 if (nn->parent->line == nn->line)
632 if ((ival = a2width(p, nn->string)) >= 0)
633 len = (size_t)ival;
634
635 switch (n->type) {
636 case (MAN_HEAD):
637 /* Handle zero-length properly. */
638 if (0 == len)
639 len = term_len(p, 1);
640
641 p->offset = mt->offset;
642 p->rmargin = mt->offset + len;
643
644 savelit = MANT_LITERAL & mt->fl;
645 mt->fl &= ~MANT_LITERAL;
646
647 /* Don't print same-line elements. */
648 for (nn = n->child; nn; nn = nn->next)
649 if (nn->line > n->line)
650 print_man_node(p, mt, nn, m);
651
652 if (savelit)
653 mt->fl |= MANT_LITERAL;
654 if (ival >= 0)
655 mt->lmargin[mt->lmargincur] = (size_t)ival;
656
657 return(0);
658 case (MAN_BODY):
659 p->offset = mt->offset + len;
660 p->rmargin = p->maxrmargin;
661 break;
662 default:
663 break;
664 }
665
666 return(1);
667 }
668
669
670 /* ARGSUSED */
671 static void
672 post_TP(DECL_ARGS)
673 {
674
675 switch (n->type) {
676 case (MAN_HEAD):
677 term_flushln(p);
678 p->flags &= ~TERMP_NOBREAK;
679 p->flags &= ~TERMP_TWOSPACE;
680 p->rmargin = p->maxrmargin;
681 break;
682 case (MAN_BODY):
683 term_newln(p);
684 p->flags &= ~TERMP_NOLPAD;
685 break;
686 default:
687 break;
688 }
689 }
690
691
692 /* ARGSUSED */
693 static int
694 pre_SS(DECL_ARGS)
695 {
696
697 switch (n->type) {
698 case (MAN_BLOCK):
699 mt->fl &= ~MANT_LITERAL;
700 mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
701 mt->offset = term_len(p, INDENT);
702 /* If following a prior empty `SS', no vspace. */
703 if (n->prev && MAN_SS == n->prev->tok)
704 if (NULL == n->prev->body->child)
705 break;
706 if (NULL == n->prev)
707 break;
708 term_vspace(p);
709 break;
710 case (MAN_HEAD):
711 term_fontrepl(p, TERMFONT_BOLD);
712 p->offset = term_len(p, HALFINDENT);
713 break;
714 case (MAN_BODY):
715 p->offset = mt->offset;
716 break;
717 default:
718 break;
719 }
720
721 return(1);
722 }
723
724
725 /* ARGSUSED */
726 static void
727 post_SS(DECL_ARGS)
728 {
729
730 switch (n->type) {
731 case (MAN_HEAD):
732 term_newln(p);
733 break;
734 case (MAN_BODY):
735 term_newln(p);
736 break;
737 default:
738 break;
739 }
740 }
741
742
743 /* ARGSUSED */
744 static int
745 pre_SH(DECL_ARGS)
746 {
747
748 switch (n->type) {
749 case (MAN_BLOCK):
750 mt->fl &= ~MANT_LITERAL;
751 mt->lmargin[mt->lmargincur] = term_len(p, INDENT);
752 mt->offset = term_len(p, INDENT);
753 /* If following a prior empty `SH', no vspace. */
754 if (n->prev && MAN_SH == n->prev->tok)
755 if (NULL == n->prev->body->child)
756 break;
757 /* If the first macro, no vspae. */
758 if (NULL == n->prev)
759 break;
760 term_vspace(p);
761 break;
762 case (MAN_HEAD):
763 term_fontrepl(p, TERMFONT_BOLD);
764 p->offset = 0;
765 break;
766 case (MAN_BODY):
767 p->offset = mt->offset;
768 break;
769 default:
770 break;
771 }
772
773 return(1);
774 }
775
776
777 /* ARGSUSED */
778 static void
779 post_SH(DECL_ARGS)
780 {
781
782 switch (n->type) {
783 case (MAN_HEAD):
784 term_newln(p);
785 break;
786 case (MAN_BODY):
787 term_newln(p);
788 break;
789 default:
790 break;
791 }
792 }
793
794 /* ARGSUSED */
795 static int
796 pre_RS(DECL_ARGS)
797 {
798 int ival;
799 size_t sz;
800
801 switch (n->type) {
802 case (MAN_BLOCK):
803 term_newln(p);
804 return(1);
805 case (MAN_HEAD):
806 return(0);
807 default:
808 break;
809 }
810
811 sz = term_len(p, INDENT);
812
813 if (NULL != (n = n->parent->head->child))
814 if ((ival = a2width(p, n->string)) >= 0)
815 sz = (size_t)ival;
816
817 mt->offset += sz;
818 p->offset = mt->offset;
819
820 if (++mt->lmarginsz < MAXMARGINS)
821 mt->lmargincur = mt->lmarginsz;
822
823 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
824 return(1);
825 }
826
827 /* ARGSUSED */
828 static void
829 post_RS(DECL_ARGS)
830 {
831 int ival;
832 size_t sz;
833
834 switch (n->type) {
835 case (MAN_BLOCK):
836 return;
837 case (MAN_HEAD):
838 return;
839 default:
840 term_newln(p);
841 break;
842 }
843
844 sz = term_len(p, INDENT);
845
846 if (NULL != (n = n->parent->head->child))
847 if ((ival = a2width(p, n->string)) >= 0)
848 sz = (size_t)ival;
849
850 mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
851 p->offset = mt->offset;
852
853 if (--mt->lmarginsz < MAXMARGINS)
854 mt->lmargincur = mt->lmarginsz;
855 }
856
857 static void
858 print_man_node(DECL_ARGS)
859 {
860 size_t rm, rmax;
861 int c;
862
863 switch (n->type) {
864 case(MAN_TEXT):
865 /*
866 * If we have a blank line, output a vertical space.
867 * If we have a space as the first character, break
868 * before printing the line's data.
869 */
870 if ('\0' == *n->string) {
871 term_vspace(p);
872 return;
873 } else if (' ' == *n->string && MAN_LINE & n->flags)
874 term_newln(p);
875
876 term_word(p, n->string);
877
878 /*
879 * If we're in a literal context, make sure that words
880 * togehter on the same line stay together. This is a
881 * POST-printing call, so we check the NEXT word. Since
882 * -man doesn't have nested macros, we don't need to be
883 * more specific than this.
884 */
885 if (MANT_LITERAL & mt->fl &&
886 (NULL == n->next ||
887 n->next->line > n->line)) {
888 rm = p->rmargin;
889 rmax = p->maxrmargin;
890 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
891 p->flags |= TERMP_NOSPACE;
892 term_flushln(p);
893 p->flags &= ~TERMP_NOLPAD;
894 p->rmargin = rm;
895 p->maxrmargin = rmax;
896 }
897
898 if (MAN_EOS & n->flags)
899 p->flags |= TERMP_SENTENCE;
900 return;
901 case (MAN_EQN):
902 term_eqn(p, n->eqn);
903 return;
904 case (MAN_TBL):
905 /*
906 * Tables are preceded by a newline. Then process a
907 * table line, which will cause line termination,
908 */
909 if (TBL_SPAN_FIRST & n->span->flags)
910 term_newln(p);
911 term_tbl(p, n->span);
912 return;
913 default:
914 break;
915 }
916
917 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
918 term_fontrepl(p, TERMFONT_NONE);
919
920 c = 1;
921 if (termacts[n->tok].pre)
922 c = (*termacts[n->tok].pre)(p, mt, n, m);
923
924 if (c && n->child)
925 print_man_nodelist(p, mt, n->child, m);
926
927 if (termacts[n->tok].post)
928 (*termacts[n->tok].post)(p, mt, n, m);
929 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
930 term_fontrepl(p, TERMFONT_NONE);
931
932 if (MAN_EOS & n->flags)
933 p->flags |= TERMP_SENTENCE;
934 }
935
936
937 static void
938 print_man_nodelist(DECL_ARGS)
939 {
940
941 print_man_node(p, mt, n, m);
942 if ( ! n->next)
943 return;
944 print_man_nodelist(p, mt, n->next, m);
945 }
946
947
948 static void
949 print_man_foot(struct termp *p, const void *arg)
950 {
951 const struct man_meta *meta;
952
953 meta = (const struct man_meta *)arg;
954
955 term_fontrepl(p, TERMFONT_NONE);
956
957 term_vspace(p);
958 term_vspace(p);
959 term_vspace(p);
960
961 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
962 p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
963 p->offset = 0;
964
965 /* term_strlen() can return zero. */
966 if (p->rmargin == p->maxrmargin)
967 p->rmargin--;
968
969 if (meta->source)
970 term_word(p, meta->source);
971 if (meta->source)
972 term_word(p, "");
973 term_flushln(p);
974
975 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
976 p->offset = p->rmargin;
977 p->rmargin = p->maxrmargin;
978 p->flags &= ~TERMP_NOBREAK;
979
980 term_word(p, meta->date);
981 term_flushln(p);
982 }
983
984
985 static void
986 print_man_head(struct termp *p, const void *arg)
987 {
988 char buf[BUFSIZ], title[BUFSIZ];
989 size_t buflen, titlen;
990 const struct man_meta *m;
991
992 m = (const struct man_meta *)arg;
993
994 /*
995 * Note that old groff would spit out some spaces before the
996 * header. We discontinue this strange behaviour, but at one
997 * point we did so here.
998 */
999
1000 p->rmargin = p->maxrmargin;
1001
1002 p->offset = 0;
1003 buf[0] = title[0] = '\0';
1004
1005 if (m->vol)
1006 strlcpy(buf, m->vol, BUFSIZ);
1007 buflen = term_strlen(p, buf);
1008
1009 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1010 titlen = term_strlen(p, title);
1011
1012 p->offset = 0;
1013 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1014 (p->maxrmargin -
1015 term_strlen(p, buf) + term_len(p, 1)) / 2 :
1016 p->maxrmargin - buflen;
1017 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1018
1019 term_word(p, title);
1020 term_flushln(p);
1021
1022 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1023 p->offset = p->rmargin;
1024 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1025 p->maxrmargin - titlen : p->maxrmargin;
1026
1027 term_word(p, buf);
1028 term_flushln(p);
1029
1030 p->flags &= ~TERMP_NOBREAK;
1031 if (p->rmargin + titlen <= p->maxrmargin) {
1032 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1033 p->offset = p->rmargin;
1034 p->rmargin = p->maxrmargin;
1035 term_word(p, title);
1036 term_flushln(p);
1037 }
1038
1039 p->rmargin = p->maxrmargin;
1040 p->offset = 0;
1041 p->flags &= ~TERMP_NOSPACE;
1042
1043 /*
1044 * Groff likes to have some leading spaces before content. Well
1045 * that's fine by me.
1046 */
1047
1048 term_vspace(p);
1049 term_vspace(p);
1050 term_vspace(p);
1051 }