Print this page
Update to 1.12.3.
   1 /*      $Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #ifdef HAVE_CONFIG_H
  19 #include "config.h"
  20 #endif
  21 
  22 #include <sys/types.h>
  23 
  24 #include <assert.h>
  25 #include <ctype.h>
  26 #include <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 "main.h"
  35 
  36 static  void             adjbuf(struct termp *p, int);

  37 static  void             bufferc(struct termp *, char);
  38 static  void             encode(struct termp *, const char *, size_t);
  39 static  void             encode1(struct termp *, int);
  40 
  41 void
  42 term_free(struct termp *p)
  43 {
  44 
  45         if (p->buf)
  46                 free(p->buf);
  47         if (p->symtab)
  48                 mchars_free(p->symtab);
  49 
  50         free(p);
  51 }
  52 
  53 
  54 void
  55 term_begin(struct termp *p, term_margin head, 
  56                 term_margin foot, const void *arg)


  65 
  66 void
  67 term_end(struct termp *p)
  68 {
  69 
  70         (*p->end)(p);
  71 }
  72 
  73 /*
  74  * Flush a line of text.  A "line" is loosely defined as being something
  75  * that should be followed by a newline, regardless of whether it's
  76  * broken apart by newlines getting there.  A line can also be a
  77  * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
  78  * not have a trailing newline.
  79  *
  80  * The following flags may be specified:
  81  *
  82  *  - TERMP_NOBREAK: this is the most important and is used when making
  83  *    columns.  In short: don't print a newline and instead expect the
  84  *    next call to do the padding up to the start of the next column.


  85  *
  86  *  - TERMP_TWOSPACE: make sure there is room for at least two space
  87  *    characters of padding.  Otherwise, rather break the line.
  88  *
  89  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
  90  *    the line is overrun, and don't pad-right if it's underrun.
  91  *
  92  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
  93  *    overrunning, instead save the position and continue at that point
  94  *    when the next invocation.
  95  *
  96  *  In-line line breaking:
  97  *
  98  *  If TERMP_NOBREAK is specified and the line overruns the right
  99  *  margin, it will break and pad-right to the right margin after
 100  *  writing.  If maxrmargin is violated, it will break and continue
 101  *  writing from the right-margin, which will lead to the above scenario
 102  *  upon exit.  Otherwise, the line will break at the right margin.
 103  */
 104 void
 105 term_flushln(struct termp *p)
 106 {
 107         int              i;     /* current input position in p->buf */

 108         size_t           vis;   /* current visual position on output */
 109         size_t           vbl;   /* number of blanks to prepend to output */
 110         size_t           vend;  /* end of word visual position on output */
 111         size_t           bp;    /* visual right border position */
 112         size_t           dv;    /* temporary for visual pos calculations */
 113         int              j;     /* temporary loop index for p->buf */
 114         int              jhy;   /* last hyph before overflow w/r/t j */
 115         size_t           maxvis; /* output position of visible boundary */
 116         size_t           mmax; /* used in calculating bp */
 117 
 118         /*
 119          * First, establish the maximum columns of "visible" content.
 120          * This is usually the difference between the right-margin and
 121          * an indentation, but can be, for tagged lists or columns, a
 122          * small set of values. 





 123          */
 124         assert  (p->rmargin >= p->offset);
 125         dv     = p->rmargin - p->offset;
 126         maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
 127         dv     = p->maxrmargin - p->offset;
 128         mmax   = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
 129 
 130         bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
 131 
 132         /*
 133          * Calculate the required amount of padding.
 134          */
 135         vbl = p->offset + p->overstep > p->viscol ?
 136               p->offset + p->overstep - p->viscol : 0;
 137 
 138         vis = vend = 0;
 139         i = 0;
 140 
 141         while (i < p->col) {
 142                 /*
 143                  * Handle literal tab characters: collapse all
 144                  * subsequent tabs into a single huge set of spaces.
 145                  */

 146                 while (i < p->col && '\t' == p->buf[i]) {
 147                         vend = (vis / p->tabwidth + 1) * p->tabwidth;
 148                         vbl += vend - vis;
 149                         vis = vend;

 150                         i++;
 151                 }
 152 
 153                 /*
 154                  * Count up visible word characters.  Control sequences
 155                  * (starting with the CSI) aren't counted.  A space
 156                  * generates a non-printing word, which is valid (the
 157                  * space is printed according to regular spacing rules).
 158                  */
 159 
 160                 for (j = i, jhy = 0; j < p->col; j++) {
 161                         if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
 162                                 break;
 163 
 164                         /* Back over the the last printed character. */
 165                         if (8 == p->buf[j]) {
 166                                 assert(j);
 167                                 vend -= (*p->width)(p, p->buf[j - 1]);
 168                                 continue;
 169                         }
 170 
 171                         /* Regular word. */
 172                         /* Break at the hyphen point if we overrun. */
 173                         if (vend > vis && vend < bp && 
 174                                         ASCII_HYPH == p->buf[j])
 175                                 jhy = j;
 176 
 177                         vend += (*p->width)(p, p->buf[j]);
 178                 }
 179 
 180                 /*
 181                  * Find out whether we would exceed the right margin.
 182                  * If so, break to the next line.
 183                  */
 184                 if (vend > bp && 0 == jhy && vis > 0) {
 185                         vend -= vis;
 186                         (*p->endline)(p);
 187                         p->viscol = 0;
 188                         if (TERMP_NOBREAK & p->flags) {
 189                                 vbl = p->rmargin;
 190                                 vend += p->rmargin - p->offset;
 191                         } else
 192                                 vbl = p->offset;
 193 
 194                         /* Remove the p->overstep width. */
 195 









 196                         bp += (size_t)p->overstep;
 197                         p->overstep = 0;
 198                 }
 199 
 200                 /* Write out the [remaining] word. */
 201                 for ( ; i < p->col; i++) {
 202                         if (vend > bp && jhy > 0 && i > jhy)
 203                                 break;
 204                         if ('\t' == p->buf[i])
 205                                 break;
 206                         if (' ' == p->buf[i]) {
 207                                 j = i;
 208                                 while (' ' == p->buf[i])
 209                                         i++;
 210                                 dv = (size_t)(i - j) * (*p->width)(p, ' ');
 211                                 vbl += dv;
 212                                 vend += dv;
 213                                 break;
 214                         }
 215                         if (ASCII_NBRSP == p->buf[i]) {
 216                                 vbl += (*p->width)(p, ' ');
 217                                 continue;
 218                         }
 219 
 220                         /*
 221                          * Now we definitely know there will be
 222                          * printable characters to output,
 223                          * so write preceding white space now.
 224                          */
 225                         if (vbl) {
 226                                 (*p->advance)(p, vbl);
 227                                 p->viscol += vbl;
 228                                 vbl = 0;
 229                         }
 230 


 243                 vis = vend;
 244         }
 245 
 246         /*
 247          * If there was trailing white space, it was not printed;
 248          * so reset the cursor position accordingly.
 249          */
 250         if (vis)
 251                 vis -= vbl;
 252 
 253         p->col = 0;
 254         p->overstep = 0;
 255 
 256         if ( ! (TERMP_NOBREAK & p->flags)) {
 257                 p->viscol = 0;
 258                 (*p->endline)(p);
 259                 return;
 260         }
 261 
 262         if (TERMP_HANG & p->flags) {
 263                 /* We need one blank after the tag. */
 264                 p->overstep = (int)(vis - maxvis + (*p->width)(p, ' '));
 265 
 266                 /*
 267                  * Behave exactly the same way as groff:
 268                  * If we have overstepped the margin, temporarily move
 269                  * it to the right and flag the rest of the line to be
 270                  * shorter.
 271                  * If we landed right at the margin, be happy.
 272                  * If we are one step before the margin, temporarily
 273                  * move it one step LEFT and flag the rest of the line
 274                  * to be longer.
 275                  */
 276                 if (p->overstep < -1)
 277                         p->overstep = 0;
 278                 return;
 279 
 280         } else if (TERMP_DANGLE & p->flags)
 281                 return;
 282 
 283         /* If the column was overrun, break the line. */
 284         if (maxvis <= vis +
 285             ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) {
 286                 (*p->endline)(p);
 287                 p->viscol = 0;
 288         }
 289 }
 290 
 291 
 292 /* 
 293  * A newline only breaks an existing line; it won't assert vertical
 294  * space.  All data in the output buffer is flushed prior to the newline
 295  * assertion.
 296  */
 297 void
 298 term_newln(struct termp *p)
 299 {
 300 
 301         p->flags |= TERMP_NOSPACE;
 302         if (p->col || p->viscol)
 303                 term_flushln(p);
 304 }
 305 
 306 
 307 /*
 308  * Asserts a vertical space (a full, empty line-break between lines).
 309  * Note that if used twice, this will cause two blank spaces and so on.
 310  * All data in the output buffer is flushed prior to the newline
 311  * assertion.
 312  */
 313 void
 314 term_vspace(struct termp *p)
 315 {
 316 
 317         term_newln(p);
 318         p->viscol = 0;



 319         (*p->endline)(p);
 320 }
 321 
 322 void
 323 term_fontlast(struct termp *p)
 324 {
 325         enum termfont    f;
 326 
 327         f = p->fontl;
 328         p->fontl = p->fontq[p->fonti];
 329         p->fontq[p->fonti] = f;
 330 }
 331 
 332 
 333 void
 334 term_fontrepl(struct termp *p, enum termfont f)
 335 {
 336 
 337         p->fontl = p->fontq[p->fonti];
 338         p->fontq[p->fonti] = f;


 352 const void *
 353 term_fontq(struct termp *p)
 354 {
 355 
 356         return(&p->fontq[p->fonti]);
 357 }
 358 
 359 
 360 enum termfont
 361 term_fonttop(struct termp *p)
 362 {
 363 
 364         return(p->fontq[p->fonti]);
 365 }
 366 
 367 
 368 void
 369 term_fontpopq(struct termp *p, const void *key)
 370 {
 371 
 372         while (p->fonti >= 0 && key != &p->fontq[p->fonti])
 373                 p->fonti--;
 374         assert(p->fonti >= 0);
 375 }
 376 
 377 
 378 void
 379 term_fontpop(struct termp *p)
 380 {
 381 
 382         assert(p->fonti);
 383         p->fonti--;
 384 }
 385 
 386 /*
 387  * Handle pwords, partial words, which may be either a single word or a
 388  * phrase that cannot be broken down (such as a literal string).  This
 389  * handles word styling.
 390  */
 391 void
 392 term_word(struct termp *p, const char *word)
 393 {

 394         const char      *seq, *cp;
 395         char             c;
 396         int              sz, uc;
 397         size_t           ssz;
 398         enum mandoc_esc  esc;
 399 
 400         if ( ! (TERMP_NOSPACE & p->flags)) {
 401                 if ( ! (TERMP_KEEP & p->flags)) {
 402                         if (TERMP_PREKEEP & p->flags)
 403                                 p->flags |= TERMP_KEEP;
 404                         bufferc(p, ' ');
 405                         if (TERMP_SENTENCE & p->flags)
 406                                 bufferc(p, ' ');
 407                 } else
 408                         bufferc(p, ASCII_NBRSP);
 409         }


 410 
 411         if ( ! (p->flags & TERMP_NONOSPACE))
 412                 p->flags &= ~TERMP_NOSPACE;
 413         else
 414                 p->flags |= TERMP_NOSPACE;
 415 
 416         p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM);
 417 
 418         while ('\0' != *word) {
 419                 if ((ssz = strcspn(word, "\\")) > 0)














 420                         encode(p, word, ssz);
 421 
 422                 word += (int)ssz;
 423                 if ('\\' != *word)
 424                         continue;

 425 
 426                 word++;
 427                 esc = mandoc_escape(&word, &seq, &sz);
 428                 if (ESCAPE_ERROR == esc)
 429                         break;
 430 
 431                 if (TERMENC_ASCII != p->enc)
 432                         switch (esc) {
 433                         case (ESCAPE_UNICODE):
 434                                 uc = mchars_num2uc(seq + 1, sz - 1);
 435                                 if ('\0' == uc)
 436                                         break;
 437                                 encode1(p, uc);
 438                                 continue;
 439                         case (ESCAPE_SPECIAL):
 440                                 uc = mchars_spec2cp(p->symtab, seq, sz);
 441                                 if (uc <= 0)
 442                                         break;
 443                                 encode1(p, uc);
 444                                 continue;


 451                         encode1(p, '?');
 452                         break;
 453                 case (ESCAPE_NUMBERED):
 454                         c = mchars_num2char(seq, sz);
 455                         if ('\0' != c)
 456                                 encode(p, &c, 1);
 457                         break;
 458                 case (ESCAPE_SPECIAL):
 459                         cp = mchars_spec2str(p->symtab, seq, sz, &ssz);
 460                         if (NULL != cp) 
 461                                 encode(p, cp, ssz);
 462                         else if (1 == ssz)
 463                                 encode(p, seq, sz);
 464                         break;
 465                 case (ESCAPE_FONTBOLD):
 466                         term_fontrepl(p, TERMFONT_BOLD);
 467                         break;
 468                 case (ESCAPE_FONTITALIC):
 469                         term_fontrepl(p, TERMFONT_UNDER);
 470                         break;



 471                 case (ESCAPE_FONT):
 472                         /* FALLTHROUGH */
 473                 case (ESCAPE_FONTROMAN):
 474                         term_fontrepl(p, TERMFONT_NONE);
 475                         break;
 476                 case (ESCAPE_FONTPREV):
 477                         term_fontlast(p);
 478                         break;
 479                 case (ESCAPE_NOSPACE):
 480                         if ('\0' == *word)


 481                                 p->flags |= TERMP_NOSPACE;
 482                         break;



 483                 default:
 484                         break;
 485                 }
 486         }

 487 }
 488 
 489 static void
 490 adjbuf(struct termp *p, int sz)
 491 {
 492 
 493         if (0 == p->maxcols)
 494                 p->maxcols = 1024;
 495         while (sz >= p->maxcols)
 496                 p->maxcols <<= 2;
 497 
 498         p->buf = mandoc_realloc
 499                 (p->buf, sizeof(int) * (size_t)p->maxcols);
 500 }
 501 
 502 static void
 503 bufferc(struct termp *p, char c)
 504 {
 505 
 506         if (p->col + 1 >= p->maxcols)
 507                 adjbuf(p, p->col + 1);
 508 
 509         p->buf[p->col++] = c;
 510 }
 511 
 512 /*
 513  * See encode().
 514  * Do this for a single (probably unicode) value.
 515  * Does not check for non-decorated glyphs.
 516  */
 517 static void
 518 encode1(struct termp *p, int c)
 519 {
 520         enum termfont     f;
 521 
 522         if (p->col + 4 >= p->maxcols)
 523                 adjbuf(p, p->col + 4);


 524 



 525         f = term_fonttop(p);
 526 
 527         if (TERMFONT_NONE == f) {
 528                 p->buf[p->col++] = c;
 529                 return;
 530         } else if (TERMFONT_UNDER == f) {
 531                 p->buf[p->col++] = '_';
 532         } else





 533                 p->buf[p->col++] = c;
 534 
 535         p->buf[p->col++] = 8;

 536         p->buf[p->col++] = c;
 537 }
 538 
 539 static void
 540 encode(struct termp *p, const char *word, size_t sz)
 541 {
 542         enum termfont     f;
 543         int               i, len;
 544 
 545         /* LINTED */
 546         len = sz;


 547 
 548         /*
 549          * Encode and buffer a string of characters.  If the current
 550          * font mode is unset, buffer directly, else encode then buffer
 551          * character by character.
 552          */
 553 
 554         if (TERMFONT_NONE == (f = term_fonttop(p))) {
 555                 if (p->col + len >= p->maxcols) 
 556                         adjbuf(p, p->col + len);
 557                 for (i = 0; i < len; i++)
 558                         p->buf[p->col++] = word[i];
 559                 return;
 560         }
 561 
 562         /* Pre-buffer, assuming worst-case. */
 563 
 564         if (p->col + 1 + (len * 3) >= p->maxcols)
 565                 adjbuf(p, p->col + 1 + (len * 3));
 566 
 567         for (i = 0; i < len; i++) {
 568                 if (ASCII_HYPH != word[i] &&
 569                     ! isgraph((unsigned char)word[i])) {
 570                         p->buf[p->col++] = word[i];
 571                         continue;
 572                 }
 573 
 574                 if (TERMFONT_UNDER == f)
 575                         p->buf[p->col++] = '_';
 576                 else if (ASCII_HYPH == word[i])
 577                         p->buf[p->col++] = '-';
 578                 else
 579                         p->buf[p->col++] = word[i];
 580 
 581                 p->buf[p->col++] = 8;
 582                 p->buf[p->col++] = word[i];
 583         }
 584 }
 585 
 586 size_t
 587 term_len(const struct termp *p, size_t sz)
 588 {
 589 
 590         return((*p->width)(p, ' ') * sz);
 591 }
 592 



 593 







 594 size_t
 595 term_strlen(const struct termp *p, const char *cp)
 596 {
 597         size_t           sz, rsz, i;
 598         int              ssz, c;
 599         const char      *seq, *rhs;
 600         enum mandoc_esc  esc;
 601         static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' };
 602 
 603         /*
 604          * Account for escaped sequences within string length
 605          * calculations.  This follows the logic in term_word() as we
 606          * must calculate the width of produced strings.
 607          */
 608 
 609         sz = 0;

 610         while ('\0' != *cp) {
 611                 rsz = strcspn(cp, rej);
 612                 for (i = 0; i < rsz; i++)
 613                         sz += (*p->width)(p, *cp++);
 614 
 615                 c = 0;
 616                 switch (*cp) {
 617                 case ('\\'):
 618                         cp++;
 619                         esc = mandoc_escape(&cp, &seq, &ssz);
 620                         if (ESCAPE_ERROR == esc)
 621                                 return(sz);
 622 
 623                         if (TERMENC_ASCII != p->enc)
 624                                 switch (esc) {
 625                                 case (ESCAPE_UNICODE):
 626                                         c = mchars_num2uc
 627                                                 (seq + 1, ssz - 1);
 628                                         if ('\0' == c)
 629                                                 break;
 630                                         sz += (*p->width)(p, c);
 631                                         continue;
 632                                 case (ESCAPE_SPECIAL):
 633                                         c = mchars_spec2cp
 634                                                 (p->symtab, seq, ssz);
 635                                         if (c <= 0)
 636                                                 break;
 637                                         sz += (*p->width)(p, c);
 638                                         continue;
 639                                 default:
 640                                         break;
 641                                 }
 642 
 643                         rhs = NULL;
 644 
 645                         switch (esc) {
 646                         case (ESCAPE_UNICODE):
 647                                 sz += (*p->width)(p, '?');
 648                                 break;
 649                         case (ESCAPE_NUMBERED):
 650                                 c = mchars_num2char(seq, ssz);
 651                                 if ('\0' != c)
 652                                         sz += (*p->width)(p, c);
 653                                 break;
 654                         case (ESCAPE_SPECIAL):
 655                                 rhs = mchars_spec2str
 656                                         (p->symtab, seq, ssz, &rsz);
 657 
 658                                 if (ssz != 1 || rhs)
 659                                         break;
 660 
 661                                 rhs = seq;
 662                                 rsz = ssz;
 663                                 break;



 664                         default:
 665                                 break;
 666                         }
 667 
 668                         if (NULL == rhs)
 669                                 break;
 670 





 671                         for (i = 0; i < rsz; i++)
 672                                 sz += (*p->width)(p, *rhs++);
 673                         break;
 674                 case (ASCII_NBRSP):
 675                         sz += (*p->width)(p, ' ');
 676                         cp++;
 677                         break;
 678                 case (ASCII_HYPH):
 679                         sz += (*p->width)(p, '-');
 680                         cp++;
 681                         break;
 682                 default:
 683                         break;
 684                 }
 685         }
 686 
 687         return(sz);
 688 }
 689 
 690 /* ARGSUSED */
 691 size_t
 692 term_vspan(const struct termp *p, const struct roffsu *su)
 693 {
 694         double           r;
 695 
 696         switch (su->unit) {
 697         case (SCALE_CM):
 698                 r = su->scale * 2;
 699                 break;


   1 /*      $Id: term.c,v 1.214 2013/12/25 00:39:31 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #ifdef HAVE_CONFIG_H
  19 #include "config.h"
  20 #endif
  21 
  22 #include <sys/types.h>
  23 
  24 #include <assert.h>
  25 #include <ctype.h>
  26 #include <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 "main.h"
  35 
  36 static  size_t           cond_width(const struct termp *, int, int *);
  37 static  void             adjbuf(struct termp *p, size_t);
  38 static  void             bufferc(struct termp *, char);
  39 static  void             encode(struct termp *, const char *, size_t);
  40 static  void             encode1(struct termp *, int);
  41 
  42 void
  43 term_free(struct termp *p)
  44 {
  45 
  46         if (p->buf)
  47                 free(p->buf);
  48         if (p->symtab)
  49                 mchars_free(p->symtab);
  50 
  51         free(p);
  52 }
  53 
  54 
  55 void
  56 term_begin(struct termp *p, term_margin head, 
  57                 term_margin foot, const void *arg)


  66 
  67 void
  68 term_end(struct termp *p)
  69 {
  70 
  71         (*p->end)(p);
  72 }
  73 
  74 /*
  75  * Flush a line of text.  A "line" is loosely defined as being something
  76  * that should be followed by a newline, regardless of whether it's
  77  * broken apart by newlines getting there.  A line can also be a
  78  * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
  79  * not have a trailing newline.
  80  *
  81  * The following flags may be specified:
  82  *
  83  *  - TERMP_NOBREAK: this is the most important and is used when making
  84  *    columns.  In short: don't print a newline and instead expect the
  85  *    next call to do the padding up to the start of the next column.
  86  *    p->trailspace may be set to 0, 1, or 2, depending on how many
  87  *    space characters are required at the end of the column.
  88  *



  89  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
  90  *    the line is overrun, and don't pad-right if it's underrun.
  91  *
  92  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
  93  *    overrunning, instead save the position and continue at that point
  94  *    when the next invocation.
  95  *
  96  *  In-line line breaking:
  97  *
  98  *  If TERMP_NOBREAK is specified and the line overruns the right
  99  *  margin, it will break and pad-right to the right margin after
 100  *  writing.  If maxrmargin is violated, it will break and continue
 101  *  writing from the right-margin, which will lead to the above scenario
 102  *  upon exit.  Otherwise, the line will break at the right margin.
 103  */
 104 void
 105 term_flushln(struct termp *p)
 106 {
 107         size_t           i;     /* current input position in p->buf */
 108         int              ntab;  /* number of tabs to prepend */
 109         size_t           vis;   /* current visual position on output */
 110         size_t           vbl;   /* number of blanks to prepend to output */
 111         size_t           vend;  /* end of word visual position on output */
 112         size_t           bp;    /* visual right border position */
 113         size_t           dv;    /* temporary for visual pos calculations */
 114         size_t           j;     /* temporary loop index for p->buf */
 115         size_t           jhy;   /* last hyph before overflow w/r/t j */
 116         size_t           maxvis; /* output position of visible boundary */
 117         size_t           mmax; /* used in calculating bp */
 118 
 119         /*
 120          * First, establish the maximum columns of "visible" content.
 121          * This is usually the difference between the right-margin and
 122          * an indentation, but can be, for tagged lists or columns, a
 123          * small set of values.
 124          *
 125          * The following unsigned-signed subtractions look strange,
 126          * but they are actually correct.  If the int p->overstep
 127          * is negative, it gets sign extended.  Subtracting that
 128          * very large size_t effectively adds a small number to dv.
 129          */
 130         assert  (p->rmargin >= p->offset);
 131         dv     = p->rmargin - p->offset;
 132         maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
 133         dv     = p->maxrmargin - p->offset;
 134         mmax   = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
 135 
 136         bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
 137 
 138         /*
 139          * Calculate the required amount of padding.
 140          */
 141         vbl = p->offset + p->overstep > p->viscol ?
 142               p->offset + p->overstep - p->viscol : 0;
 143 
 144         vis = vend = 0;
 145         i = 0;
 146 
 147         while (i < p->col) {
 148                 /*
 149                  * Handle literal tab characters: collapse all
 150                  * subsequent tabs into a single huge set of spaces.
 151                  */
 152                 ntab = 0;
 153                 while (i < p->col && '\t' == p->buf[i]) {
 154                         vend = (vis / p->tabwidth + 1) * p->tabwidth;
 155                         vbl += vend - vis;
 156                         vis = vend;
 157                         ntab++;
 158                         i++;
 159                 }
 160 
 161                 /*
 162                  * Count up visible word characters.  Control sequences
 163                  * (starting with the CSI) aren't counted.  A space
 164                  * generates a non-printing word, which is valid (the
 165                  * space is printed according to regular spacing rules).
 166                  */
 167 
 168                 for (j = i, jhy = 0; j < p->col; j++) {
 169                         if (' ' == p->buf[j] || '\t' == p->buf[j])
 170                                 break;
 171 
 172                         /* Back over the the last printed character. */
 173                         if (8 == p->buf[j]) {
 174                                 assert(j);
 175                                 vend -= (*p->width)(p, p->buf[j - 1]);
 176                                 continue;
 177                         }
 178 
 179                         /* Regular word. */
 180                         /* Break at the hyphen point if we overrun. */
 181                         if (vend > vis && vend < bp && 
 182                                         ASCII_HYPH == p->buf[j])
 183                                 jhy = j;
 184 
 185                         vend += (*p->width)(p, p->buf[j]);
 186                 }
 187 
 188                 /*
 189                  * Find out whether we would exceed the right margin.
 190                  * If so, break to the next line.
 191                  */
 192                 if (vend > bp && 0 == jhy && vis > 0) {
 193                         vend -= vis;
 194                         (*p->endline)(p);
 195                         p->viscol = 0;
 196                         if (TERMP_NOBREAK & p->flags) {
 197                                 vbl = p->rmargin;
 198                                 vend += p->rmargin - p->offset;
 199                         } else
 200                                 vbl = p->offset;
 201 
 202                         /* use pending tabs on the new line */
 203 
 204                         if (0 < ntab)
 205                                 vbl += ntab * p->tabwidth;
 206 
 207                         /*
 208                          * Remove the p->overstep width.
 209                          * Again, if p->overstep is negative,
 210                          * sign extension does the right thing.
 211                          */
 212 
 213                         bp += (size_t)p->overstep;
 214                         p->overstep = 0;
 215                 }
 216 
 217                 /* Write out the [remaining] word. */
 218                 for ( ; i < p->col; i++) {
 219                         if (vend > bp && jhy > 0 && i > jhy)
 220                                 break;
 221                         if ('\t' == p->buf[i])
 222                                 break;
 223                         if (' ' == p->buf[i]) {
 224                                 j = i;
 225                                 while (' ' == p->buf[i])
 226                                         i++;
 227                                 dv = (i - j) * (*p->width)(p, ' ');
 228                                 vbl += dv;
 229                                 vend += dv;
 230                                 break;
 231                         }
 232                         if (ASCII_NBRSP == p->buf[i]) {
 233                                 vbl += (*p->width)(p, ' ');
 234                                 continue;
 235                         }
 236 
 237                         /*
 238                          * Now we definitely know there will be
 239                          * printable characters to output,
 240                          * so write preceding white space now.
 241                          */
 242                         if (vbl) {
 243                                 (*p->advance)(p, vbl);
 244                                 p->viscol += vbl;
 245                                 vbl = 0;
 246                         }
 247 


 260                 vis = vend;
 261         }
 262 
 263         /*
 264          * If there was trailing white space, it was not printed;
 265          * so reset the cursor position accordingly.
 266          */
 267         if (vis)
 268                 vis -= vbl;
 269 
 270         p->col = 0;
 271         p->overstep = 0;
 272 
 273         if ( ! (TERMP_NOBREAK & p->flags)) {
 274                 p->viscol = 0;
 275                 (*p->endline)(p);
 276                 return;
 277         }
 278 
 279         if (TERMP_HANG & p->flags) {
 280                 p->overstep = (int)(vis - maxvis +
 281                                 p->trailspace * (*p->width)(p, ' '));
 282 
 283                 /*

 284                  * If we have overstepped the margin, temporarily move
 285                  * it to the right and flag the rest of the line to be
 286                  * shorter.
 287                  * If there is a request to keep the columns together,
 288                  * allow negative overstep when the column is not full.


 289                  */
 290                 if (p->trailspace && p->overstep < 0)
 291                         p->overstep = 0;
 292                 return;
 293 
 294         } else if (TERMP_DANGLE & p->flags)
 295                 return;
 296 
 297         /* If the column was overrun, break the line. */
 298         if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) {

 299                 (*p->endline)(p);
 300                 p->viscol = 0;
 301         }
 302 }
 303 
 304 
 305 /* 
 306  * A newline only breaks an existing line; it won't assert vertical
 307  * space.  All data in the output buffer is flushed prior to the newline
 308  * assertion.
 309  */
 310 void
 311 term_newln(struct termp *p)
 312 {
 313 
 314         p->flags |= TERMP_NOSPACE;
 315         if (p->col || p->viscol)
 316                 term_flushln(p);
 317 }
 318 
 319 
 320 /*
 321  * Asserts a vertical space (a full, empty line-break between lines).
 322  * Note that if used twice, this will cause two blank spaces and so on.
 323  * All data in the output buffer is flushed prior to the newline
 324  * assertion.
 325  */
 326 void
 327 term_vspace(struct termp *p)
 328 {
 329 
 330         term_newln(p);
 331         p->viscol = 0;
 332         if (0 < p->skipvsp)
 333                 p->skipvsp--;
 334         else
 335                 (*p->endline)(p);
 336 }
 337 
 338 void
 339 term_fontlast(struct termp *p)
 340 {
 341         enum termfont    f;
 342 
 343         f = p->fontl;
 344         p->fontl = p->fontq[p->fonti];
 345         p->fontq[p->fonti] = f;
 346 }
 347 
 348 
 349 void
 350 term_fontrepl(struct termp *p, enum termfont f)
 351 {
 352 
 353         p->fontl = p->fontq[p->fonti];
 354         p->fontq[p->fonti] = f;


 368 const void *
 369 term_fontq(struct termp *p)
 370 {
 371 
 372         return(&p->fontq[p->fonti]);
 373 }
 374 
 375 
 376 enum termfont
 377 term_fonttop(struct termp *p)
 378 {
 379 
 380         return(p->fontq[p->fonti]);
 381 }
 382 
 383 
 384 void
 385 term_fontpopq(struct termp *p, const void *key)
 386 {
 387 
 388         while (p->fonti >= 0 && key < (void *)(p->fontq + p->fonti))
 389                 p->fonti--;
 390         assert(p->fonti >= 0);
 391 }
 392 
 393 
 394 void
 395 term_fontpop(struct termp *p)
 396 {
 397 
 398         assert(p->fonti);
 399         p->fonti--;
 400 }
 401 
 402 /*
 403  * Handle pwords, partial words, which may be either a single word or a
 404  * phrase that cannot be broken down (such as a literal string).  This
 405  * handles word styling.
 406  */
 407 void
 408 term_word(struct termp *p, const char *word)
 409 {
 410         const char       nbrsp[2] = { ASCII_NBRSP, 0 };
 411         const char      *seq, *cp;
 412         char             c;
 413         int              sz, uc;
 414         size_t           ssz;
 415         enum mandoc_esc  esc;
 416 
 417         if ( ! (TERMP_NOSPACE & p->flags)) {
 418                 if ( ! (TERMP_KEEP & p->flags)) {


 419                         bufferc(p, ' ');
 420                         if (TERMP_SENTENCE & p->flags)
 421                                 bufferc(p, ' ');
 422                 } else
 423                         bufferc(p, ASCII_NBRSP);
 424         }
 425         if (TERMP_PREKEEP & p->flags)
 426                 p->flags |= TERMP_KEEP;
 427 
 428         if ( ! (p->flags & TERMP_NONOSPACE))
 429                 p->flags &= ~TERMP_NOSPACE;
 430         else
 431                 p->flags |= TERMP_NOSPACE;
 432 
 433         p->flags &= ~TERMP_SENTENCE;
 434 
 435         while ('\0' != *word) {
 436                 if ('\\' != *word) {
 437                         if (TERMP_SKIPCHAR & p->flags) {
 438                                 p->flags &= ~TERMP_SKIPCHAR;
 439                                 word++;
 440                                 continue;
 441                         }
 442                         if (TERMP_NBRWORD & p->flags) {
 443                                 if (' ' == *word) {
 444                                         encode(p, nbrsp, 1);
 445                                         word++;
 446                                         continue;
 447                                 }
 448                                 ssz = strcspn(word, "\\ ");
 449                         } else
 450                                 ssz = strcspn(word, "\\");
 451                         encode(p, word, ssz);

 452                         word += (int)ssz;

 453                         continue;
 454                 }
 455 
 456                 word++;
 457                 esc = mandoc_escape(&word, &seq, &sz);
 458                 if (ESCAPE_ERROR == esc)
 459                         break;
 460 
 461                 if (TERMENC_ASCII != p->enc)
 462                         switch (esc) {
 463                         case (ESCAPE_UNICODE):
 464                                 uc = mchars_num2uc(seq + 1, sz - 1);
 465                                 if ('\0' == uc)
 466                                         break;
 467                                 encode1(p, uc);
 468                                 continue;
 469                         case (ESCAPE_SPECIAL):
 470                                 uc = mchars_spec2cp(p->symtab, seq, sz);
 471                                 if (uc <= 0)
 472                                         break;
 473                                 encode1(p, uc);
 474                                 continue;


 481                         encode1(p, '?');
 482                         break;
 483                 case (ESCAPE_NUMBERED):
 484                         c = mchars_num2char(seq, sz);
 485                         if ('\0' != c)
 486                                 encode(p, &c, 1);
 487                         break;
 488                 case (ESCAPE_SPECIAL):
 489                         cp = mchars_spec2str(p->symtab, seq, sz, &ssz);
 490                         if (NULL != cp) 
 491                                 encode(p, cp, ssz);
 492                         else if (1 == ssz)
 493                                 encode(p, seq, sz);
 494                         break;
 495                 case (ESCAPE_FONTBOLD):
 496                         term_fontrepl(p, TERMFONT_BOLD);
 497                         break;
 498                 case (ESCAPE_FONTITALIC):
 499                         term_fontrepl(p, TERMFONT_UNDER);
 500                         break;
 501                 case (ESCAPE_FONTBI):
 502                         term_fontrepl(p, TERMFONT_BI);
 503                         break;
 504                 case (ESCAPE_FONT):
 505                         /* FALLTHROUGH */
 506                 case (ESCAPE_FONTROMAN):
 507                         term_fontrepl(p, TERMFONT_NONE);
 508                         break;
 509                 case (ESCAPE_FONTPREV):
 510                         term_fontlast(p);
 511                         break;
 512                 case (ESCAPE_NOSPACE):
 513                         if (TERMP_SKIPCHAR & p->flags)
 514                                 p->flags &= ~TERMP_SKIPCHAR;
 515                         else if ('\0' == *word)
 516                                 p->flags |= TERMP_NOSPACE;
 517                         break;
 518                 case (ESCAPE_SKIPCHAR):
 519                         p->flags |= TERMP_SKIPCHAR;
 520                         break;
 521                 default:
 522                         break;
 523                 }
 524         }
 525         p->flags &= ~TERMP_NBRWORD;
 526 }
 527 
 528 static void
 529 adjbuf(struct termp *p, size_t sz)
 530 {
 531 
 532         if (0 == p->maxcols)
 533                 p->maxcols = 1024;
 534         while (sz >= p->maxcols)
 535                 p->maxcols <<= 2;
 536 
 537         p->buf = mandoc_realloc(p->buf, sizeof(int) * p->maxcols);

 538 }
 539 
 540 static void
 541 bufferc(struct termp *p, char c)
 542 {
 543 
 544         if (p->col + 1 >= p->maxcols)
 545                 adjbuf(p, p->col + 1);
 546 
 547         p->buf[p->col++] = c;
 548 }
 549 
 550 /*
 551  * See encode().
 552  * Do this for a single (probably unicode) value.
 553  * Does not check for non-decorated glyphs.
 554  */
 555 static void
 556 encode1(struct termp *p, int c)
 557 {
 558         enum termfont     f;
 559 
 560         if (TERMP_SKIPCHAR & p->flags) {
 561                 p->flags &= ~TERMP_SKIPCHAR;
 562                 return;
 563         }
 564 
 565         if (p->col + 6 >= p->maxcols)
 566                 adjbuf(p, p->col + 6);
 567 
 568         f = term_fonttop(p);
 569 
 570         if (TERMFONT_UNDER == f || TERMFONT_BI == f) {



 571                 p->buf[p->col++] = '_';
 572                 p->buf[p->col++] = 8;
 573         }
 574         if (TERMFONT_BOLD == f || TERMFONT_BI == f) {
 575                 if (ASCII_HYPH == c)
 576                         p->buf[p->col++] = '-';
 577                 else
 578                         p->buf[p->col++] = c;

 579                 p->buf[p->col++] = 8;
 580         }
 581         p->buf[p->col++] = c;
 582 }
 583 
 584 static void
 585 encode(struct termp *p, const char *word, size_t sz)
 586 {
 587         size_t            i;

 588 
 589         if (TERMP_SKIPCHAR & p->flags) {
 590                 p->flags &= ~TERMP_SKIPCHAR;
 591                 return;
 592         }
 593 
 594         /*
 595          * Encode and buffer a string of characters.  If the current
 596          * font mode is unset, buffer directly, else encode then buffer
 597          * character by character.
 598          */
 599 
 600         if (TERMFONT_NONE == term_fonttop(p)) {
 601                 if (p->col + sz >= p->maxcols) 
 602                         adjbuf(p, p->col + sz);
 603                 for (i = 0; i < sz; i++)
 604                         p->buf[p->col++] = word[i];
 605                 return;
 606         }
 607 
 608         /* Pre-buffer, assuming worst-case. */
 609 
 610         if (p->col + 1 + (sz * 5) >= p->maxcols)
 611                 adjbuf(p, p->col + 1 + (sz * 5));
 612 
 613         for (i = 0; i < sz; i++) {
 614                 if (ASCII_HYPH == word[i] ||
 615                     isgraph((unsigned char)word[i]))
 616                         encode1(p, word[i]);







 617                 else
 618                         p->buf[p->col++] = word[i];



 619         }
 620 }
 621 
 622 size_t
 623 term_len(const struct termp *p, size_t sz)
 624 {
 625 
 626         return((*p->width)(p, ' ') * sz);
 627 }
 628 
 629 static size_t
 630 cond_width(const struct termp *p, int c, int *skip)
 631 {
 632 
 633         if (*skip) {
 634                 (*skip) = 0;
 635                 return(0);
 636         } else
 637                 return((*p->width)(p, c));
 638 }
 639 
 640 size_t
 641 term_strlen(const struct termp *p, const char *cp)
 642 {
 643         size_t           sz, rsz, i;
 644         int              ssz, skip, c;
 645         const char      *seq, *rhs;
 646         enum mandoc_esc  esc;
 647         static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' };
 648 
 649         /*
 650          * Account for escaped sequences within string length
 651          * calculations.  This follows the logic in term_word() as we
 652          * must calculate the width of produced strings.
 653          */
 654 
 655         sz = 0;
 656         skip = 0;
 657         while ('\0' != *cp) {
 658                 rsz = strcspn(cp, rej);
 659                 for (i = 0; i < rsz; i++)
 660                         sz += cond_width(p, *cp++, &skip);
 661 
 662                 c = 0;
 663                 switch (*cp) {
 664                 case ('\\'):
 665                         cp++;
 666                         esc = mandoc_escape(&cp, &seq, &ssz);
 667                         if (ESCAPE_ERROR == esc)
 668                                 return(sz);
 669 
 670                         if (TERMENC_ASCII != p->enc)
 671                                 switch (esc) {
 672                                 case (ESCAPE_UNICODE):
 673                                         c = mchars_num2uc
 674                                                 (seq + 1, ssz - 1);
 675                                         if ('\0' == c)
 676                                                 break;
 677                                         sz += cond_width(p, c, &skip);
 678                                         continue;
 679                                 case (ESCAPE_SPECIAL):
 680                                         c = mchars_spec2cp
 681                                                 (p->symtab, seq, ssz);
 682                                         if (c <= 0)
 683                                                 break;
 684                                         sz += cond_width(p, c, &skip);
 685                                         continue;
 686                                 default:
 687                                         break;
 688                                 }
 689 
 690                         rhs = NULL;
 691 
 692                         switch (esc) {
 693                         case (ESCAPE_UNICODE):
 694                                 sz += cond_width(p, '?', &skip);
 695                                 break;
 696                         case (ESCAPE_NUMBERED):
 697                                 c = mchars_num2char(seq, ssz);
 698                                 if ('\0' != c)
 699                                         sz += cond_width(p, c, &skip);
 700                                 break;
 701                         case (ESCAPE_SPECIAL):
 702                                 rhs = mchars_spec2str
 703                                         (p->symtab, seq, ssz, &rsz);
 704 
 705                                 if (ssz != 1 || rhs)
 706                                         break;
 707 
 708                                 rhs = seq;
 709                                 rsz = ssz;
 710                                 break;
 711                         case (ESCAPE_SKIPCHAR):
 712                                 skip = 1;
 713                                 break;
 714                         default:
 715                                 break;
 716                         }
 717 
 718                         if (NULL == rhs)
 719                                 break;
 720 
 721                         if (skip) {
 722                                 skip = 0;
 723                                 break;
 724                         }
 725 
 726                         for (i = 0; i < rsz; i++)
 727                                 sz += (*p->width)(p, *rhs++);
 728                         break;
 729                 case (ASCII_NBRSP):
 730                         sz += cond_width(p, ' ', &skip);
 731                         cp++;
 732                         break;
 733                 case (ASCII_HYPH):
 734                         sz += cond_width(p, '-', &skip);
 735                         cp++;
 736                         break;
 737                 default:
 738                         break;
 739                 }
 740         }
 741 
 742         return(sz);
 743 }
 744 
 745 /* ARGSUSED */
 746 size_t
 747 term_vspan(const struct termp *p, const struct roffsu *su)
 748 {
 749         double           r;
 750 
 751         switch (su->unit) {
 752         case (SCALE_CM):
 753                 r = su->scale * 2;
 754                 break;