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