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