Print this page
Update to 1.12.3.
   1 /*      $Id: mdoc_term.c,v 1.238 2011/11/13 13:15:14 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010 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 <stdint.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 
  31 #include "mandoc.h"
  32 #include "out.h"
  33 #include "term.h"
  34 #include "mdoc.h"
  35 #include "main.h"
  36 
  37 struct  termpair {
  38         struct termpair  *ppair;
  39         int               count;
  40 };
  41 
  42 #define DECL_ARGS struct termp *p, \
  43                   struct termpair *pair, \
  44                   const struct mdoc_meta *m, \
  45                   const struct mdoc_node *n
  46 
  47 struct  termact {
  48         int     (*pre)(DECL_ARGS);
  49         void    (*post)(DECL_ARGS);
  50 };
  51 
  52 static  size_t    a2width(const struct termp *, const char *);
  53 static  size_t    a2height(const struct termp *, const char *);
  54 static  size_t    a2offs(const struct termp *, const char *);
  55 
  56 static  void      print_bvspace(struct termp *,
  57                         const struct mdoc_node *,
  58                         const struct mdoc_node *);
  59 static  void      print_mdoc_node(DECL_ARGS);
  60 static  void      print_mdoc_nodelist(DECL_ARGS);
  61 static  void      print_mdoc_head(struct termp *, const void *);
  62 static  void      print_mdoc_foot(struct termp *, const void *);
  63 static  void      synopsis_pre(struct termp *, 
  64                         const struct mdoc_node *);
  65 
  66 static  void      termp____post(DECL_ARGS);
  67 static  void      termp__t_post(DECL_ARGS);
  68 static  void      termp_an_post(DECL_ARGS);
  69 static  void      termp_bd_post(DECL_ARGS);
  70 static  void      termp_bk_post(DECL_ARGS);
  71 static  void      termp_bl_post(DECL_ARGS);
  72 static  void      termp_d1_post(DECL_ARGS);
  73 static  void      termp_fo_post(DECL_ARGS);
  74 static  void      termp_in_post(DECL_ARGS);
  75 static  void      termp_it_post(DECL_ARGS);
  76 static  void      termp_lb_post(DECL_ARGS);
  77 static  void      termp_nm_post(DECL_ARGS);
  78 static  void      termp_pf_post(DECL_ARGS);
  79 static  void      termp_quote_post(DECL_ARGS);
  80 static  void      termp_sh_post(DECL_ARGS);
  81 static  void      termp_ss_post(DECL_ARGS);
  82 
  83 static  int       termp__a_pre(DECL_ARGS);
  84 static  int       termp__t_pre(DECL_ARGS);
  85 static  int       termp_an_pre(DECL_ARGS);
  86 static  int       termp_ap_pre(DECL_ARGS);
  87 static  int       termp_bd_pre(DECL_ARGS);
  88 static  int       termp_bf_pre(DECL_ARGS);
  89 static  int       termp_bk_pre(DECL_ARGS);
  90 static  int       termp_bl_pre(DECL_ARGS);
  91 static  int       termp_bold_pre(DECL_ARGS);
  92 static  int       termp_bt_pre(DECL_ARGS);
  93 static  int       termp_bx_pre(DECL_ARGS);
  94 static  int       termp_cd_pre(DECL_ARGS);
  95 static  int       termp_d1_pre(DECL_ARGS);
  96 static  int       termp_ex_pre(DECL_ARGS);
  97 static  int       termp_fa_pre(DECL_ARGS);
  98 static  int       termp_fd_pre(DECL_ARGS);
  99 static  int       termp_fl_pre(DECL_ARGS);
 100 static  int       termp_fn_pre(DECL_ARGS);
 101 static  int       termp_fo_pre(DECL_ARGS);
 102 static  int       termp_ft_pre(DECL_ARGS);
 103 static  int       termp_igndelim_pre(DECL_ARGS);
 104 static  int       termp_in_pre(DECL_ARGS);
 105 static  int       termp_it_pre(DECL_ARGS);
 106 static  int       termp_li_pre(DECL_ARGS);
 107 static  int       termp_lk_pre(DECL_ARGS);
 108 static  int       termp_nd_pre(DECL_ARGS);
 109 static  int       termp_nm_pre(DECL_ARGS);
 110 static  int       termp_ns_pre(DECL_ARGS);
 111 static  int       termp_quote_pre(DECL_ARGS);
 112 static  int       termp_rs_pre(DECL_ARGS);
 113 static  int       termp_rv_pre(DECL_ARGS);
 114 static  int       termp_sh_pre(DECL_ARGS);
 115 static  int       termp_sm_pre(DECL_ARGS);
 116 static  int       termp_sp_pre(DECL_ARGS);
 117 static  int       termp_ss_pre(DECL_ARGS);
 118 static  int       termp_under_pre(DECL_ARGS);
 119 static  int       termp_ud_pre(DECL_ARGS);
 120 static  int       termp_vt_pre(DECL_ARGS);
 121 static  int       termp_xr_pre(DECL_ARGS);
 122 static  int       termp_xx_pre(DECL_ARGS);
 123 
 124 static  const struct termact termacts[MDOC_MAX] = {
 125         { termp_ap_pre, NULL }, /* Ap */
 126         { NULL, NULL }, /* Dd */
 127         { NULL, NULL }, /* Dt */
 128         { NULL, NULL }, /* Os */
 129         { termp_sh_pre, termp_sh_post }, /* Sh */
 130         { termp_ss_pre, termp_ss_post }, /* Ss */ 
 131         { termp_sp_pre, NULL }, /* Pp */ 
 132         { termp_d1_pre, termp_d1_post }, /* D1 */
 133         { termp_d1_pre, termp_d1_post }, /* Dl */
 134         { termp_bd_pre, termp_bd_post }, /* Bd */
 135         { NULL, NULL }, /* Ed */
 136         { termp_bl_pre, termp_bl_post }, /* Bl */
 137         { NULL, NULL }, /* El */
 138         { termp_it_pre, termp_it_post }, /* It */
 139         { termp_under_pre, NULL }, /* Ad */ 
 140         { termp_an_pre, termp_an_post }, /* An */
 141         { termp_under_pre, NULL }, /* Ar */
 142         { termp_cd_pre, NULL }, /* Cd */
 143         { termp_bold_pre, NULL }, /* Cm */
 144         { NULL, NULL }, /* Dv */ 
 145         { NULL, NULL }, /* Er */ 
 146         { NULL, NULL }, /* Ev */ 
 147         { termp_ex_pre, NULL }, /* Ex */
 148         { termp_fa_pre, NULL }, /* Fa */ 
 149         { termp_fd_pre, NULL }, /* Fd */ 
 150         { termp_fl_pre, NULL }, /* Fl */
 151         { termp_fn_pre, NULL }, /* Fn */ 
 152         { termp_ft_pre, NULL }, /* Ft */ 
 153         { termp_bold_pre, NULL }, /* Ic */ 
 154         { termp_in_pre, termp_in_post }, /* In */ 
 155         { termp_li_pre, NULL }, /* Li */
 156         { termp_nd_pre, NULL }, /* Nd */ 
 157         { termp_nm_pre, termp_nm_post }, /* Nm */ 
 158         { termp_quote_pre, termp_quote_post }, /* Op */
 159         { NULL, NULL }, /* Ot */
 160         { termp_under_pre, NULL }, /* Pa */
 161         { termp_rv_pre, NULL }, /* Rv */
 162         { NULL, NULL }, /* St */ 
 163         { termp_under_pre, NULL }, /* Va */
 164         { termp_vt_pre, NULL }, /* Vt */
 165         { termp_xr_pre, NULL }, /* Xr */
 166         { termp__a_pre, termp____post }, /* %A */
 167         { termp_under_pre, termp____post }, /* %B */
 168         { NULL, termp____post }, /* %D */
 169         { termp_under_pre, termp____post }, /* %I */


 177         { NULL, NULL }, /* Ac */
 178         { termp_quote_pre, termp_quote_post }, /* Ao */
 179         { termp_quote_pre, termp_quote_post }, /* Aq */
 180         { NULL, NULL }, /* At */
 181         { NULL, NULL }, /* Bc */
 182         { termp_bf_pre, NULL }, /* Bf */ 
 183         { termp_quote_pre, termp_quote_post }, /* Bo */
 184         { termp_quote_pre, termp_quote_post }, /* Bq */
 185         { termp_xx_pre, NULL }, /* Bsx */
 186         { termp_bx_pre, NULL }, /* Bx */
 187         { NULL, NULL }, /* Db */
 188         { NULL, NULL }, /* Dc */
 189         { termp_quote_pre, termp_quote_post }, /* Do */
 190         { termp_quote_pre, termp_quote_post }, /* Dq */
 191         { NULL, NULL }, /* Ec */ /* FIXME: no space */
 192         { NULL, NULL }, /* Ef */
 193         { termp_under_pre, NULL }, /* Em */ 
 194         { termp_quote_pre, termp_quote_post }, /* Eo */
 195         { termp_xx_pre, NULL }, /* Fx */
 196         { termp_bold_pre, NULL }, /* Ms */
 197         { termp_igndelim_pre, NULL }, /* No */
 198         { termp_ns_pre, NULL }, /* Ns */
 199         { termp_xx_pre, NULL }, /* Nx */
 200         { termp_xx_pre, NULL }, /* Ox */
 201         { NULL, NULL }, /* Pc */
 202         { termp_igndelim_pre, termp_pf_post }, /* Pf */
 203         { termp_quote_pre, termp_quote_post }, /* Po */
 204         { termp_quote_pre, termp_quote_post }, /* Pq */
 205         { NULL, NULL }, /* Qc */
 206         { termp_quote_pre, termp_quote_post }, /* Ql */
 207         { termp_quote_pre, termp_quote_post }, /* Qo */
 208         { termp_quote_pre, termp_quote_post }, /* Qq */
 209         { NULL, NULL }, /* Re */
 210         { termp_rs_pre, NULL }, /* Rs */
 211         { NULL, NULL }, /* Sc */
 212         { termp_quote_pre, termp_quote_post }, /* So */
 213         { termp_quote_pre, termp_quote_post }, /* Sq */
 214         { termp_sm_pre, NULL }, /* Sm */
 215         { termp_under_pre, NULL }, /* Sx */
 216         { termp_bold_pre, NULL }, /* Sy */
 217         { NULL, NULL }, /* Tn */
 218         { termp_xx_pre, NULL }, /* Ux */
 219         { NULL, NULL }, /* Xc */
 220         { NULL, NULL }, /* Xo */
 221         { termp_fo_pre, termp_fo_post }, /* Fo */ 
 222         { NULL, NULL }, /* Fc */ 


 225         { termp_bk_pre, termp_bk_post }, /* Bk */
 226         { NULL, NULL }, /* Ek */
 227         { termp_bt_pre, NULL }, /* Bt */
 228         { NULL, NULL }, /* Hf */
 229         { NULL, NULL }, /* Fr */
 230         { termp_ud_pre, NULL }, /* Ud */
 231         { NULL, termp_lb_post }, /* Lb */
 232         { termp_sp_pre, NULL }, /* Lp */ 
 233         { termp_lk_pre, NULL }, /* Lk */ 
 234         { termp_under_pre, NULL }, /* Mt */ 
 235         { termp_quote_pre, termp_quote_post }, /* Brq */ 
 236         { termp_quote_pre, termp_quote_post }, /* Bro */ 
 237         { NULL, NULL }, /* Brc */ 
 238         { NULL, termp____post }, /* %C */ 
 239         { NULL, NULL }, /* Es */ /* TODO */
 240         { NULL, NULL }, /* En */ /* TODO */
 241         { termp_xx_pre, NULL }, /* Dx */ 
 242         { NULL, termp____post }, /* %Q */ 
 243         { termp_sp_pre, NULL }, /* br */
 244         { termp_sp_pre, NULL }, /* sp */ 
 245         { termp_under_pre, termp____post }, /* %U */ 
 246         { NULL, NULL }, /* Ta */ 
 247 };
 248 
 249 
 250 void
 251 terminal_mdoc(void *arg, const struct mdoc *mdoc)
 252 {
 253         const struct mdoc_node  *n;
 254         const struct mdoc_meta  *m;
 255         struct termp            *p;
 256 
 257         p = (struct termp *)arg;
 258 
 259         if (0 == p->defindent)
 260                 p->defindent = 5;
 261 
 262         p->overstep = 0;
 263         p->maxrmargin = p->defrmargin;
 264         p->tabwidth = term_len(p, 5);
 265 
 266         if (NULL == p->symtab)
 267                 p->symtab = mchars_alloc();
 268 
 269         n = mdoc_node(mdoc);
 270         m = mdoc_meta(mdoc);
 271 
 272         term_begin(p, print_mdoc_head, print_mdoc_foot, m);
 273 
 274         if (n->child)
 275                 print_mdoc_nodelist(p, NULL, m, n->child);
 276 
 277         term_end(p);
 278 }
 279 
 280 
 281 static void
 282 print_mdoc_nodelist(DECL_ARGS)
 283 {
 284 
 285         print_mdoc_node(p, pair, m, n);
 286         if (n->next)
 287                 print_mdoc_nodelist(p, pair, m, n->next);
 288 }
 289 
 290 
 291 /* ARGSUSED */
 292 static void
 293 print_mdoc_node(DECL_ARGS)
 294 {
 295         int              chld;
 296         const void      *font;
 297         struct termpair  npair;
 298         size_t           offset, rmargin;
 299 
 300         chld = 1;
 301         offset = p->offset;
 302         rmargin = p->rmargin;
 303         font = term_fontq(p);
 304 
 305         memset(&npair, 0, sizeof(struct termpair));
 306         npair.ppair = pair;
 307 
 308         /*
 309          * Keeps only work until the end of a line.  If a keep was
 310          * invoked in a prior line, revert it to PREKEEP.
 311          *
 312          * Also let SYNPRETTY sections behave as if they were wrapped
 313          * in a `Bk' block.
 314          */
 315 
 316         if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) {
 317                 if (n->prev && n->prev->line != n->line) {

 318                         p->flags &= ~TERMP_KEEP;
 319                         p->flags |= TERMP_PREKEEP;
 320                 } else if (NULL == n->prev) {
 321                         if (n->parent && n->parent->line != n->line) {
 322                                 p->flags &= ~TERMP_KEEP;
 323                                 p->flags |= TERMP_PREKEEP;
 324                         }
 325                 }
 326         }
 327 
 328         /*
 329          * Since SYNPRETTY sections aren't "turned off" with `Ek',
 330          * we have to intuit whether we should disable formatting.
 331          */
 332 
 333         if ( ! (MDOC_SYNPRETTY & n->flags) &&
 334             ((n->prev   && MDOC_SYNPRETTY & n->prev->flags) ||
 335              (n->parent && MDOC_SYNPRETTY & n->parent->flags)))
 336                 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
 337 
 338         /*
 339          * After the keep flags have been set up, we may now
 340          * produce output.  Note that some pre-handlers do so.
 341          */
 342 
 343         switch (n->type) {
 344         case (MDOC_TEXT):
 345                 if (' ' == *n->string && MDOC_LINE & n->flags)
 346                         term_newln(p);
 347                 if (MDOC_DELIMC & n->flags)
 348                         p->flags |= TERMP_NOSPACE;
 349                 term_word(p, n->string);
 350                 if (MDOC_DELIMO & n->flags)
 351                         p->flags |= TERMP_NOSPACE;
 352                 break;
 353         case (MDOC_EQN):
 354                 term_eqn(p, n->eqn);
 355                 break;
 356         case (MDOC_TBL):
 357                 term_tbl(p, n->span);
 358                 break;
 359         default:
 360                 if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
 361                         chld = (*termacts[n->tok].pre)
 362                                 (p, &npair, m, n);
 363                 break;
 364         }
 365 
 366         if (chld && n->child)
 367                 print_mdoc_nodelist(p, &npair, m, n->child);
 368 
 369         term_fontpopq(p, font);

 370 
 371         switch (n->type) {
 372         case (MDOC_TEXT):
 373                 break;
 374         case (MDOC_TBL):
 375                 break;
 376         case (MDOC_EQN):
 377                 break;
 378         default:
 379                 if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
 380                         break;
 381                 (void)(*termacts[n->tok].post)(p, &npair, m, n);
 382 
 383                 /*
 384                  * Explicit end tokens not only call the post
 385                  * handler, but also tell the respective block
 386                  * that it must not call the post handler again.
 387                  */
 388                 if (ENDBODY_NOT != n->end)
 389                         n->pending->flags |= MDOC_ENDED;
 390 
 391                 /*
 392                  * End of line terminating an implicit block
 393                  * while an explicit block is still open.
 394                  * Continue the explicit block without spacing.
 395                  */
 396                 if (ENDBODY_NOSPACE == n->end)
 397                         p->flags |= TERMP_NOSPACE;
 398                 break;
 399         }
 400 
 401         if (MDOC_EOS & n->flags)
 402                 p->flags |= TERMP_SENTENCE;
 403 
 404         p->offset = offset;
 405         p->rmargin = rmargin;
 406 }
 407 
 408 
 409 static void
 410 print_mdoc_foot(struct termp *p, const void *arg)
 411 {
 412         const struct mdoc_meta *m;
 413 
 414         m = (const struct mdoc_meta *)arg;
 415 
 416         term_fontrepl(p, TERMFONT_NONE);
 417 
 418         /* 
 419          * Output the footer in new-groff style, that is, three columns
 420          * with the middle being the manual date and flanking columns
 421          * being the operating system:
 422          *
 423          * SYSTEM                  DATE                    SYSTEM
 424          */
 425 
 426         term_vspace(p);
 427 
 428         p->offset = 0;
 429         p->rmargin = (p->maxrmargin - 
 430                         term_strlen(p, m->date) + term_len(p, 1)) / 2;

 431         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
 432 
 433         term_word(p, m->os);
 434         term_flushln(p);
 435 
 436         p->offset = p->rmargin;
 437         p->rmargin = p->maxrmargin - term_strlen(p, m->os);
 438         p->flags |= TERMP_NOSPACE;
 439 
 440         term_word(p, m->date);
 441         term_flushln(p);
 442 
 443         p->offset = p->rmargin;
 444         p->rmargin = p->maxrmargin;

 445         p->flags &= ~TERMP_NOBREAK;
 446         p->flags |= TERMP_NOSPACE;
 447 
 448         term_word(p, m->os);
 449         term_flushln(p);
 450 
 451         p->offset = 0;
 452         p->rmargin = p->maxrmargin;
 453         p->flags = 0;
 454 }
 455 
 456 
 457 static void
 458 print_mdoc_head(struct termp *p, const void *arg)
 459 {
 460         char            buf[BUFSIZ], title[BUFSIZ];
 461         size_t          buflen, titlen;
 462         const struct mdoc_meta *m;
 463 
 464         m = (const struct mdoc_meta *)arg;
 465 
 466         /*
 467          * The header is strange.  It has three components, which are
 468          * really two with the first duplicated.  It goes like this:
 469          *
 470          * IDENTIFIER              TITLE                   IDENTIFIER
 471          *
 472          * The IDENTIFIER is NAME(SECTION), which is the command-name
 473          * (if given, or "unknown" if not) followed by the manual page
 474          * section.  These are given in `Dt'.  The TITLE is a free-form
 475          * string depending on the manual volume.  If not specified, it
 476          * switches on the manual section.
 477          */
 478 
 479         p->offset = 0;
 480         p->rmargin = p->maxrmargin;
 481 
 482         assert(m->vol);
 483         strlcpy(buf, m->vol, BUFSIZ);
 484         buflen = term_strlen(p, buf);
 485 
 486         if (m->arch) {
 487                 strlcat(buf, " (", BUFSIZ);
 488                 strlcat(buf, m->arch, BUFSIZ);
 489                 strlcat(buf, ")", BUFSIZ);
 490         }
 491 
 492         snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
 493         titlen = term_strlen(p, title);
 494 
 495         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;

 496         p->offset = 0;
 497         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
 498             (p->maxrmargin -
 499              term_strlen(p, buf) + term_len(p, 1)) / 2 :
 500             p->maxrmargin - buflen;
 501 
 502         term_word(p, title);
 503         term_flushln(p);
 504 
 505         p->flags |= TERMP_NOSPACE;
 506         p->offset = p->rmargin;
 507         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
 508             p->maxrmargin - titlen : p->maxrmargin;
 509 
 510         term_word(p, buf);
 511         term_flushln(p);
 512 
 513         p->flags &= ~TERMP_NOBREAK;

 514         if (p->rmargin + titlen <= p->maxrmargin) {
 515                 p->flags |= TERMP_NOSPACE;
 516                 p->offset = p->rmargin;
 517                 p->rmargin = p->maxrmargin;
 518                 term_word(p, title);
 519                 term_flushln(p);
 520         }
 521 
 522         p->flags &= ~TERMP_NOSPACE;
 523         p->offset = 0;
 524         p->rmargin = p->maxrmargin;
 525 }
 526 
 527 
 528 static size_t
 529 a2height(const struct termp *p, const char *v)
 530 {
 531         struct roffsu    su;
 532 
 533 


 710                  * number for buffering single arguments.  See the above
 711                  * handling for column for how this changes.
 712                  */
 713                 assert(bl->norm->Bl.width);
 714                 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
 715                 break;
 716         }
 717 
 718         /* 
 719          * List-type can override the width in the case of fixed-head
 720          * values (bullet, dash/hyphen, enum).  Tags need a non-zero
 721          * offset.
 722          */
 723 
 724         switch (type) {
 725         case (LIST_bullet):
 726                 /* FALLTHROUGH */
 727         case (LIST_dash):
 728                 /* FALLTHROUGH */
 729         case (LIST_hyphen):
 730                 if (width < term_len(p, 4))
 731                         width = term_len(p, 4);
 732                 break;
 733         case (LIST_enum):
 734                 if (width < term_len(p, 5))
 735                         width = term_len(p, 5);
 736                 break;
 737         case (LIST_hang):
 738                 if (0 == width)
 739                         width = term_len(p, 8);
 740                 break;
 741         case (LIST_column):
 742                 /* FALLTHROUGH */
 743         case (LIST_tag):
 744                 if (0 == width)
 745                         width = term_len(p, 10);
 746                 break;
 747         default:
 748                 break;
 749         }
 750 
 751         /* 
 752          * Whitespace control.  Inset bodies need an initial space,
 753          * while diagonal bodies need two.
 754          */
 755 


 770 
 771         p->flags |= TERMP_NOSPACE;
 772 
 773         switch (type) {
 774         case (LIST_diag):
 775                 if (MDOC_HEAD == n->type)
 776                         term_fontpush(p, TERMFONT_BOLD);
 777                 break;
 778         default:
 779                 break;
 780         }
 781 
 782         /*
 783          * Pad and break control.  This is the tricky part.  These flags
 784          * are documented in term_flushln() in term.c.  Note that we're
 785          * going to unset all of these flags in termp_it_post() when we
 786          * exit.
 787          */
 788 
 789         switch (type) {








 790         case (LIST_bullet):
 791                 /* FALLTHROUGH */
 792         case (LIST_dash):
 793                 /* FALLTHROUGH */
 794         case (LIST_enum):
 795                 /* FALLTHROUGH */
 796         case (LIST_hyphen):
 797                 if (MDOC_HEAD == n->type)

 798                         p->flags |= TERMP_NOBREAK;

 799                 break;
 800         case (LIST_hang):
 801                 if (MDOC_HEAD == n->type)
 802                         p->flags |= TERMP_NOBREAK;
 803                 else
 804                         break;
 805 
 806                 /*
 807                  * This is ugly.  If `-hang' is specified and the body
 808                  * is a `Bl' or `Bd', then we want basically to nullify
 809                  * the "overstep" effect in term_flushln() and treat
 810                  * this as a `-ohang' list instead.
 811                  */
 812                 if (n->next->child && 
 813                                 (MDOC_Bl == n->next->child->tok ||
 814                                  MDOC_Bd == n->next->child->tok))
 815                         p->flags &= ~TERMP_NOBREAK;
 816                 else
 817                         p->flags |= TERMP_HANG;
 818                 break;
 819         case (LIST_tag):
 820                 if (MDOC_HEAD == n->type)
 821                         p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
 822 




 823                 if (MDOC_HEAD != n->type)
 824                         break;




 825                 if (NULL == n->next || NULL == n->next->child)
 826                         p->flags |= TERMP_DANGLE;
 827                 break;
 828         case (LIST_column):
 829                 if (MDOC_HEAD == n->type)
 830                         break;
 831 
 832                 if (NULL == n->next)
 833                         p->flags &= ~TERMP_NOBREAK;
 834                 else

 835                         p->flags |= TERMP_NOBREAK;


 836 
 837                 break;
 838         case (LIST_diag):
 839                 if (MDOC_HEAD == n->type)

 840                         p->flags |= TERMP_NOBREAK;

 841                 break;
 842         default:
 843                 break;
 844         }
 845 
 846         /* 
 847          * Margin control.  Set-head-width lists have their right
 848          * margins shortened.  The body for these lists has the offset
 849          * necessarily lengthened.  Everybody gets the offset.
 850          */
 851 
 852         p->offset += offset;
 853 
 854         switch (type) {
 855         case (LIST_hang):
 856                 /*
 857                  * Same stipulation as above, regarding `-hang'.  We
 858                  * don't want to recalculate rmargin and offsets when
 859                  * using `Bd' or `Bl' within `-hang' overstep lists.
 860                  */


 972                 if (MDOC_BODY == n->type)
 973                         term_newln(p);
 974                 break;
 975         case (LIST_column):
 976                 if (MDOC_BODY == n->type)
 977                         term_flushln(p);
 978                 break;
 979         default:
 980                 term_newln(p);
 981                 break;
 982         }
 983 
 984         /* 
 985          * Now that our output is flushed, we can reset our tags.  Since
 986          * only `It' sets these flags, we're free to assume that nobody
 987          * has munged them in the meanwhile.
 988          */
 989 
 990         p->flags &= ~TERMP_DANGLE;
 991         p->flags &= ~TERMP_NOBREAK;
 992         p->flags &= ~TERMP_TWOSPACE;
 993         p->flags &= ~TERMP_HANG;

 994 }
 995 
 996 
 997 /* ARGSUSED */
 998 static int
 999 termp_nm_pre(DECL_ARGS)
1000 {
1001 
1002         if (MDOC_BLOCK == n->type)

1003                 return(1);

1004 
1005         if (MDOC_BODY == n->type) {
1006                 if (NULL == n->child)
1007                         return(0);
1008                 p->flags |= TERMP_NOSPACE;
1009                 p->offset += term_len(p, 1) +
1010                     (NULL == n->prev->child ? term_strlen(p, m->name) :

1011                      MDOC_TEXT == n->prev->child->type ?
1012                         term_strlen(p, n->prev->child->string) :
1013                      term_len(p, 5));
1014                 return(1);
1015         }
1016 
1017         if (NULL == n->child && NULL == m->name)
1018                 return(0);
1019 
1020         if (MDOC_HEAD == n->type)
1021                 synopsis_pre(p, n->parent);
1022 
1023         if (MDOC_HEAD == n->type && n->next->child) {
1024                 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;

1025                 p->rmargin = p->offset + term_len(p, 1);
1026                 if (NULL == n->child) {
1027                         p->rmargin += term_strlen(p, m->name);
1028                 } else if (MDOC_TEXT == n->child->type) {
1029                         p->rmargin += term_strlen(p, n->child->string);
1030                         if (n->child->next)
1031                                 p->flags |= TERMP_HANG;
1032                 } else {
1033                         p->rmargin += term_len(p, 5);
1034                         p->flags |= TERMP_HANG;
1035                 }
1036         }
1037 
1038         term_fontpush(p, TERMFONT_BOLD);
1039         if (NULL == n->child)
1040                 term_word(p, m->name);
1041         return(1);
1042 }
1043 
1044 
1045 /* ARGSUSED */
1046 static void
1047 termp_nm_post(DECL_ARGS)
1048 {
1049 
1050         if (MDOC_HEAD == n->type && n->next->child) {


1051                 term_flushln(p);
1052                 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);

1053         } else if (MDOC_BODY == n->type && n->child)
1054                 term_flushln(p);
1055 }
1056 
1057                 
1058 /* ARGSUSED */
1059 static int
1060 termp_fl_pre(DECL_ARGS)
1061 {
1062 
1063         term_fontpush(p, TERMFONT_BOLD);
1064         term_word(p, "\\-");
1065 
1066         if (n->child)
1067                 p->flags |= TERMP_NOSPACE;
1068         else if (n->next && n->next->line == n->line)
1069                 p->flags |= TERMP_NOSPACE;
1070 
1071         return(1);
1072 }


1358                 break;
1359         case (MDOC_Ft):
1360                 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1361                         term_vspace(p);
1362                         break;
1363                 }
1364                 /* FALLTHROUGH */
1365         default:
1366                 term_newln(p);
1367                 break;
1368         }
1369 }
1370 
1371 
1372 static int
1373 termp_vt_pre(DECL_ARGS)
1374 {
1375 
1376         if (MDOC_ELEM == n->type) {
1377                 synopsis_pre(p, n);
1378                 return(termp_under_pre(p, pair, m, n));
1379         } else if (MDOC_BLOCK == n->type) {
1380                 synopsis_pre(p, n);
1381                 return(1);
1382         } else if (MDOC_HEAD == n->type)
1383                 return(0);
1384 
1385         return(termp_under_pre(p, pair, m, n));
1386 }
1387 
1388 
1389 /* ARGSUSED */
1390 static int
1391 termp_bold_pre(DECL_ARGS)
1392 {
1393 
1394         term_fontpush(p, TERMFONT_BOLD);
1395         return(1);
1396 }
1397 
1398 
1399 /* ARGSUSED */
1400 static int
1401 termp_fd_pre(DECL_ARGS)
1402 {
1403 
1404         synopsis_pre(p, n);
1405         return(termp_bold_pre(p, pair, m, n));
1406 }
1407 
1408 
1409 /* ARGSUSED */









1410 static int
1411 termp_sh_pre(DECL_ARGS)
1412 {
1413 
1414         /* No vspace between consecutive `Sh' calls. */
1415 
1416         switch (n->type) {
1417         case (MDOC_BLOCK):
1418                 if (n->prev && MDOC_Sh == n->prev->tok)
1419                         if (NULL == n->prev->body->child)
1420                                 break;
1421                 term_vspace(p);
1422                 break;
1423         case (MDOC_HEAD):
1424                 term_fontpush(p, TERMFONT_BOLD);
1425                 break;
1426         case (MDOC_BODY):
1427                 p->offset = term_len(p, p->defindent);


1428                 break;
1429         default:
1430                 break;
1431         }
1432         return(1);
1433 }
1434 
1435 
1436 /* ARGSUSED */
1437 static void
1438 termp_sh_post(DECL_ARGS)
1439 {
1440 
1441         switch (n->type) {
1442         case (MDOC_HEAD):
1443                 term_newln(p);
1444                 break;
1445         case (MDOC_BODY):
1446                 term_newln(p);
1447                 p->offset = 0;


1481         term_word(p, "currently under development.");
1482         p->flags |= TERMP_SENTENCE;
1483         return(0);
1484 }
1485 
1486 
1487 /* ARGSUSED */
1488 static int
1489 termp_d1_pre(DECL_ARGS)
1490 {
1491 
1492         if (MDOC_BLOCK != n->type)
1493                 return(1);
1494         term_newln(p);
1495         p->offset += term_len(p, p->defindent + 1);
1496         return(1);
1497 }
1498 
1499 
1500 /* ARGSUSED */
1501 static void
1502 termp_d1_post(DECL_ARGS)
1503 {
1504 
1505         if (MDOC_BLOCK != n->type) 
1506                 return;
1507         term_newln(p);
1508 }
1509 
1510 
1511 /* ARGSUSED */
1512 static int
1513 termp_ft_pre(DECL_ARGS)
1514 {
1515 
1516         /* NB: MDOC_LINE does not effect this! */
1517         synopsis_pre(p, n);
1518         term_fontpush(p, TERMFONT_UNDER);
1519         return(1);
1520 }
1521 
1522 
1523 /* ARGSUSED */
1524 static int
1525 termp_fn_pre(DECL_ARGS)
1526 {

1527         int              pretty;
1528 
1529         pretty = MDOC_SYNPRETTY & n->flags;
1530 
1531         synopsis_pre(p, n);
1532 
1533         if (NULL == (n = n->child))
1534                 return(0);
1535 






1536         assert(MDOC_TEXT == n->type);
1537         term_fontpush(p, TERMFONT_BOLD);
1538         term_word(p, n->string);
1539         term_fontpop(p);
1540 







1541         p->flags |= TERMP_NOSPACE;
1542         term_word(p, "(");
1543         p->flags |= TERMP_NOSPACE;
1544 
1545         for (n = n->next; n; n = n->next) {
1546                 assert(MDOC_TEXT == n->type);
1547                 term_fontpush(p, TERMFONT_UNDER);


1548                 term_word(p, n->string);
1549                 term_fontpop(p);
1550 
1551                 if (n->next) {
1552                         p->flags |= TERMP_NOSPACE;
1553                         term_word(p, ",");
1554                 }
1555         }
1556 
1557         p->flags |= TERMP_NOSPACE;
1558         term_word(p, ")");
1559 
1560         if (pretty) {
1561                 p->flags |= TERMP_NOSPACE;
1562                 term_word(p, ";");

1563         }
1564 
1565         return(0);
1566 }
1567 
1568 
1569 /* ARGSUSED */
1570 static int
1571 termp_fa_pre(DECL_ARGS)
1572 {
1573         const struct mdoc_node  *nn;
1574 
1575         if (n->parent->tok != MDOC_Fo) {
1576                 term_fontpush(p, TERMFONT_UNDER);
1577                 return(1);
1578         }
1579 
1580         for (nn = n->child; nn; nn = nn->next) {
1581                 term_fontpush(p, TERMFONT_UNDER);

1582                 term_word(p, nn->string);
1583                 term_fontpop(p);
1584 
1585                 if (nn->next) {
1586                         p->flags |= TERMP_NOSPACE;
1587                         term_word(p, ",");
1588                 }
1589         }
1590 
1591         if (n->child && n->next && n->next->tok == MDOC_Fa) {
1592                 p->flags |= TERMP_NOSPACE;
1593                 term_word(p, ",");
1594         }
1595 
1596         return(0);
1597 }
1598 
1599 
1600 /* ARGSUSED */
1601 static int
1602 termp_bd_pre(DECL_ARGS)
1603 {
1604         size_t                   tabwidth, rm, rmax;
1605         const struct mdoc_node  *nn;
1606 
1607         if (MDOC_BLOCK == n->type) {
1608                 print_bvspace(p, n, n);
1609                 return(1);
1610         } else if (MDOC_HEAD == n->type)
1611                 return(0);
1612 
1613         if (n->norm->Bd.offs)
1614                 p->offset += a2offs(p, n->norm->Bd.offs);
1615 
1616         /*
1617          * If -ragged or -filled are specified, the block does nothing
1618          * but change the indentation.  If -unfilled or -literal are
1619          * specified, text is printed exactly as entered in the display:
1620          * for macro lines, a newline is appended to the line.  Blank
1621          * lines are allowed.
1622          */
1623         
1624         if (DISP_literal != n->norm->Bd.type && 
1625                         DISP_unfilled != n->norm->Bd.type)
1626                 return(1);
1627 
1628         tabwidth = p->tabwidth;
1629         if (DISP_literal == n->norm->Bd.type)
1630                 p->tabwidth = term_len(p, 8);
1631 
1632         rm = p->rmargin;
1633         rmax = p->maxrmargin;
1634         p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1635 
1636         for (nn = n->child; nn; nn = nn->next) {
1637                 print_mdoc_node(p, pair, m, nn);
1638                 /*
1639                  * If the printed node flushes its own line, then we
1640                  * needn't do it here as well.  This is hacky, but the
1641                  * notion of selective eoln whitespace is pretty dumb
1642                  * anyway, so don't sweat it.
1643                  */
1644                 switch (nn->tok) {
1645                 case (MDOC_Sm):
1646                         /* FALLTHROUGH */
1647                 case (MDOC_br):
1648                         /* FALLTHROUGH */
1649                 case (MDOC_sp):
1650                         /* FALLTHROUGH */
1651                 case (MDOC_Bl):
1652                         /* FALLTHROUGH */
1653                 case (MDOC_D1):
1654                         /* FALLTHROUGH */
1655                 case (MDOC_Dl):
1656                         /* FALLTHROUGH */
1657                 case (MDOC_Lp):


1734         switch (n->tok) {
1735         case (MDOC_Bsx):
1736                 pp = "BSD/OS";
1737                 break;
1738         case (MDOC_Dx):
1739                 pp = "DragonFly";
1740                 break;
1741         case (MDOC_Fx):
1742                 pp = "FreeBSD";
1743                 break;
1744         case (MDOC_Nx):
1745                 pp = "NetBSD";
1746                 break;
1747         case (MDOC_Ox):
1748                 pp = "OpenBSD";
1749                 break;
1750         case (MDOC_Ux):
1751                 pp = "UNIX";
1752                 break;
1753         default:
1754                 break;

1755         }
1756 
1757         term_word(p, pp);
1758         if (n->child) {
1759                 flags = p->flags;
1760                 p->flags |= TERMP_KEEP;
1761                 term_word(p, n->child->string);
1762                 p->flags = flags;
1763         }
1764         return(0);
1765 }
1766 
1767 
1768 /* ARGSUSED */
1769 static int
1770 termp_igndelim_pre(DECL_ARGS)
1771 {
1772 
1773         p->flags |= TERMP_IGNDELIM;
1774         return(1);
1775 }
1776 
1777 
1778 /* ARGSUSED */
1779 static void
1780 termp_pf_post(DECL_ARGS)
1781 {
1782 
1783         p->flags |= TERMP_NOSPACE;
1784 }
1785 
1786 
1787 /* ARGSUSED */
1788 static int
1789 termp_ss_pre(DECL_ARGS)
1790 {
1791 
1792         switch (n->type) {
1793         case (MDOC_BLOCK):
1794                 term_newln(p);
1795                 if (n->prev)
1796                         term_vspace(p);
1797                 break;
1798         case (MDOC_HEAD):


1906         case (MDOC_Aq):
1907                 term_word(p, "<");
1908                 break;
1909         case (MDOC_Bro):
1910                 /* FALLTHROUGH */
1911         case (MDOC_Brq):
1912                 term_word(p, "{");
1913                 break;
1914         case (MDOC_Oo):
1915                 /* FALLTHROUGH */
1916         case (MDOC_Op):
1917                 /* FALLTHROUGH */
1918         case (MDOC_Bo):
1919                 /* FALLTHROUGH */
1920         case (MDOC_Bq):
1921                 term_word(p, "[");
1922                 break;
1923         case (MDOC_Do):
1924                 /* FALLTHROUGH */
1925         case (MDOC_Dq):
1926                 term_word(p, "``");
1927                 break;
1928         case (MDOC_Eo):
1929                 break;
1930         case (MDOC_Po):
1931                 /* FALLTHROUGH */
1932         case (MDOC_Pq):
1933                 term_word(p, "(");
1934                 break;
1935         case (MDOC__T):
1936                 /* FALLTHROUGH */
1937         case (MDOC_Qo):
1938                 /* FALLTHROUGH */
1939         case (MDOC_Qq):
1940                 term_word(p, "\"");
1941                 break;
1942         case (MDOC_Ql):
1943                 /* FALLTHROUGH */
1944         case (MDOC_So):
1945                 /* FALLTHROUGH */
1946         case (MDOC_Sq):
1947                 term_word(p, "`");
1948                 break;
1949         default:
1950                 abort();
1951                 /* NOTREACHED */
1952         }
1953 
1954         p->flags |= TERMP_NOSPACE;
1955         return(1);
1956 }
1957 
1958 
1959 /* ARGSUSED */
1960 static void
1961 termp_quote_post(DECL_ARGS)
1962 {
1963 
1964         if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
1965                 return;
1966 
1967         p->flags |= TERMP_NOSPACE;


1972         case (MDOC_Aq):
1973                 term_word(p, ">");
1974                 break;
1975         case (MDOC_Bro):
1976                 /* FALLTHROUGH */
1977         case (MDOC_Brq):
1978                 term_word(p, "}");
1979                 break;
1980         case (MDOC_Oo):
1981                 /* FALLTHROUGH */
1982         case (MDOC_Op):
1983                 /* FALLTHROUGH */
1984         case (MDOC_Bo):
1985                 /* FALLTHROUGH */
1986         case (MDOC_Bq):
1987                 term_word(p, "]");
1988                 break;
1989         case (MDOC_Do):
1990                 /* FALLTHROUGH */
1991         case (MDOC_Dq):
1992                 term_word(p, "''");
1993                 break;
1994         case (MDOC_Eo):
1995                 break;
1996         case (MDOC_Po):
1997                 /* FALLTHROUGH */
1998         case (MDOC_Pq):
1999                 term_word(p, ")");
2000                 break;
2001         case (MDOC__T):
2002                 /* FALLTHROUGH */
2003         case (MDOC_Qo):
2004                 /* FALLTHROUGH */
2005         case (MDOC_Qq):
2006                 term_word(p, "\"");
2007                 break;
2008         case (MDOC_Ql):
2009                 /* FALLTHROUGH */
2010         case (MDOC_So):
2011                 /* FALLTHROUGH */
2012         case (MDOC_Sq):
2013                 term_word(p, "'");
2014                 break;
2015         default:
2016                 abort();
2017                 /* NOTREACHED */
2018         }
2019 }
2020 
2021 
2022 /* ARGSUSED */
2023 static int
2024 termp_fo_pre(DECL_ARGS)
2025 {


2026 


2027         if (MDOC_BLOCK == n->type) {
2028                 synopsis_pre(p, n);
2029                 return(1);
2030         } else if (MDOC_BODY == n->type) {





2031                 p->flags |= TERMP_NOSPACE;
2032                 term_word(p, "(");
2033                 p->flags |= TERMP_NOSPACE;






2034                 return(1);
2035         } 
2036 
2037         if (NULL == n->child)
2038                 return(0);
2039 
2040         /* XXX: we drop non-initial arguments as per groff. */
2041 
2042         assert(n->child->string);
2043         term_fontpush(p, TERMFONT_BOLD);
2044         term_word(p, n->child->string);
2045         return(0);
2046 }
2047 
2048 
2049 /* ARGSUSED */
2050 static void
2051 termp_fo_post(DECL_ARGS)
2052 {
2053 
2054         if (MDOC_BODY != n->type) 
2055                 return;
2056 
2057         p->flags |= TERMP_NOSPACE;
2058         term_word(p, ")");
2059 
2060         if (MDOC_SYNPRETTY & n->flags) {
2061                 p->flags |= TERMP_NOSPACE;
2062                 term_word(p, ";");

2063         }
2064 }
2065 
2066 
2067 /* ARGSUSED */
2068 static int
2069 termp_bf_pre(DECL_ARGS)
2070 {
2071 
2072         if (MDOC_HEAD == n->type)
2073                 return(0);
2074         else if (MDOC_BLOCK != n->type)
2075                 return(1);
2076 
2077         if (FONT_Em == n->norm->Bf.font) 
2078                 term_fontpush(p, TERMFONT_UNDER);
2079         else if (FONT_Sy == n->norm->Bf.font) 
2080                 term_fontpush(p, TERMFONT_BOLD);
2081         else 
2082                 term_fontpush(p, TERMFONT_NONE);
2083 
2084         return(1);
2085 }
2086 
2087 
2088 /* ARGSUSED */
2089 static int
2090 termp_sm_pre(DECL_ARGS)
2091 {
2092 
2093         assert(n->child && MDOC_TEXT == n->child->type);
2094         if (0 == strcmp("on", n->child->string)) {


2140                 p->flags |= TERMP_SENTENCE;
2141         } else
2142                 term_word(p, ",");
2143 }
2144 
2145 
2146 /* ARGSUSED */
2147 static int
2148 termp_li_pre(DECL_ARGS)
2149 {
2150 
2151         term_fontpush(p, TERMFONT_NONE);
2152         return(1);
2153 }
2154 
2155 
2156 /* ARGSUSED */
2157 static int
2158 termp_lk_pre(DECL_ARGS)
2159 {
2160         const struct mdoc_node *nn, *sv;
2161 
2162         term_fontpush(p, TERMFONT_UNDER);

2163 
2164         nn = sv = n->child;
2165 
2166         if (NULL == nn || NULL == nn->next)
2167                 return(1);
2168 
2169         for (nn = nn->next; nn; nn = nn->next) 
2170                 term_word(p, nn->string);
2171 
2172         term_fontpop(p);
2173 
2174         p->flags |= TERMP_NOSPACE;
2175         term_word(p, ":");


2176 
2177         term_fontpush(p, TERMFONT_BOLD);
2178         term_word(p, sv->string);
2179         term_fontpop(p);
2180 
2181         return(0);
2182 }
2183 
2184 
2185 /* ARGSUSED */
2186 static int
2187 termp_bk_pre(DECL_ARGS)
2188 {
2189 
2190         switch (n->type) {
2191         case (MDOC_BLOCK):
2192                 break;
2193         case (MDOC_HEAD):
2194                 return(0);
2195         case (MDOC_BODY):
2196                 if (n->parent->args || 0 == n->prev->nchild)
2197                         p->flags |= TERMP_PREKEEP;
2198                 break;


2208 /* ARGSUSED */
2209 static void
2210 termp_bk_post(DECL_ARGS)
2211 {
2212 
2213         if (MDOC_BODY == n->type)
2214                 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2215 }
2216 
2217 /* ARGSUSED */
2218 static void
2219 termp__t_post(DECL_ARGS)
2220 {
2221 
2222         /*
2223          * If we're in an `Rs' and there's a journal present, then quote
2224          * us instead of underlining us (for disambiguation).
2225          */
2226         if (n->parent && MDOC_Rs == n->parent->tok &&
2227                         n->parent->norm->Rs.quote_T)
2228                 termp_quote_post(p, pair, m, n);
2229 
2230         termp____post(p, pair, m, n);
2231 }
2232 
2233 /* ARGSUSED */
2234 static int
2235 termp__t_pre(DECL_ARGS)
2236 {
2237 
2238         /*
2239          * If we're in an `Rs' and there's a journal present, then quote
2240          * us instead of underlining us (for disambiguation).
2241          */
2242         if (n->parent && MDOC_Rs == n->parent->tok &&
2243                         n->parent->norm->Rs.quote_T)
2244                 return(termp_quote_pre(p, pair, m, n));
2245 
2246         term_fontpush(p, TERMFONT_UNDER);
2247         return(1);
2248 }
2249 
2250 /* ARGSUSED */
2251 static int
2252 termp_under_pre(DECL_ARGS)
2253 {
2254 
2255         term_fontpush(p, TERMFONT_UNDER);
2256         return(1);
2257 }
   1 /*      $Id: mdoc_term.c,v 1.258 2013/12/25 21:24:12 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
   5  * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
   6  *
   7  * Permission to use, copy, modify, and distribute this software for any
   8  * purpose with or without fee is hereby granted, provided that the above
   9  * copyright notice and this permission notice appear in all copies.
  10  *
  11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18  */
  19 #ifdef HAVE_CONFIG_H
  20 #include "config.h"
  21 #endif
  22 
  23 #include <sys/types.h>
  24 
  25 #include <assert.h>
  26 #include <ctype.h>
  27 #include <stdint.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <string.h>
  31 
  32 #include "mandoc.h"
  33 #include "out.h"
  34 #include "term.h"
  35 #include "mdoc.h"
  36 #include "main.h"
  37 
  38 struct  termpair {
  39         struct termpair  *ppair;
  40         int               count;
  41 };
  42 
  43 #define DECL_ARGS struct termp *p, \
  44                   struct termpair *pair, \
  45                   const struct mdoc_meta *meta, \
  46                   struct mdoc_node *n
  47 
  48 struct  termact {
  49         int     (*pre)(DECL_ARGS);
  50         void    (*post)(DECL_ARGS);
  51 };
  52 
  53 static  size_t    a2width(const struct termp *, const char *);
  54 static  size_t    a2height(const struct termp *, const char *);
  55 static  size_t    a2offs(const struct termp *, const char *);
  56 
  57 static  void      print_bvspace(struct termp *,
  58                         const struct mdoc_node *,
  59                         const struct mdoc_node *);
  60 static  void      print_mdoc_node(DECL_ARGS);
  61 static  void      print_mdoc_nodelist(DECL_ARGS);
  62 static  void      print_mdoc_head(struct termp *, const void *);
  63 static  void      print_mdoc_foot(struct termp *, const void *);
  64 static  void      synopsis_pre(struct termp *, 
  65                         const struct mdoc_node *);
  66 
  67 static  void      termp____post(DECL_ARGS);
  68 static  void      termp__t_post(DECL_ARGS);
  69 static  void      termp_an_post(DECL_ARGS);
  70 static  void      termp_bd_post(DECL_ARGS);
  71 static  void      termp_bk_post(DECL_ARGS);
  72 static  void      termp_bl_post(DECL_ARGS);
  73 static  void      termp_fd_post(DECL_ARGS);
  74 static  void      termp_fo_post(DECL_ARGS);
  75 static  void      termp_in_post(DECL_ARGS);
  76 static  void      termp_it_post(DECL_ARGS);
  77 static  void      termp_lb_post(DECL_ARGS);
  78 static  void      termp_nm_post(DECL_ARGS);
  79 static  void      termp_pf_post(DECL_ARGS);
  80 static  void      termp_quote_post(DECL_ARGS);
  81 static  void      termp_sh_post(DECL_ARGS);
  82 static  void      termp_ss_post(DECL_ARGS);
  83 
  84 static  int       termp__a_pre(DECL_ARGS);
  85 static  int       termp__t_pre(DECL_ARGS);
  86 static  int       termp_an_pre(DECL_ARGS);
  87 static  int       termp_ap_pre(DECL_ARGS);
  88 static  int       termp_bd_pre(DECL_ARGS);
  89 static  int       termp_bf_pre(DECL_ARGS);
  90 static  int       termp_bk_pre(DECL_ARGS);
  91 static  int       termp_bl_pre(DECL_ARGS);
  92 static  int       termp_bold_pre(DECL_ARGS);
  93 static  int       termp_bt_pre(DECL_ARGS);
  94 static  int       termp_bx_pre(DECL_ARGS);
  95 static  int       termp_cd_pre(DECL_ARGS);
  96 static  int       termp_d1_pre(DECL_ARGS);
  97 static  int       termp_ex_pre(DECL_ARGS);
  98 static  int       termp_fa_pre(DECL_ARGS);
  99 static  int       termp_fd_pre(DECL_ARGS);
 100 static  int       termp_fl_pre(DECL_ARGS);
 101 static  int       termp_fn_pre(DECL_ARGS);
 102 static  int       termp_fo_pre(DECL_ARGS);
 103 static  int       termp_ft_pre(DECL_ARGS);

 104 static  int       termp_in_pre(DECL_ARGS);
 105 static  int       termp_it_pre(DECL_ARGS);
 106 static  int       termp_li_pre(DECL_ARGS);
 107 static  int       termp_lk_pre(DECL_ARGS);
 108 static  int       termp_nd_pre(DECL_ARGS);
 109 static  int       termp_nm_pre(DECL_ARGS);
 110 static  int       termp_ns_pre(DECL_ARGS);
 111 static  int       termp_quote_pre(DECL_ARGS);
 112 static  int       termp_rs_pre(DECL_ARGS);
 113 static  int       termp_rv_pre(DECL_ARGS);
 114 static  int       termp_sh_pre(DECL_ARGS);
 115 static  int       termp_sm_pre(DECL_ARGS);
 116 static  int       termp_sp_pre(DECL_ARGS);
 117 static  int       termp_ss_pre(DECL_ARGS);
 118 static  int       termp_under_pre(DECL_ARGS);
 119 static  int       termp_ud_pre(DECL_ARGS);
 120 static  int       termp_vt_pre(DECL_ARGS);
 121 static  int       termp_xr_pre(DECL_ARGS);
 122 static  int       termp_xx_pre(DECL_ARGS);
 123 
 124 static  const struct termact termacts[MDOC_MAX] = {
 125         { termp_ap_pre, NULL }, /* Ap */
 126         { NULL, NULL }, /* Dd */
 127         { NULL, NULL }, /* Dt */
 128         { NULL, NULL }, /* Os */
 129         { termp_sh_pre, termp_sh_post }, /* Sh */
 130         { termp_ss_pre, termp_ss_post }, /* Ss */ 
 131         { termp_sp_pre, NULL }, /* Pp */ 
 132         { termp_d1_pre, termp_bl_post }, /* D1 */
 133         { termp_d1_pre, termp_bl_post }, /* Dl */
 134         { termp_bd_pre, termp_bd_post }, /* Bd */
 135         { NULL, NULL }, /* Ed */
 136         { termp_bl_pre, termp_bl_post }, /* Bl */
 137         { NULL, NULL }, /* El */
 138         { termp_it_pre, termp_it_post }, /* It */
 139         { termp_under_pre, NULL }, /* Ad */ 
 140         { termp_an_pre, termp_an_post }, /* An */
 141         { termp_under_pre, NULL }, /* Ar */
 142         { termp_cd_pre, NULL }, /* Cd */
 143         { termp_bold_pre, NULL }, /* Cm */
 144         { NULL, NULL }, /* Dv */ 
 145         { NULL, NULL }, /* Er */ 
 146         { NULL, NULL }, /* Ev */ 
 147         { termp_ex_pre, NULL }, /* Ex */
 148         { termp_fa_pre, NULL }, /* Fa */ 
 149         { termp_fd_pre, termp_fd_post }, /* Fd */ 
 150         { termp_fl_pre, NULL }, /* Fl */
 151         { termp_fn_pre, NULL }, /* Fn */ 
 152         { termp_ft_pre, NULL }, /* Ft */ 
 153         { termp_bold_pre, NULL }, /* Ic */ 
 154         { termp_in_pre, termp_in_post }, /* In */ 
 155         { termp_li_pre, NULL }, /* Li */
 156         { termp_nd_pre, NULL }, /* Nd */ 
 157         { termp_nm_pre, termp_nm_post }, /* Nm */ 
 158         { termp_quote_pre, termp_quote_post }, /* Op */
 159         { NULL, NULL }, /* Ot */
 160         { termp_under_pre, NULL }, /* Pa */
 161         { termp_rv_pre, NULL }, /* Rv */
 162         { NULL, NULL }, /* St */ 
 163         { termp_under_pre, NULL }, /* Va */
 164         { termp_vt_pre, NULL }, /* Vt */
 165         { termp_xr_pre, NULL }, /* Xr */
 166         { termp__a_pre, termp____post }, /* %A */
 167         { termp_under_pre, termp____post }, /* %B */
 168         { NULL, termp____post }, /* %D */
 169         { termp_under_pre, termp____post }, /* %I */


 177         { NULL, NULL }, /* Ac */
 178         { termp_quote_pre, termp_quote_post }, /* Ao */
 179         { termp_quote_pre, termp_quote_post }, /* Aq */
 180         { NULL, NULL }, /* At */
 181         { NULL, NULL }, /* Bc */
 182         { termp_bf_pre, NULL }, /* Bf */ 
 183         { termp_quote_pre, termp_quote_post }, /* Bo */
 184         { termp_quote_pre, termp_quote_post }, /* Bq */
 185         { termp_xx_pre, NULL }, /* Bsx */
 186         { termp_bx_pre, NULL }, /* Bx */
 187         { NULL, NULL }, /* Db */
 188         { NULL, NULL }, /* Dc */
 189         { termp_quote_pre, termp_quote_post }, /* Do */
 190         { termp_quote_pre, termp_quote_post }, /* Dq */
 191         { NULL, NULL }, /* Ec */ /* FIXME: no space */
 192         { NULL, NULL }, /* Ef */
 193         { termp_under_pre, NULL }, /* Em */ 
 194         { termp_quote_pre, termp_quote_post }, /* Eo */
 195         { termp_xx_pre, NULL }, /* Fx */
 196         { termp_bold_pre, NULL }, /* Ms */
 197         { NULL, NULL }, /* No */
 198         { termp_ns_pre, NULL }, /* Ns */
 199         { termp_xx_pre, NULL }, /* Nx */
 200         { termp_xx_pre, NULL }, /* Ox */
 201         { NULL, NULL }, /* Pc */
 202         { NULL, termp_pf_post }, /* Pf */
 203         { termp_quote_pre, termp_quote_post }, /* Po */
 204         { termp_quote_pre, termp_quote_post }, /* Pq */
 205         { NULL, NULL }, /* Qc */
 206         { termp_quote_pre, termp_quote_post }, /* Ql */
 207         { termp_quote_pre, termp_quote_post }, /* Qo */
 208         { termp_quote_pre, termp_quote_post }, /* Qq */
 209         { NULL, NULL }, /* Re */
 210         { termp_rs_pre, NULL }, /* Rs */
 211         { NULL, NULL }, /* Sc */
 212         { termp_quote_pre, termp_quote_post }, /* So */
 213         { termp_quote_pre, termp_quote_post }, /* Sq */
 214         { termp_sm_pre, NULL }, /* Sm */
 215         { termp_under_pre, NULL }, /* Sx */
 216         { termp_bold_pre, NULL }, /* Sy */
 217         { NULL, NULL }, /* Tn */
 218         { termp_xx_pre, NULL }, /* Ux */
 219         { NULL, NULL }, /* Xc */
 220         { NULL, NULL }, /* Xo */
 221         { termp_fo_pre, termp_fo_post }, /* Fo */ 
 222         { NULL, NULL }, /* Fc */ 


 225         { termp_bk_pre, termp_bk_post }, /* Bk */
 226         { NULL, NULL }, /* Ek */
 227         { termp_bt_pre, NULL }, /* Bt */
 228         { NULL, NULL }, /* Hf */
 229         { NULL, NULL }, /* Fr */
 230         { termp_ud_pre, NULL }, /* Ud */
 231         { NULL, termp_lb_post }, /* Lb */
 232         { termp_sp_pre, NULL }, /* Lp */ 
 233         { termp_lk_pre, NULL }, /* Lk */ 
 234         { termp_under_pre, NULL }, /* Mt */ 
 235         { termp_quote_pre, termp_quote_post }, /* Brq */ 
 236         { termp_quote_pre, termp_quote_post }, /* Bro */ 
 237         { NULL, NULL }, /* Brc */ 
 238         { NULL, termp____post }, /* %C */ 
 239         { NULL, NULL }, /* Es */ /* TODO */
 240         { NULL, NULL }, /* En */ /* TODO */
 241         { termp_xx_pre, NULL }, /* Dx */ 
 242         { NULL, termp____post }, /* %Q */ 
 243         { termp_sp_pre, NULL }, /* br */
 244         { termp_sp_pre, NULL }, /* sp */ 
 245         { NULL, termp____post }, /* %U */ 
 246         { NULL, NULL }, /* Ta */ 
 247 };
 248 
 249 
 250 void
 251 terminal_mdoc(void *arg, const struct mdoc *mdoc)
 252 {
 253         const struct mdoc_node  *n;
 254         const struct mdoc_meta  *meta;
 255         struct termp            *p;
 256 
 257         p = (struct termp *)arg;
 258 
 259         if (0 == p->defindent)
 260                 p->defindent = 5;
 261 
 262         p->overstep = 0;
 263         p->maxrmargin = p->defrmargin;
 264         p->tabwidth = term_len(p, 5);
 265 
 266         if (NULL == p->symtab)
 267                 p->symtab = mchars_alloc();
 268 
 269         n = mdoc_node(mdoc);
 270         meta = mdoc_meta(mdoc);
 271 
 272         term_begin(p, print_mdoc_head, print_mdoc_foot, meta);
 273 
 274         if (n->child)
 275                 print_mdoc_nodelist(p, NULL, meta, n->child);
 276 
 277         term_end(p);
 278 }
 279 
 280 
 281 static void
 282 print_mdoc_nodelist(DECL_ARGS)
 283 {
 284 
 285         print_mdoc_node(p, pair, meta, n);
 286         if (n->next)
 287                 print_mdoc_nodelist(p, pair, meta, n->next);
 288 }
 289 
 290 
 291 /* ARGSUSED */
 292 static void
 293 print_mdoc_node(DECL_ARGS)
 294 {
 295         int              chld;

 296         struct termpair  npair;
 297         size_t           offset, rmargin;
 298 
 299         chld = 1;
 300         offset = p->offset;
 301         rmargin = p->rmargin;
 302         n->prev_font = term_fontq(p);
 303 
 304         memset(&npair, 0, sizeof(struct termpair));
 305         npair.ppair = pair;
 306 
 307         /*
 308          * Keeps only work until the end of a line.  If a keep was
 309          * invoked in a prior line, revert it to PREKEEP.



 310          */
 311 
 312         if (TERMP_KEEP & p->flags) {
 313                 if (n->prev ? (n->prev->lastline != n->line) :
 314                     (n->parent && n->parent->line != n->line)) {
 315                         p->flags &= ~TERMP_KEEP;
 316                         p->flags |= TERMP_PREKEEP;




 317                 }
 318         }

 319 
 320         /*










 321          * After the keep flags have been set up, we may now
 322          * produce output.  Note that some pre-handlers do so.
 323          */
 324 
 325         switch (n->type) {
 326         case (MDOC_TEXT):
 327                 if (' ' == *n->string && MDOC_LINE & n->flags)
 328                         term_newln(p);
 329                 if (MDOC_DELIMC & n->flags)
 330                         p->flags |= TERMP_NOSPACE;
 331                 term_word(p, n->string);
 332                 if (MDOC_DELIMO & n->flags)
 333                         p->flags |= TERMP_NOSPACE;
 334                 break;
 335         case (MDOC_EQN):
 336                 term_eqn(p, n->eqn);
 337                 break;
 338         case (MDOC_TBL):
 339                 term_tbl(p, n->span);
 340                 break;
 341         default:
 342                 if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
 343                         chld = (*termacts[n->tok].pre)
 344                                 (p, &npair, meta, n);
 345                 break;
 346         }
 347 
 348         if (chld && n->child)
 349                 print_mdoc_nodelist(p, &npair, meta, n->child);
 350 
 351         term_fontpopq(p,
 352             (ENDBODY_NOT == n->end ? n : n->pending)->prev_font);
 353 
 354         switch (n->type) {
 355         case (MDOC_TEXT):
 356                 break;
 357         case (MDOC_TBL):
 358                 break;
 359         case (MDOC_EQN):
 360                 break;
 361         default:
 362                 if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
 363                         break;
 364                 (void)(*termacts[n->tok].post)(p, &npair, meta, n);
 365 
 366                 /*
 367                  * Explicit end tokens not only call the post
 368                  * handler, but also tell the respective block
 369                  * that it must not call the post handler again.
 370                  */
 371                 if (ENDBODY_NOT != n->end)
 372                         n->pending->flags |= MDOC_ENDED;
 373 
 374                 /*
 375                  * End of line terminating an implicit block
 376                  * while an explicit block is still open.
 377                  * Continue the explicit block without spacing.
 378                  */
 379                 if (ENDBODY_NOSPACE == n->end)
 380                         p->flags |= TERMP_NOSPACE;
 381                 break;
 382         }
 383 
 384         if (MDOC_EOS & n->flags)
 385                 p->flags |= TERMP_SENTENCE;
 386 
 387         p->offset = offset;
 388         p->rmargin = rmargin;
 389 }
 390 
 391 
 392 static void
 393 print_mdoc_foot(struct termp *p, const void *arg)
 394 {
 395         const struct mdoc_meta *meta;
 396 
 397         meta = (const struct mdoc_meta *)arg;
 398 
 399         term_fontrepl(p, TERMFONT_NONE);
 400 
 401         /* 
 402          * Output the footer in new-groff style, that is, three columns
 403          * with the middle being the manual date and flanking columns
 404          * being the operating system:
 405          *
 406          * SYSTEM                  DATE                    SYSTEM
 407          */
 408 
 409         term_vspace(p);
 410 
 411         p->offset = 0;
 412         p->rmargin = (p->maxrmargin - 
 413                         term_strlen(p, meta->date) + term_len(p, 1)) / 2;
 414         p->trailspace = 1;
 415         p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
 416 
 417         term_word(p, meta->os);
 418         term_flushln(p);
 419 
 420         p->offset = p->rmargin;
 421         p->rmargin = p->maxrmargin - term_strlen(p, meta->os);
 422         p->flags |= TERMP_NOSPACE;
 423 
 424         term_word(p, meta->date);
 425         term_flushln(p);
 426 
 427         p->offset = p->rmargin;
 428         p->rmargin = p->maxrmargin;
 429         p->trailspace = 0;
 430         p->flags &= ~TERMP_NOBREAK;
 431         p->flags |= TERMP_NOSPACE;
 432 
 433         term_word(p, meta->os);
 434         term_flushln(p);
 435 
 436         p->offset = 0;
 437         p->rmargin = p->maxrmargin;
 438         p->flags = 0;
 439 }
 440 
 441 
 442 static void
 443 print_mdoc_head(struct termp *p, const void *arg)
 444 {
 445         char            buf[BUFSIZ], title[BUFSIZ];
 446         size_t          buflen, titlen;
 447         const struct mdoc_meta *meta;
 448 
 449         meta = (const struct mdoc_meta *)arg;
 450 
 451         /*
 452          * The header is strange.  It has three components, which are
 453          * really two with the first duplicated.  It goes like this:
 454          *
 455          * IDENTIFIER              TITLE                   IDENTIFIER
 456          *
 457          * The IDENTIFIER is NAME(SECTION), which is the command-name
 458          * (if given, or "unknown" if not) followed by the manual page
 459          * section.  These are given in `Dt'.  The TITLE is a free-form
 460          * string depending on the manual volume.  If not specified, it
 461          * switches on the manual section.
 462          */
 463 
 464         p->offset = 0;
 465         p->rmargin = p->maxrmargin;
 466 
 467         assert(meta->vol);
 468         strlcpy(buf, meta->vol, BUFSIZ);
 469         buflen = term_strlen(p, buf);
 470 
 471         if (meta->arch) {
 472                 strlcat(buf, " (", BUFSIZ);
 473                 strlcat(buf, meta->arch, BUFSIZ);
 474                 strlcat(buf, ")", BUFSIZ);
 475         }
 476 
 477         snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
 478         titlen = term_strlen(p, title);
 479 
 480         p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
 481         p->trailspace = 1;
 482         p->offset = 0;
 483         p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
 484             (p->maxrmargin -
 485              term_strlen(p, buf) + term_len(p, 1)) / 2 :
 486             p->maxrmargin - buflen;
 487 
 488         term_word(p, title);
 489         term_flushln(p);
 490 
 491         p->flags |= TERMP_NOSPACE;
 492         p->offset = p->rmargin;
 493         p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
 494             p->maxrmargin - titlen : p->maxrmargin;
 495 
 496         term_word(p, buf);
 497         term_flushln(p);
 498 
 499         p->flags &= ~TERMP_NOBREAK;
 500         p->trailspace = 0;
 501         if (p->rmargin + titlen <= p->maxrmargin) {
 502                 p->flags |= TERMP_NOSPACE;
 503                 p->offset = p->rmargin;
 504                 p->rmargin = p->maxrmargin;
 505                 term_word(p, title);
 506                 term_flushln(p);
 507         }
 508 
 509         p->flags &= ~TERMP_NOSPACE;
 510         p->offset = 0;
 511         p->rmargin = p->maxrmargin;
 512 }
 513 
 514 
 515 static size_t
 516 a2height(const struct termp *p, const char *v)
 517 {
 518         struct roffsu    su;
 519 
 520 


 697                  * number for buffering single arguments.  See the above
 698                  * handling for column for how this changes.
 699                  */
 700                 assert(bl->norm->Bl.width);
 701                 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
 702                 break;
 703         }
 704 
 705         /* 
 706          * List-type can override the width in the case of fixed-head
 707          * values (bullet, dash/hyphen, enum).  Tags need a non-zero
 708          * offset.
 709          */
 710 
 711         switch (type) {
 712         case (LIST_bullet):
 713                 /* FALLTHROUGH */
 714         case (LIST_dash):
 715                 /* FALLTHROUGH */
 716         case (LIST_hyphen):
 717                 /* FALLTHROUGH */


 718         case (LIST_enum):
 719                 if (width < term_len(p, 2))
 720                         width = term_len(p, 2);
 721                 break;
 722         case (LIST_hang):
 723                 if (0 == width)
 724                         width = term_len(p, 8);
 725                 break;
 726         case (LIST_column):
 727                 /* FALLTHROUGH */
 728         case (LIST_tag):
 729                 if (0 == width)
 730                         width = term_len(p, 10);
 731                 break;
 732         default:
 733                 break;
 734         }
 735 
 736         /* 
 737          * Whitespace control.  Inset bodies need an initial space,
 738          * while diagonal bodies need two.
 739          */
 740 


 755 
 756         p->flags |= TERMP_NOSPACE;
 757 
 758         switch (type) {
 759         case (LIST_diag):
 760                 if (MDOC_HEAD == n->type)
 761                         term_fontpush(p, TERMFONT_BOLD);
 762                 break;
 763         default:
 764                 break;
 765         }
 766 
 767         /*
 768          * Pad and break control.  This is the tricky part.  These flags
 769          * are documented in term_flushln() in term.c.  Note that we're
 770          * going to unset all of these flags in termp_it_post() when we
 771          * exit.
 772          */
 773 
 774         switch (type) {
 775         case (LIST_enum):
 776                 /*
 777                  * Weird special case.
 778                  * Very narrow enum lists actually hang.
 779                  */
 780                 if (width == term_len(p, 2))
 781                         p->flags |= TERMP_HANG;
 782                 /* FALLTHROUGH */
 783         case (LIST_bullet):
 784                 /* FALLTHROUGH */
 785         case (LIST_dash):
 786                 /* FALLTHROUGH */


 787         case (LIST_hyphen):
 788                 if (MDOC_HEAD != n->type)
 789                         break;
 790                 p->flags |= TERMP_NOBREAK;
 791                 p->trailspace = 1;
 792                 break;
 793         case (LIST_hang):
 794                 if (MDOC_HEAD != n->type)


 795                         break;
 796 
 797                 /*
 798                  * This is ugly.  If `-hang' is specified and the body
 799                  * is a `Bl' or `Bd', then we want basically to nullify
 800                  * the "overstep" effect in term_flushln() and treat
 801                  * this as a `-ohang' list instead.
 802                  */
 803                 if (n->next->child && 
 804                                 (MDOC_Bl == n->next->child->tok ||
 805                                  MDOC_Bd == n->next->child->tok))



 806                         break;



 807 
 808                 p->flags |= TERMP_NOBREAK | TERMP_HANG;
 809                 p->trailspace = 1;
 810                 break;
 811         case (LIST_tag):
 812                 if (MDOC_HEAD != n->type)
 813                         break;
 814 
 815                 p->flags |= TERMP_NOBREAK;
 816                 p->trailspace = 2;
 817 
 818                 if (NULL == n->next || NULL == n->next->child)
 819                         p->flags |= TERMP_DANGLE;
 820                 break;
 821         case (LIST_column):
 822                 if (MDOC_HEAD == n->type)
 823                         break;
 824 
 825                 if (NULL == n->next) {
 826                         p->flags &= ~TERMP_NOBREAK;
 827                         p->trailspace = 0;
 828                 } else {
 829                         p->flags |= TERMP_NOBREAK;
 830                         p->trailspace = 1;
 831                 }
 832 
 833                 break;
 834         case (LIST_diag):
 835                 if (MDOC_HEAD != n->type)
 836                         break;
 837                 p->flags |= TERMP_NOBREAK;
 838                 p->trailspace = 1;
 839                 break;
 840         default:
 841                 break;
 842         }
 843 
 844         /* 
 845          * Margin control.  Set-head-width lists have their right
 846          * margins shortened.  The body for these lists has the offset
 847          * necessarily lengthened.  Everybody gets the offset.
 848          */
 849 
 850         p->offset += offset;
 851 
 852         switch (type) {
 853         case (LIST_hang):
 854                 /*
 855                  * Same stipulation as above, regarding `-hang'.  We
 856                  * don't want to recalculate rmargin and offsets when
 857                  * using `Bd' or `Bl' within `-hang' overstep lists.
 858                  */


 970                 if (MDOC_BODY == n->type)
 971                         term_newln(p);
 972                 break;
 973         case (LIST_column):
 974                 if (MDOC_BODY == n->type)
 975                         term_flushln(p);
 976                 break;
 977         default:
 978                 term_newln(p);
 979                 break;
 980         }
 981 
 982         /* 
 983          * Now that our output is flushed, we can reset our tags.  Since
 984          * only `It' sets these flags, we're free to assume that nobody
 985          * has munged them in the meanwhile.
 986          */
 987 
 988         p->flags &= ~TERMP_DANGLE;
 989         p->flags &= ~TERMP_NOBREAK;

 990         p->flags &= ~TERMP_HANG;
 991         p->trailspace = 0;
 992 }
 993 
 994 
 995 /* ARGSUSED */
 996 static int
 997 termp_nm_pre(DECL_ARGS)
 998 {
 999 
1000         if (MDOC_BLOCK == n->type) {
1001                 p->flags |= TERMP_PREKEEP;
1002                 return(1);
1003         }
1004 
1005         if (MDOC_BODY == n->type) {
1006                 if (NULL == n->child)
1007                         return(0);
1008                 p->flags |= TERMP_NOSPACE;
1009                 p->offset += term_len(p, 1) +
1010                     (NULL == n->prev->child ?
1011                      term_strlen(p, meta->name) :
1012                      MDOC_TEXT == n->prev->child->type ?
1013                      term_strlen(p, n->prev->child->string) :
1014                      term_len(p, 5));
1015                 return(1);
1016         }
1017 
1018         if (NULL == n->child && NULL == meta->name)
1019                 return(0);
1020 
1021         if (MDOC_HEAD == n->type)
1022                 synopsis_pre(p, n->parent);
1023 
1024         if (MDOC_HEAD == n->type && n->next->child) {
1025                 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1026                 p->trailspace = 1;
1027                 p->rmargin = p->offset + term_len(p, 1);
1028                 if (NULL == n->child) {
1029                         p->rmargin += term_strlen(p, meta->name);
1030                 } else if (MDOC_TEXT == n->child->type) {
1031                         p->rmargin += term_strlen(p, n->child->string);
1032                         if (n->child->next)
1033                                 p->flags |= TERMP_HANG;
1034                 } else {
1035                         p->rmargin += term_len(p, 5);
1036                         p->flags |= TERMP_HANG;
1037                 }
1038         }
1039 
1040         term_fontpush(p, TERMFONT_BOLD);
1041         if (NULL == n->child)
1042                 term_word(p, meta->name);
1043         return(1);
1044 }
1045 
1046 
1047 /* ARGSUSED */
1048 static void
1049 termp_nm_post(DECL_ARGS)
1050 {
1051 
1052         if (MDOC_BLOCK == n->type) {
1053                 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1054         } else if (MDOC_HEAD == n->type && n->next->child) {
1055                 term_flushln(p);
1056                 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
1057                 p->trailspace = 0;
1058         } else if (MDOC_BODY == n->type && n->child)
1059                 term_flushln(p);
1060 }
1061 
1062                 
1063 /* ARGSUSED */
1064 static int
1065 termp_fl_pre(DECL_ARGS)
1066 {
1067 
1068         term_fontpush(p, TERMFONT_BOLD);
1069         term_word(p, "\\-");
1070 
1071         if (n->child)
1072                 p->flags |= TERMP_NOSPACE;
1073         else if (n->next && n->next->line == n->line)
1074                 p->flags |= TERMP_NOSPACE;
1075 
1076         return(1);
1077 }


1363                 break;
1364         case (MDOC_Ft):
1365                 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1366                         term_vspace(p);
1367                         break;
1368                 }
1369                 /* FALLTHROUGH */
1370         default:
1371                 term_newln(p);
1372                 break;
1373         }
1374 }
1375 
1376 
1377 static int
1378 termp_vt_pre(DECL_ARGS)
1379 {
1380 
1381         if (MDOC_ELEM == n->type) {
1382                 synopsis_pre(p, n);
1383                 return(termp_under_pre(p, pair, meta, n));
1384         } else if (MDOC_BLOCK == n->type) {
1385                 synopsis_pre(p, n);
1386                 return(1);
1387         } else if (MDOC_HEAD == n->type)
1388                 return(0);
1389 
1390         return(termp_under_pre(p, pair, meta, n));
1391 }
1392 
1393 
1394 /* ARGSUSED */
1395 static int
1396 termp_bold_pre(DECL_ARGS)
1397 {
1398 
1399         term_fontpush(p, TERMFONT_BOLD);
1400         return(1);
1401 }
1402 
1403 
1404 /* ARGSUSED */
1405 static int
1406 termp_fd_pre(DECL_ARGS)
1407 {
1408 
1409         synopsis_pre(p, n);
1410         return(termp_bold_pre(p, pair, meta, n));
1411 }
1412 
1413 
1414 /* ARGSUSED */
1415 static void
1416 termp_fd_post(DECL_ARGS)
1417 {
1418 
1419         term_newln(p);
1420 }
1421 
1422 
1423 /* ARGSUSED */
1424 static int
1425 termp_sh_pre(DECL_ARGS)
1426 {
1427 
1428         /* No vspace between consecutive `Sh' calls. */
1429 
1430         switch (n->type) {
1431         case (MDOC_BLOCK):
1432                 if (n->prev && MDOC_Sh == n->prev->tok)
1433                         if (NULL == n->prev->body->child)
1434                                 break;
1435                 term_vspace(p);
1436                 break;
1437         case (MDOC_HEAD):
1438                 term_fontpush(p, TERMFONT_BOLD);
1439                 break;
1440         case (MDOC_BODY):
1441                 p->offset = term_len(p, p->defindent);
1442                 if (SEC_AUTHORS == n->sec)
1443                         p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1444                 break;
1445         default:
1446                 break;
1447         }
1448         return(1);
1449 }
1450 
1451 
1452 /* ARGSUSED */
1453 static void
1454 termp_sh_post(DECL_ARGS)
1455 {
1456 
1457         switch (n->type) {
1458         case (MDOC_HEAD):
1459                 term_newln(p);
1460                 break;
1461         case (MDOC_BODY):
1462                 term_newln(p);
1463                 p->offset = 0;


1497         term_word(p, "currently under development.");
1498         p->flags |= TERMP_SENTENCE;
1499         return(0);
1500 }
1501 
1502 
1503 /* ARGSUSED */
1504 static int
1505 termp_d1_pre(DECL_ARGS)
1506 {
1507 
1508         if (MDOC_BLOCK != n->type)
1509                 return(1);
1510         term_newln(p);
1511         p->offset += term_len(p, p->defindent + 1);
1512         return(1);
1513 }
1514 
1515 
1516 /* ARGSUSED */











1517 static int
1518 termp_ft_pre(DECL_ARGS)
1519 {
1520 
1521         /* NB: MDOC_LINE does not effect this! */
1522         synopsis_pre(p, n);
1523         term_fontpush(p, TERMFONT_UNDER);
1524         return(1);
1525 }
1526 
1527 
1528 /* ARGSUSED */
1529 static int
1530 termp_fn_pre(DECL_ARGS)
1531 {
1532         size_t           rmargin = 0;
1533         int              pretty;
1534 
1535         pretty = MDOC_SYNPRETTY & n->flags;
1536 
1537         synopsis_pre(p, n);
1538 
1539         if (NULL == (n = n->child))
1540                 return(0);
1541 
1542         if (pretty) {
1543                 rmargin = p->rmargin;
1544                 p->rmargin = p->offset + term_len(p, 4);
1545                 p->flags |= TERMP_NOBREAK | TERMP_HANG;
1546         }
1547 
1548         assert(MDOC_TEXT == n->type);
1549         term_fontpush(p, TERMFONT_BOLD);
1550         term_word(p, n->string);
1551         term_fontpop(p);
1552 
1553         if (pretty) {
1554                 term_flushln(p);
1555                 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
1556                 p->offset = p->rmargin;
1557                 p->rmargin = rmargin;
1558         }
1559 
1560         p->flags |= TERMP_NOSPACE;
1561         term_word(p, "(");
1562         p->flags |= TERMP_NOSPACE;
1563 
1564         for (n = n->next; n; n = n->next) {
1565                 assert(MDOC_TEXT == n->type);
1566                 term_fontpush(p, TERMFONT_UNDER);
1567                 if (pretty)
1568                         p->flags |= TERMP_NBRWORD;
1569                 term_word(p, n->string);
1570                 term_fontpop(p);
1571 
1572                 if (n->next) {
1573                         p->flags |= TERMP_NOSPACE;
1574                         term_word(p, ",");
1575                 }
1576         }
1577 
1578         p->flags |= TERMP_NOSPACE;
1579         term_word(p, ")");
1580 
1581         if (pretty) {
1582                 p->flags |= TERMP_NOSPACE;
1583                 term_word(p, ";");
1584                 term_flushln(p);
1585         }
1586 
1587         return(0);
1588 }
1589 
1590 
1591 /* ARGSUSED */
1592 static int
1593 termp_fa_pre(DECL_ARGS)
1594 {
1595         const struct mdoc_node  *nn;
1596 
1597         if (n->parent->tok != MDOC_Fo) {
1598                 term_fontpush(p, TERMFONT_UNDER);
1599                 return(1);
1600         }
1601 
1602         for (nn = n->child; nn; nn = nn->next) {
1603                 term_fontpush(p, TERMFONT_UNDER);
1604                 p->flags |= TERMP_NBRWORD;
1605                 term_word(p, nn->string);
1606                 term_fontpop(p);
1607 
1608                 if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1609                         p->flags |= TERMP_NOSPACE;
1610                         term_word(p, ",");
1611                 }
1612         }
1613 





1614         return(0);
1615 }
1616 
1617 
1618 /* ARGSUSED */
1619 static int
1620 termp_bd_pre(DECL_ARGS)
1621 {
1622         size_t                   tabwidth, rm, rmax;
1623         struct mdoc_node        *nn;
1624 
1625         if (MDOC_BLOCK == n->type) {
1626                 print_bvspace(p, n, n);
1627                 return(1);
1628         } else if (MDOC_HEAD == n->type)
1629                 return(0);
1630 
1631         if (n->norm->Bd.offs)
1632                 p->offset += a2offs(p, n->norm->Bd.offs);
1633 
1634         /*
1635          * If -ragged or -filled are specified, the block does nothing
1636          * but change the indentation.  If -unfilled or -literal are
1637          * specified, text is printed exactly as entered in the display:
1638          * for macro lines, a newline is appended to the line.  Blank
1639          * lines are allowed.
1640          */
1641         
1642         if (DISP_literal != n->norm->Bd.type && 
1643                         DISP_unfilled != n->norm->Bd.type)
1644                 return(1);
1645 
1646         tabwidth = p->tabwidth;
1647         if (DISP_literal == n->norm->Bd.type)
1648                 p->tabwidth = term_len(p, 8);
1649 
1650         rm = p->rmargin;
1651         rmax = p->maxrmargin;
1652         p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1653 
1654         for (nn = n->child; nn; nn = nn->next) {
1655                 print_mdoc_node(p, pair, meta, nn);
1656                 /*
1657                  * If the printed node flushes its own line, then we
1658                  * needn't do it here as well.  This is hacky, but the
1659                  * notion of selective eoln whitespace is pretty dumb
1660                  * anyway, so don't sweat it.
1661                  */
1662                 switch (nn->tok) {
1663                 case (MDOC_Sm):
1664                         /* FALLTHROUGH */
1665                 case (MDOC_br):
1666                         /* FALLTHROUGH */
1667                 case (MDOC_sp):
1668                         /* FALLTHROUGH */
1669                 case (MDOC_Bl):
1670                         /* FALLTHROUGH */
1671                 case (MDOC_D1):
1672                         /* FALLTHROUGH */
1673                 case (MDOC_Dl):
1674                         /* FALLTHROUGH */
1675                 case (MDOC_Lp):


1752         switch (n->tok) {
1753         case (MDOC_Bsx):
1754                 pp = "BSD/OS";
1755                 break;
1756         case (MDOC_Dx):
1757                 pp = "DragonFly";
1758                 break;
1759         case (MDOC_Fx):
1760                 pp = "FreeBSD";
1761                 break;
1762         case (MDOC_Nx):
1763                 pp = "NetBSD";
1764                 break;
1765         case (MDOC_Ox):
1766                 pp = "OpenBSD";
1767                 break;
1768         case (MDOC_Ux):
1769                 pp = "UNIX";
1770                 break;
1771         default:
1772                 abort();
1773                 /* NOTREACHED */
1774         }
1775 
1776         term_word(p, pp);
1777         if (n->child) {
1778                 flags = p->flags;
1779                 p->flags |= TERMP_KEEP;
1780                 term_word(p, n->child->string);
1781                 p->flags = flags;
1782         }
1783         return(0);
1784 }
1785 
1786 
1787 /* ARGSUSED */










1788 static void
1789 termp_pf_post(DECL_ARGS)
1790 {
1791 
1792         p->flags |= TERMP_NOSPACE;
1793 }
1794 
1795 
1796 /* ARGSUSED */
1797 static int
1798 termp_ss_pre(DECL_ARGS)
1799 {
1800 
1801         switch (n->type) {
1802         case (MDOC_BLOCK):
1803                 term_newln(p);
1804                 if (n->prev)
1805                         term_vspace(p);
1806                 break;
1807         case (MDOC_HEAD):


1915         case (MDOC_Aq):
1916                 term_word(p, "<");
1917                 break;
1918         case (MDOC_Bro):
1919                 /* FALLTHROUGH */
1920         case (MDOC_Brq):
1921                 term_word(p, "{");
1922                 break;
1923         case (MDOC_Oo):
1924                 /* FALLTHROUGH */
1925         case (MDOC_Op):
1926                 /* FALLTHROUGH */
1927         case (MDOC_Bo):
1928                 /* FALLTHROUGH */
1929         case (MDOC_Bq):
1930                 term_word(p, "[");
1931                 break;
1932         case (MDOC_Do):
1933                 /* FALLTHROUGH */
1934         case (MDOC_Dq):
1935                 term_word(p, "\\(lq");
1936                 break;
1937         case (MDOC_Eo):
1938                 break;
1939         case (MDOC_Po):
1940                 /* FALLTHROUGH */
1941         case (MDOC_Pq):
1942                 term_word(p, "(");
1943                 break;
1944         case (MDOC__T):
1945                 /* FALLTHROUGH */
1946         case (MDOC_Qo):
1947                 /* FALLTHROUGH */
1948         case (MDOC_Qq):
1949                 term_word(p, "\"");
1950                 break;
1951         case (MDOC_Ql):
1952                 /* FALLTHROUGH */
1953         case (MDOC_So):
1954                 /* FALLTHROUGH */
1955         case (MDOC_Sq):
1956                 term_word(p, "\\(oq");
1957                 break;
1958         default:
1959                 abort();
1960                 /* NOTREACHED */
1961         }
1962 
1963         p->flags |= TERMP_NOSPACE;
1964         return(1);
1965 }
1966 
1967 
1968 /* ARGSUSED */
1969 static void
1970 termp_quote_post(DECL_ARGS)
1971 {
1972 
1973         if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
1974                 return;
1975 
1976         p->flags |= TERMP_NOSPACE;


1981         case (MDOC_Aq):
1982                 term_word(p, ">");
1983                 break;
1984         case (MDOC_Bro):
1985                 /* FALLTHROUGH */
1986         case (MDOC_Brq):
1987                 term_word(p, "}");
1988                 break;
1989         case (MDOC_Oo):
1990                 /* FALLTHROUGH */
1991         case (MDOC_Op):
1992                 /* FALLTHROUGH */
1993         case (MDOC_Bo):
1994                 /* FALLTHROUGH */
1995         case (MDOC_Bq):
1996                 term_word(p, "]");
1997                 break;
1998         case (MDOC_Do):
1999                 /* FALLTHROUGH */
2000         case (MDOC_Dq):
2001                 term_word(p, "\\(rq");
2002                 break;
2003         case (MDOC_Eo):
2004                 break;
2005         case (MDOC_Po):
2006                 /* FALLTHROUGH */
2007         case (MDOC_Pq):
2008                 term_word(p, ")");
2009                 break;
2010         case (MDOC__T):
2011                 /* FALLTHROUGH */
2012         case (MDOC_Qo):
2013                 /* FALLTHROUGH */
2014         case (MDOC_Qq):
2015                 term_word(p, "\"");
2016                 break;
2017         case (MDOC_Ql):
2018                 /* FALLTHROUGH */
2019         case (MDOC_So):
2020                 /* FALLTHROUGH */
2021         case (MDOC_Sq):
2022                 term_word(p, "\\(cq");
2023                 break;
2024         default:
2025                 abort();
2026                 /* NOTREACHED */
2027         }
2028 }
2029 
2030 
2031 /* ARGSUSED */
2032 static int
2033 termp_fo_pre(DECL_ARGS)
2034 {
2035         size_t           rmargin = 0;
2036         int              pretty;
2037 
2038         pretty = MDOC_SYNPRETTY & n->flags;
2039 
2040         if (MDOC_BLOCK == n->type) {
2041                 synopsis_pre(p, n);
2042                 return(1);
2043         } else if (MDOC_BODY == n->type) {
2044                 if (pretty) {
2045                         rmargin = p->rmargin;
2046                         p->rmargin = p->offset + term_len(p, 4);
2047                         p->flags |= TERMP_NOBREAK | TERMP_HANG;
2048                 }
2049                 p->flags |= TERMP_NOSPACE;
2050                 term_word(p, "(");
2051                 p->flags |= TERMP_NOSPACE;
2052                 if (pretty) {
2053                         term_flushln(p);
2054                         p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
2055                         p->offset = p->rmargin;
2056                         p->rmargin = rmargin;
2057                 }
2058                 return(1);
2059         }
2060 
2061         if (NULL == n->child)
2062                 return(0);
2063 
2064         /* XXX: we drop non-initial arguments as per groff. */
2065 
2066         assert(n->child->string);
2067         term_fontpush(p, TERMFONT_BOLD);
2068         term_word(p, n->child->string);
2069         return(0);
2070 }
2071 
2072 
2073 /* ARGSUSED */
2074 static void
2075 termp_fo_post(DECL_ARGS)
2076 {
2077 
2078         if (MDOC_BODY != n->type) 
2079                 return;
2080 
2081         p->flags |= TERMP_NOSPACE;
2082         term_word(p, ")");
2083 
2084         if (MDOC_SYNPRETTY & n->flags) {
2085                 p->flags |= TERMP_NOSPACE;
2086                 term_word(p, ";");
2087                 term_flushln(p);
2088         }
2089 }
2090 
2091 
2092 /* ARGSUSED */
2093 static int
2094 termp_bf_pre(DECL_ARGS)
2095 {
2096 
2097         if (MDOC_HEAD == n->type)
2098                 return(0);
2099         else if (MDOC_BODY != n->type)
2100                 return(1);
2101 
2102         if (FONT_Em == n->norm->Bf.font) 
2103                 term_fontpush(p, TERMFONT_UNDER);
2104         else if (FONT_Sy == n->norm->Bf.font) 
2105                 term_fontpush(p, TERMFONT_BOLD);
2106         else 
2107                 term_fontpush(p, TERMFONT_NONE);
2108 
2109         return(1);
2110 }
2111 
2112 
2113 /* ARGSUSED */
2114 static int
2115 termp_sm_pre(DECL_ARGS)
2116 {
2117 
2118         assert(n->child && MDOC_TEXT == n->child->type);
2119         if (0 == strcmp("on", n->child->string)) {


2165                 p->flags |= TERMP_SENTENCE;
2166         } else
2167                 term_word(p, ",");
2168 }
2169 
2170 
2171 /* ARGSUSED */
2172 static int
2173 termp_li_pre(DECL_ARGS)
2174 {
2175 
2176         term_fontpush(p, TERMFONT_NONE);
2177         return(1);
2178 }
2179 
2180 
2181 /* ARGSUSED */
2182 static int
2183 termp_lk_pre(DECL_ARGS)
2184 {
2185         const struct mdoc_node *link, *descr;
2186 
2187         if (NULL == (link = n->child))
2188                 return(0);
2189 
2190         if (NULL != (descr = link->next)) {
2191                 term_fontpush(p, TERMFONT_UNDER);
2192                 while (NULL != descr) {
2193                         term_word(p, descr->string);
2194                         descr = descr->next;
2195                 }




2196                 p->flags |= TERMP_NOSPACE;
2197                 term_word(p, ":");
2198                 term_fontpop(p);
2199         }
2200 
2201         term_fontpush(p, TERMFONT_BOLD);
2202         term_word(p, link->string);
2203         term_fontpop(p);
2204 
2205         return(0);
2206 }
2207 
2208 
2209 /* ARGSUSED */
2210 static int
2211 termp_bk_pre(DECL_ARGS)
2212 {
2213 
2214         switch (n->type) {
2215         case (MDOC_BLOCK):
2216                 break;
2217         case (MDOC_HEAD):
2218                 return(0);
2219         case (MDOC_BODY):
2220                 if (n->parent->args || 0 == n->prev->nchild)
2221                         p->flags |= TERMP_PREKEEP;
2222                 break;


2232 /* ARGSUSED */
2233 static void
2234 termp_bk_post(DECL_ARGS)
2235 {
2236 
2237         if (MDOC_BODY == n->type)
2238                 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2239 }
2240 
2241 /* ARGSUSED */
2242 static void
2243 termp__t_post(DECL_ARGS)
2244 {
2245 
2246         /*
2247          * If we're in an `Rs' and there's a journal present, then quote
2248          * us instead of underlining us (for disambiguation).
2249          */
2250         if (n->parent && MDOC_Rs == n->parent->tok &&
2251                         n->parent->norm->Rs.quote_T)
2252                 termp_quote_post(p, pair, meta, n);
2253 
2254         termp____post(p, pair, meta, n);
2255 }
2256 
2257 /* ARGSUSED */
2258 static int
2259 termp__t_pre(DECL_ARGS)
2260 {
2261 
2262         /*
2263          * If we're in an `Rs' and there's a journal present, then quote
2264          * us instead of underlining us (for disambiguation).
2265          */
2266         if (n->parent && MDOC_Rs == n->parent->tok &&
2267                         n->parent->norm->Rs.quote_T)
2268                 return(termp_quote_pre(p, pair, meta, n));
2269 
2270         term_fontpush(p, TERMFONT_UNDER);
2271         return(1);
2272 }
2273 
2274 /* ARGSUSED */
2275 static int
2276 termp_under_pre(DECL_ARGS)
2277 {
2278 
2279         term_fontpush(p, TERMFONT_UNDER);
2280         return(1);
2281 }