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