Annotation of mandoc/mdoc_term.c, Revision 1.50
1.50 ! kristaps 1: /* $Id: mdoc_term.c,v 1.49 2009/07/20 19:30:46 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:
815: switch (type) {
816: case (MDOC_Inset):
817: if (MDOC_BODY == node->type)
818: p->flags &= ~TERMP_NOSPACE;
819: else
820: p->flags |= TERMP_NOSPACE;
821: break;
822: default:
823: p->flags |= TERMP_NOSPACE;
824: break;
825: }
826:
827: /*
828: * Style flags. Diagnostic heads need TTYPE_DIAG.
829: */
830:
831: switch (type) {
832: case (MDOC_Diag):
833: if (MDOC_HEAD == node->type)
834: p->flags |= ttypes[TTYPE_DIAG];
835: break;
836: default:
837: break;
838: }
839:
840: /*
841: * Pad and break control. This is the tricker part. Lists with
842: * set right-margins for the head get TERMP_NOBREAK because, if
843: * they overrun the margin, they wrap to the new margin.
844: * Correspondingly, the body for these types don't left-pad, as
845: * the head will pad out to to the right.
846: */
847:
848: switch (type) {
849: case (MDOC_Bullet):
850: /* FALLTHROUGH */
851: case (MDOC_Dash):
852: /* FALLTHROUGH */
853: case (MDOC_Enum):
854: /* FALLTHROUGH */
855: case (MDOC_Hyphen):
1.39 kristaps 856: if (MDOC_HEAD == node->type)
857: p->flags |= TERMP_NOBREAK;
858: else
859: p->flags |= TERMP_NOLPAD;
860: break;
1.38 kristaps 861: case (MDOC_Hang):
1.39 kristaps 862: if (MDOC_HEAD == node->type)
863: p->flags |= TERMP_NOBREAK;
864: else
865: p->flags |= TERMP_NOLPAD;
866:
867: if (MDOC_HEAD == node->type)
868: p->flags |= TERMP_HANG;
869: break;
1.1 kristaps 870: case (MDOC_Tag):
871: if (MDOC_HEAD == node->type)
872: p->flags |= TERMP_NOBREAK;
873: else
874: p->flags |= TERMP_NOLPAD;
1.38 kristaps 875:
1.39 kristaps 876: if (MDOC_HEAD != node->type)
877: break;
878: if (NULL == node->next || NULL == node->next->child)
879: p->flags |= TERMP_DANGLE;
1.1 kristaps 880: break;
881: case (MDOC_Column):
882: if (MDOC_HEAD == node->type) {
883: assert(node->next);
884: if (MDOC_BODY == node->next->type)
885: p->flags &= ~TERMP_NOBREAK;
886: else
887: p->flags |= TERMP_NOBREAK;
888: if (node->prev)
889: p->flags |= TERMP_NOLPAD;
890: }
891: break;
892: case (MDOC_Diag):
893: if (MDOC_HEAD == node->type)
894: p->flags |= TERMP_NOBREAK;
895: break;
896: default:
897: break;
898: }
899:
900: /*
901: * Margin control. Set-head-width lists have their right
902: * margins shortened. The body for these lists has the offset
903: * necessarily lengthened. Everybody gets the offset.
904: */
905:
906: p->offset += offset;
907:
908: switch (type) {
909: case (MDOC_Bullet):
910: /* FALLTHROUGH */
911: case (MDOC_Dash):
912: /* FALLTHROUGH */
913: case (MDOC_Enum):
914: /* FALLTHROUGH */
915: case (MDOC_Hyphen):
916: /* FALLTHROUGH */
1.38 kristaps 917: case (MDOC_Hang):
918: /* FALLTHROUGH */
1.1 kristaps 919: case (MDOC_Tag):
1.49 kristaps 920: assert(width);
1.1 kristaps 921: if (MDOC_HEAD == node->type)
922: p->rmargin = p->offset + width;
923: else
924: p->offset += width;
925: break;
926: case (MDOC_Column):
1.49 kristaps 927: assert(width);
1.1 kristaps 928: p->rmargin = p->offset + width;
1.50 ! kristaps 929: /*
! 930: * XXX - this behaviour is not documented: the
! 931: * right-most column is filled to the right margin.
! 932: */
! 933: if (MDOC_HEAD == node->type &&
! 934: MDOC_BODY == node->next->type)
! 935: p->rmargin = p->maxrmargin;
1.1 kristaps 936: break;
937: default:
938: break;
939: }
940:
941: /*
942: * The dash, hyphen, bullet and enum lists all have a special
1.15 kristaps 943: * HEAD character (temporarily bold, in some cases).
1.1 kristaps 944: */
945:
946: if (MDOC_HEAD == node->type)
947: switch (type) {
948: case (MDOC_Bullet):
1.15 kristaps 949: p->flags |= TERMP_BOLD;
1.1 kristaps 950: term_word(p, "\\[bu]");
1.39 kristaps 951: p->flags &= ~TERMP_BOLD;
1.1 kristaps 952: break;
953: case (MDOC_Dash):
954: /* FALLTHROUGH */
955: case (MDOC_Hyphen):
1.15 kristaps 956: p->flags |= TERMP_BOLD;
1.27 kristaps 957: term_word(p, "\\(hy");
1.39 kristaps 958: p->flags &= ~TERMP_BOLD;
1.1 kristaps 959: break;
960: case (MDOC_Enum):
961: (pair->ppair->ppair->count)++;
962: (void)snprintf(buf, sizeof(buf), "%d.",
963: pair->ppair->ppair->count);
964: term_word(p, buf);
965: break;
966: default:
967: break;
968: }
969:
970: /*
971: * If we're not going to process our children, indicate so here.
972: */
973:
974: switch (type) {
975: case (MDOC_Bullet):
976: /* FALLTHROUGH */
977: case (MDOC_Item):
978: /* FALLTHROUGH */
979: case (MDOC_Dash):
980: /* FALLTHROUGH */
981: case (MDOC_Hyphen):
982: /* FALLTHROUGH */
983: case (MDOC_Enum):
984: if (MDOC_HEAD == node->type)
985: return(0);
986: break;
987: case (MDOC_Column):
988: if (MDOC_BODY == node->type)
989: return(0);
990: break;
991: default:
992: break;
993: }
994:
995: return(1);
996: }
997:
998:
999: /* ARGSUSED */
1000: static void
1001: termp_it_post(DECL_ARGS)
1002: {
1003: int type;
1004:
1005: if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
1006: return;
1007:
1008: type = arg_listtype(node->parent->parent->parent);
1.38 kristaps 1009: assert(-1 != type);
1.1 kristaps 1010:
1011: switch (type) {
1012: case (MDOC_Diag):
1.43 kristaps 1013: term_word(p, "\\ ");
1.1 kristaps 1014: /* FALLTHROUGH */
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_pa_pre(DECL_ARGS)
1764: {
1765:
1.28 kristaps 1766: pair->flag |= ttypes[TTYPE_FILE];
1.1 kristaps 1767: return(1);
1768: }
1769:
1770:
1771: /* ARGSUSED */
1772: static int
1773: termp_em_pre(DECL_ARGS)
1774: {
1775:
1.28 kristaps 1776: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 1777: return(1);
1778: }
1779:
1780:
1781: /* ARGSUSED */
1782: static int
1783: termp_cd_pre(DECL_ARGS)
1784: {
1785:
1.28 kristaps 1786: pair->flag |= ttypes[TTYPE_CONFIG];
1.1 kristaps 1787: term_newln(p);
1788: return(1);
1789: }
1790:
1791:
1792: /* ARGSUSED */
1793: static int
1794: termp_cm_pre(DECL_ARGS)
1795: {
1796:
1.28 kristaps 1797: pair->flag |= ttypes[TTYPE_CMD_FLAG];
1.1 kristaps 1798: return(1);
1799: }
1800:
1801:
1802: /* ARGSUSED */
1803: static int
1804: termp_ic_pre(DECL_ARGS)
1805: {
1806:
1.28 kristaps 1807: pair->flag |= ttypes[TTYPE_CMD];
1.1 kristaps 1808: return(1);
1809: }
1810:
1811:
1812: /* ARGSUSED */
1813: static int
1814: termp_in_pre(DECL_ARGS)
1815: {
1816:
1.28 kristaps 1817: pair->flag |= ttypes[TTYPE_INCLUDE];
1818: p->flags |= ttypes[TTYPE_INCLUDE];
1.22 kristaps 1819:
1820: if (SEC_SYNOPSIS == node->sec)
1821: term_word(p, "#include");
1822:
1.1 kristaps 1823: term_word(p, "<");
1824: p->flags |= TERMP_NOSPACE;
1825: return(1);
1826: }
1827:
1828:
1829: /* ARGSUSED */
1830: static void
1831: termp_in_post(DECL_ARGS)
1832: {
1833:
1.46 kristaps 1834: p->flags |= TERMP_NOSPACE | ttypes[TTYPE_INCLUDE];
1.1 kristaps 1835: term_word(p, ">");
1.46 kristaps 1836: p->flags &= ~ttypes[TTYPE_INCLUDE];
1.1 kristaps 1837:
1838: if (SEC_SYNOPSIS != node->sec)
1839: return;
1.22 kristaps 1840:
1841: term_newln(p);
1842: /*
1843: * XXX Not entirely correct. If `.In foo bar' is specified in
1844: * the SYNOPSIS section, then it produces a single break after
1845: * the <foo>; mandoc asserts a vertical space. Since this
1846: * construction is rarely used, I think it's fine.
1847: */
1.1 kristaps 1848: if (node->next && MDOC_In != node->next->tok)
1849: term_vspace(p);
1850: }
1851:
1852:
1853: /* ARGSUSED */
1854: static int
1.45 kristaps 1855: termp_sp_pre(DECL_ARGS)
1856: {
1857: int i, len;
1858:
1859: if (NULL == node->child) {
1860: term_vspace(p);
1861: return(0);
1862: }
1863:
1864: len = atoi(node->child->string);
1865: if (0 == len)
1866: term_newln(p);
1867: for (i = 0; i < len; i++)
1868: term_vspace(p);
1869:
1870: return(0);
1871: }
1872:
1873:
1874: /* ARGSUSED */
1875: static int
1.44 kristaps 1876: termp_br_pre(DECL_ARGS)
1877: {
1878:
1879: term_newln(p);
1880: return(1);
1881: }
1882:
1883:
1884: /* ARGSUSED */
1885: static int
1.1 kristaps 1886: termp_brq_pre(DECL_ARGS)
1887: {
1888:
1889: if (MDOC_BODY != node->type)
1890: return(1);
1891: term_word(p, "\\(lC");
1892: p->flags |= TERMP_NOSPACE;
1893: return(1);
1894: }
1895:
1896:
1897: /* ARGSUSED */
1898: static void
1899: termp_brq_post(DECL_ARGS)
1900: {
1901:
1902: if (MDOC_BODY != node->type)
1903: return;
1904: p->flags |= TERMP_NOSPACE;
1905: term_word(p, "\\(rC");
1906: }
1907:
1908:
1909: /* ARGSUSED */
1910: static int
1911: termp_bq_pre(DECL_ARGS)
1912: {
1913:
1914: if (MDOC_BODY != node->type)
1915: return(1);
1916: term_word(p, "\\(lB");
1917: p->flags |= TERMP_NOSPACE;
1918: return(1);
1919: }
1920:
1921:
1922: /* ARGSUSED */
1923: static void
1924: termp_bq_post(DECL_ARGS)
1925: {
1926:
1927: if (MDOC_BODY != node->type)
1928: return;
1929: p->flags |= TERMP_NOSPACE;
1930: term_word(p, "\\(rB");
1931: }
1932:
1933:
1934: /* ARGSUSED */
1935: static int
1936: termp_pq_pre(DECL_ARGS)
1937: {
1938:
1939: if (MDOC_BODY != node->type)
1940: return(1);
1941: term_word(p, "\\&(");
1942: p->flags |= TERMP_NOSPACE;
1943: return(1);
1944: }
1945:
1946:
1947: /* ARGSUSED */
1948: static void
1949: termp_pq_post(DECL_ARGS)
1950: {
1951:
1952: if (MDOC_BODY != node->type)
1953: return;
1954: term_word(p, ")");
1955: }
1956:
1957:
1958: /* ARGSUSED */
1959: static int
1960: termp_fo_pre(DECL_ARGS)
1961: {
1962: const struct mdoc_node *n;
1963:
1964: if (MDOC_BODY == node->type) {
1.33 kristaps 1965: p->flags |= TERMP_NOSPACE;
1.1 kristaps 1966: term_word(p, "(");
1967: p->flags |= TERMP_NOSPACE;
1968: return(1);
1969: } else if (MDOC_HEAD != node->type)
1970: return(1);
1971:
1972: p->flags |= ttypes[TTYPE_FUNC_NAME];
1973: for (n = node->child; n; n = n->next) {
1.11 kristaps 1974: assert(MDOC_TEXT == n->type);
1.1 kristaps 1975: term_word(p, n->string);
1976: }
1977: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1978:
1979: return(0);
1980: }
1981:
1982:
1983: /* ARGSUSED */
1984: static void
1985: termp_fo_post(DECL_ARGS)
1986: {
1987:
1988: if (MDOC_BODY != node->type)
1989: return;
1990: p->flags |= TERMP_NOSPACE;
1991: term_word(p, ")");
1992: p->flags |= TERMP_NOSPACE;
1993: term_word(p, ";");
1994: term_newln(p);
1995: }
1996:
1997:
1998: /* ARGSUSED */
1999: static int
2000: termp_bf_pre(DECL_ARGS)
2001: {
2002: const struct mdoc_node *n;
2003:
1.28 kristaps 2004: if (MDOC_HEAD == node->type)
1.1 kristaps 2005: return(0);
1.28 kristaps 2006: else if (MDOC_BLOCK != node->type)
1.1 kristaps 2007: return(1);
2008:
2009: if (NULL == (n = node->head->child)) {
2010: if (arg_hasattr(MDOC_Emphasis, node))
1.28 kristaps 2011: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 2012: else if (arg_hasattr(MDOC_Symbolic, node))
1.28 kristaps 2013: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2014:
2015: return(1);
2016: }
2017:
1.11 kristaps 2018: assert(MDOC_TEXT == n->type);
1.1 kristaps 2019: if (0 == strcmp("Em", n->string))
1.28 kristaps 2020: pair->flag |= ttypes[TTYPE_EMPH];
1.1 kristaps 2021: else if (0 == strcmp("Sy", n->string))
1.28 kristaps 2022: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2023:
2024: return(1);
2025: }
2026:
2027:
2028: /* ARGSUSED */
2029: static int
2030: termp_sy_pre(DECL_ARGS)
2031: {
2032:
1.28 kristaps 2033: pair->flag |= ttypes[TTYPE_SYMB];
1.1 kristaps 2034: return(1);
2035: }
2036:
2037:
2038: /* ARGSUSED */
2039: static int
2040: termp_ms_pre(DECL_ARGS)
2041: {
2042:
1.28 kristaps 2043: pair->flag |= ttypes[TTYPE_SYMBOL];
1.1 kristaps 2044: return(1);
2045: }
2046:
2047:
2048:
2049: /* ARGSUSED */
2050: static int
2051: termp_sm_pre(DECL_ARGS)
2052: {
2053:
1.11 kristaps 2054: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 2055: if (0 == strcmp("on", node->child->string)) {
2056: p->flags &= ~TERMP_NONOSPACE;
2057: p->flags &= ~TERMP_NOSPACE;
2058: } else
2059: p->flags |= TERMP_NONOSPACE;
2060:
2061: return(0);
2062: }
2063:
2064:
2065: /* ARGSUSED */
2066: static int
2067: termp_ap_pre(DECL_ARGS)
2068: {
2069:
2070: p->flags |= TERMP_NOSPACE;
2071: term_word(p, "\\(aq");
2072: p->flags |= TERMP_NOSPACE;
2073: return(1);
2074: }
2075:
2076:
2077: /* ARGSUSED */
2078: static int
2079: termp__j_pre(DECL_ARGS)
2080: {
2081:
1.28 kristaps 2082: pair->flag |= ttypes[TTYPE_REF_JOURNAL];
1.1 kristaps 2083: return(1);
2084: }
2085:
2086:
2087: /* ARGSUSED */
2088: static int
2089: termp__t_pre(DECL_ARGS)
2090: {
2091:
2092: term_word(p, "\"");
2093: p->flags |= TERMP_NOSPACE;
2094: return(1);
2095: }
2096:
2097:
2098: /* ARGSUSED */
2099: static void
2100: termp__t_post(DECL_ARGS)
2101: {
2102:
2103: p->flags |= TERMP_NOSPACE;
2104: term_word(p, "\"");
2105: termp____post(p, pair, meta, node);
2106: }
2107:
2108:
2109: /* ARGSUSED */
2110: static void
2111: termp____post(DECL_ARGS)
2112: {
2113:
2114: p->flags |= TERMP_NOSPACE;
2115: term_word(p, node->next ? "," : ".");
2116: }
2117:
2118:
2119: /* ARGSUSED */
2120: static int
2121: termp_lk_pre(DECL_ARGS)
2122: {
2123: const struct mdoc_node *n;
2124:
1.11 kristaps 2125: assert(node->child);
2126: n = node->child;
1.1 kristaps 2127:
1.12 kristaps 2128: if (NULL == n->next) {
1.28 kristaps 2129: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.12 kristaps 2130: return(1);
2131: }
2132:
1.1 kristaps 2133: p->flags |= ttypes[TTYPE_LINK_ANCHOR];
2134: term_word(p, n->string);
2135: p->flags |= TERMP_NOSPACE;
2136: term_word(p, ":");
1.12 kristaps 2137: p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2138:
2139: p->flags |= ttypes[TTYPE_LINK_TEXT];
1.12 kristaps 2140: for (n = n->next; n; n = n->next)
1.1 kristaps 2141: term_word(p, n->string);
1.12 kristaps 2142:
1.1 kristaps 2143: p->flags &= ~ttypes[TTYPE_LINK_TEXT];
2144: return(0);
2145: }
2146:
2147:
2148: /* ARGSUSED */
2149: static int
2150: termp_mt_pre(DECL_ARGS)
2151: {
2152:
1.28 kristaps 2153: pair->flag |= ttypes[TTYPE_LINK_ANCHOR];
1.1 kristaps 2154: return(1);
2155: }
2156:
2157:
CVSweb