]>
git.cameronkatri.com Git - mandoc.git/blob - term.c
1 /* $Id: term.c,v 1.286 2022/04/27 13:41:13 schwarze Exp $ */
3 * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
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>
30 #include "mandoc_aux.h"
35 static size_t cond_width(const struct termp
*, int, int *);
36 static void adjbuf(struct termp_col
*, size_t);
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 static void endline(struct termp
*);
41 static void term_field(struct termp
*, size_t, size_t);
42 static void term_fill(struct termp
*, size_t *, size_t *,
47 term_setcol(struct termp
*p
, size_t maxtcol
)
49 if (maxtcol
> p
->maxtcol
) {
50 p
->tcols
= mandoc_recallocarray(p
->tcols
,
51 p
->maxtcol
, maxtcol
, sizeof(*p
->tcols
));
54 p
->lasttcol
= maxtcol
- 1;
59 term_free(struct termp
*p
)
62 for (p
->tcol
= p
->tcols
; p
->tcol
< p
->tcols
+ p
->maxtcol
; p
->tcol
++)
70 term_begin(struct termp
*p
, term_margin head
,
71 term_margin foot
, const struct roff_meta
*arg
)
81 term_end(struct termp
*p
)
88 * Flush a chunk of text. By default, break the output line each time
89 * the right margin is reached, and continue output on the next line
90 * at the same offset as the chunk itself. By default, also break the
91 * output line at the end of the chunk. There are many flags modifying
92 * this behaviour, see the comments in the body of the function.
95 term_flushln(struct termp
*p
)
97 size_t vbl
; /* Number of blanks to prepend to the output. */
98 size_t vbr
; /* Actual visual position of the end of field. */
99 size_t vfield
; /* Desired visual field width. */
100 size_t vtarget
; /* Desired visual position of the right margin. */
101 size_t ic
; /* Character position in the input buffer. */
102 size_t nbr
; /* Number of characters to print in this field. */
105 * Normally, start writing at the left margin, but with the
106 * NOPAD flag, start writing at the current position instead.
109 vbl
= (p
->flags
& TERMP_NOPAD
) || p
->tcol
->offset
< p
->viscol
?
110 0 : p
->tcol
->offset
- p
->viscol
;
111 if (p
->minbl
&& vbl
< p
->minbl
)
114 if ((p
->flags
& TERMP_MULTICOL
) == 0)
117 /* Loop over output lines. */
120 vfield
= p
->tcol
->rmargin
> p
->viscol
+ vbl
?
121 p
->tcol
->rmargin
- p
->viscol
- vbl
: 0;
124 * Normally, break the line at the the right margin
125 * of the field, but with the NOBREAK flag, only
126 * break it at the max right margin of the screen,
127 * and with the BRNEVER flag, never break it at all.
130 vtarget
= (p
->flags
& TERMP_NOBREAK
) == 0 ? vfield
:
131 p
->maxrmargin
> p
->viscol
+ vbl
?
132 p
->maxrmargin
- p
->viscol
- vbl
: 0;
135 * Figure out how much text will fit in the field.
136 * If there is whitespace only, print nothing.
139 term_fill(p
, &nbr
, &vbr
,
140 p
->flags
& TERMP_BRNEVER
? SIZE_MAX
: vtarget
);
145 * With the CENTER or RIGHT flag, increase the indentation
146 * to center the text between the left and right margins
147 * or to adjust it to the right margin, respectively.
151 if (p
->flags
& TERMP_CENTER
)
152 vbl
+= (vtarget
- vbr
) / 2;
153 else if (p
->flags
& TERMP_RIGHT
)
154 vbl
+= vtarget
- vbr
;
157 /* Finally, print the field content. */
159 term_field(p
, vbl
, nbr
);
162 * If there is no text left in the field, exit the loop.
163 * If the BRTRSP flag is set, consider trailing
164 * whitespace significant when deciding whether
165 * the field fits or not.
168 for (ic
= p
->tcol
->col
; ic
< p
->tcol
->lastcol
; ic
++) {
169 switch (p
->tcol
->buf
[ic
]) {
171 if (p
->flags
& TERMP_BRTRSP
)
172 vbr
= term_tab_next(vbr
);
175 if (p
->flags
& TERMP_BRTRSP
)
176 vbr
+= (*p
->width
)(p
, ' ');
186 if (ic
== p
->tcol
->lastcol
)
190 * At the location of an automtic line break, input
191 * space characters are consumed by the line break.
194 while (p
->tcol
->col
< p
->tcol
->lastcol
&&
195 p
->tcol
->buf
[p
->tcol
->col
] == ' ')
199 * In multi-column mode, leave the rest of the text
200 * in the buffer to be handled by a subsequent
201 * invocation, such that the other columns of the
202 * table can be handled first.
203 * In single-column mode, simply break the line.
206 if (p
->flags
& TERMP_MULTICOL
)
213 * Normally, start the next line at the same indentation
214 * as this one, but with the BRIND flag, start it at the
215 * right margin instead. This is used together with
216 * NOBREAK for the tags in various kinds of tagged lists.
219 vbl
= p
->flags
& TERMP_BRIND
?
220 p
->tcol
->rmargin
: p
->tcol
->offset
;
223 /* Reset output state in preparation for the next field. */
225 p
->col
= p
->tcol
->col
= p
->tcol
->lastcol
= 0;
226 p
->minbl
= p
->trailspace
;
227 p
->flags
&= ~(TERMP_BACKAFTER
| TERMP_BACKBEFORE
| TERMP_NOPAD
);
229 if (p
->flags
& TERMP_MULTICOL
)
233 * The HANG flag means that the next field
234 * always follows on the same line.
235 * The NOBREAK flag means that the next field
236 * follows on the same line unless the field was overrun.
237 * Normally, break the line at the end of each field.
240 if ((p
->flags
& TERMP_HANG
) == 0 &&
241 ((p
->flags
& TERMP_NOBREAK
) == 0 ||
242 vbr
+ term_len(p
, p
->trailspace
) > vfield
))
247 * Store the number of input characters to print in this field in *nbr
248 * and their total visual width to print in *vbr.
249 * If there is only whitespace in the field, both remain zero.
250 * The desired visual width of the field is provided by vtarget.
251 * If the first word is longer, the field will be overrun.
254 term_fill(struct termp
*p
, size_t *nbr
, size_t *vbr
, size_t vtarget
)
256 size_t ic
; /* Character position in the input buffer. */
257 size_t vis
; /* Visual position of the current character. */
258 size_t vn
; /* Visual position of the next character. */
259 int breakline
; /* Break at the end of this word. */
260 int graph
; /* Last character was non-blank. */
262 *nbr
= *vbr
= vis
= 0;
263 breakline
= graph
= 0;
264 for (ic
= p
->tcol
->col
; ic
< p
->tcol
->lastcol
; ic
++) {
265 switch (p
->tcol
->buf
[ic
]) {
266 case '\b': /* Escape \o (overstrike) or backspace markup. */
268 vis
-= (*p
->width
)(p
, p
->tcol
->buf
[ic
- 1]);
271 case '\t': /* Normal ASCII whitespace. */
273 case ASCII_BREAK
: /* Escape \: (breakpoint). */
274 switch (p
->tcol
->buf
[ic
]) {
276 vn
= term_tab_next(vis
);
279 vn
= vis
+ (*p
->width
)(p
, ' ');
287 /* Can break at the end of a word. */
288 if (breakline
|| vn
> vtarget
)
298 case '\n': /* Escape \p (break at the end of the word). */
302 case ASCII_HYPH
: /* Breakable hyphen. */
305 * We are about to decide whether to break the
306 * line or not, so we no longer need this hyphen
307 * to be marked as breakable. Put back a real
308 * hyphen such that we get the correct width.
310 p
->tcol
->buf
[ic
] = '-';
311 vis
+= (*p
->width
)(p
, '-');
320 case ASCII_NBRSP
: /* Non-breakable space. */
321 p
->tcol
->buf
[ic
] = ' ';
323 default: /* Printable character. */
325 vis
+= (*p
->width
)(p
, p
->tcol
->buf
[ic
]);
326 if (vis
> vtarget
&& *nbr
> 0)
334 * If the last word extends to the end of the field without any
335 * trailing whitespace, the loop could not check yet whether it
336 * can remain on this line. So do the check now.
339 if (graph
&& (vis
<= vtarget
|| *nbr
== 0)) {
346 * Print the contents of one field
347 * with an indentation of vbl visual columns,
348 * and an input string length of nbr characters.
351 term_field(struct termp
*p
, size_t vbl
, size_t nbr
)
353 size_t ic
; /* Character position in the input buffer. */
354 size_t vis
; /* Visual position of the current character. */
355 size_t dv
; /* Visual width of the current character. */
356 size_t vn
; /* Visual position of the next character. */
359 for (ic
= p
->tcol
->col
; ic
< nbr
; ic
++) {
362 * To avoid the printing of trailing whitespace,
363 * do not print whitespace right away, only count it.
366 switch (p
->tcol
->buf
[ic
]) {
371 vn
= term_tab_next(vis
);
377 dv
= (*p
->width
)(p
, ' ');
386 * We found a non-blank character to print,
387 * so write preceding white space now.
391 (*p
->advance
)(p
, vbl
);
396 /* Print the character and adjust the visual position. */
398 (*p
->letter
)(p
, p
->tcol
->buf
[ic
]);
399 if (p
->tcol
->buf
[ic
] == '\b') {
400 dv
= (*p
->width
)(p
, p
->tcol
->buf
[ic
- 1]);
404 dv
= (*p
->width
)(p
, p
->tcol
->buf
[ic
]);
413 endline(struct termp
*p
)
415 if ((p
->flags
& (TERMP_NEWMC
| TERMP_ENDMC
)) == TERMP_ENDMC
) {
417 p
->flags
&= ~TERMP_ENDMC
;
420 if (p
->viscol
&& p
->maxrmargin
>= p
->viscol
)
421 (*p
->advance
)(p
, p
->maxrmargin
- p
->viscol
+ 1);
422 p
->flags
|= TERMP_NOBUF
| TERMP_NOSPACE
;
424 p
->flags
&= ~(TERMP_NOBUF
| TERMP_NEWMC
);
432 * A newline only breaks an existing line; it won't assert vertical
433 * space. All data in the output buffer is flushed prior to the newline
437 term_newln(struct termp
*p
)
440 p
->flags
|= TERMP_NOSPACE
;
441 if (p
->tcol
->lastcol
|| p
->viscol
)
446 * Asserts a vertical space (a full, empty line-break between lines).
447 * Note that if used twice, this will cause two blank spaces and so on.
448 * All data in the output buffer is flushed prior to the newline
452 term_vspace(struct termp
*p
)
464 /* Swap current and previous font; for \fP and .ft P */
466 term_fontlast(struct termp
*p
)
471 p
->fontl
= p
->fontq
[p
->fonti
];
472 p
->fontq
[p
->fonti
] = f
;
475 /* Set font, save current, discard previous; for \f, .ft, .B etc. */
477 term_fontrepl(struct termp
*p
, enum termfont f
)
480 p
->fontl
= p
->fontq
[p
->fonti
];
481 p
->fontq
[p
->fonti
] = f
;
484 /* Set font, save previous. */
486 term_fontpush(struct termp
*p
, enum termfont f
)
489 p
->fontl
= p
->fontq
[p
->fonti
];
490 if (++p
->fonti
== p
->fontsz
) {
492 p
->fontq
= mandoc_reallocarray(p
->fontq
,
493 p
->fontsz
, sizeof(*p
->fontq
));
495 p
->fontq
[p
->fonti
] = f
;
498 /* Flush to make the saved pointer current again. */
500 term_fontpopq(struct termp
*p
, int i
)
508 /* Pop one font off the stack. */
510 term_fontpop(struct termp
*p
)
518 * Handle pwords, partial words, which may be either a single word or a
519 * phrase that cannot be broken down (such as a literal string). This
520 * handles word styling.
523 term_word(struct termp
*p
, const char *word
)
526 const char nbrsp
[2] = { ASCII_NBRSP
, 0 };
527 const char *seq
, *cp
;
529 size_t csz
, lsz
, ssz
;
532 if ((p
->flags
& TERMP_NOBUF
) == 0) {
533 if ((p
->flags
& TERMP_NOSPACE
) == 0) {
534 if ((p
->flags
& TERMP_KEEP
) == 0) {
536 if (p
->flags
& TERMP_SENTENCE
)
539 bufferc(p
, ASCII_NBRSP
);
541 if (p
->flags
& TERMP_PREKEEP
)
542 p
->flags
|= TERMP_KEEP
;
543 if (p
->flags
& TERMP_NONOSPACE
)
544 p
->flags
|= TERMP_NOSPACE
;
546 p
->flags
&= ~TERMP_NOSPACE
;
547 p
->flags
&= ~(TERMP_SENTENCE
| TERMP_NONEWLINE
);
551 while ('\0' != *word
) {
553 if (TERMP_NBRWORD
& p
->flags
) {
559 ssz
= strcspn(word
, "\\ ");
561 ssz
= strcspn(word
, "\\");
562 encode(p
, word
, ssz
);
568 esc
= mandoc_escape(&word
, &seq
, &sz
);
571 uc
= mchars_num2uc(seq
+ 1, sz
- 1);
573 case ESCAPE_NUMBERED
:
574 uc
= mchars_num2char(seq
, sz
);
579 if (p
->enc
== TERMENC_ASCII
) {
580 cp
= mchars_spec2str(seq
, sz
, &ssz
);
584 uc
= mchars_spec2cp(seq
, sz
);
592 case ESCAPE_FONTBOLD
:
594 term_fontrepl(p
, TERMFONT_BOLD
);
596 case ESCAPE_FONTITALIC
:
598 term_fontrepl(p
, TERMFONT_UNDER
);
601 term_fontrepl(p
, TERMFONT_BI
);
605 case ESCAPE_FONTROMAN
:
606 term_fontrepl(p
, TERMFONT_NONE
);
608 case ESCAPE_FONTPREV
:
615 if (p
->flags
& TERMP_BACKAFTER
)
616 p
->flags
&= ~TERMP_BACKAFTER
;
617 else if (*word
== '\0')
618 p
->flags
|= (TERMP_NOSPACE
| TERMP_NONEWLINE
);
621 if (p
->type
== TERMTYPE_PDF
)
623 else if (p
->type
== TERMTYPE_PS
)
625 else if (p
->enc
== TERMENC_ASCII
)
626 encode(p
, "ascii", 5);
628 encode(p
, "utf8", 4);
631 if (p
->flags
& TERMP_BACKAFTER
) {
632 p
->flags
&= ~TERMP_BACKAFTER
;
640 if (a2roffsu(seq
, &su
, SCALE_EM
) == NULL
)
642 uc
+= term_hen(p
, &su
);
645 uc
-= term_len(p
, 1);
646 if (p
->flags
& TERMP_BACKBEFORE
)
647 p
->flags
&= ~TERMP_BACKBEFORE
;
649 bufferc(p
, ASCII_NBRSP
);
653 if (p
->flags
& TERMP_BACKBEFORE
) {
654 p
->flags
&= ~TERMP_BACKBEFORE
;
658 if (p
->col
>= (size_t)(-uc
)) {
663 if (p
->tcol
->offset
> (size_t)(-uc
)) {
665 p
->tcol
->offset
+= uc
;
667 p
->ti
-= p
->tcol
->offset
;
673 if ((cp
= a2roffsu(seq
, &su
, SCALE_EM
)) == NULL
)
675 uc
= term_hen(p
, &su
);
677 if (p
->tcol
->rmargin
<= p
->tcol
->offset
)
679 lsz
= p
->tcol
->rmargin
- p
->tcol
->offset
;
684 else if (*cp
== '\\') {
686 esc
= mandoc_escape(&seq
, &cp
, &sz
);
689 uc
= mchars_num2uc(cp
+ 1, sz
- 1);
691 case ESCAPE_NUMBERED
:
692 uc
= mchars_num2char(cp
, sz
);
695 uc
= mchars_spec2cp(cp
, sz
);
706 if (uc
< 0x20 || (uc
> 0x7E && uc
< 0xA0))
708 if (p
->enc
== TERMENC_ASCII
) {
709 cp
= ascii_uc2str(uc
);
710 csz
= term_strlen(p
, cp
);
713 csz
= (*p
->width
)(p
, uc
);
715 if (p
->enc
== TERMENC_ASCII
)
722 case ESCAPE_SKIPCHAR
:
723 p
->flags
|= TERMP_BACKAFTER
;
725 case ESCAPE_OVERSTRIKE
:
729 mandoc_escape(&seq
, NULL
, NULL
);
734 if (p
->flags
& TERMP_BACKBEFORE
)
735 p
->flags
|= TERMP_BACKAFTER
;
737 p
->flags
|= TERMP_BACKBEFORE
;
740 /* Trim trailing backspace/blank pair. */
741 if (p
->tcol
->lastcol
> 2 &&
742 (p
->tcol
->buf
[p
->tcol
->lastcol
- 1] == ' ' ||
743 p
->tcol
->buf
[p
->tcol
->lastcol
- 1] == '\t'))
744 p
->tcol
->lastcol
-= 2;
745 if (p
->col
> p
->tcol
->lastcol
)
746 p
->col
= p
->tcol
->lastcol
;
753 * Common handling for Unicode and numbered
754 * character escape sequences.
757 if (p
->enc
== TERMENC_ASCII
) {
758 cp
= ascii_uc2str(uc
);
759 encode(p
, cp
, strlen(cp
));
761 if ((uc
< 0x20 && uc
!= 0x09) ||
762 (uc
> 0x7E && uc
< 0xA0))
767 p
->flags
&= ~TERMP_NBRWORD
;
771 adjbuf(struct termp_col
*c
, size_t sz
)
775 while (c
->maxcols
<= sz
)
777 c
->buf
= mandoc_reallocarray(c
->buf
, c
->maxcols
, sizeof(*c
->buf
));
781 bufferc(struct termp
*p
, char c
)
783 if (p
->flags
& TERMP_NOBUF
) {
787 if (p
->col
+ 1 >= p
->tcol
->maxcols
)
788 adjbuf(p
->tcol
, p
->col
+ 1);
789 if (p
->tcol
->lastcol
<= p
->col
|| (c
!= ' ' && c
!= ASCII_NBRSP
))
790 p
->tcol
->buf
[p
->col
] = c
;
791 if (p
->tcol
->lastcol
< ++p
->col
)
792 p
->tcol
->lastcol
= p
->col
;
797 * Do this for a single (probably unicode) value.
798 * Does not check for non-decorated glyphs.
801 encode1(struct termp
*p
, int c
)
805 if (p
->flags
& TERMP_NOBUF
) {
810 if (p
->col
+ 7 >= p
->tcol
->maxcols
)
811 adjbuf(p
->tcol
, p
->col
+ 7);
813 f
= (c
== ASCII_HYPH
|| c
> 127 || isgraph(c
)) ?
814 p
->fontq
[p
->fonti
] : TERMFONT_NONE
;
816 if (p
->flags
& TERMP_BACKBEFORE
) {
817 if (p
->tcol
->buf
[p
->col
- 1] == ' ' ||
818 p
->tcol
->buf
[p
->col
- 1] == '\t')
821 p
->tcol
->buf
[p
->col
++] = '\b';
822 p
->flags
&= ~TERMP_BACKBEFORE
;
824 if (f
== TERMFONT_UNDER
|| f
== TERMFONT_BI
) {
825 p
->tcol
->buf
[p
->col
++] = '_';
826 p
->tcol
->buf
[p
->col
++] = '\b';
828 if (f
== TERMFONT_BOLD
|| f
== TERMFONT_BI
) {
830 p
->tcol
->buf
[p
->col
++] = '-';
832 p
->tcol
->buf
[p
->col
++] = c
;
833 p
->tcol
->buf
[p
->col
++] = '\b';
835 if (p
->tcol
->lastcol
<= p
->col
|| (c
!= ' ' && c
!= ASCII_NBRSP
))
836 p
->tcol
->buf
[p
->col
] = c
;
837 if (p
->tcol
->lastcol
< ++p
->col
)
838 p
->tcol
->lastcol
= p
->col
;
839 if (p
->flags
& TERMP_BACKAFTER
) {
840 p
->flags
|= TERMP_BACKBEFORE
;
841 p
->flags
&= ~TERMP_BACKAFTER
;
846 encode(struct termp
*p
, const char *word
, size_t sz
)
850 if (p
->flags
& TERMP_NOBUF
) {
851 for (i
= 0; i
< sz
; i
++)
852 (*p
->letter
)(p
, word
[i
]);
856 if (p
->col
+ 2 + (sz
* 5) >= p
->tcol
->maxcols
)
857 adjbuf(p
->tcol
, p
->col
+ 2 + (sz
* 5));
859 for (i
= 0; i
< sz
; i
++) {
860 if (ASCII_HYPH
== word
[i
] ||
861 isgraph((unsigned char)word
[i
]))
864 if (p
->tcol
->lastcol
<= p
->col
||
865 (word
[i
] != ' ' && word
[i
] != ASCII_NBRSP
))
866 p
->tcol
->buf
[p
->col
] = word
[i
];
870 * Postpone the effect of \z while handling
871 * an overstrike sequence from ascii_uc2str().
874 if (word
[i
] == '\b' &&
875 (p
->flags
& TERMP_BACKBEFORE
)) {
876 p
->flags
&= ~TERMP_BACKBEFORE
;
877 p
->flags
|= TERMP_BACKAFTER
;
881 if (p
->tcol
->lastcol
< p
->col
)
882 p
->tcol
->lastcol
= p
->col
;
886 term_setwidth(struct termp
*p
, const char *wstr
)
906 if (a2roffsu(wstr
, &su
, SCALE_MAX
) != NULL
)
907 width
= term_hspan(p
, &su
);
911 (*p
->setwidth
)(p
, iop
, width
);
915 term_len(const struct termp
*p
, size_t sz
)
918 return (*p
->width
)(p
, ' ') * sz
;
922 cond_width(const struct termp
*p
, int c
, int *skip
)
929 return (*p
->width
)(p
, c
);
933 term_strlen(const struct termp
*p
, const char *cp
)
937 const char *seq
, *rhs
;
939 static const char rej
[] = { '\\', ASCII_NBRSP
, ASCII_HYPH
,
943 * Account for escaped sequences within string length
944 * calculations. This follows the logic in term_word() as we
945 * must calculate the width of produced strings.
950 while ('\0' != *cp
) {
951 rsz
= strcspn(cp
, rej
);
952 for (i
= 0; i
< rsz
; i
++)
953 sz
+= cond_width(p
, *cp
++, &skip
);
959 esc
= mandoc_escape(&cp
, &seq
, &ssz
);
962 uc
= mchars_num2uc(seq
+ 1, ssz
- 1);
964 case ESCAPE_NUMBERED
:
965 uc
= mchars_num2char(seq
, ssz
);
970 if (p
->enc
== TERMENC_ASCII
) {
971 rhs
= mchars_spec2str(seq
, ssz
, &rsz
);
975 uc
= mchars_spec2cp(seq
, ssz
);
977 sz
+= cond_width(p
, uc
, &skip
);
984 if (p
->type
== TERMTYPE_PDF
) {
987 } else if (p
->type
== TERMTYPE_PS
) {
990 } else if (p
->enc
== TERMENC_ASCII
) {
998 case ESCAPE_SKIPCHAR
:
1001 case ESCAPE_OVERSTRIKE
:
1006 mandoc_escape(&seq
, NULL
, NULL
);
1009 i
= (*p
->width
)(p
, *seq
++);
1020 * Common handling for Unicode and numbered
1021 * character escape sequences.
1025 if (p
->enc
== TERMENC_ASCII
) {
1026 rhs
= ascii_uc2str(uc
);
1029 if ((uc
< 0x20 && uc
!= 0x09) ||
1030 (uc
> 0x7E && uc
< 0xA0))
1032 sz
+= cond_width(p
, uc
, &skip
);
1043 * Common handling for all escape sequences
1044 * printing more than one character.
1047 for (i
= 0; i
< rsz
; i
++)
1048 sz
+= (*p
->width
)(p
, *rhs
++);
1051 sz
+= cond_width(p
, ' ', &skip
);
1055 sz
+= cond_width(p
, '-', &skip
);
1067 term_vspan(const struct termp
*p
, const struct roffsu
*su
)
1074 r
= su
->scale
/ 40.0;
1077 r
= su
->scale
* 6.0 / 2.54;
1080 r
= su
->scale
* 65536.0 / 40.0;
1083 r
= su
->scale
* 6.0;
1086 r
= su
->scale
* 0.006;
1092 r
= su
->scale
/ 12.0;
1096 r
= su
->scale
* 0.6;
1104 ri
= r
> 0.0 ? r
+ 0.4995 : r
- 0.4995;
1105 return ri
< 66 ? ri
: 1;
1109 * Convert a scaling width to basic units, rounding towards 0.
1112 term_hspan(const struct termp
*p
, const struct roffsu
*su
)
1115 return (*p
->hspan
)(p
, su
);
1119 * Convert a scaling width to basic units, rounding to closest.
1122 term_hen(const struct termp
*p
, const struct roffsu
*su
)
1126 if ((bu
= (*p
->hspan
)(p
, su
)) >= 0)
1127 return (bu
+ 11) / 24;
1129 return -((-bu
+ 11) / 24);