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