]> git.cameronkatri.com Git - mandoc.git/blob - term.c
2236703ecd95cd1ee42bfadfdb411582f710a6f3
[mandoc.git] / term.c
1 /* $Id: term.c,v 1.39 2009/03/04 13:57:35 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
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 <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "term.h"
27
28 /*
29 * Performs actions on nodes of the abstract syntax tree. Both pre- and
30 * post-fix operations are defined here.
31 */
32
33 /* FIXME: macro arguments can be escaped. */
34
35 #define TTYPE_PROG 0
36 #define TTYPE_CMD_FLAG 1
37 #define TTYPE_CMD_ARG 2
38 #define TTYPE_SECTION 3
39 #define TTYPE_FUNC_DECL 4
40 #define TTYPE_VAR_DECL 5
41 #define TTYPE_FUNC_TYPE 6
42 #define TTYPE_FUNC_NAME 7
43 #define TTYPE_FUNC_ARG 8
44 #define TTYPE_LINK 9
45 #define TTYPE_SSECTION 10
46 #define TTYPE_FILE 11
47 #define TTYPE_EMPH 12
48 #define TTYPE_CONFIG 13
49 #define TTYPE_CMD 14
50 #define TTYPE_INCLUDE 15
51 #define TTYPE_SYMB 16
52 #define TTYPE_SYMBOL 17
53 #define TTYPE_DIAG 18
54 #define TTYPE_NMAX 19
55
56 /*
57 * These define "styles" for element types, like command arguments or
58 * executable names. This is useful when multiple macros must decorate
59 * the same thing (like .Ex -std cmd and .Nm cmd).
60 */
61
62 /* TODO: abstract this into mdocterm.c. */
63
64 const int ttypes[TTYPE_NMAX] = {
65 TERMP_BOLD, /* TTYPE_PROG */
66 TERMP_BOLD, /* TTYPE_CMD_FLAG */
67 TERMP_UNDERLINE, /* TTYPE_CMD_ARG */
68 TERMP_BOLD, /* TTYPE_SECTION */
69 TERMP_BOLD, /* TTYPE_FUNC_DECL */
70 TERMP_UNDERLINE, /* TTYPE_VAR_DECL */
71 TERMP_UNDERLINE, /* TTYPE_FUNC_TYPE */
72 TERMP_BOLD, /* TTYPE_FUNC_NAME */
73 TERMP_UNDERLINE, /* TTYPE_FUNC_ARG */
74 TERMP_UNDERLINE, /* TTYPE_LINK */
75 TERMP_BOLD, /* TTYPE_SSECTION */
76 TERMP_UNDERLINE, /* TTYPE_FILE */
77 TERMP_UNDERLINE, /* TTYPE_EMPH */
78 TERMP_BOLD, /* TTYPE_CONFIG */
79 TERMP_BOLD, /* TTYPE_CMD */
80 TERMP_BOLD, /* TTYPE_INCLUDE */
81 TERMP_BOLD, /* TTYPE_SYMB */
82 TERMP_BOLD, /* TTYPE_SYMBOL */
83 TERMP_BOLD /* TTYPE_DIAG */
84 };
85
86 static int arg_hasattr(int, size_t,
87 const struct mdoc_arg *);
88 static int arg_getattr(int, size_t,
89 const struct mdoc_arg *);
90 static size_t arg_offset(const struct mdoc_arg *);
91 static size_t arg_width(const struct mdoc_arg *);
92 static int arg_listtype(const struct mdoc_node *);
93
94 /*
95 * What follows describes prefix and postfix operations for the abstract
96 * syntax tree descent.
97 */
98
99 #define DECL_ARGS \
100 struct termp *p, \
101 struct termpair *pair, \
102 const struct mdoc_meta *meta, \
103 const struct mdoc_node *node
104
105 #define DECL_PRE(name) \
106 static int name##_pre(DECL_ARGS)
107 #define DECL_POST(name) \
108 static void name##_post(DECL_ARGS)
109 #define DECL_PREPOST(name) \
110 DECL_PRE(name); \
111 DECL_POST(name);
112
113 DECL_PREPOST(termp__t);
114 DECL_PREPOST(termp_aq);
115 DECL_PREPOST(termp_bd);
116 DECL_PREPOST(termp_bq);
117 DECL_PREPOST(termp_d1);
118 DECL_PREPOST(termp_dq);
119 DECL_PREPOST(termp_fd);
120 DECL_PREPOST(termp_fn);
121 DECL_PREPOST(termp_fo);
122 DECL_PREPOST(termp_ft);
123 DECL_PREPOST(termp_in);
124 DECL_PREPOST(termp_it);
125 DECL_PREPOST(termp_op);
126 DECL_PREPOST(termp_pf);
127 DECL_PREPOST(termp_pq);
128 DECL_PREPOST(termp_qq);
129 DECL_PREPOST(termp_sh);
130 DECL_PREPOST(termp_ss);
131 DECL_PREPOST(termp_sq);
132 DECL_PREPOST(termp_vt);
133
134 DECL_PRE(termp_ar);
135 DECL_PRE(termp_at);
136 DECL_PRE(termp_bf);
137 DECL_PRE(termp_bsx);
138 DECL_PRE(termp_bt);
139 DECL_PRE(termp_cd);
140 DECL_PRE(termp_cm);
141 DECL_PRE(termp_em);
142 DECL_PRE(termp_ex);
143 DECL_PRE(termp_fa);
144 DECL_PRE(termp_fl);
145 DECL_PRE(termp_fx);
146 DECL_PRE(termp_ic);
147 DECL_PRE(termp_ms);
148 DECL_PRE(termp_nd);
149 DECL_PRE(termp_nm);
150 DECL_PRE(termp_ns);
151 DECL_PRE(termp_nx);
152 DECL_PRE(termp_ox);
153 DECL_PRE(termp_pa);
154 DECL_PRE(termp_pp);
155 DECL_PRE(termp_rs);
156 DECL_PRE(termp_rv);
157 DECL_PRE(termp_sm);
158 DECL_PRE(termp_st);
159 DECL_PRE(termp_sx);
160 DECL_PRE(termp_sy);
161 DECL_PRE(termp_ud);
162 DECL_PRE(termp_ux);
163 DECL_PRE(termp_va);
164 DECL_PRE(termp_xr);
165
166 DECL_POST(termp___);
167 DECL_POST(termp_bl);
168 DECL_POST(termp_bx);
169
170 const struct termact __termacts[MDOC_MAX] = {
171 { NULL, NULL }, /* \" */
172 { NULL, NULL }, /* Dd */
173 { NULL, NULL }, /* Dt */
174 { NULL, NULL }, /* Os */
175 { termp_sh_pre, termp_sh_post }, /* Sh */
176 { termp_ss_pre, termp_ss_post }, /* Ss */
177 { termp_pp_pre, NULL }, /* Pp */
178 { termp_d1_pre, termp_d1_post }, /* D1 */
179 { termp_d1_pre, termp_d1_post }, /* Dl */
180 { termp_bd_pre, termp_bd_post }, /* Bd */
181 { NULL, NULL }, /* Ed */
182 { NULL, termp_bl_post }, /* Bl */
183 { NULL, NULL }, /* El */
184 { termp_it_pre, termp_it_post }, /* It */
185 { NULL, NULL }, /* Ad */
186 { NULL, NULL }, /* An */
187 { termp_ar_pre, NULL }, /* Ar */
188 { termp_cd_pre, NULL }, /* Cd */
189 { termp_cm_pre, NULL }, /* Cm */
190 { NULL, NULL }, /* Dv */
191 { NULL, NULL }, /* Er */
192 { NULL, NULL }, /* Ev */
193 { termp_ex_pre, NULL }, /* Ex */
194 { termp_fa_pre, NULL }, /* Fa */
195 { termp_fd_pre, termp_fd_post }, /* Fd */
196 { termp_fl_pre, NULL }, /* Fl */
197 { termp_fn_pre, termp_fn_post }, /* Fn */
198 { termp_ft_pre, termp_ft_post }, /* Ft */
199 { termp_ic_pre, NULL }, /* Ic */
200 { termp_in_pre, termp_in_post }, /* In */
201 { NULL, NULL }, /* Li */
202 { termp_nd_pre, NULL }, /* Nd */
203 { termp_nm_pre, NULL }, /* Nm */
204 { termp_op_pre, termp_op_post }, /* Op */
205 { NULL, NULL }, /* Ot */
206 { termp_pa_pre, NULL }, /* Pa */
207 { termp_rv_pre, NULL }, /* Rv */
208 { termp_st_pre, NULL }, /* St */
209 { termp_va_pre, NULL }, /* Va */
210 { termp_vt_pre, termp_vt_post }, /* Vt */
211 { termp_xr_pre, NULL }, /* Xr */
212 { NULL, termp____post }, /* %A */
213 { NULL, termp____post }, /* %B */
214 { NULL, termp____post }, /* %D */
215 { NULL, termp____post }, /* %I */
216 { NULL, termp____post }, /* %J */
217 { NULL, termp____post }, /* %N */
218 { NULL, termp____post }, /* %O */
219 { NULL, termp____post }, /* %P */
220 { NULL, termp____post }, /* %R */
221 { termp__t_pre, termp__t_post }, /* %T */
222 { NULL, termp____post }, /* %V */
223 { NULL, NULL }, /* Ac */
224 { termp_aq_pre, termp_aq_post }, /* Ao */
225 { termp_aq_pre, termp_aq_post }, /* Aq */
226 { termp_at_pre, NULL }, /* At */
227 { NULL, NULL }, /* Bc */
228 { termp_bf_pre, NULL }, /* Bf */
229 { termp_bq_pre, termp_bq_post }, /* Bo */
230 { termp_bq_pre, termp_bq_post }, /* Bq */
231 { termp_bsx_pre, NULL }, /* Bsx */
232 { NULL, termp_bx_post }, /* Bx */
233 { NULL, NULL }, /* Db */
234 { NULL, NULL }, /* Dc */
235 { termp_dq_pre, termp_dq_post }, /* Do */
236 { termp_dq_pre, termp_dq_post }, /* Dq */
237 { NULL, NULL }, /* Ec */
238 { NULL, NULL }, /* Ef */
239 { termp_em_pre, NULL }, /* Em */
240 { NULL, NULL }, /* Eo */
241 { termp_fx_pre, NULL }, /* Fx */
242 { termp_ms_pre, NULL }, /* Ms */
243 { NULL, NULL }, /* No */
244 { termp_ns_pre, NULL }, /* Ns */
245 { termp_nx_pre, NULL }, /* Nx */
246 { termp_ox_pre, NULL }, /* Ox */
247 { NULL, NULL }, /* Pc */
248 { termp_pf_pre, termp_pf_post }, /* Pf */
249 { termp_pq_pre, termp_pq_post }, /* Po */
250 { termp_pq_pre, termp_pq_post }, /* Pq */
251 { NULL, NULL }, /* Qc */
252 { termp_sq_pre, termp_sq_post }, /* Ql */
253 { termp_qq_pre, termp_qq_post }, /* Qo */
254 { termp_qq_pre, termp_qq_post }, /* Qq */
255 { NULL, NULL }, /* Re */
256 { termp_rs_pre, NULL }, /* Rs */
257 { NULL, NULL }, /* Sc */
258 { termp_sq_pre, termp_sq_post }, /* So */
259 { termp_sq_pre, termp_sq_post }, /* Sq */
260 { termp_sm_pre, NULL }, /* Sm */
261 { termp_sx_pre, NULL }, /* Sx */
262 { termp_sy_pre, NULL }, /* Sy */
263 { NULL, NULL }, /* Tn */
264 { termp_ux_pre, NULL }, /* Ux */
265 { NULL, NULL }, /* Xc */
266 { NULL, NULL }, /* Xo */
267 { termp_fo_pre, termp_fo_post }, /* Fo */
268 { NULL, NULL }, /* Fc */
269 { termp_op_pre, termp_op_post }, /* Oo */
270 { NULL, NULL }, /* Oc */
271 { NULL, NULL }, /* Bk */
272 { NULL, NULL }, /* Ek */
273 { termp_bt_pre, NULL }, /* Bt */
274 { NULL, NULL }, /* Hf */
275 { NULL, NULL }, /* Fr */
276 { termp_ud_pre, NULL }, /* Ud */
277 };
278
279 const struct termact *termacts = __termacts;
280
281
282 static size_t
283 arg_width(const struct mdoc_arg *arg)
284 {
285 size_t v;
286 int i, len;
287
288 assert(*arg->value);
289 if (0 == strcmp(*arg->value, "indent"))
290 return(INDENT);
291 if (0 == strcmp(*arg->value, "indent-two"))
292 return(INDENT * 2);
293
294 len = (int)strlen(*arg->value);
295 assert(len > 0);
296
297 for (i = 0; i < len - 1; i++)
298 if ( ! isdigit((int)(*arg->value)[i]))
299 break;
300
301 if (i == len - 1) {
302 if ('n' == (*arg->value)[len - 1]) {
303 v = (size_t)atoi(*arg->value);
304 return(v);
305 }
306
307 }
308 return(strlen(*arg->value) + 1);
309 }
310
311
312 static int
313 arg_listtype(const struct mdoc_node *n)
314 {
315 const struct mdoc_block *bl;
316 int i, len;
317
318 bl = &n->data.block;
319 len = (int)bl->argc;
320
321 for (i = 0; i < len; i++)
322 switch (bl->argv[i].arg) {
323 case (MDOC_Bullet):
324 /* FALLTHROUGH */
325 case (MDOC_Dash):
326 /* FALLTHROUGH */
327 case (MDOC_Enum):
328 /* FALLTHROUGH */
329 case (MDOC_Hyphen):
330 /* FALLTHROUGH */
331 case (MDOC_Tag):
332 /* FALLTHROUGH */
333 case (MDOC_Inset):
334 /* FALLTHROUGH */
335 case (MDOC_Diag):
336 /* FALLTHROUGH */
337 case (MDOC_Item):
338 /* FALLTHROUGH */
339 case (MDOC_Ohang):
340 return(bl->argv[i].arg);
341 default:
342 break;
343 }
344
345 errx(1, "list type not supported");
346 /* NOTREACHED */
347 }
348
349
350 static size_t
351 arg_offset(const struct mdoc_arg *arg)
352 {
353
354 /* TODO */
355 assert(*arg->value);
356 if (0 == strcmp(*arg->value, "indent"))
357 return(INDENT);
358 if (0 == strcmp(*arg->value, "indent-two"))
359 return(INDENT * 2);
360 return(strlen(*arg->value));
361 }
362
363
364 static int
365 arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
366 {
367
368 return(-1 != arg_getattr(arg, argc, argv));
369 }
370
371
372 static int
373 arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
374 {
375 int i;
376
377 for (i = 0; i < (int)argc; i++)
378 if (argv[i].arg == arg)
379 return(i);
380 return(-1);
381 }
382
383
384 /* ARGSUSED */
385 static int
386 termp_dq_pre(DECL_ARGS)
387 {
388
389 if (MDOC_BODY != node->type)
390 return(1);
391
392 word(p, "``");
393 p->flags |= TERMP_NOSPACE;
394 return(1);
395 }
396
397
398 /* ARGSUSED */
399 static void
400 termp_dq_post(DECL_ARGS)
401 {
402
403 if (MDOC_BODY != node->type)
404 return;
405
406 p->flags |= TERMP_NOSPACE;
407 word(p, "''");
408 }
409
410
411 /* ARGSUSED */
412 static int
413 termp_it_pre_block(DECL_ARGS)
414 {
415 const struct mdoc_node *n;
416 const struct mdoc_block *bl;
417
418 n = node->parent->parent;
419 bl = &n->data.block;
420
421 newln(p);
422 if ( ! arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
423 if (node->prev || n->prev)
424 vspace(p);
425
426 return(1);
427 }
428
429
430 /* ARGSUSED */
431 static int
432 termp_it_pre(DECL_ARGS)
433 {
434 const struct mdoc_block *bl;
435 char buf[7];
436 int i, type;
437 size_t width, offset;
438
439 if (MDOC_BLOCK == node->type)
440 return(termp_it_pre_block(p, pair, meta, node));
441
442 /* Get ptr to list block, type, etc. */
443
444 bl = &node->parent->parent->parent->data.block;
445 type = arg_listtype(node->parent->parent->parent);
446
447 /* Save parent attributes. */
448
449 pair->offset = p->offset;
450 pair->rmargin = p->rmargin;
451 pair->flag = p->flags;
452
453 /* Get list width and offset. */
454
455 i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
456 width = i >= 0 ? arg_width(&bl->argv[i]) : 0;
457
458 i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
459 offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
460
461 /*
462 * List-type can override the width in the case of fixed-head
463 * values (bullet, dash/hyphen, enum). Tags need a non-zero
464 * offset.
465 */
466
467 switch (type) {
468 case (MDOC_Bullet):
469 /* FALLTHROUGH */
470 case (MDOC_Dash):
471 /* FALLTHROUGH */
472 case (MDOC_Enum):
473 /* FALLTHROUGH */
474 case (MDOC_Hyphen):
475 width = width > 4 ? width : 4;
476 break;
477 case (MDOC_Tag):
478 if (width)
479 break;
480 errx(1, "need non-zero %s for list type",
481 mdoc_argnames[MDOC_Width]);
482 default:
483 break;
484 }
485
486 /*
487 * Whitespace control. Inset bodies need an initial space.
488 */
489
490 switch (type) {
491 case (MDOC_Diag):
492 /* FALLTHROUGH */
493 case (MDOC_Inset):
494 if (MDOC_BODY == node->type)
495 p->flags &= ~TERMP_NOSPACE;
496 else
497 p->flags |= TERMP_NOSPACE;
498 break;
499 default:
500 p->flags |= TERMP_NOSPACE;
501 break;
502 }
503
504 /*
505 * Style flags. Diagnostic heads need TTYPE_DIAG.
506 */
507
508 switch (type) {
509 case (MDOC_Diag):
510 if (MDOC_HEAD == node->type)
511 p->flags |= ttypes[TTYPE_DIAG];
512 break;
513 default:
514 break;
515 }
516
517 /*
518 * Pad and break control. This is the tricker part. Lists with
519 * set right-margins for the head get TERMP_NOBREAK because, if
520 * they overrun the margin, they wrap to the new margin.
521 * Correspondingly, the body for these types don't left-pad, as
522 * the head will pad out to to the right.
523 */
524
525 switch (type) {
526 case (MDOC_Bullet):
527 /* FALLTHROUGH */
528 case (MDOC_Dash):
529 /* FALLTHROUGH */
530 case (MDOC_Enum):
531 /* FALLTHROUGH */
532 case (MDOC_Hyphen):
533 /* FALLTHROUGH */
534 case (MDOC_Tag):
535 if (MDOC_HEAD == node->type)
536 p->flags |= TERMP_NOBREAK;
537 else
538 p->flags |= TERMP_NOLPAD;
539 break;
540 case (MDOC_Diag):
541 if (MDOC_HEAD == node->type)
542 p->flags |= TERMP_NOBREAK;
543 break;
544 default:
545 break;
546 }
547
548 /*
549 * Margin control. Set-head-width lists have their right
550 * margins shortened. The body for these lists has the offset
551 * necessarily lengthened. Everybody gets the offset.
552 */
553
554 p->offset += offset;
555
556 switch (type) {
557 case (MDOC_Bullet):
558 /* FALLTHROUGH */
559 case (MDOC_Dash):
560 /* FALLTHROUGH */
561 case (MDOC_Enum):
562 /* FALLTHROUGH */
563 case (MDOC_Hyphen):
564 /* FALLTHROUGH */
565 case (MDOC_Tag):
566 if (MDOC_HEAD == node->type)
567 p->rmargin = p->offset + width;
568 else
569 p->offset += width;
570 /* FALLTHROUGH */
571 default:
572 break;
573 }
574
575 /*
576 * The dash, hyphen, bullet and enum lists all have a special
577 * HEAD character. Print it now.
578 */
579
580 if (MDOC_HEAD == node->type)
581 switch (type) {
582 case (MDOC_Bullet):
583 word(p, "\\[bu]");
584 break;
585 case (MDOC_Dash):
586 /* FALLTHROUGH */
587 case (MDOC_Hyphen):
588 word(p, "\\-");
589 break;
590 case (MDOC_Enum):
591 /* TODO: have a wordfmt or something. */
592 (pair->ppair->ppair->count)++;
593 (void)snprintf(buf, sizeof(buf), "%d.",
594 pair->ppair->ppair->count);
595 word(p, buf);
596 break;
597 default:
598 break;
599 }
600
601 /*
602 * If we're not going to process our header children, indicate
603 * so here.
604 */
605
606 if (MDOC_HEAD == node->type)
607 switch (type) {
608 case (MDOC_Bullet):
609 /* FALLTHROUGH */
610 case (MDOC_Item):
611 /* FALLTHROUGH */
612 case (MDOC_Dash):
613 /* FALLTHROUGH */
614 case (MDOC_Hyphen):
615 /* FALLTHROUGH */
616 case (MDOC_Enum):
617 return(0);
618 default:
619 break;
620 }
621
622 return(1);
623 }
624
625
626 /* ARGSUSED */
627 static void
628 termp_it_post(DECL_ARGS)
629 {
630 int type;
631
632 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
633 return;
634
635 type = arg_listtype(node->parent->parent->parent);
636
637 switch (type) {
638 case (MDOC_Diag):
639 /* FALLTHROUGH */
640 case (MDOC_Item):
641 /* FALLTHROUGH */
642 case (MDOC_Inset):
643 if (MDOC_BODY != node->type)
644 break;
645 flushln(p);
646 break;
647 default:
648 flushln(p);
649 break;
650 }
651
652 p->offset = pair->offset;
653 p->rmargin = pair->rmargin;
654 p->flags = pair->flag;
655 }
656
657
658 /* ARGSUSED */
659 static int
660 termp_nm_pre(DECL_ARGS)
661 {
662
663 if (SEC_SYNOPSIS == node->sec)
664 newln(p);
665
666 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_PROG]);
667 if (NULL == node->child)
668 word(p, meta->name);
669
670 return(1);
671 }
672
673
674 /* ARGSUSED */
675 static int
676 termp_fl_pre(DECL_ARGS)
677 {
678
679 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
680 word(p, "\\-");
681 p->flags |= TERMP_NOSPACE;
682 return(1);
683 }
684
685
686 /* ARGSUSED */
687 static int
688 termp_ar_pre(DECL_ARGS)
689 {
690
691 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_ARG]);
692 if (NULL == node->child) {
693 word(p, "file");
694 word(p, "...");
695 }
696 return(1);
697 }
698
699
700 /* ARGSUSED */
701 static int
702 termp_ns_pre(DECL_ARGS)
703 {
704
705 p->flags |= TERMP_NOSPACE;
706 return(1);
707 }
708
709
710 /* ARGSUSED */
711 static int
712 termp_pp_pre(DECL_ARGS)
713 {
714
715 vspace(p);
716 return(1);
717 }
718
719
720 /* ARGSUSED */
721 static int
722 termp_st_pre(DECL_ARGS)
723 {
724 const char *tp;
725
726 assert(1 == node->data.elem.argc);
727
728 tp = mdoc_st2a(node->data.elem.argv[0].arg);
729 word(p, tp);
730
731 return(1);
732 }
733
734
735 /* ARGSUSED */
736 static int
737 termp_rs_pre(DECL_ARGS)
738 {
739
740 if (MDOC_BLOCK == node->type && node->prev)
741 vspace(p);
742 return(1);
743 }
744
745
746 /* ARGSUSED */
747 static int
748 termp_rv_pre(DECL_ARGS)
749 {
750 int i;
751
752 i = arg_getattr(MDOC_Std, node->data.elem.argc,
753 node->data.elem.argv);
754 assert(i >= 0);
755
756 newln(p);
757 word(p, "The");
758
759 p->flags |= ttypes[TTYPE_FUNC_NAME];
760 word(p, *node->data.elem.argv[i].value);
761 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
762
763 word(p, "() function returns the value 0 if successful;");
764 word(p, "otherwise the value -1 is returned and the");
765 word(p, "global variable");
766
767 p->flags |= ttypes[TTYPE_VAR_DECL];
768 word(p, "errno");
769 p->flags &= ~ttypes[TTYPE_VAR_DECL];
770
771 word(p, "is set to indicate the error.");
772
773 return(1);
774 }
775
776
777 /* ARGSUSED */
778 static int
779 termp_ex_pre(DECL_ARGS)
780 {
781 int i;
782
783 i = arg_getattr(MDOC_Std, node->data.elem.argc,
784 node->data.elem.argv);
785 assert(i >= 0);
786
787 word(p, "The");
788 p->flags |= ttypes[TTYPE_PROG];
789 word(p, *node->data.elem.argv[i].value);
790 p->flags &= ~ttypes[TTYPE_PROG];
791 word(p, "utility exits 0 on success, and >0 if an error occurs.");
792
793 return(1);
794 }
795
796
797 /* ARGSUSED */
798 static int
799 termp_nd_pre(DECL_ARGS)
800 {
801
802 word(p, "\\-");
803 return(1);
804 }
805
806
807 /* ARGSUSED */
808 static void
809 termp_bl_post(DECL_ARGS)
810 {
811
812 if (MDOC_BLOCK == node->type)
813 newln(p);
814 }
815
816
817 /* ARGSUSED */
818 static void
819 termp_op_post(DECL_ARGS)
820 {
821
822 if (MDOC_BODY != node->type)
823 return;
824 p->flags |= TERMP_NOSPACE;
825 word(p, "\\(rB");
826 }
827
828
829 /* ARGSUSED */
830 static int
831 termp_xr_pre(DECL_ARGS)
832 {
833 const struct mdoc_node *n;
834
835 n = node->child;
836 assert(n);
837
838 assert(MDOC_TEXT == n->type);
839 word(p, n->data.text.string);
840
841 if (NULL == (n = n->next))
842 return(0);
843
844 assert(MDOC_TEXT == n->type);
845 p->flags |= TERMP_NOSPACE;
846 word(p, "(");
847 p->flags |= TERMP_NOSPACE;
848 word(p, n->data.text.string);
849 p->flags |= TERMP_NOSPACE;
850 word(p, ")");
851
852 return(0);
853 }
854
855
856 /* ARGSUSED */
857 static int
858 termp_vt_pre(DECL_ARGS)
859 {
860
861 /* FIXME: this can be "type name". */
862 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
863 return(1);
864 }
865
866
867 /* ARGSUSED */
868 static void
869 termp_vt_post(DECL_ARGS)
870 {
871
872 if (node->sec == SEC_SYNOPSIS)
873 vspace(p);
874 }
875
876
877 /* ARGSUSED */
878 static int
879 termp_fd_pre(DECL_ARGS)
880 {
881
882 /*
883 * FIXME: this naming is bad. This value is used, in general,
884 * for the #include header or other preprocessor statement.
885 */
886 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_DECL]);
887 return(1);
888 }
889
890
891 /* ARGSUSED */
892 static void
893 termp_fd_post(DECL_ARGS)
894 {
895
896 if (node->sec != SEC_SYNOPSIS)
897 return;
898 newln(p);
899 if (node->next && MDOC_Fd != node->next->tok)
900 vspace(p);
901 }
902
903
904 /* ARGSUSED */
905 static int
906 termp_sh_pre(DECL_ARGS)
907 {
908
909 switch (node->type) {
910 case (MDOC_HEAD):
911 vspace(p);
912 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SECTION]);
913 break;
914 case (MDOC_BODY):
915 p->offset = INDENT;
916 break;
917 default:
918 break;
919 }
920 return(1);
921 }
922
923
924 /* ARGSUSED */
925 static void
926 termp_sh_post(DECL_ARGS)
927 {
928
929 switch (node->type) {
930 case (MDOC_HEAD):
931 newln(p);
932 break;
933 case (MDOC_BODY):
934 newln(p);
935 p->offset = 0;
936 break;
937 default:
938 break;
939 }
940 }
941
942
943 /* ARGSUSED */
944 static int
945 termp_op_pre(DECL_ARGS)
946 {
947
948 switch (node->type) {
949 case (MDOC_BODY):
950 word(p, "\\(lB");
951 p->flags |= TERMP_NOSPACE;
952 break;
953 default:
954 break;
955 }
956 return(1);
957 }
958
959
960 /* ARGSUSED */
961 static int
962 termp_bt_pre(DECL_ARGS)
963 {
964
965 word(p, "is currently in beta test.");
966 return(1);
967 }
968
969
970 /* ARGSUSED */
971 static int
972 termp_ud_pre(DECL_ARGS)
973 {
974
975 word(p, "currently under development.");
976 return(1);
977 }
978
979
980 /* ARGSUSED */
981 static int
982 termp_d1_pre(DECL_ARGS)
983 {
984
985 if (MDOC_BODY != node->type)
986 return(1);
987 newln(p);
988 p->offset += (pair->offset = INDENT);
989 return(1);
990 }
991
992
993 /* ARGSUSED */
994 static void
995 termp_d1_post(DECL_ARGS)
996 {
997
998 if (MDOC_BODY != node->type)
999 return;
1000 newln(p);
1001 p->offset -= pair->offset;
1002 }
1003
1004
1005 /* ARGSUSED */
1006 static int
1007 termp_aq_pre(DECL_ARGS)
1008 {
1009
1010 if (MDOC_BODY != node->type)
1011 return(1);
1012 word(p, "<");
1013 p->flags |= TERMP_NOSPACE;
1014 return(1);
1015 }
1016
1017
1018 /* ARGSUSED */
1019 static void
1020 termp_aq_post(DECL_ARGS)
1021 {
1022
1023 if (MDOC_BODY != node->type)
1024 return;
1025 p->flags |= TERMP_NOSPACE;
1026 word(p, ">");
1027 }
1028
1029
1030 /* ARGSUSED */
1031 static int
1032 termp_ft_pre(DECL_ARGS)
1033 {
1034
1035 if (SEC_SYNOPSIS == node->sec)
1036 if (node->prev && MDOC_Fo == node->prev->tok)
1037 vspace(p);
1038 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_TYPE]);
1039 return(1);
1040 }
1041
1042
1043 /* ARGSUSED */
1044 static void
1045 termp_ft_post(DECL_ARGS)
1046 {
1047
1048 if (SEC_SYNOPSIS == node->sec)
1049 newln(p);
1050 }
1051
1052
1053 /* ARGSUSED */
1054 static int
1055 termp_fn_pre(DECL_ARGS)
1056 {
1057 const struct mdoc_node *n;
1058
1059 assert(node->child);
1060 assert(MDOC_TEXT == node->child->type);
1061
1062 /* FIXME: can be "type funcname" "type varname"... */
1063
1064 p->flags |= ttypes[TTYPE_FUNC_NAME];
1065 word(p, node->child->data.text.string);
1066 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1067
1068 word(p, "(");
1069
1070 p->flags |= TERMP_NOSPACE;
1071 for (n = node->child->next; n; n = n->next) {
1072 assert(MDOC_TEXT == n->type);
1073 p->flags |= ttypes[TTYPE_FUNC_ARG];
1074 word(p, n->data.text.string);
1075 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1076 if (n->next)
1077 word(p, ",");
1078 }
1079
1080 word(p, ")");
1081
1082 if (SEC_SYNOPSIS == node->sec)
1083 word(p, ";");
1084
1085 return(0);
1086 }
1087
1088
1089 /* ARGSUSED */
1090 static void
1091 termp_fn_post(DECL_ARGS)
1092 {
1093
1094 if (node->sec == SEC_SYNOPSIS && node->next)
1095 vspace(p);
1096
1097 }
1098
1099
1100 /* ARGSUSED */
1101 static int
1102 termp_sx_pre(DECL_ARGS)
1103 {
1104
1105 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK]);
1106 return(1);
1107 }
1108
1109
1110 /* ARGSUSED */
1111 static int
1112 termp_fa_pre(DECL_ARGS)
1113 {
1114 struct mdoc_node *n;
1115
1116 if (node->parent->tok != MDOC_Fo) {
1117 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_ARG]);
1118 return(1);
1119 }
1120
1121 for (n = node->child; n; n = n->next) {
1122 assert(MDOC_TEXT == n->type);
1123
1124 p->flags |= ttypes[TTYPE_FUNC_ARG];
1125 word(p, n->data.text.string);
1126 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1127
1128 if (n->next)
1129 word(p, ",");
1130 }
1131
1132 if (node->next && node->next->tok == MDOC_Fa)
1133 word(p, ",");
1134
1135 return(0);
1136 }
1137
1138
1139 /* ARGSUSED */
1140 static int
1141 termp_va_pre(DECL_ARGS)
1142 {
1143
1144 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1145 return(1);
1146 }
1147
1148
1149 /* ARGSUSED */
1150 static int
1151 termp_bd_pre(DECL_ARGS)
1152 {
1153 const struct mdoc_block *bl;
1154 const struct mdoc_node *n;
1155 int i, type;
1156
1157 if (MDOC_BLOCK == node->type) {
1158 if (node->prev)
1159 vspace(p);
1160 return(1);
1161 } else if (MDOC_BODY != node->type)
1162 return(1);
1163
1164 pair->offset = p->offset;
1165 bl = &node->parent->data.block;
1166
1167 for (type = -1, i = 0; i < (int)bl->argc; i++) {
1168 switch (bl->argv[i].arg) {
1169 case (MDOC_Ragged):
1170 /* FALLTHROUGH */
1171 case (MDOC_Filled):
1172 /* FALLTHROUGH */
1173 case (MDOC_Unfilled):
1174 /* FALLTHROUGH */
1175 case (MDOC_Literal):
1176 type = bl->argv[i].arg;
1177 i = (int)bl->argc;
1178 break;
1179 default:
1180 errx(1, "display type not supported");
1181 }
1182 }
1183
1184 assert(-1 != type);
1185
1186 i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1187 if (-1 != i) {
1188 assert(1 == bl->argv[i].sz);
1189 p->offset += arg_offset(&bl->argv[i]);
1190 }
1191
1192
1193 switch (type) {
1194 case (MDOC_Literal):
1195 /* FALLTHROUGH */
1196 case (MDOC_Unfilled):
1197 break;
1198 default:
1199 return(1);
1200 }
1201
1202 p->flags |= TERMP_LITERAL;
1203
1204 for (n = node->child; n; n = n->next) {
1205 if (MDOC_TEXT != n->type) {
1206 warnx("non-text children not yet allowed");
1207 continue;
1208 }
1209 word(p, n->data.text.string);
1210 flushln(p);
1211 }
1212
1213 return(0);
1214 }
1215
1216
1217 /* ARGSUSED */
1218 static void
1219 termp_bd_post(DECL_ARGS)
1220 {
1221
1222 if (MDOC_BODY != node->type)
1223 return;
1224
1225 if ( ! (p->flags & TERMP_LITERAL))
1226 flushln(p);
1227
1228 p->flags &= ~TERMP_LITERAL;
1229 p->offset = pair->offset;
1230 }
1231
1232
1233 /* ARGSUSED */
1234 static int
1235 termp_qq_pre(DECL_ARGS)
1236 {
1237
1238 if (MDOC_BODY != node->type)
1239 return(1);
1240 word(p, "\"");
1241 p->flags |= TERMP_NOSPACE;
1242 return(1);
1243 }
1244
1245
1246 /* ARGSUSED */
1247 static void
1248 termp_qq_post(DECL_ARGS)
1249 {
1250
1251 if (MDOC_BODY != node->type)
1252 return;
1253 p->flags |= TERMP_NOSPACE;
1254 word(p, "\"");
1255 }
1256
1257
1258 /* ARGSUSED */
1259 static int
1260 termp_bsx_pre(DECL_ARGS)
1261 {
1262
1263 word(p, "BSDI BSD/OS");
1264 return(1);
1265 }
1266
1267
1268 /* ARGSUSED */
1269 static void
1270 termp_bx_post(DECL_ARGS)
1271 {
1272
1273 if (node->child)
1274 p->flags |= TERMP_NOSPACE;
1275 word(p, "BSD");
1276 }
1277
1278
1279 /* ARGSUSED */
1280 static int
1281 termp_ox_pre(DECL_ARGS)
1282 {
1283
1284 word(p, "OpenBSD");
1285 return(1);
1286 }
1287
1288
1289 /* ARGSUSED */
1290 static int
1291 termp_ux_pre(DECL_ARGS)
1292 {
1293
1294 word(p, "UNIX");
1295 return(1);
1296 }
1297
1298
1299 /* ARGSUSED */
1300 static int
1301 termp_fx_pre(DECL_ARGS)
1302 {
1303
1304 word(p, "FreeBSD");
1305 return(1);
1306 }
1307
1308
1309 /* ARGSUSED */
1310 static int
1311 termp_nx_pre(DECL_ARGS)
1312 {
1313
1314 word(p, "NetBSD");
1315 return(1);
1316 }
1317
1318
1319 /* ARGSUSED */
1320 static int
1321 termp_sq_pre(DECL_ARGS)
1322 {
1323
1324 if (MDOC_BODY != node->type)
1325 return(1);
1326 word(p, "`");
1327 p->flags |= TERMP_NOSPACE;
1328 return(1);
1329 }
1330
1331
1332 /* ARGSUSED */
1333 static void
1334 termp_sq_post(DECL_ARGS)
1335 {
1336
1337 if (MDOC_BODY != node->type)
1338 return;
1339 p->flags |= TERMP_NOSPACE;
1340 word(p, "\'");
1341 }
1342
1343
1344 /* ARGSUSED */
1345 static int
1346 termp_pf_pre(DECL_ARGS)
1347 {
1348
1349 p->flags |= TERMP_IGNDELIM;
1350 return(1);
1351 }
1352
1353
1354 /* ARGSUSED */
1355 static void
1356 termp_pf_post(DECL_ARGS)
1357 {
1358
1359 p->flags &= ~TERMP_IGNDELIM;
1360 p->flags |= TERMP_NOSPACE;
1361 }
1362
1363
1364 /* ARGSUSED */
1365 static int
1366 termp_ss_pre(DECL_ARGS)
1367 {
1368
1369 switch (node->type) {
1370 case (MDOC_HEAD):
1371 vspace(p);
1372 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1373 p->offset = INDENT / 2;
1374 break;
1375 default:
1376 break;
1377 }
1378
1379 return(1);
1380 }
1381
1382
1383 /* ARGSUSED */
1384 static void
1385 termp_ss_post(DECL_ARGS)
1386 {
1387
1388 switch (node->type) {
1389 case (MDOC_HEAD):
1390 newln(p);
1391 p->offset = INDENT;
1392 break;
1393 default:
1394 break;
1395 }
1396 }
1397
1398
1399 /* ARGSUSED */
1400 static int
1401 termp_pa_pre(DECL_ARGS)
1402 {
1403
1404 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1405 return(1);
1406 }
1407
1408
1409 /* ARGSUSED */
1410 static int
1411 termp_em_pre(DECL_ARGS)
1412 {
1413
1414 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1415 return(1);
1416 }
1417
1418
1419 /* ARGSUSED */
1420 static int
1421 termp_cd_pre(DECL_ARGS)
1422 {
1423
1424 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1425 newln(p);
1426 return(1);
1427 }
1428
1429
1430 /* ARGSUSED */
1431 static int
1432 termp_cm_pre(DECL_ARGS)
1433 {
1434
1435 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1436 return(1);
1437 }
1438
1439
1440 /* ARGSUSED */
1441 static int
1442 termp_ic_pre(DECL_ARGS)
1443 {
1444
1445 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1446 return(1);
1447 }
1448
1449
1450 /* ARGSUSED */
1451 static int
1452 termp_in_pre(DECL_ARGS)
1453 {
1454
1455 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1456 word(p, "#include");
1457 word(p, "<");
1458 p->flags |= TERMP_NOSPACE;
1459 return(1);
1460 }
1461
1462
1463 /* ARGSUSED */
1464 static void
1465 termp_in_post(DECL_ARGS)
1466 {
1467
1468 p->flags |= TERMP_NOSPACE;
1469 word(p, ">");
1470
1471 newln(p);
1472 if (SEC_SYNOPSIS != node->sec)
1473 return;
1474 if (node->next && MDOC_In != node->next->tok)
1475 vspace(p);
1476 }
1477
1478
1479 /* ARGSUSED */
1480 static int
1481 termp_at_pre(DECL_ARGS)
1482 {
1483 enum mdoc_att c;
1484
1485 c = ATT_DEFAULT;
1486 if (node->child) {
1487 assert(MDOC_TEXT == node->child->type);
1488 c = mdoc_atoatt(node->child->data.text.string);
1489 }
1490
1491 word(p, mdoc_att2a(c));
1492 return(0);
1493 }
1494
1495
1496 /* ARGSUSED */
1497 static int
1498 termp_bq_pre(DECL_ARGS)
1499 {
1500
1501 if (MDOC_BODY != node->type)
1502 return(1);
1503 word(p, "[");
1504 p->flags |= TERMP_NOSPACE;
1505 return(1);
1506 }
1507
1508
1509 /* ARGSUSED */
1510 static void
1511 termp_bq_post(DECL_ARGS)
1512 {
1513
1514 if (MDOC_BODY != node->type)
1515 return;
1516 word(p, "]");
1517 }
1518
1519
1520 /* ARGSUSED */
1521 static int
1522 termp_pq_pre(DECL_ARGS)
1523 {
1524
1525 if (MDOC_BODY != node->type)
1526 return(1);
1527 word(p, "\\&(");
1528 p->flags |= TERMP_NOSPACE;
1529 return(1);
1530 }
1531
1532
1533 /* ARGSUSED */
1534 static void
1535 termp_pq_post(DECL_ARGS)
1536 {
1537
1538 if (MDOC_BODY != node->type)
1539 return;
1540 word(p, ")");
1541 }
1542
1543
1544 /* ARGSUSED */
1545 static int
1546 termp_fo_pre(DECL_ARGS)
1547 {
1548 const struct mdoc_node *n;
1549
1550 if (MDOC_BODY == node->type) {
1551 word(p, "(");
1552 p->flags |= TERMP_NOSPACE;
1553 return(1);
1554 } else if (MDOC_HEAD != node->type)
1555 return(1);
1556
1557 /* XXX - groff shows only first parameter */
1558
1559 p->flags |= ttypes[TTYPE_FUNC_NAME];
1560 for (n = node->child; n; n = n->next) {
1561 assert(MDOC_TEXT == n->type);
1562 word(p, n->data.text.string);
1563 }
1564 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1565
1566 return(0);
1567 }
1568
1569
1570 /* ARGSUSED */
1571 static void
1572 termp_fo_post(DECL_ARGS)
1573 {
1574
1575 if (MDOC_BODY != node->type)
1576 return;
1577 word(p, ")");
1578 word(p, ";");
1579 newln(p);
1580 }
1581
1582
1583 /* ARGSUSED */
1584 static int
1585 termp_bf_pre(DECL_ARGS)
1586 {
1587 const struct mdoc_node *n;
1588 const struct mdoc_block *b;
1589
1590 /* XXX - we skip over possible trailing HEAD tokens. */
1591
1592 if (MDOC_HEAD == node->type)
1593 return(0);
1594 else if (MDOC_BLOCK != node->type)
1595 return(1);
1596
1597 b = &node->data.block;
1598
1599 if (NULL == (n = b->head->child)) {
1600 if (arg_hasattr(MDOC_Emphasis, b->argc, b->argv))
1601 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1602 else if (arg_hasattr(MDOC_Symbolic, b->argc, b->argv))
1603 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1604
1605 return(1);
1606 }
1607
1608 assert(MDOC_TEXT == n->type);
1609
1610 if (0 == strcmp("Em", n->data.text.string))
1611 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1612 else if (0 == strcmp("Sy", n->data.text.string))
1613 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1614
1615 return(1);
1616 }
1617
1618
1619 /* ARGSUSED */
1620 static int
1621 termp_sy_pre(DECL_ARGS)
1622 {
1623
1624 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1625 return(1);
1626 }
1627
1628
1629 /* ARGSUSED */
1630 static int
1631 termp_ms_pre(DECL_ARGS)
1632 {
1633
1634 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMBOL]);
1635 return(1);
1636 }
1637
1638
1639
1640 /* ARGSUSED */
1641 static int
1642 termp_sm_pre(DECL_ARGS)
1643 {
1644
1645 #if notyet
1646 assert(node->child);
1647 if (0 == strcmp("off", node->child->data.text.string)) {
1648 p->flags &= ~TERMP_NONOSPACE;
1649 p->flags &= ~TERMP_NOSPACE;
1650 } else {
1651 p->flags |= TERMP_NONOSPACE;
1652 p->flags |= TERMP_NOSPACE;
1653 }
1654 #endif
1655
1656 return(0);
1657 }
1658
1659
1660 /* ARGSUSED */
1661 static int
1662 termp__t_pre(DECL_ARGS)
1663 {
1664
1665 word(p, "\"");
1666 p->flags |= TERMP_NOSPACE;
1667 return(1);
1668 }
1669
1670
1671 /* ARGSUSED */
1672 static void
1673 termp__t_post(DECL_ARGS)
1674 {
1675
1676 p->flags |= TERMP_NOSPACE;
1677 word(p, "\"");
1678 word(p, node->next ? "," : ".");
1679 }
1680
1681
1682 /* ARGSUSED */
1683 static void
1684 termp____post(DECL_ARGS)
1685 {
1686
1687 p->flags |= TERMP_NOSPACE;
1688 word(p, node->next ? "," : ".");
1689 }