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