]> git.cameronkatri.com Git - mandoc.git/blob - term.c
e1fa44da23f3308da1146805af1fab906b44ec40
[mandoc.git] / term.c
1 /* $Id: term.c,v 1.34 2009/03/02 12:09:32 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;
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 assert(MDOC_BLOCK == node->parent->type);
1065 pair->offset = p->offset;
1066
1067 bl = &node->parent->data.block;
1068
1069 i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1070 if (-1 != i) {
1071 assert(1 == bl->argv[i].sz);
1072 p->offset += arg_offset(&bl->argv[i]);
1073 }
1074
1075 p->flags |= TERMP_LITERAL;
1076
1077 for (n = node->child; n; n = n->next) {
1078 if (MDOC_TEXT != n->type)
1079 errx(1, "non-text displays unsupported");
1080 if ((*n->data.text.string)) {
1081 word(p, n->data.text.string);
1082 flushln(p);
1083 } else
1084 vspace(p);
1085
1086 }
1087
1088 p->flags &= ~TERMP_LITERAL;
1089 return(0);
1090 }
1091
1092
1093 /* ARGSUSED */
1094 static void
1095 termp_bd_post(DECL_ARGS)
1096 {
1097
1098 if (MDOC_BODY != node->type)
1099 return;
1100 newln(p);
1101 p->offset = pair->offset;
1102 }
1103
1104
1105 /* ARGSUSED */
1106 static int
1107 termp_qq_pre(DECL_ARGS)
1108 {
1109
1110 if (MDOC_BODY != node->type)
1111 return(1);
1112 word(p, "\"");
1113 p->flags |= TERMP_NOSPACE;
1114 return(1);
1115 }
1116
1117
1118 /* ARGSUSED */
1119 static void
1120 termp_qq_post(DECL_ARGS)
1121 {
1122
1123 if (MDOC_BODY != node->type)
1124 return;
1125 p->flags |= TERMP_NOSPACE;
1126 word(p, "\"");
1127 }
1128
1129
1130 /* ARGSUSED */
1131 static int
1132 termp_bsx_pre(DECL_ARGS)
1133 {
1134
1135 word(p, "BSDI BSD/OS");
1136 return(1);
1137 }
1138
1139
1140 /* ARGSUSED */
1141 static void
1142 termp_bx_post(DECL_ARGS)
1143 {
1144
1145 if (node->child)
1146 p->flags |= TERMP_NOSPACE;
1147 word(p, "BSD");
1148 }
1149
1150
1151 /* ARGSUSED */
1152 static int
1153 termp_ox_pre(DECL_ARGS)
1154 {
1155
1156 word(p, "OpenBSD");
1157 return(1);
1158 }
1159
1160
1161 /* ARGSUSED */
1162 static int
1163 termp_ux_pre(DECL_ARGS)
1164 {
1165
1166 word(p, "UNIX");
1167 return(1);
1168 }
1169
1170
1171 /* ARGSUSED */
1172 static int
1173 termp_fx_pre(DECL_ARGS)
1174 {
1175
1176 word(p, "FreeBSD");
1177 return(1);
1178 }
1179
1180
1181 /* ARGSUSED */
1182 static int
1183 termp_nx_pre(DECL_ARGS)
1184 {
1185
1186 word(p, "NetBSD");
1187 return(1);
1188 }
1189
1190
1191 /* ARGSUSED */
1192 static int
1193 termp_sq_pre(DECL_ARGS)
1194 {
1195
1196 if (MDOC_BODY != node->type)
1197 return(1);
1198 word(p, "`");
1199 p->flags |= TERMP_NOSPACE;
1200 return(1);
1201 }
1202
1203
1204 /* ARGSUSED */
1205 static void
1206 termp_sq_post(DECL_ARGS)
1207 {
1208
1209 if (MDOC_BODY != node->type)
1210 return;
1211 p->flags |= TERMP_NOSPACE;
1212 word(p, "\'");
1213 }
1214
1215
1216 /* ARGSUSED */
1217 static int
1218 termp_pf_pre(DECL_ARGS)
1219 {
1220
1221 p->flags |= TERMP_IGNDELIM;
1222 return(1);
1223 }
1224
1225
1226 /* ARGSUSED */
1227 static void
1228 termp_pf_post(DECL_ARGS)
1229 {
1230
1231 p->flags &= ~TERMP_IGNDELIM;
1232 p->flags |= TERMP_NOSPACE;
1233 }
1234
1235
1236 /* ARGSUSED */
1237 static int
1238 termp_ss_pre(DECL_ARGS)
1239 {
1240
1241 switch (node->type) {
1242 case (MDOC_HEAD):
1243 vspace(p);
1244 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1245 p->offset = INDENT / 2;
1246 break;
1247 default:
1248 break;
1249 }
1250
1251 return(1);
1252 }
1253
1254
1255 /* ARGSUSED */
1256 static void
1257 termp_ss_post(DECL_ARGS)
1258 {
1259
1260 switch (node->type) {
1261 case (MDOC_HEAD):
1262 newln(p);
1263 p->offset = INDENT;
1264 break;
1265 default:
1266 break;
1267 }
1268 }
1269
1270
1271 /* ARGSUSED */
1272 static int
1273 termp_pa_pre(DECL_ARGS)
1274 {
1275
1276 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1277 return(1);
1278 }
1279
1280
1281 /* ARGSUSED */
1282 static int
1283 termp_em_pre(DECL_ARGS)
1284 {
1285
1286 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1287 return(1);
1288 }
1289
1290
1291 /* ARGSUSED */
1292 static int
1293 termp_cd_pre(DECL_ARGS)
1294 {
1295
1296 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1297 newln(p);
1298 return(1);
1299 }
1300
1301
1302 /* ARGSUSED */
1303 static int
1304 termp_cm_pre(DECL_ARGS)
1305 {
1306
1307 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1308 return(1);
1309 }
1310
1311
1312 /* ARGSUSED */
1313 static int
1314 termp_ic_pre(DECL_ARGS)
1315 {
1316
1317 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1318 return(1);
1319 }
1320
1321
1322 /* ARGSUSED */
1323 static int
1324 termp_in_pre(DECL_ARGS)
1325 {
1326
1327 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1328 word(p, "#include");
1329 word(p, "<");
1330 p->flags |= TERMP_NOSPACE;
1331 return(1);
1332 }
1333
1334
1335 /* ARGSUSED */
1336 static void
1337 termp_in_post(DECL_ARGS)
1338 {
1339
1340 p->flags |= TERMP_NOSPACE;
1341 word(p, ">");
1342
1343 newln(p);
1344 if (SEC_SYNOPSIS != node->sec)
1345 return;
1346 if (node->next && MDOC_In != node->next->tok)
1347 vspace(p);
1348 }
1349
1350
1351 /* ARGSUSED */
1352 static int
1353 termp_at_pre(DECL_ARGS)
1354 {
1355 enum mdoc_att c;
1356
1357 c = ATT_DEFAULT;
1358 if (node->child) {
1359 assert(MDOC_TEXT == node->child->type);
1360 c = mdoc_atoatt(node->child->data.text.string);
1361 }
1362
1363 word(p, mdoc_att2a(c));
1364 return(0);
1365 }
1366
1367
1368 /* ARGSUSED */
1369 static int
1370 termp_bq_pre(DECL_ARGS)
1371 {
1372
1373 if (MDOC_BODY != node->type)
1374 return(1);
1375 word(p, "[");
1376 p->flags |= TERMP_NOSPACE;
1377 return(1);
1378 }
1379
1380
1381 /* ARGSUSED */
1382 static void
1383 termp_bq_post(DECL_ARGS)
1384 {
1385
1386 if (MDOC_BODY != node->type)
1387 return;
1388 word(p, "]");
1389 }
1390
1391
1392 /* ARGSUSED */
1393 static int
1394 termp_pq_pre(DECL_ARGS)
1395 {
1396
1397 if (MDOC_BODY != node->type)
1398 return(1);
1399 word(p, "\\&(");
1400 p->flags |= TERMP_NOSPACE;
1401 return(1);
1402 }
1403
1404
1405 /* ARGSUSED */
1406 static void
1407 termp_pq_post(DECL_ARGS)
1408 {
1409
1410 if (MDOC_BODY != node->type)
1411 return;
1412 word(p, ")");
1413 }
1414
1415
1416 /* ARGSUSED */
1417 static int
1418 termp_fo_pre(DECL_ARGS)
1419 {
1420 const struct mdoc_node *n;
1421
1422 if (MDOC_BODY == node->type) {
1423 word(p, "(");
1424 p->flags |= TERMP_NOSPACE;
1425 return(1);
1426 } else if (MDOC_HEAD != node->type)
1427 return(1);
1428
1429 /* XXX - groff shows only first parameter */
1430
1431 p->flags |= ttypes[TTYPE_FUNC_NAME];
1432 for (n = node->child; n; n = n->next) {
1433 assert(MDOC_TEXT == n->type);
1434 word(p, n->data.text.string);
1435 }
1436 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1437
1438 return(0);
1439 }
1440
1441
1442 /* ARGSUSED */
1443 static void
1444 termp_fo_post(DECL_ARGS)
1445 {
1446
1447 if (MDOC_BODY != node->type)
1448 return;
1449 word(p, ")");
1450 word(p, ";");
1451 newln(p);
1452 }
1453
1454
1455 /* ARGSUSED */
1456 static int
1457 termp_bf_pre(DECL_ARGS)
1458 {
1459 const struct mdoc_node *n;
1460 const struct mdoc_block *b;
1461
1462 /* XXX - we skip over possible trailing HEAD tokens. */
1463
1464 if (MDOC_HEAD == node->type)
1465 return(0);
1466 else if (MDOC_BLOCK != node->type)
1467 return(1);
1468
1469 b = &node->data.block;
1470
1471 if (NULL == (n = b->head->child)) {
1472 if (arg_hasattr(MDOC_Emphasis, b->argc, b->argv))
1473 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1474 else if (arg_hasattr(MDOC_Symbolic, b->argc, b->argv))
1475 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1476
1477 return(1);
1478 }
1479
1480 assert(MDOC_TEXT == n->type);
1481
1482 if (0 == strcmp("Em", n->data.text.string))
1483 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1484 else if (0 == strcmp("Sy", n->data.text.string))
1485 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1486
1487 return(1);
1488 }
1489
1490
1491 /* ARGSUSED */
1492 static int
1493 termp_sy_pre(DECL_ARGS)
1494 {
1495
1496 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1497 return(1);
1498 }
1499
1500
1501 /* ARGSUSED */
1502 static int
1503 termp_ms_pre(DECL_ARGS)
1504 {
1505
1506 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMBOL]);
1507 return(1);
1508 }
1509
1510
1511
1512 /* ARGSUSED */
1513 static int
1514 termp_sm_pre(DECL_ARGS)
1515 {
1516
1517 #if notyet
1518 assert(node->child);
1519 if (0 == strcmp("off", node->child->data.text.string)) {
1520 p->flags &= ~TERMP_NONOSPACE;
1521 p->flags &= ~TERMP_NOSPACE;
1522 } else {
1523 p->flags |= TERMP_NONOSPACE;
1524 p->flags |= TERMP_NOSPACE;
1525 }
1526 #endif
1527
1528 return(0);
1529 }
1530
1531
1532 /* ARGSUSED */
1533 static int
1534 termp__t_pre(DECL_ARGS)
1535 {
1536
1537 word(p, "\"");
1538 p->flags |= TERMP_NOSPACE;
1539 return(1);
1540 }
1541
1542
1543 /* ARGSUSED */
1544 static void
1545 termp__t_post(DECL_ARGS)
1546 {
1547
1548 p->flags |= TERMP_NOSPACE;
1549 word(p, "\"");
1550 word(p, node->next ? "," : ".");
1551 }
1552
1553
1554 /* ARGSUSED */
1555 static void
1556 termp____post(DECL_ARGS)
1557 {
1558
1559 p->flags |= TERMP_NOSPACE;
1560 word(p, node->next ? "," : ".");
1561 }