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