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