]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Have the standard manpage header and footer print on every page of -Tps
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.74 2010/06/09 08:07:13 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
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 man_node *);
74 static int a2height(const struct man_node *);
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_B(DECL_ARGS);
84 static int pre_BI(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_RB(DECL_ARGS);
90 static int pre_RI(DECL_ARGS);
91 static int pre_RS(DECL_ARGS);
92 static int pre_SH(DECL_ARGS);
93 static int pre_SS(DECL_ARGS);
94 static int pre_TP(DECL_ARGS);
95 static int pre_br(DECL_ARGS);
96 static int pre_fi(DECL_ARGS);
97 static int pre_ign(DECL_ARGS);
98 static int pre_nf(DECL_ARGS);
99 static int pre_sp(DECL_ARGS);
100
101 static void post_IP(DECL_ARGS);
102 static void post_HP(DECL_ARGS);
103 static void post_RS(DECL_ARGS);
104 static void post_SH(DECL_ARGS);
105 static void post_SS(DECL_ARGS);
106 static void post_TP(DECL_ARGS);
107
108 static const struct termact termacts[MAN_MAX] = {
109 { pre_br, NULL, MAN_NOTEXT }, /* br */
110 { NULL, NULL, 0 }, /* TH */
111 { pre_SH, post_SH, 0 }, /* SH */
112 { pre_SS, post_SS, 0 }, /* SS */
113 { pre_TP, post_TP, 0 }, /* TP */
114 { pre_PP, NULL, 0 }, /* LP */
115 { pre_PP, NULL, 0 }, /* PP */
116 { pre_PP, NULL, 0 }, /* P */
117 { pre_IP, post_IP, 0 }, /* IP */
118 { pre_HP, post_HP, 0 }, /* HP */
119 { NULL, NULL, 0 }, /* SM */
120 { pre_B, NULL, 0 }, /* SB */
121 { pre_BI, NULL, 0 }, /* BI */
122 { pre_BI, NULL, 0 }, /* IB */
123 { pre_RB, NULL, 0 }, /* BR */
124 { pre_RB, NULL, 0 }, /* RB */
125 { NULL, NULL, 0 }, /* R */
126 { pre_B, NULL, 0 }, /* B */
127 { pre_I, NULL, 0 }, /* I */
128 { pre_RI, NULL, 0 }, /* IR */
129 { pre_RI, NULL, 0 }, /* RI */
130 { NULL, NULL, MAN_NOTEXT }, /* na */
131 { pre_I, NULL, 0 }, /* i */
132 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
133 { pre_nf, NULL, 0 }, /* nf */
134 { pre_fi, NULL, 0 }, /* fi */
135 { NULL, NULL, 0 }, /* r */
136 { NULL, NULL, 0 }, /* RE */
137 { pre_RS, post_RS, 0 }, /* RS */
138 { pre_ign, NULL, 0 }, /* DT */
139 { pre_ign, NULL, 0 }, /* UC */
140 { pre_ign, NULL, 0 }, /* PD */
141 { pre_sp, NULL, MAN_NOTEXT }, /* Sp */
142 { pre_nf, NULL, 0 }, /* Vb */
143 { pre_fi, NULL, 0 }, /* Ve */
144 { pre_ign, NULL, 0 }, /* AT */
145 };
146
147
148
149 void
150 terminal_man(void *arg, const struct man *man)
151 {
152 struct termp *p;
153 const struct man_node *n;
154 const struct man_meta *m;
155 struct mtermp mt;
156
157 p = (struct termp *)arg;
158
159 p->overstep = 0;
160 p->maxrmargin = p->defrmargin;
161
162 term_begin(p, print_man_head, print_man_foot, man_meta(man));
163
164 if (NULL == p->symtab)
165 switch (p->enc) {
166 case (TERMENC_ASCII):
167 p->symtab = chars_init(CHARS_ASCII);
168 break;
169 default:
170 abort();
171 /* NOTREACHED */
172 }
173
174 n = man_node(man);
175 m = man_meta(man);
176
177 p->flags |= TERMP_NOSPACE;
178
179 mt.fl = 0;
180 mt.lmargin = INDENT;
181 mt.offset = INDENT;
182
183 if (n->child)
184 print_man_nodelist(p, &mt, n->child, m);
185
186 term_end(p);
187 }
188
189
190 static int
191 a2height(const struct man_node *n)
192 {
193 struct roffsu su;
194
195 assert(MAN_TEXT == n->type);
196 assert(n->string);
197 if ( ! a2roffsu(n->string, &su, SCALE_VS))
198 SCALE_VS_INIT(&su, strlen(n->string));
199
200 return((int)term_vspan(&su));
201 }
202
203
204 static int
205 a2width(const struct man_node *n)
206 {
207 struct roffsu su;
208
209 assert(MAN_TEXT == n->type);
210 assert(n->string);
211 if ( ! a2roffsu(n->string, &su, SCALE_BU))
212 return(-1);
213
214 return((int)term_hspan(&su));
215 }
216
217
218 static void
219 print_bvspace(struct termp *p, const struct man_node *n)
220 {
221 term_newln(p);
222
223 if (NULL == n->prev)
224 return;
225
226 if (MAN_SS == n->prev->tok)
227 return;
228 if (MAN_SH == n->prev->tok)
229 return;
230
231 term_vspace(p);
232 }
233
234
235 /* ARGSUSED */
236 static int
237 pre_ign(DECL_ARGS)
238 {
239
240 return(0);
241 }
242
243
244 /* ARGSUSED */
245 static int
246 pre_I(DECL_ARGS)
247 {
248
249 term_fontrepl(p, TERMFONT_UNDER);
250 return(1);
251 }
252
253
254 /* ARGSUSED */
255 static int
256 pre_fi(DECL_ARGS)
257 {
258
259 mt->fl &= ~MANT_LITERAL;
260 return(1);
261 }
262
263
264 /* ARGSUSED */
265 static int
266 pre_nf(DECL_ARGS)
267 {
268
269 mt->fl |= MANT_LITERAL;
270 return(MAN_Vb != n->tok);
271 }
272
273
274 /* ARGSUSED */
275 static int
276 pre_RB(DECL_ARGS)
277 {
278 const struct man_node *nn;
279 int i;
280
281 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
282 if (i % 2 && MAN_RB == n->tok)
283 term_fontrepl(p, TERMFONT_BOLD);
284 else if ( ! (i % 2) && MAN_RB != n->tok)
285 term_fontrepl(p, TERMFONT_BOLD);
286 else
287 term_fontrepl(p, TERMFONT_NONE);
288
289 if (i > 0)
290 p->flags |= TERMP_NOSPACE;
291
292 print_man_node(p, mt, nn, m);
293 }
294 return(0);
295 }
296
297
298 /* ARGSUSED */
299 static int
300 pre_RI(DECL_ARGS)
301 {
302 const struct man_node *nn;
303 int i;
304
305 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
306 if (i % 2 && MAN_RI == n->tok)
307 term_fontrepl(p, TERMFONT_UNDER);
308 else if ( ! (i % 2) && MAN_RI != n->tok)
309 term_fontrepl(p, TERMFONT_UNDER);
310 else
311 term_fontrepl(p, TERMFONT_NONE);
312
313 if (i > 0)
314 p->flags |= TERMP_NOSPACE;
315
316 print_man_node(p, mt, nn, m);
317 }
318 return(0);
319 }
320
321
322 /* ARGSUSED */
323 static int
324 pre_BI(DECL_ARGS)
325 {
326 const struct man_node *nn;
327 int i;
328
329 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
330 if (i % 2 && MAN_BI == n->tok)
331 term_fontrepl(p, TERMFONT_UNDER);
332 else if (i % 2)
333 term_fontrepl(p, TERMFONT_BOLD);
334 else if (MAN_BI == n->tok)
335 term_fontrepl(p, TERMFONT_BOLD);
336 else
337 term_fontrepl(p, TERMFONT_UNDER);
338
339 if (i)
340 p->flags |= TERMP_NOSPACE;
341
342 print_man_node(p, mt, nn, m);
343 }
344 return(0);
345 }
346
347
348 /* ARGSUSED */
349 static int
350 pre_B(DECL_ARGS)
351 {
352
353 term_fontrepl(p, TERMFONT_BOLD);
354 return(1);
355 }
356
357
358 /* ARGSUSED */
359 static int
360 pre_sp(DECL_ARGS)
361 {
362 int i, len;
363
364 len = n->child ? a2height(n->child) : 1;
365
366 if (0 == len)
367 term_newln(p);
368 for (i = 0; i <= len; i++)
369 term_vspace(p);
370
371 return(0);
372 }
373
374
375 /* ARGSUSED */
376 static int
377 pre_br(DECL_ARGS)
378 {
379
380 term_newln(p);
381 return(0);
382 }
383
384
385 /* ARGSUSED */
386 static int
387 pre_HP(DECL_ARGS)
388 {
389 size_t len;
390 int ival;
391 const struct man_node *nn;
392
393 switch (n->type) {
394 case (MAN_BLOCK):
395 print_bvspace(p, n);
396 return(1);
397 case (MAN_BODY):
398 p->flags |= TERMP_NOBREAK;
399 p->flags |= TERMP_TWOSPACE;
400 break;
401 default:
402 return(0);
403 }
404
405 len = mt->lmargin;
406 ival = -1;
407
408 /* Calculate offset. */
409
410 if (NULL != (nn = n->parent->head->child))
411 if ((ival = a2width(nn)) >= 0)
412 len = (size_t)ival;
413
414 if (0 == len)
415 len = 1;
416
417 p->offset = mt->offset;
418 p->rmargin = mt->offset + len;
419
420 if (ival >= 0)
421 mt->lmargin = (size_t)ival;
422
423 return(1);
424 }
425
426
427 /* ARGSUSED */
428 static void
429 post_HP(DECL_ARGS)
430 {
431
432 switch (n->type) {
433 case (MAN_BLOCK):
434 term_flushln(p);
435 break;
436 case (MAN_BODY):
437 term_flushln(p);
438 p->flags &= ~TERMP_NOBREAK;
439 p->flags &= ~TERMP_TWOSPACE;
440 p->offset = mt->offset;
441 p->rmargin = p->maxrmargin;
442 break;
443 default:
444 break;
445 }
446 }
447
448
449 /* ARGSUSED */
450 static int
451 pre_PP(DECL_ARGS)
452 {
453
454 switch (n->type) {
455 case (MAN_BLOCK):
456 mt->lmargin = INDENT;
457 print_bvspace(p, n);
458 break;
459 default:
460 p->offset = mt->offset;
461 break;
462 }
463
464 return(1);
465 }
466
467
468 /* ARGSUSED */
469 static int
470 pre_IP(DECL_ARGS)
471 {
472 const struct man_node *nn;
473 size_t len;
474 int ival;
475
476 switch (n->type) {
477 case (MAN_BODY):
478 p->flags |= TERMP_NOLPAD;
479 p->flags |= TERMP_NOSPACE;
480 break;
481 case (MAN_HEAD):
482 p->flags |= TERMP_NOBREAK;
483 break;
484 case (MAN_BLOCK):
485 print_bvspace(p, n);
486 /* FALLTHROUGH */
487 default:
488 return(1);
489 }
490
491 len = mt->lmargin;
492 ival = -1;
493
494 /* Calculate offset. */
495
496 if (NULL != (nn = n->parent->head->child))
497 if (NULL != (nn = nn->next)) {
498 for ( ; nn->next; nn = nn->next)
499 /* Do nothing. */ ;
500 if ((ival = a2width(nn)) >= 0)
501 len = (size_t)ival;
502 }
503
504 switch (n->type) {
505 case (MAN_HEAD):
506 /* Handle zero-width lengths. */
507 if (0 == len)
508 len = 1;
509
510 p->offset = mt->offset;
511 p->rmargin = mt->offset + len;
512 if (ival < 0)
513 break;
514
515 /* Set the saved left-margin. */
516 mt->lmargin = (size_t)ival;
517
518 /* Don't print the length value. */
519 for (nn = n->child; nn->next; nn = nn->next)
520 print_man_node(p, mt, nn, m);
521 return(0);
522 case (MAN_BODY):
523 p->offset = mt->offset + len;
524 p->rmargin = p->maxrmargin;
525 break;
526 default:
527 break;
528 }
529
530 return(1);
531 }
532
533
534 /* ARGSUSED */
535 static void
536 post_IP(DECL_ARGS)
537 {
538
539 switch (n->type) {
540 case (MAN_HEAD):
541 term_flushln(p);
542 p->flags &= ~TERMP_NOBREAK;
543 p->rmargin = p->maxrmargin;
544 break;
545 case (MAN_BODY):
546 term_flushln(p);
547 p->flags &= ~TERMP_NOLPAD;
548 break;
549 default:
550 break;
551 }
552 }
553
554
555 /* ARGSUSED */
556 static int
557 pre_TP(DECL_ARGS)
558 {
559 const struct man_node *nn;
560 size_t len;
561 int ival;
562
563 switch (n->type) {
564 case (MAN_HEAD):
565 p->flags |= TERMP_NOBREAK;
566 p->flags |= TERMP_TWOSPACE;
567 break;
568 case (MAN_BODY):
569 p->flags |= TERMP_NOLPAD;
570 p->flags |= TERMP_NOSPACE;
571 break;
572 case (MAN_BLOCK):
573 print_bvspace(p, n);
574 /* FALLTHROUGH */
575 default:
576 return(1);
577 }
578
579 len = (size_t)mt->lmargin;
580 ival = -1;
581
582 /* Calculate offset. */
583
584 if (NULL != (nn = n->parent->head->child)) {
585 while (nn && MAN_TEXT != nn->type)
586 nn = nn->next;
587 if (nn && nn->next)
588 if ((ival = a2width(nn)) >= 0)
589 len = (size_t)ival;
590 }
591
592 switch (n->type) {
593 case (MAN_HEAD):
594 /* Handle zero-length properly. */
595 if (0 == len)
596 len = 1;
597
598 p->offset = mt->offset;
599 p->rmargin = mt->offset + len;
600
601 /* Don't print same-line elements. */
602 for (nn = n->child; nn; nn = nn->next)
603 if (nn->line > n->line)
604 print_man_node(p, mt, nn, m);
605
606 if (ival >= 0)
607 mt->lmargin = (size_t)ival;
608
609 return(0);
610 case (MAN_BODY):
611 p->offset = mt->offset + len;
612 p->rmargin = p->maxrmargin;
613 break;
614 default:
615 break;
616 }
617
618 return(1);
619 }
620
621
622 /* ARGSUSED */
623 static void
624 post_TP(DECL_ARGS)
625 {
626
627 switch (n->type) {
628 case (MAN_HEAD):
629 term_flushln(p);
630 p->flags &= ~TERMP_NOBREAK;
631 p->flags &= ~TERMP_TWOSPACE;
632 p->rmargin = p->maxrmargin;
633 break;
634 case (MAN_BODY):
635 term_flushln(p);
636 p->flags &= ~TERMP_NOLPAD;
637 break;
638 default:
639 break;
640 }
641 }
642
643
644 /* ARGSUSED */
645 static int
646 pre_SS(DECL_ARGS)
647 {
648
649 switch (n->type) {
650 case (MAN_BLOCK):
651 mt->lmargin = INDENT;
652 mt->offset = INDENT;
653 /* If following a prior empty `SS', no vspace. */
654 if (n->prev && MAN_SS == n->prev->tok)
655 if (NULL == n->prev->body->child)
656 break;
657 if (NULL == n->prev)
658 break;
659 term_vspace(p);
660 break;
661 case (MAN_HEAD):
662 term_fontrepl(p, TERMFONT_BOLD);
663 p->offset = HALFINDENT;
664 break;
665 case (MAN_BODY):
666 p->offset = mt->offset;
667 break;
668 default:
669 break;
670 }
671
672 return(1);
673 }
674
675
676 /* ARGSUSED */
677 static void
678 post_SS(DECL_ARGS)
679 {
680
681 switch (n->type) {
682 case (MAN_HEAD):
683 term_newln(p);
684 break;
685 case (MAN_BODY):
686 term_newln(p);
687 break;
688 default:
689 break;
690 }
691 }
692
693
694 /* ARGSUSED */
695 static int
696 pre_SH(DECL_ARGS)
697 {
698
699 switch (n->type) {
700 case (MAN_BLOCK):
701 mt->lmargin = INDENT;
702 mt->offset = INDENT;
703 /* If following a prior empty `SH', no vspace. */
704 if (n->prev && MAN_SH == n->prev->tok)
705 if (NULL == n->prev->body->child)
706 break;
707 /* If the first macro, no vspae. */
708 if (NULL == n->prev)
709 break;
710 term_vspace(p);
711 break;
712 case (MAN_HEAD):
713 term_fontrepl(p, TERMFONT_BOLD);
714 p->offset = 0;
715 break;
716 case (MAN_BODY):
717 p->offset = mt->offset;
718 break;
719 default:
720 break;
721 }
722
723 return(1);
724 }
725
726
727 /* ARGSUSED */
728 static void
729 post_SH(DECL_ARGS)
730 {
731
732 switch (n->type) {
733 case (MAN_HEAD):
734 term_newln(p);
735 break;
736 case (MAN_BODY):
737 term_newln(p);
738 break;
739 default:
740 break;
741 }
742 }
743
744
745 /* ARGSUSED */
746 static int
747 pre_RS(DECL_ARGS)
748 {
749 const struct man_node *nn;
750 int ival;
751
752 switch (n->type) {
753 case (MAN_BLOCK):
754 term_newln(p);
755 return(1);
756 case (MAN_HEAD):
757 return(0);
758 default:
759 break;
760 }
761
762 if (NULL == (nn = n->parent->head->child)) {
763 mt->offset = mt->lmargin + INDENT;
764 p->offset = mt->offset;
765 return(1);
766 }
767
768 if ((ival = a2width(nn)) < 0)
769 return(1);
770
771 mt->offset = INDENT + (size_t)ival;
772 p->offset = mt->offset;
773
774 return(1);
775 }
776
777
778 /* ARGSUSED */
779 static void
780 post_RS(DECL_ARGS)
781 {
782
783 switch (n->type) {
784 case (MAN_BLOCK):
785 mt->offset = mt->lmargin = INDENT;
786 break;
787 case (MAN_HEAD):
788 break;
789 default:
790 term_newln(p);
791 p->offset = INDENT;
792 break;
793 }
794 }
795
796
797 static void
798 print_man_node(DECL_ARGS)
799 {
800 size_t rm, rmax;
801 int c;
802
803 c = 1;
804
805 switch (n->type) {
806 case(MAN_TEXT):
807 if (0 == *n->string) {
808 term_vspace(p);
809 break;
810 }
811
812 term_word(p, n->string);
813
814 /* FIXME: this means that macro lines are munged! */
815
816 if (MANT_LITERAL & mt->fl) {
817 rm = p->rmargin;
818 rmax = p->maxrmargin;
819 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
820 p->flags |= TERMP_NOSPACE;
821 term_flushln(p);
822 p->rmargin = rm;
823 p->maxrmargin = rmax;
824 }
825 break;
826 default:
827 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
828 term_fontrepl(p, TERMFONT_NONE);
829 if (termacts[n->tok].pre)
830 c = (*termacts[n->tok].pre)(p, mt, n, m);
831 break;
832 }
833
834 if (c && n->child)
835 print_man_nodelist(p, mt, n->child, m);
836
837 if (MAN_TEXT != n->type) {
838 if (termacts[n->tok].post)
839 (*termacts[n->tok].post)(p, mt, n, m);
840 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
841 term_fontrepl(p, TERMFONT_NONE);
842 }
843
844 if (MAN_EOS & n->flags)
845 p->flags |= TERMP_SENTENCE;
846 }
847
848
849 static void
850 print_man_nodelist(DECL_ARGS)
851 {
852
853 print_man_node(p, mt, n, m);
854 if ( ! n->next)
855 return;
856 print_man_nodelist(p, mt, n->next, m);
857 }
858
859
860 static void
861 print_man_foot(struct termp *p, const void *arg)
862 {
863 char buf[DATESIZ];
864 const struct man_meta *meta;
865
866 meta = (const struct man_meta *)arg;
867
868 term_fontrepl(p, TERMFONT_NONE);
869
870 if (meta->rawdate)
871 strlcpy(buf, meta->rawdate, DATESIZ);
872 else
873 time2a(meta->date, buf, DATESIZ);
874
875 term_vspace(p);
876 term_vspace(p);
877 term_vspace(p);
878
879 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
880 p->rmargin = p->maxrmargin - strlen(buf);
881 p->offset = 0;
882
883 if (meta->source)
884 term_word(p, meta->source);
885 if (meta->source)
886 term_word(p, "");
887 term_flushln(p);
888
889 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
890 p->offset = p->rmargin;
891 p->rmargin = p->maxrmargin;
892 p->flags &= ~TERMP_NOBREAK;
893
894 term_word(p, buf);
895 term_flushln(p);
896 }
897
898
899 static void
900 print_man_head(struct termp *p, const void *arg)
901 {
902 char buf[BUFSIZ], title[BUFSIZ];
903 size_t buflen, titlen;
904 const struct man_meta *m;
905
906 m = (const struct man_meta *)arg;
907
908 /*
909 * Note that old groff would spit out some spaces before the
910 * header. We discontinue this strange behaviour, but at one
911 * point we did so here.
912 */
913
914 p->rmargin = p->maxrmargin;
915
916 p->offset = 0;
917 buf[0] = title[0] = '\0';
918
919 if (m->vol)
920 strlcpy(buf, m->vol, BUFSIZ);
921 buflen = strlen(buf);
922
923 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
924 titlen = strlen(title);
925
926 p->offset = 0;
927 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
928 (p->maxrmargin - strlen(buf) + 1) / 2 :
929 p->maxrmargin - buflen;
930 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
931
932 term_word(p, title);
933 term_flushln(p);
934
935 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
936 p->offset = p->rmargin;
937 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
938 p->maxrmargin - titlen : p->maxrmargin;
939
940 term_word(p, buf);
941 term_flushln(p);
942
943 p->flags &= ~TERMP_NOBREAK;
944 if (p->rmargin + titlen <= p->maxrmargin) {
945 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
946 p->offset = p->rmargin;
947 p->rmargin = p->maxrmargin;
948 term_word(p, title);
949 term_flushln(p);
950 }
951
952 p->rmargin = p->maxrmargin;
953 p->offset = 0;
954 p->flags &= ~TERMP_NOSPACE;
955
956 /*
957 * Groff likes to have some leading spaces before content. Well
958 * that's fine by me.
959 */
960
961 term_vspace(p);
962 term_vspace(p);
963 term_vspace(p);
964 }