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