Return to term.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.77, 2009/06/11 07:46:41 | version 1.109, 2009/10/22 18:19:36 | ||
---|---|---|---|
|
|
||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include "chars.h" | |||
#include "out.h" | |||
#include "term.h" | #include "term.h" | ||
#include "man.h" | #include "man.h" | ||
#include "mdoc.h" | #include "mdoc.h" | ||
#include "main.h" | |||
extern int man_run(struct termp *, | /* FIXME: accomodate non-breaking, non-collapsing white-space. */ | ||
const struct man *); | /* FIXME: accomodate non-breaking, collapsing white-space. */ | ||
extern int mdoc_run(struct termp *, | |||
const struct mdoc *); | |||
static struct termp *term_alloc(enum termenc); | static struct termp *term_alloc(enum termenc); | ||
static void term_free(struct termp *); | static void term_free(struct termp *); | ||
static void term_pword(struct termp *, const char *, int); | |||
static void term_pescape(struct termp *, | static void do_escaped(struct termp *, const char **); | ||
const char *, int *, int); | static void do_special(struct termp *, | ||
static void term_nescape(struct termp *, | |||
const char *, size_t); | const char *, size_t); | ||
static void term_chara(struct termp *, char); | static void do_reserved(struct termp *, | ||
static void term_stringa(struct termp *, | |||
const char *, size_t); | const char *, size_t); | ||
static int term_isopendelim(const char *, int); | static void buffer(struct termp *, char); | ||
static int term_isclosedelim(const char *, int); | static void encode(struct termp *, char); | ||
void * | void * | ||
|
|
||
} | } | ||
int | |||
terminal_man(void *arg, const struct man *man) | |||
{ | |||
struct termp *p; | |||
p = (struct termp *)arg; | |||
if (NULL == p->symtab) | |||
p->symtab = term_ascii2htab(); | |||
return(man_run(p, man)); | |||
} | |||
int | |||
terminal_mdoc(void *arg, const struct mdoc *mdoc) | |||
{ | |||
struct termp *p; | |||
p = (struct termp *)arg; | |||
if (NULL == p->symtab) | |||
p->symtab = term_ascii2htab(); | |||
return(mdoc_run(p, mdoc)); | |||
} | |||
void | void | ||
terminal_free(void *arg) | terminal_free(void *arg) | ||
{ | { | ||
|
|
||
if (p->buf) | if (p->buf) | ||
free(p->buf); | free(p->buf); | ||
if (TERMENC_ASCII == p->enc && p->symtab) | if (p->symtab) | ||
term_asciifree(p->symtab); | chars_free(p->symtab); | ||
free(p); | free(p); | ||
} | } | ||
|
|
||
struct termp *p; | struct termp *p; | ||
if (NULL == (p = malloc(sizeof(struct termp)))) | if (NULL == (p = malloc(sizeof(struct termp)))) | ||
err(1, "malloc"); | return(NULL); | ||
bzero(p, sizeof(struct termp)); | bzero(p, sizeof(struct termp)); | ||
p->maxrmargin = 80; | p->maxrmargin = 78; | ||
p->enc = enc; | p->enc = enc; | ||
return(p); | return(p); | ||
} | } | ||
static int | |||
term_isclosedelim(const char *p, int len) | |||
{ | |||
if (1 != len) | |||
return(0); | |||
switch (*p) { | |||
case('.'): | |||
/* FALLTHROUGH */ | |||
case(','): | |||
/* FALLTHROUGH */ | |||
case(';'): | |||
/* FALLTHROUGH */ | |||
case(':'): | |||
/* FALLTHROUGH */ | |||
case('?'): | |||
/* FALLTHROUGH */ | |||
case('!'): | |||
/* FALLTHROUGH */ | |||
case(')'): | |||
/* FALLTHROUGH */ | |||
case(']'): | |||
/* FALLTHROUGH */ | |||
case('}'): | |||
return(1); | |||
default: | |||
break; | |||
} | |||
return(0); | |||
} | |||
static int | |||
term_isopendelim(const char *p, int len) | |||
{ | |||
if (1 != len) | |||
return(0); | |||
switch (*p) { | |||
case('('): | |||
/* FALLTHROUGH */ | |||
case('['): | |||
/* FALLTHROUGH */ | |||
case('{'): | |||
return(1); | |||
default: | |||
break; | |||
} | |||
return(0); | |||
} | |||
/* | /* | ||
* Flush a line of text. A "line" is loosely defined as being something | * Flush a line of text. A "line" is loosely defined as being something | ||
* that should be followed by a newline, regardless of whether it's | * that should be followed by a newline, regardless of whether it's | ||
|
|
||
* Specifically, a line is whatever's in p->buf of length p->col, which | * Specifically, a line is whatever's in p->buf of length p->col, which | ||
* is zeroed after this function returns. | * is zeroed after this function returns. | ||
* | * | ||
* The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of | * The usage of termp:flags is as follows: | ||
* critical importance here. Their behaviour follows: | |||
* | * | ||
* - TERMP_NOLPAD: when beginning to write the line, don't left-pad the | * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the | ||
* offset value. This is useful when doing columnar lists where the | * offset value. This is useful when doing columnar lists where the | ||
|
|
||
* columns. In short: don't print a newline and instead pad to the | * columns. In short: don't print a newline and instead pad to the | ||
* right margin. Used in conjunction with TERMP_NOLPAD. | * right margin. Used in conjunction with TERMP_NOLPAD. | ||
* | * | ||
* - TERMP_NONOBREAK: don't newline when TERMP_NOBREAK is specified. | * - TERMP_TWOSPACE: when padding, make sure there are at least two | ||
* space characters of padding. Otherwise, rather break the line. | |||
* | * | ||
* - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and | |||
* the line is overrun, and don't pad-right if it's underrun. | |||
* | |||
* - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when | |||
* overruning, instead save the position and continue at that point | |||
* when the next invocation. | |||
* | |||
* In-line line breaking: | * In-line line breaking: | ||
* | * | ||
* If TERMP_NOBREAK is specified and the line overruns the right | * If TERMP_NOBREAK is specified and the line overruns the right | ||
|
|
||
term_flushln(struct termp *p) | term_flushln(struct termp *p) | ||
{ | { | ||
int i, j; | int i, j; | ||
size_t vsz, vis, maxvis, mmax, bp; | size_t vbl, vsz, vis, maxvis, mmax, bp, os; | ||
static int overstep = 0; | |||
/* | /* | ||
* First, establish the maximum columns of "visible" content. | * First, establish the maximum columns of "visible" content. | ||
|
|
||
*/ | */ | ||
assert(p->offset < p->rmargin); | assert(p->offset < p->rmargin); | ||
maxvis = p->rmargin - p->offset; | assert((int)(p->rmargin - p->offset) - overstep > 0); | ||
mmax = p->maxrmargin - p->offset; | |||
/* Save the overstep. */ | |||
os = (size_t)overstep; | |||
maxvis = /* LINTED */ | |||
p->rmargin - p->offset - overstep; | |||
mmax = /* LINTED */ | |||
p->maxrmargin - p->offset - overstep; | |||
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | ||
vis = 0; | vis = 0; | ||
overstep = 0; | |||
/* | /* | ||
* If in the standard case (left-justified), then begin with our | * If in the standard case (left-justified), then begin with our | ||
|
|
||
/* LINTED */ | /* LINTED */ | ||
for (j = i, vsz = 0; j < (int)p->col; j++) { | for (j = i, vsz = 0; j < (int)p->col; j++) { | ||
if (' ' == p->buf[j]) | if (j && ' ' == p->buf[j]) | ||
break; | break; | ||
else if (8 == p->buf[j]) | else if (8 == p->buf[j]) | ||
j += 1; | vsz--; | ||
else | else | ||
vsz++; | vsz++; | ||
} | } | ||
/* | /* | ||
* Do line-breaking. If we're greater than our | * Choose the number of blanks to prepend: no blank at the | ||
* break-point and already in-line, break to the next | * beginning of a line, one between words -- but do not | ||
* line and start writing. If we're at the line start, | * actually write them yet. | ||
* then write out the word (TODO: hyphenate) and break | |||
* in a subsequent loop invocation. | |||
*/ | */ | ||
vbl = (size_t)(0 == vis ? 0 : 1); | |||
if ( ! (TERMP_NOBREAK & p->flags)) { | /* | ||
if (vis && vis + vsz > bp) { | * Find out whether we would exceed the right margin. | ||
putchar('\n'); | * If so, break to the next line. (TODO: hyphenate) | ||
* Otherwise, write the chosen number of blanks now. | |||
*/ | |||
if (vis && vis + vbl + vsz > bp) { | |||
putchar('\n'); | |||
if (TERMP_NOBREAK & p->flags) { | |||
for (j = 0; j < (int)p->rmargin; j++) | |||
putchar(' '); | |||
vis = p->rmargin - p->offset; | |||
} else { | |||
for (j = 0; j < (int)p->offset; j++) | for (j = 0; j < (int)p->offset; j++) | ||
putchar(' '); | putchar(' '); | ||
vis = 0; | vis = 0; | ||
} | } | ||
} else if (vis && vis + vsz > bp) { | /* Remove the overstep width. */ | ||
putchar('\n'); | bp += os; | ||
for (j = 0; j < (int)p->rmargin; j++) | os = 0; | ||
} else { | |||
for (j = 0; j < (int)vbl; j++) | |||
putchar(' '); | putchar(' '); | ||
vis = p->rmargin - p->offset; | vis += vbl; | ||
} | } | ||
/* | /* | ||
* Write out the word and a trailing space. Omit the | * Finally, write out the word. | ||
* space if we're the last word in the line or beyond | |||
* our breakpoint. | |||
*/ | */ | ||
if (0 < vis++) | |||
putchar(' '); | |||
for ( ; i < (int)p->col; i++) { | for ( ; i < (int)p->col; i++) { | ||
if (' ' == p->buf[i]) | if (' ' == p->buf[i]) | ||
break; | break; | ||
|
|
||
} | } | ||
vis += vsz; | vis += vsz; | ||
} | } | ||
p->col = 0; | |||
/* | if ( ! (TERMP_NOBREAK & p->flags)) { | ||
* If we've overstepped our maximum visible no-break space, then | putchar('\n'); | ||
* cause a newline and offset at the right margin. | |||
*/ | |||
if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) { | |||
if ( ! (TERMP_NONOBREAK & p->flags)) { | |||
putchar('\n'); | |||
for (i = 0; i < (int)p->rmargin; i++) | |||
putchar(' '); | |||
} | |||
p->col = 0; | |||
return; | return; | ||
} | } | ||
/* | if (TERMP_HANG & p->flags) { | ||
* If we're not to right-marginalise it (newline), then instead | /* We need one blank after the tag. */ | ||
* pad to the right margin and stay off. | overstep = /* LINTED */ | ||
*/ | vis - maxvis + 1; | ||
if (p->flags & TERMP_NOBREAK) { | /* | ||
if ( ! (TERMP_NONOBREAK & p->flags)) | * Behave exactly the same way as groff: | ||
for ( ; vis <= maxvis; vis++) | * If we have overstepped the margin, temporarily move | ||
putchar(' '); | * it to the right and flag the rest of the line to be | ||
} else | * shorter. | ||
putchar('\n'); | * If we landed right at the margin, be happy. | ||
* If we are one step before the margin, temporarily | |||
* move it one step LEFT and flag the rest of the line | |||
* to be longer. | |||
*/ | |||
if (overstep >= -1) { | |||
assert((int)maxvis + overstep >= 0); | |||
/* LINTED */ | |||
maxvis += overstep; | |||
} else | |||
overstep = 0; | |||
p->col = 0; | } else if (TERMP_DANGLE & p->flags) | ||
return; | |||
/* Right-pad. */ | |||
if (maxvis > vis + /* LINTED */ | |||
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) | |||
for ( ; vis < maxvis; vis++) | |||
putchar(' '); | |||
else { /* ...or newline break. */ | |||
putchar('\n'); | |||
for (i = 0; i < (int)p->rmargin; i++) | |||
putchar(' '); | |||
} | |||
} | } | ||
|
|
||
} | } | ||
/* | static void | ||
* Break apart a word into "pwords" (partial-words, usually from | do_special(struct termp *p, const char *word, size_t len) | ||
* breaking up a phrase into individual words) and, eventually, put them | |||
* into the output buffer. If we're a literal word, then don't break up | |||
* the word and put it verbatim into the output buffer. | |||
*/ | |||
void | |||
term_word(struct termp *p, const char *word) | |||
{ | { | ||
int i, j, len; | const char *rhs; | ||
size_t sz; | |||
int i; | |||
len = (int)strlen(word); | rhs = chars_a2ascii(p->symtab, word, len, &sz); | ||
if (p->flags & TERMP_LITERAL) { | if (NULL == rhs) { | ||
term_pword(p, word, len); | #if 0 | ||
fputs("Unknown special character: ", stderr); | |||
for (i = 0; i < (int)len; i++) | |||
fputc(word[i], stderr); | |||
fputc('\n', stderr); | |||
#endif | |||
return; | return; | ||
} | } | ||
for (i = 0; i < (int)sz; i++) | |||
/* LINTED */ | encode(p, rhs[i]); | ||
for (j = i = 0; i < len; i++) { | |||
if (' ' != word[i]) { | |||
j++; | |||
continue; | |||
} | |||
/* Escaped spaces don't delimit... */ | |||
if (i && ' ' == word[i] && '\\' == word[i - 1]) { | |||
j++; | |||
continue; | |||
} | |||
if (0 == j) | |||
continue; | |||
assert(i >= j); | |||
term_pword(p, &word[i - j], j); | |||
j = 0; | |||
} | |||
if (j > 0) { | |||
assert(i >= j); | |||
term_pword(p, &word[i - j], j); | |||
} | |||
} | } | ||
/* | |||
* Determine the symbol indicated by an escape sequences, that is, one | |||
* starting with a backslash. Once done, we pass this value into the | |||
* output buffer by way of the symbol table. | |||
*/ | |||
static void | static void | ||
term_nescape(struct termp *p, const char *word, size_t len) | do_reserved(struct termp *p, const char *word, size_t len) | ||
{ | { | ||
const char *rhs; | const char *rhs; | ||
size_t sz; | size_t sz; | ||
int i; | |||
if (NULL == (rhs = term_a2ascii(p->symtab, word, len, &sz))) | rhs = chars_a2res(p->symtab, word, len, &sz); | ||
if (NULL == rhs) { | |||
#if 0 | |||
fputs("Unknown reserved word: ", stderr); | |||
for (i = 0; i < (int)len; i++) | |||
fputc(word[i], stderr); | |||
fputc('\n', stderr); | |||
#endif | |||
return; | return; | ||
term_stringa(p, rhs, sz); | } | ||
for (i = 0; i < (int)sz; i++) | |||
encode(p, rhs[i]); | |||
} | } | ||
|
|
||
* the escape sequence (we assert upon badly-formed escape sequences). | * the escape sequence (we assert upon badly-formed escape sequences). | ||
*/ | */ | ||
static void | static void | ||
term_pescape(struct termp *p, const char *word, int *i, int len) | do_escaped(struct termp *p, const char **word) | ||
{ | { | ||
int j; | int j, type; | ||
const char *wp; | |||
if (++(*i) >= len) | wp = *word; | ||
type = 1; | |||
if (0 == *(++wp)) { | |||
*word = wp; | |||
return; | return; | ||
} | |||
if ('(' == word[*i]) { | if ('(' == *wp) { | ||
(*i)++; | wp++; | ||
if (*i + 1 >= len) | if (0 == *wp || 0 == *(wp + 1)) { | ||
*word = 0 == *wp ? wp : wp + 1; | |||
return; | return; | ||
} | |||
term_nescape(p, &word[*i], 2); | do_special(p, wp, 2); | ||
(*i)++; | *word = ++wp; | ||
return; | return; | ||
} else if ('*' == word[*i]) { | } else if ('*' == *wp) { | ||
(*i)++; | if (0 == *(++wp)) { | ||
if (*i >= len) | *word = wp; | ||
return; | return; | ||
} | |||
switch (word[*i]) { | switch (*wp) { | ||
case ('('): | case ('('): | ||
(*i)++; | wp++; | ||
if (*i + 1 >= len) | if (0 == *wp || 0 == *(wp + 1)) { | ||
*word = 0 == *wp ? wp : wp + 1; | |||
return; | return; | ||
} | |||
term_nescape(p, &word[*i], 2); | do_reserved(p, wp, 2); | ||
(*i)++; | *word = ++wp; | ||
return; | return; | ||
case ('['): | case ('['): | ||
type = 0; | |||
break; | break; | ||
default: | default: | ||
term_nescape(p, &word[*i], 1); | do_reserved(p, wp, 1); | ||
*word = wp; | |||
return; | return; | ||
} | } | ||
} else if ('f' == word[*i]) { | } else if ('f' == *wp) { | ||
(*i)++; | if (0 == *(++wp)) { | ||
if (*i >= len) | *word = wp; | ||
return; | return; | ||
switch (word[*i]) { | } | ||
switch (*wp) { | |||
case ('B'): | case ('B'): | ||
p->flags |= TERMP_BOLD; | p->bold++; | ||
break; | break; | ||
case ('I'): | case ('I'): | ||
p->flags |= TERMP_UNDER; | p->under++; | ||
break; | break; | ||
case ('P'): | case ('P'): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case ('R'): | case ('R'): | ||
p->flags &= ~TERMP_STYLE; | p->bold = p->under = 0; | ||
break; | break; | ||
default: | default: | ||
break; | break; | ||
} | } | ||
*word = wp; | |||
return; | return; | ||
} else if ('[' != word[*i]) { | } else if ('[' != *wp) { | ||
term_nescape(p, &word[*i], 1); | do_special(p, wp, 1); | ||
*word = wp; | |||
return; | return; | ||
} | } | ||
(*i)++; | wp++; | ||
for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++) | for (j = 0; *wp && ']' != *wp; wp++, j++) | ||
/* Loop... */ ; | /* Loop... */ ; | ||
if (0 == word[*i]) | if (0 == *wp) { | ||
*word = wp; | |||
return; | return; | ||
} | |||
term_nescape(p, &word[*i - j], (size_t)j); | if (type) | ||
do_special(p, wp - j, (size_t)j); | |||
else | |||
do_reserved(p, wp - j, (size_t)j); | |||
*word = wp; | |||
} | } | ||
|
|
||
* phrase that cannot be broken down (such as a literal string). This | * phrase that cannot be broken down (such as a literal string). This | ||
* handles word styling. | * handles word styling. | ||
*/ | */ | ||
static void | void | ||
term_pword(struct termp *p, const char *word, int len) | term_word(struct termp *p, const char *word) | ||
{ | { | ||
int i; | const char *sv; | ||
if (term_isclosedelim(word, len)) | sv = word; | ||
if ( ! (TERMP_IGNDELIM & p->flags)) | |||
p->flags |= TERMP_NOSPACE; | |||
if (word[0] && 0 == word[1]) | |||
switch (word[0]) { | |||
case('.'): | |||
/* FALLTHROUGH */ | |||
case(','): | |||
/* FALLTHROUGH */ | |||
case(';'): | |||
/* FALLTHROUGH */ | |||
case(':'): | |||
/* FALLTHROUGH */ | |||
case('?'): | |||
/* FALLTHROUGH */ | |||
case('!'): | |||
/* FALLTHROUGH */ | |||
case(')'): | |||
/* FALLTHROUGH */ | |||
case(']'): | |||
/* FALLTHROUGH */ | |||
case('}'): | |||
if ( ! (TERMP_IGNDELIM & p->flags)) | |||
p->flags |= TERMP_NOSPACE; | |||
break; | |||
default: | |||
break; | |||
} | |||
if ( ! (TERMP_NOSPACE & p->flags)) | if ( ! (TERMP_NOSPACE & p->flags)) | ||
term_chara(p, ' '); | buffer(p, ' '); | ||
if ( ! (p->flags & TERMP_NONOSPACE)) | if ( ! (p->flags & TERMP_NONOSPACE)) | ||
p->flags &= ~TERMP_NOSPACE; | p->flags &= ~TERMP_NOSPACE; | ||
/* | for ( ; *word; word++) | ||
* If ANSI (word-length styling), then apply our style now, | if ('\\' != *word) | ||
* before the word. | encode(p, *word); | ||
*/ | else | ||
do_escaped(p, &word); | |||
for (i = 0; i < len; i++) { | if (sv[0] && 0 == sv[1]) | ||
if ('\\' == word[i]) { | switch (sv[0]) { | ||
term_pescape(p, word, &i, len); | case('('): | ||
continue; | /* FALLTHROUGH */ | ||
case('['): | |||
/* FALLTHROUGH */ | |||
case('{'): | |||
p->flags |= TERMP_NOSPACE; | |||
break; | |||
default: | |||
break; | |||
} | } | ||
if (TERMP_STYLE & p->flags) { | |||
if (TERMP_BOLD & p->flags) { | |||
term_chara(p, word[i]); | |||
term_chara(p, 8); | |||
} | |||
if (TERMP_UNDER & p->flags) { | |||
term_chara(p, '_'); | |||
term_chara(p, 8); | |||
} | |||
} | |||
term_chara(p, word[i]); | |||
} | |||
if (term_isopendelim(word, len)) | |||
p->flags |= TERMP_NOSPACE; | |||
} | } | ||
/* | /* | ||
* Like term_chara() but for arbitrary-length buffers. Resize the | * Insert a single character into the line-buffer. If the buffer's | ||
* buffer by a factor of two (if the buffer is less than that) or the | * space is exceeded, then allocate more space by doubling the buffer | ||
* buffer's size. | * size. | ||
*/ | */ | ||
static void | static void | ||
term_stringa(struct termp *p, const char *c, size_t sz) | buffer(struct termp *p, char c) | ||
{ | { | ||
size_t s; | size_t s; | ||
if (0 == sz) | if (p->col + 1 >= p->maxcols) { | ||
return; | |||
assert(c); | |||
if (p->col + sz >= p->maxcols) { | |||
if (0 == p->maxcols) | if (0 == p->maxcols) | ||
p->maxcols = 256; | p->maxcols = 256; | ||
s = sz > p->maxcols * 2 ? sz : p->maxcols * 2; | s = p->maxcols * 2; | ||
p->buf = realloc(p->buf, s); | p->buf = realloc(p->buf, s); | ||
if (NULL == p->buf) | if (NULL == p->buf) | ||
err(1, "realloc"); | err(1, "realloc"); /* FIXME: shouldn't be here! */ | ||
p->maxcols = s; | p->maxcols = s; | ||
} | } | ||
p->buf[(int)(p->col)++] = c; | |||
(void)memcpy(&p->buf[(int)p->col], c, sz); | |||
p->col += sz; | |||
} | } | ||
/* | |||
* Insert a single character into the line-buffer. If the buffer's | |||
* space is exceeded, then allocate more space by doubling the buffer | |||
* size. | |||
*/ | |||
static void | static void | ||
term_chara(struct termp *p, char c) | encode(struct termp *p, char c) | ||
{ | { | ||
size_t s; | |||
if (' ' != c) { | |||
if (p->under) { | |||
buffer(p, '_'); | |||
buffer(p, 8); | |||
} | |||
if (p->bold) { | |||
buffer(p, c); | |||
buffer(p, 8); | |||
} | |||
} | |||
buffer(p, c); | |||
} | |||
if (p->col + 1 >= p->maxcols) { | |||
if (0 == p->maxcols) | size_t | ||
p->maxcols = 256; | term_vspan(const struct roffsu *su) | ||
s = p->maxcols * 2; | { | ||
p->buf = realloc(p->buf, s); | double r; | ||
if (NULL == p->buf) | |||
err(1, "realloc"); | switch (su->unit) { | ||
p->maxcols = s; | case (SCALE_CM): | ||
r = su->scale * 2; | |||
break; | |||
case (SCALE_IN): | |||
r = su->scale * 6; | |||
break; | |||
case (SCALE_PC): | |||
r = su->scale; | |||
break; | |||
case (SCALE_PT): | |||
r = su->scale / 8; | |||
break; | |||
case (SCALE_MM): | |||
r = su->scale / 1000; | |||
break; | |||
case (SCALE_VS): | |||
r = su->scale; | |||
break; | |||
default: | |||
r = su->scale - 1; | |||
break; | |||
} | } | ||
p->buf[(int)(p->col)++] = c; | |||
if (r < 0.0) | |||
r = 0.0; | |||
return(/* LINTED */(size_t) | |||
r); | |||
} | } | ||
size_t | |||
term_hspan(const struct roffsu *su) | |||
{ | |||
double r; | |||
/* XXX: CM, IN, and PT are approximations. */ | |||
switch (su->unit) { | |||
case (SCALE_CM): | |||
r = 4 * su->scale; | |||
break; | |||
case (SCALE_IN): | |||
/* XXX: this is an approximation. */ | |||
r = 10 * su->scale; | |||
break; | |||
case (SCALE_PC): | |||
r = (10 * su->scale) / 6; | |||
break; | |||
case (SCALE_PT): | |||
r = (10 * su->scale) / 72; | |||
break; | |||
case (SCALE_MM): | |||
r = su->scale / 1000; /* FIXME: double-check. */ | |||
break; | |||
case (SCALE_VS): | |||
r = su->scale * 2 - 1; /* FIXME: double-check. */ | |||
break; | |||
default: | |||
r = su->scale; | |||
break; | |||
} | |||
if (r < 0.0) | |||
r = 0.0; | |||
return((size_t)/* LINTED */ | |||
r); | |||
} | |||