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