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