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