]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
3e10d34c1426030e714bb272d63a44863646806f
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.29 2009/08/22 09:10:38 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_ign(DECL_ARGS);
80 static int pre_nf(DECL_ARGS);
81 static int pre_r(DECL_ARGS);
82 static int pre_sp(DECL_ARGS);
83
84 static void post_B(DECL_ARGS);
85 static void post_I(DECL_ARGS);
86 static void post_IP(DECL_ARGS);
87 static void post_HP(DECL_ARGS);
88 static void post_RS(DECL_ARGS);
89 static void post_SH(DECL_ARGS);
90 static void post_SS(DECL_ARGS);
91 static void post_TP(DECL_ARGS);
92 static void post_i(DECL_ARGS);
93
94 static const struct termact termacts[MAN_MAX] = {
95 { pre_br, NULL }, /* br */
96 { NULL, NULL }, /* TH */
97 { pre_SH, post_SH }, /* SH */
98 { pre_SS, post_SS }, /* SS */
99 { pre_TP, post_TP }, /* TP */
100 { pre_PP, NULL }, /* LP */
101 { pre_PP, NULL }, /* PP */
102 { pre_PP, NULL }, /* P */
103 { pre_IP, post_IP }, /* IP */
104 { pre_HP, post_HP }, /* HP */
105 { NULL, NULL }, /* SM */
106 { pre_B, post_B }, /* SB */
107 { pre_BI, NULL }, /* BI */
108 { pre_IB, NULL }, /* IB */
109 { pre_BR, NULL }, /* BR */
110 { pre_RB, NULL }, /* RB */
111 { NULL, NULL }, /* R */
112 { pre_B, post_B }, /* B */
113 { pre_I, post_I }, /* I */
114 { pre_IR, NULL }, /* IR */
115 { pre_RI, NULL }, /* RI */
116 { NULL, NULL }, /* na */
117 { pre_I, post_i }, /* i */
118 { pre_sp, NULL }, /* sp */
119 { pre_nf, NULL }, /* nf */
120 { pre_fi, NULL }, /* fi */
121 { pre_r, NULL }, /* r */
122 { NULL, NULL }, /* RE */
123 { pre_RS, post_RS }, /* RS */
124 { pre_ign, NULL }, /* DT */
125 { pre_ign, NULL }, /* UC */
126 };
127
128 #ifdef __linux__
129 extern size_t strlcpy(char *, const char *, size_t);
130 extern size_t strlcat(char *, const char *, size_t);
131 #endif
132
133 static void print_head(struct termp *,
134 const struct man_meta *);
135 static void print_body(DECL_ARGS);
136 static void print_node(DECL_ARGS);
137 static void print_foot(struct termp *,
138 const struct man_meta *);
139 static void fmt_block_vspace(struct termp *,
140 const struct man_node *);
141 static int arg_width(const struct man_node *);
142
143
144 int
145 man_run(struct termp *p, const struct man *m)
146 {
147 struct mtermp mt;
148
149 print_head(p, man_meta(m));
150 p->flags |= TERMP_NOSPACE;
151 assert(man_node(m));
152 assert(MAN_ROOT == man_node(m)->type);
153
154 mt.fl = 0;
155 mt.lmargin = INDENT;
156 mt.offset = INDENT;
157
158 if (man_node(m)->child)
159 print_body(p, &mt, man_node(m)->child, man_meta(m));
160 print_foot(p, man_meta(m));
161
162 return(1);
163 }
164
165
166 static void
167 fmt_block_vspace(struct termp *p, const struct man_node *n)
168 {
169 term_newln(p);
170
171 if (NULL == n->prev)
172 return;
173
174 if (MAN_SS == n->prev->tok)
175 return;
176 if (MAN_SH == n->prev->tok)
177 return;
178
179 term_vspace(p);
180 }
181
182
183 static int
184 arg_width(const struct man_node *n)
185 {
186 int i, len;
187 const char *p;
188
189 assert(MAN_TEXT == n->type);
190 assert(n->string);
191
192 p = n->string;
193
194 if (0 == (len = (int)strlen(p)))
195 return(-1);
196
197 for (i = 0; i < len; i++)
198 if ( ! isdigit((u_char)p[i]))
199 break;
200
201 if (i == len - 1) {
202 if ('n' == p[len - 1] || 'm' == p[len - 1])
203 return(atoi(p));
204 } else if (i == len)
205 return(atoi(p));
206
207 return(-1);
208 }
209
210
211 /* ARGSUSED */
212 static int
213 pre_ign(DECL_ARGS)
214 {
215
216 return(0);
217 }
218
219
220 /* ARGSUSED */
221 static int
222 pre_I(DECL_ARGS)
223 {
224
225 p->flags |= TERMP_UNDER;
226 return(1);
227 }
228
229
230 /* ARGSUSED */
231 static int
232 pre_r(DECL_ARGS)
233 {
234
235 p->flags &= ~TERMP_UNDER;
236 p->flags &= ~TERMP_BOLD;
237 return(1);
238 }
239
240
241 /* ARGSUSED */
242 static void
243 post_i(DECL_ARGS)
244 {
245
246 if (n->nchild)
247 p->flags &= ~TERMP_UNDER;
248 }
249
250
251 /* ARGSUSED */
252 static void
253 post_I(DECL_ARGS)
254 {
255
256 p->flags &= ~TERMP_UNDER;
257 }
258
259
260 /* ARGSUSED */
261 static int
262 pre_fi(DECL_ARGS)
263 {
264
265 mt->fl &= ~MANT_LITERAL;
266 return(1);
267 }
268
269
270 /* ARGSUSED */
271 static int
272 pre_nf(DECL_ARGS)
273 {
274
275 term_newln(p);
276 mt->fl |= MANT_LITERAL;
277 return(1);
278 }
279
280
281 /* ARGSUSED */
282 static int
283 pre_IR(DECL_ARGS)
284 {
285 const struct man_node *nn;
286 int i;
287
288 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
289 if ( ! (i % 2))
290 p->flags |= TERMP_UNDER;
291 if (i > 0)
292 p->flags |= TERMP_NOSPACE;
293 print_node(p, mt, nn, m);
294 if ( ! (i % 2))
295 p->flags &= ~TERMP_UNDER;
296 }
297 return(0);
298 }
299
300
301 /* ARGSUSED */
302 static int
303 pre_IB(DECL_ARGS)
304 {
305 const struct man_node *nn;
306 int i;
307
308 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
309 p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
310 if (i > 0)
311 p->flags |= TERMP_NOSPACE;
312 print_node(p, mt, nn, m);
313 p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
314 }
315 return(0);
316 }
317
318
319 /* ARGSUSED */
320 static int
321 pre_RB(DECL_ARGS)
322 {
323 const struct man_node *nn;
324 int i;
325
326 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
327 if (i % 2)
328 p->flags |= TERMP_BOLD;
329 if (i > 0)
330 p->flags |= TERMP_NOSPACE;
331 print_node(p, mt, nn, m);
332 if (i % 2)
333 p->flags &= ~TERMP_BOLD;
334 }
335 return(0);
336 }
337
338
339 /* ARGSUSED */
340 static int
341 pre_RI(DECL_ARGS)
342 {
343 const struct man_node *nn;
344 int i;
345
346 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
347 if ( ! (i % 2))
348 p->flags |= TERMP_UNDER;
349 if (i > 0)
350 p->flags |= TERMP_NOSPACE;
351 print_node(p, mt, nn, m);
352 if ( ! (i % 2))
353 p->flags &= ~TERMP_UNDER;
354 }
355 return(0);
356 }
357
358
359 /* ARGSUSED */
360 static int
361 pre_BR(DECL_ARGS)
362 {
363 const struct man_node *nn;
364 int i;
365
366 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
367 if ( ! (i % 2))
368 p->flags |= TERMP_BOLD;
369 if (i > 0)
370 p->flags |= TERMP_NOSPACE;
371 print_node(p, mt, nn, m);
372 if ( ! (i % 2))
373 p->flags &= ~TERMP_BOLD;
374 }
375 return(0);
376 }
377
378
379 /* ARGSUSED */
380 static int
381 pre_BI(DECL_ARGS)
382 {
383 const struct man_node *nn;
384 int i;
385
386 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
387 p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
388 if (i > 0)
389 p->flags |= TERMP_NOSPACE;
390 print_node(p, mt, nn, m);
391 p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
392 }
393 return(0);
394 }
395
396
397 /* ARGSUSED */
398 static int
399 pre_B(DECL_ARGS)
400 {
401
402 p->flags |= TERMP_BOLD;
403 return(1);
404 }
405
406
407 /* ARGSUSED */
408 static void
409 post_B(DECL_ARGS)
410 {
411
412 p->flags &= ~TERMP_BOLD;
413 }
414
415
416 /* ARGSUSED */
417 static int
418 pre_sp(DECL_ARGS)
419 {
420 int i, len;
421
422 if (NULL == n->child) {
423 term_vspace(p);
424 return(0);
425 }
426
427 len = atoi(n->child->string);
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 fmt_block_vspace(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 = arg_width(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 fmt_block_vspace(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 fmt_block_vspace(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 = arg_width(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_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 fmt_block_vspace(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 = arg_width(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_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->flags |= TERMP_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->flags &= ~TERMP_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->flags |= TERMP_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->flags &= ~TERMP_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 = arg_width(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_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_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_body(DECL_ARGS)
902 {
903
904 print_node(p, mt, n, m);
905 if ( ! n->next)
906 return;
907 print_body(p, mt, n->next, m);
908 }
909
910
911 static void
912 print_foot(struct termp *p, const struct man_meta *meta)
913 {
914 struct tm *tm;
915 char *buf;
916
917 if (NULL == (buf = malloc(p->rmargin)))
918 err(1, "malloc");
919
920 tm = localtime(&meta->date);
921
922 if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
923 err(1, "strftime");
924
925 term_vspace(p);
926
927 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
928 p->rmargin = p->maxrmargin - strlen(buf);
929 p->offset = 0;
930
931 if (meta->source)
932 term_word(p, meta->source);
933 if (meta->source)
934 term_word(p, "");
935 term_flushln(p);
936
937 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
938 p->offset = p->rmargin;
939 p->rmargin = p->maxrmargin;
940 p->flags &= ~TERMP_NOBREAK;
941
942 term_word(p, buf);
943 term_flushln(p);
944
945 free(buf);
946 }
947
948
949 static void
950 print_head(struct termp *p, const struct man_meta *meta)
951 {
952 char *buf, *title;
953
954 p->rmargin = p->maxrmargin;
955 p->offset = 0;
956
957 if (NULL == (buf = malloc(p->rmargin)))
958 err(1, "malloc");
959 if (NULL == (title = malloc(p->rmargin)))
960 err(1, "malloc");
961
962 if (meta->vol)
963 (void)strlcpy(buf, meta->vol, p->rmargin);
964 else
965 *buf = 0;
966
967 (void)snprintf(title, p->rmargin, "%s(%d)",
968 meta->title, meta->msec);
969
970 p->offset = 0;
971 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
972 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
973
974 term_word(p, title);
975 term_flushln(p);
976
977 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
978 p->offset = p->rmargin;
979 p->rmargin = p->maxrmargin - strlen(title);
980
981 term_word(p, buf);
982 term_flushln(p);
983
984 p->offset = p->rmargin;
985 p->rmargin = p->maxrmargin;
986 p->flags &= ~TERMP_NOBREAK;
987 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
988
989 term_word(p, title);
990 term_flushln(p);
991
992 p->rmargin = p->maxrmargin;
993 p->offset = 0;
994 p->flags &= ~TERMP_NOSPACE;
995
996 free(title);
997 free(buf);
998 }
999