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