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