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