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