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