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