kernel - shmget() adjustments
[dragonfly.git] / contrib / mdocml / man_term.c
1 /*      $Id: man_term.c,v 1.109 2011/05/17 14:38:34 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                 p->symtab = mchars_alloc();
160
161         n = man_node(man);
162         m = man_meta(man);
163
164         term_begin(p, print_man_head, print_man_foot, m);
165         p->flags |= TERMP_NOSPACE;
166
167         mt.fl = 0;
168         mt.lmargin = term_len(p, INDENT);
169         mt.offset = term_len(p, INDENT);
170
171         if (n->child)
172                 print_man_nodelist(p, &mt, n->child, m);
173
174         term_end(p);
175 }
176
177
178 static size_t
179 a2height(const struct termp *p, const char *cp)
180 {
181         struct roffsu    su;
182
183         if ( ! a2roffsu(cp, &su, SCALE_VS))
184                 SCALE_VS_INIT(&su, term_strlen(p, cp));
185
186         return(term_vspan(p, &su));
187 }
188
189
190 static int
191 a2width(const struct termp *p, const char *cp)
192 {
193         struct roffsu    su;
194
195         if ( ! a2roffsu(cp, &su, SCALE_BU))
196                 return(-1);
197
198         return((int)term_hspan(p, &su));
199 }
200
201
202 static void
203 print_bvspace(struct termp *p, const struct man_node *n)
204 {
205         term_newln(p);
206
207         if (n->body && n->body->child && MAN_TBL == n->body->child->type)
208                 return;
209
210         if (NULL == n->prev)
211                 return;
212
213         if (MAN_SS == n->prev->tok)
214                 return;
215         if (MAN_SH == n->prev->tok)
216                 return;
217
218         term_vspace(p);
219 }
220
221
222 /* ARGSUSED */
223 static int
224 pre_ign(DECL_ARGS)
225 {
226
227         return(0);
228 }
229
230
231 /* ARGSUSED */
232 static int
233 pre_I(DECL_ARGS)
234 {
235
236         term_fontrepl(p, TERMFONT_UNDER);
237         return(1);
238 }
239
240
241 /* ARGSUSED */
242 static int
243 pre_literal(DECL_ARGS)
244 {
245
246         term_newln(p);
247
248         if (MAN_nf == n->tok)
249                 mt->fl |= MANT_LITERAL;
250         else
251                 mt->fl &= ~MANT_LITERAL;
252
253         return(0);
254 }
255
256 /* ARGSUSED */
257 static int
258 pre_alternate(DECL_ARGS)
259 {
260         enum termfont            font[2];
261         const struct man_node   *nn;
262         int                      savelit, i;
263
264         switch (n->tok) {
265         case (MAN_RB):
266                 font[0] = TERMFONT_NONE;
267                 font[1] = TERMFONT_BOLD;
268                 break;
269         case (MAN_RI):
270                 font[0] = TERMFONT_NONE;
271                 font[1] = TERMFONT_UNDER;
272                 break;
273         case (MAN_BR):
274                 font[0] = TERMFONT_BOLD;
275                 font[1] = TERMFONT_NONE;
276                 break;
277         case (MAN_BI):
278                 font[0] = TERMFONT_BOLD;
279                 font[1] = TERMFONT_UNDER;
280                 break;
281         case (MAN_IR):
282                 font[0] = TERMFONT_UNDER;
283                 font[1] = TERMFONT_NONE;
284                 break;
285         case (MAN_IB):
286                 font[0] = TERMFONT_UNDER;
287                 font[1] = TERMFONT_BOLD;
288                 break;
289         default:
290                 abort();
291         }
292
293         savelit = MANT_LITERAL & mt->fl;
294         mt->fl &= ~MANT_LITERAL;
295
296         for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
297                 term_fontrepl(p, font[i]);
298                 if (savelit && NULL == nn->next)
299                         mt->fl |= MANT_LITERAL;
300                 print_man_node(p, mt, nn, m);
301                 if (nn->next)
302                         p->flags |= TERMP_NOSPACE;
303         }
304
305         return(0);
306 }
307
308 /* ARGSUSED */
309 static int
310 pre_B(DECL_ARGS)
311 {
312
313         term_fontrepl(p, TERMFONT_BOLD);
314         return(1);
315 }
316
317 /* ARGSUSED */
318 static int
319 pre_ft(DECL_ARGS)
320 {
321         const char      *cp;
322
323         if (NULL == n->child) {
324                 term_fontlast(p);
325                 return(0);
326         }
327
328         cp = n->child->string;
329         switch (*cp) {
330         case ('4'):
331                 /* FALLTHROUGH */
332         case ('3'):
333                 /* FALLTHROUGH */
334         case ('B'):
335                 term_fontrepl(p, TERMFONT_BOLD);
336                 break;
337         case ('2'):
338                 /* FALLTHROUGH */
339         case ('I'):
340                 term_fontrepl(p, TERMFONT_UNDER);
341                 break;
342         case ('P'):
343                 term_fontlast(p);
344                 break;
345         case ('1'):
346                 /* FALLTHROUGH */
347         case ('C'):
348                 /* FALLTHROUGH */
349         case ('R'):
350                 term_fontrepl(p, TERMFONT_NONE);
351                 break;
352         default:
353                 break;
354         }
355         return(0);
356 }
357
358 /* ARGSUSED */
359 static int
360 pre_in(DECL_ARGS)
361 {
362         int              len, less;
363         size_t           v;
364         const char      *cp;
365
366         term_newln(p);
367
368         if (NULL == n->child) {
369                 p->offset = mt->offset;
370                 return(0);
371         }
372
373         cp = n->child->string;
374         less = 0;
375
376         if ('-' == *cp)
377                 less = -1;
378         else if ('+' == *cp)
379                 less = 1;
380         else
381                 cp--;
382
383         if ((len = a2width(p, ++cp)) < 0)
384                 return(0);
385
386         v = (size_t)len;
387
388         if (less < 0)
389                 p->offset -= p->offset > v ? v : p->offset;
390         else if (less > 0)
391                 p->offset += v;
392         else 
393                 p->offset = v;
394
395         /* Don't let this creep beyond the right margin. */
396
397         if (p->offset > p->rmargin)
398                 p->offset = p->rmargin;
399
400         return(0);
401 }
402
403
404 /* ARGSUSED */
405 static int
406 pre_sp(DECL_ARGS)
407 {
408         size_t           i, len;
409
410         switch (n->tok) {
411         case (MAN_br):
412                 len = 0;
413                 break;
414         default:
415                 len = n->child ? a2height(p, n->child->string) : 1;
416                 break;
417         }
418
419         if (0 == len)
420                 term_newln(p);
421         for (i = 0; i < len; i++)
422                 term_vspace(p);
423
424         return(0);
425 }
426
427
428 /* ARGSUSED */
429 static int
430 pre_HP(DECL_ARGS)
431 {
432         size_t                   len;
433         int                      ival;
434         const struct man_node   *nn;
435
436         switch (n->type) {
437         case (MAN_BLOCK):
438                 print_bvspace(p, n);
439                 return(1);
440         case (MAN_BODY):
441                 p->flags |= TERMP_NOBREAK;
442                 p->flags |= TERMP_TWOSPACE;
443                 break;
444         default:
445                 return(0);
446         }
447
448         len = mt->lmargin;
449         ival = -1;
450
451         /* Calculate offset. */
452
453         if (NULL != (nn = n->parent->head->child))
454                 if ((ival = a2width(p, nn->string)) >= 0)
455                         len = (size_t)ival;
456
457         if (0 == len)
458                 len = term_len(p, 1);
459
460         p->offset = mt->offset;
461         p->rmargin = mt->offset + len;
462
463         if (ival >= 0)
464                 mt->lmargin = (size_t)ival;
465
466         return(1);
467 }
468
469
470 /* ARGSUSED */
471 static void
472 post_HP(DECL_ARGS)
473 {
474
475         switch (n->type) {
476         case (MAN_BLOCK):
477                 term_flushln(p);
478                 break;
479         case (MAN_BODY):
480                 term_flushln(p);
481                 p->flags &= ~TERMP_NOBREAK;
482                 p->flags &= ~TERMP_TWOSPACE;
483                 p->offset = mt->offset;
484                 p->rmargin = p->maxrmargin;
485                 break;
486         default:
487                 break;
488         }
489 }
490
491
492 /* ARGSUSED */
493 static int
494 pre_PP(DECL_ARGS)
495 {
496
497         switch (n->type) {
498         case (MAN_BLOCK):
499                 mt->lmargin = term_len(p, INDENT);
500                 print_bvspace(p, n);
501                 break;
502         default:
503                 p->offset = mt->offset;
504                 break;
505         }
506
507         return(MAN_HEAD != n->type);
508 }
509
510
511 /* ARGSUSED */
512 static int
513 pre_IP(DECL_ARGS)
514 {
515         const struct man_node   *nn;
516         size_t                   len;
517         int                      savelit, ival;
518
519         switch (n->type) {
520         case (MAN_BODY):
521                 p->flags |= TERMP_NOLPAD;
522                 p->flags |= TERMP_NOSPACE;
523                 break;
524         case (MAN_HEAD):
525                 p->flags |= TERMP_NOBREAK;
526                 break;
527         case (MAN_BLOCK):
528                 print_bvspace(p, n);
529                 /* FALLTHROUGH */
530         default:
531                 return(1);
532         }
533
534         len = mt->lmargin;
535         ival = -1;
536
537         /* Calculate the offset from the optional second argument. */
538         if (NULL != (nn = n->parent->head->child))
539                 if (NULL != (nn = nn->next))
540                         if ((ival = a2width(p, nn->string)) >= 0)
541                                 len = (size_t)ival;
542
543         switch (n->type) {
544         case (MAN_HEAD):
545                 /* Handle zero-width lengths. */
546                 if (0 == len)
547                         len = term_len(p, 1);
548
549                 p->offset = mt->offset;
550                 p->rmargin = mt->offset + len;
551                 if (ival < 0)
552                         break;
553
554                 /* Set the saved left-margin. */
555                 mt->lmargin = (size_t)ival;
556
557                 savelit = MANT_LITERAL & mt->fl;
558                 mt->fl &= ~MANT_LITERAL;
559
560                 if (n->child)
561                         print_man_node(p, mt, n->child, m);
562
563                 if (savelit)
564                         mt->fl |= MANT_LITERAL;
565
566                 return(0);
567         case (MAN_BODY):
568                 p->offset = mt->offset + len;
569                 p->rmargin = p->maxrmargin;
570                 break;
571         default:
572                 break;
573         }
574
575         return(1);
576 }
577
578
579 /* ARGSUSED */
580 static void
581 post_IP(DECL_ARGS)
582 {
583
584         switch (n->type) {
585         case (MAN_HEAD):
586                 term_flushln(p);
587                 p->flags &= ~TERMP_NOBREAK;
588                 p->rmargin = p->maxrmargin;
589                 break;
590         case (MAN_BODY):
591                 term_newln(p);
592                 p->flags &= ~TERMP_NOLPAD;
593                 break;
594         default:
595                 break;
596         }
597 }
598
599
600 /* ARGSUSED */
601 static int
602 pre_TP(DECL_ARGS)
603 {
604         const struct man_node   *nn;
605         size_t                   len;
606         int                      savelit, ival;
607
608         switch (n->type) {
609         case (MAN_HEAD):
610                 p->flags |= TERMP_NOBREAK;
611                 break;
612         case (MAN_BODY):
613                 p->flags |= TERMP_NOLPAD;
614                 p->flags |= TERMP_NOSPACE;
615                 break;
616         case (MAN_BLOCK):
617                 print_bvspace(p, n);
618                 /* FALLTHROUGH */
619         default:
620                 return(1);
621         }
622
623         len = (size_t)mt->lmargin;
624         ival = -1;
625
626         /* Calculate offset. */
627
628         if (NULL != (nn = n->parent->head->child)) {
629                 while (nn && MAN_TEXT != nn->type)
630                         nn = nn->next;
631                 if (nn && nn->next)
632                         if ((ival = a2width(p, nn->string)) >= 0)
633                                 len = (size_t)ival;
634         }
635
636         switch (n->type) {
637         case (MAN_HEAD):
638                 /* Handle zero-length properly. */
639                 if (0 == len)
640                         len = term_len(p, 1);
641
642                 p->offset = mt->offset;
643                 p->rmargin = mt->offset + len;
644
645                 savelit = MANT_LITERAL & mt->fl;
646                 mt->fl &= ~MANT_LITERAL;
647
648                 /* Don't print same-line elements. */
649                 for (nn = n->child; nn; nn = nn->next)
650                         if (nn->line > n->line)
651                                 print_man_node(p, mt, nn, m);
652
653                 if (savelit)
654                         mt->fl |= MANT_LITERAL;
655
656                 if (ival >= 0)
657                         mt->lmargin = (size_t)ival;
658
659                 return(0);
660         case (MAN_BODY):
661                 p->offset = mt->offset + len;
662                 p->rmargin = p->maxrmargin;
663                 break;
664         default:
665                 break;
666         }
667
668         return(1);
669 }
670
671
672 /* ARGSUSED */
673 static void
674 post_TP(DECL_ARGS)
675 {
676
677         switch (n->type) {
678         case (MAN_HEAD):
679                 term_flushln(p);
680                 p->flags &= ~TERMP_NOBREAK;
681                 p->flags &= ~TERMP_TWOSPACE;
682                 p->rmargin = p->maxrmargin;
683                 break;
684         case (MAN_BODY):
685                 term_newln(p);
686                 p->flags &= ~TERMP_NOLPAD;
687                 break;
688         default:
689                 break;
690         }
691 }
692
693
694 /* ARGSUSED */
695 static int
696 pre_SS(DECL_ARGS)
697 {
698
699         switch (n->type) {
700         case (MAN_BLOCK):
701                 mt->lmargin = term_len(p, INDENT);
702                 mt->offset = term_len(p, INDENT);
703                 /* If following a prior empty `SS', no vspace. */
704                 if (n->prev && MAN_SS == n->prev->tok)
705                         if (NULL == n->prev->body->child)
706                                 break;
707                 if (NULL == n->prev)
708                         break;
709                 term_vspace(p);
710                 break;
711         case (MAN_HEAD):
712                 term_fontrepl(p, TERMFONT_BOLD);
713                 p->offset = term_len(p, HALFINDENT);
714                 break;
715         case (MAN_BODY):
716                 p->offset = mt->offset;
717                 break;
718         default:
719                 break;
720         }
721
722         return(1);
723 }
724
725
726 /* ARGSUSED */
727 static void
728 post_SS(DECL_ARGS)
729 {
730         
731         switch (n->type) {
732         case (MAN_HEAD):
733                 term_newln(p);
734                 break;
735         case (MAN_BODY):
736                 term_newln(p);
737                 break;
738         default:
739                 break;
740         }
741 }
742
743
744 /* ARGSUSED */
745 static int
746 pre_SH(DECL_ARGS)
747 {
748
749         switch (n->type) {
750         case (MAN_BLOCK):
751                 mt->lmargin = term_len(p, INDENT);
752                 mt->offset = term_len(p, INDENT);
753                 /* If following a prior empty `SH', no vspace. */
754                 if (n->prev && MAN_SH == n->prev->tok)
755                         if (NULL == n->prev->body->child)
756                                 break;
757                 /* If the first macro, no vspae. */
758                 if (NULL == n->prev)
759                         break;
760                 term_vspace(p);
761                 break;
762         case (MAN_HEAD):
763                 term_fontrepl(p, TERMFONT_BOLD);
764                 p->offset = 0;
765                 break;
766         case (MAN_BODY):
767                 p->offset = mt->offset;
768                 break;
769         default:
770                 break;
771         }
772
773         return(1);
774 }
775
776
777 /* ARGSUSED */
778 static void
779 post_SH(DECL_ARGS)
780 {
781         
782         switch (n->type) {
783         case (MAN_HEAD):
784                 term_newln(p);
785                 break;
786         case (MAN_BODY):
787                 term_newln(p);
788                 break;
789         default:
790                 break;
791         }
792 }
793
794
795 /* ARGSUSED */
796 static int
797 pre_RS(DECL_ARGS)
798 {
799         const struct man_node   *nn;
800         int                      ival;
801
802         switch (n->type) {
803         case (MAN_BLOCK):
804                 term_newln(p);
805                 return(1);
806         case (MAN_HEAD):
807                 return(0);
808         default:
809                 break;
810         }
811
812         if (NULL == (nn = n->parent->head->child)) {
813                 mt->offset = mt->lmargin + term_len(p, INDENT);
814                 p->offset = mt->offset;
815                 return(1);
816         }
817
818         if ((ival = a2width(p, nn->string)) < 0)
819                 return(1);
820
821         mt->offset = term_len(p, INDENT) + (size_t)ival;
822         p->offset = mt->offset;
823
824         return(1);
825 }
826
827
828 /* ARGSUSED */
829 static void
830 post_RS(DECL_ARGS)
831 {
832
833         switch (n->type) {
834         case (MAN_BLOCK):
835                 mt->offset = mt->lmargin = term_len(p, INDENT);
836                 break;
837         case (MAN_HEAD):
838                 break;
839         default:
840                 term_newln(p);
841                 p->offset = term_len(p, INDENT);
842                 break;
843         }
844 }
845
846
847 static void
848 print_man_node(DECL_ARGS)
849 {
850         size_t           rm, rmax;
851         int              c;
852
853         switch (n->type) {
854         case(MAN_TEXT):
855                 /*
856                  * If we have a blank line, output a vertical space.
857                  * If we have a space as the first character, break
858                  * before printing the line's data.
859                  */
860                 if ('\0' == *n->string) {
861                         term_vspace(p);
862                         return;
863                 } else if (' ' == *n->string && MAN_LINE & n->flags)
864                         term_newln(p);
865
866                 term_word(p, n->string);
867
868                 /*
869                  * If we're in a literal context, make sure that words
870                  * togehter on the same line stay together.  This is a
871                  * POST-printing call, so we check the NEXT word.  Since
872                  * -man doesn't have nested macros, we don't need to be
873                  * more specific than this.
874                  */
875                 if (MANT_LITERAL & mt->fl && 
876                                 (NULL == n->next || 
877                                  n->next->line > n->line)) {
878                         rm = p->rmargin;
879                         rmax = p->maxrmargin;
880                         p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
881                         p->flags |= TERMP_NOSPACE;
882                         term_flushln(p);
883                         p->flags &= ~TERMP_NOLPAD;
884                         p->rmargin = rm;
885                         p->maxrmargin = rmax;
886                 }
887
888                 if (MAN_EOS & n->flags)
889                         p->flags |= TERMP_SENTENCE;
890                 return;
891         case (MAN_EQN):
892                 term_word(p, n->eqn->data);
893                 return;
894         case (MAN_TBL):
895                 /*
896                  * Tables are preceded by a newline.  Then process a
897                  * table line, which will cause line termination,
898                  */
899                 if (TBL_SPAN_FIRST & n->span->flags) 
900                         term_newln(p);
901                 term_tbl(p, n->span);
902                 return;
903         default:
904                 break;
905         }
906
907         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
908                 term_fontrepl(p, TERMFONT_NONE);
909
910         c = 1;
911         if (termacts[n->tok].pre)
912                 c = (*termacts[n->tok].pre)(p, mt, n, m);
913
914         if (c && n->child)
915                 print_man_nodelist(p, mt, n->child, m);
916
917         if (termacts[n->tok].post)
918                 (*termacts[n->tok].post)(p, mt, n, m);
919         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
920                 term_fontrepl(p, TERMFONT_NONE);
921
922         if (MAN_EOS & n->flags)
923                 p->flags |= TERMP_SENTENCE;
924 }
925
926
927 static void
928 print_man_nodelist(DECL_ARGS)
929 {
930
931         print_man_node(p, mt, n, m);
932         if ( ! n->next)
933                 return;
934         print_man_nodelist(p, mt, n->next, m);
935 }
936
937
938 static void
939 print_man_foot(struct termp *p, const void *arg)
940 {
941         const struct man_meta *meta;
942
943         meta = (const struct man_meta *)arg;
944
945         term_fontrepl(p, TERMFONT_NONE);
946
947         term_vspace(p);
948         term_vspace(p);
949         term_vspace(p);
950
951         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
952         p->rmargin = p->maxrmargin - term_strlen(p, meta->date);
953         p->offset = 0;
954
955         /* term_strlen() can return zero. */
956         if (p->rmargin == p->maxrmargin)
957                 p->rmargin--;
958
959         if (meta->source)
960                 term_word(p, meta->source);
961         if (meta->source)
962                 term_word(p, "");
963         term_flushln(p);
964
965         p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
966         p->offset = p->rmargin;
967         p->rmargin = p->maxrmargin;
968         p->flags &= ~TERMP_NOBREAK;
969
970         term_word(p, meta->date);
971         term_flushln(p);
972 }
973
974
975 static void
976 print_man_head(struct termp *p, const void *arg)
977 {
978         char            buf[BUFSIZ], title[BUFSIZ];
979         size_t          buflen, titlen;
980         const struct man_meta *m;
981
982         m = (const struct man_meta *)arg;
983
984         /*
985          * Note that old groff would spit out some spaces before the
986          * header.  We discontinue this strange behaviour, but at one
987          * point we did so here.
988          */
989
990         p->rmargin = p->maxrmargin;
991
992         p->offset = 0;
993         buf[0] = title[0] = '\0';
994
995         if (m->vol)
996                 strlcpy(buf, m->vol, BUFSIZ);
997         buflen = term_strlen(p, buf);
998
999         snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1000         titlen = term_strlen(p, title);
1001
1002         p->offset = 0;
1003         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1004             (p->maxrmargin - 
1005              term_strlen(p, buf) + term_len(p, 1)) / 2 :
1006             p->maxrmargin - buflen;
1007         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1008
1009         term_word(p, title);
1010         term_flushln(p);
1011
1012         p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1013         p->offset = p->rmargin;
1014         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1015             p->maxrmargin - titlen : p->maxrmargin;
1016
1017         term_word(p, buf);
1018         term_flushln(p);
1019
1020         p->flags &= ~TERMP_NOBREAK;
1021         if (p->rmargin + titlen <= p->maxrmargin) {
1022                 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1023                 p->offset = p->rmargin;
1024                 p->rmargin = p->maxrmargin;
1025                 term_word(p, title);
1026                 term_flushln(p);
1027         }
1028
1029         p->rmargin = p->maxrmargin;
1030         p->offset = 0;
1031         p->flags &= ~TERMP_NOSPACE;
1032
1033         /* 
1034          * Groff likes to have some leading spaces before content.  Well
1035          * that's fine by me.
1036          */
1037
1038         term_vspace(p);
1039         term_vspace(p);
1040         term_vspace(p);
1041 }