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