kernel: Remove the old ep(4) and ex(4) network drivers.
[dragonfly.git] / contrib / mdocml / man_term.c
1 /*      $Id: man_term.c,v 1.149 2014/06/20 23:02:31 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010-2014 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 "mandoc_aux.h"
32 #include "out.h"
33 #include "man.h"
34 #include "term.h"
35 #include "main.h"
36
37 #define MAXMARGINS        64 /* maximum number of indented scopes */
38
39 struct  mtermp {
40         int               fl;
41 #define MANT_LITERAL     (1 << 0)
42         size_t            lmargin[MAXMARGINS]; /* margins (incl. visible page) */
43         int               lmargincur; /* index of current margin */
44         int               lmarginsz; /* actual number of nested margins */
45         size_t            offset; /* default offset to visible page */
46         int               pardist; /* vert. space before par., unit: [v] */
47 };
48
49 #define DECL_ARGS         struct termp *p, \
50                           struct mtermp *mt, \
51                           const struct man_node *n, \
52                           const struct man_meta *meta
53
54 struct  termact {
55         int             (*pre)(DECL_ARGS);
56         void            (*post)(DECL_ARGS);
57         int               flags;
58 #define MAN_NOTEXT       (1 << 0) /* Never has text children. */
59 };
60
61 static  int               a2width(const struct termp *, const char *);
62 static  size_t            a2height(const struct termp *, const char *);
63
64 static  void              print_man_nodelist(DECL_ARGS);
65 static  void              print_man_node(DECL_ARGS);
66 static  void              print_man_head(struct termp *, const void *);
67 static  void              print_man_foot(struct termp *, const void *);
68 static  void              print_bvspace(struct termp *,
69                                 const struct man_node *, int);
70
71 static  int               pre_B(DECL_ARGS);
72 static  int               pre_HP(DECL_ARGS);
73 static  int               pre_I(DECL_ARGS);
74 static  int               pre_IP(DECL_ARGS);
75 static  int               pre_OP(DECL_ARGS);
76 static  int               pre_PD(DECL_ARGS);
77 static  int               pre_PP(DECL_ARGS);
78 static  int               pre_RS(DECL_ARGS);
79 static  int               pre_SH(DECL_ARGS);
80 static  int               pre_SS(DECL_ARGS);
81 static  int               pre_TP(DECL_ARGS);
82 static  int               pre_UR(DECL_ARGS);
83 static  int               pre_alternate(DECL_ARGS);
84 static  int               pre_ft(DECL_ARGS);
85 static  int               pre_ign(DECL_ARGS);
86 static  int               pre_in(DECL_ARGS);
87 static  int               pre_literal(DECL_ARGS);
88 static  int               pre_ll(DECL_ARGS);
89 static  int               pre_sp(DECL_ARGS);
90
91 static  void              post_IP(DECL_ARGS);
92 static  void              post_HP(DECL_ARGS);
93 static  void              post_RS(DECL_ARGS);
94 static  void              post_SH(DECL_ARGS);
95 static  void              post_SS(DECL_ARGS);
96 static  void              post_TP(DECL_ARGS);
97 static  void              post_UR(DECL_ARGS);
98
99 static  const struct termact termacts[MAN_MAX] = {
100         { pre_sp, NULL, MAN_NOTEXT }, /* br */
101         { NULL, NULL, 0 }, /* TH */
102         { pre_SH, post_SH, 0 }, /* SH */
103         { pre_SS, post_SS, 0 }, /* SS */
104         { pre_TP, post_TP, 0 }, /* TP */
105         { pre_PP, NULL, 0 }, /* LP */
106         { pre_PP, NULL, 0 }, /* PP */
107         { pre_PP, NULL, 0 }, /* P */
108         { pre_IP, post_IP, 0 }, /* IP */
109         { pre_HP, post_HP, 0 }, /* HP */
110         { NULL, NULL, 0 }, /* SM */
111         { pre_B, NULL, 0 }, /* SB */
112         { pre_alternate, NULL, 0 }, /* BI */
113         { pre_alternate, NULL, 0 }, /* IB */
114         { pre_alternate, NULL, 0 }, /* BR */
115         { pre_alternate, NULL, 0 }, /* RB */
116         { NULL, NULL, 0 }, /* R */
117         { pre_B, NULL, 0 }, /* B */
118         { pre_I, NULL, 0 }, /* I */
119         { pre_alternate, NULL, 0 }, /* IR */
120         { pre_alternate, NULL, 0 }, /* RI */
121         { pre_ign, NULL, MAN_NOTEXT }, /* na */
122         { pre_sp, NULL, MAN_NOTEXT }, /* sp */
123         { pre_literal, NULL, 0 }, /* nf */
124         { pre_literal, NULL, 0 }, /* fi */
125         { NULL, NULL, 0 }, /* RE */
126         { pre_RS, post_RS, 0 }, /* RS */
127         { pre_ign, NULL, 0 }, /* DT */
128         { pre_ign, NULL, 0 }, /* UC */
129         { pre_PD, NULL, MAN_NOTEXT }, /* PD */
130         { pre_ign, NULL, 0 }, /* AT */
131         { pre_in, NULL, MAN_NOTEXT }, /* in */
132         { pre_ft, NULL, MAN_NOTEXT }, /* ft */
133         { pre_OP, NULL, 0 }, /* OP */
134         { pre_literal, NULL, 0 }, /* EX */
135         { pre_literal, NULL, 0 }, /* EE */
136         { pre_UR, post_UR, 0 }, /* UR */
137         { NULL, NULL, 0 }, /* UE */
138         { pre_ll, NULL, MAN_NOTEXT }, /* ll */
139 };
140
141
142 void
143 terminal_man(void *arg, const struct man *man)
144 {
145         struct termp            *p;
146         const struct man_node   *n;
147         const struct man_meta   *meta;
148         struct mtermp            mt;
149
150         p = (struct termp *)arg;
151
152         if (0 == p->defindent)
153                 p->defindent = 7;
154
155         p->overstep = 0;
156         p->maxrmargin = p->defrmargin;
157         p->tabwidth = term_len(p, 5);
158
159         if (NULL == p->symtab)
160                 p->symtab = mchars_alloc();
161
162         n = man_node(man);
163         meta = man_meta(man);
164
165         term_begin(p, print_man_head, print_man_foot, meta);
166         p->flags |= TERMP_NOSPACE;
167
168         memset(&mt, 0, sizeof(struct mtermp));
169
170         mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
171         mt.offset = term_len(p, p->defindent);
172         mt.pardist = 1;
173
174         if (n->child)
175                 print_man_nodelist(p, &mt, n->child, meta);
176
177         term_end(p);
178 }
179
180
181 static size_t
182 a2height(const struct termp *p, const char *cp)
183 {
184         struct roffsu    su;
185
186         if ( ! a2roffsu(cp, &su, SCALE_VS))
187                 SCALE_VS_INIT(&su, atoi(cp));
188
189         return(term_vspan(p, &su));
190 }
191
192 static int
193 a2width(const struct termp *p, const char *cp)
194 {
195         struct roffsu    su;
196
197         if ( ! a2roffsu(cp, &su, SCALE_BU))
198                 return(-1);
199
200         return((int)term_hspan(p, &su));
201 }
202
203 /*
204  * Printing leading vertical space before a block.
205  * This is used for the paragraph macros.
206  * The rules are pretty simple, since there's very little nesting going
207  * on here.  Basically, if we're the first within another block (SS/SH),
208  * then don't emit vertical space.  If we are (RS), then do.  If not the
209  * first, print it.
210  */
211 static void
212 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
213 {
214         int      i;
215
216         term_newln(p);
217
218         if (n->body && n->body->child)
219                 if (MAN_TBL == n->body->child->type)
220                         return;
221
222         if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
223                 if (NULL == n->prev)
224                         return;
225
226         for (i = 0; i < pardist; i++)
227                 term_vspace(p);
228 }
229
230
231 static int
232 pre_ign(DECL_ARGS)
233 {
234
235         return(0);
236 }
237
238 static int
239 pre_ll(DECL_ARGS)
240 {
241
242         term_setwidth(p, n->nchild ? n->child->string : NULL);
243         return(0);
244 }
245
246 static int
247 pre_I(DECL_ARGS)
248 {
249
250         term_fontrepl(p, TERMFONT_UNDER);
251         return(1);
252 }
253
254 static int
255 pre_literal(DECL_ARGS)
256 {
257
258         term_newln(p);
259
260         if (MAN_nf == n->tok || MAN_EX == n->tok)
261                 mt->fl |= MANT_LITERAL;
262         else
263                 mt->fl &= ~MANT_LITERAL;
264
265         /*
266          * Unlike .IP and .TP, .HP does not have a HEAD.
267          * So in case a second call to term_flushln() is needed,
268          * indentation has to be set up explicitly.
269          */
270         if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
271                 p->offset = p->rmargin;
272                 p->rmargin = p->maxrmargin;
273                 p->trailspace = 0;
274                 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
275                 p->flags |= TERMP_NOSPACE;
276         }
277
278         return(0);
279 }
280
281 static int
282 pre_PD(DECL_ARGS)
283 {
284
285         n = n->child;
286         if (0 == n) {
287                 mt->pardist = 1;
288                 return(0);
289         }
290         assert(MAN_TEXT == n->type);
291         mt->pardist = atoi(n->string);
292         return(0);
293 }
294
295 static int
296 pre_alternate(DECL_ARGS)
297 {
298         enum termfont            font[2];
299         const struct man_node   *nn;
300         int                      savelit, i;
301
302         switch (n->tok) {
303         case MAN_RB:
304                 font[0] = TERMFONT_NONE;
305                 font[1] = TERMFONT_BOLD;
306                 break;
307         case MAN_RI:
308                 font[0] = TERMFONT_NONE;
309                 font[1] = TERMFONT_UNDER;
310                 break;
311         case MAN_BR:
312                 font[0] = TERMFONT_BOLD;
313                 font[1] = TERMFONT_NONE;
314                 break;
315         case MAN_BI:
316                 font[0] = TERMFONT_BOLD;
317                 font[1] = TERMFONT_UNDER;
318                 break;
319         case MAN_IR:
320                 font[0] = TERMFONT_UNDER;
321                 font[1] = TERMFONT_NONE;
322                 break;
323         case MAN_IB:
324                 font[0] = TERMFONT_UNDER;
325                 font[1] = TERMFONT_BOLD;
326                 break;
327         default:
328                 abort();
329         }
330
331         savelit = MANT_LITERAL & mt->fl;
332         mt->fl &= ~MANT_LITERAL;
333
334         for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
335                 term_fontrepl(p, font[i]);
336                 if (savelit && NULL == nn->next)
337                         mt->fl |= MANT_LITERAL;
338                 print_man_node(p, mt, nn, meta);
339                 if (nn->next)
340                         p->flags |= TERMP_NOSPACE;
341         }
342
343         return(0);
344 }
345
346 static int
347 pre_B(DECL_ARGS)
348 {
349
350         term_fontrepl(p, TERMFONT_BOLD);
351         return(1);
352 }
353
354 static int
355 pre_OP(DECL_ARGS)
356 {
357
358         term_word(p, "[");
359         p->flags |= TERMP_NOSPACE;
360
361         if (NULL != (n = n->child)) {
362                 term_fontrepl(p, TERMFONT_BOLD);
363                 term_word(p, n->string);
364         }
365         if (NULL != n && NULL != n->next) {
366                 term_fontrepl(p, TERMFONT_UNDER);
367                 term_word(p, n->next->string);
368         }
369
370         term_fontrepl(p, TERMFONT_NONE);
371         p->flags |= TERMP_NOSPACE;
372         term_word(p, "]");
373         return(0);
374 }
375
376 static int
377 pre_ft(DECL_ARGS)
378 {
379         const char      *cp;
380
381         if (NULL == n->child) {
382                 term_fontlast(p);
383                 return(0);
384         }
385
386         cp = n->child->string;
387         switch (*cp) {
388         case '4':
389                 /* FALLTHROUGH */
390         case '3':
391                 /* FALLTHROUGH */
392         case 'B':
393                 term_fontrepl(p, TERMFONT_BOLD);
394                 break;
395         case '2':
396                 /* FALLTHROUGH */
397         case 'I':
398                 term_fontrepl(p, TERMFONT_UNDER);
399                 break;
400         case 'P':
401                 term_fontlast(p);
402                 break;
403         case '1':
404                 /* FALLTHROUGH */
405         case 'C':
406                 /* FALLTHROUGH */
407         case 'R':
408                 term_fontrepl(p, TERMFONT_NONE);
409                 break;
410         default:
411                 break;
412         }
413         return(0);
414 }
415
416 static int
417 pre_in(DECL_ARGS)
418 {
419         int              len, less;
420         size_t           v;
421         const char      *cp;
422
423         term_newln(p);
424
425         if (NULL == n->child) {
426                 p->offset = mt->offset;
427                 return(0);
428         }
429
430         cp = n->child->string;
431         less = 0;
432
433         if ('-' == *cp)
434                 less = -1;
435         else if ('+' == *cp)
436                 less = 1;
437         else
438                 cp--;
439
440         if ((len = a2width(p, ++cp)) < 0)
441                 return(0);
442
443         v = (size_t)len;
444
445         if (less < 0)
446                 p->offset -= p->offset > v ? v : p->offset;
447         else if (less > 0)
448                 p->offset += v;
449         else
450                 p->offset = v;
451
452         /* Don't let this creep beyond the right margin. */
453
454         if (p->offset > p->rmargin)
455                 p->offset = p->rmargin;
456
457         return(0);
458 }
459
460 static int
461 pre_sp(DECL_ARGS)
462 {
463         char            *s;
464         size_t           i, len;
465         int              neg;
466
467         if ((NULL == n->prev && n->parent)) {
468                 switch (n->parent->tok) {
469                 case MAN_SH:
470                         /* FALLTHROUGH */
471                 case MAN_SS:
472                         /* FALLTHROUGH */
473                 case MAN_PP:
474                         /* FALLTHROUGH */
475                 case MAN_LP:
476                         /* FALLTHROUGH */
477                 case MAN_P:
478                         /* FALLTHROUGH */
479                         return(0);
480                 default:
481                         break;
482                 }
483         }
484
485         neg = 0;
486         switch (n->tok) {
487         case MAN_br:
488                 len = 0;
489                 break;
490         default:
491                 if (NULL == n->child) {
492                         len = 1;
493                         break;
494                 }
495                 s = n->child->string;
496                 if ('-' == *s) {
497                         neg = 1;
498                         s++;
499                 }
500                 len = a2height(p, s);
501                 break;
502         }
503
504         if (0 == len)
505                 term_newln(p);
506         else if (neg)
507                 p->skipvsp += len;
508         else
509                 for (i = 0; i < len; i++)
510                         term_vspace(p);
511
512         return(0);
513 }
514
515 static int
516 pre_HP(DECL_ARGS)
517 {
518         size_t                   len, one;
519         int                      ival;
520         const struct man_node   *nn;
521
522         switch (n->type) {
523         case MAN_BLOCK:
524                 print_bvspace(p, n, mt->pardist);
525                 return(1);
526         case MAN_BODY:
527                 break;
528         default:
529                 return(0);
530         }
531
532         if ( ! (MANT_LITERAL & mt->fl)) {
533                 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
534                 p->trailspace = 2;
535         }
536
537         len = mt->lmargin[mt->lmargincur];
538         ival = -1;
539
540         /* Calculate offset. */
541
542         if (NULL != (nn = n->parent->head->child))
543                 if ((ival = a2width(p, nn->string)) >= 0)
544                         len = (size_t)ival;
545
546         one = term_len(p, 1);
547         if (len < one)
548                 len = one;
549
550         p->offset = mt->offset;
551         p->rmargin = mt->offset + len;
552
553         if (ival >= 0)
554                 mt->lmargin[mt->lmargincur] = (size_t)ival;
555
556         return(1);
557 }
558
559 static void
560 post_HP(DECL_ARGS)
561 {
562
563         switch (n->type) {
564         case MAN_BODY:
565                 term_newln(p);
566                 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
567                 p->trailspace = 0;
568                 p->offset = mt->offset;
569                 p->rmargin = p->maxrmargin;
570                 break;
571         default:
572                 break;
573         }
574 }
575
576 static int
577 pre_PP(DECL_ARGS)
578 {
579
580         switch (n->type) {
581         case MAN_BLOCK:
582                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
583                 print_bvspace(p, n, mt->pardist);
584                 break;
585         default:
586                 p->offset = mt->offset;
587                 break;
588         }
589
590         return(MAN_HEAD != n->type);
591 }
592
593 static int
594 pre_IP(DECL_ARGS)
595 {
596         const struct man_node   *nn;
597         size_t                   len;
598         int                      savelit, ival;
599
600         switch (n->type) {
601         case MAN_BODY:
602                 p->flags |= TERMP_NOSPACE;
603                 break;
604         case MAN_HEAD:
605                 p->flags |= TERMP_NOBREAK;
606                 p->trailspace = 1;
607                 break;
608         case MAN_BLOCK:
609                 print_bvspace(p, n, mt->pardist);
610                 /* FALLTHROUGH */
611         default:
612                 return(1);
613         }
614
615         len = mt->lmargin[mt->lmargincur];
616         ival = -1;
617
618         /* Calculate the offset from the optional second argument. */
619         if (NULL != (nn = n->parent->head->child))
620                 if (NULL != (nn = nn->next))
621                         if ((ival = a2width(p, nn->string)) >= 0)
622                                 len = (size_t)ival;
623
624         switch (n->type) {
625         case MAN_HEAD:
626                 /* Handle zero-width lengths. */
627                 if (0 == len)
628                         len = term_len(p, 1);
629
630                 p->offset = mt->offset;
631                 p->rmargin = mt->offset + len;
632                 if (ival < 0)
633                         break;
634
635                 /* Set the saved left-margin. */
636                 mt->lmargin[mt->lmargincur] = (size_t)ival;
637
638                 savelit = MANT_LITERAL & mt->fl;
639                 mt->fl &= ~MANT_LITERAL;
640
641                 if (n->child)
642                         print_man_node(p, mt, n->child, meta);
643
644                 if (savelit)
645                         mt->fl |= MANT_LITERAL;
646
647                 return(0);
648         case MAN_BODY:
649                 p->offset = mt->offset + len;
650                 p->rmargin = p->maxrmargin > p->offset ?
651                     p->maxrmargin : p->offset;
652                 break;
653         default:
654                 break;
655         }
656
657         return(1);
658 }
659
660 static void
661 post_IP(DECL_ARGS)
662 {
663
664         switch (n->type) {
665         case MAN_HEAD:
666                 term_flushln(p);
667                 p->flags &= ~TERMP_NOBREAK;
668                 p->trailspace = 0;
669                 p->rmargin = p->maxrmargin;
670                 break;
671         case MAN_BODY:
672                 term_newln(p);
673                 p->offset = mt->offset;
674                 break;
675         default:
676                 break;
677         }
678 }
679
680 static int
681 pre_TP(DECL_ARGS)
682 {
683         const struct man_node   *nn;
684         size_t                   len;
685         int                      savelit, ival;
686
687         switch (n->type) {
688         case MAN_HEAD:
689                 p->flags |= TERMP_NOBREAK;
690                 p->trailspace = 1;
691                 break;
692         case MAN_BODY:
693                 p->flags |= TERMP_NOSPACE;
694                 break;
695         case MAN_BLOCK:
696                 print_bvspace(p, n, mt->pardist);
697                 /* FALLTHROUGH */
698         default:
699                 return(1);
700         }
701
702         len = (size_t)mt->lmargin[mt->lmargincur];
703         ival = -1;
704
705         /* Calculate offset. */
706
707         if (NULL != (nn = n->parent->head->child))
708                 if (nn->string && 0 == (MAN_LINE & nn->flags))
709                         if ((ival = a2width(p, nn->string)) >= 0)
710                                 len = (size_t)ival;
711
712         switch (n->type) {
713         case MAN_HEAD:
714                 /* Handle zero-length properly. */
715                 if (0 == len)
716                         len = term_len(p, 1);
717
718                 p->offset = mt->offset;
719                 p->rmargin = mt->offset + len;
720
721                 savelit = MANT_LITERAL & mt->fl;
722                 mt->fl &= ~MANT_LITERAL;
723
724                 /* Don't print same-line elements. */
725                 nn = n->child;
726                 while (NULL != nn && 0 == (MAN_LINE & nn->flags))
727                         nn = nn->next;
728
729                 while (NULL != nn) {
730                         print_man_node(p, mt, nn, meta);
731                         nn = nn->next;
732                 }
733
734                 if (savelit)
735                         mt->fl |= MANT_LITERAL;
736                 if (ival >= 0)
737                         mt->lmargin[mt->lmargincur] = (size_t)ival;
738
739                 return(0);
740         case MAN_BODY:
741                 p->offset = mt->offset + len;
742                 p->rmargin = p->maxrmargin > p->offset ?
743                     p->maxrmargin : p->offset;
744                 p->trailspace = 0;
745                 p->flags &= ~TERMP_NOBREAK;
746                 break;
747         default:
748                 break;
749         }
750
751         return(1);
752 }
753
754 static void
755 post_TP(DECL_ARGS)
756 {
757
758         switch (n->type) {
759         case MAN_HEAD:
760                 term_flushln(p);
761                 break;
762         case MAN_BODY:
763                 term_newln(p);
764                 p->offset = mt->offset;
765                 break;
766         default:
767                 break;
768         }
769 }
770
771 static int
772 pre_SS(DECL_ARGS)
773 {
774         int      i;
775
776         switch (n->type) {
777         case MAN_BLOCK:
778                 mt->fl &= ~MANT_LITERAL;
779                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
780                 mt->offset = term_len(p, p->defindent);
781                 /* If following a prior empty `SS', no vspace. */
782                 if (n->prev && MAN_SS == n->prev->tok)
783                         if (NULL == n->prev->body->child)
784                                 break;
785                 if (NULL == n->prev)
786                         break;
787                 for (i = 0; i < mt->pardist; i++)
788                         term_vspace(p);
789                 break;
790         case MAN_HEAD:
791                 term_fontrepl(p, TERMFONT_BOLD);
792                 p->offset = term_len(p, 3);
793                 break;
794         case MAN_BODY:
795                 p->offset = mt->offset;
796                 break;
797         default:
798                 break;
799         }
800
801         return(1);
802 }
803
804 static void
805 post_SS(DECL_ARGS)
806 {
807
808         switch (n->type) {
809         case MAN_HEAD:
810                 term_newln(p);
811                 break;
812         case MAN_BODY:
813                 term_newln(p);
814                 break;
815         default:
816                 break;
817         }
818 }
819
820 static int
821 pre_SH(DECL_ARGS)
822 {
823         int      i;
824
825         switch (n->type) {
826         case MAN_BLOCK:
827                 mt->fl &= ~MANT_LITERAL;
828                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
829                 mt->offset = term_len(p, p->defindent);
830                 /* If following a prior empty `SH', no vspace. */
831                 if (n->prev && MAN_SH == n->prev->tok)
832                         if (NULL == n->prev->body->child)
833                                 break;
834                 /* If the first macro, no vspae. */
835                 if (NULL == n->prev)
836                         break;
837                 for (i = 0; i < mt->pardist; i++)
838                         term_vspace(p);
839                 break;
840         case MAN_HEAD:
841                 term_fontrepl(p, TERMFONT_BOLD);
842                 p->offset = 0;
843                 break;
844         case MAN_BODY:
845                 p->offset = mt->offset;
846                 break;
847         default:
848                 break;
849         }
850
851         return(1);
852 }
853
854 static void
855 post_SH(DECL_ARGS)
856 {
857
858         switch (n->type) {
859         case MAN_HEAD:
860                 term_newln(p);
861                 break;
862         case MAN_BODY:
863                 term_newln(p);
864                 break;
865         default:
866                 break;
867         }
868 }
869
870 static int
871 pre_RS(DECL_ARGS)
872 {
873         int              ival;
874         size_t           sz;
875
876         switch (n->type) {
877         case MAN_BLOCK:
878                 term_newln(p);
879                 return(1);
880         case MAN_HEAD:
881                 return(0);
882         default:
883                 break;
884         }
885
886         sz = term_len(p, p->defindent);
887
888         if (NULL != (n = n->parent->head->child))
889                 if ((ival = a2width(p, n->string)) >= 0)
890                         sz = (size_t)ival;
891
892         mt->offset += sz;
893         p->offset = mt->offset;
894         p->rmargin = p->maxrmargin > p->offset ?
895             p->maxrmargin : p->offset;
896
897         if (++mt->lmarginsz < MAXMARGINS)
898                 mt->lmargincur = mt->lmarginsz;
899
900         mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
901         return(1);
902 }
903
904 static void
905 post_RS(DECL_ARGS)
906 {
907         int              ival;
908         size_t           sz;
909
910         switch (n->type) {
911         case MAN_BLOCK:
912                 return;
913         case MAN_HEAD:
914                 return;
915         default:
916                 term_newln(p);
917                 break;
918         }
919
920         sz = term_len(p, p->defindent);
921
922         if (NULL != (n = n->parent->head->child))
923                 if ((ival = a2width(p, n->string)) >= 0)
924                         sz = (size_t)ival;
925
926         mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
927         p->offset = mt->offset;
928
929         if (--mt->lmarginsz < MAXMARGINS)
930                 mt->lmargincur = mt->lmarginsz;
931 }
932
933 static int
934 pre_UR(DECL_ARGS)
935 {
936
937         return (MAN_HEAD != n->type);
938 }
939
940 static void
941 post_UR(DECL_ARGS)
942 {
943
944         if (MAN_BLOCK != n->type)
945                 return;
946
947         term_word(p, "<");
948         p->flags |= TERMP_NOSPACE;
949
950         if (NULL != n->child->child)
951                 print_man_node(p, mt, n->child->child, meta);
952
953         p->flags |= TERMP_NOSPACE;
954         term_word(p, ">");
955 }
956
957 static void
958 print_man_node(DECL_ARGS)
959 {
960         size_t           rm, rmax;
961         int              c;
962
963         switch (n->type) {
964         case MAN_TEXT:
965                 /*
966                  * If we have a blank line, output a vertical space.
967                  * If we have a space as the first character, break
968                  * before printing the line's data.
969                  */
970                 if ('\0' == *n->string) {
971                         term_vspace(p);
972                         return;
973                 } else if (' ' == *n->string && MAN_LINE & n->flags)
974                         term_newln(p);
975
976                 term_word(p, n->string);
977                 goto out;
978
979         case MAN_EQN:
980                 term_eqn(p, n->eqn);
981                 return;
982         case MAN_TBL:
983                 /*
984                  * Tables are preceded by a newline.  Then process a
985                  * table line, which will cause line termination,
986                  */
987                 if (TBL_SPAN_FIRST & n->span->flags)
988                         term_newln(p);
989                 term_tbl(p, n->span);
990                 return;
991         default:
992                 break;
993         }
994
995         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
996                 term_fontrepl(p, TERMFONT_NONE);
997
998         c = 1;
999         if (termacts[n->tok].pre)
1000                 c = (*termacts[n->tok].pre)(p, mt, n, meta);
1001
1002         if (c && n->child)
1003                 print_man_nodelist(p, mt, n->child, meta);
1004
1005         if (termacts[n->tok].post)
1006                 (*termacts[n->tok].post)(p, mt, n, meta);
1007         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1008                 term_fontrepl(p, TERMFONT_NONE);
1009
1010 out:
1011         /*
1012          * If we're in a literal context, make sure that words
1013          * together on the same line stay together.  This is a
1014          * POST-printing call, so we check the NEXT word.  Since
1015          * -man doesn't have nested macros, we don't need to be
1016          * more specific than this.
1017          */
1018         if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1019             (NULL == n->next || MAN_LINE & n->next->flags)) {
1020                 rm = p->rmargin;
1021                 rmax = p->maxrmargin;
1022                 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1023                 p->flags |= TERMP_NOSPACE;
1024                 if (NULL != n->string && '\0' != *n->string)
1025                         term_flushln(p);
1026                 else
1027                         term_newln(p);
1028                 if (rm < rmax && n->parent->tok == MAN_HP) {
1029                         p->offset = rm;
1030                         p->rmargin = rmax;
1031                 } else
1032                         p->rmargin = rm;
1033                 p->maxrmargin = rmax;
1034         }
1035         if (MAN_EOS & n->flags)
1036                 p->flags |= TERMP_SENTENCE;
1037 }
1038
1039
1040 static void
1041 print_man_nodelist(DECL_ARGS)
1042 {
1043
1044         print_man_node(p, mt, n, meta);
1045         if ( ! n->next)
1046                 return;
1047         print_man_nodelist(p, mt, n->next, meta);
1048 }
1049
1050 static void
1051 print_man_foot(struct termp *p, const void *arg)
1052 {
1053         const struct man_meta   *meta;
1054         char                    *title;
1055         size_t                   datelen;
1056
1057         meta = (const struct man_meta *)arg;
1058         assert(meta->title);
1059         assert(meta->msec);
1060         assert(meta->date);
1061
1062         term_fontrepl(p, TERMFONT_NONE);
1063
1064         if (meta->hasbody)
1065                 term_vspace(p);
1066
1067         /*
1068          * Temporary, undocumented option to imitate mdoc(7) output.
1069          * In the bottom right corner, use the source instead of
1070          * the title.
1071          */
1072
1073         if ( ! p->mdocstyle) {
1074                 if (meta->hasbody) {
1075                         term_vspace(p);
1076                         term_vspace(p);
1077                 }
1078                 mandoc_asprintf(&title, "%s(%s)",
1079                     meta->title, meta->msec);
1080         } else if (meta->source) {
1081                 title = mandoc_strdup(meta->source);
1082         } else {
1083                 title = mandoc_strdup("");
1084         }
1085         datelen = term_strlen(p, meta->date);
1086
1087         /* Bottom left corner: manual source. */
1088
1089         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1090         p->trailspace = 1;
1091         p->offset = 0;
1092         p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1093
1094         if (meta->source)
1095                 term_word(p, meta->source);
1096         term_flushln(p);
1097
1098         /* At the bottom in the middle: manual date. */
1099
1100         p->flags |= TERMP_NOSPACE;
1101         p->offset = p->rmargin;
1102         p->rmargin = p->maxrmargin - term_strlen(p, title);
1103         if (p->offset + datelen >= p->rmargin)
1104                 p->rmargin = p->offset + datelen;
1105
1106         term_word(p, meta->date);
1107         term_flushln(p);
1108
1109         /* Bottom right corner: manual title and section. */
1110
1111         p->flags &= ~TERMP_NOBREAK;
1112         p->flags |= TERMP_NOSPACE;
1113         p->trailspace = 0;
1114         p->offset = p->rmargin;
1115         p->rmargin = p->maxrmargin;
1116
1117         term_word(p, title);
1118         term_flushln(p);
1119         free(title);
1120 }
1121
1122 static void
1123 print_man_head(struct termp *p, const void *arg)
1124 {
1125         const struct man_meta   *meta;
1126         const char              *volume;
1127         char                    *title;
1128         size_t                   vollen, titlen;
1129
1130         meta = (const struct man_meta *)arg;
1131         assert(meta->title);
1132         assert(meta->msec);
1133
1134         volume = NULL == meta->vol ? "" : meta->vol;
1135         vollen = term_strlen(p, volume);
1136
1137         /* Top left corner: manual title and section. */
1138
1139         mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
1140         titlen = term_strlen(p, title);
1141
1142         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1143         p->trailspace = 1;
1144         p->offset = 0;
1145         p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
1146             (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
1147             p->maxrmargin - vollen;
1148
1149         term_word(p, title);
1150         term_flushln(p);
1151
1152         /* At the top in the middle: manual volume. */
1153
1154         p->flags |= TERMP_NOSPACE;
1155         p->offset = p->rmargin;
1156         p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
1157             p->maxrmargin - titlen : p->maxrmargin;
1158
1159         term_word(p, volume);
1160         term_flushln(p);
1161
1162         /* Top right corner: title and section, again. */
1163
1164         p->flags &= ~TERMP_NOBREAK;
1165         p->trailspace = 0;
1166         if (p->rmargin + titlen <= p->maxrmargin) {
1167                 p->flags |= TERMP_NOSPACE;
1168                 p->offset = p->rmargin;
1169                 p->rmargin = p->maxrmargin;
1170                 term_word(p, title);
1171                 term_flushln(p);
1172         }
1173
1174         p->flags &= ~TERMP_NOSPACE;
1175         p->offset = 0;
1176         p->rmargin = p->maxrmargin;
1177
1178         /*
1179          * Groff prints three blank lines before the content.
1180          * Do the same, except in the temporary, undocumented
1181          * mode imitating mdoc(7) output.
1182          */
1183
1184         term_vspace(p);
1185         if ( ! p->mdocstyle) {
1186                 term_vspace(p);
1187                 term_vspace(p);
1188         }
1189         free(title);
1190 }