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