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