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