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