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