]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Allow -man to process EQN as well. Also fix a segfault in missing case
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.102 2011/02/06 21:44:36 kristaps 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 { pre_ign, 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 (n->body && n->body->child && MAN_TBL == n->body->child->type)
216 return;
217
218 if (NULL == n->prev)
219 return;
220
221 if (MAN_SS == n->prev->tok)
222 return;
223 if (MAN_SH == n->prev->tok)
224 return;
225
226 term_vspace(p);
227 }
228
229
230 /* ARGSUSED */
231 static int
232 pre_ign(DECL_ARGS)
233 {
234
235 return(0);
236 }
237
238
239 /* ARGSUSED */
240 static int
241 pre_I(DECL_ARGS)
242 {
243
244 term_fontrepl(p, TERMFONT_UNDER);
245 return(1);
246 }
247
248
249 /* ARGSUSED */
250 static int
251 pre_literal(DECL_ARGS)
252 {
253
254 term_newln(p);
255
256 if (MAN_nf == n->tok)
257 mt->fl |= MANT_LITERAL;
258 else
259 mt->fl &= ~MANT_LITERAL;
260
261 return(0);
262 }
263
264 /* ARGSUSED */
265 static int
266 pre_alternate(DECL_ARGS)
267 {
268 enum termfont font[2];
269 const struct man_node *nn;
270 int savelit, i;
271
272 switch (n->tok) {
273 case (MAN_RB):
274 font[0] = TERMFONT_NONE;
275 font[1] = TERMFONT_BOLD;
276 break;
277 case (MAN_RI):
278 font[0] = TERMFONT_NONE;
279 font[1] = TERMFONT_UNDER;
280 break;
281 case (MAN_BR):
282 font[0] = TERMFONT_BOLD;
283 font[1] = TERMFONT_NONE;
284 break;
285 case (MAN_BI):
286 font[0] = TERMFONT_BOLD;
287 font[1] = TERMFONT_UNDER;
288 break;
289 case (MAN_IR):
290 font[0] = TERMFONT_UNDER;
291 font[1] = TERMFONT_NONE;
292 break;
293 case (MAN_IB):
294 font[0] = TERMFONT_UNDER;
295 font[1] = TERMFONT_BOLD;
296 break;
297 default:
298 abort();
299 }
300
301 savelit = MANT_LITERAL & mt->fl;
302 mt->fl &= ~MANT_LITERAL;
303
304 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
305 term_fontrepl(p, font[i]);
306 if (savelit && NULL == nn->next)
307 mt->fl |= MANT_LITERAL;
308 print_man_node(p, mt, nn, m);
309 if (nn->next)
310 p->flags |= TERMP_NOSPACE;
311 }
312
313 return(0);
314 }
315
316 /* ARGSUSED */
317 static int
318 pre_B(DECL_ARGS)
319 {
320
321 term_fontrepl(p, TERMFONT_BOLD);
322 return(1);
323 }
324
325 /* ARGSUSED */
326 static int
327 pre_ft(DECL_ARGS)
328 {
329 const char *cp;
330
331 if (NULL == n->child) {
332 term_fontlast(p);
333 return(0);
334 }
335
336 cp = n->child->string;
337 switch (*cp) {
338 case ('4'):
339 /* FALLTHROUGH */
340 case ('3'):
341 /* FALLTHROUGH */
342 case ('B'):
343 term_fontrepl(p, TERMFONT_BOLD);
344 break;
345 case ('2'):
346 /* FALLTHROUGH */
347 case ('I'):
348 term_fontrepl(p, TERMFONT_UNDER);
349 break;
350 case ('P'):
351 term_fontlast(p);
352 break;
353 case ('1'):
354 /* FALLTHROUGH */
355 case ('C'):
356 /* FALLTHROUGH */
357 case ('R'):
358 term_fontrepl(p, TERMFONT_NONE);
359 break;
360 default:
361 break;
362 }
363 return(0);
364 }
365
366 /* ARGSUSED */
367 static int
368 pre_in(DECL_ARGS)
369 {
370 int len, less;
371 size_t v;
372 const char *cp;
373
374 term_newln(p);
375
376 if (NULL == n->child) {
377 p->offset = mt->offset;
378 return(0);
379 }
380
381 cp = n->child->string;
382 less = 0;
383
384 if ('-' == *cp)
385 less = -1;
386 else if ('+' == *cp)
387 less = 1;
388 else
389 cp--;
390
391 if ((len = a2width(p, ++cp)) < 0)
392 return(0);
393
394 v = (size_t)len;
395
396 if (less < 0)
397 p->offset -= p->offset > v ? v : p->offset;
398 else if (less > 0)
399 p->offset += v;
400 else
401 p->offset = v;
402
403 /* Don't let this creep beyond the right margin. */
404
405 if (p->offset > p->rmargin)
406 p->offset = p->rmargin;
407
408 return(0);
409 }
410
411
412 /* ARGSUSED */
413 static int
414 pre_sp(DECL_ARGS)
415 {
416 size_t i, len;
417
418 switch (n->tok) {
419 case (MAN_br):
420 len = 0;
421 break;
422 default:
423 len = n->child ? a2height(p, n->child->string) : 1;
424 break;
425 }
426
427 if (0 == len)
428 term_newln(p);
429 for (i = 0; i < len; i++)
430 term_vspace(p);
431
432 return(0);
433 }
434
435
436 /* ARGSUSED */
437 static int
438 pre_HP(DECL_ARGS)
439 {
440 size_t len;
441 int ival;
442 const struct man_node *nn;
443
444 switch (n->type) {
445 case (MAN_BLOCK):
446 print_bvspace(p, n);
447 return(1);
448 case (MAN_BODY):
449 p->flags |= TERMP_NOBREAK;
450 p->flags |= TERMP_TWOSPACE;
451 break;
452 default:
453 return(0);
454 }
455
456 len = mt->lmargin;
457 ival = -1;
458
459 /* Calculate offset. */
460
461 if (NULL != (nn = n->parent->head->child))
462 if ((ival = a2width(p, nn->string)) >= 0)
463 len = (size_t)ival;
464
465 if (0 == len)
466 len = term_len(p, 1);
467
468 p->offset = mt->offset;
469 p->rmargin = mt->offset + len;
470
471 if (ival >= 0)
472 mt->lmargin = (size_t)ival;
473
474 return(1);
475 }
476
477
478 /* ARGSUSED */
479 static void
480 post_HP(DECL_ARGS)
481 {
482
483 switch (n->type) {
484 case (MAN_BLOCK):
485 term_flushln(p);
486 break;
487 case (MAN_BODY):
488 term_flushln(p);
489 p->flags &= ~TERMP_NOBREAK;
490 p->flags &= ~TERMP_TWOSPACE;
491 p->offset = mt->offset;
492 p->rmargin = p->maxrmargin;
493 break;
494 default:
495 break;
496 }
497 }
498
499
500 /* ARGSUSED */
501 static int
502 pre_PP(DECL_ARGS)
503 {
504
505 switch (n->type) {
506 case (MAN_BLOCK):
507 mt->lmargin = term_len(p, INDENT);
508 print_bvspace(p, n);
509 break;
510 default:
511 p->offset = mt->offset;
512 break;
513 }
514
515 return(MAN_HEAD != n->type);
516 }
517
518
519 /* ARGSUSED */
520 static int
521 pre_IP(DECL_ARGS)
522 {
523 const struct man_node *nn;
524 size_t len;
525 int savelit, ival;
526
527 switch (n->type) {
528 case (MAN_BODY):
529 p->flags |= TERMP_NOLPAD;
530 p->flags |= TERMP_NOSPACE;
531 break;
532 case (MAN_HEAD):
533 p->flags |= TERMP_NOBREAK;
534 break;
535 case (MAN_BLOCK):
536 print_bvspace(p, n);
537 /* FALLTHROUGH */
538 default:
539 return(1);
540 }
541
542 len = mt->lmargin;
543 ival = -1;
544
545 /* Calculate the offset from the optional second argument. */
546 if (NULL != (nn = n->parent->head->child))
547 if (NULL != (nn = nn->next))
548 if ((ival = a2width(p, nn->string)) >= 0)
549 len = (size_t)ival;
550
551 switch (n->type) {
552 case (MAN_HEAD):
553 /* Handle zero-width lengths. */
554 if (0 == len)
555 len = term_len(p, 1);
556
557 p->offset = mt->offset;
558 p->rmargin = mt->offset + len;
559 if (ival < 0)
560 break;
561
562 /* Set the saved left-margin. */
563 mt->lmargin = (size_t)ival;
564
565 savelit = MANT_LITERAL & mt->fl;
566 mt->fl &= ~MANT_LITERAL;
567
568 if (n->child)
569 print_man_node(p, mt, n->child, m);
570
571 if (savelit)
572 mt->fl |= MANT_LITERAL;
573
574 return(0);
575 case (MAN_BODY):
576 p->offset = mt->offset + len;
577 p->rmargin = p->maxrmargin;
578 break;
579 default:
580 break;
581 }
582
583 return(1);
584 }
585
586
587 /* ARGSUSED */
588 static void
589 post_IP(DECL_ARGS)
590 {
591
592 switch (n->type) {
593 case (MAN_HEAD):
594 term_flushln(p);
595 p->flags &= ~TERMP_NOBREAK;
596 p->rmargin = p->maxrmargin;
597 break;
598 case (MAN_BODY):
599 term_newln(p);
600 p->flags &= ~TERMP_NOLPAD;
601 break;
602 default:
603 break;
604 }
605 }
606
607
608 /* ARGSUSED */
609 static int
610 pre_TP(DECL_ARGS)
611 {
612 const struct man_node *nn;
613 size_t len;
614 int savelit, ival;
615
616 switch (n->type) {
617 case (MAN_HEAD):
618 p->flags |= TERMP_NOBREAK;
619 break;
620 case (MAN_BODY):
621 p->flags |= TERMP_NOLPAD;
622 p->flags |= TERMP_NOSPACE;
623 break;
624 case (MAN_BLOCK):
625 print_bvspace(p, n);
626 /* FALLTHROUGH */
627 default:
628 return(1);
629 }
630
631 len = (size_t)mt->lmargin;
632 ival = -1;
633
634 /* Calculate offset. */
635
636 if (NULL != (nn = n->parent->head->child)) {
637 while (nn && MAN_TEXT != nn->type)
638 nn = nn->next;
639 if (nn && nn->next)
640 if ((ival = a2width(p, nn->string)) >= 0)
641 len = (size_t)ival;
642 }
643
644 switch (n->type) {
645 case (MAN_HEAD):
646 /* Handle zero-length properly. */
647 if (0 == len)
648 len = term_len(p, 1);
649
650 p->offset = mt->offset;
651 p->rmargin = mt->offset + len;
652
653 savelit = MANT_LITERAL & mt->fl;
654 mt->fl &= ~MANT_LITERAL;
655
656 /* Don't print same-line elements. */
657 for (nn = n->child; nn; nn = nn->next)
658 if (nn->line > n->line)
659 print_man_node(p, mt, nn, m);
660
661 if (savelit)
662 mt->fl |= MANT_LITERAL;
663
664 if (ival >= 0)
665 mt->lmargin = (size_t)ival;
666
667 return(0);
668 case (MAN_BODY):
669 p->offset = mt->offset + len;
670 p->rmargin = p->maxrmargin;
671 break;
672 default:
673 break;
674 }
675
676 return(1);
677 }
678
679
680 /* ARGSUSED */
681 static void
682 post_TP(DECL_ARGS)
683 {
684
685 switch (n->type) {
686 case (MAN_HEAD):
687 term_flushln(p);
688 p->flags &= ~TERMP_NOBREAK;
689 p->flags &= ~TERMP_TWOSPACE;
690 p->rmargin = p->maxrmargin;
691 break;
692 case (MAN_BODY):
693 term_newln(p);
694 p->flags &= ~TERMP_NOLPAD;
695 break;
696 default:
697 break;
698 }
699 }
700
701
702 /* ARGSUSED */
703 static int
704 pre_SS(DECL_ARGS)
705 {
706
707 switch (n->type) {
708 case (MAN_BLOCK):
709 mt->lmargin = term_len(p, INDENT);
710 mt->offset = term_len(p, INDENT);
711 /* If following a prior empty `SS', no vspace. */
712 if (n->prev && MAN_SS == n->prev->tok)
713 if (NULL == n->prev->body->child)
714 break;
715 if (NULL == n->prev)
716 break;
717 term_vspace(p);
718 break;
719 case (MAN_HEAD):
720 term_fontrepl(p, TERMFONT_BOLD);
721 p->offset = term_len(p, HALFINDENT);
722 break;
723 case (MAN_BODY):
724 p->offset = mt->offset;
725 break;
726 default:
727 break;
728 }
729
730 return(1);
731 }
732
733
734 /* ARGSUSED */
735 static void
736 post_SS(DECL_ARGS)
737 {
738
739 switch (n->type) {
740 case (MAN_HEAD):
741 term_newln(p);
742 break;
743 case (MAN_BODY):
744 term_newln(p);
745 break;
746 default:
747 break;
748 }
749 }
750
751
752 /* ARGSUSED */
753 static int
754 pre_SH(DECL_ARGS)
755 {
756
757 switch (n->type) {
758 case (MAN_BLOCK):
759 mt->lmargin = term_len(p, INDENT);
760 mt->offset = term_len(p, INDENT);
761 /* If following a prior empty `SH', no vspace. */
762 if (n->prev && MAN_SH == n->prev->tok)
763 if (NULL == n->prev->body->child)
764 break;
765 /* If the first macro, no vspae. */
766 if (NULL == n->prev)
767 break;
768 term_vspace(p);
769 break;
770 case (MAN_HEAD):
771 term_fontrepl(p, TERMFONT_BOLD);
772 p->offset = 0;
773 break;
774 case (MAN_BODY):
775 p->offset = mt->offset;
776 break;
777 default:
778 break;
779 }
780
781 return(1);
782 }
783
784
785 /* ARGSUSED */
786 static void
787 post_SH(DECL_ARGS)
788 {
789
790 switch (n->type) {
791 case (MAN_HEAD):
792 term_newln(p);
793 break;
794 case (MAN_BODY):
795 term_newln(p);
796 break;
797 default:
798 break;
799 }
800 }
801
802
803 /* ARGSUSED */
804 static int
805 pre_RS(DECL_ARGS)
806 {
807 const struct man_node *nn;
808 int ival;
809
810 switch (n->type) {
811 case (MAN_BLOCK):
812 term_newln(p);
813 return(1);
814 case (MAN_HEAD):
815 return(0);
816 default:
817 break;
818 }
819
820 if (NULL == (nn = n->parent->head->child)) {
821 mt->offset = mt->lmargin + term_len(p, INDENT);
822 p->offset = mt->offset;
823 return(1);
824 }
825
826 if ((ival = a2width(p, nn->string)) < 0)
827 return(1);
828
829 mt->offset = term_len(p, INDENT) + (size_t)ival;
830 p->offset = mt->offset;
831
832 return(1);
833 }
834
835
836 /* ARGSUSED */
837 static void
838 post_RS(DECL_ARGS)
839 {
840
841 switch (n->type) {
842 case (MAN_BLOCK):
843 mt->offset = mt->lmargin = term_len(p, INDENT);
844 break;
845 case (MAN_HEAD):
846 break;
847 default:
848 term_newln(p);
849 p->offset = term_len(p, INDENT);
850 break;
851 }
852 }
853
854
855 static void
856 print_man_node(DECL_ARGS)
857 {
858 size_t rm, rmax;
859 int c;
860
861 switch (n->type) {
862 case(MAN_TEXT):
863 /*
864 * If we have a blank line, output a vertical space.
865 * If we have a space as the first character, break
866 * before printing the line's data.
867 */
868 if ('\0' == *n->string) {
869 term_vspace(p);
870 return;
871 } else if (' ' == *n->string && MAN_LINE & n->flags)
872 term_newln(p);
873
874 term_word(p, n->string);
875
876 /*
877 * If we're in a literal context, make sure that words
878 * togehter on the same line stay together. This is a
879 * POST-printing call, so we check the NEXT word. Since
880 * -man doesn't have nested macros, we don't need to be
881 * more specific than this.
882 */
883 if (MANT_LITERAL & mt->fl &&
884 (NULL == n->next ||
885 n->next->line > n->line)) {
886 rm = p->rmargin;
887 rmax = p->maxrmargin;
888 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
889 p->flags |= TERMP_NOSPACE;
890 term_flushln(p);
891 p->flags &= ~TERMP_NOLPAD;
892 p->rmargin = rm;
893 p->maxrmargin = rmax;
894 }
895
896 if (MAN_EOS & n->flags)
897 p->flags |= TERMP_SENTENCE;
898 return;
899 case (MAN_EQN):
900 return;
901 case (MAN_TBL):
902 /*
903 * Tables are preceded by a newline. Then process a
904 * table line, which will cause line termination,
905 */
906 if (TBL_SPAN_FIRST & n->span->flags)
907 term_newln(p);
908 term_tbl(p, n->span);
909 return;
910 default:
911 break;
912 }
913
914 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
915 term_fontrepl(p, TERMFONT_NONE);
916
917 c = 1;
918 if (termacts[n->tok].pre)
919 c = (*termacts[n->tok].pre)(p, mt, n, m);
920
921 if (c && n->child)
922 print_man_nodelist(p, mt, n->child, m);
923
924 if (termacts[n->tok].post)
925 (*termacts[n->tok].post)(p, mt, n, m);
926 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
927 term_fontrepl(p, TERMFONT_NONE);
928
929 if (MAN_EOS & n->flags)
930 p->flags |= TERMP_SENTENCE;
931 }
932
933
934 static void
935 print_man_nodelist(DECL_ARGS)
936 {
937
938 print_man_node(p, mt, n, m);
939 if ( ! n->next)
940 return;
941 print_man_nodelist(p, mt, n->next, m);
942 }
943
944
945 static void
946 print_man_foot(struct termp *p, const void *arg)
947 {
948 char buf[DATESIZ];
949 const struct man_meta *meta;
950
951 meta = (const struct man_meta *)arg;
952
953 term_fontrepl(p, TERMFONT_NONE);
954
955 if (meta->rawdate)
956 strlcpy(buf, meta->rawdate, DATESIZ);
957 else
958 time2a(meta->date, buf, DATESIZ);
959
960 term_vspace(p);
961 term_vspace(p);
962 term_vspace(p);
963
964 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
965 p->rmargin = p->maxrmargin - term_strlen(p, buf);
966 p->offset = 0;
967
968 /* term_strlen() can return zero. */
969 if (p->rmargin == p->maxrmargin)
970 p->rmargin--;
971
972 if (meta->source)
973 term_word(p, meta->source);
974 if (meta->source)
975 term_word(p, "");
976 term_flushln(p);
977
978 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
979 p->offset = p->rmargin;
980 p->rmargin = p->maxrmargin;
981 p->flags &= ~TERMP_NOBREAK;
982
983 term_word(p, buf);
984 term_flushln(p);
985 }
986
987
988 static void
989 print_man_head(struct termp *p, const void *arg)
990 {
991 char buf[BUFSIZ], title[BUFSIZ];
992 size_t buflen, titlen;
993 const struct man_meta *m;
994
995 m = (const struct man_meta *)arg;
996
997 /*
998 * Note that old groff would spit out some spaces before the
999 * header. We discontinue this strange behaviour, but at one
1000 * point we did so here.
1001 */
1002
1003 p->rmargin = p->maxrmargin;
1004
1005 p->offset = 0;
1006 buf[0] = title[0] = '\0';
1007
1008 if (m->vol)
1009 strlcpy(buf, m->vol, BUFSIZ);
1010 buflen = term_strlen(p, buf);
1011
1012 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1013 titlen = term_strlen(p, title);
1014
1015 p->offset = 0;
1016 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1017 (p->maxrmargin -
1018 term_strlen(p, buf) + term_len(p, 1)) / 2 :
1019 p->maxrmargin - buflen;
1020 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1021
1022 term_word(p, title);
1023 term_flushln(p);
1024
1025 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1026 p->offset = p->rmargin;
1027 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1028 p->maxrmargin - titlen : p->maxrmargin;
1029
1030 term_word(p, buf);
1031 term_flushln(p);
1032
1033 p->flags &= ~TERMP_NOBREAK;
1034 if (p->rmargin + titlen <= p->maxrmargin) {
1035 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1036 p->offset = p->rmargin;
1037 p->rmargin = p->maxrmargin;
1038 term_word(p, title);
1039 term_flushln(p);
1040 }
1041
1042 p->rmargin = p->maxrmargin;
1043 p->offset = 0;
1044 p->flags &= ~TERMP_NOSPACE;
1045
1046 /*
1047 * Groff likes to have some leading spaces before content. Well
1048 * that's fine by me.
1049 */
1050
1051 term_vspace(p);
1052 term_vspace(p);
1053 term_vspace(p);
1054 }