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