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