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