]> git.cameronkatri.com Git - mandoc.git/blob - man_term.c
Prevent negative arguments to the .ll request from causing integer
[mandoc.git] / man_term.c
1 /* $Id: man_term.c,v 1.155 2014/10/28 17:36:19 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2014 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 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "mandoc.h"
29 #include "mandoc_aux.h"
30 #include "out.h"
31 #include "man.h"
32 #include "term.h"
33 #include "main.h"
34
35 #define MAXMARGINS 64 /* maximum number of indented scopes */
36
37 struct mtermp {
38 int fl;
39 #define MANT_LITERAL (1 << 0)
40 size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */
41 int lmargincur; /* index of current margin */
42 int lmarginsz; /* actual number of nested margins */
43 size_t offset; /* default offset to visible page */
44 int pardist; /* vert. space before par., unit: [v] */
45 };
46
47 #define DECL_ARGS struct termp *p, \
48 struct mtermp *mt, \
49 const struct man_node *n, \
50 const struct man_meta *meta
51
52 struct termact {
53 int (*pre)(DECL_ARGS);
54 void (*post)(DECL_ARGS);
55 int flags;
56 #define MAN_NOTEXT (1 << 0) /* Never has text children. */
57 };
58
59 static int a2width(const struct termp *, const char *);
60 static size_t a2height(const struct termp *, const char *);
61
62 static void print_man_nodelist(DECL_ARGS);
63 static void print_man_node(DECL_ARGS);
64 static void print_man_head(struct termp *, const void *);
65 static void print_man_foot(struct termp *, const void *);
66 static void print_bvspace(struct termp *,
67 const struct man_node *, int);
68
69 static int pre_B(DECL_ARGS);
70 static int pre_HP(DECL_ARGS);
71 static int pre_I(DECL_ARGS);
72 static int pre_IP(DECL_ARGS);
73 static int pre_OP(DECL_ARGS);
74 static int pre_PD(DECL_ARGS);
75 static int pre_PP(DECL_ARGS);
76 static int pre_RS(DECL_ARGS);
77 static int pre_SH(DECL_ARGS);
78 static int pre_SS(DECL_ARGS);
79 static int pre_TP(DECL_ARGS);
80 static int pre_UR(DECL_ARGS);
81 static int pre_alternate(DECL_ARGS);
82 static int pre_ft(DECL_ARGS);
83 static int pre_ign(DECL_ARGS);
84 static int pre_in(DECL_ARGS);
85 static int pre_literal(DECL_ARGS);
86 static int pre_ll(DECL_ARGS);
87 static int pre_sp(DECL_ARGS);
88
89 static void post_IP(DECL_ARGS);
90 static void post_HP(DECL_ARGS);
91 static void post_RS(DECL_ARGS);
92 static void post_SH(DECL_ARGS);
93 static void post_SS(DECL_ARGS);
94 static void post_TP(DECL_ARGS);
95 static void post_UR(DECL_ARGS);
96
97 static const struct termact termacts[MAN_MAX] = {
98 { pre_sp, NULL, MAN_NOTEXT }, /* br */
99 { NULL, NULL, 0 }, /* TH */
100 { pre_SH, post_SH, 0 }, /* SH */
101 { pre_SS, post_SS, 0 }, /* SS */
102 { pre_TP, post_TP, 0 }, /* TP */
103 { pre_PP, NULL, 0 }, /* LP */
104 { pre_PP, NULL, 0 }, /* PP */
105 { pre_PP, NULL, 0 }, /* P */
106 { pre_IP, post_IP, 0 }, /* IP */
107 { pre_HP, post_HP, 0 }, /* HP */
108 { NULL, NULL, 0 }, /* SM */
109 { pre_B, NULL, 0 }, /* SB */
110 { pre_alternate, NULL, 0 }, /* BI */
111 { pre_alternate, NULL, 0 }, /* IB */
112 { pre_alternate, NULL, 0 }, /* BR */
113 { pre_alternate, NULL, 0 }, /* RB */
114 { NULL, NULL, 0 }, /* R */
115 { pre_B, NULL, 0 }, /* B */
116 { pre_I, NULL, 0 }, /* I */
117 { pre_alternate, NULL, 0 }, /* IR */
118 { pre_alternate, NULL, 0 }, /* RI */
119 { pre_ign, NULL, MAN_NOTEXT }, /* na */
120 { pre_sp, NULL, MAN_NOTEXT }, /* sp */
121 { pre_literal, NULL, 0 }, /* nf */
122 { pre_literal, NULL, 0 }, /* fi */
123 { NULL, NULL, 0 }, /* RE */
124 { pre_RS, post_RS, 0 }, /* RS */
125 { pre_ign, NULL, 0 }, /* DT */
126 { pre_ign, NULL, 0 }, /* UC */
127 { pre_PD, NULL, MAN_NOTEXT }, /* PD */
128 { pre_ign, NULL, 0 }, /* AT */
129 { pre_in, NULL, MAN_NOTEXT }, /* in */
130 { pre_ft, NULL, MAN_NOTEXT }, /* ft */
131 { pre_OP, NULL, 0 }, /* OP */
132 { pre_literal, NULL, 0 }, /* EX */
133 { pre_literal, NULL, 0 }, /* EE */
134 { pre_UR, post_UR, 0 }, /* UR */
135 { NULL, NULL, 0 }, /* UE */
136 { pre_ll, NULL, MAN_NOTEXT }, /* ll */
137 };
138
139
140 void
141 terminal_man(void *arg, const struct man *man)
142 {
143 struct termp *p;
144 const struct man_meta *meta;
145 struct man_node *n;
146 struct mtermp mt;
147
148 p = (struct termp *)arg;
149
150 p->overstep = 0;
151 p->rmargin = p->maxrmargin = p->defrmargin;
152 p->tabwidth = term_len(p, 5);
153
154 n = man_node(man)->child;
155 meta = man_meta(man);
156
157 memset(&mt, 0, sizeof(struct mtermp));
158
159 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
160 mt.offset = term_len(p, p->defindent);
161 mt.pardist = 1;
162
163 if (p->synopsisonly) {
164 while (n != NULL) {
165 if (n->tok == MAN_SH &&
166 n->child->child->type == MAN_TEXT &&
167 !strcmp(n->child->child->string, "SYNOPSIS")) {
168 if (n->child->next->child != NULL)
169 print_man_nodelist(p, &mt,
170 n->child->next->child, meta);
171 term_newln(p);
172 break;
173 }
174 n = n->next;
175 }
176 } else {
177 if (p->defindent == 0)
178 p->defindent = 7;
179 term_begin(p, print_man_head, print_man_foot, meta);
180 p->flags |= TERMP_NOSPACE;
181 if (n != NULL)
182 print_man_nodelist(p, &mt, n, meta);
183 term_end(p);
184 }
185 }
186
187
188 static size_t
189 a2height(const struct termp *p, const char *cp)
190 {
191 struct roffsu su;
192
193 if ( ! a2roffsu(cp, &su, SCALE_VS))
194 SCALE_VS_INIT(&su, atoi(cp));
195
196 return(term_vspan(p, &su));
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 * Printing leading vertical space before a block.
212 * This is used for the paragraph macros.
213 * The rules are pretty simple, since there's very little nesting going
214 * on here. Basically, if we're the first within another block (SS/SH),
215 * then don't emit vertical space. If we are (RS), then do. If not the
216 * first, print it.
217 */
218 static void
219 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
220 {
221 int i;
222
223 term_newln(p);
224
225 if (n->body && n->body->child)
226 if (MAN_TBL == n->body->child->type)
227 return;
228
229 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
230 if (NULL == n->prev)
231 return;
232
233 for (i = 0; i < pardist; i++)
234 term_vspace(p);
235 }
236
237
238 static int
239 pre_ign(DECL_ARGS)
240 {
241
242 return(0);
243 }
244
245 static int
246 pre_ll(DECL_ARGS)
247 {
248
249 term_setwidth(p, n->nchild ? n->child->string : NULL);
250 return(0);
251 }
252
253 static int
254 pre_I(DECL_ARGS)
255 {
256
257 term_fontrepl(p, TERMFONT_UNDER);
258 return(1);
259 }
260
261 static int
262 pre_literal(DECL_ARGS)
263 {
264
265 term_newln(p);
266
267 if (MAN_nf == n->tok || MAN_EX == n->tok)
268 mt->fl |= MANT_LITERAL;
269 else
270 mt->fl &= ~MANT_LITERAL;
271
272 /*
273 * Unlike .IP and .TP, .HP does not have a HEAD.
274 * So in case a second call to term_flushln() is needed,
275 * indentation has to be set up explicitly.
276 */
277 if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
278 p->offset = p->rmargin;
279 p->rmargin = p->maxrmargin;
280 p->trailspace = 0;
281 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
282 p->flags |= TERMP_NOSPACE;
283 }
284
285 return(0);
286 }
287
288 static int
289 pre_PD(DECL_ARGS)
290 {
291
292 n = n->child;
293 if (0 == n) {
294 mt->pardist = 1;
295 return(0);
296 }
297 assert(MAN_TEXT == n->type);
298 mt->pardist = atoi(n->string);
299 return(0);
300 }
301
302 static int
303 pre_alternate(DECL_ARGS)
304 {
305 enum termfont font[2];
306 const struct man_node *nn;
307 int savelit, i;
308
309 switch (n->tok) {
310 case MAN_RB:
311 font[0] = TERMFONT_NONE;
312 font[1] = TERMFONT_BOLD;
313 break;
314 case MAN_RI:
315 font[0] = TERMFONT_NONE;
316 font[1] = TERMFONT_UNDER;
317 break;
318 case MAN_BR:
319 font[0] = TERMFONT_BOLD;
320 font[1] = TERMFONT_NONE;
321 break;
322 case MAN_BI:
323 font[0] = TERMFONT_BOLD;
324 font[1] = TERMFONT_UNDER;
325 break;
326 case MAN_IR:
327 font[0] = TERMFONT_UNDER;
328 font[1] = TERMFONT_NONE;
329 break;
330 case MAN_IB:
331 font[0] = TERMFONT_UNDER;
332 font[1] = TERMFONT_BOLD;
333 break;
334 default:
335 abort();
336 }
337
338 savelit = MANT_LITERAL & mt->fl;
339 mt->fl &= ~MANT_LITERAL;
340
341 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
342 term_fontrepl(p, font[i]);
343 if (savelit && NULL == nn->next)
344 mt->fl |= MANT_LITERAL;
345 print_man_node(p, mt, nn, meta);
346 if (nn->next)
347 p->flags |= TERMP_NOSPACE;
348 }
349
350 return(0);
351 }
352
353 static int
354 pre_B(DECL_ARGS)
355 {
356
357 term_fontrepl(p, TERMFONT_BOLD);
358 return(1);
359 }
360
361 static int
362 pre_OP(DECL_ARGS)
363 {
364
365 term_word(p, "[");
366 p->flags |= TERMP_NOSPACE;
367
368 if (NULL != (n = n->child)) {
369 term_fontrepl(p, TERMFONT_BOLD);
370 term_word(p, n->string);
371 }
372 if (NULL != n && NULL != n->next) {
373 term_fontrepl(p, TERMFONT_UNDER);
374 term_word(p, n->next->string);
375 }
376
377 term_fontrepl(p, TERMFONT_NONE);
378 p->flags |= TERMP_NOSPACE;
379 term_word(p, "]");
380 return(0);
381 }
382
383 static int
384 pre_ft(DECL_ARGS)
385 {
386 const char *cp;
387
388 if (NULL == n->child) {
389 term_fontlast(p);
390 return(0);
391 }
392
393 cp = n->child->string;
394 switch (*cp) {
395 case '4':
396 /* FALLTHROUGH */
397 case '3':
398 /* FALLTHROUGH */
399 case 'B':
400 term_fontrepl(p, TERMFONT_BOLD);
401 break;
402 case '2':
403 /* FALLTHROUGH */
404 case 'I':
405 term_fontrepl(p, TERMFONT_UNDER);
406 break;
407 case 'P':
408 term_fontlast(p);
409 break;
410 case '1':
411 /* FALLTHROUGH */
412 case 'C':
413 /* FALLTHROUGH */
414 case 'R':
415 term_fontrepl(p, TERMFONT_NONE);
416 break;
417 default:
418 break;
419 }
420 return(0);
421 }
422
423 static int
424 pre_in(DECL_ARGS)
425 {
426 int len, less;
427 size_t v;
428 const char *cp;
429
430 term_newln(p);
431
432 if (NULL == n->child) {
433 p->offset = mt->offset;
434 return(0);
435 }
436
437 cp = n->child->string;
438 less = 0;
439
440 if ('-' == *cp)
441 less = -1;
442 else if ('+' == *cp)
443 less = 1;
444 else
445 cp--;
446
447 if ((len = a2width(p, ++cp)) < 0)
448 return(0);
449
450 v = (size_t)len;
451
452 if (less < 0)
453 p->offset -= p->offset > v ? v : p->offset;
454 else if (less > 0)
455 p->offset += v;
456 else
457 p->offset = v;
458
459 /* Don't let this creep beyond the right margin. */
460
461 if (p->offset > p->rmargin)
462 p->offset = p->rmargin;
463
464 return(0);
465 }
466
467 static int
468 pre_sp(DECL_ARGS)
469 {
470 char *s;
471 size_t i, len;
472 int neg;
473
474 if ((NULL == n->prev && n->parent)) {
475 switch (n->parent->tok) {
476 case MAN_SH:
477 /* FALLTHROUGH */
478 case MAN_SS:
479 /* FALLTHROUGH */
480 case MAN_PP:
481 /* FALLTHROUGH */
482 case MAN_LP:
483 /* FALLTHROUGH */
484 case MAN_P:
485 /* FALLTHROUGH */
486 return(0);
487 default:
488 break;
489 }
490 }
491
492 neg = 0;
493 switch (n->tok) {
494 case MAN_br:
495 len = 0;
496 break;
497 default:
498 if (NULL == n->child) {
499 len = 1;
500 break;
501 }
502 s = n->child->string;
503 if ('-' == *s) {
504 neg = 1;
505 s++;
506 }
507 len = a2height(p, s);
508 break;
509 }
510
511 if (0 == len)
512 term_newln(p);
513 else if (neg)
514 p->skipvsp += len;
515 else
516 for (i = 0; i < len; i++)
517 term_vspace(p);
518
519 return(0);
520 }
521
522 static int
523 pre_HP(DECL_ARGS)
524 {
525 size_t len, one;
526 int ival;
527 const struct man_node *nn;
528
529 switch (n->type) {
530 case MAN_BLOCK:
531 print_bvspace(p, n, mt->pardist);
532 return(1);
533 case MAN_BODY:
534 break;
535 default:
536 return(0);
537 }
538
539 if ( ! (MANT_LITERAL & mt->fl)) {
540 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
541 p->trailspace = 2;
542 }
543
544 len = mt->lmargin[mt->lmargincur];
545 ival = -1;
546
547 /* Calculate offset. */
548
549 if (NULL != (nn = n->parent->head->child))
550 if ((ival = a2width(p, nn->string)) >= 0)
551 len = (size_t)ival;
552
553 one = term_len(p, 1);
554 if (len < one)
555 len = one;
556
557 p->offset = mt->offset;
558 p->rmargin = mt->offset + len;
559
560 if (ival >= 0)
561 mt->lmargin[mt->lmargincur] = (size_t)ival;
562
563 return(1);
564 }
565
566 static void
567 post_HP(DECL_ARGS)
568 {
569
570 switch (n->type) {
571 case MAN_BODY:
572 term_newln(p);
573 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
574 p->trailspace = 0;
575 p->offset = mt->offset;
576 p->rmargin = p->maxrmargin;
577 break;
578 default:
579 break;
580 }
581 }
582
583 static int
584 pre_PP(DECL_ARGS)
585 {
586
587 switch (n->type) {
588 case MAN_BLOCK:
589 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
590 print_bvspace(p, n, mt->pardist);
591 break;
592 default:
593 p->offset = mt->offset;
594 break;
595 }
596
597 return(MAN_HEAD != n->type);
598 }
599
600 static int
601 pre_IP(DECL_ARGS)
602 {
603 const struct man_node *nn;
604 size_t len;
605 int savelit, ival;
606
607 switch (n->type) {
608 case MAN_BODY:
609 p->flags |= TERMP_NOSPACE;
610 break;
611 case MAN_HEAD:
612 p->flags |= TERMP_NOBREAK;
613 p->trailspace = 1;
614 break;
615 case MAN_BLOCK:
616 print_bvspace(p, n, mt->pardist);
617 /* FALLTHROUGH */
618 default:
619 return(1);
620 }
621
622 len = mt->lmargin[mt->lmargincur];
623 ival = -1;
624
625 /* Calculate the offset from the optional second argument. */
626 if (NULL != (nn = n->parent->head->child))
627 if (NULL != (nn = nn->next))
628 if ((ival = a2width(p, nn->string)) >= 0)
629 len = (size_t)ival;
630
631 switch (n->type) {
632 case MAN_HEAD:
633 /* Handle zero-width lengths. */
634 if (0 == len)
635 len = term_len(p, 1);
636
637 p->offset = mt->offset;
638 p->rmargin = mt->offset + len;
639 if (ival < 0)
640 break;
641
642 /* Set the saved left-margin. */
643 mt->lmargin[mt->lmargincur] = (size_t)ival;
644
645 savelit = MANT_LITERAL & mt->fl;
646 mt->fl &= ~MANT_LITERAL;
647
648 if (n->child)
649 print_man_node(p, mt, n->child, meta);
650
651 if (savelit)
652 mt->fl |= MANT_LITERAL;
653
654 return(0);
655 case MAN_BODY:
656 p->offset = mt->offset + len;
657 p->rmargin = p->maxrmargin > p->offset ?
658 p->maxrmargin : p->offset;
659 break;
660 default:
661 break;
662 }
663
664 return(1);
665 }
666
667 static void
668 post_IP(DECL_ARGS)
669 {
670
671 switch (n->type) {
672 case MAN_HEAD:
673 term_flushln(p);
674 p->flags &= ~TERMP_NOBREAK;
675 p->trailspace = 0;
676 p->rmargin = p->maxrmargin;
677 break;
678 case MAN_BODY:
679 term_newln(p);
680 p->offset = mt->offset;
681 break;
682 default:
683 break;
684 }
685 }
686
687 static int
688 pre_TP(DECL_ARGS)
689 {
690 const struct man_node *nn;
691 size_t len;
692 int savelit, ival;
693
694 switch (n->type) {
695 case MAN_HEAD:
696 p->flags |= TERMP_NOBREAK;
697 p->trailspace = 1;
698 break;
699 case MAN_BODY:
700 p->flags |= TERMP_NOSPACE;
701 break;
702 case MAN_BLOCK:
703 print_bvspace(p, n, mt->pardist);
704 /* FALLTHROUGH */
705 default:
706 return(1);
707 }
708
709 len = (size_t)mt->lmargin[mt->lmargincur];
710 ival = -1;
711
712 /* Calculate offset. */
713
714 if (NULL != (nn = n->parent->head->child))
715 if (nn->string && 0 == (MAN_LINE & nn->flags))
716 if ((ival = a2width(p, nn->string)) >= 0)
717 len = (size_t)ival;
718
719 switch (n->type) {
720 case MAN_HEAD:
721 /* Handle zero-length properly. */
722 if (0 == len)
723 len = term_len(p, 1);
724
725 p->offset = mt->offset;
726 p->rmargin = mt->offset + len;
727
728 savelit = MANT_LITERAL & mt->fl;
729 mt->fl &= ~MANT_LITERAL;
730
731 /* Don't print same-line elements. */
732 nn = n->child;
733 while (NULL != nn && 0 == (MAN_LINE & nn->flags))
734 nn = nn->next;
735
736 while (NULL != nn) {
737 print_man_node(p, mt, nn, meta);
738 nn = nn->next;
739 }
740
741 if (savelit)
742 mt->fl |= MANT_LITERAL;
743 if (ival >= 0)
744 mt->lmargin[mt->lmargincur] = (size_t)ival;
745
746 return(0);
747 case MAN_BODY:
748 p->offset = mt->offset + len;
749 p->rmargin = p->maxrmargin > p->offset ?
750 p->maxrmargin : p->offset;
751 p->trailspace = 0;
752 p->flags &= ~TERMP_NOBREAK;
753 break;
754 default:
755 break;
756 }
757
758 return(1);
759 }
760
761 static void
762 post_TP(DECL_ARGS)
763 {
764
765 switch (n->type) {
766 case MAN_HEAD:
767 term_flushln(p);
768 break;
769 case MAN_BODY:
770 term_newln(p);
771 p->offset = mt->offset;
772 break;
773 default:
774 break;
775 }
776 }
777
778 static int
779 pre_SS(DECL_ARGS)
780 {
781 int i;
782
783 switch (n->type) {
784 case MAN_BLOCK:
785 mt->fl &= ~MANT_LITERAL;
786 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
787 mt->offset = term_len(p, p->defindent);
788 /* If following a prior empty `SS', no vspace. */
789 if (n->prev && MAN_SS == n->prev->tok)
790 if (NULL == n->prev->body->child)
791 break;
792 if (NULL == n->prev)
793 break;
794 for (i = 0; i < mt->pardist; i++)
795 term_vspace(p);
796 break;
797 case MAN_HEAD:
798 term_fontrepl(p, TERMFONT_BOLD);
799 p->offset = term_len(p, 3);
800 break;
801 case MAN_BODY:
802 p->offset = mt->offset;
803 break;
804 default:
805 break;
806 }
807
808 return(1);
809 }
810
811 static void
812 post_SS(DECL_ARGS)
813 {
814
815 switch (n->type) {
816 case MAN_HEAD:
817 term_newln(p);
818 break;
819 case MAN_BODY:
820 term_newln(p);
821 break;
822 default:
823 break;
824 }
825 }
826
827 static int
828 pre_SH(DECL_ARGS)
829 {
830 int i;
831
832 switch (n->type) {
833 case MAN_BLOCK:
834 mt->fl &= ~MANT_LITERAL;
835 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
836 mt->offset = term_len(p, p->defindent);
837 /* If following a prior empty `SH', no vspace. */
838 if (n->prev && MAN_SH == n->prev->tok)
839 if (NULL == n->prev->body->child)
840 break;
841 /* If the first macro, no vspae. */
842 if (NULL == n->prev)
843 break;
844 for (i = 0; i < mt->pardist; i++)
845 term_vspace(p);
846 break;
847 case MAN_HEAD:
848 term_fontrepl(p, TERMFONT_BOLD);
849 p->offset = 0;
850 break;
851 case MAN_BODY:
852 p->offset = mt->offset;
853 break;
854 default:
855 break;
856 }
857
858 return(1);
859 }
860
861 static void
862 post_SH(DECL_ARGS)
863 {
864
865 switch (n->type) {
866 case MAN_HEAD:
867 term_newln(p);
868 break;
869 case MAN_BODY:
870 term_newln(p);
871 break;
872 default:
873 break;
874 }
875 }
876
877 static int
878 pre_RS(DECL_ARGS)
879 {
880 int ival;
881 size_t sz;
882
883 switch (n->type) {
884 case MAN_BLOCK:
885 term_newln(p);
886 return(1);
887 case MAN_HEAD:
888 return(0);
889 default:
890 break;
891 }
892
893 sz = term_len(p, p->defindent);
894
895 if (NULL != (n = n->parent->head->child))
896 if ((ival = a2width(p, n->string)) >= 0)
897 sz = (size_t)ival;
898
899 mt->offset += sz;
900 p->offset = mt->offset;
901 p->rmargin = p->maxrmargin > p->offset ?
902 p->maxrmargin : p->offset;
903
904 if (++mt->lmarginsz < MAXMARGINS)
905 mt->lmargincur = mt->lmarginsz;
906
907 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
908 return(1);
909 }
910
911 static void
912 post_RS(DECL_ARGS)
913 {
914 int ival;
915 size_t sz;
916
917 switch (n->type) {
918 case MAN_BLOCK:
919 return;
920 case MAN_HEAD:
921 return;
922 default:
923 term_newln(p);
924 break;
925 }
926
927 sz = term_len(p, p->defindent);
928
929 if (NULL != (n = n->parent->head->child))
930 if ((ival = a2width(p, n->string)) >= 0)
931 sz = (size_t)ival;
932
933 mt->offset = mt->offset < sz ? 0 : mt->offset - sz;
934 p->offset = mt->offset;
935
936 if (--mt->lmarginsz < MAXMARGINS)
937 mt->lmargincur = mt->lmarginsz;
938 }
939
940 static int
941 pre_UR(DECL_ARGS)
942 {
943
944 return (MAN_HEAD != n->type);
945 }
946
947 static void
948 post_UR(DECL_ARGS)
949 {
950
951 if (MAN_BLOCK != n->type)
952 return;
953
954 term_word(p, "<");
955 p->flags |= TERMP_NOSPACE;
956
957 if (NULL != n->child->child)
958 print_man_node(p, mt, n->child->child, meta);
959
960 p->flags |= TERMP_NOSPACE;
961 term_word(p, ">");
962 }
963
964 static void
965 print_man_node(DECL_ARGS)
966 {
967 size_t rm, rmax;
968 int c;
969
970 switch (n->type) {
971 case MAN_TEXT:
972 /*
973 * If we have a blank line, output a vertical space.
974 * If we have a space as the first character, break
975 * before printing the line's data.
976 */
977 if ('\0' == *n->string) {
978 term_vspace(p);
979 return;
980 } else if (' ' == *n->string && MAN_LINE & n->flags)
981 term_newln(p);
982
983 term_word(p, n->string);
984 goto out;
985
986 case MAN_EQN:
987 if ( ! (n->flags & MAN_LINE))
988 p->flags |= TERMP_NOSPACE;
989 term_eqn(p, n->eqn);
990 if (n->next != NULL && ! (n->next->flags & MAN_LINE))
991 p->flags |= TERMP_NOSPACE;
992 return;
993 case MAN_TBL:
994 /*
995 * Tables are preceded by a newline. Then process a
996 * table line, which will cause line termination,
997 */
998 if (TBL_SPAN_FIRST & n->span->flags)
999 term_newln(p);
1000 term_tbl(p, n->span);
1001 return;
1002 default:
1003 break;
1004 }
1005
1006 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1007 term_fontrepl(p, TERMFONT_NONE);
1008
1009 c = 1;
1010 if (termacts[n->tok].pre)
1011 c = (*termacts[n->tok].pre)(p, mt, n, meta);
1012
1013 if (c && n->child)
1014 print_man_nodelist(p, mt, n->child, meta);
1015
1016 if (termacts[n->tok].post)
1017 (*termacts[n->tok].post)(p, mt, n, meta);
1018 if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1019 term_fontrepl(p, TERMFONT_NONE);
1020
1021 out:
1022 /*
1023 * If we're in a literal context, make sure that words
1024 * together on the same line stay together. This is a
1025 * POST-printing call, so we check the NEXT word. Since
1026 * -man doesn't have nested macros, we don't need to be
1027 * more specific than this.
1028 */
1029 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1030 (NULL == n->next || MAN_LINE & n->next->flags)) {
1031 rm = p->rmargin;
1032 rmax = p->maxrmargin;
1033 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1034 p->flags |= TERMP_NOSPACE;
1035 if (NULL != n->string && '\0' != *n->string)
1036 term_flushln(p);
1037 else
1038 term_newln(p);
1039 if (rm < rmax && n->parent->tok == MAN_HP) {
1040 p->offset = rm;
1041 p->rmargin = rmax;
1042 } else
1043 p->rmargin = rm;
1044 p->maxrmargin = rmax;
1045 }
1046 if (MAN_EOS & n->flags)
1047 p->flags |= TERMP_SENTENCE;
1048 }
1049
1050
1051 static void
1052 print_man_nodelist(DECL_ARGS)
1053 {
1054
1055 print_man_node(p, mt, n, meta);
1056 if ( ! n->next)
1057 return;
1058 print_man_nodelist(p, mt, n->next, meta);
1059 }
1060
1061 static void
1062 print_man_foot(struct termp *p, const void *arg)
1063 {
1064 const struct man_meta *meta;
1065 char *title;
1066 size_t datelen;
1067
1068 meta = (const struct man_meta *)arg;
1069 assert(meta->title);
1070 assert(meta->msec);
1071 assert(meta->date);
1072
1073 term_fontrepl(p, TERMFONT_NONE);
1074
1075 if (meta->hasbody)
1076 term_vspace(p);
1077
1078 /*
1079 * Temporary, undocumented option to imitate mdoc(7) output.
1080 * In the bottom right corner, use the source instead of
1081 * the title.
1082 */
1083
1084 if ( ! p->mdocstyle) {
1085 if (meta->hasbody) {
1086 term_vspace(p);
1087 term_vspace(p);
1088 }
1089 mandoc_asprintf(&title, "%s(%s)",
1090 meta->title, meta->msec);
1091 } else if (meta->source) {
1092 title = mandoc_strdup(meta->source);
1093 } else {
1094 title = mandoc_strdup("");
1095 }
1096 datelen = term_strlen(p, meta->date);
1097
1098 /* Bottom left corner: manual source. */
1099
1100 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1101 p->trailspace = 1;
1102 p->offset = 0;
1103 p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1104
1105 if (meta->source)
1106 term_word(p, meta->source);
1107 term_flushln(p);
1108
1109 /* At the bottom in the middle: manual date. */
1110
1111 p->flags |= TERMP_NOSPACE;
1112 p->offset = p->rmargin;
1113 p->rmargin = p->maxrmargin - term_strlen(p, title);
1114 if (p->offset + datelen >= p->rmargin)
1115 p->rmargin = p->offset + datelen;
1116
1117 term_word(p, meta->date);
1118 term_flushln(p);
1119
1120 /* Bottom right corner: manual title and section. */
1121
1122 p->flags &= ~TERMP_NOBREAK;
1123 p->flags |= TERMP_NOSPACE;
1124 p->trailspace = 0;
1125 p->offset = p->rmargin;
1126 p->rmargin = p->maxrmargin;
1127
1128 term_word(p, title);
1129 term_flushln(p);
1130 free(title);
1131 }
1132
1133 static void
1134 print_man_head(struct termp *p, const void *arg)
1135 {
1136 const struct man_meta *meta;
1137 const char *volume;
1138 char *title;
1139 size_t vollen, titlen;
1140
1141 meta = (const struct man_meta *)arg;
1142 assert(meta->title);
1143 assert(meta->msec);
1144
1145 volume = NULL == meta->vol ? "" : meta->vol;
1146 vollen = term_strlen(p, volume);
1147
1148 /* Top left corner: manual title and section. */
1149
1150 mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1151 titlen = term_strlen(p, title);
1152
1153 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1154 p->trailspace = 1;
1155 p->offset = 0;
1156 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1157 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1158 p->maxrmargin - vollen;
1159
1160 term_word(p, title);
1161 term_flushln(p);
1162
1163 /* At the top in the middle: manual volume. */
1164
1165 p->flags |= TERMP_NOSPACE;
1166 p->offset = p->rmargin;
1167 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1168 p->maxrmargin - titlen : p->maxrmargin;
1169
1170 term_word(p, volume);
1171 term_flushln(p);
1172
1173 /* Top right corner: title and section, again. */
1174
1175 p->flags &= ~TERMP_NOBREAK;
1176 p->trailspace = 0;
1177 if (p->rmargin + titlen <= p->maxrmargin) {
1178 p->flags |= TERMP_NOSPACE;
1179 p->offset = p->rmargin;
1180 p->rmargin = p->maxrmargin;
1181 term_word(p, title);
1182 term_flushln(p);
1183 }
1184
1185 p->flags &= ~TERMP_NOSPACE;
1186 p->offset = 0;
1187 p->rmargin = p->maxrmargin;
1188
1189 /*
1190 * Groff prints three blank lines before the content.
1191 * Do the same, except in the temporary, undocumented
1192 * mode imitating mdoc(7) output.
1193 */
1194
1195 term_vspace(p);
1196 if ( ! p->mdocstyle) {
1197 term_vspace(p);
1198 term_vspace(p);
1199 }
1200 free(title);
1201 }