mandoc(1): Update to 1.9.15.
[dragonfly.git] / usr.bin / mandoc / man_term.c
blob4da50c5a593f4d51043645d1167e36c5ff5fc4bf
1 /* $Id: man_term.c,v 1.55 2010/01/01 17:14:28 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
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.
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.
17 #include <sys/types.h>
19 #include <assert.h>
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
25 #include "out.h"
26 #include "man.h"
27 #include "term.h"
28 #include "chars.h"
29 #include "main.h"
31 #define INDENT 7
32 #define HALFINDENT 3
34 /* FIXME: have PD set the default vspace width. */
36 struct mtermp {
37 int fl;
38 #define MANT_LITERAL (1 << 0)
40 * Default amount to indent the left margin after leading text
41 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
42 * indent). This needs to be saved because `HP' and so on, if
43 * not having a specified value, must default.
45 * Note that this is the indentation AFTER the left offset, so
46 * the total offset is usually offset + lmargin.
48 size_t lmargin;
50 * The default offset, i.e., the amount between any text and the
51 * page boundary.
53 size_t offset;
56 #define DECL_ARGS struct termp *p, \
57 struct mtermp *mt, \
58 const struct man_node *n, \
59 const struct man_meta *m
61 struct termact {
62 int (*pre)(DECL_ARGS);
63 void (*post)(DECL_ARGS);
66 static int a2width(const struct man_node *);
67 static int a2height(const struct man_node *);
69 static void print_man_head(struct termp *,
70 const struct man_meta *);
71 static void print_man_nodelist(DECL_ARGS);
72 static void print_man_node(DECL_ARGS);
73 static void print_man_foot(struct termp *,
74 const struct man_meta *);
75 static void print_bvspace(struct termp *,
76 const struct man_node *);
78 static int pre_B(DECL_ARGS);
79 static int pre_BI(DECL_ARGS);
80 static int pre_HP(DECL_ARGS);
81 static int pre_I(DECL_ARGS);
82 static int pre_IP(DECL_ARGS);
83 static int pre_PP(DECL_ARGS);
84 static int pre_RB(DECL_ARGS);
85 static int pre_RI(DECL_ARGS);
86 static int pre_RS(DECL_ARGS);
87 static int pre_SH(DECL_ARGS);
88 static int pre_SS(DECL_ARGS);
89 static int pre_TP(DECL_ARGS);
90 static int pre_br(DECL_ARGS);
91 static int pre_fi(DECL_ARGS);
92 static int pre_ign(DECL_ARGS);
93 static int pre_nf(DECL_ARGS);
94 static int pre_sp(DECL_ARGS);
96 static void post_IP(DECL_ARGS);
97 static void post_HP(DECL_ARGS);
98 static void post_RS(DECL_ARGS);
99 static void post_SH(DECL_ARGS);
100 static void post_SS(DECL_ARGS);
101 static void post_TP(DECL_ARGS);
103 static const struct termact termacts[MAN_MAX] = {
104 { pre_br, NULL }, /* br */
105 { NULL, NULL }, /* TH */
106 { pre_SH, post_SH }, /* SH */
107 { pre_SS, post_SS }, /* SS */
108 { pre_TP, post_TP }, /* TP */
109 { pre_PP, NULL }, /* LP */
110 { pre_PP, NULL }, /* PP */
111 { pre_PP, NULL }, /* P */
112 { pre_IP, post_IP }, /* IP */
113 { pre_HP, post_HP }, /* HP */
114 { NULL, NULL }, /* SM */
115 { pre_B, NULL }, /* SB */
116 { pre_BI, NULL }, /* BI */
117 { pre_BI, NULL }, /* IB */
118 { pre_RB, NULL }, /* BR */
119 { pre_RB, NULL }, /* RB */
120 { NULL, NULL }, /* R */
121 { pre_B, NULL }, /* B */
122 { pre_I, NULL }, /* I */
123 { pre_RI, NULL }, /* IR */
124 { pre_RI, NULL }, /* RI */
125 { NULL, NULL }, /* na */
126 { pre_I, NULL }, /* i */
127 { pre_sp, NULL }, /* sp */
128 { pre_nf, NULL }, /* nf */
129 { pre_fi, NULL }, /* fi */
130 { NULL, NULL }, /* r */
131 { NULL, NULL }, /* RE */
132 { pre_RS, post_RS }, /* RS */
133 { pre_ign, NULL }, /* DT */
134 { pre_ign, NULL }, /* UC */
135 { pre_ign, NULL }, /* PD */
140 void
141 terminal_man(void *arg, const struct man *man)
143 struct termp *p;
144 const struct man_node *n;
145 const struct man_meta *m;
146 struct mtermp mt;
148 p = (struct termp *)arg;
150 if (NULL == p->symtab)
151 switch (p->enc) {
152 case (TERMENC_ASCII):
153 p->symtab = chars_init(CHARS_ASCII);
154 break;
155 default:
156 abort();
157 /* NOTREACHED */
160 n = man_node(man);
161 m = man_meta(man);
163 print_man_head(p, m);
164 p->flags |= TERMP_NOSPACE;
166 mt.fl = 0;
167 mt.lmargin = INDENT;
168 mt.offset = INDENT;
170 if (n->child)
171 print_man_nodelist(p, &mt, n->child, m);
172 print_man_foot(p, m);
176 static int
177 a2height(const struct man_node *n)
179 struct roffsu su;
181 assert(MAN_TEXT == n->type);
182 assert(n->string);
183 if ( ! a2roffsu(n->string, &su, SCALE_VS))
184 SCALE_VS_INIT(&su, strlen(n->string));
186 return((int)term_vspan(&su));
190 static int
191 a2width(const struct man_node *n)
193 struct roffsu su;
195 assert(MAN_TEXT == n->type);
196 assert(n->string);
197 if ( ! a2roffsu(n->string, &su, SCALE_BU))
198 return(-1);
200 return((int)term_hspan(&su));
204 static void
205 print_bvspace(struct termp *p, const struct man_node *n)
207 term_newln(p);
209 if (NULL == n->prev)
210 return;
212 if (MAN_SS == n->prev->tok)
213 return;
214 if (MAN_SH == n->prev->tok)
215 return;
217 term_vspace(p);
221 /* ARGSUSED */
222 static int
223 pre_ign(DECL_ARGS)
226 return(0);
230 /* ARGSUSED */
231 static int
232 pre_I(DECL_ARGS)
235 term_fontrepl(p, TERMFONT_UNDER);
236 return(1);
240 /* ARGSUSED */
241 static int
242 pre_fi(DECL_ARGS)
245 mt->fl &= ~MANT_LITERAL;
246 return(1);
250 /* ARGSUSED */
251 static int
252 pre_nf(DECL_ARGS)
255 term_newln(p);
256 mt->fl |= MANT_LITERAL;
257 return(1);
261 /* ARGSUSED */
262 static int
263 pre_RB(DECL_ARGS)
265 const struct man_node *nn;
266 int i;
268 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
269 if (i % 2 && MAN_RB == n->tok)
270 term_fontrepl(p, TERMFONT_BOLD);
271 else if ( ! (i % 2) && MAN_RB != n->tok)
272 term_fontrepl(p, TERMFONT_BOLD);
273 else
274 term_fontrepl(p, TERMFONT_NONE);
276 if (i > 0)
277 p->flags |= TERMP_NOSPACE;
279 print_man_node(p, mt, nn, m);
281 return(0);
285 /* ARGSUSED */
286 static int
287 pre_RI(DECL_ARGS)
289 const struct man_node *nn;
290 int i;
292 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
293 if (i % 2 && MAN_RI == n->tok)
294 term_fontrepl(p, TERMFONT_UNDER);
295 else if ( ! (i % 2) && MAN_RI != n->tok)
296 term_fontrepl(p, TERMFONT_UNDER);
297 else
298 term_fontrepl(p, TERMFONT_NONE);
300 if (i > 0)
301 p->flags |= TERMP_NOSPACE;
303 print_man_node(p, mt, nn, m);
305 return(0);
309 /* ARGSUSED */
310 static int
311 pre_BI(DECL_ARGS)
313 const struct man_node *nn;
314 int i;
316 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
317 if (i % 2 && MAN_BI == n->tok)
318 term_fontrepl(p, TERMFONT_UNDER);
319 else if (i % 2)
320 term_fontrepl(p, TERMFONT_BOLD);
321 else if (MAN_BI == n->tok)
322 term_fontrepl(p, TERMFONT_BOLD);
323 else
324 term_fontrepl(p, TERMFONT_UNDER);
326 if (i)
327 p->flags |= TERMP_NOSPACE;
329 print_man_node(p, mt, nn, m);
331 return(0);
335 /* ARGSUSED */
336 static int
337 pre_B(DECL_ARGS)
340 term_fontrepl(p, TERMFONT_BOLD);
341 return(1);
345 /* ARGSUSED */
346 static int
347 pre_sp(DECL_ARGS)
349 int i, len;
351 len = n->child ? a2height(n->child) : 1;
353 if (0 == len)
354 term_newln(p);
355 for (i = 0; i < len; i++)
356 term_vspace(p);
358 return(0);
362 /* ARGSUSED */
363 static int
364 pre_br(DECL_ARGS)
367 term_newln(p);
368 return(0);
372 /* ARGSUSED */
373 static int
374 pre_HP(DECL_ARGS)
376 size_t len;
377 int ival;
378 const struct man_node *nn;
380 switch (n->type) {
381 case (MAN_BLOCK):
382 print_bvspace(p, n);
383 return(1);
384 case (MAN_BODY):
385 p->flags |= TERMP_NOBREAK;
386 p->flags |= TERMP_TWOSPACE;
387 break;
388 default:
389 return(0);
392 len = mt->lmargin;
393 ival = -1;
395 /* Calculate offset. */
397 if (NULL != (nn = n->parent->head->child))
398 if ((ival = a2width(nn)) >= 0)
399 len = (size_t)ival;
401 if (0 == len)
402 len = 1;
404 p->offset = mt->offset;
405 p->rmargin = mt->offset + len;
407 if (ival >= 0)
408 mt->lmargin = (size_t)ival;
410 return(1);
414 /* ARGSUSED */
415 static void
416 post_HP(DECL_ARGS)
419 switch (n->type) {
420 case (MAN_BLOCK):
421 term_flushln(p);
422 break;
423 case (MAN_BODY):
424 term_flushln(p);
425 p->flags &= ~TERMP_NOBREAK;
426 p->flags &= ~TERMP_TWOSPACE;
427 p->offset = mt->offset;
428 p->rmargin = p->maxrmargin;
429 break;
430 default:
431 break;
436 /* ARGSUSED */
437 static int
438 pre_PP(DECL_ARGS)
441 switch (n->type) {
442 case (MAN_BLOCK):
443 mt->lmargin = INDENT;
444 print_bvspace(p, n);
445 break;
446 default:
447 p->offset = mt->offset;
448 break;
451 return(1);
455 /* ARGSUSED */
456 static int
457 pre_IP(DECL_ARGS)
459 const struct man_node *nn;
460 size_t len;
461 int ival;
463 switch (n->type) {
464 case (MAN_BODY):
465 p->flags |= TERMP_NOLPAD;
466 p->flags |= TERMP_NOSPACE;
467 break;
468 case (MAN_HEAD):
469 p->flags |= TERMP_NOBREAK;
470 p->flags |= TERMP_TWOSPACE;
471 break;
472 case (MAN_BLOCK):
473 print_bvspace(p, n);
474 /* FALLTHROUGH */
475 default:
476 return(1);
479 len = mt->lmargin;
480 ival = -1;
482 /* Calculate offset. */
484 if (NULL != (nn = n->parent->head->child))
485 if (NULL != (nn = nn->next)) {
486 for ( ; nn->next; nn = nn->next)
487 /* Do nothing. */ ;
488 if ((ival = a2width(nn)) >= 0)
489 len = (size_t)ival;
492 switch (n->type) {
493 case (MAN_HEAD):
494 /* Handle zero-width lengths. */
495 if (0 == len)
496 len = 1;
498 p->offset = mt->offset;
499 p->rmargin = mt->offset + len;
500 if (ival < 0)
501 break;
503 /* Set the saved left-margin. */
504 mt->lmargin = (size_t)ival;
506 /* Don't print the length value. */
507 for (nn = n->child; nn->next; nn = nn->next)
508 print_man_node(p, mt, nn, m);
509 return(0);
510 case (MAN_BODY):
511 p->offset = mt->offset + len;
512 p->rmargin = p->maxrmargin;
513 break;
514 default:
515 break;
518 return(1);
522 /* ARGSUSED */
523 static void
524 post_IP(DECL_ARGS)
527 switch (n->type) {
528 case (MAN_HEAD):
529 term_flushln(p);
530 p->flags &= ~TERMP_NOBREAK;
531 p->flags &= ~TERMP_TWOSPACE;
532 p->rmargin = p->maxrmargin;
533 break;
534 case (MAN_BODY):
535 term_flushln(p);
536 p->flags &= ~TERMP_NOLPAD;
537 break;
538 default:
539 break;
544 /* ARGSUSED */
545 static int
546 pre_TP(DECL_ARGS)
548 const struct man_node *nn;
549 size_t len;
550 int ival;
552 switch (n->type) {
553 case (MAN_HEAD):
554 p->flags |= TERMP_NOBREAK;
555 p->flags |= TERMP_TWOSPACE;
556 break;
557 case (MAN_BODY):
558 p->flags |= TERMP_NOLPAD;
559 p->flags |= TERMP_NOSPACE;
560 break;
561 case (MAN_BLOCK):
562 print_bvspace(p, n);
563 /* FALLTHROUGH */
564 default:
565 return(1);
568 len = (size_t)mt->lmargin;
569 ival = -1;
571 /* Calculate offset. */
573 if (NULL != (nn = n->parent->head->child))
574 if (NULL != nn->next)
575 if ((ival = a2width(nn)) >= 0)
576 len = (size_t)ival;
578 switch (n->type) {
579 case (MAN_HEAD):
580 /* Handle zero-length properly. */
581 if (0 == len)
582 len = 1;
584 p->offset = mt->offset;
585 p->rmargin = mt->offset + len;
587 /* Don't print same-line elements. */
588 for (nn = n->child; nn; nn = nn->next)
589 if (nn->line > n->line)
590 print_man_node(p, mt, nn, m);
592 if (ival >= 0)
593 mt->lmargin = (size_t)ival;
595 return(0);
596 case (MAN_BODY):
597 p->offset = mt->offset + len;
598 p->rmargin = p->maxrmargin;
599 break;
600 default:
601 break;
604 return(1);
608 /* ARGSUSED */
609 static void
610 post_TP(DECL_ARGS)
613 switch (n->type) {
614 case (MAN_HEAD):
615 term_flushln(p);
616 p->flags &= ~TERMP_NOBREAK;
617 p->flags &= ~TERMP_TWOSPACE;
618 p->rmargin = p->maxrmargin;
619 break;
620 case (MAN_BODY):
621 term_flushln(p);
622 p->flags &= ~TERMP_NOLPAD;
623 break;
624 default:
625 break;
630 /* ARGSUSED */
631 static int
632 pre_SS(DECL_ARGS)
635 switch (n->type) {
636 case (MAN_BLOCK):
637 mt->lmargin = INDENT;
638 mt->offset = INDENT;
639 /* If following a prior empty `SS', no vspace. */
640 if (n->prev && MAN_SS == n->prev->tok)
641 if (NULL == n->prev->body->child)
642 break;
643 if (NULL == n->prev)
644 break;
645 term_vspace(p);
646 break;
647 case (MAN_HEAD):
648 term_fontrepl(p, TERMFONT_BOLD);
649 p->offset = HALFINDENT;
650 break;
651 case (MAN_BODY):
652 p->offset = mt->offset;
653 break;
654 default:
655 break;
658 return(1);
662 /* ARGSUSED */
663 static void
664 post_SS(DECL_ARGS)
667 switch (n->type) {
668 case (MAN_HEAD):
669 term_newln(p);
670 break;
671 case (MAN_BODY):
672 term_newln(p);
673 break;
674 default:
675 break;
680 /* ARGSUSED */
681 static int
682 pre_SH(DECL_ARGS)
685 switch (n->type) {
686 case (MAN_BLOCK):
687 mt->lmargin = INDENT;
688 mt->offset = INDENT;
689 /* If following a prior empty `SH', no vspace. */
690 if (n->prev && MAN_SH == n->prev->tok)
691 if (NULL == n->prev->body->child)
692 break;
693 term_vspace(p);
694 break;
695 case (MAN_HEAD):
696 term_fontrepl(p, TERMFONT_BOLD);
697 p->offset = 0;
698 break;
699 case (MAN_BODY):
700 p->offset = mt->offset;
701 break;
702 default:
703 break;
706 return(1);
710 /* ARGSUSED */
711 static void
712 post_SH(DECL_ARGS)
715 switch (n->type) {
716 case (MAN_HEAD):
717 term_newln(p);
718 break;
719 case (MAN_BODY):
720 term_newln(p);
721 break;
722 default:
723 break;
728 /* ARGSUSED */
729 static int
730 pre_RS(DECL_ARGS)
732 const struct man_node *nn;
733 int ival;
735 switch (n->type) {
736 case (MAN_BLOCK):
737 term_newln(p);
738 return(1);
739 case (MAN_HEAD):
740 return(0);
741 default:
742 break;
745 if (NULL == (nn = n->parent->head->child)) {
746 mt->offset = mt->lmargin + INDENT;
747 p->offset = mt->offset;
748 return(1);
751 if ((ival = a2width(nn)) < 0)
752 return(1);
754 mt->offset = INDENT + (size_t)ival;
755 p->offset = mt->offset;
757 return(1);
761 /* ARGSUSED */
762 static void
763 post_RS(DECL_ARGS)
766 switch (n->type) {
767 case (MAN_BLOCK):
768 mt->offset = mt->lmargin = INDENT;
769 break;
770 default:
771 term_newln(p);
772 p->offset = INDENT;
773 break;
778 static void
779 print_man_node(DECL_ARGS)
781 int c;
783 c = 1;
785 switch (n->type) {
786 case(MAN_TEXT):
787 if (0 == *n->string) {
788 term_vspace(p);
789 break;
792 term_word(p, n->string);
794 /* FIXME: this means that macro lines are munged! */
796 if (MANT_LITERAL & mt->fl) {
797 p->flags |= TERMP_NOSPACE;
798 term_flushln(p);
800 break;
801 default:
802 term_fontrepl(p, TERMFONT_NONE);
803 if (termacts[n->tok].pre)
804 c = (*termacts[n->tok].pre)(p, mt, n, m);
805 break;
808 if (c && n->child)
809 print_man_nodelist(p, mt, n->child, m);
811 if (MAN_TEXT != n->type) {
812 if (termacts[n->tok].post)
813 (*termacts[n->tok].post)(p, mt, n, m);
814 term_fontrepl(p, TERMFONT_NONE);
819 static void
820 print_man_nodelist(DECL_ARGS)
823 print_man_node(p, mt, n, m);
824 if ( ! n->next)
825 return;
826 print_man_nodelist(p, mt, n->next, m);
830 static void
831 print_man_foot(struct termp *p, const struct man_meta *meta)
833 char buf[DATESIZ];
835 term_fontrepl(p, TERMFONT_NONE);
837 time2a(meta->date, buf, DATESIZ);
839 term_vspace(p);
841 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
842 p->rmargin = p->maxrmargin - strlen(buf);
843 p->offset = 0;
845 if (meta->source)
846 term_word(p, meta->source);
847 if (meta->source)
848 term_word(p, "");
849 term_flushln(p);
851 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
852 p->offset = p->rmargin;
853 p->rmargin = p->maxrmargin;
854 p->flags &= ~TERMP_NOBREAK;
856 term_word(p, buf);
857 term_flushln(p);
861 static void
862 print_man_head(struct termp *p, const struct man_meta *m)
864 char buf[BUFSIZ], title[BUFSIZ];
866 p->rmargin = p->maxrmargin;
867 p->offset = 0;
868 buf[0] = title[0] = '\0';
870 if (m->vol)
871 strlcpy(buf, m->vol, BUFSIZ);
873 snprintf(title, BUFSIZ, "%s(%d)", m->title, m->msec);
875 p->offset = 0;
876 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
877 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
879 term_word(p, title);
880 term_flushln(p);
882 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
883 p->offset = p->rmargin;
884 p->rmargin = p->maxrmargin - strlen(title);
886 term_word(p, buf);
887 term_flushln(p);
889 p->offset = p->rmargin;
890 p->rmargin = p->maxrmargin;
891 p->flags &= ~TERMP_NOBREAK;
892 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
894 term_word(p, title);
895 term_flushln(p);
897 p->rmargin = p->maxrmargin;
898 p->offset = 0;
899 p->flags &= ~TERMP_NOSPACE;