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