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