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