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