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