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