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