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