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