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