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