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