]> git.cameronkatri.com Git - mandoc.git/blob - term.c
-inset and -diag lists now supported.
[mandoc.git] / term.c
1 /* $Id: term.c,v 1.37 2009/03/03 22:17:19 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 > 6 ? width : 6;
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 if (MDOC_HEAD == node->type)
473 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_DIAG]);
474 /* FALLTHROUGH */
475 case (MDOC_Inset):
476 if (MDOC_HEAD == node->type)
477 p->flags |= TERMP_NOSPACE;
478 break;
479 case (MDOC_Bullet):
480 /* FALLTHROUGH */
481 case (MDOC_Dash):
482 /* FALLTHROUGH */
483 case (MDOC_Enum):
484 /* FALLTHROUGH */
485 case (MDOC_Hyphen):
486 /* FALLTHROUGH */
487 case (MDOC_Tag):
488 p->flags |= TERMP_NOSPACE;
489 if (MDOC_HEAD == node->type)
490 p->flags |= TERMP_NOBREAK;
491 else if (MDOC_BODY == node->type)
492 p->flags |= TERMP_NOLPAD;
493 break;
494 default:
495 break;
496 }
497
498 /*
499 * Get a token to use as the HEAD lead-in. If NULL, we use the
500 * HEAD child.
501 */
502
503 tp = NULL;
504
505 if (MDOC_HEAD == node->type) {
506 if (arg_hasattr(MDOC_Bullet, bl->argc, bl->argv))
507 tp = "\\[bu]";
508 if (arg_hasattr(MDOC_Dash, bl->argc, bl->argv))
509 tp = "\\-";
510 if (arg_hasattr(MDOC_Enum, bl->argc, bl->argv)) {
511 (pair->ppair->ppair->count)++;
512 (void)snprintf(buf, sizeof(buf), "%d.",
513 pair->ppair->ppair->count);
514 tp = buf;
515 }
516 if (arg_hasattr(MDOC_Hyphen, bl->argc, bl->argv))
517 tp = "\\-";
518 }
519
520 /* Margin control. */
521
522 p->offset += offset;
523
524 switch (type) {
525 case (MDOC_Bullet):
526 /* FALLTHROUGH */
527 case (MDOC_Dash):
528 /* FALLTHROUGH */
529 case (MDOC_Enum):
530 /* FALLTHROUGH */
531 case (MDOC_Hyphen):
532 /* FALLTHROUGH */
533 case (MDOC_Tag):
534 if (MDOC_HEAD == node->type)
535 p->rmargin = p->offset + width;
536 else if (MDOC_BODY == node->type)
537 p->offset += width;
538 break;
539 default:
540 break;
541 }
542
543 if (NULL == tp)
544 return(1);
545
546 word(p, tp);
547 return(0);
548 }
549
550
551 /* ARGSUSED */
552 static void
553 termp_it_post(DECL_ARGS)
554 {
555 int type, i;
556 struct mdoc_block *bl;
557
558 if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
559 return;
560
561 assert(MDOC_BLOCK == node->parent->parent->parent->type);
562 assert(MDOC_Bl == node->parent->parent->parent->tok);
563 bl = &node->parent->parent->parent->data.block;
564
565 for (type = -1, i = 0; i < (int)bl->argc; i++)
566 switch (bl->argv[i].arg) {
567 case (MDOC_Bullet):
568 /* FALLTHROUGH */
569 case (MDOC_Dash):
570 /* FALLTHROUGH */
571 case (MDOC_Enum):
572 /* FALLTHROUGH */
573 case (MDOC_Hyphen):
574 /* FALLTHROUGH */
575 case (MDOC_Tag):
576 /* FALLTHROUGH */
577 case (MDOC_Diag):
578 /* FALLTHROUGH */
579 case (MDOC_Inset):
580 /* FALLTHROUGH */
581 case (MDOC_Ohang):
582 type = bl->argv[i].arg;
583 i = (int)bl->argc;
584 break;
585 default:
586 errx(1, "list type not supported");
587 /* NOTREACHED */
588 }
589
590
591 switch (type) {
592 case (MDOC_Diag):
593 /* FALLTHROUGH */
594 case (MDOC_Inset):
595 break;
596 default:
597 flushln(p);
598 break;
599 }
600
601 p->offset = pair->offset;
602 p->rmargin = pair->rmargin;
603
604 switch (type) {
605 case (MDOC_Inset):
606 break;
607 default:
608 if (MDOC_HEAD == node->type)
609 p->flags &= ~TERMP_NOBREAK;
610 else if (MDOC_BODY == node->type)
611 p->flags &= ~TERMP_NOLPAD;
612 break;
613 }
614 }
615
616
617 /* ARGSUSED */
618 static int
619 termp_nm_pre(DECL_ARGS)
620 {
621
622 if (SEC_SYNOPSIS == node->sec)
623 newln(p);
624
625 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_PROG]);
626 if (NULL == node->child)
627 word(p, meta->name);
628
629 return(1);
630 }
631
632
633 /* ARGSUSED */
634 static int
635 termp_fl_pre(DECL_ARGS)
636 {
637
638 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
639 word(p, "\\-");
640 p->flags |= TERMP_NOSPACE;
641 return(1);
642 }
643
644
645 /* ARGSUSED */
646 static int
647 termp_ar_pre(DECL_ARGS)
648 {
649
650 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_ARG]);
651 if (NULL == node->child) {
652 word(p, "file");
653 word(p, "...");
654 }
655 return(1);
656 }
657
658
659 /* ARGSUSED */
660 static int
661 termp_ns_pre(DECL_ARGS)
662 {
663
664 p->flags |= TERMP_NOSPACE;
665 return(1);
666 }
667
668
669 /* ARGSUSED */
670 static int
671 termp_pp_pre(DECL_ARGS)
672 {
673
674 vspace(p);
675 return(1);
676 }
677
678
679 /* ARGSUSED */
680 static int
681 termp_st_pre(DECL_ARGS)
682 {
683 const char *tp;
684
685 assert(1 == node->data.elem.argc);
686
687 tp = mdoc_st2a(node->data.elem.argv[0].arg);
688 word(p, tp);
689
690 return(1);
691 }
692
693
694 /* ARGSUSED */
695 static int
696 termp_rs_pre(DECL_ARGS)
697 {
698
699 if (MDOC_BLOCK == node->type && node->prev)
700 vspace(p);
701 return(1);
702 }
703
704
705 /* ARGSUSED */
706 static int
707 termp_rv_pre(DECL_ARGS)
708 {
709 int i;
710
711 i = arg_getattr(MDOC_Std, node->data.elem.argc,
712 node->data.elem.argv);
713 assert(i >= 0);
714
715 newln(p);
716 word(p, "The");
717
718 p->flags |= ttypes[TTYPE_FUNC_NAME];
719 word(p, *node->data.elem.argv[i].value);
720 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
721
722 word(p, "() function returns the value 0 if successful;");
723 word(p, "otherwise the value -1 is returned and the");
724 word(p, "global variable");
725
726 p->flags |= ttypes[TTYPE_VAR_DECL];
727 word(p, "errno");
728 p->flags &= ~ttypes[TTYPE_VAR_DECL];
729
730 word(p, "is set to indicate the error.");
731
732 return(1);
733 }
734
735
736 /* ARGSUSED */
737 static int
738 termp_ex_pre(DECL_ARGS)
739 {
740 int i;
741
742 i = arg_getattr(MDOC_Std, node->data.elem.argc,
743 node->data.elem.argv);
744 assert(i >= 0);
745
746 word(p, "The");
747 p->flags |= ttypes[TTYPE_PROG];
748 word(p, *node->data.elem.argv[i].value);
749 p->flags &= ~ttypes[TTYPE_PROG];
750 word(p, "utility exits 0 on success, and >0 if an error occurs.");
751
752 return(1);
753 }
754
755
756 /* ARGSUSED */
757 static int
758 termp_nd_pre(DECL_ARGS)
759 {
760
761 word(p, "\\-");
762 return(1);
763 }
764
765
766 /* ARGSUSED */
767 static void
768 termp_bl_post(DECL_ARGS)
769 {
770
771 if (MDOC_BLOCK == node->type)
772 newln(p);
773 }
774
775
776 /* ARGSUSED */
777 static void
778 termp_op_post(DECL_ARGS)
779 {
780
781 if (MDOC_BODY != node->type)
782 return;
783 p->flags |= TERMP_NOSPACE;
784 word(p, "\\(rB");
785 }
786
787
788 /* ARGSUSED */
789 static int
790 termp_xr_pre(DECL_ARGS)
791 {
792 const struct mdoc_node *n;
793
794 n = node->child;
795 assert(n);
796
797 assert(MDOC_TEXT == n->type);
798 word(p, n->data.text.string);
799
800 if (NULL == (n = n->next))
801 return(0);
802
803 assert(MDOC_TEXT == n->type);
804 p->flags |= TERMP_NOSPACE;
805 word(p, "(");
806 p->flags |= TERMP_NOSPACE;
807 word(p, n->data.text.string);
808 p->flags |= TERMP_NOSPACE;
809 word(p, ")");
810
811 return(0);
812 }
813
814
815 /* ARGSUSED */
816 static int
817 termp_vt_pre(DECL_ARGS)
818 {
819
820 /* FIXME: this can be "type name". */
821 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
822 return(1);
823 }
824
825
826 /* ARGSUSED */
827 static void
828 termp_vt_post(DECL_ARGS)
829 {
830
831 if (node->sec == SEC_SYNOPSIS)
832 vspace(p);
833 }
834
835
836 /* ARGSUSED */
837 static int
838 termp_fd_pre(DECL_ARGS)
839 {
840
841 /*
842 * FIXME: this naming is bad. This value is used, in general,
843 * for the #include header or other preprocessor statement.
844 */
845 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_DECL]);
846 return(1);
847 }
848
849
850 /* ARGSUSED */
851 static void
852 termp_fd_post(DECL_ARGS)
853 {
854
855 if (node->sec != SEC_SYNOPSIS)
856 return;
857 newln(p);
858 if (node->next && MDOC_Fd != node->next->tok)
859 vspace(p);
860 }
861
862
863 /* ARGSUSED */
864 static int
865 termp_sh_pre(DECL_ARGS)
866 {
867
868 switch (node->type) {
869 case (MDOC_HEAD):
870 vspace(p);
871 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SECTION]);
872 break;
873 case (MDOC_BODY):
874 p->offset = INDENT;
875 break;
876 default:
877 break;
878 }
879 return(1);
880 }
881
882
883 /* ARGSUSED */
884 static void
885 termp_sh_post(DECL_ARGS)
886 {
887
888 switch (node->type) {
889 case (MDOC_HEAD):
890 newln(p);
891 break;
892 case (MDOC_BODY):
893 newln(p);
894 p->offset = 0;
895 break;
896 default:
897 break;
898 }
899 }
900
901
902 /* ARGSUSED */
903 static int
904 termp_op_pre(DECL_ARGS)
905 {
906
907 switch (node->type) {
908 case (MDOC_BODY):
909 word(p, "\\(lB");
910 p->flags |= TERMP_NOSPACE;
911 break;
912 default:
913 break;
914 }
915 return(1);
916 }
917
918
919 /* ARGSUSED */
920 static int
921 termp_bt_pre(DECL_ARGS)
922 {
923
924 word(p, "is currently in beta test.");
925 return(1);
926 }
927
928
929 /* ARGSUSED */
930 static int
931 termp_ud_pre(DECL_ARGS)
932 {
933
934 word(p, "currently under development.");
935 return(1);
936 }
937
938
939 /* ARGSUSED */
940 static int
941 termp_d1_pre(DECL_ARGS)
942 {
943
944 if (MDOC_BODY != node->type)
945 return(1);
946 newln(p);
947 p->offset += (pair->offset = INDENT);
948 return(1);
949 }
950
951
952 /* ARGSUSED */
953 static void
954 termp_d1_post(DECL_ARGS)
955 {
956
957 if (MDOC_BODY != node->type)
958 return;
959 newln(p);
960 p->offset -= pair->offset;
961 }
962
963
964 /* ARGSUSED */
965 static int
966 termp_aq_pre(DECL_ARGS)
967 {
968
969 if (MDOC_BODY != node->type)
970 return(1);
971 word(p, "<");
972 p->flags |= TERMP_NOSPACE;
973 return(1);
974 }
975
976
977 /* ARGSUSED */
978 static void
979 termp_aq_post(DECL_ARGS)
980 {
981
982 if (MDOC_BODY != node->type)
983 return;
984 p->flags |= TERMP_NOSPACE;
985 word(p, ">");
986 }
987
988
989 /* ARGSUSED */
990 static int
991 termp_ft_pre(DECL_ARGS)
992 {
993
994 if (SEC_SYNOPSIS == node->sec)
995 if (node->prev && MDOC_Fo == node->prev->tok)
996 vspace(p);
997 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_TYPE]);
998 return(1);
999 }
1000
1001
1002 /* ARGSUSED */
1003 static void
1004 termp_ft_post(DECL_ARGS)
1005 {
1006
1007 if (SEC_SYNOPSIS == node->sec)
1008 newln(p);
1009 }
1010
1011
1012 /* ARGSUSED */
1013 static int
1014 termp_fn_pre(DECL_ARGS)
1015 {
1016 const struct mdoc_node *n;
1017
1018 assert(node->child);
1019 assert(MDOC_TEXT == node->child->type);
1020
1021 /* FIXME: can be "type funcname" "type varname"... */
1022
1023 p->flags |= ttypes[TTYPE_FUNC_NAME];
1024 word(p, node->child->data.text.string);
1025 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1026
1027 word(p, "(");
1028
1029 p->flags |= TERMP_NOSPACE;
1030 for (n = node->child->next; n; n = n->next) {
1031 assert(MDOC_TEXT == n->type);
1032 p->flags |= ttypes[TTYPE_FUNC_ARG];
1033 word(p, n->data.text.string);
1034 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1035 if (n->next)
1036 word(p, ",");
1037 }
1038
1039 word(p, ")");
1040
1041 if (SEC_SYNOPSIS == node->sec)
1042 word(p, ";");
1043
1044 return(0);
1045 }
1046
1047
1048 /* ARGSUSED */
1049 static void
1050 termp_fn_post(DECL_ARGS)
1051 {
1052
1053 if (node->sec == SEC_SYNOPSIS && node->next)
1054 vspace(p);
1055
1056 }
1057
1058
1059 /* ARGSUSED */
1060 static int
1061 termp_sx_pre(DECL_ARGS)
1062 {
1063
1064 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK]);
1065 return(1);
1066 }
1067
1068
1069 /* ARGSUSED */
1070 static int
1071 termp_fa_pre(DECL_ARGS)
1072 {
1073 struct mdoc_node *n;
1074
1075 if (node->parent->tok != MDOC_Fo) {
1076 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FUNC_ARG]);
1077 return(1);
1078 }
1079
1080 for (n = node->child; n; n = n->next) {
1081 assert(MDOC_TEXT == n->type);
1082
1083 p->flags |= ttypes[TTYPE_FUNC_ARG];
1084 word(p, n->data.text.string);
1085 p->flags &= ~ttypes[TTYPE_FUNC_ARG];
1086
1087 if (n->next)
1088 word(p, ",");
1089 }
1090
1091 if (node->next && node->next->tok == MDOC_Fa)
1092 word(p, ",");
1093
1094 return(0);
1095 }
1096
1097
1098 /* ARGSUSED */
1099 static int
1100 termp_va_pre(DECL_ARGS)
1101 {
1102
1103 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_VAR_DECL]);
1104 return(1);
1105 }
1106
1107
1108 /* ARGSUSED */
1109 static int
1110 termp_bd_pre(DECL_ARGS)
1111 {
1112 const struct mdoc_block *bl;
1113 const struct mdoc_node *n;
1114 int i, type;
1115
1116 if (MDOC_BLOCK == node->type) {
1117 if (node->prev)
1118 vspace(p);
1119 return(1);
1120 } else if (MDOC_BODY != node->type)
1121 return(1);
1122
1123 pair->offset = p->offset;
1124 bl = &node->parent->data.block;
1125
1126 for (type = -1, i = 0; i < (int)bl->argc; i++) {
1127 switch (bl->argv[i].arg) {
1128 case (MDOC_Ragged):
1129 /* FALLTHROUGH */
1130 case (MDOC_Filled):
1131 /* FALLTHROUGH */
1132 case (MDOC_Unfilled):
1133 /* FALLTHROUGH */
1134 case (MDOC_Literal):
1135 type = bl->argv[i].arg;
1136 i = (int)bl->argc;
1137 break;
1138 default:
1139 errx(1, "display type not supported");
1140 }
1141 }
1142
1143 assert(-1 != type);
1144
1145 i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
1146 if (-1 != i) {
1147 assert(1 == bl->argv[i].sz);
1148 p->offset += arg_offset(&bl->argv[i]);
1149 }
1150
1151
1152 switch (type) {
1153 case (MDOC_Literal):
1154 /* FALLTHROUGH */
1155 case (MDOC_Unfilled):
1156 break;
1157 default:
1158 return(1);
1159 }
1160
1161 p->flags |= TERMP_LITERAL;
1162
1163 for (n = node->child; n; n = n->next) {
1164 if (MDOC_TEXT != n->type) {
1165 warnx("non-text children not yet allowed");
1166 continue;
1167 }
1168 word(p, n->data.text.string);
1169 flushln(p);
1170 }
1171
1172 return(0);
1173 }
1174
1175
1176 /* ARGSUSED */
1177 static void
1178 termp_bd_post(DECL_ARGS)
1179 {
1180
1181 if (MDOC_BODY != node->type)
1182 return;
1183
1184 if ( ! (p->flags & TERMP_LITERAL))
1185 flushln(p);
1186
1187 p->flags &= ~TERMP_LITERAL;
1188 p->offset = pair->offset;
1189 }
1190
1191
1192 /* ARGSUSED */
1193 static int
1194 termp_qq_pre(DECL_ARGS)
1195 {
1196
1197 if (MDOC_BODY != node->type)
1198 return(1);
1199 word(p, "\"");
1200 p->flags |= TERMP_NOSPACE;
1201 return(1);
1202 }
1203
1204
1205 /* ARGSUSED */
1206 static void
1207 termp_qq_post(DECL_ARGS)
1208 {
1209
1210 if (MDOC_BODY != node->type)
1211 return;
1212 p->flags |= TERMP_NOSPACE;
1213 word(p, "\"");
1214 }
1215
1216
1217 /* ARGSUSED */
1218 static int
1219 termp_bsx_pre(DECL_ARGS)
1220 {
1221
1222 word(p, "BSDI BSD/OS");
1223 return(1);
1224 }
1225
1226
1227 /* ARGSUSED */
1228 static void
1229 termp_bx_post(DECL_ARGS)
1230 {
1231
1232 if (node->child)
1233 p->flags |= TERMP_NOSPACE;
1234 word(p, "BSD");
1235 }
1236
1237
1238 /* ARGSUSED */
1239 static int
1240 termp_ox_pre(DECL_ARGS)
1241 {
1242
1243 word(p, "OpenBSD");
1244 return(1);
1245 }
1246
1247
1248 /* ARGSUSED */
1249 static int
1250 termp_ux_pre(DECL_ARGS)
1251 {
1252
1253 word(p, "UNIX");
1254 return(1);
1255 }
1256
1257
1258 /* ARGSUSED */
1259 static int
1260 termp_fx_pre(DECL_ARGS)
1261 {
1262
1263 word(p, "FreeBSD");
1264 return(1);
1265 }
1266
1267
1268 /* ARGSUSED */
1269 static int
1270 termp_nx_pre(DECL_ARGS)
1271 {
1272
1273 word(p, "NetBSD");
1274 return(1);
1275 }
1276
1277
1278 /* ARGSUSED */
1279 static int
1280 termp_sq_pre(DECL_ARGS)
1281 {
1282
1283 if (MDOC_BODY != node->type)
1284 return(1);
1285 word(p, "`");
1286 p->flags |= TERMP_NOSPACE;
1287 return(1);
1288 }
1289
1290
1291 /* ARGSUSED */
1292 static void
1293 termp_sq_post(DECL_ARGS)
1294 {
1295
1296 if (MDOC_BODY != node->type)
1297 return;
1298 p->flags |= TERMP_NOSPACE;
1299 word(p, "\'");
1300 }
1301
1302
1303 /* ARGSUSED */
1304 static int
1305 termp_pf_pre(DECL_ARGS)
1306 {
1307
1308 p->flags |= TERMP_IGNDELIM;
1309 return(1);
1310 }
1311
1312
1313 /* ARGSUSED */
1314 static void
1315 termp_pf_post(DECL_ARGS)
1316 {
1317
1318 p->flags &= ~TERMP_IGNDELIM;
1319 p->flags |= TERMP_NOSPACE;
1320 }
1321
1322
1323 /* ARGSUSED */
1324 static int
1325 termp_ss_pre(DECL_ARGS)
1326 {
1327
1328 switch (node->type) {
1329 case (MDOC_HEAD):
1330 vspace(p);
1331 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
1332 p->offset = INDENT / 2;
1333 break;
1334 default:
1335 break;
1336 }
1337
1338 return(1);
1339 }
1340
1341
1342 /* ARGSUSED */
1343 static void
1344 termp_ss_post(DECL_ARGS)
1345 {
1346
1347 switch (node->type) {
1348 case (MDOC_HEAD):
1349 newln(p);
1350 p->offset = INDENT;
1351 break;
1352 default:
1353 break;
1354 }
1355 }
1356
1357
1358 /* ARGSUSED */
1359 static int
1360 termp_pa_pre(DECL_ARGS)
1361 {
1362
1363 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_FILE]);
1364 return(1);
1365 }
1366
1367
1368 /* ARGSUSED */
1369 static int
1370 termp_em_pre(DECL_ARGS)
1371 {
1372
1373 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1374 return(1);
1375 }
1376
1377
1378 /* ARGSUSED */
1379 static int
1380 termp_cd_pre(DECL_ARGS)
1381 {
1382
1383 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CONFIG]);
1384 newln(p);
1385 return(1);
1386 }
1387
1388
1389 /* ARGSUSED */
1390 static int
1391 termp_cm_pre(DECL_ARGS)
1392 {
1393
1394 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD_FLAG]);
1395 return(1);
1396 }
1397
1398
1399 /* ARGSUSED */
1400 static int
1401 termp_ic_pre(DECL_ARGS)
1402 {
1403
1404 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_CMD]);
1405 return(1);
1406 }
1407
1408
1409 /* ARGSUSED */
1410 static int
1411 termp_in_pre(DECL_ARGS)
1412 {
1413
1414 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_INCLUDE]);
1415 word(p, "#include");
1416 word(p, "<");
1417 p->flags |= TERMP_NOSPACE;
1418 return(1);
1419 }
1420
1421
1422 /* ARGSUSED */
1423 static void
1424 termp_in_post(DECL_ARGS)
1425 {
1426
1427 p->flags |= TERMP_NOSPACE;
1428 word(p, ">");
1429
1430 newln(p);
1431 if (SEC_SYNOPSIS != node->sec)
1432 return;
1433 if (node->next && MDOC_In != node->next->tok)
1434 vspace(p);
1435 }
1436
1437
1438 /* ARGSUSED */
1439 static int
1440 termp_at_pre(DECL_ARGS)
1441 {
1442 enum mdoc_att c;
1443
1444 c = ATT_DEFAULT;
1445 if (node->child) {
1446 assert(MDOC_TEXT == node->child->type);
1447 c = mdoc_atoatt(node->child->data.text.string);
1448 }
1449
1450 word(p, mdoc_att2a(c));
1451 return(0);
1452 }
1453
1454
1455 /* ARGSUSED */
1456 static int
1457 termp_bq_pre(DECL_ARGS)
1458 {
1459
1460 if (MDOC_BODY != node->type)
1461 return(1);
1462 word(p, "[");
1463 p->flags |= TERMP_NOSPACE;
1464 return(1);
1465 }
1466
1467
1468 /* ARGSUSED */
1469 static void
1470 termp_bq_post(DECL_ARGS)
1471 {
1472
1473 if (MDOC_BODY != node->type)
1474 return;
1475 word(p, "]");
1476 }
1477
1478
1479 /* ARGSUSED */
1480 static int
1481 termp_pq_pre(DECL_ARGS)
1482 {
1483
1484 if (MDOC_BODY != node->type)
1485 return(1);
1486 word(p, "\\&(");
1487 p->flags |= TERMP_NOSPACE;
1488 return(1);
1489 }
1490
1491
1492 /* ARGSUSED */
1493 static void
1494 termp_pq_post(DECL_ARGS)
1495 {
1496
1497 if (MDOC_BODY != node->type)
1498 return;
1499 word(p, ")");
1500 }
1501
1502
1503 /* ARGSUSED */
1504 static int
1505 termp_fo_pre(DECL_ARGS)
1506 {
1507 const struct mdoc_node *n;
1508
1509 if (MDOC_BODY == node->type) {
1510 word(p, "(");
1511 p->flags |= TERMP_NOSPACE;
1512 return(1);
1513 } else if (MDOC_HEAD != node->type)
1514 return(1);
1515
1516 /* XXX - groff shows only first parameter */
1517
1518 p->flags |= ttypes[TTYPE_FUNC_NAME];
1519 for (n = node->child; n; n = n->next) {
1520 assert(MDOC_TEXT == n->type);
1521 word(p, n->data.text.string);
1522 }
1523 p->flags &= ~ttypes[TTYPE_FUNC_NAME];
1524
1525 return(0);
1526 }
1527
1528
1529 /* ARGSUSED */
1530 static void
1531 termp_fo_post(DECL_ARGS)
1532 {
1533
1534 if (MDOC_BODY != node->type)
1535 return;
1536 word(p, ")");
1537 word(p, ";");
1538 newln(p);
1539 }
1540
1541
1542 /* ARGSUSED */
1543 static int
1544 termp_bf_pre(DECL_ARGS)
1545 {
1546 const struct mdoc_node *n;
1547 const struct mdoc_block *b;
1548
1549 /* XXX - we skip over possible trailing HEAD tokens. */
1550
1551 if (MDOC_HEAD == node->type)
1552 return(0);
1553 else if (MDOC_BLOCK != node->type)
1554 return(1);
1555
1556 b = &node->data.block;
1557
1558 if (NULL == (n = b->head->child)) {
1559 if (arg_hasattr(MDOC_Emphasis, b->argc, b->argv))
1560 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1561 else if (arg_hasattr(MDOC_Symbolic, b->argc, b->argv))
1562 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1563
1564 return(1);
1565 }
1566
1567 assert(MDOC_TEXT == n->type);
1568
1569 if (0 == strcmp("Em", n->data.text.string))
1570 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1571 else if (0 == strcmp("Sy", n->data.text.string))
1572 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
1573
1574 return(1);
1575 }
1576
1577
1578 /* ARGSUSED */
1579 static int
1580 termp_sy_pre(DECL_ARGS)
1581 {
1582
1583 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMB]);
1584 return(1);
1585 }
1586
1587
1588 /* ARGSUSED */
1589 static int
1590 termp_ms_pre(DECL_ARGS)
1591 {
1592
1593 TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SYMBOL]);
1594 return(1);
1595 }
1596
1597
1598
1599 /* ARGSUSED */
1600 static int
1601 termp_sm_pre(DECL_ARGS)
1602 {
1603
1604 #if notyet
1605 assert(node->child);
1606 if (0 == strcmp("off", node->child->data.text.string)) {
1607 p->flags &= ~TERMP_NONOSPACE;
1608 p->flags &= ~TERMP_NOSPACE;
1609 } else {
1610 p->flags |= TERMP_NONOSPACE;
1611 p->flags |= TERMP_NOSPACE;
1612 }
1613 #endif
1614
1615 return(0);
1616 }
1617
1618
1619 /* ARGSUSED */
1620 static int
1621 termp__t_pre(DECL_ARGS)
1622 {
1623
1624 word(p, "\"");
1625 p->flags |= TERMP_NOSPACE;
1626 return(1);
1627 }
1628
1629
1630 /* ARGSUSED */
1631 static void
1632 termp__t_post(DECL_ARGS)
1633 {
1634
1635 p->flags |= TERMP_NOSPACE;
1636 word(p, "\"");
1637 word(p, node->next ? "," : ".");
1638 }
1639
1640
1641 /* ARGSUSED */
1642 static void
1643 termp____post(DECL_ARGS)
1644 {
1645
1646 p->flags |= TERMP_NOSPACE;
1647 word(p, node->next ? "," : ".");
1648 }