man_term.c revision 1.1.1.5 1 /* $Vendor-Id: man_term.c,v 1.55 2010/01/01 17:14:28 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps (at) kth.se>
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 "out.h"
30 #include "man.h"
31 #include "term.h"
32 #include "chars.h"
33 #include "main.h"
34
35 #define INDENT 7
36 #define HALFINDENT 3
37
38 /* FIXME: have PD set the default vspace width. */
39
40 struct mtermp {
41 int fl;
42 #define MANT_LITERAL (1 << 0)
43 /*
44 * Default amount to indent the left margin after leading text
45 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
46 * indent). This needs to be saved because `HP' and so on, if
47 * not having a specified value, must default.
48 *
49 * Note that this is the indentation AFTER the left offset, so
50 * the total offset is usually offset + lmargin.
51 */
52 size_t lmargin;
53 /*
54 * The default offset, i.e., the amount between any text and the
55 * page boundary.
56 */
57 size_t offset;
58 };
59
60 #define DECL_ARGS struct termp *p, \
61 struct mtermp *mt, \
62 const struct man_node *n, \
63 const struct man_meta *m
64
65 struct termact {
66 int (*pre)(DECL_ARGS);
67 void (*post)(DECL_ARGS);
68 };
69
70 static int a2width(const struct man_node *);
71 static int a2height(const struct man_node *);
72
73 static void print_man_head(struct termp *,
74 const struct man_meta *);
75 static void print_man_nodelist(DECL_ARGS);
76 static void print_man_node(DECL_ARGS);
77 static void print_man_foot(struct termp *,
78 const struct man_meta *);
79 static void print_bvspace(struct termp *,
80 const struct man_node *);
81
82 static int pre_B(DECL_ARGS);
83 static int pre_BI(DECL_ARGS);
84 static int pre_HP(DECL_ARGS);
85 static int pre_I(DECL_ARGS);
86 static int pre_IP(DECL_ARGS);
87 static int pre_PP(DECL_ARGS);
88 static int pre_RB(DECL_ARGS);
89 static int pre_RI(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_br(DECL_ARGS);
95 static int pre_fi(DECL_ARGS);
96 static int pre_ign(DECL_ARGS);
97 static int pre_nf(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_br, NULL }, /* br */
109 { NULL, NULL }, /* TH */
110 { pre_SH, post_SH }, /* SH */
111 { pre_SS, post_SS }, /* SS */
112 { pre_TP, post_TP }, /* TP */
113 { pre_PP, NULL }, /* LP */
114 { pre_PP, NULL }, /* PP */
115 { pre_PP, NULL }, /* P */
116 { pre_IP, post_IP }, /* IP */
117 { pre_HP, post_HP }, /* HP */
118 { NULL, NULL }, /* SM */
119 { pre_B, NULL }, /* SB */
120 { pre_BI, NULL }, /* BI */
121 { pre_BI, NULL }, /* IB */
122 { pre_RB, NULL }, /* BR */
123 { pre_RB, NULL }, /* RB */
124 { NULL, NULL }, /* R */
125 { pre_B, NULL }, /* B */
126 { pre_I, NULL }, /* I */
127 { pre_RI, NULL }, /* IR */
128 { pre_RI, NULL }, /* RI */
129 { NULL, NULL }, /* na */
130 { pre_I, NULL }, /* i */
131 { pre_sp, NULL }, /* sp */
132 { pre_nf, NULL }, /* nf */
133 { pre_fi, NULL }, /* fi */
134 { NULL, NULL }, /* r */
135 { NULL, NULL }, /* RE */
136 { pre_RS, post_RS }, /* RS */
137 { pre_ign, NULL }, /* DT */
138 { pre_ign, NULL }, /* UC */
139 { pre_ign, NULL }, /* PD */
140 };
141
142
143
144 void
145 terminal_man(void *arg, const struct man *man)
146 {
147 struct termp *p;
148 const struct man_node *n;
149 const struct man_meta *m;
150 struct mtermp mt;
151
152 p = (struct termp *)arg;
153
154 if (NULL == p->symtab)
155 switch (p->enc) {
156 case (TERMENC_ASCII):
157 p->symtab = chars_init(CHARS_ASCII);
158 break;
159 default:
160 abort();
161 /* NOTREACHED */
162 }
163
164 n = man_node(man);
165 m = man_meta(man);
166
167 print_man_head(p, m);
168 p->flags |= TERMP_NOSPACE;
169
170 mt.fl = 0;
171 mt.lmargin = INDENT;
172 mt.offset = INDENT;
173
174 if (n->child)
175 print_man_nodelist(p, &mt, n->child, m);
176 print_man_foot(p, m);
177 }
178
179
180 static int
181 a2height(const struct man_node *n)
182 {
183 struct roffsu su;
184
185 assert(MAN_TEXT == n->type);
186 assert(n->string);
187 if ( ! a2roffsu(n->string, &su, SCALE_VS))
188 SCALE_VS_INIT(&su, strlen(n->string));
189
190 return((int)term_vspan(&su));
191 }
192
193
194 static int
195 a2width(const struct man_node *n)
196 {
197 struct roffsu su;
198
199 assert(MAN_TEXT == n->type);
200 assert(n->string);
201 if ( ! a2roffsu(n->string, &su, SCALE_BU))
202 return(-1);
203
204 return((int)term_hspan(&su));
205 }
206
207
208 static void
209 print_bvspace(struct termp *p, const struct man_node *n)
210 {
211 term_newln(p);
212
213 if (NULL == n->prev)
214 return;
215
216 if (MAN_SS == n->prev->tok)
217 return;
218 if (MAN_SH == n->prev->tok)
219 return;
220
221 term_vspace(p);
222 }
223
224
225 /* ARGSUSED */
226 static int
227 pre_ign(DECL_ARGS)
228 {
229
230 return(0);
231 }
232
233
234 /* ARGSUSED */
235 static int
236 pre_I(DECL_ARGS)
237 {
238
239 term_fontrepl(p, TERMFONT_UNDER);
240 return(1);
241 }
242
243
244 /* ARGSUSED */
245 static int
246 pre_fi(DECL_ARGS)
247 {
248
249 mt->fl &= ~MANT_LITERAL;
250 return(1);
251 }
252
253
254 /* ARGSUSED */
255 static int
256 pre_nf(DECL_ARGS)
257 {
258
259 term_newln(p);
260 mt->fl |= MANT_LITERAL;
261 return(1);
262 }
263
264
265 /* ARGSUSED */
266 static int
267 pre_RB(DECL_ARGS)
268 {
269 const struct man_node *nn;
270 int i;
271
272 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
273 if (i % 2 && MAN_RB == n->tok)
274 term_fontrepl(p, TERMFONT_BOLD);
275 else if ( ! (i % 2) && MAN_RB != n->tok)
276 term_fontrepl(p, TERMFONT_BOLD);
277 else
278 term_fontrepl(p, TERMFONT_NONE);
279
280 if (i > 0)
281 p->flags |= TERMP_NOSPACE;
282
283 print_man_node(p, mt, nn, m);
284 }
285 return(0);
286 }
287
288
289 /* ARGSUSED */
290 static int
291 pre_RI(DECL_ARGS)
292 {
293 const struct man_node *nn;
294 int i;
295
296 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
297 if (i % 2 && MAN_RI == n->tok)
298 term_fontrepl(p, TERMFONT_UNDER);
299 else if ( ! (i % 2) && MAN_RI != n->tok)
300 term_fontrepl(p, TERMFONT_UNDER);
301 else
302 term_fontrepl(p, TERMFONT_NONE);
303
304 if (i > 0)
305 p->flags |= TERMP_NOSPACE;
306
307 print_man_node(p, mt, nn, m);
308 }
309 return(0);
310 }
311
312
313 /* ARGSUSED */
314 static int
315 pre_BI(DECL_ARGS)
316 {
317 const struct man_node *nn;
318 int i;
319
320 for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
321 if (i % 2 && MAN_BI == n->tok)
322 term_fontrepl(p, TERMFONT_UNDER);
323 else if (i % 2)
324 term_fontrepl(p, TERMFONT_BOLD);
325 else if (MAN_BI == n->tok)
326 term_fontrepl(p, TERMFONT_BOLD);
327 else
328 term_fontrepl(p, TERMFONT_UNDER);
329
330 if (i)
331 p->flags |= TERMP_NOSPACE;
332
333 print_man_node(p, mt, nn, m);
334 }
335 return(0);
336 }
337
338
339 /* ARGSUSED */
340 static int
341 pre_B(DECL_ARGS)
342 {
343
344 term_fontrepl(p, TERMFONT_BOLD);
345 return(1);
346 }
347
348
349 /* ARGSUSED */
350 static int
351 pre_sp(DECL_ARGS)
352 {
353 int i, len;
354
355 len = n->child ? a2height(n->child) : 1;
356
357 if (0 == len)
358 term_newln(p);
359 for (i = 0; i < len; i++)
360 term_vspace(p);
361
362 return(0);
363 }
364
365
366 /* ARGSUSED */
367 static int
368 pre_br(DECL_ARGS)
369 {
370
371 term_newln(p);
372 return(0);
373 }
374
375
376 /* ARGSUSED */
377 static int
378 pre_HP(DECL_ARGS)
379 {
380 size_t len;
381 int ival;
382 const struct man_node *nn;
383
384 switch (n->type) {
385 case (MAN_BLOCK):
386 print_bvspace(p, n);
387 return(1);
388 case (MAN_BODY):
389 p->flags |= TERMP_NOBREAK;
390 p->flags |= TERMP_TWOSPACE;
391 break;
392 default:
393 return(0);
394 }
395
396 len = mt->lmargin;
397 ival = -1;
398
399 /* Calculate offset. */
400
401 if (NULL != (nn = n->parent->head->child))
402 if ((ival = a2width(nn)) >= 0)
403 len = (size_t)ival;
404
405 if (0 == len)
406 len = 1;
407
408 p->offset = mt->offset;
409 p->rmargin = mt->offset + len;
410
411 if (ival >= 0)
412 mt->lmargin = (size_t)ival;
413
414 return(1);
415 }
416
417
418 /* ARGSUSED */
419 static void
420 post_HP(DECL_ARGS)
421 {
422
423 switch (n->type) {
424 case (MAN_BLOCK):
425 term_flushln(p);
426 break;
427 case (MAN_BODY):
428 term_flushln(p);
429 p->flags &= ~TERMP_NOBREAK;
430 p->flags &= ~TERMP_TWOSPACE;
431 p->offset = mt->offset;
432 p->rmargin = p->maxrmargin;
433 break;
434 default:
435 break;
436 }
437 }
438
439
440 /* ARGSUSED */
441 static int
442 pre_PP(DECL_ARGS)
443 {
444
445 switch (n->type) {
446 case (MAN_BLOCK):
447 mt->lmargin = INDENT;
448 print_bvspace(p, n);
449 break;
450 default:
451 p->offset = mt->offset;
452 break;
453 }
454
455 return(1);
456 }
457
458
459 /* ARGSUSED */
460 static int
461 pre_IP(DECL_ARGS)
462 {
463 const struct man_node *nn;
464 size_t len;
465 int ival;
466
467 switch (n->type) {
468 case (MAN_BODY):
469 p->flags |= TERMP_NOLPAD;
470 p->flags |= TERMP_NOSPACE;
471 break;
472 case (MAN_HEAD):
473 p->flags |= TERMP_NOBREAK;
474 p->flags |= TERMP_TWOSPACE;
475 break;
476 case (MAN_BLOCK):
477 print_bvspace(p, n);
478 /* FALLTHROUGH */
479 default:
480 return(1);
481 }
482
483 len = mt->lmargin;
484 ival = -1;
485
486 /* Calculate offset. */
487
488 if (NULL != (nn = n->parent->head->child))
489 if (NULL != (nn = nn->next)) {
490 for ( ; nn->next; nn = nn->next)
491 /* Do nothing. */ ;
492 if ((ival = a2width(nn)) >= 0)
493 len = (size_t)ival;
494 }
495
496 switch (n->type) {
497 case (MAN_HEAD):
498 /* Handle zero-width lengths. */
499 if (0 == len)
500 len = 1;
501
502 p->offset = mt->offset;
503 p->rmargin = mt->offset + len;
504 if (ival < 0)
505 break;
506
507 /* Set the saved left-margin. */
508 mt->lmargin = (size_t)ival;
509
510 /* Don't print the length value. */
511 for (nn = n->child; nn->next; nn = nn->next)
512 print_man_node(p, mt, nn, m);
513 return(0);
514 case (MAN_BODY):
515 p->offset = mt->offset + len;
516 p->rmargin = p->maxrmargin;
517 break;
518 default:
519 break;
520 }
521
522 return(1);
523 }
524
525
526 /* ARGSUSED */
527 static void
528 post_IP(DECL_ARGS)
529 {
530
531 switch (n->type) {
532 case (MAN_HEAD):
533 term_flushln(p);
534 p->flags &= ~TERMP_NOBREAK;
535 p->flags &= ~TERMP_TWOSPACE;
536 p->rmargin = p->maxrmargin;
537 break;
538 case (MAN_BODY):
539 term_flushln(p);
540 p->flags &= ~TERMP_NOLPAD;
541 break;
542 default:
543 break;
544 }
545 }
546
547
548 /* ARGSUSED */
549 static int
550 pre_TP(DECL_ARGS)
551 {
552 const struct man_node *nn;
553 size_t len;
554 int ival;
555
556 switch (n->type) {
557 case (MAN_HEAD):
558 p->flags |= TERMP_NOBREAK;
559 p->flags |= TERMP_TWOSPACE;
560 break;
561 case (MAN_BODY):
562 p->flags |= TERMP_NOLPAD;
563 p->flags |= TERMP_NOSPACE;
564 break;
565 case (MAN_BLOCK):
566 print_bvspace(p, n);
567 /* FALLTHROUGH */
568 default:
569 return(1);
570 }
571
572 len = (size_t)mt->lmargin;
573 ival = -1;
574
575 /* Calculate offset. */
576
577 if (NULL != (nn = n->parent->head->child))
578 if (NULL != nn->next)
579 if ((ival = a2width(nn)) >= 0)
580 len = (size_t)ival;
581
582 switch (n->type) {
583 case (MAN_HEAD):
584 /* Handle zero-length properly. */
585 if (0 == len)
586 len = 1;
587
588 p->offset = mt->offset;
589 p->rmargin = mt->offset + len;
590
591 /* Don't print same-line elements. */
592 for (nn = n->child; nn; nn = nn->next)
593 if (nn->line > n->line)
594 print_man_node(p, mt, nn, m);
595
596 if (ival >= 0)
597 mt->lmargin = (size_t)ival;
598
599 return(0);
600 case (MAN_BODY):
601 p->offset = mt->offset + len;
602 p->rmargin = p->maxrmargin;
603 break;
604 default:
605 break;
606 }
607
608 return(1);
609 }
610
611
612 /* ARGSUSED */
613 static void
614 post_TP(DECL_ARGS)
615 {
616
617 switch (n->type) {
618 case (MAN_HEAD):
619 term_flushln(p);
620 p->flags &= ~TERMP_NOBREAK;
621 p->flags &= ~TERMP_TWOSPACE;
622 p->rmargin = p->maxrmargin;
623 break;
624 case (MAN_BODY):
625 term_flushln(p);
626 p->flags &= ~TERMP_NOLPAD;
627 break;
628 default:
629 break;
630 }
631 }
632
633
634 /* ARGSUSED */
635 static int
636 pre_SS(DECL_ARGS)
637 {
638
639 switch (n->type) {
640 case (MAN_BLOCK):
641 mt->lmargin = INDENT;
642 mt->offset = INDENT;
643 /* If following a prior empty `SS', no vspace. */
644 if (n->prev && MAN_SS == n->prev->tok)
645 if (NULL == n->prev->body->child)
646 break;
647 if (NULL == n->prev)
648 break;
649 term_vspace(p);
650 break;
651 case (MAN_HEAD):
652 term_fontrepl(p, TERMFONT_BOLD);
653 p->offset = HALFINDENT;
654 break;
655 case (MAN_BODY):
656 p->offset = mt->offset;
657 break;
658 default:
659 break;
660 }
661
662 return(1);
663 }
664
665
666 /* ARGSUSED */
667 static void
668 post_SS(DECL_ARGS)
669 {
670
671 switch (n->type) {
672 case (MAN_HEAD):
673 term_newln(p);
674 break;
675 case (MAN_BODY):
676 term_newln(p);
677 break;
678 default:
679 break;
680 }
681 }
682
683
684 /* ARGSUSED */
685 static int
686 pre_SH(DECL_ARGS)
687 {
688
689 switch (n->type) {
690 case (MAN_BLOCK):
691 mt->lmargin = INDENT;
692 mt->offset = INDENT;
693 /* If following a prior empty `SH', no vspace. */
694 if (n->prev && MAN_SH == n->prev->tok)
695 if (NULL == n->prev->body->child)
696 break;
697 term_vspace(p);
698 break;
699 case (MAN_HEAD):
700 term_fontrepl(p, TERMFONT_BOLD);
701 p->offset = 0;
702 break;
703 case (MAN_BODY):
704 p->offset = mt->offset;
705 break;
706 default:
707 break;
708 }
709
710 return(1);
711 }
712
713
714 /* ARGSUSED */
715 static void
716 post_SH(DECL_ARGS)
717 {
718
719 switch (n->type) {
720 case (MAN_HEAD):
721 term_newln(p);
722 break;
723 case (MAN_BODY):
724 term_newln(p);
725 break;
726 default:
727 break;
728 }
729 }
730
731
732 /* ARGSUSED */
733 static int
734 pre_RS(DECL_ARGS)
735 {
736 const struct man_node *nn;
737 int ival;
738
739 switch (n->type) {
740 case (MAN_BLOCK):
741 term_newln(p);
742 return(1);
743 case (MAN_HEAD):
744 return(0);
745 default:
746 break;
747 }
748
749 if (NULL == (nn = n->parent->head->child)) {
750 mt->offset = mt->lmargin + INDENT;
751 p->offset = mt->offset;
752 return(1);
753 }
754
755 if ((ival = a2width(nn)) < 0)
756 return(1);
757
758 mt->offset = INDENT + (size_t)ival;
759 p->offset = mt->offset;
760
761 return(1);
762 }
763
764
765 /* ARGSUSED */
766 static void
767 post_RS(DECL_ARGS)
768 {
769
770 switch (n->type) {
771 case (MAN_BLOCK):
772 mt->offset = mt->lmargin = INDENT;
773 break;
774 default:
775 term_newln(p);
776 p->offset = INDENT;
777 break;
778 }
779 }
780
781
782 static void
783 print_man_node(DECL_ARGS)
784 {
785 int c;
786
787 c = 1;
788
789 switch (n->type) {
790 case(MAN_TEXT):
791 if (0 == *n->string) {
792 term_vspace(p);
793 break;
794 }
795
796 term_word(p, n->string);
797
798 /* FIXME: this means that macro lines are munged! */
799
800 if (MANT_LITERAL & mt->fl) {
801 p->flags |= TERMP_NOSPACE;
802 term_flushln(p);
803 }
804 break;
805 default:
806 term_fontrepl(p, TERMFONT_NONE);
807 if (termacts[n->tok].pre)
808 c = (*termacts[n->tok].pre)(p, mt, n, m);
809 break;
810 }
811
812 if (c && n->child)
813 print_man_nodelist(p, mt, n->child, m);
814
815 if (MAN_TEXT != n->type) {
816 if (termacts[n->tok].post)
817 (*termacts[n->tok].post)(p, mt, n, m);
818 term_fontrepl(p, TERMFONT_NONE);
819 }
820 }
821
822
823 static void
824 print_man_nodelist(DECL_ARGS)
825 {
826
827 print_man_node(p, mt, n, m);
828 if ( ! n->next)
829 return;
830 print_man_nodelist(p, mt, n->next, m);
831 }
832
833
834 static void
835 print_man_foot(struct termp *p, const struct man_meta *meta)
836 {
837 char buf[DATESIZ];
838
839 term_fontrepl(p, TERMFONT_NONE);
840
841 time2a(meta->date, buf, DATESIZ);
842
843 term_vspace(p);
844
845 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
846 p->rmargin = p->maxrmargin - strlen(buf);
847 p->offset = 0;
848
849 if (meta->source)
850 term_word(p, meta->source);
851 if (meta->source)
852 term_word(p, "");
853 term_flushln(p);
854
855 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
856 p->offset = p->rmargin;
857 p->rmargin = p->maxrmargin;
858 p->flags &= ~TERMP_NOBREAK;
859
860 term_word(p, buf);
861 term_flushln(p);
862 }
863
864
865 static void
866 print_man_head(struct termp *p, const struct man_meta *m)
867 {
868 char buf[BUFSIZ], title[BUFSIZ];
869
870 p->rmargin = p->maxrmargin;
871 p->offset = 0;
872 buf[0] = title[0] = '\0';
873
874 if (m->vol)
875 strlcpy(buf, m->vol, BUFSIZ);
876
877 snprintf(title, BUFSIZ, "%s(%d)", m->title, m->msec);
878
879 p->offset = 0;
880 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
881 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
882
883 term_word(p, title);
884 term_flushln(p);
885
886 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
887 p->offset = p->rmargin;
888 p->rmargin = p->maxrmargin - strlen(title);
889
890 term_word(p, buf);
891 term_flushln(p);
892
893 p->offset = p->rmargin;
894 p->rmargin = p->maxrmargin;
895 p->flags &= ~TERMP_NOBREAK;
896 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
897
898 term_word(p, title);
899 term_flushln(p);
900
901 p->rmargin = p->maxrmargin;
902 p->offset = 0;
903 p->flags &= ~TERMP_NOSPACE;
904 }
905