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