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