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