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