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