Merge branch 'vendor/BINUTILS220'
[dragonfly.git] / contrib / mdocml / man_term.c
1 /*      $Id: man_term.c,v 1.105 2011/03/22 10:13:01 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <sys/types.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "mandoc.h"
31 #include "out.h"
32 #include "man.h"
33 #include "term.h"
34 #include "main.h"
35
36 #define INDENT            7
37 #define HALFINDENT        3
38
39 /* FIXME: have PD set the default vspace width. */
40
41 struct  mtermp {
42         int               fl;
43 #define MANT_LITERAL     (1 << 0)
44         /* 
45          * Default amount to indent the left margin after leading text
46          * has been printed (e.g., `HP' left-indent, `TP' and `IP' body
47          * indent).  This needs to be saved because `HP' and so on, if
48          * not having a specified value, must default.
49          *
50          * Note that this is the indentation AFTER the left offset, so
51          * the total offset is usually offset + lmargin.
52          */
53         size_t            lmargin;
54         /*
55          * The default offset, i.e., the amount between any text and the
56          * page boundary.
57          */
58         size_t            offset;
59 };
60
61 #define DECL_ARGS         struct termp *p, \
62                           struct mtermp *mt, \
63                           const struct man_node *n, \
64                           const struct man_meta *m
65
66 struct  termact {
67         int             (*pre)(DECL_ARGS);
68         void            (*post)(DECL_ARGS);
69         int               flags;
70 #define MAN_NOTEXT       (1 << 0) /* Never has text children. */
71 };
72
73 static  int               a2width(const struct termp *, const char *);
74 static  size_t            a2height(const struct termp *, const char *);
75
76 static  void              print_man_nodelist(DECL_ARGS);
77 static  void              print_man_node(DECL_ARGS);
78 static  void              print_man_head(struct termp *, const void *);
79 static  void              print_man_foot(struct termp *, const void *);
80 static  void              print_bvspace(struct termp *, 
81                                 const struct man_node *);
82
83 static  int               pre_alternate(DECL_ARGS);
84 static  int               pre_B(DECL_ARGS);
85 static  int               pre_HP(DECL_ARGS);
86 static  int               pre_I(DECL_ARGS);
87 static  int               pre_IP(DECL_ARGS);
88 static  int               pre_PP(DECL_ARGS);
89 static  int               pre_RS(DECL_ARGS);
90 static  int               pre_SH(DECL_ARGS);
91 static  int               pre_SS(DECL_ARGS);
92 static  int               pre_TP(DECL_ARGS);
93 static  int               pre_ign(DECL_ARGS);
94 static  int               pre_in(DECL_ARGS);
95 static  int               pre_literal(DECL_ARGS);
96 static  int               pre_sp(DECL_ARGS);
97 static  int               pre_ft(DECL_ARGS);
98
99 static  void              post_IP(DECL_ARGS);
100 static  void              post_HP(DECL_ARGS);
101 static  void              post_RS(DECL_ARGS);
102 static  void              post_SH(DECL_ARGS);
103 static  void              post_SS(DECL_ARGS);
104 static  void              post_TP(DECL_ARGS);
105
106 static  const struct termact termacts[MAN_MAX] = {
107         { pre_sp, NULL, MAN_NOTEXT }, /* br */
108         { NULL, NULL, 0 }, /* TH */
109         { pre_SH, post_SH, 0 }, /* SH */
110         { pre_SS, post_SS, 0 }, /* SS */
111         { pre_TP, post_TP, 0 }, /* TP */
112         { pre_PP, NULL, 0 }, /* LP */
113         { pre_PP, NULL, 0 }, /* PP */
114         { pre_PP, NULL, 0 }, /* P */
115         { pre_IP, post_IP, 0 }, /* IP */
116         { pre_HP, post_HP, 0 }, /* HP */ 
117         { NULL, NULL, 0 }, /* SM */
118         { pre_B, NULL, 0 }, /* SB */
119         { pre_alternate, NULL, 0 }, /* BI */
120         { pre_alternate, NULL, 0 }, /* IB */
121         { pre_alternate, NULL, 0 }, /* BR */
122         { pre_alternate, NULL, 0 }, /* RB */
123         { NULL, NULL, 0 }, /* R */
124         { pre_B, NULL, 0 }, /* B */
125         { pre_I, NULL, 0 }, /* I */
126         { pre_alternate, NULL, 0 }, /* IR */
127         { pre_alternate, NULL, 0 }, /* RI */
128         { pre_ign, NULL, MAN_NOTEXT }, /* na */
129         { pre_sp, NULL, MAN_NOTEXT }, /* sp */
130         { pre_literal, NULL, 0 }, /* nf */
131         { pre_literal, NULL, 0 }, /* fi */
132         { NULL, NULL, 0 }, /* RE */
133         { pre_RS, post_RS, 0 }, /* RS */
134         { pre_ign, NULL, 0 }, /* DT */
135         { pre_ign, NULL, 0 }, /* UC */
136         { pre_ign, NULL, 0 }, /* PD */
137         { pre_ign, NULL, 0 }, /* AT */
138         { pre_in, NULL, MAN_NOTEXT }, /* in */
139         { pre_ft, NULL, MAN_NOTEXT }, /* ft */
140 };
141
142
143
144 void
145 terminal_man(void *arg, const struct man *man)
146 {
147         struct termp            *p;
148         const struct man_node   *n;
149         const struct man_meta   *m;
150         struct mtermp            mt;
151
152         p = (struct termp *)arg;
153
154         p->overstep = 0;
155         p->maxrmargin = p->defrmargin;
156         p->tabwidth = term_len(p, 5);
157
158         if (NULL == p->symtab)
159                 switch (p->enc) {
160                 case (TERMENC_ASCII):
161                         p->symtab = chars_init(CHARS_ASCII);
162                         break;
163                 default:
164                         abort();
165                         /* NOTREACHED */
166                 }
167
168         n = man_node(man);
169         m = man_meta(man);
170
171         term_begin(p, print_man_head, print_man_foot, m);
172         p->flags |= TERMP_NOSPACE;
173
174         mt.fl = 0;
175         mt.lmargin = term_len(p, INDENT);
176         mt.offset = term_len(p, INDENT);
177
178         if (n->child)
179                 print_man_nodelist(p, &mt, n->child, m);
180
181         term_end(p);
182 }
183
184
185 static size_t
186 a2height(const struct termp *p, const char *cp)
187 {
188         struct roffsu    su;
189
190         if ( ! a2roffsu(cp, &su, SCALE_VS))
191                 SCALE_VS_INIT(&su, term_strlen(p, cp));
192
193         return(term_vspan(p, &su));
194 }
195
196
197 static int
198 a2width(const struct termp *p, const char *cp)
199 {
200         struct roffsu    su;
201
202         if ( ! a2roffsu(cp, &su, SCALE_BU))
203                 return(-1);
204
205         return((int)term_hspan(p, &su));
206 }
207
208
209 static void
210 print_bvspace(struct termp *p, const struct man_node *n)
211 {
212         term_newln(p);
213
214         if (n->body && n->body->child && MAN_TBL == n->body->child->type)
215                 return;
216
217         if (NULL == n->prev)
218                 return;
219
220         if (MAN_SS == n->prev->tok)
221                 return;
222         if (MAN_SH == n->prev->tok)
223                 return;
224
225         term_vspace(p);
226 }
227
228
229 /* ARGSUSED */
230 static int
231 pre_ign(DECL_ARGS)
232 {
233
234         return(0);
235 }
236
237
238 /* ARGSUSED */
239 static int
240 pre_I(DECL_ARGS)
241 {
242
243         term_fontrepl(p, TERMFONT_UNDER);
244         return(1);
245 }
246
247
248 /* ARGSUSED */
249 static int
250 pre_literal(DECL_ARGS)
251 {
252
253         term_newln(p);
254
255         if (MAN_nf == n->tok)
256                 mt->fl |= MANT_LITERAL;
257         else
258                 mt->fl &= ~MANT_LITERAL;
259
260         return(0);
261 }
262
263 /* ARGSUSED */
264 static int
265 pre_alternate(DECL_ARGS)
266 {
267         enum termfont            font[2];
268         const struct man_node   *nn;
269         int                      savelit, i;
270
271         switch (n->tok) {
272         case (MAN_RB):
273                 font[0] = TERMFONT_NONE;
274                 font[1] = TERMFONT_BOLD;
275                 break;
276         case (MAN_RI):
277                 font[0] = TERMFONT_NONE;
278                 font[1] = TERMFONT_UNDER;
279                 break;
280         case (MAN_BR):
281                 font[0] = TERMFONT_BOLD;
282                 font[1] = TERMFONT_NONE;
283                 break;
284         case (MAN_BI):
285                 font[0] = TERMFONT_BOLD;
286                 font[1] = TERMFONT_UNDER;
287                 break;
288         case (MAN_IR):
289                 font[0] = TERMFONT_UNDER;
290                 font[1] = TERMFONT_NONE;
291                 break;
292         case (MAN_IB):
293                 font[0] = TERMFONT_UNDER;
294                 font[1] = TERMFONT_BOLD;
295                 break;
296         default:
297                 abort();
298         }
299
300         savelit = MANT_LITERAL & mt->fl;
301         mt->fl &= ~MANT_LITERAL;
302
303         for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
304                 term_fontrepl(p, font[i]);
305                 if (savelit && NULL == nn->next)
306                         mt->fl |= MANT_LITERAL;
307                 print_man_node(p, mt, nn, m);
308                 if (nn->next)
309                         p->flags |= TERMP_NOSPACE;
310         }
311
312         return(0);
313 }
314
315 /* ARGSUSED */
316 static int
317 pre_B(DECL_ARGS)
318 {
319
320         term_fontrepl(p, TERMFONT_BOLD);
321         return(1);
322 }
323
324 /* ARGSUSED */
325 static int
326 pre_ft(DECL_ARGS)
327 {
328         const char      *cp;
329
330         if (NULL == n->child) {
331                 term_fontlast(p);
332                 return(0);
333         }
334
335         cp = n->child->string;
336         switch (*cp) {
337         case ('4'):
338                 /* FALLTHROUGH */
339         case ('3'):
340                 /* FALLTHROUGH */
341         case ('B'):
342                 term_fontrepl(p, TERMFONT_BOLD);
343                 break;
344         case ('2'):
345                 /* FALLTHROUGH */
346         case ('I'):
347                 term_fontrepl(p, TERMFONT_UNDER);
348                 break;
349         case ('P'):
350                 term_fontlast(p);
351                 break;
352         case ('1'):
353                 /* FALLTHROUGH */
354         case ('C'):
355                 /* FALLTHROUGH */
356         case ('R'):
357                 term_fontrepl(p, TERMFONT_NONE);
358                 break;
359         default:
360                 break;
361         }
362         return(0);
363 }
364
365 /* ARGSUSED */
366 static int
367 pre_in(DECL_ARGS)
368 {
369         int              len, less;
370         size_t           v;
371         const char      *cp;
372
373         term_newln(p);
374
375         if (NULL == n->child) {
376                 p->offset = mt->offset;
377                 return(0);
378         }
379
380         cp = n->child->string;
381         less = 0;
382
383         if ('-' == *cp)
384                 less = -1;
385         else if ('+' == *cp)
386                 less = 1;
387         else
388                 cp--;
389
390         if ((len = a2width(p, ++cp)) < 0)
391                 return(0);
392
393         v = (size_t)len;
394
395         if (less < 0)
396                 p->offset -= p->offset > v ? v : p->offset;
397         else if (less > 0)
398                 p->offset += v;
399         else 
400                 p->offset = v;
401
402         /* Don't let this creep beyond the right margin. */
403
404         if (p->offset > p->rmargin)
405                 p->offset = p->rmargin;
406
407         return(0);
408 }
409
410
411 /* ARGSUSED */
412 static int
413 pre_sp(DECL_ARGS)
414 {
415         size_t           i, len;
416
417         switch (n->tok) {
418         case (MAN_br):
419                 len = 0;
420                 break;
421         default:
422                 len = n->child ? a2height(p, n->child->string) : 1;
423                 break;
424         }
425
426         if (0 == len)
427                 term_newln(p);
428         for (i = 0; i < len; i++)
429                 term_vspace(p);
430
431         return(0);
432 }
433
434
435 /* ARGSUSED */
436 static int
437 pre_HP(DECL_ARGS)
438 {
439         size_t                   len;
440         int                      ival;
441         const struct man_node   *nn;
442
443         switch (n->type) {
444         case (MAN_BLOCK):
445                 print_bvspace(p, n);
446                 return(1);
447         case (MAN_BODY):
448                 p->flags |= TERMP_NOBREAK;
449                 p->flags |= TERMP_TWOSPACE;
450                 break;
451         default:
452                 return(0);
453         }
454
455         len = mt->lmargin;
456         ival = -1;
457
458         /* Calculate offset. */
459
460         if (NULL != (nn = n->parent->head->child))
461                 if ((ival = a2width(p, nn->string)) >= 0)
462                         len = (size_t)ival;
463
464         if (0 == len)
465                 len = term_len(p, 1);
466
467         p->offset = mt->offset;
468         p->rmargin = mt->offset + len;
469
470         if (ival >= 0)
471                 mt->lmargin = (size_t)ival;
472
473         return(1);
474 }
475
476
477 /* ARGSUSED */
478 static void
479 post_HP(DECL_ARGS)
480 {
481
482         switch (n->type) {
483         case (MAN_BLOCK):
484                 term_flushln(p);
485                 break;
486         case (MAN_BODY):
487                 term_flushln(p);
488                 p->flags &= ~TERMP_NOBREAK;
489                 p->flags &= ~TERMP_TWOSPACE;
490                 p->offset = mt->offset;
491                 p->rmargin = p->maxrmargin;
492                 break;
493         default:
494                 break;
495         }
496 }
497
498
499 /* ARGSUSED */
500 static int
501 pre_PP(DECL_ARGS)
502 {
503
504         switch (n->type) {
505         case (MAN_BLOCK):
506                 mt->lmargin = term_len(p, INDENT);
507                 print_bvspace(p, n);
508                 break;
509         default:
510                 p->offset = mt->offset;
511                 break;
512         }
513
514         return(MAN_HEAD != n->type);
515 }
516
517
518 /* ARGSUSED */
519 static int
520 pre_IP(DECL_ARGS)
521 {
522         const struct man_node   *nn;
523         size_t                   len;
524         int                      savelit, ival;
525
526         switch (n->type) {
527         case (MAN_BODY):
528                 p->flags |= TERMP_NOLPAD;
529                 p->flags |= TERMP_NOSPACE;
530                 break;
531         case (MAN_HEAD):
532                 p->flags |= TERMP_NOBREAK;
533                 break;
534         case (MAN_BLOCK):
535                 print_bvspace(p, n);
536                 /* FALLTHROUGH */
537         default:
538                 return(1);
539         }
540
541         len = mt->lmargin;
542         ival = -1;
543
544         /* Calculate the offset from the optional second argument. */
545         if (NULL != (nn = n->parent->head->child))
546                 if (NULL != (nn = nn->next))
547                         if ((ival = a2width(p, nn->string)) >= 0)
548                                 len = (size_t)ival;
549
550         switch (n->type) {
551         case (MAN_HEAD):
552                 /* Handle zero-width lengths. */
553                 if (0 == len)
554                         len = term_len(p, 1);
555
556                 p->offset = mt->offset;
557                 p->rmargin = mt->offset + len;
558                 if (ival < 0)
559                         break;
560
561                 /* Set the saved left-margin. */
562                 mt->lmargin = (size_t)ival;
563
564                 savelit = MANT_LITERAL & mt->fl;
565                 mt->fl &= ~MANT_LITERAL;
566
567                 if (n->child)
568                         print_man_node(p, mt, n->child, m);
569
570                 if (savelit)
571                         mt->fl |= MANT_LITERAL;
572
573                 return(0);
574         case (MAN_BODY):
575                 p->offset = mt->offset + len;
576                 p->rmargin = p->maxrmargin;
577                 break;
578         default:
579                 break;
580         }
581
582         return(1);
583 }
584
585
586 /* ARGSUSED */
587 static void
588 post_IP(DECL_ARGS)
589 {
590
591         switch (n->type) {
592         case (MAN_HEAD):
593                 term_flushln(p);
594                 p->flags &= ~TERMP_NOBREAK;
595                 p->rmargin = p->maxrmargin;
596                 break;
597         case (MAN_BODY):
598                 term_newln(p);
599                 p->flags &= ~TERMP_NOLPAD;
600                 break;
601         default:
602                 break;
603         }
604 }
605
606
607 /* ARGSUSED */
608 static int
609 pre_TP(DECL_ARGS)
610 {
611         const struct man_node   *nn;
612         size_t                   len;
613         int                      savelit, ival;
614
615         switch (n->type) {
616         case (MAN_HEAD):
617                 p->flags |= TERMP_NOBREAK;
618                 break;
619         case (MAN_BODY):
620                 p->flags |= TERMP_NOLPAD;
621                 p->flags |= TERMP_NOSPACE;
622                 break;
623         case (MAN_BLOCK):
624                 print_bvspace(p, n);
625                 /* FALLTHROUGH */
626         default:
627                 return(1);
628         }
629
630         len = (size_t)mt->lmargin;
631         ival = -1;
632
633         /* Calculate offset. */
634
635         if (NULL != (nn = n->parent->head->child)) {
636                 while (nn && MAN_TEXT != nn->type)
637                         nn = nn->next;
638                 if (nn && nn->next)
639                         if ((ival = a2width(p, nn->string)) >= 0)
640                                 len = (size_t)ival;
641         }
642
643         switch (n->type) {
644         case (MAN_HEAD):
645                 /* Handle zero-length properly. */
646                 if (0 == len)
647                         len = term_len(p, 1);
648
649                 p->offset = mt->offset;
650                 p->rmargin = mt->offset + len;
651
652                 savelit = MANT_LITERAL & mt->fl;
653                 mt->fl &= ~MANT_LITERAL;
654
655                 /* Don't print same-line elements. */
656                 for (nn = n->child; nn; nn = nn->next)
657                         if (nn->line > n->line)
658                                 print_man_node(p, mt, nn, m);
659
660                 if (savelit)
661                         mt->fl |= MANT_LITERAL;
662
663                 if (ival >= 0)
664                         mt->lmargin = (size_t)ival;
665
666                 return(0);
667         case (MAN_BODY):
668                 p->offset = mt->offset + len;
669                 p->rmargin = p->maxrmargin;
670                 break;
671         default:
672                 break;
673         }
674
675         return(1);
676 }
677
678
679 /* ARGSUSED */
680 static void
681 post_TP(DECL_ARGS)
682 {
683
684         switch (n->type) {
685         case (MAN_HEAD):
686                 term_flushln(p);
687                 p->flags &= ~TERMP_NOBREAK;
688                 p->flags &= ~TERMP_TWOSPACE;
689                 p->rmargin = p->maxrmargin;
690                 break;
691         case (MAN_BODY):
692                 term_newln(p);
693                 p->flags &= ~TERMP_NOLPAD;
694                 break;
695         default:
696                 break;
697         }
698 }
699
700
701 /* ARGSUSED */
702 static int
703 pre_SS(DECL_ARGS)
704 {
705
706         switch (n->type) {
707         case (MAN_BLOCK):
708                 mt->lmargin = term_len(p, INDENT);
709                 mt->offset = term_len(p, INDENT);
710                 /* If following a prior empty `SS', no vspace. */
711                 if (n->prev && MAN_SS == n->prev->tok)
712                         if (NULL == n->prev->body->child)
713                                 break;
714                 if (NULL == n->prev)
715                         break;
716                 term_vspace(p);
717                 break;
718         case (MAN_HEAD):
719                 term_fontrepl(p, TERMFONT_BOLD);
720                 p->offset = term_len(p, HALFINDENT);
721                 break;
722         case (MAN_BODY):
723                 p->offset = mt->offset;
724                 break;
725         default:
726                 break;
727         }
728
729         return(1);
730 }
731
732
733 /* ARGSUSED */
734 static void
735 post_SS(DECL_ARGS)
736 {
737         
738         switch (n->type) {
739         case (MAN_HEAD):
740                 term_newln(p);
741                 break;
742         case (MAN_BODY):
743                 term_newln(p);
744                 break;
745         default:
746                 break;
747         }
748 }
749
750
751 /* ARGSUSED */
752 static int
753 pre_SH(DECL_ARGS)
754 {
755
756         switch (n->type) {
757         case (MAN_BLOCK):
758                 mt->lmargin = term_len(p, INDENT);
759                 mt->offset = term_len(p, INDENT);
760                 /* If following a prior empty `SH', no vspace. */
761                 if (n->prev && MAN_SH == n->prev->tok)
762                         if (NULL == n->prev->body->child)
763                                 break;
764                 /* If the first macro, no vspae. */
765                 if (NULL == n->prev)
766                         break;
767                 term_vspace(p);
768                 break;
769         case (MAN_HEAD):
770                 term_fontrepl(p, TERMFONT_BOLD);
771                 p->offset = 0;
772                 break;
773         case (MAN_BODY):
774                 p->offset = mt->offset;
775                 break;
776         default:
777                 break;
778         }
779
780         return(1);
781 }
782
783
784 /* ARGSUSED */
785 static void
786 post_SH(DECL_ARGS)
787 {
788         
789         switch (n->type) {
790         case (MAN_HEAD):
791                 term_newln(p);
792                 break;
793         case (MAN_BODY):
794                 term_newln(p);
795                 break;
796         default:
797                 break;
798         }
799 }
800
801
802 /* ARGSUSED */
803 static int
804 pre_RS(DECL_ARGS)
805 {
806         const struct man_node   *nn;
807         int                      ival;
808
809         switch (n->type) {
810         case (MAN_BLOCK):
811                 term_newln(p);
812                 return(1);
813         case (MAN_HEAD):
814                 return(0);
815         default:
816                 break;
817         }
818
819         if (NULL == (nn = n->parent->head->child)) {
820                 mt->offset = mt->lmargin + term_len(p, INDENT);
821                 p->offset = mt->offset;
822                 return(1);
823         }
824
825         if ((ival = a2width(p, nn->string)) < 0)
826                 return(1);
827
828         mt->offset = term_len(p, INDENT) + (size_t)ival;
829         p->offset = mt->offset;
830
831         return(1);
832 }
833
834
835 /* ARGSUSED */
836 static void
837 post_RS(DECL_ARGS)
838 {
839
840         switch (n->type) {
841         case (MAN_BLOCK):
842                 mt->offset = mt->lmargin = term_len(p, INDENT);
843                 break;
844         case (MAN_HEAD):
845                 break;
846         default:
847                 term_newln(p);
848                 p->offset = term_len(p, INDENT);
849                 break;
850         }
851 }
852
853
854 static void
855 print_man_node(DECL_ARGS)
856 {
857         size_t           rm, rmax;
858         int              c;
859
860         switch (n->type) {
861         case(MAN_TEXT):
862                 /*
863                  * If we have a blank line, output a vertical space.
864                  * If we have a space as the first character, break
865                  * before printing the line's data.
866                  */
867                 if ('\0' == *n->string) {
868                         term_vspace(p);
869                         return;
870                 } else if (' ' == *n->string && MAN_LINE & n->flags)
871                         term_newln(p);
872
873                 term_word(p, n->string);
874
875                 /*
876                  * If we're in a literal context, make sure that words
877                  * togehter on the same line stay together.  This is a
878                  * POST-printing call, so we check the NEXT word.  Since
879                  * -man doesn't have nested macros, we don't need to be
880                  * more specific than this.
881                  */
882                 if (MANT_LITERAL & mt->fl && 
883                                 (NULL == n->next || 
884                                  n->next->line > n->line)) {
885                         rm = p->rmargin;
886                         rmax = p->maxrmargin;
887                         p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
888                         p->flags |= TERMP_NOSPACE;
889                         term_flushln(p);
890                         p->flags &= ~TERMP_NOLPAD;
891                         p->rmargin = rm;
892                         p->maxrmargin = rmax;
893                 }
894
895                 if (MAN_EOS & n->flags)
896                         p->flags |= TERMP_SENTENCE;
897                 return;
898         case (MAN_EQN):
899                 term_word(p, n->eqn->data);
900                 return;
901         case (MAN_TBL):
902                 /*
903                  * Tables are preceded by a newline.  Then process a
904                  * table line, which will cause line termination,
905                  */
906                 if (TBL_SPAN_FIRST & n->span->flags) 
907                         term_newln(p);
908                 term_tbl(p, n->span);
909                 return;
910         default:
911                 break;
912         }
913
914         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
915                 term_fontrepl(p, TERMFONT_NONE);
916
917         c = 1;
918         if (termacts[n->tok].pre)
919                 c = (*termacts[n->tok].pre)(p, mt, n, m);
920
921         if (c && n->child)
922                 print_man_nodelist(p, mt, n->child, m);
923
924         if (termacts[n->tok].post)
925                 (*termacts[n->tok].post)(p, mt, n, m);
926         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
927                 term_fontrepl(p, TERMFONT_NONE);
928
929         if (MAN_EOS & n->flags)
930                 p->flags |= TERMP_SENTENCE;
931 }
932
933
934 static void
935 print_man_nodelist(DECL_ARGS)
936 {
937
938         print_man_node(p, mt, n, m);
939         if ( ! n->next)
940                 return;
941         print_man_nodelist(p, mt, n->next, m);
942 }
943
944
945 static void
946 print_man_foot(struct termp *p, const void *arg)
947 {
948         const struct man_meta *meta;
949
950         meta = (const struct man_meta *)arg;
951
952         term_fontrepl(p, TERMFONT_NONE);
953
954         term_vspace(p);
955         term_vspace(p);
956         term_vspace(p);
957
958         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
959         p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
960         p->offset = 0;
961
962         /* term_strlen() can return zero. */
963         if (p->rmargin == p->maxrmargin)
964                 p->rmargin--;
965
966         if (meta->source)
967                 term_word(p, meta->source);
968         if (meta->source)
969                 term_word(p, "");
970         term_flushln(p);
971
972         p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
973         p->offset = p->rmargin;
974         p->rmargin = p->maxrmargin;
975         p->flags &= ~TERMP_NOBREAK;
976
977         term_word(p, meta->date);
978         term_flushln(p);
979 }
980
981
982 static void
983 print_man_head(struct termp *p, const void *arg)
984 {
985         char            buf[BUFSIZ], title[BUFSIZ];
986         size_t          buflen, titlen;
987         const struct man_meta *m;
988
989         m = (const struct man_meta *)arg;
990
991         /*
992          * Note that old groff would spit out some spaces before the
993          * header.  We discontinue this strange behaviour, but at one
994          * point we did so here.
995          */
996
997         p->rmargin = p->maxrmargin;
998
999         p->offset = 0;
1000         buf[0] = title[0] = '\0';
1001
1002         if (m->vol)
1003                 strlcpy(buf, m->vol, BUFSIZ);
1004         buflen = term_strlen(p, buf);
1005
1006         snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1007         titlen = term_strlen(p, title);
1008
1009         p->offset = 0;
1010         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1011             (p->maxrmargin - 
1012              term_strlen(p, buf) + term_len(p, 1)) / 2 :
1013             p->maxrmargin - buflen;
1014         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1015
1016         term_word(p, title);
1017         term_flushln(p);
1018
1019         p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1020         p->offset = p->rmargin;
1021         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1022             p->maxrmargin - titlen : p->maxrmargin;
1023
1024         term_word(p, buf);
1025         term_flushln(p);
1026
1027         p->flags &= ~TERMP_NOBREAK;
1028         if (p->rmargin + titlen <= p->maxrmargin) {
1029                 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1030                 p->offset = p->rmargin;
1031                 p->rmargin = p->maxrmargin;
1032                 term_word(p, title);
1033                 term_flushln(p);
1034         }
1035
1036         p->rmargin = p->maxrmargin;
1037         p->offset = 0;
1038         p->flags &= ~TERMP_NOSPACE;
1039
1040         /* 
1041          * Groff likes to have some leading spaces before content.  Well
1042          * that's fine by me.
1043          */
1044
1045         term_vspace(p);
1046         term_vspace(p);
1047         term_vspace(p);
1048 }