Print this page
5051 import mdocml-1.12.3
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Approved by: TBD
   1 /*      $Id: man_term.c,v 1.127 2012/01/03 15:16:24 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 MAXMARGINS        64 /* maximum number of indented scopes */
  37 
  38 /* FIXME: have PD set the default vspace width. */
  39 
  40 struct  mtermp {
  41         int               fl;
  42 #define MANT_LITERAL     (1 << 0)
  43         size_t            lmargin[MAXMARGINS]; /* margins (incl. visible page) */
  44         int               lmargincur; /* index of current margin */
  45         int               lmarginsz; /* actual number of nested margins */
  46         size_t            offset; /* default offset to visible page */

  47 };
  48 
  49 #define DECL_ARGS         struct termp *p, \
  50                           struct mtermp *mt, \
  51                           const struct man_node *n, \
  52                           const struct man_meta *m
  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 *);
  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_PP(DECL_ARGS);
  77 static  int               pre_RS(DECL_ARGS);
  78 static  int               pre_SH(DECL_ARGS);
  79 static  int               pre_SS(DECL_ARGS);
  80 static  int               pre_TP(DECL_ARGS);

  81 static  int               pre_alternate(DECL_ARGS);
  82 static  int               pre_ft(DECL_ARGS);
  83 static  int               pre_ign(DECL_ARGS);
  84 static  int               pre_in(DECL_ARGS);
  85 static  int               pre_literal(DECL_ARGS);
  86 static  int               pre_sp(DECL_ARGS);
  87 
  88 static  void              post_IP(DECL_ARGS);
  89 static  void              post_HP(DECL_ARGS);
  90 static  void              post_RS(DECL_ARGS);
  91 static  void              post_SH(DECL_ARGS);
  92 static  void              post_SS(DECL_ARGS);
  93 static  void              post_TP(DECL_ARGS);

  94 
  95 static  const struct termact termacts[MAN_MAX] = {
  96         { pre_sp, NULL, MAN_NOTEXT }, /* br */
  97         { NULL, NULL, 0 }, /* TH */
  98         { pre_SH, post_SH, 0 }, /* SH */
  99         { pre_SS, post_SS, 0 }, /* SS */
 100         { pre_TP, post_TP, 0 }, /* TP */
 101         { pre_PP, NULL, 0 }, /* LP */
 102         { pre_PP, NULL, 0 }, /* PP */
 103         { pre_PP, NULL, 0 }, /* P */
 104         { pre_IP, post_IP, 0 }, /* IP */
 105         { pre_HP, post_HP, 0 }, /* HP */ 
 106         { NULL, NULL, 0 }, /* SM */
 107         { pre_B, NULL, 0 }, /* SB */
 108         { pre_alternate, NULL, 0 }, /* BI */
 109         { pre_alternate, NULL, 0 }, /* IB */
 110         { pre_alternate, NULL, 0 }, /* BR */
 111         { pre_alternate, NULL, 0 }, /* RB */
 112         { NULL, NULL, 0 }, /* R */
 113         { pre_B, NULL, 0 }, /* B */
 114         { pre_I, NULL, 0 }, /* I */
 115         { pre_alternate, NULL, 0 }, /* IR */
 116         { pre_alternate, NULL, 0 }, /* RI */
 117         { pre_ign, NULL, MAN_NOTEXT }, /* na */
 118         { pre_sp, NULL, MAN_NOTEXT }, /* sp */
 119         { pre_literal, NULL, 0 }, /* nf */
 120         { pre_literal, NULL, 0 }, /* fi */
 121         { NULL, NULL, 0 }, /* RE */
 122         { pre_RS, post_RS, 0 }, /* RS */
 123         { pre_ign, NULL, 0 }, /* DT */
 124         { pre_ign, NULL, 0 }, /* UC */
 125         { pre_ign, NULL, 0 }, /* PD */
 126         { pre_ign, NULL, 0 }, /* AT */
 127         { pre_in, NULL, MAN_NOTEXT }, /* in */
 128         { pre_ft, NULL, MAN_NOTEXT }, /* ft */
 129         { pre_OP, NULL, 0 }, /* OP */




 130 };
 131 
 132 
 133 
 134 void
 135 terminal_man(void *arg, const struct man *man)
 136 {
 137         struct termp            *p;
 138         const struct man_node   *n;
 139         const struct man_meta   *m;
 140         struct mtermp            mt;
 141 
 142         p = (struct termp *)arg;
 143 
 144         if (0 == p->defindent)
 145                 p->defindent = 7;
 146 
 147         p->overstep = 0;
 148         p->maxrmargin = p->defrmargin;
 149         p->tabwidth = term_len(p, 5);
 150 
 151         if (NULL == p->symtab)
 152                 p->symtab = mchars_alloc();
 153 
 154         n = man_node(man);
 155         m = man_meta(man);
 156 
 157         term_begin(p, print_man_head, print_man_foot, m);
 158         p->flags |= TERMP_NOSPACE;
 159 
 160         memset(&mt, 0, sizeof(struct mtermp));
 161 
 162         mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
 163         mt.offset = term_len(p, p->defindent);

 164 
 165         if (n->child)
 166                 print_man_nodelist(p, &mt, n->child, m);
 167 
 168         term_end(p);
 169 }
 170 
 171 
 172 static size_t
 173 a2height(const struct termp *p, const char *cp)
 174 {
 175         struct roffsu    su;
 176 
 177         if ( ! a2roffsu(cp, &su, SCALE_VS))
 178                 SCALE_VS_INIT(&su, atoi(cp));
 179 
 180         return(term_vspan(p, &su));
 181 }
 182 
 183 
 184 static int
 185 a2width(const struct termp *p, const char *cp)
 186 {
 187         struct roffsu    su;
 188 
 189         if ( ! a2roffsu(cp, &su, SCALE_BU))
 190                 return(-1);
 191 
 192         return((int)term_hspan(p, &su));
 193 }
 194 
 195 /*
 196  * Printing leading vertical space before a block.
 197  * This is used for the paragraph macros.
 198  * The rules are pretty simple, since there's very little nesting going
 199  * on here.  Basically, if we're the first within another block (SS/SH),
 200  * then don't emit vertical space.  If we are (RS), then do.  If not the
 201  * first, print it.
 202  */
 203 static void
 204 print_bvspace(struct termp *p, const struct man_node *n)
 205 {

 206 
 207         term_newln(p);
 208 
 209         if (n->body && n->body->child)
 210                 if (MAN_TBL == n->body->child->type)
 211                         return;
 212 
 213         if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
 214                 if (NULL == n->prev)
 215                         return;
 216 

 217         term_vspace(p);
 218 }
 219 
 220 /* ARGSUSED */
 221 static int
 222 pre_ign(DECL_ARGS)
 223 {
 224 
 225         return(0);
 226 }
 227 
 228 
 229 /* ARGSUSED */
 230 static int
 231 pre_I(DECL_ARGS)
 232 {
 233 
 234         term_fontrepl(p, TERMFONT_UNDER);
 235         return(1);
 236 }
 237 
 238 
 239 /* ARGSUSED */
 240 static int
 241 pre_literal(DECL_ARGS)
 242 {
 243 
 244         term_newln(p);
 245 
 246         if (MAN_nf == n->tok)
 247                 mt->fl |= MANT_LITERAL;
 248         else
 249                 mt->fl &= ~MANT_LITERAL;
 250 
 251         /*
 252          * Unlike .IP and .TP, .HP does not have a HEAD.
 253          * So in case a second call to term_flushln() is needed,
 254          * indentation has to be set up explicitly.
 255          */
 256         if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
 257                 p->offset = p->rmargin;
 258                 p->rmargin = p->maxrmargin;
 259                 p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE);

 260                 p->flags |= TERMP_NOSPACE;
 261         }
 262 
 263         return(0);
 264 }
 265 
 266 /* ARGSUSED */
 267 static int















 268 pre_alternate(DECL_ARGS)
 269 {
 270         enum termfont            font[2];
 271         const struct man_node   *nn;
 272         int                      savelit, i;
 273 
 274         switch (n->tok) {
 275         case (MAN_RB):
 276                 font[0] = TERMFONT_NONE;
 277                 font[1] = TERMFONT_BOLD;
 278                 break;
 279         case (MAN_RI):
 280                 font[0] = TERMFONT_NONE;
 281                 font[1] = TERMFONT_UNDER;
 282                 break;
 283         case (MAN_BR):
 284                 font[0] = TERMFONT_BOLD;
 285                 font[1] = TERMFONT_NONE;
 286                 break;
 287         case (MAN_BI):


 290                 break;
 291         case (MAN_IR):
 292                 font[0] = TERMFONT_UNDER;
 293                 font[1] = TERMFONT_NONE;
 294                 break;
 295         case (MAN_IB):
 296                 font[0] = TERMFONT_UNDER;
 297                 font[1] = TERMFONT_BOLD;
 298                 break;
 299         default:
 300                 abort();
 301         }
 302 
 303         savelit = MANT_LITERAL & mt->fl;
 304         mt->fl &= ~MANT_LITERAL;
 305 
 306         for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
 307                 term_fontrepl(p, font[i]);
 308                 if (savelit && NULL == nn->next)
 309                         mt->fl |= MANT_LITERAL;
 310                 print_man_node(p, mt, nn, m);
 311                 if (nn->next)
 312                         p->flags |= TERMP_NOSPACE;
 313         }
 314 
 315         return(0);
 316 }
 317 
 318 /* ARGSUSED */
 319 static int
 320 pre_B(DECL_ARGS)
 321 {
 322 
 323         term_fontrepl(p, TERMFONT_BOLD);
 324         return(1);
 325 }
 326 
 327 /* ARGSUSED */
 328 static int
 329 pre_OP(DECL_ARGS)
 330 {


 421         if (less < 0)
 422                 p->offset -= p->offset > v ? v : p->offset;
 423         else if (less > 0)
 424                 p->offset += v;
 425         else 
 426                 p->offset = v;
 427 
 428         /* Don't let this creep beyond the right margin. */
 429 
 430         if (p->offset > p->rmargin)
 431                 p->offset = p->rmargin;
 432 
 433         return(0);
 434 }
 435 
 436 
 437 /* ARGSUSED */
 438 static int
 439 pre_sp(DECL_ARGS)
 440 {

 441         size_t           i, len;

 442 
 443         if ((NULL == n->prev && n->parent)) {
 444                 if (MAN_SS == n->parent->tok)










 445                         return(0);
 446                 if (MAN_SH == n->parent->tok)
 447                         return(0);
 448         }

 449 

 450         switch (n->tok) {
 451         case (MAN_br):
 452                 len = 0;
 453                 break;
 454         default:
 455                 len = n->child ? a2height(p, n->child->string) : 1;

 456                 break;
 457         }








 458 
 459         if (0 == len)
 460                 term_newln(p);



 461         for (i = 0; i < len; i++)
 462                 term_vspace(p);
 463 
 464         return(0);
 465 }
 466 
 467 
 468 /* ARGSUSED */
 469 static int
 470 pre_HP(DECL_ARGS)
 471 {
 472         size_t                   len, one;
 473         int                      ival;
 474         const struct man_node   *nn;
 475 
 476         switch (n->type) {
 477         case (MAN_BLOCK):
 478                 print_bvspace(p, n);
 479                 return(1);
 480         case (MAN_BODY):
 481                 p->flags |= TERMP_NOBREAK;
 482                 p->flags |= TERMP_TWOSPACE;
 483                 break;
 484         default:
 485                 return(0);
 486         }
 487 





 488         len = mt->lmargin[mt->lmargincur];
 489         ival = -1;
 490 
 491         /* Calculate offset. */
 492 
 493         if (NULL != (nn = n->parent->head->child))
 494                 if ((ival = a2width(p, nn->string)) >= 0)
 495                         len = (size_t)ival;
 496 
 497         one = term_len(p, 1);
 498         if (len < one)
 499                 len = one;
 500 
 501         p->offset = mt->offset;
 502         p->rmargin = mt->offset + len;
 503 
 504         if (ival >= 0)
 505                 mt->lmargin[mt->lmargincur] = (size_t)ival;
 506 
 507         return(1);
 508 }
 509 
 510 
 511 /* ARGSUSED */
 512 static void
 513 post_HP(DECL_ARGS)
 514 {
 515 
 516         switch (n->type) {
 517         case (MAN_BLOCK):
 518                 term_flushln(p);
 519                 break;
 520         case (MAN_BODY):
 521                 term_flushln(p);
 522                 p->flags &= ~TERMP_NOBREAK;
 523                 p->flags &= ~TERMP_TWOSPACE;
 524                 p->offset = mt->offset;
 525                 p->rmargin = p->maxrmargin;
 526                 break;
 527         default:
 528                 break;
 529         }
 530 }
 531 
 532 
 533 /* ARGSUSED */
 534 static int
 535 pre_PP(DECL_ARGS)
 536 {
 537 
 538         switch (n->type) {
 539         case (MAN_BLOCK):
 540                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 541                 print_bvspace(p, n);
 542                 break;
 543         default:
 544                 p->offset = mt->offset;
 545                 break;
 546         }
 547 
 548         return(MAN_HEAD != n->type);
 549 }
 550 
 551 
 552 /* ARGSUSED */
 553 static int
 554 pre_IP(DECL_ARGS)
 555 {
 556         const struct man_node   *nn;
 557         size_t                   len;
 558         int                      savelit, ival;
 559 
 560         switch (n->type) {
 561         case (MAN_BODY):
 562                 p->flags |= TERMP_NOSPACE;
 563                 break;
 564         case (MAN_HEAD):
 565                 p->flags |= TERMP_NOBREAK;

 566                 break;
 567         case (MAN_BLOCK):
 568                 print_bvspace(p, n);
 569                 /* FALLTHROUGH */
 570         default:
 571                 return(1);
 572         }
 573 
 574         len = mt->lmargin[mt->lmargincur];
 575         ival = -1;
 576 
 577         /* Calculate the offset from the optional second argument. */
 578         if (NULL != (nn = n->parent->head->child))
 579                 if (NULL != (nn = nn->next))
 580                         if ((ival = a2width(p, nn->string)) >= 0)
 581                                 len = (size_t)ival;
 582 
 583         switch (n->type) {
 584         case (MAN_HEAD):
 585                 /* Handle zero-width lengths. */
 586                 if (0 == len)
 587                         len = term_len(p, 1);
 588 
 589                 p->offset = mt->offset;
 590                 p->rmargin = mt->offset + len;
 591                 if (ival < 0)
 592                         break;
 593 
 594                 /* Set the saved left-margin. */
 595                 mt->lmargin[mt->lmargincur] = (size_t)ival;
 596 
 597                 savelit = MANT_LITERAL & mt->fl;
 598                 mt->fl &= ~MANT_LITERAL;
 599 
 600                 if (n->child)
 601                         print_man_node(p, mt, n->child, m);
 602 
 603                 if (savelit)
 604                         mt->fl |= MANT_LITERAL;
 605 
 606                 return(0);
 607         case (MAN_BODY):
 608                 p->offset = mt->offset + len;
 609                 p->rmargin = p->maxrmargin;
 610                 break;
 611         default:
 612                 break;
 613         }
 614 
 615         return(1);
 616 }
 617 
 618 
 619 /* ARGSUSED */
 620 static void
 621 post_IP(DECL_ARGS)
 622 {
 623 
 624         switch (n->type) {
 625         case (MAN_HEAD):
 626                 term_flushln(p);
 627                 p->flags &= ~TERMP_NOBREAK;

 628                 p->rmargin = p->maxrmargin;
 629                 break;
 630         case (MAN_BODY):
 631                 term_newln(p);

 632                 break;
 633         default:
 634                 break;
 635         }
 636 }
 637 
 638 
 639 /* ARGSUSED */
 640 static int
 641 pre_TP(DECL_ARGS)
 642 {
 643         const struct man_node   *nn;
 644         size_t                   len;
 645         int                      savelit, ival;
 646 
 647         switch (n->type) {
 648         case (MAN_HEAD):
 649                 p->flags |= TERMP_NOBREAK;

 650                 break;
 651         case (MAN_BODY):
 652                 p->flags |= TERMP_NOSPACE;
 653                 break;
 654         case (MAN_BLOCK):
 655                 print_bvspace(p, n);
 656                 /* FALLTHROUGH */
 657         default:
 658                 return(1);
 659         }
 660 
 661         len = (size_t)mt->lmargin[mt->lmargincur];
 662         ival = -1;
 663 
 664         /* Calculate offset. */
 665 
 666         if (NULL != (nn = n->parent->head->child))
 667                 if (nn->string && nn->parent->line == nn->line)
 668                         if ((ival = a2width(p, nn->string)) >= 0)
 669                                 len = (size_t)ival;
 670 
 671         switch (n->type) {
 672         case (MAN_HEAD):
 673                 /* Handle zero-length properly. */
 674                 if (0 == len)
 675                         len = term_len(p, 1);
 676 
 677                 p->offset = mt->offset;
 678                 p->rmargin = mt->offset + len;
 679 
 680                 savelit = MANT_LITERAL & mt->fl;
 681                 mt->fl &= ~MANT_LITERAL;
 682 
 683                 /* Don't print same-line elements. */
 684                 for (nn = n->child; nn; nn = nn->next)
 685                         if (nn->line > n->line)
 686                                 print_man_node(p, mt, nn, m);
 687 
 688                 if (savelit)
 689                         mt->fl |= MANT_LITERAL;
 690                 if (ival >= 0)
 691                         mt->lmargin[mt->lmargincur] = (size_t)ival;
 692 
 693                 return(0);
 694         case (MAN_BODY):
 695                 p->offset = mt->offset + len;
 696                 p->rmargin = p->maxrmargin;


 697                 break;
 698         default:
 699                 break;
 700         }
 701 
 702         return(1);
 703 }
 704 
 705 
 706 /* ARGSUSED */
 707 static void
 708 post_TP(DECL_ARGS)
 709 {
 710 
 711         switch (n->type) {
 712         case (MAN_HEAD):
 713                 term_flushln(p);
 714                 p->flags &= ~TERMP_NOBREAK;
 715                 p->flags &= ~TERMP_TWOSPACE;
 716                 p->rmargin = p->maxrmargin;
 717                 break;
 718         case (MAN_BODY):
 719                 term_newln(p);

 720                 break;
 721         default:
 722                 break;
 723         }
 724 }
 725 
 726 
 727 /* ARGSUSED */
 728 static int
 729 pre_SS(DECL_ARGS)
 730 {

 731 
 732         switch (n->type) {
 733         case (MAN_BLOCK):
 734                 mt->fl &= ~MANT_LITERAL;
 735                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 736                 mt->offset = term_len(p, p->defindent);
 737                 /* If following a prior empty `SS', no vspace. */
 738                 if (n->prev && MAN_SS == n->prev->tok)
 739                         if (NULL == n->prev->body->child)
 740                                 break;
 741                 if (NULL == n->prev)
 742                         break;

 743                 term_vspace(p);
 744                 break;
 745         case (MAN_HEAD):
 746                 term_fontrepl(p, TERMFONT_BOLD);
 747                 p->offset = term_len(p, p->defindent/2);
 748                 break;
 749         case (MAN_BODY):
 750                 p->offset = mt->offset;
 751                 break;
 752         default:
 753                 break;
 754         }
 755 
 756         return(1);
 757 }
 758 
 759 
 760 /* ARGSUSED */
 761 static void
 762 post_SS(DECL_ARGS)
 763 {
 764         
 765         switch (n->type) {
 766         case (MAN_HEAD):
 767                 term_newln(p);
 768                 break;
 769         case (MAN_BODY):
 770                 term_newln(p);
 771                 break;
 772         default:
 773                 break;
 774         }
 775 }
 776 
 777 
 778 /* ARGSUSED */
 779 static int
 780 pre_SH(DECL_ARGS)
 781 {

 782 
 783         switch (n->type) {
 784         case (MAN_BLOCK):
 785                 mt->fl &= ~MANT_LITERAL;
 786                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 787                 mt->offset = term_len(p, p->defindent);
 788                 /* If following a prior empty `SH', no vspace. */
 789                 if (n->prev && MAN_SH == n->prev->tok)
 790                         if (NULL == n->prev->body->child)
 791                                 break;
 792                 /* If the first macro, no vspae. */
 793                 if (NULL == n->prev)
 794                         break;

 795                 term_vspace(p);
 796                 break;
 797         case (MAN_HEAD):
 798                 term_fontrepl(p, TERMFONT_BOLD);
 799                 p->offset = 0;
 800                 break;
 801         case (MAN_BODY):
 802                 p->offset = mt->offset;
 803                 break;
 804         default:
 805                 break;
 806         }
 807 
 808         return(1);
 809 }
 810 
 811 
 812 /* ARGSUSED */
 813 static void
 814 post_SH(DECL_ARGS)


 873         case (MAN_HEAD):
 874                 return;
 875         default:
 876                 term_newln(p);
 877                 break;
 878         }
 879 
 880         sz = term_len(p, p->defindent);
 881 
 882         if (NULL != (n = n->parent->head->child)) 
 883                 if ((ival = a2width(p, n->string)) >= 0) 
 884                         sz = (size_t)ival;
 885 
 886         mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
 887         p->offset = mt->offset;
 888 
 889         if (--mt->lmarginsz < MAXMARGINS)
 890                 mt->lmargincur = mt->lmarginsz;
 891 }
 892 









 893 static void

















 894 print_man_node(DECL_ARGS)
 895 {
 896         size_t           rm, rmax;
 897         int              c;
 898 
 899         switch (n->type) {
 900         case(MAN_TEXT):
 901                 /*
 902                  * If we have a blank line, output a vertical space.
 903                  * If we have a space as the first character, break
 904                  * before printing the line's data.
 905                  */
 906                 if ('\0' == *n->string) {
 907                         term_vspace(p);
 908                         return;
 909                 } else if (' ' == *n->string && MAN_LINE & n->flags)
 910                         term_newln(p);
 911 
 912                 term_word(p, n->string);

 913 
 914                 /*
 915                  * If we're in a literal context, make sure that words
 916                  * togehter on the same line stay together.  This is a
 917                  * POST-printing call, so we check the NEXT word.  Since
 918                  * -man doesn't have nested macros, we don't need to be
 919                  * more specific than this.
 920                  */
 921                 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
 922                                 (NULL == n->next || 
 923                                  n->next->line > n->line)) {
 924                         rm = p->rmargin;
 925                         rmax = p->maxrmargin;
 926                         p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
 927                         p->flags |= TERMP_NOSPACE;
 928                         term_flushln(p);
 929                         p->rmargin = rm;
 930                         p->maxrmargin = rmax;
 931                 }
 932 
 933                 if (MAN_EOS & n->flags)
 934                         p->flags |= TERMP_SENTENCE;
 935                 return;
 936         case (MAN_EQN):
 937                 term_eqn(p, n->eqn);
 938                 return;
 939         case (MAN_TBL):
 940                 /*
 941                  * Tables are preceded by a newline.  Then process a
 942                  * table line, which will cause line termination,
 943                  */
 944                 if (TBL_SPAN_FIRST & n->span->flags) 
 945                         term_newln(p);
 946                 term_tbl(p, n->span);
 947                 return;
 948         default:
 949                 break;
 950         }
 951 
 952         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
 953                 term_fontrepl(p, TERMFONT_NONE);
 954 
 955         c = 1;
 956         if (termacts[n->tok].pre)
 957                 c = (*termacts[n->tok].pre)(p, mt, n, m);
 958 
 959         if (c && n->child)
 960                 print_man_nodelist(p, mt, n->child, m);
 961 
 962         if (termacts[n->tok].post)
 963                 (*termacts[n->tok].post)(p, mt, n, m);
 964         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
 965                 term_fontrepl(p, TERMFONT_NONE);
 966 

























 967         if (MAN_EOS & n->flags)
 968                 p->flags |= TERMP_SENTENCE;
 969 }
 970 
 971 
 972 static void
 973 print_man_nodelist(DECL_ARGS)
 974 {
 975 
 976         print_man_node(p, mt, n, m);
 977         if ( ! n->next)
 978                 return;
 979         print_man_nodelist(p, mt, n->next, m);
 980 }
 981 
 982 
 983 static void
 984 print_man_foot(struct termp *p, const void *arg)
 985 {
 986         char            title[BUFSIZ];
 987         size_t          datelen;
 988         const struct man_meta *meta;
 989 
 990         meta = (const struct man_meta *)arg;
 991         assert(meta->title);
 992         assert(meta->msec);
 993         assert(meta->date);
 994 
 995         term_fontrepl(p, TERMFONT_NONE);
 996 
 997         term_vspace(p);
 998 
 999         /*
1000          * Temporary, undocumented option to imitate mdoc(7) output.
1001          * In the bottom right corner, use the source instead of
1002          * the title.
1003          */
1004 
1005         if ( ! p->mdocstyle) {
1006                 term_vspace(p);
1007                 term_vspace(p);
1008                 snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1009         } else if (meta->source) {
1010                 strlcpy(title, meta->source, BUFSIZ);
1011         } else {
1012                 title[0] = '\0';
1013         }
1014         datelen = term_strlen(p, meta->date);
1015 
1016         /* Bottom left corner: manual source. */
1017 
1018         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;

1019         p->offset = 0;
1020         p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1021 
1022         if (meta->source)
1023                 term_word(p, meta->source);
1024         term_flushln(p);
1025 
1026         /* At the bottom in the middle: manual date. */
1027 
1028         p->flags |= TERMP_NOSPACE;
1029         p->offset = p->rmargin;
1030         p->rmargin = p->maxrmargin - term_strlen(p, title);
1031         if (p->offset + datelen >= p->rmargin)
1032                 p->rmargin = p->offset + datelen;
1033 
1034         term_word(p, meta->date);
1035         term_flushln(p);
1036 
1037         /* Bottom right corner: manual title and section. */
1038 
1039         p->flags &= ~TERMP_NOBREAK;
1040         p->flags |= TERMP_NOSPACE;

1041         p->offset = p->rmargin;
1042         p->rmargin = p->maxrmargin;
1043 
1044         term_word(p, title);
1045         term_flushln(p);
1046 }
1047 
1048 
1049 static void
1050 print_man_head(struct termp *p, const void *arg)
1051 {
1052         char            buf[BUFSIZ], title[BUFSIZ];
1053         size_t          buflen, titlen;
1054         const struct man_meta *m;
1055 
1056         m = (const struct man_meta *)arg;
1057         assert(m->title);
1058         assert(m->msec);
1059 
1060         if (m->vol)
1061                 strlcpy(buf, m->vol, BUFSIZ);
1062         else
1063                 buf[0] = '\0';
1064         buflen = term_strlen(p, buf);
1065 
1066         /* Top left corner: manual title and section. */
1067 
1068         snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1069         titlen = term_strlen(p, title);
1070 
1071         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;

1072         p->offset = 0;
1073         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1074             (p->maxrmargin - 
1075              term_strlen(p, buf) + term_len(p, 1)) / 2 :
1076             p->maxrmargin - buflen;
1077 
1078         term_word(p, title);
1079         term_flushln(p);
1080 
1081         /* At the top in the middle: manual volume. */
1082 
1083         p->flags |= TERMP_NOSPACE;
1084         p->offset = p->rmargin;
1085         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1086             p->maxrmargin - titlen : p->maxrmargin;
1087 
1088         term_word(p, buf);
1089         term_flushln(p);
1090 
1091         /* Top right corner: title and section, again. */
1092 
1093         p->flags &= ~TERMP_NOBREAK;

1094         if (p->rmargin + titlen <= p->maxrmargin) {
1095                 p->flags |= TERMP_NOSPACE;
1096                 p->offset = p->rmargin;
1097                 p->rmargin = p->maxrmargin;
1098                 term_word(p, title);
1099                 term_flushln(p);
1100         }
1101 
1102         p->flags &= ~TERMP_NOSPACE;
1103         p->offset = 0;
1104         p->rmargin = p->maxrmargin;
1105 
1106         /* 
1107          * Groff prints three blank lines before the content.
1108          * Do the same, except in the temporary, undocumented
1109          * mode imitating mdoc(7) output.
1110          */
1111 
1112         term_vspace(p);
1113         if ( ! p->mdocstyle) {
   1 /*      $Id: man_term.c,v 1.139 2013/12/22 23:34:13 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010, 2011, 2012, 2013 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 MAXMARGINS        64 /* maximum number of indented scopes */
  37 


  38 struct  mtermp {
  39         int               fl;
  40 #define MANT_LITERAL     (1 << 0)
  41         size_t            lmargin[MAXMARGINS]; /* margins (incl. visible page) */
  42         int               lmargincur; /* index of current margin */
  43         int               lmarginsz; /* actual number of nested margins */
  44         size_t            offset; /* default offset to visible page */
  45         int               pardist; /* vert. space before par., unit: [v] */
  46 };
  47 
  48 #define DECL_ARGS         struct termp *p, \
  49                           struct mtermp *mt, \
  50                           const struct man_node *n, \
  51                           const struct man_meta *meta
  52 
  53 struct  termact {
  54         int             (*pre)(DECL_ARGS);
  55         void            (*post)(DECL_ARGS);
  56         int               flags;
  57 #define MAN_NOTEXT       (1 << 0) /* Never has text children. */
  58 };
  59 
  60 static  int               a2width(const struct termp *, const char *);
  61 static  size_t            a2height(const struct termp *, const char *);
  62 
  63 static  void              print_man_nodelist(DECL_ARGS);
  64 static  void              print_man_node(DECL_ARGS);
  65 static  void              print_man_head(struct termp *, const void *);
  66 static  void              print_man_foot(struct termp *, const void *);
  67 static  void              print_bvspace(struct termp *, 
  68                                 const struct man_node *, int);
  69 
  70 static  int               pre_B(DECL_ARGS);
  71 static  int               pre_HP(DECL_ARGS);
  72 static  int               pre_I(DECL_ARGS);
  73 static  int               pre_IP(DECL_ARGS);
  74 static  int               pre_OP(DECL_ARGS);
  75 static  int               pre_PD(DECL_ARGS);
  76 static  int               pre_PP(DECL_ARGS);
  77 static  int               pre_RS(DECL_ARGS);
  78 static  int               pre_SH(DECL_ARGS);
  79 static  int               pre_SS(DECL_ARGS);
  80 static  int               pre_TP(DECL_ARGS);
  81 static  int               pre_UR(DECL_ARGS);
  82 static  int               pre_alternate(DECL_ARGS);
  83 static  int               pre_ft(DECL_ARGS);
  84 static  int               pre_ign(DECL_ARGS);
  85 static  int               pre_in(DECL_ARGS);
  86 static  int               pre_literal(DECL_ARGS);
  87 static  int               pre_sp(DECL_ARGS);
  88 
  89 static  void              post_IP(DECL_ARGS);
  90 static  void              post_HP(DECL_ARGS);
  91 static  void              post_RS(DECL_ARGS);
  92 static  void              post_SH(DECL_ARGS);
  93 static  void              post_SS(DECL_ARGS);
  94 static  void              post_TP(DECL_ARGS);
  95 static  void              post_UR(DECL_ARGS);
  96 
  97 static  const struct termact termacts[MAN_MAX] = {
  98         { pre_sp, NULL, MAN_NOTEXT }, /* br */
  99         { NULL, NULL, 0 }, /* TH */
 100         { pre_SH, post_SH, 0 }, /* SH */
 101         { pre_SS, post_SS, 0 }, /* SS */
 102         { pre_TP, post_TP, 0 }, /* TP */
 103         { pre_PP, NULL, 0 }, /* LP */
 104         { pre_PP, NULL, 0 }, /* PP */
 105         { pre_PP, NULL, 0 }, /* P */
 106         { pre_IP, post_IP, 0 }, /* IP */
 107         { pre_HP, post_HP, 0 }, /* HP */ 
 108         { NULL, NULL, 0 }, /* SM */
 109         { pre_B, NULL, 0 }, /* SB */
 110         { pre_alternate, NULL, 0 }, /* BI */
 111         { pre_alternate, NULL, 0 }, /* IB */
 112         { pre_alternate, NULL, 0 }, /* BR */
 113         { pre_alternate, NULL, 0 }, /* RB */
 114         { NULL, NULL, 0 }, /* R */
 115         { pre_B, NULL, 0 }, /* B */
 116         { pre_I, NULL, 0 }, /* I */
 117         { pre_alternate, NULL, 0 }, /* IR */
 118         { pre_alternate, NULL, 0 }, /* RI */
 119         { pre_ign, NULL, MAN_NOTEXT }, /* na */
 120         { pre_sp, NULL, MAN_NOTEXT }, /* sp */
 121         { pre_literal, NULL, 0 }, /* nf */
 122         { pre_literal, NULL, 0 }, /* fi */
 123         { NULL, NULL, 0 }, /* RE */
 124         { pre_RS, post_RS, 0 }, /* RS */
 125         { pre_ign, NULL, 0 }, /* DT */
 126         { pre_ign, NULL, 0 }, /* UC */
 127         { pre_PD, NULL, MAN_NOTEXT }, /* PD */
 128         { pre_ign, NULL, 0 }, /* AT */
 129         { pre_in, NULL, MAN_NOTEXT }, /* in */
 130         { pre_ft, NULL, MAN_NOTEXT }, /* ft */
 131         { pre_OP, NULL, 0 }, /* OP */
 132         { pre_literal, NULL, 0 }, /* EX */
 133         { pre_literal, NULL, 0 }, /* EE */
 134         { pre_UR, post_UR, 0 }, /* UR */
 135         { NULL, NULL, 0 }, /* UE */
 136 };
 137 
 138 
 139 
 140 void
 141 terminal_man(void *arg, const struct man *man)
 142 {
 143         struct termp            *p;
 144         const struct man_node   *n;
 145         const struct man_meta   *meta;
 146         struct mtermp            mt;
 147 
 148         p = (struct termp *)arg;
 149 
 150         if (0 == p->defindent)
 151                 p->defindent = 7;
 152 
 153         p->overstep = 0;
 154         p->maxrmargin = p->defrmargin;
 155         p->tabwidth = term_len(p, 5);
 156 
 157         if (NULL == p->symtab)
 158                 p->symtab = mchars_alloc();
 159 
 160         n = man_node(man);
 161         meta = man_meta(man);
 162 
 163         term_begin(p, print_man_head, print_man_foot, meta);
 164         p->flags |= TERMP_NOSPACE;
 165 
 166         memset(&mt, 0, sizeof(struct mtermp));
 167 
 168         mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
 169         mt.offset = term_len(p, p->defindent);
 170         mt.pardist = 1;
 171 
 172         if (n->child)
 173                 print_man_nodelist(p, &mt, n->child, meta);
 174 
 175         term_end(p);
 176 }
 177 
 178 
 179 static size_t
 180 a2height(const struct termp *p, const char *cp)
 181 {
 182         struct roffsu    su;
 183 
 184         if ( ! a2roffsu(cp, &su, SCALE_VS))
 185                 SCALE_VS_INIT(&su, atoi(cp));
 186 
 187         return(term_vspan(p, &su));
 188 }
 189 
 190 
 191 static int
 192 a2width(const struct termp *p, const char *cp)
 193 {
 194         struct roffsu    su;
 195 
 196         if ( ! a2roffsu(cp, &su, SCALE_BU))
 197                 return(-1);
 198 
 199         return((int)term_hspan(p, &su));
 200 }
 201 
 202 /*
 203  * Printing leading vertical space before a block.
 204  * This is used for the paragraph macros.
 205  * The rules are pretty simple, since there's very little nesting going
 206  * on here.  Basically, if we're the first within another block (SS/SH),
 207  * then don't emit vertical space.  If we are (RS), then do.  If not the
 208  * first, print it.
 209  */
 210 static void
 211 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
 212 {
 213         int      i;
 214 
 215         term_newln(p);
 216 
 217         if (n->body && n->body->child)
 218                 if (MAN_TBL == n->body->child->type)
 219                         return;
 220 
 221         if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
 222                 if (NULL == n->prev)
 223                         return;
 224 
 225         for (i = 0; i < pardist; i++)
 226                 term_vspace(p);
 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 || MAN_EX == n->tok)
 256                 mt->fl |= MANT_LITERAL;
 257         else
 258                 mt->fl &= ~MANT_LITERAL;
 259 
 260         /*
 261          * Unlike .IP and .TP, .HP does not have a HEAD.
 262          * So in case a second call to term_flushln() is needed,
 263          * indentation has to be set up explicitly.
 264          */
 265         if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
 266                 p->offset = p->rmargin;
 267                 p->rmargin = p->maxrmargin;
 268                 p->trailspace = 0;
 269                 p->flags &= ~TERMP_NOBREAK;
 270                 p->flags |= TERMP_NOSPACE;
 271         }
 272 
 273         return(0);
 274 }
 275 
 276 /* ARGSUSED */
 277 static int
 278 pre_PD(DECL_ARGS)
 279 {
 280 
 281         n = n->child;
 282         if (0 == n) {
 283                 mt->pardist = 1;
 284                 return(0);
 285         }
 286         assert(MAN_TEXT == n->type);
 287         mt->pardist = atoi(n->string);
 288         return(0);
 289 }
 290 
 291 /* ARGSUSED */
 292 static int
 293 pre_alternate(DECL_ARGS)
 294 {
 295         enum termfont            font[2];
 296         const struct man_node   *nn;
 297         int                      savelit, i;
 298 
 299         switch (n->tok) {
 300         case (MAN_RB):
 301                 font[0] = TERMFONT_NONE;
 302                 font[1] = TERMFONT_BOLD;
 303                 break;
 304         case (MAN_RI):
 305                 font[0] = TERMFONT_NONE;
 306                 font[1] = TERMFONT_UNDER;
 307                 break;
 308         case (MAN_BR):
 309                 font[0] = TERMFONT_BOLD;
 310                 font[1] = TERMFONT_NONE;
 311                 break;
 312         case (MAN_BI):


 315                 break;
 316         case (MAN_IR):
 317                 font[0] = TERMFONT_UNDER;
 318                 font[1] = TERMFONT_NONE;
 319                 break;
 320         case (MAN_IB):
 321                 font[0] = TERMFONT_UNDER;
 322                 font[1] = TERMFONT_BOLD;
 323                 break;
 324         default:
 325                 abort();
 326         }
 327 
 328         savelit = MANT_LITERAL & mt->fl;
 329         mt->fl &= ~MANT_LITERAL;
 330 
 331         for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
 332                 term_fontrepl(p, font[i]);
 333                 if (savelit && NULL == nn->next)
 334                         mt->fl |= MANT_LITERAL;
 335                 print_man_node(p, mt, nn, meta);
 336                 if (nn->next)
 337                         p->flags |= TERMP_NOSPACE;
 338         }
 339 
 340         return(0);
 341 }
 342 
 343 /* ARGSUSED */
 344 static int
 345 pre_B(DECL_ARGS)
 346 {
 347 
 348         term_fontrepl(p, TERMFONT_BOLD);
 349         return(1);
 350 }
 351 
 352 /* ARGSUSED */
 353 static int
 354 pre_OP(DECL_ARGS)
 355 {


 446         if (less < 0)
 447                 p->offset -= p->offset > v ? v : p->offset;
 448         else if (less > 0)
 449                 p->offset += v;
 450         else 
 451                 p->offset = v;
 452 
 453         /* Don't let this creep beyond the right margin. */
 454 
 455         if (p->offset > p->rmargin)
 456                 p->offset = p->rmargin;
 457 
 458         return(0);
 459 }
 460 
 461 
 462 /* ARGSUSED */
 463 static int
 464 pre_sp(DECL_ARGS)
 465 {
 466         char            *s;
 467         size_t           i, len;
 468         int              neg;
 469 
 470         if ((NULL == n->prev && n->parent)) {
 471                 switch (n->parent->tok) {
 472                 case (MAN_SH):
 473                         /* FALLTHROUGH */
 474                 case (MAN_SS):
 475                         /* FALLTHROUGH */
 476                 case (MAN_PP):
 477                         /* FALLTHROUGH */
 478                 case (MAN_LP):
 479                         /* FALLTHROUGH */
 480                 case (MAN_P):
 481                         /* FALLTHROUGH */
 482                         return(0);
 483                 default:
 484                         break;
 485                 }
 486         }
 487 
 488         neg = 0;
 489         switch (n->tok) {
 490         case (MAN_br):
 491                 len = 0;
 492                 break;
 493         default:
 494                 if (NULL == n->child) {
 495                         len = 1;
 496                         break;
 497                 }
 498                 s = n->child->string;
 499                 if ('-' == *s) {
 500                         neg = 1;
 501                         s++;
 502                 }
 503                 len = a2height(p, s);
 504                 break;
 505         }
 506 
 507         if (0 == len)
 508                 term_newln(p);
 509         else if (neg)
 510                 p->skipvsp += len;
 511         else
 512                 for (i = 0; i < len; i++)
 513                         term_vspace(p);
 514 
 515         return(0);
 516 }
 517 
 518 
 519 /* ARGSUSED */
 520 static int
 521 pre_HP(DECL_ARGS)
 522 {
 523         size_t                   len, one;
 524         int                      ival;
 525         const struct man_node   *nn;
 526 
 527         switch (n->type) {
 528         case (MAN_BLOCK):
 529                 print_bvspace(p, n, mt->pardist);
 530                 return(1);
 531         case (MAN_BODY):


 532                 break;
 533         default:
 534                 return(0);
 535         }
 536 
 537         if ( ! (MANT_LITERAL & mt->fl)) {
 538                 p->flags |= TERMP_NOBREAK;
 539                 p->trailspace = 2;
 540         }
 541 
 542         len = mt->lmargin[mt->lmargincur];
 543         ival = -1;
 544 
 545         /* Calculate offset. */
 546 
 547         if (NULL != (nn = n->parent->head->child))
 548                 if ((ival = a2width(p, nn->string)) >= 0)
 549                         len = (size_t)ival;
 550 
 551         one = term_len(p, 1);
 552         if (len < one)
 553                 len = one;
 554 
 555         p->offset = mt->offset;
 556         p->rmargin = mt->offset + len;
 557 
 558         if (ival >= 0)
 559                 mt->lmargin[mt->lmargincur] = (size_t)ival;
 560 
 561         return(1);
 562 }
 563 
 564 
 565 /* ARGSUSED */
 566 static void
 567 post_HP(DECL_ARGS)
 568 {
 569 
 570         switch (n->type) {



 571         case (MAN_BODY):
 572                 term_newln(p);
 573                 p->flags &= ~TERMP_NOBREAK;
 574                 p->trailspace = 0;
 575                 p->offset = mt->offset;
 576                 p->rmargin = p->maxrmargin;
 577                 break;
 578         default:
 579                 break;
 580         }
 581 }
 582 
 583 
 584 /* ARGSUSED */
 585 static int
 586 pre_PP(DECL_ARGS)
 587 {
 588 
 589         switch (n->type) {
 590         case (MAN_BLOCK):
 591                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 592                 print_bvspace(p, n, mt->pardist);
 593                 break;
 594         default:
 595                 p->offset = mt->offset;
 596                 break;
 597         }
 598 
 599         return(MAN_HEAD != n->type);
 600 }
 601 
 602 
 603 /* ARGSUSED */
 604 static int
 605 pre_IP(DECL_ARGS)
 606 {
 607         const struct man_node   *nn;
 608         size_t                   len;
 609         int                      savelit, ival;
 610 
 611         switch (n->type) {
 612         case (MAN_BODY):
 613                 p->flags |= TERMP_NOSPACE;
 614                 break;
 615         case (MAN_HEAD):
 616                 p->flags |= TERMP_NOBREAK;
 617                 p->trailspace = 1;
 618                 break;
 619         case (MAN_BLOCK):
 620                 print_bvspace(p, n, mt->pardist);
 621                 /* FALLTHROUGH */
 622         default:
 623                 return(1);
 624         }
 625 
 626         len = mt->lmargin[mt->lmargincur];
 627         ival = -1;
 628 
 629         /* Calculate the offset from the optional second argument. */
 630         if (NULL != (nn = n->parent->head->child))
 631                 if (NULL != (nn = nn->next))
 632                         if ((ival = a2width(p, nn->string)) >= 0)
 633                                 len = (size_t)ival;
 634 
 635         switch (n->type) {
 636         case (MAN_HEAD):
 637                 /* Handle zero-width lengths. */
 638                 if (0 == len)
 639                         len = term_len(p, 1);
 640 
 641                 p->offset = mt->offset;
 642                 p->rmargin = mt->offset + len;
 643                 if (ival < 0)
 644                         break;
 645 
 646                 /* Set the saved left-margin. */
 647                 mt->lmargin[mt->lmargincur] = (size_t)ival;
 648 
 649                 savelit = MANT_LITERAL & mt->fl;
 650                 mt->fl &= ~MANT_LITERAL;
 651 
 652                 if (n->child)
 653                         print_man_node(p, mt, n->child, meta);
 654 
 655                 if (savelit)
 656                         mt->fl |= MANT_LITERAL;
 657 
 658                 return(0);
 659         case (MAN_BODY):
 660                 p->offset = mt->offset + len;
 661                 p->rmargin = p->maxrmargin;
 662                 break;
 663         default:
 664                 break;
 665         }
 666 
 667         return(1);
 668 }
 669 
 670 
 671 /* ARGSUSED */
 672 static void
 673 post_IP(DECL_ARGS)
 674 {
 675 
 676         switch (n->type) {
 677         case (MAN_HEAD):
 678                 term_flushln(p);
 679                 p->flags &= ~TERMP_NOBREAK;
 680                 p->trailspace = 0;
 681                 p->rmargin = p->maxrmargin;
 682                 break;
 683         case (MAN_BODY):
 684                 term_newln(p);
 685                 p->offset = mt->offset;
 686                 break;
 687         default:
 688                 break;
 689         }
 690 }
 691 
 692 
 693 /* ARGSUSED */
 694 static int
 695 pre_TP(DECL_ARGS)
 696 {
 697         const struct man_node   *nn;
 698         size_t                   len;
 699         int                      savelit, ival;
 700 
 701         switch (n->type) {
 702         case (MAN_HEAD):
 703                 p->flags |= TERMP_NOBREAK;
 704                 p->trailspace = 1;
 705                 break;
 706         case (MAN_BODY):
 707                 p->flags |= TERMP_NOSPACE;
 708                 break;
 709         case (MAN_BLOCK):
 710                 print_bvspace(p, n, mt->pardist);
 711                 /* FALLTHROUGH */
 712         default:
 713                 return(1);
 714         }
 715 
 716         len = (size_t)mt->lmargin[mt->lmargincur];
 717         ival = -1;
 718 
 719         /* Calculate offset. */
 720 
 721         if (NULL != (nn = n->parent->head->child))
 722                 if (nn->string && nn->parent->line == nn->line)
 723                         if ((ival = a2width(p, nn->string)) >= 0)
 724                                 len = (size_t)ival;
 725 
 726         switch (n->type) {
 727         case (MAN_HEAD):
 728                 /* Handle zero-length properly. */
 729                 if (0 == len)
 730                         len = term_len(p, 1);
 731 
 732                 p->offset = mt->offset;
 733                 p->rmargin = mt->offset + len;
 734 
 735                 savelit = MANT_LITERAL & mt->fl;
 736                 mt->fl &= ~MANT_LITERAL;
 737 
 738                 /* Don't print same-line elements. */
 739                 for (nn = n->child; nn; nn = nn->next)
 740                         if (nn->line > n->line)
 741                                 print_man_node(p, mt, nn, meta);
 742 
 743                 if (savelit)
 744                         mt->fl |= MANT_LITERAL;
 745                 if (ival >= 0)
 746                         mt->lmargin[mt->lmargincur] = (size_t)ival;
 747 
 748                 return(0);
 749         case (MAN_BODY):
 750                 p->offset = mt->offset + len;
 751                 p->rmargin = p->maxrmargin;
 752                 p->trailspace = 0;
 753                 p->flags &= ~TERMP_NOBREAK;
 754                 break;
 755         default:
 756                 break;
 757         }
 758 
 759         return(1);
 760 }
 761 
 762 
 763 /* ARGSUSED */
 764 static void
 765 post_TP(DECL_ARGS)
 766 {
 767 
 768         switch (n->type) {
 769         case (MAN_HEAD):
 770                 term_flushln(p);



 771                 break;
 772         case (MAN_BODY):
 773                 term_newln(p);
 774                 p->offset = mt->offset;
 775                 break;
 776         default:
 777                 break;
 778         }
 779 }
 780 
 781 
 782 /* ARGSUSED */
 783 static int
 784 pre_SS(DECL_ARGS)
 785 {
 786         int      i;
 787 
 788         switch (n->type) {
 789         case (MAN_BLOCK):
 790                 mt->fl &= ~MANT_LITERAL;
 791                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 792                 mt->offset = term_len(p, p->defindent);
 793                 /* If following a prior empty `SS', no vspace. */
 794                 if (n->prev && MAN_SS == n->prev->tok)
 795                         if (NULL == n->prev->body->child)
 796                                 break;
 797                 if (NULL == n->prev)
 798                         break;
 799                 for (i = 0; i < mt->pardist; i++)
 800                         term_vspace(p);
 801                 break;
 802         case (MAN_HEAD):
 803                 term_fontrepl(p, TERMFONT_BOLD);
 804                 p->offset = term_len(p, 3);
 805                 break;
 806         case (MAN_BODY):
 807                 p->offset = mt->offset;
 808                 break;
 809         default:
 810                 break;
 811         }
 812 
 813         return(1);
 814 }
 815 
 816 
 817 /* ARGSUSED */
 818 static void
 819 post_SS(DECL_ARGS)
 820 {
 821         
 822         switch (n->type) {
 823         case (MAN_HEAD):
 824                 term_newln(p);
 825                 break;
 826         case (MAN_BODY):
 827                 term_newln(p);
 828                 break;
 829         default:
 830                 break;
 831         }
 832 }
 833 
 834 
 835 /* ARGSUSED */
 836 static int
 837 pre_SH(DECL_ARGS)
 838 {
 839         int      i;
 840 
 841         switch (n->type) {
 842         case (MAN_BLOCK):
 843                 mt->fl &= ~MANT_LITERAL;
 844                 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 845                 mt->offset = term_len(p, p->defindent);
 846                 /* If following a prior empty `SH', no vspace. */
 847                 if (n->prev && MAN_SH == n->prev->tok)
 848                         if (NULL == n->prev->body->child)
 849                                 break;
 850                 /* If the first macro, no vspae. */
 851                 if (NULL == n->prev)
 852                         break;
 853                 for (i = 0; i < mt->pardist; i++)
 854                         term_vspace(p);
 855                 break;
 856         case (MAN_HEAD):
 857                 term_fontrepl(p, TERMFONT_BOLD);
 858                 p->offset = 0;
 859                 break;
 860         case (MAN_BODY):
 861                 p->offset = mt->offset;
 862                 break;
 863         default:
 864                 break;
 865         }
 866 
 867         return(1);
 868 }
 869 
 870 
 871 /* ARGSUSED */
 872 static void
 873 post_SH(DECL_ARGS)


 932         case (MAN_HEAD):
 933                 return;
 934         default:
 935                 term_newln(p);
 936                 break;
 937         }
 938 
 939         sz = term_len(p, p->defindent);
 940 
 941         if (NULL != (n = n->parent->head->child)) 
 942                 if ((ival = a2width(p, n->string)) >= 0) 
 943                         sz = (size_t)ival;
 944 
 945         mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
 946         p->offset = mt->offset;
 947 
 948         if (--mt->lmarginsz < MAXMARGINS)
 949                 mt->lmargincur = mt->lmarginsz;
 950 }
 951 
 952 /* ARGSUSED */
 953 static int
 954 pre_UR(DECL_ARGS)
 955 {
 956 
 957         return (MAN_HEAD != n->type);
 958 }
 959 
 960 /* ARGSUSED */
 961 static void
 962 post_UR(DECL_ARGS)
 963 {
 964 
 965         if (MAN_BLOCK != n->type)
 966                 return;
 967 
 968         term_word(p, "<");
 969         p->flags |= TERMP_NOSPACE;
 970 
 971         if (NULL != n->child->child)
 972                 print_man_node(p, mt, n->child->child, meta);
 973 
 974         p->flags |= TERMP_NOSPACE;
 975         term_word(p, ">");
 976 }
 977 
 978 static void
 979 print_man_node(DECL_ARGS)
 980 {
 981         size_t           rm, rmax;
 982         int              c;
 983 
 984         switch (n->type) {
 985         case(MAN_TEXT):
 986                 /*
 987                  * If we have a blank line, output a vertical space.
 988                  * If we have a space as the first character, break
 989                  * before printing the line's data.
 990                  */
 991                 if ('\0' == *n->string) {
 992                         term_vspace(p);
 993                         return;
 994                 } else if (' ' == *n->string && MAN_LINE & n->flags)
 995                         term_newln(p);
 996 
 997                 term_word(p, n->string);
 998                 goto out;
 999 






















1000         case (MAN_EQN):
1001                 term_eqn(p, n->eqn);
1002                 return;
1003         case (MAN_TBL):
1004                 /*
1005                  * Tables are preceded by a newline.  Then process a
1006                  * table line, which will cause line termination,
1007                  */
1008                 if (TBL_SPAN_FIRST & n->span->flags) 
1009                         term_newln(p);
1010                 term_tbl(p, n->span);
1011                 return;
1012         default:
1013                 break;
1014         }
1015 
1016         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1017                 term_fontrepl(p, TERMFONT_NONE);
1018 
1019         c = 1;
1020         if (termacts[n->tok].pre)
1021                 c = (*termacts[n->tok].pre)(p, mt, n, meta);
1022 
1023         if (c && n->child)
1024                 print_man_nodelist(p, mt, n->child, meta);
1025 
1026         if (termacts[n->tok].post)
1027                 (*termacts[n->tok].post)(p, mt, n, meta);
1028         if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
1029                 term_fontrepl(p, TERMFONT_NONE);
1030 
1031 out:
1032         /*
1033          * If we're in a literal context, make sure that words
1034          * together on the same line stay together.  This is a
1035          * POST-printing call, so we check the NEXT word.  Since
1036          * -man doesn't have nested macros, we don't need to be
1037          * more specific than this.
1038          */
1039         if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
1040             (NULL == n->next || n->next->line > n->line)) {
1041                 rm = p->rmargin;
1042                 rmax = p->maxrmargin;
1043                 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1044                 p->flags |= TERMP_NOSPACE;
1045                 if (NULL != n->string && '\0' != *n->string)
1046                         term_flushln(p);
1047                 else
1048                         term_newln(p);
1049                 if (rm < rmax && n->parent->tok == MAN_HP) {
1050                         p->offset = rm;
1051                         p->rmargin = rmax;
1052                 } else
1053                         p->rmargin = rm;
1054                 p->maxrmargin = rmax;
1055         }
1056         if (MAN_EOS & n->flags)
1057                 p->flags |= TERMP_SENTENCE;
1058 }
1059 
1060 
1061 static void
1062 print_man_nodelist(DECL_ARGS)
1063 {
1064 
1065         print_man_node(p, mt, n, meta);
1066         if ( ! n->next)
1067                 return;
1068         print_man_nodelist(p, mt, n->next, meta);
1069 }
1070 
1071 
1072 static void
1073 print_man_foot(struct termp *p, const void *arg)
1074 {
1075         char            title[BUFSIZ];
1076         size_t          datelen;
1077         const struct man_meta *meta;
1078 
1079         meta = (const struct man_meta *)arg;
1080         assert(meta->title);
1081         assert(meta->msec);
1082         assert(meta->date);
1083 
1084         term_fontrepl(p, TERMFONT_NONE);
1085 
1086         term_vspace(p);
1087 
1088         /*
1089          * Temporary, undocumented option to imitate mdoc(7) output.
1090          * In the bottom right corner, use the source instead of
1091          * the title.
1092          */
1093 
1094         if ( ! p->mdocstyle) {
1095                 term_vspace(p);
1096                 term_vspace(p);
1097                 snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1098         } else if (meta->source) {
1099                 strlcpy(title, meta->source, BUFSIZ);
1100         } else {
1101                 title[0] = '\0';
1102         }
1103         datelen = term_strlen(p, meta->date);
1104 
1105         /* Bottom left corner: manual source. */
1106 
1107         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1108         p->trailspace = 1;
1109         p->offset = 0;
1110         p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1111 
1112         if (meta->source)
1113                 term_word(p, meta->source);
1114         term_flushln(p);
1115 
1116         /* At the bottom in the middle: manual date. */
1117 
1118         p->flags |= TERMP_NOSPACE;
1119         p->offset = p->rmargin;
1120         p->rmargin = p->maxrmargin - term_strlen(p, title);
1121         if (p->offset + datelen >= p->rmargin)
1122                 p->rmargin = p->offset + datelen;
1123 
1124         term_word(p, meta->date);
1125         term_flushln(p);
1126 
1127         /* Bottom right corner: manual title and section. */
1128 
1129         p->flags &= ~TERMP_NOBREAK;
1130         p->flags |= TERMP_NOSPACE;
1131         p->trailspace = 0;
1132         p->offset = p->rmargin;
1133         p->rmargin = p->maxrmargin;
1134 
1135         term_word(p, title);
1136         term_flushln(p);
1137 }
1138 
1139 
1140 static void
1141 print_man_head(struct termp *p, const void *arg)
1142 {
1143         char            buf[BUFSIZ], title[BUFSIZ];
1144         size_t          buflen, titlen;
1145         const struct man_meta *meta;
1146 
1147         meta = (const struct man_meta *)arg;
1148         assert(meta->title);
1149         assert(meta->msec);
1150 
1151         if (meta->vol)
1152                 strlcpy(buf, meta->vol, BUFSIZ);
1153         else
1154                 buf[0] = '\0';
1155         buflen = term_strlen(p, buf);
1156 
1157         /* Top left corner: manual title and section. */
1158 
1159         snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1160         titlen = term_strlen(p, title);
1161 
1162         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1163         p->trailspace = 1;
1164         p->offset = 0;
1165         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1166             (p->maxrmargin - 
1167              term_strlen(p, buf) + term_len(p, 1)) / 2 :
1168             p->maxrmargin - buflen;
1169 
1170         term_word(p, title);
1171         term_flushln(p);
1172 
1173         /* At the top in the middle: manual volume. */
1174 
1175         p->flags |= TERMP_NOSPACE;
1176         p->offset = p->rmargin;
1177         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1178             p->maxrmargin - titlen : p->maxrmargin;
1179 
1180         term_word(p, buf);
1181         term_flushln(p);
1182 
1183         /* Top right corner: title and section, again. */
1184 
1185         p->flags &= ~TERMP_NOBREAK;
1186         p->trailspace = 0;
1187         if (p->rmargin + titlen <= p->maxrmargin) {
1188                 p->flags |= TERMP_NOSPACE;
1189                 p->offset = p->rmargin;
1190                 p->rmargin = p->maxrmargin;
1191                 term_word(p, title);
1192                 term_flushln(p);
1193         }
1194 
1195         p->flags &= ~TERMP_NOSPACE;
1196         p->offset = 0;
1197         p->rmargin = p->maxrmargin;
1198 
1199         /* 
1200          * Groff prints three blank lines before the content.
1201          * Do the same, except in the temporary, undocumented
1202          * mode imitating mdoc(7) output.
1203          */
1204 
1205         term_vspace(p);
1206         if ( ! p->mdocstyle) {