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