]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
When processing a blank text line, do not break out of text processing
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.98 2011/01/16 20:12:45 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <sys/types.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "mandoc.h"
31 #include "out.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_alternate(DECL_ARGS);
85 static int pre_B(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_RS(DECL_ARGS);
91 static int pre_SH(DECL_ARGS);
92 static int pre_SS(DECL_ARGS);
93 static int pre_TP(DECL_ARGS);
94 static int pre_ign(DECL_ARGS);
95 static int pre_in(DECL_ARGS);
96 static int pre_literal(DECL_ARGS);
97 static int pre_sp(DECL_ARGS);
98 static int pre_ft(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_alternate, NULL, 0 }, /* BI */
121 { pre_alternate, NULL, 0 }, /* IB */
122 { pre_alternate, NULL, 0 }, /* BR */
123 { pre_alternate, NULL, 0 }, /* RB */
124 { NULL, NULL, 0 }, /* R */
125 { pre_B, NULL, 0 }, /* B */
126 { pre_I, NULL, 0 }, /* I */
127 { pre_alternate, NULL, 0 }, /* IR */
128 { pre_alternate, NULL, 0 }, /* RI */
129 { NULL, NULL, MAN_NOTEXT }, /* na */
130 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
131 { pre_literal, NULL, 0 }, /* nf */
132 { pre_literal, NULL, 0 }, /* fi */
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 { pre_ft, NULL, MAN_NOTEXT }, /* ft */
141 };
142
143
144
145 void
146 terminal_man(void *arg, const struct man *man)
147 {
148 struct termp *p;
149 const struct man_node *n;
150 const struct man_meta *m;
151 struct mtermp mt;
152
153 p = (struct termp *)arg;
154
155 p->overstep = 0;
156 p->maxrmargin = p->defrmargin;
157 p->tabwidth = term_len(p, 5);
158
159 if (NULL == p->symtab)
160 switch (p->enc) {
161 case (TERMENC_ASCII):
162 p->symtab = chars_init(CHARS_ASCII);
163 break;
164 default:
165 abort();
166 /* NOTREACHED */
167 }
168
169 n = man_node(man);
170 m = man_meta(man);
171
172 term_begin(p, print_man_head, print_man_foot, m);
173 p->flags |= TERMP_NOSPACE;
174
175 mt.fl = 0;
176 mt.lmargin = term_len(p, INDENT);
177 mt.offset = term_len(p, INDENT);
178
179 if (n->child)
180 print_man_nodelist(p, &mt, n->child, m);
181
182 term_end(p);
183 }
184
185
186 static size_t
187 a2height(const struct termp *p, const char *cp)
188 {
189 struct roffsu su;
190
191 if ( ! a2roffsu(cp, &su, SCALE_VS))
192 SCALE_VS_INIT(&su, term_strlen(p, cp));
193
194 return(term_vspan(p, &su));
195 }
196
197
198 static int
199 a2width(const struct termp *p, const char *cp)
200 {
201 struct roffsu su;
202
203 if ( ! a2roffsu(cp, &su, SCALE_BU))
204 return(-1);
205
206 return((int)term_hspan(p, &su));
207 }
208
209
210 static void
211 print_bvspace(struct termp *p, const struct man_node *n)
212 {
213 term_newln(p);
214
215 if (NULL == n->prev)
216 return;
217
218 if (MAN_SS == n->prev->tok)
219 return;
220 if (MAN_SH == n->prev->tok)
221 return;
222
223 term_vspace(p);
224 }
225
226
227 /* ARGSUSED */
228 static int
229 pre_ign(DECL_ARGS)
230 {
231
232 return(0);
233 }
234
235
236 /* ARGSUSED */
237 static int
238 pre_I(DECL_ARGS)
239 {
240
241 term_fontrepl(p, TERMFONT_UNDER);
242 return(1);
243 }
244
245
246 /* ARGSUSED */
247 static int
248 pre_literal(DECL_ARGS)
249 {
250
251 term_newln(p);
252
253 if (MAN_nf == n->tok)
254 mt->fl |= MANT_LITERAL;
255 else
256 mt->fl &= ~MANT_LITERAL;
257
258 return(1);
259 }
260
261 /* ARGSUSED */
262 static int
263 pre_alternate(DECL_ARGS)
264 {
265 enum termfont font[2];
266 const struct man_node *nn;
267 int savelit, i;
268
269 switch (n->tok) {
270 case (MAN_RB):
271 font[0] = TERMFONT_NONE;
272 font[1] = TERMFONT_BOLD;
273 break;
274 case (MAN_RI):
275 font[0] = TERMFONT_NONE;
276 font[1] = TERMFONT_UNDER;
277 break;
278 case (MAN_BR):
279 font[0] = TERMFONT_BOLD;
280 font[1] = TERMFONT_NONE;
281 break;
282 case (MAN_BI):
283 font[0] = TERMFONT_BOLD;
284 font[1] = TERMFONT_UNDER;
285 break;
286 case (MAN_IR):
287 font[0] = TERMFONT_UNDER;
288 font[1] = TERMFONT_NONE;
289 break;
290 case (MAN_IB):
291 font[0] = TERMFONT_UNDER;
292 font[1] = TERMFONT_BOLD;
293 break;
294 default:
295 abort();
296 }
297
298 savelit = MANT_LITERAL & mt->fl;
299 mt->fl &= ~MANT_LITERAL;
300
301 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
302 term_fontrepl(p, font[i]);
303 if (savelit && NULL == nn->next)
304 mt->fl |= MANT_LITERAL;
305 print_man_node(p, mt, nn, m);
306 if (nn->next)
307 p->flags |= TERMP_NOSPACE;
308 }
309
310 return(0);
311 }
312
313 /* ARGSUSED */
314 static int
315 pre_B(DECL_ARGS)
316 {
317
318 term_fontrepl(p, TERMFONT_BOLD);
319 return(1);
320 }
321
322 /* ARGSUSED */
323 static int
324 pre_ft(DECL_ARGS)
325 {
326 const char *cp;
327
328 if (NULL == n->child) {
329 term_fontlast(p);
330 return(0);
331 }
332
333 cp = n->child->string;
334 switch (*cp) {
335 case ('4'):
336 /* FALLTHROUGH */
337 case ('3'):
338 /* FALLTHROUGH */
339 case ('B'):
340 term_fontrepl(p, TERMFONT_BOLD);
341 break;
342 case ('2'):
343 /* FALLTHROUGH */
344 case ('I'):
345 term_fontrepl(p, TERMFONT_UNDER);
346 break;
347 case ('P'):
348 term_fontlast(p);
349 break;
350 case ('1'):
351 /* FALLTHROUGH */
352 case ('C'):
353 /* FALLTHROUGH */
354 case ('R'):
355 term_fontrepl(p, TERMFONT_NONE);
356 break;
357 default:
358 break;
359 }
360 return(0);
361 }
362
363 /* ARGSUSED */
364 static int
365 pre_in(DECL_ARGS)
366 {
367 int len, less;
368 size_t v;
369 const char *cp;
370
371 term_newln(p);
372
373 if (NULL == n->child) {
374 p->offset = mt->offset;
375 return(0);
376 }
377
378 cp = n->child->string;
379 less = 0;
380
381 if ('-' == *cp)
382 less = -1;
383 else if ('+' == *cp)
384 less = 1;
385 else
386 cp--;
387
388 if ((len = a2width(p, ++cp)) < 0)
389 return(0);
390
391 v = (size_t)len;
392
393 if (less < 0)
394 p->offset -= p->offset > v ? v : p->offset;
395 else if (less > 0)
396 p->offset += v;
397 else
398 p->offset = v;
399
400 /* Don't let this creep beyond the right margin. */
401
402 if (p->offset > p->rmargin)
403 p->offset = p->rmargin;
404
405 return(0);
406 }
407
408
409 /* ARGSUSED */
410 static int
411 pre_sp(DECL_ARGS)
412 {
413 size_t i, len;
414
415 switch (n->tok) {
416 case (MAN_br):
417 len = 0;
418 break;
419 default:
420 len = n->child ? a2height(p, n->child->string) : 1;
421 break;
422 }
423
424 if (0 == len)
425 term_newln(p);
426 for (i = 0; i < len; i++)
427 term_vspace(p);
428
429 return(0);
430 }
431
432
433 /* ARGSUSED */
434 static int
435 pre_HP(DECL_ARGS)
436 {
437 size_t len;
438 int ival;
439 const struct man_node *nn;
440
441 switch (n->type) {
442 case (MAN_BLOCK):
443 print_bvspace(p, n);
444 return(1);
445 case (MAN_BODY):
446 p->flags |= TERMP_NOBREAK;
447 p->flags |= TERMP_TWOSPACE;
448 break;
449 default:
450 return(0);
451 }
452
453 len = mt->lmargin;
454 ival = -1;
455
456 /* Calculate offset. */
457
458 if (NULL != (nn = n->parent->head->child))
459 if ((ival = a2width(p, nn->string)) >= 0)
460 len = (size_t)ival;
461
462 if (0 == len)
463 len = term_len(p, 1);
464
465 p->offset = mt->offset;
466 p->rmargin = mt->offset + len;
467
468 if (ival >= 0)
469 mt->lmargin = (size_t)ival;
470
471 return(1);
472 }
473
474
475 /* ARGSUSED */
476 static void
477 post_HP(DECL_ARGS)
478 {
479
480 switch (n->type) {
481 case (MAN_BLOCK):
482 term_flushln(p);
483 break;
484 case (MAN_BODY):
485 term_flushln(p);
486 p->flags &= ~TERMP_NOBREAK;
487 p->flags &= ~TERMP_TWOSPACE;
488 p->offset = mt->offset;
489 p->rmargin = p->maxrmargin;
490 break;
491 default:
492 break;
493 }
494 }
495
496
497 /* ARGSUSED */
498 static int
499 pre_PP(DECL_ARGS)
500 {
501
502 switch (n->type) {
503 case (MAN_BLOCK):
504 mt->lmargin = term_len(p, INDENT);
505 print_bvspace(p, n);
506 break;
507 default:
508 p->offset = mt->offset;
509 break;
510 }
511
512 return(MAN_HEAD != n->type);
513 }
514
515
516 /* ARGSUSED */
517 static int
518 pre_IP(DECL_ARGS)
519 {
520 const struct man_node *nn;
521 size_t len;
522 int savelit, ival;
523
524 switch (n->type) {
525 case (MAN_BODY):
526 p->flags |= TERMP_NOLPAD;
527 p->flags |= TERMP_NOSPACE;
528 break;
529 case (MAN_HEAD):
530 p->flags |= TERMP_NOBREAK;
531 break;
532 case (MAN_BLOCK):
533 print_bvspace(p, n);
534 /* FALLTHROUGH */
535 default:
536 return(1);
537 }
538
539 len = mt->lmargin;
540 ival = -1;
541
542 /* Calculate the offset from the optional second argument. */
543 if (NULL != (nn = n->parent->head->child))
544 if (NULL != (nn = nn->next))
545 if ((ival = a2width(p, nn->string)) >= 0)
546 len = (size_t)ival;
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 savelit = MANT_LITERAL & mt->fl;
563 mt->fl &= ~MANT_LITERAL;
564
565 if (n->child)
566 print_man_node(p, mt, n->child, m);
567
568 if (savelit)
569 mt->fl |= MANT_LITERAL;
570
571 return(0);
572 case (MAN_BODY):
573 p->offset = mt->offset + len;
574 p->rmargin = p->maxrmargin;
575 break;
576 default:
577 break;
578 }
579
580 return(1);
581 }
582
583
584 /* ARGSUSED */
585 static void
586 post_IP(DECL_ARGS)
587 {
588
589 switch (n->type) {
590 case (MAN_HEAD):
591 term_flushln(p);
592 p->flags &= ~TERMP_NOBREAK;
593 p->rmargin = p->maxrmargin;
594 break;
595 case (MAN_BODY):
596 term_newln(p);
597 p->flags &= ~TERMP_NOLPAD;
598 break;
599 default:
600 break;
601 }
602 }
603
604
605 /* ARGSUSED */
606 static int
607 pre_TP(DECL_ARGS)
608 {
609 const struct man_node *nn;
610 size_t len;
611 int savelit, ival;
612
613 switch (n->type) {
614 case (MAN_HEAD):
615 p->flags |= TERMP_NOBREAK;
616 break;
617 case (MAN_BODY):
618 p->flags |= TERMP_NOLPAD;
619 p->flags |= TERMP_NOSPACE;
620 break;
621 case (MAN_BLOCK):
622 print_bvspace(p, n);
623 /* FALLTHROUGH */
624 default:
625 return(1);
626 }
627
628 len = (size_t)mt->lmargin;
629 ival = -1;
630
631 /* Calculate offset. */
632
633 if (NULL != (nn = n->parent->head->child)) {
634 while (nn && MAN_TEXT != nn->type)
635 nn = nn->next;
636 if (nn && nn->next)
637 if ((ival = a2width(p, nn->string)) >= 0)
638 len = (size_t)ival;
639 }
640
641 switch (n->type) {
642 case (MAN_HEAD):
643 /* Handle zero-length properly. */
644 if (0 == len)
645 len = term_len(p, 1);
646
647 p->offset = mt->offset;
648 p->rmargin = mt->offset + len;
649
650 savelit = MANT_LITERAL & mt->fl;
651 mt->fl &= ~MANT_LITERAL;
652
653 /* Don't print same-line elements. */
654 for (nn = n->child; nn; nn = nn->next)
655 if (nn->line > n->line)
656 print_man_node(p, mt, nn, m);
657
658 if (savelit)
659 mt->fl |= MANT_LITERAL;
660
661 if (ival >= 0)
662 mt->lmargin = (size_t)ival;
663
664 return(0);
665 case (MAN_BODY):
666 p->offset = mt->offset + len;
667 p->rmargin = p->maxrmargin;
668 break;
669 default:
670 break;
671 }
672
673 return(1);
674 }
675
676
677 /* ARGSUSED */
678 static void
679 post_TP(DECL_ARGS)
680 {
681
682 switch (n->type) {
683 case (MAN_HEAD):
684 term_flushln(p);
685 p->flags &= ~TERMP_NOBREAK;
686 p->flags &= ~TERMP_TWOSPACE;
687 p->rmargin = p->maxrmargin;
688 break;
689 case (MAN_BODY):
690 term_newln(p);
691 p->flags &= ~TERMP_NOLPAD;
692 break;
693 default:
694 break;
695 }
696 }
697
698
699 /* ARGSUSED */
700 static int
701 pre_SS(DECL_ARGS)
702 {
703
704 switch (n->type) {
705 case (MAN_BLOCK):
706 mt->lmargin = term_len(p, INDENT);
707 mt->offset = term_len(p, INDENT);
708 /* If following a prior empty `SS', no vspace. */
709 if (n->prev && MAN_SS == n->prev->tok)
710 if (NULL == n->prev->body->child)
711 break;
712 if (NULL == n->prev)
713 break;
714 term_vspace(p);
715 break;
716 case (MAN_HEAD):
717 term_fontrepl(p, TERMFONT_BOLD);
718 p->offset = term_len(p, HALFINDENT);
719 break;
720 case (MAN_BODY):
721 p->offset = mt->offset;
722 break;
723 default:
724 break;
725 }
726
727 return(1);
728 }
729
730
731 /* ARGSUSED */
732 static void
733 post_SS(DECL_ARGS)
734 {
735
736 switch (n->type) {
737 case (MAN_HEAD):
738 term_newln(p);
739 break;
740 case (MAN_BODY):
741 term_newln(p);
742 break;
743 default:
744 break;
745 }
746 }
747
748
749 /* ARGSUSED */
750 static int
751 pre_SH(DECL_ARGS)
752 {
753
754 switch (n->type) {
755 case (MAN_BLOCK):
756 mt->lmargin = term_len(p, INDENT);
757 mt->offset = term_len(p, INDENT);
758 /* If following a prior empty `SH', no vspace. */
759 if (n->prev && MAN_SH == n->prev->tok)
760 if (NULL == n->prev->body->child)
761 break;
762 /* If the first macro, no vspae. */
763 if (NULL == n->prev)
764 break;
765 term_vspace(p);
766 break;
767 case (MAN_HEAD):
768 term_fontrepl(p, TERMFONT_BOLD);
769 p->offset = 0;
770 break;
771 case (MAN_BODY):
772 p->offset = mt->offset;
773 break;
774 default:
775 break;
776 }
777
778 return(1);
779 }
780
781
782 /* ARGSUSED */
783 static void
784 post_SH(DECL_ARGS)
785 {
786
787 switch (n->type) {
788 case (MAN_HEAD):
789 term_newln(p);
790 break;
791 case (MAN_BODY):
792 term_newln(p);
793 break;
794 default:
795 break;
796 }
797 }
798
799
800 /* ARGSUSED */
801 static int
802 pre_RS(DECL_ARGS)
803 {
804 const struct man_node *nn;
805 int ival;
806
807 switch (n->type) {
808 case (MAN_BLOCK):
809 term_newln(p);
810 return(1);
811 case (MAN_HEAD):
812 return(0);
813 default:
814 break;
815 }
816
817 if (NULL == (nn = n->parent->head->child)) {
818 mt->offset = mt->lmargin + term_len(p, INDENT);
819 p->offset = mt->offset;
820 return(1);
821 }
822
823 if ((ival = a2width(p, nn->string)) < 0)
824 return(1);
825
826 mt->offset = term_len(p, INDENT) + (size_t)ival;
827 p->offset = mt->offset;
828
829 return(1);
830 }
831
832
833 /* ARGSUSED */
834 static void
835 post_RS(DECL_ARGS)
836 {
837
838 switch (n->type) {
839 case (MAN_BLOCK):
840 mt->offset = mt->lmargin = term_len(p, INDENT);
841 break;
842 case (MAN_HEAD):
843 break;
844 default:
845 term_newln(p);
846 p->offset = term_len(p, INDENT);
847 break;
848 }
849 }
850
851
852 static void
853 print_man_node(DECL_ARGS)
854 {
855 size_t rm, rmax;
856 int c;
857
858 switch (n->type) {
859 case(MAN_TEXT):
860 /*
861 * If we have a blank line, output a vertical space.
862 * If we have a space as the first character, break
863 * before printing the line's data.
864 */
865 if ('\0' == *n->string) {
866 term_vspace(p);
867 return;
868 } else if (' ' == *n->string && MAN_LINE & n->flags)
869 term_newln(p);
870
871 term_word(p, n->string);
872
873 /*
874 * If we're in a literal context, make sure that words
875 * togehter on the same line stay together. This is a
876 * POST-printing call, so we check the NEXT word. Since
877 * -man doesn't have nested macros, we don't need to be
878 * more specific than this.
879 */
880 if (MANT_LITERAL & mt->fl &&
881 (NULL == n->next ||
882 n->next->line > n->line)) {
883 rm = p->rmargin;
884 rmax = p->maxrmargin;
885 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
886 p->flags |= TERMP_NOSPACE;
887 term_flushln(p);
888 p->flags &= ~TERMP_NOLPAD;
889 p->rmargin = rm;
890 p->maxrmargin = rmax;
891 }
892 return;
893 case (MAN_TBL):
894 /*
895 * Tables are preceded by a newline. Then process a
896 * table line, which will cause line termination,
897 */
898 if (TBL_SPAN_FIRST & n->span->flags)
899 term_newln(p);
900 term_tbl(p, n->span);
901 return;
902 default:
903 break;
904 }
905
906 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
907 term_fontrepl(p, TERMFONT_NONE);
908
909 c = 1;
910 if (termacts[n->tok].pre)
911 c = (*termacts[n->tok].pre)(p, mt, n, m);
912
913 if (c && n->child)
914 print_man_nodelist(p, mt, n->child, m);
915
916 if (termacts[n->tok].post)
917 (*termacts[n->tok].post)(p, mt, n, m);
918 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
919 term_fontrepl(p, TERMFONT_NONE);
920
921 if (MAN_EOS & n->flags)
922 p->flags |= TERMP_SENTENCE;
923 }
924
925
926 static void
927 print_man_nodelist(DECL_ARGS)
928 {
929
930 print_man_node(p, mt, n, m);
931 if ( ! n->next)
932 return;
933 print_man_nodelist(p, mt, n->next, m);
934 }
935
936
937 static void
938 print_man_foot(struct termp *p, const void *arg)
939 {
940 char buf[DATESIZ];
941 const struct man_meta *meta;
942
943 meta = (const struct man_meta *)arg;
944
945 term_fontrepl(p, TERMFONT_NONE);
946
947 if (meta->rawdate)
948 strlcpy(buf, meta->rawdate, DATESIZ);
949 else
950 time2a(meta->date, buf, DATESIZ);
951
952 term_vspace(p);
953 term_vspace(p);
954 term_vspace(p);
955
956 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
957 p->rmargin = p->maxrmargin - term_strlen(p, buf);
958 p->offset = 0;
959
960 /* term_strlen() can return zero. */
961 if (p->rmargin == p->maxrmargin)
962 p->rmargin--;
963
964 if (meta->source)
965 term_word(p, meta->source);
966 if (meta->source)
967 term_word(p, "");
968 term_flushln(p);
969
970 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
971 p->offset = p->rmargin;
972 p->rmargin = p->maxrmargin;
973 p->flags &= ~TERMP_NOBREAK;
974
975 term_word(p, buf);
976 term_flushln(p);
977 }
978
979
980 static void
981 print_man_head(struct termp *p, const void *arg)
982 {
983 char buf[BUFSIZ], title[BUFSIZ];
984 size_t buflen, titlen;
985 const struct man_meta *m;
986
987 m = (const struct man_meta *)arg;
988
989 /*
990 * Note that old groff would spit out some spaces before the
991 * header. We discontinue this strange behaviour, but at one
992 * point we did so here.
993 */
994
995 p->rmargin = p->maxrmargin;
996
997 p->offset = 0;
998 buf[0] = title[0] = '\0';
999
1000 if (m->vol)
1001 strlcpy(buf, m->vol, BUFSIZ);
1002 buflen = term_strlen(p, buf);
1003
1004 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1005 titlen = term_strlen(p, title);
1006
1007 p->offset = 0;
1008 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1009 (p->maxrmargin -
1010 term_strlen(p, buf) + term_len(p, 1)) / 2 :
1011 p->maxrmargin - buflen;
1012 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1013
1014 term_word(p, title);
1015 term_flushln(p);
1016
1017 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1018 p->offset = p->rmargin;
1019 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1020 p->maxrmargin - titlen : p->maxrmargin;
1021
1022 term_word(p, buf);
1023 term_flushln(p);
1024
1025 p->flags &= ~TERMP_NOBREAK;
1026 if (p->rmargin + titlen <= p->maxrmargin) {
1027 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1028 p->offset = p->rmargin;
1029 p->rmargin = p->maxrmargin;
1030 term_word(p, title);
1031 term_flushln(p);
1032 }
1033
1034 p->rmargin = p->maxrmargin;
1035 p->offset = 0;
1036 p->flags &= ~TERMP_NOSPACE;
1037
1038 /*
1039 * Groff likes to have some leading spaces before content. Well
1040 * that's fine by me.
1041 */
1042
1043 term_vspace(p);
1044 term_vspace(p);
1045 term_vspace(p);
1046 }