Annotation of mandoc/mdoc_term.c, Revision 1.52
1.52 ! kristaps 1: /* $Id: mdoc_term.c,v 1.51 2009/07/21 13:34:13 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 *);
311: static int fmt_block_vspace(struct termp *,
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 */
662: static int
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))
672: return(1);
1.48 kristaps 673: /* XXX - not documented! */
674: else if (arg_hasattr(MDOC_Column, bl))
675: return(1);
1.1 kristaps 676:
677: for (n = node; n; n = n->parent) {
678: if (MDOC_BLOCK != n->type)
679: continue;
680: if (MDOC_Ss == n->tok)
681: break;
682: if (MDOC_Sh == n->tok)
683: break;
684: if (NULL == n->prev)
685: continue;
686: term_vspace(p);
687: break;
688: }
689:
690: return(1);
691: }
692:
693:
694: /* ARGSUSED */
695: static int
696: termp_dq_pre(DECL_ARGS)
697: {
698:
699: if (MDOC_BODY != node->type)
700: return(1);
701:
702: term_word(p, "\\(lq");
703: p->flags |= TERMP_NOSPACE;
704: return(1);
705: }
706:
707:
708: /* ARGSUSED */
709: static void
710: termp_dq_post(DECL_ARGS)
711: {
712:
713: if (MDOC_BODY != node->type)
714: return;
715:
716: p->flags |= TERMP_NOSPACE;
717: term_word(p, "\\(rq");
718: }
719:
720:
721: /* ARGSUSED */
722: static int
723: termp_it_pre(DECL_ARGS)
724: {
725: const struct mdoc_node *bl, *n;
726: char buf[7];
1.39 kristaps 727: int i, type, keys[3], vals[3];
1.1 kristaps 728: size_t width, offset;
729:
730: if (MDOC_BLOCK == node->type)
731: return(fmt_block_vspace(p, node->parent->parent, node));
732:
733: bl = node->parent->parent->parent;
734:
735: /* Save parent attributes. */
736:
737: pair->flag = p->flags;
738:
739: /* Get list width and offset. */
740:
741: keys[0] = MDOC_Width;
742: keys[1] = MDOC_Offset;
743: keys[2] = MDOC_Column;
744:
745: vals[0] = vals[1] = vals[2] = -1;
746:
747: width = offset = 0;
748:
749: (void)arg_getattrs(keys, vals, 3, bl);
750:
751: type = arg_listtype(bl);
1.38 kristaps 752: assert(-1 != type);
1.1 kristaps 753:
754: /* Calculate real width and offset. */
755:
756: switch (type) {
757: case (MDOC_Column):
758: if (MDOC_BODY == node->type)
759: break;
760: for (i = 0, n = node->prev; n; n = n->prev, i++)
761: offset += arg_width
762: (&bl->args->argv[vals[2]], i);
763: assert(i < (int)bl->args->argv[vals[2]].sz);
764: width = arg_width(&bl->args->argv[vals[2]], i);
765: if (vals[1] >= 0)
766: offset += arg_offset(&bl->args->argv[vals[1]]);
767: break;
768: default:
769: if (vals[0] >= 0)
770: width = arg_width(&bl->args->argv[vals[0]], 0);
771: if (vals[1] >= 0)
1.19 kristaps 772: offset += arg_offset(&bl->args->argv[vals[1]]);
1.1 kristaps 773: break;
774: }
775:
776: /*
777: * List-type can override the width in the case of fixed-head
778: * values (bullet, dash/hyphen, enum). Tags need a non-zero
1.38 kristaps 779: * offset. FIXME: double-check that correct.
1.1 kristaps 780: */
781:
782: switch (type) {
783: case (MDOC_Bullet):
784: /* FALLTHROUGH */
785: case (MDOC_Dash):
786: /* FALLTHROUGH */
787: case (MDOC_Hyphen):
788: if (width < 4)
789: width = 4;
790: break;
1.17 kristaps 791: case (MDOC_Enum):
792: if (width < 5)
793: width = 5;
794: break;
1.38 kristaps 795: case (MDOC_Hang):
1.39 kristaps 796: if (0 == width)
797: width = 8;
798: break;
1.49 kristaps 799: case (MDOC_Column):
800: /* FALLTHROUGH */
1.1 kristaps 801: case (MDOC_Tag):
802: if (0 == width)
803: width = 10;
804: break;
805: default:
806: break;
807: }
808:
809: /*
1.17 kristaps 810: * Whitespace control. Inset bodies need an initial space,
811: * while diagonal bodies need two.
1.1 kristaps 812: */
813:
1.51 kristaps 814: p->flags |= TERMP_NOSPACE;
815:
1.1 kristaps 816: switch (type) {
1.51 kristaps 817: case (MDOC_Diag):
818: term_word(p, "\\ \\ ");
819: break;
1.1 kristaps 820: case (MDOC_Inset):
821: if (MDOC_BODY == node->type)
1.51 kristaps 822: term_word(p, "\\ ");
1.1 kristaps 823: break;
824: default:
825: break;
826: }
827:
1.51 kristaps 828: p->flags |= TERMP_NOSPACE;
829:
1.1 kristaps 830: /*
831: * Style flags. Diagnostic heads need TTYPE_DIAG.
832: */
833:
834: switch (type) {
835: case (MDOC_Diag):
836: if (MDOC_HEAD == node->type)
837: p->flags |= ttypes[TTYPE_DIAG];
838: break;
839: default:
840: break;
841: }
842:
843: /*
844: * Pad and break control. This is the tricker part. Lists with
845: * set right-margins for the head get TERMP_NOBREAK because, if
846: * they overrun the margin, they wrap to the new margin.
847: * Correspondingly, the body for these types don't left-pad, as
848: * the head will pad out to to the right.
849: */
850:
851: switch (type) {
852: case (MDOC_Bullet):
853: /* FALLTHROUGH */
854: case (MDOC_Dash):
855: /* FALLTHROUGH */
856: case (MDOC_Enum):
857: /* FALLTHROUGH */
858: case (MDOC_Hyphen):
1.39 kristaps 859: if (MDOC_HEAD == node->type)
860: p->flags |= TERMP_NOBREAK;
861: else
862: p->flags |= TERMP_NOLPAD;
863: break;
1.38 kristaps 864: case (MDOC_Hang):
1.39 kristaps 865: if (MDOC_HEAD == node->type)
866: p->flags |= TERMP_NOBREAK;
867: else
868: p->flags |= TERMP_NOLPAD;
869:
870: if (MDOC_HEAD == node->type)
871: p->flags |= TERMP_HANG;
872: break;
1.1 kristaps 873: case (MDOC_Tag):
874: if (MDOC_HEAD == node->type)
1.51 kristaps 875: p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
1.1 kristaps 876: else
877: p->flags |= TERMP_NOLPAD;
1.38 kristaps 878:
1.39 kristaps 879: if (MDOC_HEAD != node->type)
880: break;
881: if (NULL == node->next || NULL == node->next->child)
882: p->flags |= TERMP_DANGLE;
1.1 kristaps 883: break;
884: case (MDOC_Column):
885: if (MDOC_HEAD == node->type) {
886: assert(node->next);
887: if (MDOC_BODY == node->next->type)
888: p->flags &= ~TERMP_NOBREAK;
889: else
890: p->flags |= TERMP_NOBREAK;
891: if (node->prev)
892: p->flags |= TERMP_NOLPAD;
893: }
894: break;
895: case (MDOC_Diag):
896: if (MDOC_HEAD == node->type)
897: p->flags |= TERMP_NOBREAK;
898: break;
899: default:
900: break;
901: }
902:
903: /*
904: * Margin control. Set-head-width lists have their right
905: * margins shortened. The body for these lists has the offset
906: * necessarily lengthened. Everybody gets the offset.
907: */
908:
909: p->offset += offset;
910:
911: switch (type) {
912: case (MDOC_Bullet):
913: /* FALLTHROUGH */
914: case (MDOC_Dash):
915: /* FALLTHROUGH */
916: case (MDOC_Enum):
917: /* FALLTHROUGH */
918: case (MDOC_Hyphen):
919: /* FALLTHROUGH */
1.38 kristaps 920: case (MDOC_Hang):
921: /* FALLTHROUGH */
1.1 kristaps 922: case (MDOC_Tag):
1.49 kristaps 923: assert(width);
1.1 kristaps 924: if (MDOC_HEAD == node->type)
925: p->rmargin = p->offset + width;
926: else
927: p->offset += width;
928: break;
929: case (MDOC_Column):
1.49 kristaps 930: assert(width);
1.1 kristaps 931: p->rmargin = p->offset + width;
1.50 kristaps 932: /*
933: * XXX - this behaviour is not documented: the
934: * right-most column is filled to the right margin.
935: */
936: if (MDOC_HEAD == node->type &&
937: MDOC_BODY == node->next->type)
938: p->rmargin = p->maxrmargin;
1.1 kristaps 939: break;
940: default:
941: break;
942: }
943:
944: /*
945: * The dash, hyphen, bullet and enum lists all have a special
1.15 kristaps 946: * HEAD character (temporarily bold, in some cases).
1.1 kristaps 947: */
948:
949: if (MDOC_HEAD == node->type)
950: switch (type) {
951: case (MDOC_Bullet):
1.15 kristaps 952: p->flags |= TERMP_BOLD;
1.1 kristaps 953: term_word(p, "\\[bu]");
1.39 kristaps 954: p->flags &= ~TERMP_BOLD;
1.1 kristaps 955: break;
956: case (MDOC_Dash):
957: /* FALLTHROUGH */
958: case (MDOC_Hyphen):
1.15 kristaps 959: p->flags |= TERMP_BOLD;
1.27 kristaps 960: term_word(p, "\\(hy");
1.39 kristaps 961: p->flags &= ~TERMP_BOLD;
1.1 kristaps 962: break;
963: case (MDOC_Enum):
964: (pair->ppair->ppair->count)++;
965: (void)snprintf(buf, sizeof(buf), "%d.",
966: pair->ppair->ppair->count);
967: term_word(p, buf);
968: break;
969: default:
970: break;
971: }
972:
973: /*
974: * If we're not going to process our children, indicate so here.
975: */
976:
977: switch (type) {
978: case (MDOC_Bullet):
979: /* FALLTHROUGH */
980: case (MDOC_Item):
981: /* FALLTHROUGH */
982: case (MDOC_Dash):
983: /* FALLTHROUGH */
984: case (MDOC_Hyphen):
985: /* FALLTHROUGH */
986: case (MDOC_Enum):
987: if (MDOC_HEAD == node->type)
988: return(0);
989: break;
990: case (MDOC_Column):
991: if (MDOC_BODY == node->type)
992: return(0);
993: break;
994: default:
995: break;
996: }
997:
998: return(1);
999: }
1000:
1001:
1002: /* ARGSUSED */
1003: static void
1004: termp_it_post(DECL_ARGS)
1005: {
1006: int type;
1007:
1008: if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
1009: return;
1010:
1011: type = arg_listtype(node->parent->parent->parent);
1.38 kristaps 1012: assert(-1 != type);
1.1 kristaps 1013:
1014: switch (type) {
1015: case (MDOC_Item):
1016: /* FALLTHROUGH */
1017: case (MDOC_Inset):
1018: if (MDOC_BODY == node->type)
1019: term_flushln(p);
1020: break;
1021: case (MDOC_Column):
1022: if (MDOC_HEAD == node->type)
1023: term_flushln(p);
1024: break;
1025: default:
1026: term_flushln(p);
1027: break;
1028: }
1029:
1030: p->flags = pair->flag;
1031: }
1032:
1033:
1034: /* ARGSUSED */
1035: static int
1036: termp_nm_pre(DECL_ARGS)
1037: {
1038:
1039: if (SEC_SYNOPSIS == node->sec)
1040: term_newln(p);
1041:
1.28 kristaps 1042: pair->flag |= ttypes[TTYPE_PROG];
1043: p->flags |= ttypes[TTYPE_PROG];
1044:
1.1 kristaps 1045: if (NULL == node->child)
1046: term_word(p, meta->name);
1047:
1048: return(1);
1049: }
1050:
1051:
1052: /* ARGSUSED */
1053: static int
1054: termp_fl_pre(DECL_ARGS)
1055: {
1056:
1.28 kristaps 1057: pair->flag |= ttypes[TTYPE_CMD_FLAG];
1058: p->flags |= ttypes[TTYPE_CMD_FLAG];
1.1 kristaps 1059: term_word(p, "\\-");
1060: p->flags |= TERMP_NOSPACE;
1061: return(1);
1062: }
1063:
1064:
1065: /* ARGSUSED */
1066: static int
1067: termp_ar_pre(DECL_ARGS)
1068: {
1069:
1.28 kristaps 1070: pair->flag |= ttypes[TTYPE_CMD_ARG];
1.1 kristaps 1071: return(1);
1072: }
1073:
1074:
1075: /* ARGSUSED */
1076: static int
1077: termp_ns_pre(DECL_ARGS)
1078: {
1079:
1080: p->flags |= TERMP_NOSPACE;
1081: return(1);
1082: }
1083:
1084:
1085: /* ARGSUSED */
1086: static int
1087: termp_pp_pre(DECL_ARGS)
1088: {
1089:
1090: term_vspace(p);
1091: return(1);
1092: }
1093:
1094:
1095: /* ARGSUSED */
1096: static int
1097: termp_rs_pre(DECL_ARGS)
1098: {
1099:
1100: if (MDOC_BLOCK == node->type && node->prev)
1101: term_vspace(p);
1102: return(1);
1103: }
1104:
1105:
1106: /* ARGSUSED */
1107: static int
1108: termp_rv_pre(DECL_ARGS)
1109: {
1110: int i;
1111:
1.34 kristaps 1112: i = arg_getattr(MDOC_Std, node);
1113: assert(-1 != i);
1114: assert(node->args->argv[i].sz);
1.1 kristaps 1115:
1116: term_newln(p);
1117: term_word(p, "The");
1118:
1119: p->flags |= ttypes[TTYPE_FUNC_NAME];
1120: term_word(p, *node->args->argv[i].value);
1121: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1122: p->flags |= TERMP_NOSPACE;
1123:
1124: term_word(p, "() function returns the value 0 if successful;");
1125: term_word(p, "otherwise the value -1 is returned and the");
1126: term_word(p, "global variable");
1127:
1128: p->flags |= ttypes[TTYPE_VAR_DECL];
1129: term_word(p, "errno");
1130: p->flags &= ~ttypes[TTYPE_VAR_DECL];
1131:
1132: term_word(p, "is set to indicate the error.");
1133:
1134: return(1);
1135: }
1136:
1137:
1138: /* ARGSUSED */
1139: static int
1140: termp_ex_pre(DECL_ARGS)
1141: {
1142: int i;
1143:
1.34 kristaps 1144: i = arg_getattr(MDOC_Std, node);
1145: assert(-1 != i);
1146: assert(node->args->argv[i].sz);
1.1 kristaps 1147:
1148: term_word(p, "The");
1149: p->flags |= ttypes[TTYPE_PROG];
1150: term_word(p, *node->args->argv[i].value);
1151: p->flags &= ~ttypes[TTYPE_PROG];
1152: term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1153:
1154: return(1);
1155: }
1156:
1157:
1158: /* ARGSUSED */
1159: static int
1160: termp_nd_pre(DECL_ARGS)
1161: {
1.25 kristaps 1162:
1163: if (MDOC_BODY != node->type)
1164: return(1);
1.30 kristaps 1165:
1.27 kristaps 1166: #if defined(__OpenBSD__) || defined(__linux__)
1.41 kristaps 1167: term_word(p, "\\(en");
1.23 kristaps 1168: #else
1169: term_word(p, "\\(em");
1170: #endif
1.1 kristaps 1171: return(1);
1172: }
1173:
1174:
1175: /* ARGSUSED */
1176: static void
1177: termp_bl_post(DECL_ARGS)
1178: {
1179:
1180: if (MDOC_BLOCK == node->type)
1181: term_newln(p);
1182: }
1183:
1184:
1185: /* ARGSUSED */
1186: static void
1187: termp_op_post(DECL_ARGS)
1188: {
1189:
1190: if (MDOC_BODY != node->type)
1191: return;
1192: p->flags |= TERMP_NOSPACE;
1193: term_word(p, "\\(rB");
1194: }
1195:
1196:
1197: /* ARGSUSED */
1198: static int
1199: termp_xr_pre(DECL_ARGS)
1200: {
1201: const struct mdoc_node *n;
1202:
1.11 kristaps 1203: assert(node->child && MDOC_TEXT == node->child->type);
1204: n = node->child;
1205:
1.1 kristaps 1206: term_word(p, n->string);
1207: if (NULL == (n = n->next))
1208: return(0);
1209: p->flags |= TERMP_NOSPACE;
1210: term_word(p, "(");
1211: p->flags |= TERMP_NOSPACE;
1212: term_word(p, n->string);
1213: p->flags |= TERMP_NOSPACE;
1214: term_word(p, ")");
1215: return(0);
1216: }
1217:
1218:
1219: /* ARGSUSED */
1220: static int
1221: termp_vt_pre(DECL_ARGS)
1222: {
1223:
1224: /* FIXME: this can be "type name". */
1.29 kristaps 1225: pair->flag |= ttypes[TTYPE_VAR_DECL];
1.1 kristaps 1226: return(1);
1227: }
1228:
1229:
1230: /* ARGSUSED */
1231: static void
1232: termp_vt_post(DECL_ARGS)
1233: {
1234:
1.32 kristaps 1235: if (node->sec != SEC_SYNOPSIS)
1236: return;
1237: if (node->next && MDOC_Vt == node->next->tok)
1238: term_newln(p);
1239: else if (node->next)
1.1 kristaps 1240: term_vspace(p);
1241: }
1242:
1243:
1244: /* ARGSUSED */
1245: static int
1246: termp_fd_pre(DECL_ARGS)
1247: {
1248:
1.29 kristaps 1249: pair->flag |= ttypes[TTYPE_FUNC_DECL];
1.1 kristaps 1250: return(1);
1251: }
1252:
1253:
1254: /* ARGSUSED */
1255: static void
1256: termp_fd_post(DECL_ARGS)
1257: {
1258:
1259: if (node->sec != SEC_SYNOPSIS)
1260: return;
1.29 kristaps 1261:
1.1 kristaps 1262: term_newln(p);
1263: if (node->next && MDOC_Fd != node->next->tok)
1264: term_vspace(p);
1265: }
1266:
1267:
1268: /* ARGSUSED */
1269: static int
1270: termp_sh_pre(DECL_ARGS)
1271: {
1272:
1273: switch (node->type) {
1274: case (MDOC_HEAD):
1275: term_vspace(p);
1.28 kristaps 1276: pair->flag |= ttypes[TTYPE_SECTION];
1.1 kristaps 1277: break;
1278: case (MDOC_BODY):
1279: p->offset = INDENT;
1280: break;
1281: default:
1282: break;
1283: }
1284: return(1);
1285: }
1286:
1287:
1288: /* ARGSUSED */
1289: static void
1290: termp_sh_post(DECL_ARGS)
1291: {
1292:
1293: switch (node->type) {
1294: case (MDOC_HEAD):
1295: term_newln(p);
1296: break;
1297: case (MDOC_BODY):
1298: term_newln(p);
1299: p->offset = 0;
1300: break;
1301: default:
1302: break;
1303: }
1304: }
1305:
1306:
1307: /* ARGSUSED */
1308: static int
1309: termp_op_pre(DECL_ARGS)
1310: {
1311:
1312: switch (node->type) {
1313: case (MDOC_BODY):
1314: term_word(p, "\\(lB");
1315: p->flags |= TERMP_NOSPACE;
1316: break;
1317: default:
1318: break;
1319: }
1320: return(1);
1321: }
1322:
1323:
1324: /* ARGSUSED */
1325: static int
1326: termp_bt_pre(DECL_ARGS)
1327: {
1328:
1329: term_word(p, "is currently in beta test.");
1330: return(1);
1331: }
1332:
1333:
1334: /* ARGSUSED */
1335: static void
1336: termp_lb_post(DECL_ARGS)
1337: {
1338:
1339: term_newln(p);
1340: }
1341:
1342:
1343: /* ARGSUSED */
1344: static int
1345: termp_ud_pre(DECL_ARGS)
1346: {
1347:
1348: term_word(p, "currently under development.");
1349: return(1);
1350: }
1351:
1352:
1353: /* ARGSUSED */
1354: static int
1355: termp_d1_pre(DECL_ARGS)
1356: {
1357:
1.4 kristaps 1358: if (MDOC_BLOCK != node->type)
1.1 kristaps 1359: return(1);
1360: term_newln(p);
1.29 kristaps 1361: p->offset += (INDENT + 1);
1.1 kristaps 1362: return(1);
1363: }
1364:
1365:
1366: /* ARGSUSED */
1367: static void
1368: termp_d1_post(DECL_ARGS)
1369: {
1370:
1.4 kristaps 1371: if (MDOC_BLOCK != node->type)
1.1 kristaps 1372: return;
1373: term_newln(p);
1374: }
1375:
1376:
1377: /* ARGSUSED */
1378: static int
1379: termp_aq_pre(DECL_ARGS)
1380: {
1381:
1382: if (MDOC_BODY != node->type)
1383: return(1);
1384: term_word(p, "\\(la");
1385: p->flags |= TERMP_NOSPACE;
1386: return(1);
1387: }
1388:
1389:
1390: /* ARGSUSED */
1391: static void
1392: termp_aq_post(DECL_ARGS)
1393: {
1394:
1395: if (MDOC_BODY != node->type)
1396: return;
1397: p->flags |= TERMP_NOSPACE;
1398: term_word(p, "\\(ra");
1399: }
1400:
1401:
1402: /* ARGSUSED */
1403: static int
1404: termp_ft_pre(DECL_ARGS)
1405: {
1406:
1407: if (SEC_SYNOPSIS == node->sec)
1408: if (node->prev && MDOC_Fo == node->prev->tok)
1409: term_vspace(p);
1.28 kristaps 1410: pair->flag |= ttypes[TTYPE_FUNC_TYPE];
1.1 kristaps 1411: return(1);
1412: }
1413:
1414:
1415: /* ARGSUSED */
1416: static void
1417: termp_ft_post(DECL_ARGS)
1418: {
1419:
1420: if (SEC_SYNOPSIS == node->sec)
1421: term_newln(p);
1422: }
1423:
1424:
1425: /* ARGSUSED */
1426: static int
1427: termp_fn_pre(DECL_ARGS)
1428: {
1429: const struct mdoc_node *n;
1430:
1.11 kristaps 1431: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1432:
1433: /* FIXME: can be "type funcname" "type varname"... */
1434:
1435: p->flags |= ttypes[TTYPE_FUNC_NAME];
1436: term_word(p, node->child->string);
1437: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1438:
1439: p->flags |= TERMP_NOSPACE;
1440: term_word(p, "(");
1441:
1442: for (n = node->child->next; n; n = n->next) {
1443: p->flags |= ttypes[TTYPE_FUNC_ARG];
1444: term_word(p, n->string);
1445: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1446: if (n->next)
1447: term_word(p, ",");
1448: }
1449:
1450: term_word(p, ")");
1451:
1452: if (SEC_SYNOPSIS == node->sec)
1453: term_word(p, ";");
1454:
1455: return(0);
1456: }
1457:
1458:
1459: /* ARGSUSED */
1460: static void
1461: termp_fn_post(DECL_ARGS)
1462: {
1463:
1464: if (node->sec == SEC_SYNOPSIS && node->next)
1465: term_vspace(p);
1466: }
1467:
1468:
1469: /* ARGSUSED */
1470: static int
1471: termp_sx_pre(DECL_ARGS)
1472: {
1473:
1.28 kristaps 1474: pair->flag |= ttypes[TTYPE_LINK];
1.1 kristaps 1475: return(1);
1476: }
1477:
1478:
1479: /* ARGSUSED */
1480: static int
1481: termp_fa_pre(DECL_ARGS)
1482: {
1483: struct mdoc_node *n;
1484:
1485: if (node->parent->tok != MDOC_Fo) {
1.28 kristaps 1486: pair->flag |= ttypes[TTYPE_FUNC_ARG];
1.1 kristaps 1487: return(1);
1488: }
1489:
1490: for (n = node->child; n; n = n->next) {
1491: p->flags |= ttypes[TTYPE_FUNC_ARG];
1492: term_word(p, n->string);
1493: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1494: if (n->next)
1495: term_word(p, ",");
1496: }
1497:
1498: if (node->child && node->next && node->next->tok == MDOC_Fa)
1499: term_word(p, ",");
1500:
1501: return(0);
1502: }
1503:
1504:
1505: /* ARGSUSED */
1506: static int
1507: termp_va_pre(DECL_ARGS)
1508: {
1509:
1.28 kristaps 1510: pair->flag |= ttypes[TTYPE_VAR_DECL];
1.1 kristaps 1511: return(1);
1512: }
1513:
1514:
1515: /* ARGSUSED */
1516: static int
1517: termp_bd_pre(DECL_ARGS)
1518: {
1519: int i, type, ln;
1520:
1521: /*
1522: * This is fairly tricky due primarily to crappy documentation.
1523: * If -ragged or -filled are specified, the block does nothing
1524: * but change the indentation.
1525: *
1526: * If, on the other hand, -unfilled or -literal are specified,
1527: * then the game changes. Text is printed exactly as entered in
1528: * the display: if a macro line, a newline is appended to the
1529: * line. Blank lines are allowed.
1530: */
1531:
1532: if (MDOC_BLOCK == node->type)
1533: return(fmt_block_vspace(p, node, node));
1534: else if (MDOC_BODY != node->type)
1535: return(1);
1536:
1.11 kristaps 1537: /* FIXME: display type should be mandated by parser. */
1538:
1.1 kristaps 1539: if (NULL == node->parent->args)
1540: errx(1, "missing display type");
1541:
1542: for (type = -1, i = 0;
1543: i < (int)node->parent->args->argc; i++) {
1544: switch (node->parent->args->argv[i].arg) {
1545: case (MDOC_Ragged):
1546: /* FALLTHROUGH */
1547: case (MDOC_Filled):
1548: /* FALLTHROUGH */
1549: case (MDOC_Unfilled):
1550: /* FALLTHROUGH */
1551: case (MDOC_Literal):
1552: type = node->parent->args->argv[i].arg;
1553: i = (int)node->parent->args->argc;
1554: break;
1555: default:
1556: break;
1557: }
1558: }
1559:
1560: if (NULL == node->parent->args)
1561: errx(1, "missing display type");
1562:
1563: i = arg_getattr(MDOC_Offset, node->parent);
1564: if (-1 != i) {
1565: if (1 != node->parent->args->argv[i].sz)
1566: errx(1, "expected single value");
1567: p->offset += arg_offset(&node->parent->args->argv[i]);
1568: }
1569:
1570: switch (type) {
1571: case (MDOC_Literal):
1572: /* FALLTHROUGH */
1573: case (MDOC_Unfilled):
1574: break;
1575: default:
1576: return(1);
1577: }
1578:
1579: /*
1580: * Tricky. Iterate through all children. If we're on a
1581: * different parse line, append a newline and then the contents.
1582: * Ew.
1583: */
1584:
1585: ln = node->child ? node->child->line : 0;
1586:
1587: for (node = node->child; node; node = node->next) {
1588: if (ln < node->line) {
1589: term_flushln(p);
1590: p->flags |= TERMP_NOSPACE;
1591: }
1592: ln = node->line;
1593: print_node(p, pair, meta, node);
1594: }
1595:
1596: return(0);
1597: }
1598:
1599:
1600: /* ARGSUSED */
1601: static void
1602: termp_bd_post(DECL_ARGS)
1603: {
1604:
1605: if (MDOC_BODY != node->type)
1606: return;
1607: term_flushln(p);
1608: }
1609:
1610:
1611: /* ARGSUSED */
1612: static int
1613: termp_qq_pre(DECL_ARGS)
1614: {
1615:
1616: if (MDOC_BODY != node->type)
1617: return(1);
1618: term_word(p, "\"");
1619: p->flags |= TERMP_NOSPACE;
1620: return(1);
1621: }
1622:
1623:
1624: /* ARGSUSED */
1625: static void
1626: termp_qq_post(DECL_ARGS)
1627: {
1628:
1629: if (MDOC_BODY != node->type)
1630: return;
1631: p->flags |= TERMP_NOSPACE;
1632: term_word(p, "\"");
1633: }
1634:
1635:
1636: /* ARGSUSED */
1637: static void
1638: termp_bx_post(DECL_ARGS)
1639: {
1640:
1641: if (node->child)
1642: p->flags |= TERMP_NOSPACE;
1643: term_word(p, "BSD");
1644: }
1645:
1646:
1647: /* ARGSUSED */
1648: static int
1.26 kristaps 1649: termp_xx_pre(DECL_ARGS)
1.1 kristaps 1650: {
1.26 kristaps 1651: const char *pp;
1.1 kristaps 1652:
1.26 kristaps 1653: pp = NULL;
1654: switch (node->tok) {
1655: case (MDOC_Bsx):
1656: pp = "BSDI BSD/OS";
1657: break;
1658: case (MDOC_Dx):
1659: pp = "DragonFlyBSD";
1660: break;
1661: case (MDOC_Fx):
1662: pp = "FreeBSD";
1663: break;
1664: case (MDOC_Nx):
1665: pp = "NetBSD";
1666: break;
1667: case (MDOC_Ox):
1668: pp = "OpenBSD";
1669: break;
1670: case (MDOC_Ux):
1671: pp = "UNIX";
1672: break;
1673: default:
1674: break;
1675: }
1.1 kristaps 1676:
1.26 kristaps 1677: assert(pp);
1678: term_word(p, pp);
1.1 kristaps 1679: return(1);
1680: }
1681:
1682:
1683: /* ARGSUSED */
1684: static int
1685: termp_sq_pre(DECL_ARGS)
1686: {
1687:
1688: if (MDOC_BODY != node->type)
1689: return(1);
1690: term_word(p, "\\(oq");
1691: p->flags |= TERMP_NOSPACE;
1692: return(1);
1693: }
1694:
1695:
1696: /* ARGSUSED */
1697: static void
1698: termp_sq_post(DECL_ARGS)
1699: {
1700:
1701: if (MDOC_BODY != node->type)
1702: return;
1703: p->flags |= TERMP_NOSPACE;
1704: term_word(p, "\\(aq");
1705: }
1706:
1707:
1708: /* ARGSUSED */
1709: static int
1710: termp_pf_pre(DECL_ARGS)
1711: {
1712:
1713: p->flags |= TERMP_IGNDELIM;
1714: return(1);
1715: }
1716:
1717:
1718: /* ARGSUSED */
1719: static void
1720: termp_pf_post(DECL_ARGS)
1721: {
1722:
1723: p->flags &= ~TERMP_IGNDELIM;
1724: p->flags |= TERMP_NOSPACE;
1725: }
1726:
1727:
1728: /* ARGSUSED */
1729: static int
1730: termp_ss_pre(DECL_ARGS)
1731: {
1732:
1733: switch (node->type) {
1734: case (MDOC_BLOCK):
1735: term_newln(p);
1736: if (node->prev)
1737: term_vspace(p);
1738: break;
1739: case (MDOC_HEAD):
1.28 kristaps 1740: pair->flag |= ttypes[TTYPE_SSECTION];
1.5 kristaps 1741: p->offset = HALFINDENT;
1.1 kristaps 1742: break;
1743: default:
1744: break;
1745: }
1746:
1747: return(1);
1748: }
1749:
1750:
1751: /* ARGSUSED */
1752: static void
1753: termp_ss_post(DECL_ARGS)
1754: {
1755:
1.29 kristaps 1756: if (MDOC_HEAD == node->type)
1.1 kristaps 1757: term_newln(p);
1758: }
1759:
1760:
1761: /* ARGSUSED */
1762: static int
1763: termp_em_pre(DECL_ARGS)
1764: {
1765:
1.28 kristaps 1766: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1767: return(1);
1768: }
1769:
1770:
1771: /* ARGSUSED */
1772: static int
1773: termp_cd_pre(DECL_ARGS)
1774: {
1775:
1.28 kristaps 1776: pair->flag |= ttypes[TTYPE_CONFIG];
1.1 kristaps 1777: term_newln(p);
1778: return(1);
1779: }
1780:
1781:
1782: /* ARGSUSED */
1783: static int
1784: termp_cm_pre(DECL_ARGS)
1785: {
1786:
1.28 kristaps 1787: pair->flag |= ttypes[TTYPE_CMD_FLAG];
1.1 kristaps 1788: return(1);
1789: }
1790:
1791:
1792: /* ARGSUSED */
1793: static int
1794: termp_ic_pre(DECL_ARGS)
1795: {
1796:
1.28 kristaps 1797: pair->flag |= ttypes[TTYPE_CMD];
1.1 kristaps 1798: return(1);
1799: }
1800:
1801:
1802: /* ARGSUSED */
1803: static int
1804: termp_in_pre(DECL_ARGS)
1805: {
1806:
1.28 kristaps 1807: pair->flag |= ttypes[TTYPE_INCLUDE];
1808: p->flags |= ttypes[TTYPE_INCLUDE];
1.22 kristaps 1809:
1810: if (SEC_SYNOPSIS == node->sec)
1811: term_word(p, "#include");
1812:
1.1 kristaps 1813: term_word(p, "<");
1814: p->flags |= TERMP_NOSPACE;
1815: return(1);
1816: }
1817:
1818:
1819: /* ARGSUSED */
1820: static void
1821: termp_in_post(DECL_ARGS)
1822: {
1823:
1.46 kristaps 1824: p->flags |= TERMP_NOSPACE | ttypes[TTYPE_INCLUDE];
1.1 kristaps 1825: term_word(p, ">");
1.46 kristaps 1826: p->flags &= ~ttypes[TTYPE_INCLUDE];
1.1 kristaps 1827:
1828: if (SEC_SYNOPSIS != node->sec)
1829: return;
1.22 kristaps 1830:
1831: term_newln(p);
1832: /*
1833: * XXX Not entirely correct. If `.In foo bar' is specified in
1834: * the SYNOPSIS section, then it produces a single break after
1835: * the <foo>; mandoc asserts a vertical space. Since this
1836: * construction is rarely used, I think it's fine.
1837: */
1.1 kristaps 1838: if (node->next && MDOC_In != node->next->tok)
1839: term_vspace(p);
1840: }
1841:
1842:
1843: /* ARGSUSED */
1844: static int
1.45 kristaps 1845: termp_sp_pre(DECL_ARGS)
1846: {
1847: int i, len;
1848:
1849: if (NULL == node->child) {
1850: term_vspace(p);
1851: return(0);
1852: }
1853:
1854: len = atoi(node->child->string);
1855: if (0 == len)
1856: term_newln(p);
1857: for (i = 0; i < len; i++)
1858: term_vspace(p);
1859:
1860: return(0);
1861: }
1862:
1863:
1864: /* ARGSUSED */
1865: static int
1.44 kristaps 1866: termp_br_pre(DECL_ARGS)
1867: {
1868:
1869: term_newln(p);
1870: return(1);
1871: }
1872:
1873:
1874: /* ARGSUSED */
1875: static int
1.1 kristaps 1876: termp_brq_pre(DECL_ARGS)
1877: {
1878:
1879: if (MDOC_BODY != node->type)
1880: return(1);
1881: term_word(p, "\\(lC");
1882: p->flags |= TERMP_NOSPACE;
1883: return(1);
1884: }
1885:
1886:
1887: /* ARGSUSED */
1888: static void
1889: termp_brq_post(DECL_ARGS)
1890: {
1891:
1892: if (MDOC_BODY != node->type)
1893: return;
1894: p->flags |= TERMP_NOSPACE;
1895: term_word(p, "\\(rC");
1896: }
1897:
1898:
1899: /* ARGSUSED */
1900: static int
1901: termp_bq_pre(DECL_ARGS)
1902: {
1903:
1904: if (MDOC_BODY != node->type)
1905: return(1);
1906: term_word(p, "\\(lB");
1907: p->flags |= TERMP_NOSPACE;
1908: return(1);
1909: }
1910:
1911:
1912: /* ARGSUSED */
1913: static void
1914: termp_bq_post(DECL_ARGS)
1915: {
1916:
1917: if (MDOC_BODY != node->type)
1918: return;
1919: p->flags |= TERMP_NOSPACE;
1920: term_word(p, "\\(rB");
1921: }
1922:
1923:
1924: /* ARGSUSED */
1925: static int
1926: termp_pq_pre(DECL_ARGS)
1927: {
1928:
1929: if (MDOC_BODY != node->type)
1930: return(1);
1931: term_word(p, "\\&(");
1932: p->flags |= TERMP_NOSPACE;
1933: return(1);
1934: }
1935:
1936:
1937: /* ARGSUSED */
1938: static void
1939: termp_pq_post(DECL_ARGS)
1940: {
1941:
1942: if (MDOC_BODY != node->type)
1943: return;
1944: term_word(p, ")");
1945: }
1946:
1947:
1948: /* ARGSUSED */
1949: static int
1950: termp_fo_pre(DECL_ARGS)
1951: {
1952: const struct mdoc_node *n;
1953:
1954: if (MDOC_BODY == node->type) {
1.33 kristaps 1955: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1956: term_word(p, "(");
1957: p->flags |= TERMP_NOSPACE;
1958: return(1);
1959: } else if (MDOC_HEAD != node->type)
1960: return(1);
1961:
1962: p->flags |= ttypes[TTYPE_FUNC_NAME];
1963: for (n = node->child; n; n = n->next) {
1.11 kristaps 1964: assert(MDOC_TEXT == n->type);
1.1 kristaps 1965: term_word(p, n->string);
1966: }
1967: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1968:
1969: return(0);
1970: }
1971:
1972:
1973: /* ARGSUSED */
1974: static void
1975: termp_fo_post(DECL_ARGS)
1976: {
1977:
1978: if (MDOC_BODY != node->type)
1979: return;
1980: p->flags |= TERMP_NOSPACE;
1981: term_word(p, ")");
1982: p->flags |= TERMP_NOSPACE;
1983: term_word(p, ";");
1984: term_newln(p);
1985: }
1986:
1987:
1988: /* ARGSUSED */
1989: static int
1990: termp_bf_pre(DECL_ARGS)
1991: {
1992: const struct mdoc_node *n;
1993:
1.28 kristaps 1994: if (MDOC_HEAD == node->type)
1.1 kristaps 1995: return(0);
1.28 kristaps 1996: else if (MDOC_BLOCK != node->type)
1.1 kristaps 1997: return(1);
1998:
1999: if (NULL == (n = node->head->child)) {
2000: if (arg_hasattr(MDOC_Emphasis, node))
1.28 kristaps 2001: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 2002: else if (arg_hasattr(MDOC_Symbolic, node))
1.28 kristaps 2003: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2004:
2005: return(1);
2006: }
2007:
1.11 kristaps 2008: assert(MDOC_TEXT == n->type);
1.1 kristaps 2009: if (0 == strcmp("Em", n->string))
1.28 kristaps 2010: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 2011: else if (0 == strcmp("Sy", n->string))
1.28 kristaps 2012: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2013:
2014: return(1);
2015: }
2016:
2017:
2018: /* ARGSUSED */
2019: static int
2020: termp_sy_pre(DECL_ARGS)
2021: {
2022:
1.28 kristaps 2023: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2024: return(1);
2025: }
2026:
2027:
2028: /* ARGSUSED */
2029: static int
2030: termp_ms_pre(DECL_ARGS)
2031: {
2032:
1.28 kristaps 2033: pair->flag |= ttypes[TTYPE_SYMBOL];
1.1 kristaps 2034: return(1);
2035: }
2036:
2037:
2038:
2039: /* ARGSUSED */
2040: static int
2041: termp_sm_pre(DECL_ARGS)
2042: {
2043:
1.11 kristaps 2044: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2045: if (0 == strcmp("on", node->child->string)) {
2046: p->flags &= ~TERMP_NONOSPACE;
2047: p->flags &= ~TERMP_NOSPACE;
2048: } else
2049: p->flags |= TERMP_NONOSPACE;
2050:
2051: return(0);
2052: }
2053:
2054:
2055: /* ARGSUSED */
2056: static int
2057: termp_ap_pre(DECL_ARGS)
2058: {
2059:
2060: p->flags |= TERMP_NOSPACE;
2061: term_word(p, "\\(aq");
2062: p->flags |= TERMP_NOSPACE;
2063: return(1);
2064: }
2065:
2066:
2067: /* ARGSUSED */
2068: static int
2069: termp__j_pre(DECL_ARGS)
2070: {
2071:
1.28 kristaps 2072: pair->flag |= ttypes[TTYPE_REF_JOURNAL];
1.1 kristaps 2073: return(1);
2074: }
2075:
2076:
2077: /* ARGSUSED */
2078: static int
2079: termp__t_pre(DECL_ARGS)
2080: {
2081:
2082: term_word(p, "\"");
2083: p->flags |= TERMP_NOSPACE;
2084: return(1);
2085: }
2086:
2087:
2088: /* ARGSUSED */
2089: static void
2090: termp__t_post(DECL_ARGS)
2091: {
2092:
2093: p->flags |= TERMP_NOSPACE;
2094: term_word(p, "\"");
2095: termp____post(p, pair, meta, node);
2096: }
2097:
2098:
2099: /* ARGSUSED */
2100: static void
2101: termp____post(DECL_ARGS)
2102: {
2103:
2104: p->flags |= TERMP_NOSPACE;
2105: term_word(p, node->next ? "," : ".");
2106: }
2107:
2108:
2109: /* ARGSUSED */
2110: static int
2111: termp_lk_pre(DECL_ARGS)
2112: {
2113: const struct mdoc_node *n;
2114:
1.11 kristaps 2115: assert(node->child);
2116: n = node->child;
1.1 kristaps 2117:
1.12 kristaps 2118: if (NULL == n->next) {
1.28 kristaps 2119: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.12 kristaps 2120: return(1);
2121: }
2122:
1.1 kristaps 2123: p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2124: term_word(p, n->string);
2125: p->flags |= TERMP_NOSPACE;
2126: term_word(p, ":");
1.12 kristaps 2127: p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2128:
2129: p->flags |= ttypes[TTYPE_LINK_TEXT];
1.12 kristaps 2130: for (n = n->next; n; n = n->next)
1.1 kristaps 2131: term_word(p, n->string);
1.12 kristaps 2132:
1.1 kristaps 2133: p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2134: return(0);
2135: }
2136:
2137:
2138: /* ARGSUSED */
2139: static int
2140: termp_mt_pre(DECL_ARGS)
2141: {
2142:
1.28 kristaps 2143: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2144: return(1);
2145: }
2146:
2147:
CVSweb