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