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