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