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