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