]>
git.cameronkatri.com Git - mandoc.git/blob - term.c
1 /* $Id: term.c,v 1.264 2017/06/04 22:44:15 schwarze Exp $ */
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
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.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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.
20 #include <sys/types.h>
29 #include "mandoc_aux.h"
34 static size_t cond_width(const struct termp
*, int, int *);
35 static void adjbuf(struct termp
*p
, size_t);
36 static void bufferc(struct termp
*, char);
37 static void encode(struct termp
*, const char *, size_t);
38 static void encode1(struct termp
*, int);
39 static void endline(struct termp
*);
43 term_free(struct termp
*p
)
52 term_begin(struct termp
*p
, term_margin head
,
53 term_margin foot
, const struct roff_meta
*arg
)
63 term_end(struct termp
*p
)
70 * Flush a chunk of text. By default, break the output line each time
71 * the right margin is reached, and continue output on the next line
72 * at the same offset as the chunk itself. By default, also break the
73 * output line at the end of the chunk.
74 * The following flags may be specified:
76 * - TERMP_NOBREAK: Do not break the output line at the right margin,
77 * but only at the max right margin. Also, do not break the output
78 * line at the end of the chunk, such that the next call can pad to
79 * the next column. However, if less than p->trailspace blanks,
80 * which can be 0, 1, or 2, remain to the right margin, the line
82 * - TERMP_BRTRSP: Consider trailing whitespace significant
83 * when deciding whether the chunk fits or not.
84 * - TERMP_BRIND: If the chunk does not fit and the output line has
85 * to be broken, start the next line at the right margin instead
86 * of at the offset. Used together with TERMP_NOBREAK for the tags
87 * in various kinds of tagged lists.
88 * - TERMP_HANG: Do not break the output line at the right margin,
89 * append the next chunk after it even if this one is too long.
90 * To be used together with TERMP_NOBREAK.
91 * - TERMP_NOPAD: Start writing at the current position,
92 * do not pad with blank characters up to the offset.
95 term_flushln(struct termp
*p
)
97 size_t i
; /* current input position in p->buf */
98 int ntab
; /* number of tabs to prepend */
99 size_t vis
; /* current visual position on output */
100 size_t vbl
; /* number of blanks to prepend to output */
101 size_t vend
; /* end of word visual position on output */
102 size_t bp
; /* visual right border position */
103 size_t dv
; /* temporary for visual pos calculations */
104 size_t j
; /* temporary loop index for p->buf */
105 size_t jhy
; /* last hyph before overflow w/r/t j */
106 size_t maxvis
; /* output position of visible boundary */
108 vbl
= (p
->flags
& TERMP_NOPAD
) || p
->offset
< p
->viscol
? 0 :
109 p
->offset
- p
->viscol
;
110 if (p
->minbl
&& vbl
< p
->minbl
)
112 maxvis
= p
->rmargin
> p
->viscol
+ vbl
?
113 p
->rmargin
- p
->viscol
- vbl
: 0;
114 bp
= !(p
->flags
& TERMP_NOBREAK
) ? maxvis
:
115 p
->maxrmargin
> p
->viscol
+ vbl
?
116 p
->maxrmargin
- p
->viscol
- vbl
: 0;
122 * Handle literal tab characters: collapse all
123 * subsequent tabs into a single huge set of spaces.
126 while (i
< p
->col
&& p
->buf
[i
] == '\t') {
127 vend
= term_tab_next(vis
);
135 * Count up visible word characters. Control sequences
136 * (starting with the CSI) aren't counted. A space
137 * generates a non-printing word, which is valid (the
138 * space is printed according to regular spacing rules).
141 for (j
= i
, jhy
= 0; j
< p
->col
; j
++) {
142 if (' ' == p
->buf
[j
] || '\t' == p
->buf
[j
])
145 /* Back over the last printed character. */
146 if (8 == p
->buf
[j
]) {
148 vend
-= (*p
->width
)(p
, p
->buf
[j
- 1]);
153 /* Break at the hyphen point if we overrun. */
154 if (vend
> vis
&& vend
< bp
&&
155 (ASCII_HYPH
== p
->buf
[j
] ||
156 ASCII_BREAK
== p
->buf
[j
]))
160 * Hyphenation now decided, put back a real
161 * hyphen such that we get the correct width.
163 if (ASCII_HYPH
== p
->buf
[j
])
166 vend
+= (*p
->width
)(p
, p
->buf
[j
]);
170 * Find out whether we would exceed the right margin.
171 * If so, break to the next line.
173 if (vend
> bp
&& 0 == jhy
&& vis
> 0 &&
174 (p
->flags
& TERMP_BRNEVER
) == 0) {
178 /* Use pending tabs on the new line. */
182 vbl
= term_tab_next(vbl
);
184 /* Re-establish indentation. */
186 if (p
->flags
& TERMP_BRIND
)
190 maxvis
= p
->rmargin
> vbl
? p
->rmargin
- vbl
: 0;
191 bp
= !(p
->flags
& TERMP_NOBREAK
) ? maxvis
:
192 p
->maxrmargin
> vbl
? p
->maxrmargin
- vbl
: 0;
195 /* Write out the [remaining] word. */
196 for ( ; i
< p
->col
; i
++) {
197 if (vend
> bp
&& jhy
> 0 && i
> jhy
)
199 if ('\t' == p
->buf
[i
])
201 if (' ' == p
->buf
[i
]) {
203 while (i
< p
->col
&& ' ' == p
->buf
[i
])
205 dv
= (i
- j
) * (*p
->width
)(p
, ' ');
210 if (ASCII_NBRSP
== p
->buf
[i
]) {
211 vbl
+= (*p
->width
)(p
, ' ');
214 if (ASCII_BREAK
== p
->buf
[i
])
218 * Now we definitely know there will be
219 * printable characters to output,
220 * so write preceding white space now.
223 (*p
->advance
)(p
, vbl
);
228 (*p
->letter
)(p
, p
->buf
[i
]);
230 p
->viscol
-= (*p
->width
)(p
, p
->buf
[i
-1]);
232 p
->viscol
+= (*p
->width
)(p
, p
->buf
[i
]);
238 * If there was trailing white space, it was not printed;
239 * so reset the cursor position accordingly.
247 p
->minbl
= p
->trailspace
;
248 p
->flags
&= ~(TERMP_BACKAFTER
| TERMP_BACKBEFORE
| TERMP_NOPAD
);
250 /* Trailing whitespace is significant in some columns. */
251 if (vis
&& vbl
&& (TERMP_BRTRSP
& p
->flags
))
254 /* If the column was overrun, break the line. */
255 if ((p
->flags
& TERMP_NOBREAK
) == 0 ||
256 ((p
->flags
& TERMP_HANG
) == 0 &&
257 vis
+ p
->trailspace
* (*p
->width
)(p
, ' ') > maxvis
))
262 endline(struct termp
*p
)
264 if ((p
->flags
& (TERMP_NEWMC
| TERMP_ENDMC
)) == TERMP_ENDMC
) {
266 p
->flags
&= ~TERMP_ENDMC
;
269 if (p
->viscol
&& p
->maxrmargin
>= p
->viscol
)
270 (*p
->advance
)(p
, p
->maxrmargin
- p
->viscol
+ 1);
271 p
->flags
|= TERMP_NOBUF
| TERMP_NOSPACE
;
273 p
->flags
&= ~(TERMP_NOBUF
| TERMP_NEWMC
);
281 * A newline only breaks an existing line; it won't assert vertical
282 * space. All data in the output buffer is flushed prior to the newline
286 term_newln(struct termp
*p
)
289 p
->flags
|= TERMP_NOSPACE
;
290 if (p
->col
|| p
->viscol
)
295 * Asserts a vertical space (a full, empty line-break between lines).
296 * Note that if used twice, this will cause two blank spaces and so on.
297 * All data in the output buffer is flushed prior to the newline
301 term_vspace(struct termp
*p
)
313 /* Swap current and previous font; for \fP and .ft P */
315 term_fontlast(struct termp
*p
)
320 p
->fontl
= p
->fontq
[p
->fonti
];
321 p
->fontq
[p
->fonti
] = f
;
324 /* Set font, save current, discard previous; for \f, .ft, .B etc. */
326 term_fontrepl(struct termp
*p
, enum termfont f
)
329 p
->fontl
= p
->fontq
[p
->fonti
];
330 p
->fontq
[p
->fonti
] = f
;
333 /* Set font, save previous. */
335 term_fontpush(struct termp
*p
, enum termfont f
)
338 p
->fontl
= p
->fontq
[p
->fonti
];
339 if (++p
->fonti
== p
->fontsz
) {
341 p
->fontq
= mandoc_reallocarray(p
->fontq
,
342 p
->fontsz
, sizeof(*p
->fontq
));
344 p
->fontq
[p
->fonti
] = f
;
347 /* Flush to make the saved pointer current again. */
349 term_fontpopq(struct termp
*p
, int i
)
357 /* Pop one font off the stack. */
359 term_fontpop(struct termp
*p
)
367 * Handle pwords, partial words, which may be either a single word or a
368 * phrase that cannot be broken down (such as a literal string). This
369 * handles word styling.
372 term_word(struct termp
*p
, const char *word
)
375 const char nbrsp
[2] = { ASCII_NBRSP
, 0 };
376 const char *seq
, *cp
;
378 size_t csz
, lsz
, ssz
;
381 if ((p
->flags
& TERMP_NOBUF
) == 0) {
382 if ((p
->flags
& TERMP_NOSPACE
) == 0) {
383 if ((p
->flags
& TERMP_KEEP
) == 0) {
385 if (p
->flags
& TERMP_SENTENCE
)
388 bufferc(p
, ASCII_NBRSP
);
390 if (p
->flags
& TERMP_PREKEEP
)
391 p
->flags
|= TERMP_KEEP
;
392 if (p
->flags
& TERMP_NONOSPACE
)
393 p
->flags
|= TERMP_NOSPACE
;
395 p
->flags
&= ~TERMP_NOSPACE
;
396 p
->flags
&= ~(TERMP_SENTENCE
| TERMP_NONEWLINE
);
400 while ('\0' != *word
) {
402 if (TERMP_NBRWORD
& p
->flags
) {
408 ssz
= strcspn(word
, "\\ ");
410 ssz
= strcspn(word
, "\\");
411 encode(p
, word
, ssz
);
417 esc
= mandoc_escape(&word
, &seq
, &sz
);
418 if (ESCAPE_ERROR
== esc
)
423 uc
= mchars_num2uc(seq
+ 1, sz
- 1);
425 case ESCAPE_NUMBERED
:
426 uc
= mchars_num2char(seq
, sz
);
431 if (p
->enc
== TERMENC_ASCII
) {
432 cp
= mchars_spec2str(seq
, sz
, &ssz
);
436 uc
= mchars_spec2cp(seq
, sz
);
441 case ESCAPE_FONTBOLD
:
442 term_fontrepl(p
, TERMFONT_BOLD
);
444 case ESCAPE_FONTITALIC
:
445 term_fontrepl(p
, TERMFONT_UNDER
);
448 term_fontrepl(p
, TERMFONT_BI
);
451 case ESCAPE_FONTROMAN
:
452 term_fontrepl(p
, TERMFONT_NONE
);
454 case ESCAPE_FONTPREV
:
458 if (p
->flags
& TERMP_BACKAFTER
)
459 p
->flags
&= ~TERMP_BACKAFTER
;
460 else if (*word
== '\0')
461 p
->flags
|= (TERMP_NOSPACE
| TERMP_NONEWLINE
);
464 if (a2roffsu(seq
, &su
, SCALE_EM
) == 0)
466 uc
= term_hspan(p
, &su
) / 24;
469 bufferc(p
, ASCII_NBRSP
);
470 else if (p
->col
> (size_t)(-uc
))
475 if (p
->offset
> (size_t)(-uc
)) {
485 if (a2roffsu(seq
, &su
, SCALE_EM
) == 0)
487 uc
= term_hspan(p
, &su
) / 24;
489 if (p
->rmargin
<= p
->offset
)
491 lsz
= p
->rmargin
- p
->offset
;
495 strchr(" %&()*+-./0123456789:<=>", *seq
)) {
499 if (sz
&& strchr("cifMmnPpuv", *seq
)) {
505 else if (*seq
== '\\') {
507 esc
= mandoc_escape(&seq
, &cp
, &sz
);
510 uc
= mchars_num2uc(cp
+ 1, sz
- 1);
512 case ESCAPE_NUMBERED
:
513 uc
= mchars_num2char(cp
, sz
);
516 uc
= mchars_spec2cp(cp
, sz
);
524 if (uc
< 0x20 || (uc
> 0x7E && uc
< 0xA0))
526 if (p
->enc
== TERMENC_ASCII
) {
527 cp
= ascii_uc2str(uc
);
528 csz
= term_strlen(p
, cp
);
531 csz
= (*p
->width
)(p
, uc
);
533 if (p
->enc
== TERMENC_ASCII
)
540 case ESCAPE_SKIPCHAR
:
541 p
->flags
|= TERMP_BACKAFTER
;
543 case ESCAPE_OVERSTRIKE
:
547 mandoc_escape(&seq
, NULL
, NULL
);
552 if (p
->flags
& TERMP_BACKBEFORE
)
553 p
->flags
|= TERMP_BACKAFTER
;
555 p
->flags
|= TERMP_BACKBEFORE
;
558 /* Trim trailing backspace/blank pair. */
560 (p
->buf
[p
->col
- 1] == ' ' ||
561 p
->buf
[p
->col
- 1] == '\t'))
569 * Common handling for Unicode and numbered
570 * character escape sequences.
573 if (p
->enc
== TERMENC_ASCII
) {
574 cp
= ascii_uc2str(uc
);
575 encode(p
, cp
, strlen(cp
));
577 if ((uc
< 0x20 && uc
!= 0x09) ||
578 (uc
> 0x7E && uc
< 0xA0))
583 p
->flags
&= ~TERMP_NBRWORD
;
587 adjbuf(struct termp
*p
, size_t sz
)
592 while (sz
>= p
->maxcols
)
595 p
->buf
= mandoc_reallocarray(p
->buf
, p
->maxcols
, sizeof(int));
599 bufferc(struct termp
*p
, char c
)
601 if (p
->flags
& TERMP_NOBUF
) {
605 if (p
->col
+ 1 >= p
->maxcols
)
606 adjbuf(p
, p
->col
+ 1);
607 p
->buf
[p
->col
++] = c
;
612 * Do this for a single (probably unicode) value.
613 * Does not check for non-decorated glyphs.
616 encode1(struct termp
*p
, int c
)
620 if (p
->flags
& TERMP_NOBUF
) {
625 if (p
->col
+ 7 >= p
->maxcols
)
626 adjbuf(p
, p
->col
+ 7);
628 f
= (c
== ASCII_HYPH
|| c
> 127 || isgraph(c
)) ?
629 p
->fontq
[p
->fonti
] : TERMFONT_NONE
;
631 if (p
->flags
& TERMP_BACKBEFORE
) {
632 if (p
->buf
[p
->col
- 1] == ' ' || p
->buf
[p
->col
- 1] == '\t')
635 p
->buf
[p
->col
++] = 8;
636 p
->flags
&= ~TERMP_BACKBEFORE
;
638 if (TERMFONT_UNDER
== f
|| TERMFONT_BI
== f
) {
639 p
->buf
[p
->col
++] = '_';
640 p
->buf
[p
->col
++] = 8;
642 if (TERMFONT_BOLD
== f
|| TERMFONT_BI
== f
) {
644 p
->buf
[p
->col
++] = '-';
646 p
->buf
[p
->col
++] = c
;
647 p
->buf
[p
->col
++] = 8;
649 p
->buf
[p
->col
++] = c
;
650 if (p
->flags
& TERMP_BACKAFTER
) {
651 p
->flags
|= TERMP_BACKBEFORE
;
652 p
->flags
&= ~TERMP_BACKAFTER
;
657 encode(struct termp
*p
, const char *word
, size_t sz
)
661 if (p
->flags
& TERMP_NOBUF
) {
662 for (i
= 0; i
< sz
; i
++)
663 (*p
->letter
)(p
, word
[i
]);
667 if (p
->col
+ 2 + (sz
* 5) >= p
->maxcols
)
668 adjbuf(p
, p
->col
+ 2 + (sz
* 5));
670 for (i
= 0; i
< sz
; i
++) {
671 if (ASCII_HYPH
== word
[i
] ||
672 isgraph((unsigned char)word
[i
]))
675 p
->buf
[p
->col
++] = word
[i
];
678 * Postpone the effect of \z while handling
679 * an overstrike sequence from ascii_uc2str().
682 if (word
[i
] == '\b' &&
683 (p
->flags
& TERMP_BACKBEFORE
)) {
684 p
->flags
&= ~TERMP_BACKBEFORE
;
685 p
->flags
|= TERMP_BACKAFTER
;
692 term_setwidth(struct termp
*p
, const char *wstr
)
712 if (a2roffsu(wstr
, &su
, SCALE_MAX
))
713 width
= term_hspan(p
, &su
);
717 (*p
->setwidth
)(p
, iop
, width
);
721 term_len(const struct termp
*p
, size_t sz
)
724 return (*p
->width
)(p
, ' ') * sz
;
728 cond_width(const struct termp
*p
, int c
, int *skip
)
735 return (*p
->width
)(p
, c
);
739 term_strlen(const struct termp
*p
, const char *cp
)
743 const char *seq
, *rhs
;
745 static const char rej
[] = { '\\', ASCII_NBRSP
, ASCII_HYPH
,
749 * Account for escaped sequences within string length
750 * calculations. This follows the logic in term_word() as we
751 * must calculate the width of produced strings.
756 while ('\0' != *cp
) {
757 rsz
= strcspn(cp
, rej
);
758 for (i
= 0; i
< rsz
; i
++)
759 sz
+= cond_width(p
, *cp
++, &skip
);
764 esc
= mandoc_escape(&cp
, &seq
, &ssz
);
765 if (ESCAPE_ERROR
== esc
)
772 uc
= mchars_num2uc(seq
+ 1, ssz
- 1);
774 case ESCAPE_NUMBERED
:
775 uc
= mchars_num2char(seq
, ssz
);
780 if (p
->enc
== TERMENC_ASCII
) {
781 rhs
= mchars_spec2str(seq
, ssz
, &rsz
);
785 uc
= mchars_spec2cp(seq
, ssz
);
787 sz
+= cond_width(p
, uc
, &skip
);
790 case ESCAPE_SKIPCHAR
:
793 case ESCAPE_OVERSTRIKE
:
798 mandoc_escape(&seq
, NULL
, NULL
);
801 i
= (*p
->width
)(p
, *seq
++);
812 * Common handling for Unicode and numbered
813 * character escape sequences.
817 if (p
->enc
== TERMENC_ASCII
) {
818 rhs
= ascii_uc2str(uc
);
821 if ((uc
< 0x20 && uc
!= 0x09) ||
822 (uc
> 0x7E && uc
< 0xA0))
824 sz
+= cond_width(p
, uc
, &skip
);
835 * Common handling for all escape sequences
836 * printing more than one character.
839 for (i
= 0; i
< rsz
; i
++)
840 sz
+= (*p
->width
)(p
, *rhs
++);
843 sz
+= cond_width(p
, ' ', &skip
);
847 sz
+= cond_width(p
, '-', &skip
);
859 term_vspan(const struct termp
*p
, const struct roffsu
*su
)
866 r
= su
->scale
/ 40.0;
869 r
= su
->scale
* 6.0 / 2.54;
872 r
= su
->scale
* 65536.0 / 40.0;
878 r
= su
->scale
* 0.006;
884 r
= su
->scale
/ 12.0;
896 ri
= r
> 0.0 ? r
+ 0.4995 : r
- 0.4995;
897 return ri
< 66 ? ri
: 1;
901 * Convert a scaling width to basic units, rounding down.
904 term_hspan(const struct termp
*p
, const struct roffsu
*su
)
907 return (*p
->hspan
)(p
, su
);