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