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