]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
Moved mdoc_a2att() into libmdoc (replacement happens in mdoc_action.c).
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.35 2009/07/12 20:24:24 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_lb_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_sq_pre(DECL_ARGS);
168 static int termp_ss_pre(DECL_ARGS);
169 static int termp_st_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 { termp_st_pre, 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__t_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 { termp_lb_pre, 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 };
298
299 #ifdef __linux__
300 extern size_t strlcpy(char *, const char *, size_t);
301 extern size_t strlcat(char *, const char *, size_t);
302 #endif
303
304 static int arg_hasattr(int, const struct mdoc_node *);
305 static int arg_getattrs(const int *, int *, size_t,
306 const struct mdoc_node *);
307 static int arg_getattr(int, const struct mdoc_node *);
308 static size_t arg_offset(const struct mdoc_argv *);
309 static size_t arg_width(const struct mdoc_argv *, int);
310 static int arg_listtype(const struct mdoc_node *);
311 static int fmt_block_vspace(struct termp *,
312 const struct mdoc_node *,
313 const struct mdoc_node *);
314 static void print_node(DECL_ARGS);
315 static void print_head(struct termp *,
316 const struct mdoc_meta *);
317 static void print_body(DECL_ARGS);
318 static void print_foot(struct termp *,
319 const struct mdoc_meta *);
320
321
322 int
323 mdoc_run(struct termp *p, const struct mdoc *m)
324 {
325 /*
326 * Main output function. When this is called, assume that the
327 * tree is properly formed.
328 */
329 print_head(p, mdoc_meta(m));
330 assert(mdoc_node(m));
331 assert(MDOC_ROOT == mdoc_node(m)->type);
332 if (mdoc_node(m)->child)
333 print_body(p, NULL, mdoc_meta(m), mdoc_node(m)->child);
334 print_foot(p, mdoc_meta(m));
335 return(1);
336 }
337
338
339 static void
340 print_body(DECL_ARGS)
341 {
342
343 print_node(p, pair, meta, node);
344 if ( ! node->next)
345 return;
346 print_body(p, pair, meta, node->next);
347 }
348
349
350 static void
351 print_node(DECL_ARGS)
352 {
353 int dochild;
354 struct termpair npair;
355 size_t offset, rmargin;
356
357 dochild = 1;
358 offset = p->offset;
359 rmargin = p->rmargin;
360
361 npair.ppair = pair;
362 npair.flag = 0;
363 npair.count = 0;
364
365 if (MDOC_TEXT != node->type) {
366 if (termacts[node->tok].pre)
367 if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node))
368 dochild = 0;
369 } else /* MDOC_TEXT == node->type */
370 term_word(p, node->string);
371
372 /* Children. */
373
374 p->flags |= npair.flag;
375
376 if (dochild && node->child)
377 print_body(p, &npair, meta, node->child);
378
379 /* Post-processing. */
380
381 if (MDOC_TEXT != node->type)
382 if (termacts[node->tok].post)
383 (*termacts[node->tok].post)(p, &npair, meta, node);
384
385 p->offset = offset;
386 p->rmargin = rmargin;
387 p->flags &= ~npair.flag;
388 }
389
390
391 static void
392 print_foot(struct termp *p, const struct mdoc_meta *meta)
393 {
394 struct tm *tm;
395 char *buf, *os;
396
397 /*
398 * Output the footer in new-groff style, that is, three columns
399 * with the middle being the manual date and flanking columns
400 * being the operating system:
401 *
402 * SYSTEM DATE SYSTEM
403 */
404
405 if (NULL == (buf = malloc(p->rmargin)))
406 err(1, "malloc");
407 if (NULL == (os = malloc(p->rmargin)))
408 err(1, "malloc");
409
410 tm = localtime(&meta->date);
411
412 if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
413 err(1, "strftime");
414
415 (void)strlcpy(os, meta->os, p->rmargin);
416
417 term_vspace(p);
418
419 p->offset = 0;
420 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
421 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
422
423 term_word(p, os);
424 term_flushln(p);
425
426 p->offset = p->rmargin;
427 p->rmargin = p->maxrmargin - strlen(os);
428 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
429
430 term_word(p, buf);
431 term_flushln(p);
432
433 p->offset = p->rmargin;
434 p->rmargin = p->maxrmargin;
435 p->flags &= ~TERMP_NOBREAK;
436 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
437
438 term_word(p, os);
439 term_flushln(p);
440
441 p->offset = 0;
442 p->rmargin = p->maxrmargin;
443 p->flags = 0;
444
445 free(buf);
446 free(os);
447 }
448
449
450 static void
451 print_head(struct termp *p, const struct mdoc_meta *meta)
452 {
453 char *buf, *title;
454
455 p->rmargin = p->maxrmargin;
456 p->offset = 0;
457
458 if (NULL == (buf = malloc(p->rmargin)))
459 err(1, "malloc");
460 if (NULL == (title = malloc(p->rmargin)))
461 err(1, "malloc");
462
463 /*
464 * The header is strange. It has three components, which are
465 * really two with the first duplicated. It goes like this:
466 *
467 * IDENTIFIER TITLE IDENTIFIER
468 *
469 * The IDENTIFIER is NAME(SECTION), which is the command-name
470 * (if given, or "unknown" if not) followed by the manual page
471 * section. These are given in `Dt'. The TITLE is a free-form
472 * string depending on the manual volume. If not specified, it
473 * switches on the manual section.
474 */
475
476 assert(meta->vol);
477 (void)strlcpy(buf, meta->vol, p->rmargin);
478
479 if (meta->arch) {
480 (void)strlcat(buf, " (", p->rmargin);
481 (void)strlcat(buf, meta->arch, p->rmargin);
482 (void)strlcat(buf, ")", p->rmargin);
483 }
484
485 (void)snprintf(title, p->rmargin, "%s(%d)",
486 meta->title, meta->msec);
487
488 p->offset = 0;
489 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
490 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
491
492 term_word(p, title);
493 term_flushln(p);
494
495 p->offset = p->rmargin;
496 p->rmargin = p->maxrmargin - strlen(title);
497 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
498
499 term_word(p, buf);
500 term_flushln(p);
501
502 p->offset = p->rmargin;
503 p->rmargin = p->maxrmargin;
504 p->flags &= ~TERMP_NOBREAK;
505 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
506
507 term_word(p, title);
508 term_flushln(p);
509
510 p->offset = 0;
511 p->rmargin = p->maxrmargin;
512 p->flags &= ~TERMP_NOSPACE;
513
514 free(title);
515 free(buf);
516 }
517
518
519 static size_t
520 arg_width(const struct mdoc_argv *arg, int pos)
521 {
522 size_t v;
523 int i, len;
524
525 assert(pos < (int)arg->sz && pos >= 0);
526 assert(arg->value[pos]);
527
528 if (0 == (len = (int)strlen(arg->value[pos])))
529 return(0);
530
531 for (i = 0; i < len - 1; i++)
532 if ( ! isdigit((u_char)arg->value[pos][i]))
533 break;
534
535 if (i == len - 1) {
536 if ('n' == arg->value[pos][len - 1] ||
537 'm' == arg->value[pos][len - 1]) {
538 v = (size_t)atoi(arg->value[pos]);
539 return(v + 2);
540 }
541
542 }
543 return(strlen(arg->value[pos]) + 2);
544 }
545
546
547 static int
548 arg_listtype(const struct mdoc_node *n)
549 {
550 int i, len;
551
552 assert(MDOC_BLOCK == n->type);
553
554 len = (int)(n->args ? n->args->argc : 0);
555
556 for (i = 0; i < len; i++)
557 switch (n->args->argv[i].arg) {
558 case (MDOC_Bullet):
559 /* FALLTHROUGH */
560 case (MDOC_Dash):
561 /* FALLTHROUGH */
562 case (MDOC_Enum):
563 /* FALLTHROUGH */
564 case (MDOC_Hyphen):
565 /* FALLTHROUGH */
566 case (MDOC_Tag):
567 /* FALLTHROUGH */
568 case (MDOC_Inset):
569 /* FALLTHROUGH */
570 case (MDOC_Diag):
571 /* FALLTHROUGH */
572 case (MDOC_Item):
573 /* FALLTHROUGH */
574 case (MDOC_Column):
575 /* FALLTHROUGH */
576 case (MDOC_Ohang):
577 return(n->args->argv[i].arg);
578 default:
579 break;
580 }
581
582 /* FIXME: mandated by parser. */
583
584 errx(1, "list type not supported");
585 /* NOTREACHED */
586 }
587
588
589 static size_t
590 arg_offset(const struct mdoc_argv *arg)
591 {
592
593 assert(*arg->value);
594 if (0 == strcmp(*arg->value, "left"))
595 return(0);
596 if (0 == strcmp(*arg->value, "indent"))
597 return(INDENT + 1);
598 if (0 == strcmp(*arg->value, "indent-two"))
599 return((INDENT + 1) * 2);
600
601 /* FIXME: needs to support field-widths (10n, etc.). */
602
603 return(strlen(*arg->value));
604 }
605
606
607 static int
608 arg_hasattr(int arg, const struct mdoc_node *n)
609 {
610
611 return(-1 != arg_getattr(arg, n));
612 }
613
614
615 static int
616 arg_getattr(int v, const struct mdoc_node *n)
617 {
618 int val;
619
620 return(arg_getattrs(&v, &val, 1, n) ? val : -1);
621 }
622
623
624 static int
625 arg_getattrs(const int *keys, int *vals,
626 size_t sz, const struct mdoc_node *n)
627 {
628 int i, j, k;
629
630 if (NULL == n->args)
631 return(0);
632
633 for (k = i = 0; i < (int)n->args->argc; i++)
634 for (j = 0; j < (int)sz; j++)
635 if (n->args->argv[i].arg == keys[j]) {
636 vals[j] = i;
637 k++;
638 }
639 return(k);
640 }
641
642
643 /* ARGSUSED */
644 static int
645 fmt_block_vspace(struct termp *p,
646 const struct mdoc_node *bl,
647 const struct mdoc_node *node)
648 {
649 const struct mdoc_node *n;
650
651 term_newln(p);
652
653 if (arg_hasattr(MDOC_Compact, bl))
654 return(1);
655
656 for (n = node; n; n = n->parent) {
657 if (MDOC_BLOCK != n->type)
658 continue;
659 if (MDOC_Ss == n->tok)
660 break;
661 if (MDOC_Sh == n->tok)
662 break;
663 if (NULL == n->prev)
664 continue;
665 term_vspace(p);
666 break;
667 }
668
669 return(1);
670 }
671
672
673 /* ARGSUSED */
674 static int
675 termp_dq_pre(DECL_ARGS)
676 {
677
678 if (MDOC_BODY != node->type)
679 return(1);
680
681 term_word(p, "\\(lq");
682 p->flags |= TERMP_NOSPACE;
683 return(1);
684 }
685
686
687 /* ARGSUSED */
688 static void
689 termp_dq_post(DECL_ARGS)
690 {
691
692 if (MDOC_BODY != node->type)
693 return;
694
695 p->flags |= TERMP_NOSPACE;
696 term_word(p, "\\(rq");
697 }
698
699
700 /* ARGSUSED */
701 static int
702 termp_it_pre(DECL_ARGS)
703 {
704 const struct mdoc_node *bl, *n;
705 char buf[7];
706 int i, type, keys[3], vals[3], sv;
707 size_t width, offset;
708
709 if (MDOC_BLOCK == node->type)
710 return(fmt_block_vspace(p, node->parent->parent, node));
711
712 bl = node->parent->parent->parent;
713
714 /* Save parent attributes. */
715
716 pair->flag = p->flags;
717
718 /* Get list width and offset. */
719
720 keys[0] = MDOC_Width;
721 keys[1] = MDOC_Offset;
722 keys[2] = MDOC_Column;
723
724 vals[0] = vals[1] = vals[2] = -1;
725
726 width = offset = 0;
727
728 (void)arg_getattrs(keys, vals, 3, bl);
729
730 type = arg_listtype(bl);
731
732 /* Calculate real width and offset. */
733
734 switch (type) {
735 case (MDOC_Column):
736 if (MDOC_BODY == node->type)
737 break;
738 for (i = 0, n = node->prev; n; n = n->prev, i++)
739 offset += arg_width
740 (&bl->args->argv[vals[2]], i);
741 assert(i < (int)bl->args->argv[vals[2]].sz);
742 width = arg_width(&bl->args->argv[vals[2]], i);
743 if (vals[1] >= 0)
744 offset += arg_offset(&bl->args->argv[vals[1]]);
745 break;
746 default:
747 if (vals[0] >= 0)
748 width = arg_width(&bl->args->argv[vals[0]], 0);
749 if (vals[1] >= 0)
750 offset += arg_offset(&bl->args->argv[vals[1]]);
751 break;
752 }
753
754 /*
755 * List-type can override the width in the case of fixed-head
756 * values (bullet, dash/hyphen, enum). Tags need a non-zero
757 * offset.
758 */
759
760 switch (type) {
761 case (MDOC_Bullet):
762 /* FALLTHROUGH */
763 case (MDOC_Dash):
764 /* FALLTHROUGH */
765 case (MDOC_Hyphen):
766 if (width < 4)
767 width = 4;
768 break;
769 case (MDOC_Enum):
770 if (width < 5)
771 width = 5;
772 break;
773 case (MDOC_Tag):
774 if (0 == width)
775 width = 10;
776 break;
777 default:
778 break;
779 }
780
781 /*
782 * Whitespace control. Inset bodies need an initial space,
783 * while diagonal bodies need two.
784 */
785
786 switch (type) {
787 case (MDOC_Diag):
788 term_word(p, "\\ ");
789 /* FALLTHROUGH */
790 case (MDOC_Inset):
791 if (MDOC_BODY == node->type)
792 p->flags &= ~TERMP_NOSPACE;
793 else
794 p->flags |= TERMP_NOSPACE;
795 break;
796 default:
797 p->flags |= TERMP_NOSPACE;
798 break;
799 }
800
801 /*
802 * Style flags. Diagnostic heads need TTYPE_DIAG.
803 */
804
805 switch (type) {
806 case (MDOC_Diag):
807 if (MDOC_HEAD == node->type)
808 p->flags |= ttypes[TTYPE_DIAG];
809 break;
810 default:
811 break;
812 }
813
814 /*
815 * Pad and break control. This is the tricker part. Lists with
816 * set right-margins for the head get TERMP_NOBREAK because, if
817 * they overrun the margin, they wrap to the new margin.
818 * Correspondingly, the body for these types don't left-pad, as
819 * the head will pad out to to the right.
820 */
821
822 switch (type) {
823 case (MDOC_Bullet):
824 /* FALLTHROUGH */
825 case (MDOC_Dash):
826 /* FALLTHROUGH */
827 case (MDOC_Enum):
828 /* FALLTHROUGH */
829 case (MDOC_Hyphen):
830 /* FALLTHROUGH */
831 case (MDOC_Tag):
832 if (MDOC_HEAD == node->type)
833 p->flags |= TERMP_NOBREAK;
834 else
835 p->flags |= TERMP_NOLPAD;
836 if (MDOC_HEAD == node->type && MDOC_Tag == type)
837 if (NULL == node->next ||
838 NULL == node->next->child)
839 p->flags |= TERMP_NONOBREAK;
840 break;
841 case (MDOC_Column):
842 if (MDOC_HEAD == node->type) {
843 assert(node->next);
844 if (MDOC_BODY == node->next->type)
845 p->flags &= ~TERMP_NOBREAK;
846 else
847 p->flags |= TERMP_NOBREAK;
848 if (node->prev)
849 p->flags |= TERMP_NOLPAD;
850 }
851 break;
852 case (MDOC_Diag):
853 if (MDOC_HEAD == node->type)
854 p->flags |= TERMP_NOBREAK;
855 break;
856 default:
857 break;
858 }
859
860 /*
861 * Margin control. Set-head-width lists have their right
862 * margins shortened. The body for these lists has the offset
863 * necessarily lengthened. Everybody gets the offset.
864 */
865
866 p->offset += offset;
867
868 switch (type) {
869 case (MDOC_Bullet):
870 /* FALLTHROUGH */
871 case (MDOC_Dash):
872 /* FALLTHROUGH */
873 case (MDOC_Enum):
874 /* FALLTHROUGH */
875 case (MDOC_Hyphen):
876 /* FALLTHROUGH */
877 case (MDOC_Tag):
878 if (MDOC_HEAD == node->type)
879 p->rmargin = p->offset + width;
880 else
881 p->offset += width;
882 break;
883 case (MDOC_Column):
884 p->rmargin = p->offset + width;
885 break;
886 default:
887 break;
888 }
889
890 /*
891 * The dash, hyphen, bullet and enum lists all have a special
892 * HEAD character (temporarily bold, in some cases).
893 */
894
895 sv = p->flags;
896 if (MDOC_HEAD == node->type)
897 switch (type) {
898 case (MDOC_Bullet):
899 p->flags |= TERMP_BOLD;
900 term_word(p, "\\[bu]");
901 break;
902 case (MDOC_Dash):
903 /* FALLTHROUGH */
904 case (MDOC_Hyphen):
905 p->flags |= TERMP_BOLD;
906 term_word(p, "\\(hy");
907 break;
908 case (MDOC_Enum):
909 (pair->ppair->ppair->count)++;
910 (void)snprintf(buf, sizeof(buf), "%d.",
911 pair->ppair->ppair->count);
912 term_word(p, buf);
913 break;
914 default:
915 break;
916 }
917
918 p->flags = sv; /* Restore saved flags. */
919
920 /*
921 * If we're not going to process our children, indicate so here.
922 */
923
924 switch (type) {
925 case (MDOC_Bullet):
926 /* FALLTHROUGH */
927 case (MDOC_Item):
928 /* FALLTHROUGH */
929 case (MDOC_Dash):
930 /* FALLTHROUGH */
931 case (MDOC_Hyphen):
932 /* FALLTHROUGH */
933 case (MDOC_Enum):
934 if (MDOC_HEAD == node->type)
935 return(0);
936 break;
937 case (MDOC_Column):
938 if (MDOC_BODY == node->type)
939 return(0);
940 break;
941 default:
942 break;
943 }
944
945 return(1);
946 }
947
948
949 /* ARGSUSED */
950 static void
951 termp_it_post(DECL_ARGS)
952 {
953 int type;
954
955 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
956 return;
957
958 type = arg_listtype(node->parent->parent->parent);
959
960 switch (type) {
961 case (MDOC_Diag):
962 /* FALLTHROUGH */
963 case (MDOC_Item):
964 /* FALLTHROUGH */
965 case (MDOC_Inset):
966 if (MDOC_BODY == node->type)
967 term_flushln(p);
968 break;
969 case (MDOC_Column):
970 if (MDOC_HEAD == node->type)
971 term_flushln(p);
972 break;
973 default:
974 term_flushln(p);
975 break;
976 }
977
978 p->flags = pair->flag;
979 }
980
981
982 /* ARGSUSED */
983 static int
984 termp_nm_pre(DECL_ARGS)
985 {
986
987 if (SEC_SYNOPSIS == node->sec)
988 term_newln(p);
989
990 pair->flag |= ttypes[TTYPE_PROG];
991 p->flags |= ttypes[TTYPE_PROG];
992
993 if (NULL == node->child)
994 term_word(p, meta->name);
995
996 return(1);
997 }
998
999
1000 /* ARGSUSED */
1001 static int
1002 termp_fl_pre(DECL_ARGS)
1003 {
1004
1005 pair->flag |= ttypes[TTYPE_CMD_FLAG];
1006 p->flags |= ttypes[TTYPE_CMD_FLAG];
1007 term_word(p, "\\-");
1008 p->flags |= TERMP_NOSPACE;
1009 return(1);
1010 }
1011
1012
1013 /* ARGSUSED */
1014 static int
1015 termp_ar_pre(DECL_ARGS)
1016 {
1017
1018 pair->flag |= ttypes[TTYPE_CMD_ARG];
1019 return(1);
1020 }
1021
1022
1023 /* ARGSUSED */
1024 static int
1025 termp_ns_pre(DECL_ARGS)
1026 {
1027
1028 p->flags |= TERMP_NOSPACE;
1029 return(1);
1030 }
1031
1032
1033 /* ARGSUSED */
1034 static int
1035 termp_pp_pre(DECL_ARGS)
1036 {
1037
1038 term_vspace(p);
1039 return(1);
1040 }
1041
1042
1043 /* ARGSUSED */
1044 static int
1045 termp_st_pre(DECL_ARGS)
1046 {
1047 const char *cp;
1048
1049 if (node->child && (cp = mdoc_a2st(node->child->string)))
1050 term_word(p, cp);
1051 return(0);
1052 }
1053
1054
1055 /* ARGSUSED */
1056 static int
1057 termp_rs_pre(DECL_ARGS)
1058 {
1059
1060 if (MDOC_BLOCK == node->type && node->prev)
1061 term_vspace(p);
1062 return(1);
1063 }
1064
1065
1066 /* ARGSUSED */
1067 static int
1068 termp_rv_pre(DECL_ARGS)
1069 {
1070 int i;
1071
1072 i = arg_getattr(MDOC_Std, node);
1073 assert(-1 != i);
1074 assert(node->args->argv[i].sz);
1075
1076 term_newln(p);
1077 term_word(p, "The");
1078
1079 p->flags |= ttypes[TTYPE_FUNC_NAME];
1080 term_word(p, *node->args->argv[i].value);
1081 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1082 p->flags |= TERMP_NOSPACE;
1083
1084 term_word(p, "() function returns the value 0 if successful;");
1085 term_word(p, "otherwise the value -1 is returned and the");
1086 term_word(p, "global variable");
1087
1088 p->flags |= ttypes[TTYPE_VAR_DECL];
1089 term_word(p, "errno");
1090 p->flags &= ~ttypes[TTYPE_VAR_DECL];
1091
1092 term_word(p, "is set to indicate the error.");
1093
1094 return(1);
1095 }
1096
1097
1098 /* ARGSUSED */
1099 static int
1100 termp_ex_pre(DECL_ARGS)
1101 {
1102 int i;
1103
1104 i = arg_getattr(MDOC_Std, node);
1105 assert(-1 != i);
1106 assert(node->args->argv[i].sz);
1107
1108 term_word(p, "The");
1109 p->flags |= ttypes[TTYPE_PROG];
1110 term_word(p, *node->args->argv[i].value);
1111 p->flags &= ~ttypes[TTYPE_PROG];
1112 term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1113
1114 return(1);
1115 }
1116
1117
1118 /* ARGSUSED */
1119 static int
1120 termp_nd_pre(DECL_ARGS)
1121 {
1122
1123 if (MDOC_BODY != node->type)
1124 return(1);
1125
1126 /*
1127 * XXX: signed off by jmc@openbsd.org. This technically
1128 * produces a minus sign after the Nd, which is wrong, but is
1129 * consistent with the historic OpenBSD tmac file.
1130 */
1131 #if defined(__OpenBSD__) || defined(__linux__)
1132 term_word(p, "\\-");
1133 #else
1134 term_word(p, "\\(em");
1135 #endif
1136 return(1);
1137 }
1138
1139
1140 /* ARGSUSED */
1141 static void
1142 termp_bl_post(DECL_ARGS)
1143 {
1144
1145 if (MDOC_BLOCK == node->type)
1146 term_newln(p);
1147 }
1148
1149
1150 /* ARGSUSED */
1151 static void
1152 termp_op_post(DECL_ARGS)
1153 {
1154
1155 if (MDOC_BODY != node->type)
1156 return;
1157 p->flags |= TERMP_NOSPACE;
1158 term_word(p, "\\(rB");
1159 }
1160
1161
1162 /* ARGSUSED */
1163 static int
1164 termp_xr_pre(DECL_ARGS)
1165 {
1166 const struct mdoc_node *n;
1167
1168 assert(node->child && MDOC_TEXT == node->child->type);
1169 n = node->child;
1170
1171 term_word(p, n->string);
1172 if (NULL == (n = n->next))
1173 return(0);
1174 p->flags |= TERMP_NOSPACE;
1175 term_word(p, "(");
1176 p->flags |= TERMP_NOSPACE;
1177 term_word(p, n->string);
1178 p->flags |= TERMP_NOSPACE;
1179 term_word(p, ")");
1180 return(0);
1181 }
1182
1183
1184 /* ARGSUSED */
1185 static int
1186 termp_vt_pre(DECL_ARGS)
1187 {
1188
1189 /* FIXME: this can be "type name". */
1190 pair->flag |= ttypes[TTYPE_VAR_DECL];
1191 return(1);
1192 }
1193
1194
1195 /* ARGSUSED */
1196 static void
1197 termp_vt_post(DECL_ARGS)
1198 {
1199
1200 if (node->sec != SEC_SYNOPSIS)
1201 return;
1202 if (node->next && MDOC_Vt == node->next->tok)
1203 term_newln(p);
1204 else if (node->next)
1205 term_vspace(p);
1206 }
1207
1208
1209 /* ARGSUSED */
1210 static int
1211 termp_fd_pre(DECL_ARGS)
1212 {
1213
1214 pair->flag |= ttypes[TTYPE_FUNC_DECL];
1215 return(1);
1216 }
1217
1218
1219 /* ARGSUSED */
1220 static void
1221 termp_fd_post(DECL_ARGS)
1222 {
1223
1224 if (node->sec != SEC_SYNOPSIS)
1225 return;
1226
1227 term_newln(p);
1228 if (node->next && MDOC_Fd != node->next->tok)
1229 term_vspace(p);
1230 }
1231
1232
1233 /* ARGSUSED */
1234 static int
1235 termp_sh_pre(DECL_ARGS)
1236 {
1237
1238 switch (node->type) {
1239 case (MDOC_HEAD):
1240 term_vspace(p);
1241 pair->flag |= ttypes[TTYPE_SECTION];
1242 break;
1243 case (MDOC_BODY):
1244 p->offset = INDENT;
1245 break;
1246 default:
1247 break;
1248 }
1249 return(1);
1250 }
1251
1252
1253 /* ARGSUSED */
1254 static void
1255 termp_sh_post(DECL_ARGS)
1256 {
1257
1258 switch (node->type) {
1259 case (MDOC_HEAD):
1260 term_newln(p);
1261 break;
1262 case (MDOC_BODY):
1263 term_newln(p);
1264 p->offset = 0;
1265 break;
1266 default:
1267 break;
1268 }
1269 }
1270
1271
1272 /* ARGSUSED */
1273 static int
1274 termp_op_pre(DECL_ARGS)
1275 {
1276
1277 switch (node->type) {
1278 case (MDOC_BODY):
1279 term_word(p, "\\(lB");
1280 p->flags |= TERMP_NOSPACE;
1281 break;
1282 default:
1283 break;
1284 }
1285 return(1);
1286 }
1287
1288
1289 /* ARGSUSED */
1290 static int
1291 termp_bt_pre(DECL_ARGS)
1292 {
1293
1294 term_word(p, "is currently in beta test.");
1295 return(1);
1296 }
1297
1298
1299 /* ARGSUSED */
1300 static int
1301 termp_lb_pre(DECL_ARGS)
1302 {
1303 const char *lb;
1304
1305 assert(node->child && MDOC_TEXT == node->child->type);
1306 lb = mdoc_a2lib(node->child->string);
1307 if (lb) {
1308 term_word(p, lb);
1309 return(0);
1310 }
1311 term_word(p, "library");
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