]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Using putchar for single-character prints (noted by Joerg Sonnenberger).
[mandoc.git] / 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 }