]>
git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.1 2009/03/26 14:38:11 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
31 /* FIXME: macro arguments can be escaped. */
32 /* FIXME: support more offset/width tokens. */
35 #define TTYPE_CMD_FLAG 1
36 #define TTYPE_CMD_ARG 2
37 #define TTYPE_SECTION 3
38 #define TTYPE_FUNC_DECL 4
39 #define TTYPE_VAR_DECL 5
40 #define TTYPE_FUNC_TYPE 6
41 #define TTYPE_FUNC_NAME 7
42 #define TTYPE_FUNC_ARG 8
44 #define TTYPE_SSECTION 10
47 #define TTYPE_CONFIG 13
49 #define TTYPE_INCLUDE 15
51 #define TTYPE_SYMBOL 17
53 #define TTYPE_LINK_ANCHOR 19
54 #define TTYPE_LINK_TEXT 20
55 #define TTYPE_REF_JOURNAL 21
59 const int ttypes
[TTYPE_NMAX
] = {
60 TERMP_BOLD
, /* TTYPE_PROG */
61 TERMP_BOLD
, /* TTYPE_CMD_FLAG */
62 TERMP_UNDER
, /* TTYPE_CMD_ARG */
63 TERMP_BOLD
, /* TTYPE_SECTION */
64 TERMP_BOLD
, /* TTYPE_FUNC_DECL */
65 TERMP_UNDER
, /* TTYPE_VAR_DECL */
66 TERMP_UNDER
, /* TTYPE_FUNC_TYPE */
67 TERMP_BOLD
, /* TTYPE_FUNC_NAME */
68 TERMP_UNDER
, /* TTYPE_FUNC_ARG */
69 TERMP_UNDER
, /* TTYPE_LINK */
70 TERMP_BOLD
, /* TTYPE_SSECTION */
71 TERMP_UNDER
, /* TTYPE_FILE */
72 TERMP_UNDER
, /* TTYPE_EMPH */
73 TERMP_BOLD
, /* TTYPE_CONFIG */
74 TERMP_BOLD
, /* TTYPE_CMD */
75 TERMP_BOLD
, /* TTYPE_INCLUDE */
76 TERMP_BOLD
, /* TTYPE_SYMB */
77 TERMP_BOLD
, /* TTYPE_SYMBOL */
78 TERMP_BOLD
, /* TTYPE_DIAG */
79 TERMP_UNDER
, /* TTYPE_LINK_ANCHOR */
80 TERMP_BOLD
, /* TTYPE_LINK_TEXT */
81 TERMP_UNDER
, /* TTYPE_REF_JOURNAL */
82 TERMP_BOLD
/* TTYPE_LIST */
85 /* XXX - clean this up. */
88 struct termpair
*ppair
;
90 #define TERMPAIR_FLAG (1 << 0)
97 #define TERMPAIR_SETFLAG(termp, p, fl) \
99 assert(! (TERMPAIR_FLAG & (p)->type)); \
100 (termp)->flags |= (fl); \
102 (p)->type |= TERMPAIR_FLAG; \
103 } while ( /* CONSTCOND */ 0)
106 struct termp *p, struct termpair *pair, \
107 const struct mdoc_meta *meta, \
108 const struct mdoc_node *node
110 #define DECL_PRE(name) \
111 static int name##_pre(DECL_ARGS)
112 #define DECL_POST(name) \
113 static void name##_post(DECL_ARGS)
114 #define DECL_PREPOST(name) \
118 DECL_PREPOST(termp__t
);
119 DECL_PREPOST(termp_aq
);
120 DECL_PREPOST(termp_bd
);
121 DECL_PREPOST(termp_bq
);
122 DECL_PREPOST(termp_brq
);
123 DECL_PREPOST(termp_d1
);
124 DECL_PREPOST(termp_dq
);
125 DECL_PREPOST(termp_fd
);
126 DECL_PREPOST(termp_fn
);
127 DECL_PREPOST(termp_fo
);
128 DECL_PREPOST(termp_ft
);
129 DECL_PREPOST(termp_in
);
130 DECL_PREPOST(termp_it
);
131 DECL_PREPOST(termp_lb
);
132 DECL_PREPOST(termp_op
);
133 DECL_PREPOST(termp_pf
);
134 DECL_PREPOST(termp_pq
);
135 DECL_PREPOST(termp_qq
);
136 DECL_PREPOST(termp_sh
);
137 DECL_PREPOST(termp_ss
);
138 DECL_PREPOST(termp_sq
);
139 DECL_PREPOST(termp_vt
);
183 int (*pre
)(DECL_ARGS
);
184 void (*post
)(DECL_ARGS
);
187 static const struct termact termacts
[MDOC_MAX
] = {
188 { NULL
, NULL
}, /* \" */
189 { NULL
, NULL
}, /* Dd */
190 { NULL
, NULL
}, /* Dt */
191 { NULL
, NULL
}, /* Os */
192 { termp_sh_pre
, termp_sh_post
}, /* Sh */
193 { termp_ss_pre
, termp_ss_post
}, /* Ss */
194 { termp_pp_pre
, NULL
}, /* Pp */
195 { termp_d1_pre
, termp_d1_post
}, /* D1 */
196 { termp_d1_pre
, termp_d1_post
}, /* Dl */
197 { termp_bd_pre
, termp_bd_post
}, /* Bd */
198 { NULL
, NULL
}, /* Ed */
199 { NULL
, termp_bl_post
}, /* Bl */
200 { NULL
, NULL
}, /* El */
201 { termp_it_pre
, termp_it_post
}, /* It */
202 { NULL
, NULL
}, /* Ad */
203 { NULL
, NULL
}, /* An */
204 { termp_ar_pre
, NULL
}, /* Ar */
205 { termp_cd_pre
, NULL
}, /* Cd */
206 { termp_cm_pre
, NULL
}, /* Cm */
207 { NULL
, NULL
}, /* Dv */
208 { NULL
, NULL
}, /* Er */
209 { NULL
, NULL
}, /* Ev */
210 { termp_ex_pre
, NULL
}, /* Ex */
211 { termp_fa_pre
, NULL
}, /* Fa */
212 { termp_fd_pre
, termp_fd_post
}, /* Fd */
213 { termp_fl_pre
, NULL
}, /* Fl */
214 { termp_fn_pre
, termp_fn_post
}, /* Fn */
215 { termp_ft_pre
, termp_ft_post
}, /* Ft */
216 { termp_ic_pre
, NULL
}, /* Ic */
217 { termp_in_pre
, termp_in_post
}, /* In */
218 { NULL
, NULL
}, /* Li */
219 { termp_nd_pre
, NULL
}, /* Nd */
220 { termp_nm_pre
, NULL
}, /* Nm */
221 { termp_op_pre
, termp_op_post
}, /* Op */
222 { NULL
, NULL
}, /* Ot */
223 { termp_pa_pre
, NULL
}, /* Pa */
224 { termp_rv_pre
, NULL
}, /* Rv */
225 { termp_st_pre
, NULL
}, /* St */
226 { termp_va_pre
, NULL
}, /* Va */
227 { termp_vt_pre
, termp_vt_post
}, /* Vt */
228 { termp_xr_pre
, NULL
}, /* Xr */
229 { NULL
, termp____post
}, /* %A */
230 { NULL
, termp____post
}, /* %B */
231 { NULL
, termp____post
}, /* %D */
232 { NULL
, termp____post
}, /* %I */
233 { termp__j_pre
, termp____post
}, /* %J */
234 { NULL
, termp____post
}, /* %N */
235 { NULL
, termp____post
}, /* %O */
236 { NULL
, termp____post
}, /* %P */
237 { NULL
, termp____post
}, /* %R */
238 { termp__t_pre
, termp__t_post
}, /* %T */
239 { NULL
, termp____post
}, /* %V */
240 { NULL
, NULL
}, /* Ac */
241 { termp_aq_pre
, termp_aq_post
}, /* Ao */
242 { termp_aq_pre
, termp_aq_post
}, /* Aq */
243 { termp_at_pre
, NULL
}, /* At */
244 { NULL
, NULL
}, /* Bc */
245 { termp_bf_pre
, NULL
}, /* Bf */
246 { termp_bq_pre
, termp_bq_post
}, /* Bo */
247 { termp_bq_pre
, termp_bq_post
}, /* Bq */
248 { termp_bsx_pre
, NULL
}, /* Bsx */
249 { NULL
, termp_bx_post
}, /* Bx */
250 { NULL
, NULL
}, /* Db */
251 { NULL
, NULL
}, /* Dc */
252 { termp_dq_pre
, termp_dq_post
}, /* Do */
253 { termp_dq_pre
, termp_dq_post
}, /* Dq */
254 { NULL
, NULL
}, /* Ec */
255 { NULL
, NULL
}, /* Ef */
256 { termp_em_pre
, NULL
}, /* Em */
257 { NULL
, NULL
}, /* Eo */
258 { termp_fx_pre
, NULL
}, /* Fx */
259 { termp_ms_pre
, NULL
}, /* Ms */
260 { NULL
, NULL
}, /* No */
261 { termp_ns_pre
, NULL
}, /* Ns */
262 { termp_nx_pre
, NULL
}, /* Nx */
263 { termp_ox_pre
, NULL
}, /* Ox */
264 { NULL
, NULL
}, /* Pc */
265 { termp_pf_pre
, termp_pf_post
}, /* Pf */
266 { termp_pq_pre
, termp_pq_post
}, /* Po */
267 { termp_pq_pre
, termp_pq_post
}, /* Pq */
268 { NULL
, NULL
}, /* Qc */
269 { termp_sq_pre
, termp_sq_post
}, /* Ql */
270 { termp_qq_pre
, termp_qq_post
}, /* Qo */
271 { termp_qq_pre
, termp_qq_post
}, /* Qq */
272 { NULL
, NULL
}, /* Re */
273 { termp_rs_pre
, NULL
}, /* Rs */
274 { NULL
, NULL
}, /* Sc */
275 { termp_sq_pre
, termp_sq_post
}, /* So */
276 { termp_sq_pre
, termp_sq_post
}, /* Sq */
277 { termp_sm_pre
, NULL
}, /* Sm */
278 { termp_sx_pre
, NULL
}, /* Sx */
279 { termp_sy_pre
, NULL
}, /* Sy */
280 { NULL
, NULL
}, /* Tn */
281 { termp_ux_pre
, NULL
}, /* Ux */
282 { NULL
, NULL
}, /* Xc */
283 { NULL
, NULL
}, /* Xo */
284 { termp_fo_pre
, termp_fo_post
}, /* Fo */
285 { NULL
, NULL
}, /* Fc */
286 { termp_op_pre
, termp_op_post
}, /* Oo */
287 { NULL
, NULL
}, /* Oc */
288 { NULL
, NULL
}, /* Bk */
289 { NULL
, NULL
}, /* Ek */
290 { termp_bt_pre
, NULL
}, /* Bt */
291 { NULL
, NULL
}, /* Hf */
292 { NULL
, NULL
}, /* Fr */
293 { termp_ud_pre
, NULL
}, /* Ud */
294 { termp_lb_pre
, termp_lb_post
}, /* Lb */
295 { termp_ap_pre
, NULL
}, /* Lb */
296 { termp_pp_pre
, NULL
}, /* Pp */
297 { termp_lk_pre
, NULL
}, /* Lk */
298 { termp_mt_pre
, NULL
}, /* Mt */
299 { termp_brq_pre
, termp_brq_post
}, /* Brq */
300 { termp_brq_pre
, termp_brq_post
}, /* Bro */
301 { NULL
, NULL
}, /* Brc */
302 { NULL
, NULL
}, /* %C */
303 { NULL
, NULL
}, /* Es */
304 { NULL
, NULL
}, /* En */
305 { termp_dx_pre
, NULL
}, /* Dx */
306 { NULL
, NULL
}, /* %Q */
309 static int arg_hasattr(int, const struct mdoc_node
*);
310 static int arg_getattrs(const int *, int *, size_t,
311 const struct mdoc_node
*);
312 static int arg_getattr(int, const struct mdoc_node
*);
313 static size_t arg_offset(const struct mdoc_argv
*);
314 static size_t arg_width(const struct mdoc_argv
*, int);
315 static int arg_listtype(const struct mdoc_node
*);
316 static int fmt_block_vspace(struct termp
*,
317 const struct mdoc_node
*,
318 const struct mdoc_node
*);
319 static int print_node(DECL_ARGS
);
320 static int print_head(struct termp
*,
321 const struct mdoc_meta
*);
322 static int print_body(DECL_ARGS
);
323 static int print_foot(struct termp
*,
324 const struct mdoc_meta
*);
325 static void sanity(const struct mdoc_node
*);
329 mdoc_run(struct termp
*p
, const struct mdoc
*m
)
332 if ( ! print_head(p
, mdoc_meta(m
)))
334 if ( ! print_body(p
, NULL
, mdoc_meta(m
), mdoc_node(m
)))
336 return(print_foot(p
, mdoc_meta(m
)));
341 print_body(DECL_ARGS
)
344 if ( ! print_node(p
, pair
, meta
, node
))
348 return(print_body(p
, pair
, meta
, node
->next
));
353 print_node(DECL_ARGS
)
356 struct termpair npair
;
358 /* Some quick sanity-checking. */
362 /* Pre-processing. */
367 npair
.offset
= npair
.rmargin
= 0;
371 if (MDOC_TEXT
!= node
->type
) {
372 if (termacts
[node
->tok
].pre
)
373 if ( ! (*termacts
[node
->tok
].pre
)(p
, &npair
, meta
, node
))
375 } else /* MDOC_TEXT == node->type */
376 term_word(p
, node
->string
);
380 if (TERMPAIR_FLAG
& npair
.type
)
381 p
->flags
|= npair
.flag
;
383 if (dochild
&& node
->child
)
384 print_body(p
, &npair
, meta
, node
->child
);
386 if (TERMPAIR_FLAG
& npair
.type
)
387 p
->flags
&= ~npair
.flag
;
389 /* Post-processing. */
391 if (MDOC_TEXT
!= node
->type
)
392 if (termacts
[node
->tok
].post
)
393 (*termacts
[node
->tok
].post
)(p
, &npair
, meta
, node
);
400 print_foot(struct termp
*p
, const struct mdoc_meta
*meta
)
405 if (NULL
== (buf
= malloc(p
->rmargin
)))
407 if (NULL
== (os
= malloc(p
->rmargin
)))
410 tm
= localtime(&meta
->date
);
413 if (NULL
== strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
415 if (0 == strftime(buf
, p
->rmargin
, "%B %d, %Y", tm
))
419 (void)strlcpy(os
, meta
->os
, p
->rmargin
);
422 * This is /slightly/ different from regular groff output
423 * because we don't have page numbers. Print the following:
430 p
->flags
|= TERMP_NOSPACE
| TERMP_NOBREAK
;
431 p
->rmargin
= p
->maxrmargin
- strlen(buf
);
437 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
438 p
->offset
= p
->rmargin
;
439 p
->rmargin
= p
->maxrmargin
;
440 p
->flags
&= ~TERMP_NOBREAK
;
453 print_head(struct termp
*p
, const struct mdoc_meta
*meta
)
457 p
->rmargin
= p
->maxrmargin
;
460 if (NULL
== (buf
= malloc(p
->rmargin
)))
462 if (NULL
== (title
= malloc(p
->rmargin
)))
466 * The header is strange. It has three components, which are
467 * really two with the first duplicated. It goes like this:
469 * IDENTIFIER TITLE IDENTIFIER
471 * The IDENTIFIER is NAME(SECTION), which is the command-name
472 * (if given, or "unknown" if not) followed by the manual page
473 * section. These are given in `Dt'. The TITLE is a free-form
474 * string depending on the manual volume. If not specified, it
475 * switches on the manual section.
479 (void)strlcpy(buf
, meta
->vol
, p
->rmargin
);
482 (void)strlcat(buf
, " (", p
->rmargin
);
483 (void)strlcat(buf
, meta
->arch
, p
->rmargin
);
484 (void)strlcat(buf
, ")", p
->rmargin
);
487 (void)snprintf(title
, p
->rmargin
, "%s(%d)",
488 meta
->title
, meta
->msec
);
491 p
->rmargin
= (p
->maxrmargin
- strlen(buf
)) / 2;
492 p
->flags
|= TERMP_NOBREAK
| TERMP_NOSPACE
;
497 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
498 p
->offset
= p
->rmargin
;
499 p
->rmargin
= p
->maxrmargin
- strlen(title
);
504 p
->offset
= p
->rmargin
;
505 p
->rmargin
= p
->maxrmargin
;
506 p
->flags
&= ~TERMP_NOBREAK
;
507 p
->flags
|= TERMP_NOLPAD
| TERMP_NOSPACE
;
512 p
->rmargin
= p
->maxrmargin
;
514 p
->flags
&= ~TERMP_NOSPACE
;
524 sanity(const struct mdoc_node
*n
)
528 p
= "regular form violated";
534 if (NULL
== n
->parent
)
536 if (NULL
== n
->string
)
538 switch (n
->parent
->type
) {
549 if (NULL
== n
->parent
)
551 switch (n
->parent
->type
) {
562 if (n
->child
) switch (n
->child
->type
) {
575 if (NULL
== n
->parent
)
577 if (MDOC_BLOCK
!= n
->parent
->type
)
579 if (n
->child
) switch (n
->child
->type
) {
592 if (NULL
== n
->parent
)
594 if (NULL
== n
->child
)
596 switch (n
->parent
->type
) {
609 switch (n
->child
->type
) {
622 if (NULL
== n
->child
)
624 switch (n
->child
->type
) {
637 arg_width(const struct mdoc_argv
*arg
, int pos
)
642 assert(pos
< (int)arg
->sz
&& pos
>= 0);
643 assert(arg
->value
[pos
]);
644 if (0 == strcmp(arg
->value
[pos
], "indent"))
646 if (0 == strcmp(arg
->value
[pos
], "indent-two"))
649 if (0 == (len
= (int)strlen(arg
->value
[pos
])))
652 for (i
= 0; i
< len
- 1; i
++)
653 if ( ! isdigit((u_char
)arg
->value
[pos
][i
]))
657 if ('n' == arg
->value
[pos
][len
- 1]) {
658 v
= (size_t)atoi(arg
->value
[pos
]);
663 return(strlen(arg
->value
[pos
]) + 1);
668 arg_listtype(const struct mdoc_node
*n
)
672 assert(MDOC_BLOCK
== n
->type
);
674 len
= (int)(n
->args
? n
->args
->argc
: 0);
676 for (i
= 0; i
< len
; i
++)
677 switch (n
->args
->argv
[i
].arg
) {
697 return(n
->args
->argv
[i
].arg
);
702 errx(1, "list type not supported");
708 arg_offset(const struct mdoc_argv
*arg
)
712 if (0 == strcmp(*arg
->value
, "indent"))
714 if (0 == strcmp(*arg
->value
, "indent-two"))
716 return(strlen(*arg
->value
));
721 arg_hasattr(int arg
, const struct mdoc_node
*n
)
724 return(-1 != arg_getattr(arg
, n
));
729 arg_getattr(int v
, const struct mdoc_node
*n
)
733 return(arg_getattrs(&v
, &val
, 1, n
) ? val
: -1);
738 arg_getattrs(const int *keys
, int *vals
,
739 size_t sz
, const struct mdoc_node
*n
)
746 for (k
= i
= 0; i
< (int)n
->args
->argc
; i
++)
747 for (j
= 0; j
< (int)sz
; j
++)
748 if (n
->args
->argv
[i
].arg
== keys
[j
]) {
758 fmt_block_vspace(struct termp
*p
,
759 const struct mdoc_node
*bl
,
760 const struct mdoc_node
*node
)
762 const struct mdoc_node
*n
;
766 if (arg_hasattr(MDOC_Compact
, bl
))
769 for (n
= node
; n
; n
= n
->parent
) {
770 if (MDOC_BLOCK
!= n
->type
)
772 if (MDOC_Ss
== n
->tok
)
774 if (MDOC_Sh
== n
->tok
)
788 termp_dq_pre(DECL_ARGS
)
791 if (MDOC_BODY
!= node
->type
)
794 term_word(p
, "\\(lq");
795 p
->flags
|= TERMP_NOSPACE
;
802 termp_dq_post(DECL_ARGS
)
805 if (MDOC_BODY
!= node
->type
)
808 p
->flags
|= TERMP_NOSPACE
;
809 term_word(p
, "\\(rq");
815 termp_it_pre(DECL_ARGS
)
817 const struct mdoc_node
*bl
, *n
;
819 int i
, type
, keys
[3], vals
[3];
820 size_t width
, offset
;
822 if (MDOC_BLOCK
== node
->type
)
823 return(fmt_block_vspace(p
, node
->parent
->parent
, node
));
825 bl
= node
->parent
->parent
->parent
;
827 /* Save parent attributes. */
829 pair
->offset
= p
->offset
;
830 pair
->rmargin
= p
->rmargin
;
831 pair
->flag
= p
->flags
;
833 /* Get list width and offset. */
835 keys
[0] = MDOC_Width
;
836 keys
[1] = MDOC_Offset
;
837 keys
[2] = MDOC_Column
;
839 vals
[0] = vals
[1] = vals
[2] = -1;
843 (void)arg_getattrs(keys
, vals
, 3, bl
);
845 type
= arg_listtype(bl
);
847 /* Calculate real width and offset. */
851 if (MDOC_BODY
== node
->type
)
853 for (i
= 0, n
= node
->prev
; n
; n
= n
->prev
, i
++)
855 (&bl
->args
->argv
[vals
[2]], i
);
856 assert(i
< (int)bl
->args
->argv
[vals
[2]].sz
);
857 width
= arg_width(&bl
->args
->argv
[vals
[2]], i
);
859 offset
+= arg_offset(&bl
->args
->argv
[vals
[1]]);
863 width
= arg_width(&bl
->args
->argv
[vals
[0]], 0);
865 offset
= arg_offset(&bl
->args
->argv
[vals
[1]]);
870 * List-type can override the width in the case of fixed-head
871 * values (bullet, dash/hyphen, enum). Tags need a non-zero
895 * Whitespace control. Inset bodies need an initial space.
902 if (MDOC_BODY
== node
->type
)
903 p
->flags
&= ~TERMP_NOSPACE
;
905 p
->flags
|= TERMP_NOSPACE
;
908 p
->flags
|= TERMP_NOSPACE
;
913 * Style flags. Diagnostic heads need TTYPE_DIAG.
918 if (MDOC_HEAD
== node
->type
)
919 p
->flags
|= ttypes
[TTYPE_DIAG
];
926 * Pad and break control. This is the tricker part. Lists with
927 * set right-margins for the head get TERMP_NOBREAK because, if
928 * they overrun the margin, they wrap to the new margin.
929 * Correspondingly, the body for these types don't left-pad, as
930 * the head will pad out to to the right.
943 if (MDOC_HEAD
== node
->type
)
944 p
->flags
|= TERMP_NOBREAK
;
946 p
->flags
|= TERMP_NOLPAD
;
947 if (MDOC_HEAD
== node
->type
&& MDOC_Tag
== type
)
948 if (NULL
== node
->next
||
949 NULL
== node
->next
->child
)
950 p
->flags
|= TERMP_NONOBREAK
;
953 if (MDOC_HEAD
== node
->type
) {
955 if (MDOC_BODY
== node
->next
->type
)
956 p
->flags
&= ~TERMP_NOBREAK
;
958 p
->flags
|= TERMP_NOBREAK
;
960 p
->flags
|= TERMP_NOLPAD
;
964 if (MDOC_HEAD
== node
->type
)
965 p
->flags
|= TERMP_NOBREAK
;
972 * Margin control. Set-head-width lists have their right
973 * margins shortened. The body for these lists has the offset
974 * necessarily lengthened. Everybody gets the offset.
989 if (MDOC_HEAD
== node
->type
)
990 p
->rmargin
= p
->offset
+ width
;
995 p
->rmargin
= p
->offset
+ width
;
1002 * The dash, hyphen, bullet and enum lists all have a special
1003 * HEAD character. Print it now.
1006 if (MDOC_HEAD
== node
->type
)
1009 term_word(p
, "\\[bu]");
1014 term_word(p
, "\\-");
1017 (pair
->ppair
->ppair
->count
)++;
1018 (void)snprintf(buf
, sizeof(buf
), "%d.",
1019 pair
->ppair
->ppair
->count
);
1027 * If we're not going to process our children, indicate so here.
1040 if (MDOC_HEAD
== node
->type
)
1044 if (MDOC_BODY
== node
->type
)
1057 termp_it_post(DECL_ARGS
)
1061 if (MDOC_BODY
!= node
->type
&& MDOC_HEAD
!= node
->type
)
1064 type
= arg_listtype(node
->parent
->parent
->parent
);
1072 if (MDOC_BODY
== node
->type
)
1076 if (MDOC_HEAD
== node
->type
)
1084 p
->offset
= pair
->offset
;
1085 p
->rmargin
= pair
->rmargin
;
1086 p
->flags
= pair
->flag
;
1092 termp_nm_pre(DECL_ARGS
)
1095 if (SEC_SYNOPSIS
== node
->sec
)
1098 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_PROG
]);
1099 if (NULL
== node
->child
)
1100 term_word(p
, meta
->name
);
1108 termp_fl_pre(DECL_ARGS
)
1111 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_CMD_FLAG
]);
1112 term_word(p
, "\\-");
1113 p
->flags
|= TERMP_NOSPACE
;
1120 termp_ar_pre(DECL_ARGS
)
1123 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_CMD_ARG
]);
1130 termp_ns_pre(DECL_ARGS
)
1133 p
->flags
|= TERMP_NOSPACE
;
1140 termp_pp_pre(DECL_ARGS
)
1150 termp_st_pre(DECL_ARGS
)
1154 if (node
->child
&& (cp
= mdoc_a2st(node
->child
->string
)))
1162 termp_rs_pre(DECL_ARGS
)
1165 if (MDOC_BLOCK
== node
->type
&& node
->prev
)
1173 termp_rv_pre(DECL_ARGS
)
1177 if (-1 == (i
= arg_getattr(MDOC_Std
, node
)))
1178 errx(1, "expected -std argument");
1179 if (1 != node
->args
->argv
[i
].sz
)
1180 errx(1, "expected -std argument");
1183 term_word(p
, "The");
1185 p
->flags
|= ttypes
[TTYPE_FUNC_NAME
];
1186 term_word(p
, *node
->args
->argv
[i
].value
);
1187 p
->flags
&= ~ttypes
[TTYPE_FUNC_NAME
];
1188 p
->flags
|= TERMP_NOSPACE
;
1190 term_word(p
, "() function returns the value 0 if successful;");
1191 term_word(p
, "otherwise the value -1 is returned and the");
1192 term_word(p
, "global variable");
1194 p
->flags
|= ttypes
[TTYPE_VAR_DECL
];
1195 term_word(p
, "errno");
1196 p
->flags
&= ~ttypes
[TTYPE_VAR_DECL
];
1198 term_word(p
, "is set to indicate the error.");
1206 termp_ex_pre(DECL_ARGS
)
1210 if (-1 == (i
= arg_getattr(MDOC_Std
, node
)))
1211 errx(1, "expected -std argument");
1212 if (1 != node
->args
->argv
[i
].sz
)
1213 errx(1, "expected -std argument");
1215 term_word(p
, "The");
1216 p
->flags
|= ttypes
[TTYPE_PROG
];
1217 term_word(p
, *node
->args
->argv
[i
].value
);
1218 p
->flags
&= ~ttypes
[TTYPE_PROG
];
1219 term_word(p
, "utility exits 0 on success, and >0 if an error occurs.");
1227 termp_nd_pre(DECL_ARGS
)
1230 term_word(p
, "\\-");
1237 termp_bl_post(DECL_ARGS
)
1240 if (MDOC_BLOCK
== node
->type
)
1247 termp_op_post(DECL_ARGS
)
1250 if (MDOC_BODY
!= node
->type
)
1252 p
->flags
|= TERMP_NOSPACE
;
1253 term_word(p
, "\\(rB");
1259 termp_xr_pre(DECL_ARGS
)
1261 const struct mdoc_node
*n
;
1263 if (NULL
== (n
= node
->child
))
1264 errx(1, "expected text line argument");
1265 term_word(p
, n
->string
);
1266 if (NULL
== (n
= n
->next
))
1268 p
->flags
|= TERMP_NOSPACE
;
1270 p
->flags
|= TERMP_NOSPACE
;
1271 term_word(p
, n
->string
);
1272 p
->flags
|= TERMP_NOSPACE
;
1280 termp_vt_pre(DECL_ARGS
)
1283 /* FIXME: this can be "type name". */
1284 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_VAR_DECL
]);
1291 termp_vt_post(DECL_ARGS
)
1294 if (node
->sec
== SEC_SYNOPSIS
)
1301 termp_fd_pre(DECL_ARGS
)
1305 * FIXME: this naming is bad. This value is used, in general,
1306 * for the #include header or other preprocessor statement.
1308 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_FUNC_DECL
]);
1315 termp_fd_post(DECL_ARGS
)
1318 if (node
->sec
!= SEC_SYNOPSIS
)
1321 if (node
->next
&& MDOC_Fd
!= node
->next
->tok
)
1328 termp_sh_pre(DECL_ARGS
)
1331 switch (node
->type
) {
1334 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_SECTION
]);
1348 termp_sh_post(DECL_ARGS
)
1351 switch (node
->type
) {
1367 termp_op_pre(DECL_ARGS
)
1370 switch (node
->type
) {
1372 term_word(p
, "\\(lB");
1373 p
->flags
|= TERMP_NOSPACE
;
1384 termp_bt_pre(DECL_ARGS
)
1387 term_word(p
, "is currently in beta test.");
1394 termp_lb_pre(DECL_ARGS
)
1398 if (NULL
== node
->child
)
1399 errx(1, "expected text line argument");
1400 if ((lb
= mdoc_a2lib(node
->child
->string
))) {
1404 term_word(p
, "library");
1411 termp_lb_post(DECL_ARGS
)
1420 termp_ud_pre(DECL_ARGS
)
1423 term_word(p
, "currently under development.");
1430 termp_d1_pre(DECL_ARGS
)
1433 if (MDOC_BODY
!= node
->type
)
1436 p
->offset
+= (pair
->offset
= INDENT
);
1443 termp_d1_post(DECL_ARGS
)
1446 if (MDOC_BODY
!= node
->type
)
1449 p
->offset
-= pair
->offset
;
1455 termp_aq_pre(DECL_ARGS
)
1458 if (MDOC_BODY
!= node
->type
)
1460 term_word(p
, "\\(la");
1461 p
->flags
|= TERMP_NOSPACE
;
1468 termp_aq_post(DECL_ARGS
)
1471 if (MDOC_BODY
!= node
->type
)
1473 p
->flags
|= TERMP_NOSPACE
;
1474 term_word(p
, "\\(ra");
1480 termp_ft_pre(DECL_ARGS
)
1483 if (SEC_SYNOPSIS
== node
->sec
)
1484 if (node
->prev
&& MDOC_Fo
== node
->prev
->tok
)
1486 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_FUNC_TYPE
]);
1493 termp_ft_post(DECL_ARGS
)
1496 if (SEC_SYNOPSIS
== node
->sec
)
1503 termp_fn_pre(DECL_ARGS
)
1505 const struct mdoc_node
*n
;
1507 if (NULL
== node
->child
)
1508 errx(1, "expected text line arguments");
1510 /* FIXME: can be "type funcname" "type varname"... */
1512 p
->flags
|= ttypes
[TTYPE_FUNC_NAME
];
1513 term_word(p
, node
->child
->string
);
1514 p
->flags
&= ~ttypes
[TTYPE_FUNC_NAME
];
1516 p
->flags
|= TERMP_NOSPACE
;
1519 for (n
= node
->child
->next
; n
; n
= n
->next
) {
1520 p
->flags
|= ttypes
[TTYPE_FUNC_ARG
];
1521 term_word(p
, n
->string
);
1522 p
->flags
&= ~ttypes
[TTYPE_FUNC_ARG
];
1529 if (SEC_SYNOPSIS
== node
->sec
)
1538 termp_fn_post(DECL_ARGS
)
1541 if (node
->sec
== SEC_SYNOPSIS
&& node
->next
)
1549 termp_sx_pre(DECL_ARGS
)
1552 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_LINK
]);
1559 termp_fa_pre(DECL_ARGS
)
1561 struct mdoc_node
*n
;
1563 if (node
->parent
->tok
!= MDOC_Fo
) {
1564 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_FUNC_ARG
]);
1568 for (n
= node
->child
; n
; n
= n
->next
) {
1569 p
->flags
|= ttypes
[TTYPE_FUNC_ARG
];
1570 term_word(p
, n
->string
);
1571 p
->flags
&= ~ttypes
[TTYPE_FUNC_ARG
];
1576 if (node
->child
&& node
->next
&& node
->next
->tok
== MDOC_Fa
)
1585 termp_va_pre(DECL_ARGS
)
1588 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_VAR_DECL
]);
1595 termp_bd_pre(DECL_ARGS
)
1600 * This is fairly tricky due primarily to crappy documentation.
1601 * If -ragged or -filled are specified, the block does nothing
1602 * but change the indentation.
1604 * If, on the other hand, -unfilled or -literal are specified,
1605 * then the game changes. Text is printed exactly as entered in
1606 * the display: if a macro line, a newline is appended to the
1607 * line. Blank lines are allowed.
1610 if (MDOC_BLOCK
== node
->type
)
1611 return(fmt_block_vspace(p
, node
, node
));
1612 else if (MDOC_BODY
!= node
->type
)
1615 if (NULL
== node
->parent
->args
)
1616 errx(1, "missing display type");
1618 pair
->offset
= p
->offset
;
1620 for (type
= -1, i
= 0;
1621 i
< (int)node
->parent
->args
->argc
; i
++) {
1622 switch (node
->parent
->args
->argv
[i
].arg
) {
1627 case (MDOC_Unfilled
):
1629 case (MDOC_Literal
):
1630 type
= node
->parent
->args
->argv
[i
].arg
;
1631 i
= (int)node
->parent
->args
->argc
;
1638 if (NULL
== node
->parent
->args
)
1639 errx(1, "missing display type");
1641 i
= arg_getattr(MDOC_Offset
, node
->parent
);
1643 if (1 != node
->parent
->args
->argv
[i
].sz
)
1644 errx(1, "expected single value");
1645 p
->offset
+= arg_offset(&node
->parent
->args
->argv
[i
]);
1649 case (MDOC_Literal
):
1651 case (MDOC_Unfilled
):
1658 * Tricky. Iterate through all children. If we're on a
1659 * different parse line, append a newline and then the contents.
1663 p
->flags
|= TERMP_LITERAL
;
1664 ln
= node
->child
? node
->child
->line
: 0;
1666 for (node
= node
->child
; node
; node
= node
->next
) {
1667 if (ln
< node
->line
) {
1669 p
->flags
|= TERMP_NOSPACE
;
1672 print_node(p
, pair
, meta
, node
);
1681 termp_bd_post(DECL_ARGS
)
1684 if (MDOC_BODY
!= node
->type
)
1688 p
->flags
&= ~TERMP_LITERAL
;
1689 p
->offset
= pair
->offset
;
1690 p
->flags
|= TERMP_NOSPACE
;
1696 termp_qq_pre(DECL_ARGS
)
1699 if (MDOC_BODY
!= node
->type
)
1702 p
->flags
|= TERMP_NOSPACE
;
1709 termp_qq_post(DECL_ARGS
)
1712 if (MDOC_BODY
!= node
->type
)
1714 p
->flags
|= TERMP_NOSPACE
;
1721 termp_bsx_pre(DECL_ARGS
)
1724 term_word(p
, "BSDI BSD/OS");
1731 termp_bx_post(DECL_ARGS
)
1735 p
->flags
|= TERMP_NOSPACE
;
1736 term_word(p
, "BSD");
1742 termp_ox_pre(DECL_ARGS
)
1745 term_word(p
, "OpenBSD");
1752 termp_dx_pre(DECL_ARGS
)
1755 term_word(p
, "DragonFly");
1762 termp_ux_pre(DECL_ARGS
)
1765 term_word(p
, "UNIX");
1772 termp_fx_pre(DECL_ARGS
)
1775 term_word(p
, "FreeBSD");
1782 termp_nx_pre(DECL_ARGS
)
1785 term_word(p
, "NetBSD");
1792 termp_sq_pre(DECL_ARGS
)
1795 if (MDOC_BODY
!= node
->type
)
1797 term_word(p
, "\\(oq");
1798 p
->flags
|= TERMP_NOSPACE
;
1805 termp_sq_post(DECL_ARGS
)
1808 if (MDOC_BODY
!= node
->type
)
1810 p
->flags
|= TERMP_NOSPACE
;
1811 term_word(p
, "\\(aq");
1817 termp_pf_pre(DECL_ARGS
)
1820 p
->flags
|= TERMP_IGNDELIM
;
1827 termp_pf_post(DECL_ARGS
)
1830 p
->flags
&= ~TERMP_IGNDELIM
;
1831 p
->flags
|= TERMP_NOSPACE
;
1837 termp_ss_pre(DECL_ARGS
)
1840 switch (node
->type
) {
1847 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_SSECTION
]);
1848 p
->offset
= INDENT
/ 2;
1860 termp_ss_post(DECL_ARGS
)
1863 switch (node
->type
) {
1876 termp_pa_pre(DECL_ARGS
)
1879 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_FILE
]);
1886 termp_em_pre(DECL_ARGS
)
1889 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_EMPH
]);
1896 termp_cd_pre(DECL_ARGS
)
1899 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_CONFIG
]);
1907 termp_cm_pre(DECL_ARGS
)
1910 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_CMD_FLAG
]);
1917 termp_ic_pre(DECL_ARGS
)
1920 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_CMD
]);
1927 termp_in_pre(DECL_ARGS
)
1930 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_INCLUDE
]);
1931 term_word(p
, "#include");
1933 p
->flags
|= TERMP_NOSPACE
;
1940 termp_in_post(DECL_ARGS
)
1943 p
->flags
|= TERMP_NOSPACE
;
1947 if (SEC_SYNOPSIS
!= node
->sec
)
1949 if (node
->next
&& MDOC_In
!= node
->next
->tok
)
1956 termp_at_pre(DECL_ARGS
)
1963 att
= mdoc_a2att(node
->child
->string
);
1974 termp_brq_pre(DECL_ARGS
)
1977 if (MDOC_BODY
!= node
->type
)
1979 term_word(p
, "\\(lC");
1980 p
->flags
|= TERMP_NOSPACE
;
1987 termp_brq_post(DECL_ARGS
)
1990 if (MDOC_BODY
!= node
->type
)
1992 p
->flags
|= TERMP_NOSPACE
;
1993 term_word(p
, "\\(rC");
1999 termp_bq_pre(DECL_ARGS
)
2002 if (MDOC_BODY
!= node
->type
)
2004 term_word(p
, "\\(lB");
2005 p
->flags
|= TERMP_NOSPACE
;
2012 termp_bq_post(DECL_ARGS
)
2015 if (MDOC_BODY
!= node
->type
)
2017 p
->flags
|= TERMP_NOSPACE
;
2018 term_word(p
, "\\(rB");
2024 termp_pq_pre(DECL_ARGS
)
2027 if (MDOC_BODY
!= node
->type
)
2029 term_word(p
, "\\&(");
2030 p
->flags
|= TERMP_NOSPACE
;
2037 termp_pq_post(DECL_ARGS
)
2040 if (MDOC_BODY
!= node
->type
)
2048 termp_fo_pre(DECL_ARGS
)
2050 const struct mdoc_node
*n
;
2052 if (MDOC_BODY
== node
->type
) {
2054 p
->flags
|= TERMP_NOSPACE
;
2056 } else if (MDOC_HEAD
!= node
->type
)
2059 /* XXX - groff shows only first parameter */
2061 p
->flags
|= ttypes
[TTYPE_FUNC_NAME
];
2062 for (n
= node
->child
; n
; n
= n
->next
) {
2063 if (MDOC_TEXT
!= n
->type
)
2064 errx(1, "expected text line argument");
2065 term_word(p
, n
->string
);
2067 p
->flags
&= ~ttypes
[TTYPE_FUNC_NAME
];
2075 termp_fo_post(DECL_ARGS
)
2078 if (MDOC_BODY
!= node
->type
)
2080 p
->flags
|= TERMP_NOSPACE
;
2082 p
->flags
|= TERMP_NOSPACE
;
2090 termp_bf_pre(DECL_ARGS
)
2092 const struct mdoc_node
*n
;
2094 if (MDOC_HEAD
== node
->type
) {
2096 } else if (MDOC_BLOCK
!= node
->type
)
2099 if (NULL
== (n
= node
->head
->child
)) {
2100 if (arg_hasattr(MDOC_Emphasis
, node
))
2101 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_EMPH
]);
2102 else if (arg_hasattr(MDOC_Symbolic
, node
))
2103 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_SYMB
]);
2108 if (MDOC_TEXT
!= n
->type
)
2109 errx(1, "expected text line arguments");
2111 if (0 == strcmp("Em", n
->string
))
2112 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_EMPH
]);
2113 else if (0 == strcmp("Sy", n
->string
))
2114 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_EMPH
]);
2122 termp_sy_pre(DECL_ARGS
)
2125 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_SYMB
]);
2132 termp_ms_pre(DECL_ARGS
)
2135 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_SYMBOL
]);
2143 termp_sm_pre(DECL_ARGS
)
2146 if (NULL
== node
->child
|| MDOC_TEXT
!= node
->child
->type
)
2147 errx(1, "expected boolean line argument");
2149 if (0 == strcmp("on", node
->child
->string
)) {
2150 p
->flags
&= ~TERMP_NONOSPACE
;
2151 p
->flags
&= ~TERMP_NOSPACE
;
2153 p
->flags
|= TERMP_NONOSPACE
;
2161 termp_ap_pre(DECL_ARGS
)
2164 p
->flags
|= TERMP_NOSPACE
;
2165 term_word(p
, "\\(aq");
2166 p
->flags
|= TERMP_NOSPACE
;
2173 termp__j_pre(DECL_ARGS
)
2176 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_REF_JOURNAL
]);
2183 termp__t_pre(DECL_ARGS
)
2187 p
->flags
|= TERMP_NOSPACE
;
2194 termp__t_post(DECL_ARGS
)
2197 p
->flags
|= TERMP_NOSPACE
;
2199 termp____post(p
, pair
, meta
, node
);
2205 termp____post(DECL_ARGS
)
2208 p
->flags
|= TERMP_NOSPACE
;
2209 term_word(p
, node
->next
? "," : ".");
2215 termp_lk_pre(DECL_ARGS
)
2217 const struct mdoc_node
*n
;
2219 if (NULL
== (n
= node
->child
))
2220 errx(1, "expected line argument");
2222 p
->flags
|= ttypes
[TTYPE_LINK_ANCHOR
];
2223 term_word(p
, n
->string
);
2224 p
->flags
&= ~ttypes
[TTYPE_LINK_ANCHOR
];
2225 p
->flags
|= TERMP_NOSPACE
;
2228 p
->flags
|= ttypes
[TTYPE_LINK_TEXT
];
2229 for ( ; n
; n
= n
->next
) {
2230 term_word(p
, n
->string
);
2232 p
->flags
&= ~ttypes
[TTYPE_LINK_TEXT
];
2240 termp_mt_pre(DECL_ARGS
)
2243 TERMPAIR_SETFLAG(p
, pair
, ttypes
[TTYPE_LINK_ANCHOR
]);