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