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