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