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