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