Annotation of src/usr.bin/mandoc/mdoc_term.c, Revision 1.11
1.11 ! schwarze 1: /* $Id: mdoc_term.c,v 1.10 2009/06/18 21:50:45 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];
709: int i, type, keys[3], vals[3];
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
893: * HEAD character. Print it now.
894: */
895:
896: if (MDOC_HEAD == node->type)
897: switch (type) {
898: case (MDOC_Bullet):
899: term_word(p, "\\[bu]");
900: break;
901: case (MDOC_Dash):
902: /* FALLTHROUGH */
903: case (MDOC_Hyphen):
904: term_word(p, "\\-");
905: break;
906: case (MDOC_Enum):
907: (pair->ppair->ppair->count)++;
908: (void)snprintf(buf, sizeof(buf), "%d.",
909: pair->ppair->ppair->count);
910: term_word(p, buf);
911: break;
912: default:
913: break;
914: }
915:
916: /*
917: * If we're not going to process our children, indicate so here.
918: */
919:
920: switch (type) {
921: case (MDOC_Bullet):
922: /* FALLTHROUGH */
923: case (MDOC_Item):
924: /* FALLTHROUGH */
925: case (MDOC_Dash):
926: /* FALLTHROUGH */
927: case (MDOC_Hyphen):
928: /* FALLTHROUGH */
929: case (MDOC_Enum):
930: if (MDOC_HEAD == node->type)
931: return(0);
932: break;
933: case (MDOC_Column):
934: if (MDOC_BODY == node->type)
935: return(0);
936: break;
937: default:
938: break;
939: }
940:
941: return(1);
942: }
943:
944:
945: /* ARGSUSED */
946: static void
947: termp_it_post(DECL_ARGS)
948: {
949: int type;
950:
951: if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
952: return;
953:
954: type = arg_listtype(node->parent->parent->parent);
955:
956: switch (type) {
957: case (MDOC_Diag):
958: /* FALLTHROUGH */
959: case (MDOC_Item):
960: /* FALLTHROUGH */
961: case (MDOC_Inset):
962: if (MDOC_BODY == node->type)
963: term_flushln(p);
964: break;
965: case (MDOC_Column):
966: if (MDOC_HEAD == node->type)
967: term_flushln(p);
968: break;
969: default:
970: term_flushln(p);
971: break;
972: }
973:
974: p->offset = pair->offset;
975: p->rmargin = pair->rmargin;
976: p->flags = pair->flag;
977: }
978:
979:
980: /* ARGSUSED */
981: static int
982: termp_nm_pre(DECL_ARGS)
983: {
984:
985: if (SEC_SYNOPSIS == node->sec)
986: term_newln(p);
987:
988: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_PROG]);
989: if (NULL == node->child)
990: term_word(p, meta->name);
991:
992: return(1);
993: }
994:
995:
996: /* ARGSUSED */
997: static int
998: termp_fl_pre(DECL_ARGS)
999: {
1000:
1001: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1002: term_word(p, "\\-");
1003: p->flags |= TERMP_NOSPACE;
1004: return(1);
1005: }
1006:
1007:
1008: /* ARGSUSED */
1009: static int
1010: termp_ar_pre(DECL_ARGS)
1011: {
1012:
1013: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_ARG]);
1014: return(1);
1015: }
1016:
1017:
1018: /* ARGSUSED */
1019: static int
1020: termp_ns_pre(DECL_ARGS)
1021: {
1022:
1023: p->flags |= TERMP_NOSPACE;
1024: return(1);
1025: }
1026:
1027:
1028: /* ARGSUSED */
1029: static int
1030: termp_pp_pre(DECL_ARGS)
1031: {
1032:
1033: term_vspace(p);
1034: return(1);
1035: }
1036:
1037:
1038: /* ARGSUSED */
1039: static int
1040: termp_st_pre(DECL_ARGS)
1041: {
1042: const char *cp;
1043:
1044: if (node->child && (cp = mdoc_a2st(node->child->string)))
1045: term_word(p, cp);
1046: return(0);
1047: }
1048:
1049:
1050: /* ARGSUSED */
1051: static int
1052: termp_rs_pre(DECL_ARGS)
1053: {
1054:
1055: if (MDOC_BLOCK == node->type && node->prev)
1056: term_vspace(p);
1057: return(1);
1058: }
1059:
1060:
1061: /* ARGSUSED */
1062: static int
1063: termp_rv_pre(DECL_ARGS)
1064: {
1065: int i;
1066:
1.10 schwarze 1067: /* FIXME: mandated by parser. */
1068:
1.1 kristaps 1069: if (-1 == (i = arg_getattr(MDOC_Std, node)))
1070: errx(1, "expected -std argument");
1071: if (1 != node->args->argv[i].sz)
1072: errx(1, "expected -std argument");
1073:
1074: term_newln(p);
1075: term_word(p, "The");
1076:
1077: p->flags |= ttypes[TTYPE_FUNC_NAME];
1078: term_word(p, *node->args->argv[i].value);
1079: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1080: p->flags |= TERMP_NOSPACE;
1081:
1082: term_word(p, "() function returns the value 0 if successful;");
1083: term_word(p, "otherwise the value -1 is returned and the");
1084: term_word(p, "global variable");
1085:
1086: p->flags |= ttypes[TTYPE_VAR_DECL];
1087: term_word(p, "errno");
1088: p->flags &= ~ttypes[TTYPE_VAR_DECL];
1089:
1090: term_word(p, "is set to indicate the error.");
1091:
1092: return(1);
1093: }
1094:
1095:
1096: /* ARGSUSED */
1097: static int
1098: termp_ex_pre(DECL_ARGS)
1099: {
1100: int i;
1101:
1.10 schwarze 1102: /* FIXME: mandated by parser? */
1103:
1.1 kristaps 1104: if (-1 == (i = arg_getattr(MDOC_Std, node)))
1105: errx(1, "expected -std argument");
1106: if (1 != node->args->argv[i].sz)
1107: errx(1, "expected -std argument");
1108:
1109: term_word(p, "The");
1110: p->flags |= ttypes[TTYPE_PROG];
1111: term_word(p, *node->args->argv[i].value);
1112: p->flags &= ~ttypes[TTYPE_PROG];
1113: term_word(p, "utility exits 0 on success, and >0 if an error occurs.");
1114:
1115: return(1);
1116: }
1117:
1118:
1119: /* ARGSUSED */
1120: static int
1121: termp_nd_pre(DECL_ARGS)
1122: {
1123:
1124: term_word(p, "\\-");
1125: return(1);
1126: }
1127:
1128:
1129: /* ARGSUSED */
1130: static void
1131: termp_bl_post(DECL_ARGS)
1132: {
1133:
1134: if (MDOC_BLOCK == node->type)
1135: term_newln(p);
1136: }
1137:
1138:
1139: /* ARGSUSED */
1140: static void
1141: termp_op_post(DECL_ARGS)
1142: {
1143:
1144: if (MDOC_BODY != node->type)
1145: return;
1146: p->flags |= TERMP_NOSPACE;
1147: term_word(p, "\\(rB");
1148: }
1149:
1150:
1151: /* ARGSUSED */
1152: static int
1153: termp_xr_pre(DECL_ARGS)
1154: {
1155: const struct mdoc_node *n;
1156:
1.10 schwarze 1157: assert(node->child && MDOC_TEXT == node->child->type);
1158: n = node->child;
1159:
1.1 kristaps 1160: term_word(p, n->string);
1161: if (NULL == (n = n->next))
1162: return(0);
1163: p->flags |= TERMP_NOSPACE;
1164: term_word(p, "(");
1165: p->flags |= TERMP_NOSPACE;
1166: term_word(p, n->string);
1167: p->flags |= TERMP_NOSPACE;
1168: term_word(p, ")");
1169: return(0);
1170: }
1171:
1172:
1173: /* ARGSUSED */
1174: static int
1175: termp_vt_pre(DECL_ARGS)
1176: {
1177:
1178: /* FIXME: this can be "type name". */
1179: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1180: return(1);
1181: }
1182:
1183:
1184: /* ARGSUSED */
1185: static void
1186: termp_vt_post(DECL_ARGS)
1187: {
1188:
1189: if (node->sec == SEC_SYNOPSIS)
1190: term_vspace(p);
1191: }
1192:
1193:
1194: /* ARGSUSED */
1195: static int
1196: termp_fd_pre(DECL_ARGS)
1197: {
1198:
1199: /*
1200: * FIXME: this naming is bad. This value is used, in general,
1201: * for the #include header or other preprocessor statement.
1202: */
1203: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_DECL]);
1204: return(1);
1205: }
1206:
1207:
1208: /* ARGSUSED */
1209: static void
1210: termp_fd_post(DECL_ARGS)
1211: {
1212:
1213: if (node->sec != SEC_SYNOPSIS)
1214: return;
1215: term_newln(p);
1216: if (node->next && MDOC_Fd != node->next->tok)
1217: term_vspace(p);
1218: }
1219:
1220:
1221: /* ARGSUSED */
1222: static int
1223: termp_sh_pre(DECL_ARGS)
1224: {
1225:
1226: switch (node->type) {
1227: case (MDOC_HEAD):
1228: term_vspace(p);
1229: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SECTION]);
1230: break;
1231: case (MDOC_BODY):
1232: p->offset = INDENT;
1233: break;
1234: default:
1235: break;
1236: }
1237: return(1);
1238: }
1239:
1240:
1241: /* ARGSUSED */
1242: static void
1243: termp_sh_post(DECL_ARGS)
1244: {
1245:
1246: switch (node->type) {
1247: case (MDOC_HEAD):
1248: term_newln(p);
1249: break;
1250: case (MDOC_BODY):
1251: term_newln(p);
1252: p->offset = 0;
1253: break;
1254: default:
1255: break;
1256: }
1257: }
1258:
1259:
1260: /* ARGSUSED */
1261: static int
1262: termp_op_pre(DECL_ARGS)
1263: {
1264:
1265: switch (node->type) {
1266: case (MDOC_BODY):
1267: term_word(p, "\\(lB");
1268: p->flags |= TERMP_NOSPACE;
1269: break;
1270: default:
1271: break;
1272: }
1273: return(1);
1274: }
1275:
1276:
1277: /* ARGSUSED */
1278: static int
1279: termp_bt_pre(DECL_ARGS)
1280: {
1281:
1282: term_word(p, "is currently in beta test.");
1283: return(1);
1284: }
1285:
1286:
1287: /* ARGSUSED */
1288: static int
1289: termp_lb_pre(DECL_ARGS)
1290: {
1291: const char *lb;
1292:
1.10 schwarze 1293: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1294: if ((lb = mdoc_a2lib(node->child->string))) {
1295: term_word(p, lb);
1296: return(0);
1297: }
1298: term_word(p, "library");
1299: return(1);
1300: }
1301:
1302:
1303: /* ARGSUSED */
1304: static void
1305: termp_lb_post(DECL_ARGS)
1306: {
1307:
1308: term_newln(p);
1309: }
1310:
1311:
1312: /* ARGSUSED */
1313: static int
1314: termp_ud_pre(DECL_ARGS)
1315: {
1316:
1317: term_word(p, "currently under development.");
1318: return(1);
1319: }
1320:
1321:
1322: /* ARGSUSED */
1323: static int
1324: termp_d1_pre(DECL_ARGS)
1325: {
1326:
1327: if (MDOC_BLOCK != node->type)
1328: return(1);
1329: term_newln(p);
1330: p->offset += (pair->offset = INDENT);
1331: return(1);
1332: }
1333:
1334:
1335: /* ARGSUSED */
1336: static void
1337: termp_d1_post(DECL_ARGS)
1338: {
1339:
1340: if (MDOC_BLOCK != node->type)
1341: return;
1342: term_newln(p);
1343: p->offset -= pair->offset;
1344: }
1345:
1346:
1347: /* ARGSUSED */
1348: static int
1349: termp_aq_pre(DECL_ARGS)
1350: {
1351:
1352: if (MDOC_BODY != node->type)
1353: return(1);
1354: term_word(p, "\\(la");
1355: p->flags |= TERMP_NOSPACE;
1356: return(1);
1357: }
1358:
1359:
1360: /* ARGSUSED */
1361: static void
1362: termp_aq_post(DECL_ARGS)
1363: {
1364:
1365: if (MDOC_BODY != node->type)
1366: return;
1367: p->flags |= TERMP_NOSPACE;
1368: term_word(p, "\\(ra");
1369: }
1370:
1371:
1372: /* ARGSUSED */
1373: static int
1374: termp_ft_pre(DECL_ARGS)
1375: {
1376:
1377: if (SEC_SYNOPSIS == node->sec)
1378: if (node->prev && MDOC_Fo == node->prev->tok)
1379: term_vspace(p);
1380: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_TYPE]);
1381: return(1);
1382: }
1383:
1384:
1385: /* ARGSUSED */
1386: static void
1387: termp_ft_post(DECL_ARGS)
1388: {
1389:
1390: if (SEC_SYNOPSIS == node->sec)
1391: term_newln(p);
1392: }
1393:
1394:
1395: /* ARGSUSED */
1396: static int
1397: termp_fn_pre(DECL_ARGS)
1398: {
1399: const struct mdoc_node *n;
1400:
1.10 schwarze 1401: assert(node->child && MDOC_TEXT == node->child->type);
1.1 kristaps 1402:
1403: /* FIXME: can be "type funcname" "type varname"... */
1404:
1405: p->flags |= ttypes[TTYPE_FUNC_NAME];
1406: term_word(p, node->child->string);
1407: p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1408:
1409: p->flags |= TERMP_NOSPACE;
1410: term_word(p, "(");
1411:
1412: for (n = node->child->next; n; n = n->next) {
1413: p->flags |= ttypes[TTYPE_FUNC_ARG];
1414: term_word(p, n->string);
1415: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1416: if (n->next)
1417: term_word(p, ",");
1418: }
1419:
1420: term_word(p, ")");
1421:
1422: if (SEC_SYNOPSIS == node->sec)
1423: term_word(p, ";");
1424:
1425: return(0);
1426: }
1427:
1428:
1429: /* ARGSUSED */
1430: static void
1431: termp_fn_post(DECL_ARGS)
1432: {
1433:
1434: if (node->sec == SEC_SYNOPSIS && node->next)
1435: term_vspace(p);
1436:
1437: }
1438:
1439:
1440: /* ARGSUSED */
1441: static int
1442: termp_sx_pre(DECL_ARGS)
1443: {
1444:
1445: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK]);
1446: return(1);
1447: }
1448:
1449:
1450: /* ARGSUSED */
1451: static int
1452: termp_fa_pre(DECL_ARGS)
1453: {
1454: struct mdoc_node *n;
1455:
1456: if (node->parent->tok != MDOC_Fo) {
1457: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_ARG]);
1458: return(1);
1459: }
1460:
1461: for (n = node->child; n; n = n->next) {
1462: p->flags |= ttypes[TTYPE_FUNC_ARG];
1463: term_word(p, n->string);
1464: p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1465: if (n->next)
1466: term_word(p, ",");
1467: }
1468:
1469: if (node->child && node->next && node->next->tok == MDOC_Fa)
1470: term_word(p, ",");
1471:
1472: return(0);
1473: }
1474:
1475:
1476: /* ARGSUSED */
1477: static int
1478: termp_va_pre(DECL_ARGS)
1479: {
1480:
1481: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1482: return(1);
1483: }
1484:
1485:
1486: /* ARGSUSED */
1487: static int
1488: termp_bd_pre(DECL_ARGS)
1489: {
1490: int i, type, ln;
1491:
1492: /*
1493: * This is fairly tricky due primarily to crappy documentation.
1494: * If -ragged or -filled are specified, the block does nothing
1495: * but change the indentation.
1496: *
1497: * If, on the other hand, -unfilled or -literal are specified,
1498: * then the game changes. Text is printed exactly as entered in
1499: * the display: if a macro line, a newline is appended to the
1500: * line. Blank lines are allowed.
1501: */
1502:
1503: if (MDOC_BLOCK == node->type)
1504: return(fmt_block_vspace(p, node, node));
1505: else if (MDOC_BODY != node->type)
1506: return(1);
1507:
1.10 schwarze 1508: /* FIXME: display type should be mandated by parser. */
1509:
1.1 kristaps 1510: if (NULL == node->parent->args)
1511: errx(1, "missing display type");
1512:
1513: pair->offset = p->offset;
1514:
1515: for (type = -1, i = 0;
1516: i < (int)node->parent->args->argc; i++) {
1517: switch (node->parent->args->argv[i].arg) {
1518: case (MDOC_Ragged):
1519: /* FALLTHROUGH */
1520: case (MDOC_Filled):
1521: /* FALLTHROUGH */
1522: case (MDOC_Unfilled):
1523: /* FALLTHROUGH */
1524: case (MDOC_Literal):
1525: type = node->parent->args->argv[i].arg;
1526: i = (int)node->parent->args->argc;
1527: break;
1528: default:
1529: break;
1530: }
1531: }
1532:
1533: if (NULL == node->parent->args)
1534: errx(1, "missing display type");
1535:
1536: i = arg_getattr(MDOC_Offset, node->parent);
1537: if (-1 != i) {
1538: if (1 != node->parent->args->argv[i].sz)
1539: errx(1, "expected single value");
1540: p->offset += arg_offset(&node->parent->args->argv[i]);
1541: }
1542:
1543: switch (type) {
1544: case (MDOC_Literal):
1545: /* FALLTHROUGH */
1546: case (MDOC_Unfilled):
1547: break;
1548: default:
1549: return(1);
1550: }
1551:
1552: /*
1553: * Tricky. Iterate through all children. If we're on a
1554: * different parse line, append a newline and then the contents.
1555: * Ew.
1556: */
1557:
1558: p->flags |= TERMP_LITERAL;
1559: ln = node->child ? node->child->line : 0;
1560:
1561: for (node = node->child; node; node = node->next) {
1562: if (ln < node->line) {
1563: term_flushln(p);
1564: p->flags |= TERMP_NOSPACE;
1565: }
1566: ln = node->line;
1567: print_node(p, pair, meta, node);
1568: }
1569:
1570: return(0);
1571: }
1572:
1573:
1574: /* ARGSUSED */
1575: static void
1576: termp_bd_post(DECL_ARGS)
1577: {
1578:
1579: if (MDOC_BODY != node->type)
1580: return;
1581:
1582: term_flushln(p);
1583: p->flags &= ~TERMP_LITERAL;
1584: p->offset = pair->offset;
1585: p->flags |= TERMP_NOSPACE;
1586: }
1587:
1588:
1589: /* ARGSUSED */
1590: static int
1591: termp_qq_pre(DECL_ARGS)
1592: {
1593:
1594: if (MDOC_BODY != node->type)
1595: return(1);
1596: term_word(p, "\"");
1597: p->flags |= TERMP_NOSPACE;
1598: return(1);
1599: }
1600:
1601:
1602: /* ARGSUSED */
1603: static void
1604: termp_qq_post(DECL_ARGS)
1605: {
1606:
1607: if (MDOC_BODY != node->type)
1608: return;
1609: p->flags |= TERMP_NOSPACE;
1610: term_word(p, "\"");
1611: }
1612:
1613:
1614: /* ARGSUSED */
1615: static int
1616: termp_bsx_pre(DECL_ARGS)
1617: {
1618:
1619: term_word(p, "BSDI BSD/OS");
1620: return(1);
1621: }
1622:
1623:
1624: /* ARGSUSED */
1625: static void
1626: termp_bx_post(DECL_ARGS)
1627: {
1628:
1629: if (node->child)
1630: p->flags |= TERMP_NOSPACE;
1631: term_word(p, "BSD");
1632: }
1633:
1634:
1635: /* ARGSUSED */
1636: static int
1637: termp_ox_pre(DECL_ARGS)
1638: {
1639:
1640: term_word(p, "OpenBSD");
1641: return(1);
1642: }
1643:
1644:
1645: /* ARGSUSED */
1646: static int
1647: termp_dx_pre(DECL_ARGS)
1648: {
1649:
1650: term_word(p, "DragonFly");
1651: return(1);
1652: }
1653:
1654:
1655: /* ARGSUSED */
1656: static int
1657: termp_ux_pre(DECL_ARGS)
1658: {
1659:
1660: term_word(p, "UNIX");
1661: return(1);
1662: }
1663:
1664:
1665: /* ARGSUSED */
1666: static int
1667: termp_fx_pre(DECL_ARGS)
1668: {
1669:
1670: term_word(p, "FreeBSD");
1671: return(1);
1672: }
1673:
1674:
1675: /* ARGSUSED */
1676: static int
1677: termp_nx_pre(DECL_ARGS)
1678: {
1679:
1680: term_word(p, "NetBSD");
1681: return(1);
1682: }
1683:
1684:
1685: /* ARGSUSED */
1686: static int
1687: termp_sq_pre(DECL_ARGS)
1688: {
1689:
1690: if (MDOC_BODY != node->type)
1691: return(1);
1692: term_word(p, "\\(oq");
1693: p->flags |= TERMP_NOSPACE;
1694: return(1);
1695: }
1696:
1697:
1698: /* ARGSUSED */
1699: static void
1700: termp_sq_post(DECL_ARGS)
1701: {
1702:
1703: if (MDOC_BODY != node->type)
1704: return;
1705: p->flags |= TERMP_NOSPACE;
1706: term_word(p, "\\(aq");
1707: }
1708:
1709:
1710: /* ARGSUSED */
1711: static int
1712: termp_pf_pre(DECL_ARGS)
1713: {
1714:
1715: p->flags |= TERMP_IGNDELIM;
1716: return(1);
1717: }
1718:
1719:
1720: /* ARGSUSED */
1721: static void
1722: termp_pf_post(DECL_ARGS)
1723: {
1724:
1725: p->flags &= ~TERMP_IGNDELIM;
1726: p->flags |= TERMP_NOSPACE;
1727: }
1728:
1729:
1730: /* ARGSUSED */
1731: static int
1732: termp_ss_pre(DECL_ARGS)
1733: {
1734:
1735: switch (node->type) {
1736: case (MDOC_BLOCK):
1737: term_newln(p);
1738: if (node->prev)
1739: term_vspace(p);
1740: break;
1741: case (MDOC_HEAD):
1742: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1.3 schwarze 1743: p->offset = HALFINDENT;
1.1 kristaps 1744: break;
1745: default:
1746: break;
1747: }
1748:
1749: return(1);
1750: }
1751:
1752:
1753: /* ARGSUSED */
1754: static void
1755: termp_ss_post(DECL_ARGS)
1756: {
1757:
1758: switch (node->type) {
1759: case (MDOC_HEAD):
1760: term_newln(p);
1761: p->offset = INDENT;
1762: break;
1763: default:
1764: break;
1765: }
1766: }
1767:
1768:
1769: /* ARGSUSED */
1770: static int
1771: termp_pa_pre(DECL_ARGS)
1772: {
1773:
1774: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1775: return(1);
1776: }
1777:
1778:
1779: /* ARGSUSED */
1780: static int
1781: termp_em_pre(DECL_ARGS)
1782: {
1783:
1784: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1785: return(1);
1786: }
1787:
1788:
1789: /* ARGSUSED */
1790: static int
1791: termp_cd_pre(DECL_ARGS)
1792: {
1793:
1794: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1795: term_newln(p);
1796: return(1);
1797: }
1798:
1799:
1800: /* ARGSUSED */
1801: static int
1802: termp_cm_pre(DECL_ARGS)
1803: {
1804:
1805: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1806: return(1);
1807: }
1808:
1809:
1810: /* ARGSUSED */
1811: static int
1812: termp_ic_pre(DECL_ARGS)
1813: {
1814:
1815: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1816: return(1);
1817: }
1818:
1819:
1820: /* ARGSUSED */
1821: static int
1822: termp_in_pre(DECL_ARGS)
1823: {
1824:
1825: TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1826: term_word(p, "#include");
1827: term_word(p, "<");
1828: p->flags |= TERMP_NOSPACE;
1829: return(1);
1830: }
1831:
1832:
1833: /* ARGSUSED */
1834: static void
1835: termp_in_post(DECL_ARGS)
1836: {
1837:
1838: p->flags |= TERMP_NOSPACE;
1839: term_word(p, ">");
1840:
1841: term_newln(p);
1842: if (SEC_SYNOPSIS != node->sec)
1843: return;
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: