]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
Finally port the OpenBSD regression suite.
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.343 2017/02/06 03:44:58 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include "config.h"
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <limits.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "mandoc_aux.h"
32 #include "mandoc.h"
33 #include "roff.h"
34 #include "mdoc.h"
35 #include "out.h"
36 #include "term.h"
37 #include "tag.h"
38 #include "main.h"
39
40 struct termpair {
41 struct termpair *ppair;
42 int count;
43 };
44
45 #define DECL_ARGS struct termp *p, \
46 struct termpair *pair, \
47 const struct roff_meta *meta, \
48 struct roff_node *n
49
50 struct termact {
51 int (*pre)(DECL_ARGS);
52 void (*post)(DECL_ARGS);
53 };
54
55 static int a2width(const struct termp *, const char *);
56
57 static void print_bvspace(struct termp *,
58 const struct roff_node *,
59 const struct roff_node *);
60 static void print_mdoc_node(DECL_ARGS);
61 static void print_mdoc_nodelist(DECL_ARGS);
62 static void print_mdoc_head(struct termp *, const struct roff_meta *);
63 static void print_mdoc_foot(struct termp *, const struct roff_meta *);
64 static void synopsis_pre(struct termp *,
65 const struct roff_node *);
66
67 static void termp____post(DECL_ARGS);
68 static void termp__t_post(DECL_ARGS);
69 static void termp_bd_post(DECL_ARGS);
70 static void termp_bk_post(DECL_ARGS);
71 static void termp_bl_post(DECL_ARGS);
72 static void termp_eo_post(DECL_ARGS);
73 static void termp_fd_post(DECL_ARGS);
74 static void termp_fo_post(DECL_ARGS);
75 static void termp_in_post(DECL_ARGS);
76 static void termp_it_post(DECL_ARGS);
77 static void termp_lb_post(DECL_ARGS);
78 static void termp_nm_post(DECL_ARGS);
79 static void termp_pf_post(DECL_ARGS);
80 static void termp_quote_post(DECL_ARGS);
81 static void termp_sh_post(DECL_ARGS);
82 static void termp_ss_post(DECL_ARGS);
83 static void termp_xx_post(DECL_ARGS);
84
85 static int termp__a_pre(DECL_ARGS);
86 static int termp__t_pre(DECL_ARGS);
87 static int termp_an_pre(DECL_ARGS);
88 static int termp_ap_pre(DECL_ARGS);
89 static int termp_bd_pre(DECL_ARGS);
90 static int termp_bf_pre(DECL_ARGS);
91 static int termp_bk_pre(DECL_ARGS);
92 static int termp_bl_pre(DECL_ARGS);
93 static int termp_bold_pre(DECL_ARGS);
94 static int termp_cd_pre(DECL_ARGS);
95 static int termp_d1_pre(DECL_ARGS);
96 static int termp_eo_pre(DECL_ARGS);
97 static int termp_em_pre(DECL_ARGS);
98 static int termp_er_pre(DECL_ARGS);
99 static int termp_ex_pre(DECL_ARGS);
100 static int termp_fa_pre(DECL_ARGS);
101 static int termp_fd_pre(DECL_ARGS);
102 static int termp_fl_pre(DECL_ARGS);
103 static int termp_fn_pre(DECL_ARGS);
104 static int termp_fo_pre(DECL_ARGS);
105 static int termp_ft_pre(DECL_ARGS);
106 static int termp_in_pre(DECL_ARGS);
107 static int termp_it_pre(DECL_ARGS);
108 static int termp_li_pre(DECL_ARGS);
109 static int termp_ll_pre(DECL_ARGS);
110 static int termp_lk_pre(DECL_ARGS);
111 static int termp_nd_pre(DECL_ARGS);
112 static int termp_nm_pre(DECL_ARGS);
113 static int termp_ns_pre(DECL_ARGS);
114 static int termp_quote_pre(DECL_ARGS);
115 static int termp_rs_pre(DECL_ARGS);
116 static int termp_sh_pre(DECL_ARGS);
117 static int termp_skip_pre(DECL_ARGS);
118 static int termp_sm_pre(DECL_ARGS);
119 static int termp_sp_pre(DECL_ARGS);
120 static int termp_ss_pre(DECL_ARGS);
121 static int termp_sy_pre(DECL_ARGS);
122 static int termp_tag_pre(DECL_ARGS);
123 static int termp_under_pre(DECL_ARGS);
124 static int termp_vt_pre(DECL_ARGS);
125 static int termp_xr_pre(DECL_ARGS);
126 static int termp_xx_pre(DECL_ARGS);
127
128 static const struct termact termacts[MDOC_MAX] = {
129 { termp_ap_pre, NULL }, /* Ap */
130 { NULL, NULL }, /* Dd */
131 { NULL, NULL }, /* Dt */
132 { NULL, NULL }, /* Os */
133 { termp_sh_pre, termp_sh_post }, /* Sh */
134 { termp_ss_pre, termp_ss_post }, /* Ss */
135 { termp_sp_pre, NULL }, /* Pp */
136 { termp_d1_pre, termp_bl_post }, /* D1 */
137 { termp_d1_pre, termp_bl_post }, /* Dl */
138 { termp_bd_pre, termp_bd_post }, /* Bd */
139 { NULL, NULL }, /* Ed */
140 { termp_bl_pre, termp_bl_post }, /* Bl */
141 { NULL, NULL }, /* El */
142 { termp_it_pre, termp_it_post }, /* It */
143 { termp_under_pre, NULL }, /* Ad */
144 { termp_an_pre, NULL }, /* An */
145 { termp_under_pre, NULL }, /* Ar */
146 { termp_cd_pre, NULL }, /* Cd */
147 { termp_bold_pre, NULL }, /* Cm */
148 { termp_li_pre, NULL }, /* Dv */
149 { termp_er_pre, NULL }, /* Er */
150 { termp_tag_pre, NULL }, /* Ev */
151 { termp_ex_pre, NULL }, /* Ex */
152 { termp_fa_pre, NULL }, /* Fa */
153 { termp_fd_pre, termp_fd_post }, /* Fd */
154 { termp_fl_pre, NULL }, /* Fl */
155 { termp_fn_pre, NULL }, /* Fn */
156 { termp_ft_pre, NULL }, /* Ft */
157 { termp_bold_pre, NULL }, /* Ic */
158 { termp_in_pre, termp_in_post }, /* In */
159 { termp_li_pre, NULL }, /* Li */
160 { termp_nd_pre, NULL }, /* Nd */
161 { termp_nm_pre, termp_nm_post }, /* Nm */
162 { termp_quote_pre, termp_quote_post }, /* Op */
163 { termp_ft_pre, NULL }, /* Ot */
164 { termp_under_pre, NULL }, /* Pa */
165 { termp_ex_pre, NULL }, /* Rv */
166 { NULL, NULL }, /* St */
167 { termp_under_pre, NULL }, /* Va */
168 { termp_vt_pre, NULL }, /* Vt */
169 { termp_xr_pre, NULL }, /* Xr */
170 { termp__a_pre, termp____post }, /* %A */
171 { termp_under_pre, termp____post }, /* %B */
172 { NULL, termp____post }, /* %D */
173 { termp_under_pre, termp____post }, /* %I */
174 { termp_under_pre, termp____post }, /* %J */
175 { NULL, termp____post }, /* %N */
176 { NULL, termp____post }, /* %O */
177 { NULL, termp____post }, /* %P */
178 { NULL, termp____post }, /* %R */
179 { termp__t_pre, termp__t_post }, /* %T */
180 { NULL, termp____post }, /* %V */
181 { NULL, NULL }, /* Ac */
182 { termp_quote_pre, termp_quote_post }, /* Ao */
183 { termp_quote_pre, termp_quote_post }, /* Aq */
184 { NULL, NULL }, /* At */
185 { NULL, NULL }, /* Bc */
186 { termp_bf_pre, NULL }, /* Bf */
187 { termp_quote_pre, termp_quote_post }, /* Bo */
188 { termp_quote_pre, termp_quote_post }, /* Bq */
189 { termp_xx_pre, termp_xx_post }, /* Bsx */
190 { NULL, NULL }, /* Bx */
191 { termp_skip_pre, NULL }, /* Db */
192 { NULL, NULL }, /* Dc */
193 { termp_quote_pre, termp_quote_post }, /* Do */
194 { termp_quote_pre, termp_quote_post }, /* Dq */
195 { NULL, NULL }, /* Ec */ /* FIXME: no space */
196 { NULL, NULL }, /* Ef */
197 { termp_em_pre, NULL }, /* Em */
198 { termp_eo_pre, termp_eo_post }, /* Eo */
199 { termp_xx_pre, termp_xx_post }, /* Fx */
200 { termp_bold_pre, NULL }, /* Ms */
201 { termp_li_pre, NULL }, /* No */
202 { termp_ns_pre, NULL }, /* Ns */
203 { termp_xx_pre, termp_xx_post }, /* Nx */
204 { termp_xx_pre, termp_xx_post }, /* Ox */
205 { NULL, NULL }, /* Pc */
206 { NULL, termp_pf_post }, /* Pf */
207 { termp_quote_pre, termp_quote_post }, /* Po */
208 { termp_quote_pre, termp_quote_post }, /* Pq */
209 { NULL, NULL }, /* Qc */
210 { termp_quote_pre, termp_quote_post }, /* Ql */
211 { termp_quote_pre, termp_quote_post }, /* Qo */
212 { termp_quote_pre, termp_quote_post }, /* Qq */
213 { NULL, NULL }, /* Re */
214 { termp_rs_pre, NULL }, /* Rs */
215 { NULL, NULL }, /* Sc */
216 { termp_quote_pre, termp_quote_post }, /* So */
217 { termp_quote_pre, termp_quote_post }, /* Sq */
218 { termp_sm_pre, NULL }, /* Sm */
219 { termp_under_pre, NULL }, /* Sx */
220 { termp_sy_pre, NULL }, /* Sy */
221 { NULL, NULL }, /* Tn */
222 { termp_xx_pre, termp_xx_post }, /* Ux */
223 { NULL, NULL }, /* Xc */
224 { NULL, NULL }, /* Xo */
225 { termp_fo_pre, termp_fo_post }, /* Fo */
226 { NULL, NULL }, /* Fc */
227 { termp_quote_pre, termp_quote_post }, /* Oo */
228 { NULL, NULL }, /* Oc */
229 { termp_bk_pre, termp_bk_post }, /* Bk */
230 { NULL, NULL }, /* Ek */
231 { NULL, NULL }, /* Bt */
232 { NULL, NULL }, /* Hf */
233 { termp_under_pre, NULL }, /* Fr */
234 { NULL, NULL }, /* Ud */
235 { NULL, termp_lb_post }, /* Lb */
236 { termp_sp_pre, NULL }, /* Lp */
237 { termp_lk_pre, NULL }, /* Lk */
238 { termp_under_pre, NULL }, /* Mt */
239 { termp_quote_pre, termp_quote_post }, /* Brq */
240 { termp_quote_pre, termp_quote_post }, /* Bro */
241 { NULL, NULL }, /* Brc */
242 { NULL, termp____post }, /* %C */
243 { termp_skip_pre, NULL }, /* Es */
244 { termp_quote_pre, termp_quote_post }, /* En */
245 { termp_xx_pre, termp_xx_post }, /* Dx */
246 { NULL, termp____post }, /* %Q */
247 { termp_sp_pre, NULL }, /* br */
248 { termp_sp_pre, NULL }, /* sp */
249 { NULL, termp____post }, /* %U */
250 { NULL, NULL }, /* Ta */
251 { termp_ll_pre, NULL }, /* ll */
252 };
253
254 static int fn_prio;
255
256 void
257 terminal_mdoc(void *arg, const struct roff_man *mdoc)
258 {
259 struct roff_node *n;
260 struct termp *p;
261 size_t save_defindent;
262
263 p = (struct termp *)arg;
264 p->overstep = 0;
265 p->rmargin = p->maxrmargin = p->defrmargin;
266 p->tabwidth = term_len(p, 5);
267
268 n = mdoc->first->child;
269 if (p->synopsisonly) {
270 while (n != NULL) {
271 if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
272 if (n->child->next->child != NULL)
273 print_mdoc_nodelist(p, NULL,
274 &mdoc->meta,
275 n->child->next->child);
276 term_newln(p);
277 break;
278 }
279 n = n->next;
280 }
281 } else {
282 save_defindent = p->defindent;
283 if (p->defindent == 0)
284 p->defindent = 5;
285 term_begin(p, print_mdoc_head, print_mdoc_foot,
286 &mdoc->meta);
287 while (n != NULL && n->flags & NODE_NOPRT)
288 n = n->next;
289 if (n != NULL) {
290 if (n->tok != MDOC_Sh)
291 term_vspace(p);
292 print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
293 }
294 term_end(p);
295 p->defindent = save_defindent;
296 }
297 }
298
299 static void
300 print_mdoc_nodelist(DECL_ARGS)
301 {
302
303 while (n != NULL) {
304 print_mdoc_node(p, pair, meta, n);
305 n = n->next;
306 }
307 }
308
309 static void
310 print_mdoc_node(DECL_ARGS)
311 {
312 int chld;
313 struct termpair npair;
314 size_t offset, rmargin;
315
316 if (n->flags & NODE_NOPRT)
317 return;
318
319 chld = 1;
320 offset = p->offset;
321 rmargin = p->rmargin;
322 n->flags &= ~NODE_ENDED;
323 n->prev_font = p->fonti;
324
325 memset(&npair, 0, sizeof(struct termpair));
326 npair.ppair = pair;
327
328 /*
329 * Keeps only work until the end of a line. If a keep was
330 * invoked in a prior line, revert it to PREKEEP.
331 */
332
333 if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
334 p->flags &= ~TERMP_KEEP;
335 p->flags |= TERMP_PREKEEP;
336 }
337
338 /*
339 * After the keep flags have been set up, we may now
340 * produce output. Note that some pre-handlers do so.
341 */
342
343 switch (n->type) {
344 case ROFFT_TEXT:
345 if (' ' == *n->string && NODE_LINE & n->flags)
346 term_newln(p);
347 if (NODE_DELIMC & n->flags)
348 p->flags |= TERMP_NOSPACE;
349 term_word(p, n->string);
350 if (NODE_DELIMO & n->flags)
351 p->flags |= TERMP_NOSPACE;
352 break;
353 case ROFFT_EQN:
354 if ( ! (n->flags & NODE_LINE))
355 p->flags |= TERMP_NOSPACE;
356 term_eqn(p, n->eqn);
357 if (n->next != NULL && ! (n->next->flags & NODE_LINE))
358 p->flags |= TERMP_NOSPACE;
359 break;
360 case ROFFT_TBL:
361 if (p->tbl.cols == NULL)
362 term_newln(p);
363 term_tbl(p, n->span);
364 break;
365 default:
366 if (termacts[n->tok].pre &&
367 (n->end == ENDBODY_NOT || n->child != NULL))
368 chld = (*termacts[n->tok].pre)
369 (p, &npair, meta, n);
370 break;
371 }
372
373 if (chld && n->child)
374 print_mdoc_nodelist(p, &npair, meta, n->child);
375
376 term_fontpopq(p,
377 (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
378
379 switch (n->type) {
380 case ROFFT_TEXT:
381 break;
382 case ROFFT_TBL:
383 break;
384 case ROFFT_EQN:
385 break;
386 default:
387 if ( ! termacts[n->tok].post || NODE_ENDED & n->flags)
388 break;
389 (void)(*termacts[n->tok].post)(p, &npair, meta, n);
390
391 /*
392 * Explicit end tokens not only call the post
393 * handler, but also tell the respective block
394 * that it must not call the post handler again.
395 */
396 if (ENDBODY_NOT != n->end)
397 n->body->flags |= NODE_ENDED;
398
399 /*
400 * End of line terminating an implicit block
401 * while an explicit block is still open.
402 * Continue the explicit block without spacing.
403 */
404 if (ENDBODY_NOSPACE == n->end)
405 p->flags |= TERMP_NOSPACE;
406 break;
407 }
408
409 if (NODE_EOS & n->flags)
410 p->flags |= TERMP_SENTENCE;
411
412 if (MDOC_ll != n->tok) {
413 p->offset = offset;
414 p->rmargin = rmargin;
415 }
416 }
417
418 static void
419 print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
420 {
421 size_t sz;
422
423 term_fontrepl(p, TERMFONT_NONE);
424
425 /*
426 * Output the footer in new-groff style, that is, three columns
427 * with the middle being the manual date and flanking columns
428 * being the operating system:
429 *
430 * SYSTEM DATE SYSTEM
431 */
432
433 term_vspace(p);
434
435 p->offset = 0;
436 sz = term_strlen(p, meta->date);
437 p->rmargin = p->maxrmargin > sz ?
438 (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
439 p->trailspace = 1;
440 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
441
442 term_word(p, meta->os);
443 term_flushln(p);
444
445 p->offset = p->rmargin;
446 sz = term_strlen(p, meta->os);
447 p->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
448 p->flags |= TERMP_NOSPACE;
449
450 term_word(p, meta->date);
451 term_flushln(p);
452
453 p->offset = p->rmargin;
454 p->rmargin = p->maxrmargin;
455 p->trailspace = 0;
456 p->flags &= ~TERMP_NOBREAK;
457 p->flags |= TERMP_NOSPACE;
458
459 term_word(p, meta->os);
460 term_flushln(p);
461
462 p->offset = 0;
463 p->rmargin = p->maxrmargin;
464 p->flags = 0;
465 }
466
467 static void
468 print_mdoc_head(struct termp *p, const struct roff_meta *meta)
469 {
470 char *volume, *title;
471 size_t vollen, titlen;
472
473 /*
474 * The header is strange. It has three components, which are
475 * really two with the first duplicated. It goes like this:
476 *
477 * IDENTIFIER TITLE IDENTIFIER
478 *
479 * The IDENTIFIER is NAME(SECTION), which is the command-name
480 * (if given, or "unknown" if not) followed by the manual page
481 * section. These are given in `Dt'. The TITLE is a free-form
482 * string depending on the manual volume. If not specified, it
483 * switches on the manual section.
484 */
485
486 assert(meta->vol);
487 if (NULL == meta->arch)
488 volume = mandoc_strdup(meta->vol);
489 else
490 mandoc_asprintf(&volume, "%s (%s)",
491 meta->vol, meta->arch);
492 vollen = term_strlen(p, volume);
493
494 if (NULL == meta->msec)
495 title = mandoc_strdup(meta->title);
496 else
497 mandoc_asprintf(&title, "%s(%s)",
498 meta->title, meta->msec);
499 titlen = term_strlen(p, title);
500
501 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
502 p->trailspace = 1;
503 p->offset = 0;
504 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
505 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
506 vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
507
508 term_word(p, title);
509 term_flushln(p);
510
511 p->flags |= TERMP_NOSPACE;
512 p->offset = p->rmargin;
513 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
514 p->maxrmargin - titlen : p->maxrmargin;
515
516 term_word(p, volume);
517 term_flushln(p);
518
519 p->flags &= ~TERMP_NOBREAK;
520 p->trailspace = 0;
521 if (p->rmargin + titlen <= p->maxrmargin) {
522 p->flags |= TERMP_NOSPACE;
523 p->offset = p->rmargin;
524 p->rmargin = p->maxrmargin;
525 term_word(p, title);
526 term_flushln(p);
527 }
528
529 p->flags &= ~TERMP_NOSPACE;
530 p->offset = 0;
531 p->rmargin = p->maxrmargin;
532 free(title);
533 free(volume);
534 }
535
536 static int
537 a2width(const struct termp *p, const char *v)
538 {
539 struct roffsu su;
540
541 if (a2roffsu(v, &su, SCALE_MAX) < 2) {
542 SCALE_HS_INIT(&su, term_strlen(p, v));
543 su.scale /= term_strlen(p, "0");
544 }
545 return term_hspan(p, &su) / 24;
546 }
547
548 /*
549 * Determine how much space to print out before block elements of `It'
550 * (and thus `Bl') and `Bd'. And then go ahead and print that space,
551 * too.
552 */
553 static void
554 print_bvspace(struct termp *p,
555 const struct roff_node *bl,
556 const struct roff_node *n)
557 {
558 const struct roff_node *nn;
559
560 assert(n);
561
562 term_newln(p);
563
564 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
565 return;
566 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
567 return;
568
569 /* Do not vspace directly after Ss/Sh. */
570
571 nn = n;
572 while (nn->prev != NULL && nn->prev->flags & NODE_NOPRT)
573 nn = nn->prev;
574 while (nn->prev == NULL) {
575 do {
576 nn = nn->parent;
577 if (nn->type == ROFFT_ROOT)
578 return;
579 } while (nn->type != ROFFT_BLOCK);
580 if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
581 return;
582 if (nn->tok == MDOC_It &&
583 nn->parent->parent->norm->Bl.type != LIST_item)
584 break;
585 }
586
587 /* A `-column' does not assert vspace within the list. */
588
589 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
590 if (n->prev && MDOC_It == n->prev->tok)
591 return;
592
593 /* A `-diag' without body does not vspace. */
594
595 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
596 if (n->prev && MDOC_It == n->prev->tok) {
597 assert(n->prev->body);
598 if (NULL == n->prev->body->child)
599 return;
600 }
601
602 term_vspace(p);
603 }
604
605
606 static int
607 termp_ll_pre(DECL_ARGS)
608 {
609
610 term_setwidth(p, n->child != NULL ? n->child->string : NULL);
611 return 0;
612 }
613
614 static int
615 termp_it_pre(DECL_ARGS)
616 {
617 char buf[24];
618 const struct roff_node *bl, *nn;
619 size_t ncols, dcol;
620 int i, offset, width;
621 enum mdoc_list type;
622
623 if (n->type == ROFFT_BLOCK) {
624 print_bvspace(p, n->parent->parent, n);
625 return 1;
626 }
627
628 bl = n->parent->parent->parent;
629 type = bl->norm->Bl.type;
630
631 /*
632 * Defaults for specific list types.
633 */
634
635 switch (type) {
636 case LIST_bullet:
637 case LIST_dash:
638 case LIST_hyphen:
639 case LIST_enum:
640 width = term_len(p, 2);
641 break;
642 case LIST_hang:
643 case LIST_tag:
644 width = term_len(p, 8);
645 break;
646 case LIST_column:
647 width = term_len(p, 10);
648 break;
649 default:
650 width = 0;
651 break;
652 }
653 offset = 0;
654
655 /*
656 * First calculate width and offset. This is pretty easy unless
657 * we're a -column list, in which case all prior columns must
658 * be accounted for.
659 */
660
661 if (bl->norm->Bl.offs != NULL) {
662 offset = a2width(p, bl->norm->Bl.offs);
663 if (offset < 0 && (size_t)(-offset) > p->offset)
664 offset = -p->offset;
665 else if (offset > SHRT_MAX)
666 offset = 0;
667 }
668
669 switch (type) {
670 case LIST_column:
671 if (n->type == ROFFT_HEAD)
672 break;
673
674 /*
675 * Imitate groff's column handling:
676 * - For each earlier column, add its width.
677 * - For less than 5 columns, add four more blanks per
678 * column.
679 * - For exactly 5 columns, add three more blank per
680 * column.
681 * - For more than 5 columns, add only one column.
682 */
683 ncols = bl->norm->Bl.ncols;
684 dcol = ncols < 5 ? term_len(p, 4) :
685 ncols == 5 ? term_len(p, 3) : term_len(p, 1);
686
687 /*
688 * Calculate the offset by applying all prior ROFFT_BODY,
689 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
690 */
691
692 for (i = 0, nn = n->prev;
693 nn->prev && i < (int)ncols;
694 nn = nn->prev, i++)
695 offset += dcol + a2width(p,
696 bl->norm->Bl.cols[i]);
697
698 /*
699 * When exceeding the declared number of columns, leave
700 * the remaining widths at 0. This will later be
701 * adjusted to the default width of 10, or, for the last
702 * column, stretched to the right margin.
703 */
704 if (i >= (int)ncols)
705 break;
706
707 /*
708 * Use the declared column widths, extended as explained
709 * in the preceding paragraph.
710 */
711 width = a2width(p, bl->norm->Bl.cols[i]) + dcol;
712 break;
713 default:
714 if (NULL == bl->norm->Bl.width)
715 break;
716
717 /*
718 * Note: buffer the width by 2, which is groff's magic
719 * number for buffering single arguments. See the above
720 * handling for column for how this changes.
721 */
722 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
723 if (width < 0 && (size_t)(-width) > p->offset)
724 width = -p->offset;
725 else if (width > SHRT_MAX)
726 width = 0;
727 break;
728 }
729
730 /*
731 * Whitespace control. Inset bodies need an initial space,
732 * while diagonal bodies need two.
733 */
734
735 p->flags |= TERMP_NOSPACE;
736
737 switch (type) {
738 case LIST_diag:
739 if (n->type == ROFFT_BODY)
740 term_word(p, "\\ \\ ");
741 break;
742 case LIST_inset:
743 if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
744 term_word(p, "\\ ");
745 break;
746 default:
747 break;
748 }
749
750 p->flags |= TERMP_NOSPACE;
751
752 switch (type) {
753 case LIST_diag:
754 if (n->type == ROFFT_HEAD)
755 term_fontpush(p, TERMFONT_BOLD);
756 break;
757 default:
758 break;
759 }
760
761 /*
762 * Pad and break control. This is the tricky part. These flags
763 * are documented in term_flushln() in term.c. Note that we're
764 * going to unset all of these flags in termp_it_post() when we
765 * exit.
766 */
767
768 switch (type) {
769 case LIST_enum:
770 case LIST_bullet:
771 case LIST_dash:
772 case LIST_hyphen:
773 /*
774 * Weird special case.
775 * Some very narrow lists actually hang.
776 */
777 if (width <= (int)term_len(p, 2))
778 p->flags |= TERMP_HANG;
779 if (n->type != ROFFT_HEAD)
780 break;
781 p->flags |= TERMP_NOBREAK;
782 p->trailspace = 1;
783 break;
784 case LIST_hang:
785 if (n->type != ROFFT_HEAD)
786 break;
787
788 /*
789 * This is ugly. If `-hang' is specified and the body
790 * is a `Bl' or `Bd', then we want basically to nullify
791 * the "overstep" effect in term_flushln() and treat
792 * this as a `-ohang' list instead.
793 */
794 if (NULL != n->next &&
795 NULL != n->next->child &&
796 (MDOC_Bl == n->next->child->tok ||
797 MDOC_Bd == n->next->child->tok))
798 break;
799
800 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
801 p->trailspace = 1;
802 break;
803 case LIST_tag:
804 if (n->type != ROFFT_HEAD)
805 break;
806
807 p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
808 p->trailspace = 2;
809
810 if (NULL == n->next || NULL == n->next->child)
811 p->flags |= TERMP_DANGLE;
812 break;
813 case LIST_column:
814 if (n->type == ROFFT_HEAD)
815 break;
816
817 if (NULL == n->next) {
818 p->flags &= ~TERMP_NOBREAK;
819 p->trailspace = 0;
820 } else {
821 p->flags |= TERMP_NOBREAK;
822 p->trailspace = 1;
823 }
824
825 break;
826 case LIST_diag:
827 if (n->type != ROFFT_HEAD)
828 break;
829 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
830 p->trailspace = 1;
831 break;
832 default:
833 break;
834 }
835
836 /*
837 * Margin control. Set-head-width lists have their right
838 * margins shortened. The body for these lists has the offset
839 * necessarily lengthened. Everybody gets the offset.
840 */
841
842 p->offset += offset;
843
844 switch (type) {
845 case LIST_hang:
846 /*
847 * Same stipulation as above, regarding `-hang'. We
848 * don't want to recalculate rmargin and offsets when
849 * using `Bd' or `Bl' within `-hang' overstep lists.
850 */
851 if (n->type == ROFFT_HEAD &&
852 NULL != n->next &&
853 NULL != n->next->child &&
854 (MDOC_Bl == n->next->child->tok ||
855 MDOC_Bd == n->next->child->tok))
856 break;
857 /* FALLTHROUGH */
858 case LIST_bullet:
859 case LIST_dash:
860 case LIST_enum:
861 case LIST_hyphen:
862 case LIST_tag:
863 if (n->type == ROFFT_HEAD)
864 p->rmargin = p->offset + width;
865 else
866 p->offset += width;
867 break;
868 case LIST_column:
869 assert(width);
870 p->rmargin = p->offset + width;
871 /*
872 * XXX - this behaviour is not documented: the
873 * right-most column is filled to the right margin.
874 */
875 if (n->type == ROFFT_HEAD)
876 break;
877 if (NULL == n->next && p->rmargin < p->maxrmargin)
878 p->rmargin = p->maxrmargin;
879 break;
880 default:
881 break;
882 }
883
884 /*
885 * The dash, hyphen, bullet and enum lists all have a special
886 * HEAD character (temporarily bold, in some cases).
887 */
888
889 if (n->type == ROFFT_HEAD)
890 switch (type) {
891 case LIST_bullet:
892 term_fontpush(p, TERMFONT_BOLD);
893 term_word(p, "\\[bu]");
894 term_fontpop(p);
895 break;
896 case LIST_dash:
897 case LIST_hyphen:
898 term_fontpush(p, TERMFONT_BOLD);
899 term_word(p, "-");
900 term_fontpop(p);
901 break;
902 case LIST_enum:
903 (pair->ppair->ppair->count)++;
904 (void)snprintf(buf, sizeof(buf), "%d.",
905 pair->ppair->ppair->count);
906 term_word(p, buf);
907 break;
908 default:
909 break;
910 }
911
912 /*
913 * If we're not going to process our children, indicate so here.
914 */
915
916 switch (type) {
917 case LIST_bullet:
918 case LIST_item:
919 case LIST_dash:
920 case LIST_hyphen:
921 case LIST_enum:
922 if (n->type == ROFFT_HEAD)
923 return 0;
924 break;
925 case LIST_column:
926 if (n->type == ROFFT_HEAD)
927 return 0;
928 break;
929 default:
930 break;
931 }
932
933 return 1;
934 }
935
936 static void
937 termp_it_post(DECL_ARGS)
938 {
939 enum mdoc_list type;
940
941 if (n->type == ROFFT_BLOCK)
942 return;
943
944 type = n->parent->parent->parent->norm->Bl.type;
945
946 switch (type) {
947 case LIST_item:
948 case LIST_diag:
949 case LIST_inset:
950 if (n->type == ROFFT_BODY)
951 term_newln(p);
952 break;
953 case LIST_column:
954 if (n->type == ROFFT_BODY)
955 term_flushln(p);
956 break;
957 default:
958 term_newln(p);
959 break;
960 }
961
962 /*
963 * Now that our output is flushed, we can reset our tags. Since
964 * only `It' sets these flags, we're free to assume that nobody
965 * has munged them in the meanwhile.
966 */
967
968 p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND |
969 TERMP_DANGLE | TERMP_HANG);
970 p->trailspace = 0;
971 }
972
973 static int
974 termp_nm_pre(DECL_ARGS)
975 {
976 const char *cp;
977
978 if (n->type == ROFFT_BLOCK) {
979 p->flags |= TERMP_PREKEEP;
980 return 1;
981 }
982
983 if (n->type == ROFFT_BODY) {
984 if (NULL == n->child)
985 return 0;
986 p->flags |= TERMP_NOSPACE;
987 cp = NULL;
988 if (n->prev->child != NULL)
989 cp = n->prev->child->string;
990 if (cp == NULL)
991 cp = meta->name;
992 if (cp == NULL)
993 p->offset += term_len(p, 6);
994 else
995 p->offset += term_len(p, 1) + term_strlen(p, cp);
996 return 1;
997 }
998
999 if (n->child == NULL)
1000 return 0;
1001
1002 if (n->type == ROFFT_HEAD)
1003 synopsis_pre(p, n->parent);
1004
1005 if (n->type == ROFFT_HEAD &&
1006 NULL != n->next && NULL != n->next->child) {
1007 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
1008 p->trailspace = 1;
1009 p->rmargin = p->offset + term_len(p, 1);
1010 if (NULL == n->child) {
1011 p->rmargin += term_strlen(p, meta->name);
1012 } else if (n->child->type == ROFFT_TEXT) {
1013 p->rmargin += term_strlen(p, n->child->string);
1014 if (n->child->next)
1015 p->flags |= TERMP_HANG;
1016 } else {
1017 p->rmargin += term_len(p, 5);
1018 p->flags |= TERMP_HANG;
1019 }
1020 }
1021
1022 term_fontpush(p, TERMFONT_BOLD);
1023 return 1;
1024 }
1025
1026 static void
1027 termp_nm_post(DECL_ARGS)
1028 {
1029
1030 if (n->type == ROFFT_BLOCK) {
1031 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1032 } else if (n->type == ROFFT_HEAD &&
1033 NULL != n->next && NULL != n->next->child) {
1034 term_flushln(p);
1035 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1036 p->trailspace = 0;
1037 } else if (n->type == ROFFT_BODY && n->child != NULL)
1038 term_flushln(p);
1039 }
1040
1041 static int
1042 termp_fl_pre(DECL_ARGS)
1043 {
1044
1045 termp_tag_pre(p, pair, meta, n);
1046 term_fontpush(p, TERMFONT_BOLD);
1047 term_word(p, "\\-");
1048
1049 if (!(n->child == NULL &&
1050 (n->next == NULL ||
1051 n->next->type == ROFFT_TEXT ||
1052 n->next->flags & NODE_LINE)))
1053 p->flags |= TERMP_NOSPACE;
1054
1055 return 1;
1056 }
1057
1058 static int
1059 termp__a_pre(DECL_ARGS)
1060 {
1061
1062 if (n->prev && MDOC__A == n->prev->tok)
1063 if (NULL == n->next || MDOC__A != n->next->tok)
1064 term_word(p, "and");
1065
1066 return 1;
1067 }
1068
1069 static int
1070 termp_an_pre(DECL_ARGS)
1071 {
1072
1073 if (n->norm->An.auth == AUTH_split) {
1074 p->flags &= ~TERMP_NOSPLIT;
1075 p->flags |= TERMP_SPLIT;
1076 return 0;
1077 }
1078 if (n->norm->An.auth == AUTH_nosplit) {
1079 p->flags &= ~TERMP_SPLIT;
1080 p->flags |= TERMP_NOSPLIT;
1081 return 0;
1082 }
1083
1084 if (p->flags & TERMP_SPLIT)
1085 term_newln(p);
1086
1087 if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
1088 p->flags |= TERMP_SPLIT;
1089
1090 return 1;
1091 }
1092
1093 static int
1094 termp_ns_pre(DECL_ARGS)
1095 {
1096
1097 if ( ! (NODE_LINE & n->flags))
1098 p->flags |= TERMP_NOSPACE;
1099 return 1;
1100 }
1101
1102 static int
1103 termp_rs_pre(DECL_ARGS)
1104 {
1105
1106 if (SEC_SEE_ALSO != n->sec)
1107 return 1;
1108 if (n->type == ROFFT_BLOCK && n->prev != NULL)
1109 term_vspace(p);
1110 return 1;
1111 }
1112
1113 static int
1114 termp_ex_pre(DECL_ARGS)
1115 {
1116 term_newln(p);
1117 return 1;
1118 }
1119
1120 static int
1121 termp_nd_pre(DECL_ARGS)
1122 {
1123
1124 if (n->type == ROFFT_BODY)
1125 term_word(p, "\\(en");
1126 return 1;
1127 }
1128
1129 static int
1130 termp_bl_pre(DECL_ARGS)
1131 {
1132
1133 return n->type != ROFFT_HEAD;
1134 }
1135
1136 static void
1137 termp_bl_post(DECL_ARGS)
1138 {
1139
1140 if (n->type == ROFFT_BLOCK)
1141 term_newln(p);
1142 }
1143
1144 static int
1145 termp_xr_pre(DECL_ARGS)
1146 {
1147
1148 if (NULL == (n = n->child))
1149 return 0;
1150
1151 assert(n->type == ROFFT_TEXT);
1152 term_word(p, n->string);
1153
1154 if (NULL == (n = n->next))
1155 return 0;
1156
1157 p->flags |= TERMP_NOSPACE;
1158 term_word(p, "(");
1159 p->flags |= TERMP_NOSPACE;
1160
1161 assert(n->type == ROFFT_TEXT);
1162 term_word(p, n->string);
1163
1164 p->flags |= TERMP_NOSPACE;
1165 term_word(p, ")");
1166
1167 return 0;
1168 }
1169
1170 /*
1171 * This decides how to assert whitespace before any of the SYNOPSIS set
1172 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
1173 * macro combos).
1174 */
1175 static void
1176 synopsis_pre(struct termp *p, const struct roff_node *n)
1177 {
1178 /*
1179 * Obviously, if we're not in a SYNOPSIS or no prior macros
1180 * exist, do nothing.
1181 */
1182 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
1183 return;
1184
1185 /*
1186 * If we're the second in a pair of like elements, emit our
1187 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
1188 * case we soldier on.
1189 */
1190 if (n->prev->tok == n->tok &&
1191 MDOC_Ft != n->tok &&
1192 MDOC_Fo != n->tok &&
1193 MDOC_Fn != n->tok) {
1194 term_newln(p);
1195 return;
1196 }
1197
1198 /*
1199 * If we're one of the SYNOPSIS set and non-like pair-wise after
1200 * another (or Fn/Fo, which we've let slip through) then assert
1201 * vertical space, else only newline and move on.
1202 */
1203 switch (n->prev->tok) {
1204 case MDOC_Fd:
1205 case MDOC_Fn:
1206 case MDOC_Fo:
1207 case MDOC_In:
1208 case MDOC_Vt:
1209 term_vspace(p);
1210 break;
1211 case MDOC_Ft:
1212 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1213 term_vspace(p);
1214 break;
1215 }
1216 /* FALLTHROUGH */
1217 default:
1218 term_newln(p);
1219 break;
1220 }
1221 }
1222
1223 static int
1224 termp_vt_pre(DECL_ARGS)
1225 {
1226
1227 if (n->type == ROFFT_ELEM) {
1228 synopsis_pre(p, n);
1229 return termp_under_pre(p, pair, meta, n);
1230 } else if (n->type == ROFFT_BLOCK) {
1231 synopsis_pre(p, n);
1232 return 1;
1233 } else if (n->type == ROFFT_HEAD)
1234 return 0;
1235
1236 return termp_under_pre(p, pair, meta, n);
1237 }
1238
1239 static int
1240 termp_bold_pre(DECL_ARGS)
1241 {
1242
1243 termp_tag_pre(p, pair, meta, n);
1244 term_fontpush(p, TERMFONT_BOLD);
1245 return 1;
1246 }
1247
1248 static int
1249 termp_fd_pre(DECL_ARGS)
1250 {
1251
1252 synopsis_pre(p, n);
1253 return termp_bold_pre(p, pair, meta, n);
1254 }
1255
1256 static void
1257 termp_fd_post(DECL_ARGS)
1258 {
1259
1260 term_newln(p);
1261 }
1262
1263 static int
1264 termp_sh_pre(DECL_ARGS)
1265 {
1266
1267 switch (n->type) {
1268 case ROFFT_BLOCK:
1269 /*
1270 * Vertical space before sections, except
1271 * when the previous section was empty.
1272 */
1273 if (n->prev == NULL ||
1274 n->prev->tok != MDOC_Sh ||
1275 (n->prev->body != NULL &&
1276 n->prev->body->child != NULL))
1277 term_vspace(p);
1278 break;
1279 case ROFFT_HEAD:
1280 term_fontpush(p, TERMFONT_BOLD);
1281 break;
1282 case ROFFT_BODY:
1283 p->offset = term_len(p, p->defindent);
1284 switch (n->sec) {
1285 case SEC_DESCRIPTION:
1286 fn_prio = 0;
1287 break;
1288 case SEC_AUTHORS:
1289 p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1290 break;
1291 default:
1292 break;
1293 }
1294 break;
1295 default:
1296 break;
1297 }
1298 return 1;
1299 }
1300
1301 static void
1302 termp_sh_post(DECL_ARGS)
1303 {
1304
1305 switch (n->type) {
1306 case ROFFT_HEAD:
1307 term_newln(p);
1308 break;
1309 case ROFFT_BODY:
1310 term_newln(p);
1311 p->offset = 0;
1312 break;
1313 default:
1314 break;
1315 }
1316 }
1317
1318 static void
1319 termp_lb_post(DECL_ARGS)
1320 {
1321
1322 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
1323 term_newln(p);
1324 }
1325
1326 static int
1327 termp_d1_pre(DECL_ARGS)
1328 {
1329
1330 if (n->type != ROFFT_BLOCK)
1331 return 1;
1332 term_newln(p);
1333 p->offset += term_len(p, p->defindent + 1);
1334 return 1;
1335 }
1336
1337 static int
1338 termp_ft_pre(DECL_ARGS)
1339 {
1340
1341 /* NB: NODE_LINE does not effect this! */
1342 synopsis_pre(p, n);
1343 term_fontpush(p, TERMFONT_UNDER);
1344 return 1;
1345 }
1346
1347 static int
1348 termp_fn_pre(DECL_ARGS)
1349 {
1350 size_t rmargin = 0;
1351 int pretty;
1352
1353 pretty = NODE_SYNPRETTY & n->flags;
1354
1355 synopsis_pre(p, n);
1356
1357 if (NULL == (n = n->child))
1358 return 0;
1359
1360 if (pretty) {
1361 rmargin = p->rmargin;
1362 p->rmargin = p->offset + term_len(p, 4);
1363 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1364 }
1365
1366 assert(n->type == ROFFT_TEXT);
1367 term_fontpush(p, TERMFONT_BOLD);
1368 term_word(p, n->string);
1369 term_fontpop(p);
1370
1371 if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
1372 tag_put(n->string, ++fn_prio, p->line);
1373
1374 if (pretty) {
1375 term_flushln(p);
1376 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1377 p->offset = p->rmargin;
1378 p->rmargin = rmargin;
1379 }
1380
1381 p->flags |= TERMP_NOSPACE;
1382 term_word(p, "(");
1383 p->flags |= TERMP_NOSPACE;
1384
1385 for (n = n->next; n; n = n->next) {
1386 assert(n->type == ROFFT_TEXT);
1387 term_fontpush(p, TERMFONT_UNDER);
1388 if (pretty)
1389 p->flags |= TERMP_NBRWORD;
1390 term_word(p, n->string);
1391 term_fontpop(p);
1392
1393 if (n->next) {
1394 p->flags |= TERMP_NOSPACE;
1395 term_word(p, ",");
1396 }
1397 }
1398
1399 p->flags |= TERMP_NOSPACE;
1400 term_word(p, ")");
1401
1402 if (pretty) {
1403 p->flags |= TERMP_NOSPACE;
1404 term_word(p, ";");
1405 term_flushln(p);
1406 }
1407
1408 return 0;
1409 }
1410
1411 static int
1412 termp_fa_pre(DECL_ARGS)
1413 {
1414 const struct roff_node *nn;
1415
1416 if (n->parent->tok != MDOC_Fo) {
1417 term_fontpush(p, TERMFONT_UNDER);
1418 return 1;
1419 }
1420
1421 for (nn = n->child; nn; nn = nn->next) {
1422 term_fontpush(p, TERMFONT_UNDER);
1423 p->flags |= TERMP_NBRWORD;
1424 term_word(p, nn->string);
1425 term_fontpop(p);
1426
1427 if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1428 p->flags |= TERMP_NOSPACE;
1429 term_word(p, ",");
1430 }
1431 }
1432
1433 return 0;
1434 }
1435
1436 static int
1437 termp_bd_pre(DECL_ARGS)
1438 {
1439 size_t tabwidth, lm, len, rm, rmax;
1440 struct roff_node *nn;
1441 int offset;
1442
1443 if (n->type == ROFFT_BLOCK) {
1444 print_bvspace(p, n, n);
1445 return 1;
1446 } else if (n->type == ROFFT_HEAD)
1447 return 0;
1448
1449 /* Handle the -offset argument. */
1450
1451 if (n->norm->Bd.offs == NULL ||
1452 ! strcmp(n->norm->Bd.offs, "left"))
1453 /* nothing */;
1454 else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1455 p->offset += term_len(p, p->defindent + 1);
1456 else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1457 p->offset += term_len(p, (p->defindent + 1) * 2);
1458 else {
1459 offset = a2width(p, n->norm->Bd.offs);
1460 if (offset < 0 && (size_t)(-offset) > p->offset)
1461 p->offset = 0;
1462 else if (offset < SHRT_MAX)
1463 p->offset += offset;
1464 }
1465
1466 /*
1467 * If -ragged or -filled are specified, the block does nothing
1468 * but change the indentation. If -unfilled or -literal are
1469 * specified, text is printed exactly as entered in the display:
1470 * for macro lines, a newline is appended to the line. Blank
1471 * lines are allowed.
1472 */
1473
1474 if (DISP_literal != n->norm->Bd.type &&
1475 DISP_unfilled != n->norm->Bd.type &&
1476 DISP_centered != n->norm->Bd.type)
1477 return 1;
1478
1479 tabwidth = p->tabwidth;
1480 if (DISP_literal == n->norm->Bd.type)
1481 p->tabwidth = term_len(p, 8);
1482
1483 lm = p->offset;
1484 rm = p->rmargin;
1485 rmax = p->maxrmargin;
1486 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1487
1488 for (nn = n->child; nn; nn = nn->next) {
1489 if (DISP_centered == n->norm->Bd.type) {
1490 if (nn->type == ROFFT_TEXT) {
1491 len = term_strlen(p, nn->string);
1492 p->offset = len >= rm ? 0 :
1493 lm + len >= rm ? rm - len :
1494 (lm + rm - len) / 2;
1495 } else
1496 p->offset = lm;
1497 }
1498 print_mdoc_node(p, pair, meta, nn);
1499 /*
1500 * If the printed node flushes its own line, then we
1501 * needn't do it here as well. This is hacky, but the
1502 * notion of selective eoln whitespace is pretty dumb
1503 * anyway, so don't sweat it.
1504 */
1505 switch (nn->tok) {
1506 case MDOC_Sm:
1507 case MDOC_br:
1508 case MDOC_sp:
1509 case MDOC_Bl:
1510 case MDOC_D1:
1511 case MDOC_Dl:
1512 case MDOC_Lp:
1513 case MDOC_Pp:
1514 continue;
1515 default:
1516 break;
1517 }
1518 if (p->flags & TERMP_NONEWLINE ||
1519 (nn->next && ! (nn->next->flags & NODE_LINE)))
1520 continue;
1521 term_flushln(p);
1522 p->flags |= TERMP_NOSPACE;
1523 }
1524
1525 p->tabwidth = tabwidth;
1526 p->rmargin = rm;
1527 p->maxrmargin = rmax;
1528 return 0;
1529 }
1530
1531 static void
1532 termp_bd_post(DECL_ARGS)
1533 {
1534 size_t rm, rmax;
1535
1536 if (n->type != ROFFT_BODY)
1537 return;
1538
1539 rm = p->rmargin;
1540 rmax = p->maxrmargin;
1541
1542 if (DISP_literal == n->norm->Bd.type ||
1543 DISP_unfilled == n->norm->Bd.type)
1544 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1545
1546 p->flags |= TERMP_NOSPACE;
1547 term_newln(p);
1548
1549 p->rmargin = rm;
1550 p->maxrmargin = rmax;
1551 }
1552
1553 static int
1554 termp_xx_pre(DECL_ARGS)
1555 {
1556 if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1557 p->flags |= TERMP_PREKEEP;
1558 return 1;
1559 }
1560
1561 static void
1562 termp_xx_post(DECL_ARGS)
1563 {
1564 if (n->aux == 0)
1565 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1566 }
1567
1568 static void
1569 termp_pf_post(DECL_ARGS)
1570 {
1571
1572 if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1573 p->flags |= TERMP_NOSPACE;
1574 }
1575
1576 static int
1577 termp_ss_pre(DECL_ARGS)
1578 {
1579 struct roff_node *nn;
1580
1581 switch (n->type) {
1582 case ROFFT_BLOCK:
1583 term_newln(p);
1584 for (nn = n->prev; nn != NULL; nn = nn->prev)
1585 if ((nn->flags & NODE_NOPRT) == 0)
1586 break;
1587 if (nn != NULL)
1588 term_vspace(p);
1589 break;
1590 case ROFFT_HEAD:
1591 term_fontpush(p, TERMFONT_BOLD);
1592 p->offset = term_len(p, (p->defindent+1)/2);
1593 break;
1594 case ROFFT_BODY:
1595 p->offset = term_len(p, p->defindent);
1596 break;
1597 default:
1598 break;
1599 }
1600
1601 return 1;
1602 }
1603
1604 static void
1605 termp_ss_post(DECL_ARGS)
1606 {
1607
1608 if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1609 term_newln(p);
1610 }
1611
1612 static int
1613 termp_cd_pre(DECL_ARGS)
1614 {
1615
1616 synopsis_pre(p, n);
1617 term_fontpush(p, TERMFONT_BOLD);
1618 return 1;
1619 }
1620
1621 static int
1622 termp_in_pre(DECL_ARGS)
1623 {
1624
1625 synopsis_pre(p, n);
1626
1627 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
1628 term_fontpush(p, TERMFONT_BOLD);
1629 term_word(p, "#include");
1630 term_word(p, "<");
1631 } else {
1632 term_word(p, "<");
1633 term_fontpush(p, TERMFONT_UNDER);
1634 }
1635
1636 p->flags |= TERMP_NOSPACE;
1637 return 1;
1638 }
1639
1640 static void
1641 termp_in_post(DECL_ARGS)
1642 {
1643
1644 if (NODE_SYNPRETTY & n->flags)
1645 term_fontpush(p, TERMFONT_BOLD);
1646
1647 p->flags |= TERMP_NOSPACE;
1648 term_word(p, ">");
1649
1650 if (NODE_SYNPRETTY & n->flags)
1651 term_fontpop(p);
1652 }
1653
1654 static int
1655 termp_sp_pre(DECL_ARGS)
1656 {
1657 struct roffsu su;
1658 int i, len;
1659
1660 switch (n->tok) {
1661 case MDOC_sp:
1662 if (n->child) {
1663 if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
1664 su.scale = 1.0;
1665 len = term_vspan(p, &su);
1666 } else
1667 len = 1;
1668 break;
1669 case MDOC_br:
1670 len = 0;
1671 break;
1672 default:
1673 len = 1;
1674 fn_prio = 0;
1675 break;
1676 }
1677
1678 if (0 == len)
1679 term_newln(p);
1680 else if (len < 0)
1681 p->skipvsp -= len;
1682 else
1683 for (i = 0; i < len; i++)
1684 term_vspace(p);
1685
1686 return 0;
1687 }
1688
1689 static int
1690 termp_skip_pre(DECL_ARGS)
1691 {
1692
1693 return 0;
1694 }
1695
1696 static int
1697 termp_quote_pre(DECL_ARGS)
1698 {
1699
1700 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1701 return 1;
1702
1703 switch (n->tok) {
1704 case MDOC_Ao:
1705 case MDOC_Aq:
1706 term_word(p, n->child != NULL && n->child->next == NULL &&
1707 n->child->tok == MDOC_Mt ? "<" : "\\(la");
1708 break;
1709 case MDOC_Bro:
1710 case MDOC_Brq:
1711 term_word(p, "{");
1712 break;
1713 case MDOC_Oo:
1714 case MDOC_Op:
1715 case MDOC_Bo:
1716 case MDOC_Bq:
1717 term_word(p, "[");
1718 break;
1719 case MDOC_Do:
1720 case MDOC_Dq:
1721 term_word(p, "\\(Lq");
1722 break;
1723 case MDOC_En:
1724 if (NULL == n->norm->Es ||
1725 NULL == n->norm->Es->child)
1726 return 1;
1727 term_word(p, n->norm->Es->child->string);
1728 break;
1729 case MDOC_Po:
1730 case MDOC_Pq:
1731 term_word(p, "(");
1732 break;
1733 case MDOC__T:
1734 case MDOC_Qo:
1735 case MDOC_Qq:
1736 term_word(p, "\"");
1737 break;
1738 case MDOC_Ql:
1739 case MDOC_So:
1740 case MDOC_Sq:
1741 term_word(p, "\\(oq");
1742 break;
1743 default:
1744 abort();
1745 }
1746
1747 p->flags |= TERMP_NOSPACE;
1748 return 1;
1749 }
1750
1751 static void
1752 termp_quote_post(DECL_ARGS)
1753 {
1754
1755 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1756 return;
1757
1758 p->flags |= TERMP_NOSPACE;
1759
1760 switch (n->tok) {
1761 case MDOC_Ao:
1762 case MDOC_Aq:
1763 term_word(p, n->child != NULL && n->child->next == NULL &&
1764 n->child->tok == MDOC_Mt ? ">" : "\\(ra");
1765 break;
1766 case MDOC_Bro:
1767 case MDOC_Brq:
1768 term_word(p, "}");
1769 break;
1770 case MDOC_Oo:
1771 case MDOC_Op:
1772 case MDOC_Bo:
1773 case MDOC_Bq:
1774 term_word(p, "]");
1775 break;
1776 case MDOC_Do:
1777 case MDOC_Dq:
1778 term_word(p, "\\(Rq");
1779 break;
1780 case MDOC_En:
1781 if (n->norm->Es == NULL ||
1782 n->norm->Es->child == NULL ||
1783 n->norm->Es->child->next == NULL)
1784 p->flags &= ~TERMP_NOSPACE;
1785 else
1786 term_word(p, n->norm->Es->child->next->string);
1787 break;
1788 case MDOC_Po:
1789 case MDOC_Pq:
1790 term_word(p, ")");
1791 break;
1792 case MDOC__T:
1793 case MDOC_Qo:
1794 case MDOC_Qq:
1795 term_word(p, "\"");
1796 break;
1797 case MDOC_Ql:
1798 case MDOC_So:
1799 case MDOC_Sq:
1800 term_word(p, "\\(cq");
1801 break;
1802 default:
1803 abort();
1804 }
1805 }
1806
1807 static int
1808 termp_eo_pre(DECL_ARGS)
1809 {
1810
1811 if (n->type != ROFFT_BODY)
1812 return 1;
1813
1814 if (n->end == ENDBODY_NOT &&
1815 n->parent->head->child == NULL &&
1816 n->child != NULL &&
1817 n->child->end != ENDBODY_NOT)
1818 term_word(p, "\\&");
1819 else if (n->end != ENDBODY_NOT ? n->child != NULL :
1820 n->parent->head->child != NULL && (n->child != NULL ||
1821 (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1822 p->flags |= TERMP_NOSPACE;
1823
1824 return 1;
1825 }
1826
1827 static void
1828 termp_eo_post(DECL_ARGS)
1829 {
1830 int body, tail;
1831
1832 if (n->type != ROFFT_BODY)
1833 return;
1834
1835 if (n->end != ENDBODY_NOT) {
1836 p->flags &= ~TERMP_NOSPACE;
1837 return;
1838 }
1839
1840 body = n->child != NULL || n->parent->head->child != NULL;
1841 tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1842
1843 if (body && tail)
1844 p->flags |= TERMP_NOSPACE;
1845 else if ( ! (body || tail))
1846 term_word(p, "\\&");
1847 else if ( ! tail)
1848 p->flags &= ~TERMP_NOSPACE;
1849 }
1850
1851 static int
1852 termp_fo_pre(DECL_ARGS)
1853 {
1854 size_t rmargin = 0;
1855 int pretty;
1856
1857 pretty = NODE_SYNPRETTY & n->flags;
1858
1859 if (n->type == ROFFT_BLOCK) {
1860 synopsis_pre(p, n);
1861 return 1;
1862 } else if (n->type == ROFFT_BODY) {
1863 if (pretty) {
1864 rmargin = p->rmargin;
1865 p->rmargin = p->offset + term_len(p, 4);
1866 p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1867 TERMP_HANG;
1868 }
1869 p->flags |= TERMP_NOSPACE;
1870 term_word(p, "(");
1871 p->flags |= TERMP_NOSPACE;
1872 if (pretty) {
1873 term_flushln(p);
1874 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1875 TERMP_HANG);
1876 p->offset = p->rmargin;
1877 p->rmargin = rmargin;
1878 }
1879 return 1;
1880 }
1881
1882 if (NULL == n->child)
1883 return 0;
1884
1885 /* XXX: we drop non-initial arguments as per groff. */
1886
1887 assert(n->child->string);
1888 term_fontpush(p, TERMFONT_BOLD);
1889 term_word(p, n->child->string);
1890 return 0;
1891 }
1892
1893 static void
1894 termp_fo_post(DECL_ARGS)
1895 {
1896
1897 if (n->type != ROFFT_BODY)
1898 return;
1899
1900 p->flags |= TERMP_NOSPACE;
1901 term_word(p, ")");
1902
1903 if (NODE_SYNPRETTY & n->flags) {
1904 p->flags |= TERMP_NOSPACE;
1905 term_word(p, ";");
1906 term_flushln(p);
1907 }
1908 }
1909
1910 static int
1911 termp_bf_pre(DECL_ARGS)
1912 {
1913
1914 if (n->type == ROFFT_HEAD)
1915 return 0;
1916 else if (n->type != ROFFT_BODY)
1917 return 1;
1918
1919 if (FONT_Em == n->norm->Bf.font)
1920 term_fontpush(p, TERMFONT_UNDER);
1921 else if (FONT_Sy == n->norm->Bf.font)
1922 term_fontpush(p, TERMFONT_BOLD);
1923 else
1924 term_fontpush(p, TERMFONT_NONE);
1925
1926 return 1;
1927 }
1928
1929 static int
1930 termp_sm_pre(DECL_ARGS)
1931 {
1932
1933 if (NULL == n->child)
1934 p->flags ^= TERMP_NONOSPACE;
1935 else if (0 == strcmp("on", n->child->string))
1936 p->flags &= ~TERMP_NONOSPACE;
1937 else
1938 p->flags |= TERMP_NONOSPACE;
1939
1940 if (p->col && ! (TERMP_NONOSPACE & p->flags))
1941 p->flags &= ~TERMP_NOSPACE;
1942
1943 return 0;
1944 }
1945
1946 static int
1947 termp_ap_pre(DECL_ARGS)
1948 {
1949
1950 p->flags |= TERMP_NOSPACE;
1951 term_word(p, "'");
1952 p->flags |= TERMP_NOSPACE;
1953 return 1;
1954 }
1955
1956 static void
1957 termp____post(DECL_ARGS)
1958 {
1959
1960 /*
1961 * Handle lists of authors. In general, print each followed by
1962 * a comma. Don't print the comma if there are only two
1963 * authors.
1964 */
1965 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
1966 if (NULL == n->next->next || MDOC__A != n->next->next->tok)
1967 if (NULL == n->prev || MDOC__A != n->prev->tok)
1968 return;
1969
1970 /* TODO: %U. */
1971
1972 if (NULL == n->parent || MDOC_Rs != n->parent->tok)
1973 return;
1974
1975 p->flags |= TERMP_NOSPACE;
1976 if (NULL == n->next) {
1977 term_word(p, ".");
1978 p->flags |= TERMP_SENTENCE;
1979 } else
1980 term_word(p, ",");
1981 }
1982
1983 static int
1984 termp_li_pre(DECL_ARGS)
1985 {
1986
1987 termp_tag_pre(p, pair, meta, n);
1988 term_fontpush(p, TERMFONT_NONE);
1989 return 1;
1990 }
1991
1992 static int
1993 termp_lk_pre(DECL_ARGS)
1994 {
1995 const struct roff_node *link, *descr;
1996
1997 if (NULL == (link = n->child))
1998 return 0;
1999
2000 if (NULL != (descr = link->next)) {
2001 term_fontpush(p, TERMFONT_UNDER);
2002 while (NULL != descr) {
2003 term_word(p, descr->string);
2004 descr = descr->next;
2005 }
2006 p->flags |= TERMP_NOSPACE;
2007 term_word(p, ":");
2008 term_fontpop(p);
2009 }
2010
2011 term_fontpush(p, TERMFONT_BOLD);
2012 term_word(p, link->string);
2013 term_fontpop(p);
2014
2015 return 0;
2016 }
2017
2018 static int
2019 termp_bk_pre(DECL_ARGS)
2020 {
2021
2022 switch (n->type) {
2023 case ROFFT_BLOCK:
2024 break;
2025 case ROFFT_HEAD:
2026 return 0;
2027 case ROFFT_BODY:
2028 if (n->parent->args != NULL || n->prev->child == NULL)
2029 p->flags |= TERMP_PREKEEP;
2030 break;
2031 default:
2032 abort();
2033 }
2034
2035 return 1;
2036 }
2037
2038 static void
2039 termp_bk_post(DECL_ARGS)
2040 {
2041
2042 if (n->type == ROFFT_BODY)
2043 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2044 }
2045
2046 static void
2047 termp__t_post(DECL_ARGS)
2048 {
2049
2050 /*
2051 * If we're in an `Rs' and there's a journal present, then quote
2052 * us instead of underlining us (for disambiguation).
2053 */
2054 if (n->parent && MDOC_Rs == n->parent->tok &&
2055 n->parent->norm->Rs.quote_T)
2056 termp_quote_post(p, pair, meta, n);
2057
2058 termp____post(p, pair, meta, n);
2059 }
2060
2061 static int
2062 termp__t_pre(DECL_ARGS)
2063 {
2064
2065 /*
2066 * If we're in an `Rs' and there's a journal present, then quote
2067 * us instead of underlining us (for disambiguation).
2068 */
2069 if (n->parent && MDOC_Rs == n->parent->tok &&
2070 n->parent->norm->Rs.quote_T)
2071 return termp_quote_pre(p, pair, meta, n);
2072
2073 term_fontpush(p, TERMFONT_UNDER);
2074 return 1;
2075 }
2076
2077 static int
2078 termp_under_pre(DECL_ARGS)
2079 {
2080
2081 term_fontpush(p, TERMFONT_UNDER);
2082 return 1;
2083 }
2084
2085 static int
2086 termp_em_pre(DECL_ARGS)
2087 {
2088 if (n->child != NULL &&
2089 n->child->type == ROFFT_TEXT)
2090 tag_put(n->child->string, 0, p->line);
2091 term_fontpush(p, TERMFONT_UNDER);
2092 return 1;
2093 }
2094
2095 static int
2096 termp_sy_pre(DECL_ARGS)
2097 {
2098 if (n->child != NULL &&
2099 n->child->type == ROFFT_TEXT)
2100 tag_put(n->child->string, 0, p->line);
2101 term_fontpush(p, TERMFONT_BOLD);
2102 return 1;
2103 }
2104
2105 static int
2106 termp_er_pre(DECL_ARGS)
2107 {
2108
2109 if (n->sec == SEC_ERRORS &&
2110 (n->parent->tok == MDOC_It ||
2111 (n->parent->tok == MDOC_Bq &&
2112 n->parent->parent->parent->tok == MDOC_It)))
2113 tag_put(n->child->string, 1, p->line);
2114 return 1;
2115 }
2116
2117 static int
2118 termp_tag_pre(DECL_ARGS)
2119 {
2120
2121 if (n->child != NULL &&
2122 n->child->type == ROFFT_TEXT &&
2123 (n->prev == NULL ||
2124 (n->prev->type == ROFFT_TEXT &&
2125 strcmp(n->prev->string, "|") == 0)) &&
2126 (n->parent->tok == MDOC_It ||
2127 (n->parent->tok == MDOC_Xo &&
2128 n->parent->parent->prev == NULL &&
2129 n->parent->parent->parent->tok == MDOC_It)))
2130 tag_put(n->child->string, 1, p->line);
2131 return 1;
2132 }