Return to term.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.131, 2010/04/08 07:05:38 | version 1.189, 2011/05/15 14:50:01 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> | |||
* | * | ||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||
|
|
||
#include <assert.h> | #include <assert.h> | ||
#include <ctype.h> | #include <ctype.h> | ||
#include <stdint.h> | |||
#include <stdio.h> | #include <stdio.h> | ||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include <time.h> | |||
#include "chars.h" | #include "mandoc.h" | ||
#include "out.h" | #include "out.h" | ||
#include "term.h" | #include "term.h" | ||
#include "man.h" | |||
#include "mdoc.h" | |||
#include "main.h" | #include "main.h" | ||
static struct termp *term_alloc(enum termenc); | |||
static void term_free(struct termp *); | |||
static void spec(struct termp *, const char *, size_t); | static void spec(struct termp *, const char *, size_t); | ||
static void res(struct termp *, const char *, size_t); | static void res(struct termp *, const char *, size_t); | ||
static void buffera(struct termp *, const char *, size_t); | |||
static void bufferc(struct termp *, char); | static void bufferc(struct termp *, char); | ||
static void adjbuf(struct termp *p, size_t); | static void adjbuf(struct termp *p, int); | ||
static void encode(struct termp *, const char *, size_t); | static void encode(struct termp *, const char *, size_t); | ||
void * | void | ||
ascii_alloc(void) | term_free(struct termp *p) | ||
{ | { | ||
return(term_alloc(TERMENC_ASCII)); | if (p->buf) | ||
free(p->buf); | |||
if (p->symtab) | |||
mchars_free(p->symtab); | |||
free(p); | |||
} | } | ||
void | void | ||
terminal_free(void *arg) | term_begin(struct termp *p, term_margin head, | ||
term_margin foot, const void *arg) | |||
{ | { | ||
term_free((struct termp *)arg); | p->headf = head; | ||
p->footf = foot; | |||
p->argf = arg; | |||
(*p->begin)(p); | |||
} | } | ||
static void | void | ||
term_free(struct termp *p) | term_end(struct termp *p) | ||
{ | { | ||
if (p->buf) | (*p->end)(p); | ||
free(p->buf); | |||
if (p->symtab) | |||
chars_free(p->symtab); | |||
free(p); | |||
} | } | ||
static struct termp * | struct termp * | ||
term_alloc(enum termenc enc) | term_alloc(enum termenc enc) | ||
{ | { | ||
struct termp *p; | struct termp *p; | ||
p = calloc(1, sizeof(struct termp)); | p = mandoc_calloc(1, sizeof(struct termp)); | ||
if (NULL == p) { | |||
perror(NULL); | |||
exit(EXIT_FAILURE); | |||
} | |||
p->enc = enc; | p->enc = enc; | ||
return(p); | return(p); | ||
} | } | ||
|
|
||
int i; /* current input position in p->buf */ | int i; /* current input position in p->buf */ | ||
size_t vis; /* current visual position on output */ | size_t vis; /* current visual position on output */ | ||
size_t vbl; /* number of blanks to prepend to output */ | size_t vbl; /* number of blanks to prepend to output */ | ||
size_t vsz; /* visual characters to write to output */ | size_t vend; /* end of word visual position on output */ | ||
size_t bp; /* visual right border position */ | size_t bp; /* visual right border position */ | ||
size_t hyph; /* visible position of hyphen */ | size_t dv; /* temporary for visual pos calculations */ | ||
int j; /* temporary loop index */ | int j; /* temporary loop index for p->buf */ | ||
size_t maxvis, mmax; | int jhy; /* last hyph before overflow w/r/t j */ | ||
size_t maxvis; /* output position of visible boundary */ | |||
size_t mmax; /* used in calculating bp */ | |||
/* | /* | ||
* First, establish the maximum columns of "visible" content. | * First, establish the maximum columns of "visible" content. | ||
|
|
||
* an indentation, but can be, for tagged lists or columns, a | * an indentation, but can be, for tagged lists or columns, a | ||
* small set of values. | * small set of values. | ||
*/ | */ | ||
assert (p->rmargin >= p->offset); | |||
dv = p->rmargin - p->offset; | |||
maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; | |||
dv = p->maxrmargin - p->offset; | |||
mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; | |||
assert(p->offset < p->rmargin); | |||
maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ? | |||
/* LINTED */ | |||
0 : p->rmargin - p->offset - p->overstep; | |||
mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ? | |||
/* LINTED */ | |||
0 : p->maxrmargin - p->offset - p->overstep; | |||
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | ||
/* | |||
* FIXME: if bp is zero, we still output the first word before | |||
* breaking the line. | |||
*/ | |||
vis = 0; | |||
/* | /* | ||
* If in the standard case (left-justified), then begin with our | * Indent the first line of a paragraph. | ||
* indentation, otherwise (columns, etc.) just start spitting | |||
* out text. | |||
*/ | */ | ||
vbl = p->flags & TERMP_NOLPAD ? (size_t)0 : p->offset; | |||
if ( ! (p->flags & TERMP_NOLPAD)) | vis = vend = 0; | ||
/* LINTED */ | i = 0; | ||
for (j = 0; j < (int)p->offset; j++) | |||
putchar(' '); | |||
for (i = 0; i < (int)p->col; i++) { | while (i < p->col) { | ||
/* | /* | ||
* Handle literal tab characters: collapse all | |||
* subsequent tabs into a single huge set of spaces. | |||
*/ | |||
while (i < p->col && '\t' == p->buf[i]) { | |||
vend = (vis / p->tabwidth + 1) * p->tabwidth; | |||
vbl += vend - vis; | |||
vis = vend; | |||
i++; | |||
} | |||
/* | |||
* Count up visible word characters. Control sequences | * Count up visible word characters. Control sequences | ||
* (starting with the CSI) aren't counted. A space | * (starting with the CSI) aren't counted. A space | ||
* generates a non-printing word, which is valid (the | * generates a non-printing word, which is valid (the | ||
* space is printed according to regular spacing rules). | * space is printed according to regular spacing rules). | ||
* Collect the number of printable characters until the | |||
* first hyphen, if found. Hyphens aren't included if | |||
* they're the first character (so `Fl' doesn't break) | |||
* or second consecutive character (`Fl -'). | |||
*/ | */ | ||
/* LINTED */ | for (j = i, jhy = 0; j < p->col; j++) { | ||
for (j = i, vsz = 0, hyph = 0; j < (int)p->col; j++) { | if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j]) | ||
if (j && ' ' == p->buf[j]) | |||
break; | break; | ||
if (8 == p->buf[j]) | |||
vsz--; | |||
else | |||
vsz++; | |||
if (j > i && '-' == p->buf[j] && 0 == hyph) | |||
if ('-' != p->buf[j - 1]) | |||
hyph = vsz; | |||
} | |||
/* | /* Back over the the last printed character. */ | ||
* Choose the number of blanks to prepend: no blank at the | if (8 == p->buf[j]) { | ||
* beginning of a line, one between words -- but do not | assert(j); | ||
* actually write them yet. | vend -= (*p->width)(p, p->buf[j - 1]); | ||
*/ | continue; | ||
} | |||
vbl = (size_t)(0 == vis ? 0 : 1); | /* Regular word. */ | ||
/* Break at the hyphen point if we overrun. */ | |||
if (vend > vis && vend < bp && | |||
ASCII_HYPH == p->buf[j]) | |||
jhy = j; | |||
vend += (*p->width)(p, p->buf[j]); | |||
} | |||
/* | /* | ||
* Find out whether we would exceed the right margin. | * Find out whether we would exceed the right margin. | ||
* If so, break to the next line, possibly after | * If so, break to the next line. | ||
* emittign character up to a hyphen. Otherwise, write | |||
* the chosen number of blanks. | |||
*/ | */ | ||
if (vend > bp && 0 == jhy && vis > 0) { | |||
if (vis && vis + vbl + vsz > bp) { | vend -= vis; | ||
/* | (*p->endline)(p); | ||
* Has a hyphen been found before the breakpoint | |||
* that we can use? | |||
*/ | |||
if (hyph && vis + vbl + hyph <= bp) { | |||
/* First prepend blanks. */ | |||
for (j = 0; j < (int)vbl; j++) | |||
putchar(' '); | |||
/* Emit up to the character. */ | |||
do { | |||
if (31 == p->buf[i]) | |||
putchar(' '); | |||
else | |||
putchar(p->buf[i]); | |||
if (8 != p->buf[i]) | |||
vsz--; | |||
} while ('-' != p->buf[i++]); | |||
/* Emit trailing decoration. */ | |||
if (8 == p->buf[i]) { | |||
putchar(p->buf[i]); | |||
putchar(p->buf[i + 1]); | |||
} | |||
} | |||
putchar('\n'); | |||
if (TERMP_NOBREAK & p->flags) { | if (TERMP_NOBREAK & p->flags) { | ||
for (j = 0; j < (int)p->rmargin; j++) | p->viscol = p->rmargin; | ||
putchar(' '); | (*p->advance)(p, p->rmargin); | ||
vis = p->rmargin - p->offset; | vend += p->rmargin - p->offset; | ||
} else { | } else { | ||
for (j = 0; j < (int)p->offset; j++) | p->viscol = 0; | ||
putchar(' '); | vbl = p->offset; | ||
vis = 0; | |||
} | } | ||
/* Remove the p->overstep width. */ | /* Remove the p->overstep width. */ | ||
bp += (int)/* LINTED */ | bp += (size_t)p->overstep; | ||
p->overstep; | |||
p->overstep = 0; | p->overstep = 0; | ||
} else { | |||
for (j = 0; j < (int)vbl; j++) | |||
putchar(' '); | |||
vis += vbl; | |||
} | } | ||
/* Write out the [remaining] word. */ | /* Write out the [remaining] word. */ | ||
for ( ; i < (int)p->col; i++) | for ( ; i < p->col; i++) { | ||
if (' ' == p->buf[i]) | if (vend > bp && jhy > 0 && i > jhy) | ||
break; | break; | ||
else if (31 == p->buf[i]) | if ('\t' == p->buf[i]) | ||
putchar(' '); | break; | ||
else | if (' ' == p->buf[i]) { | ||
putchar(p->buf[i]); | j = i; | ||
while (' ' == p->buf[i]) | |||
i++; | |||
dv = (size_t)(i - j) * (*p->width)(p, ' '); | |||
vbl += dv; | |||
vend += dv; | |||
break; | |||
} | |||
if (ASCII_NBRSP == p->buf[i]) { | |||
vbl += (*p->width)(p, ' '); | |||
continue; | |||
} | |||
vis += vsz; | /* | ||
* Now we definitely know there will be | |||
* printable characters to output, | |||
* so write preceding white space now. | |||
*/ | |||
if (vbl) { | |||
(*p->advance)(p, vbl); | |||
p->viscol += vbl; | |||
vbl = 0; | |||
} | |||
if (ASCII_HYPH == p->buf[i]) { | |||
(*p->letter)(p, '-'); | |||
p->viscol += (*p->width)(p, '-'); | |||
} else { | |||
(*p->letter)(p, p->buf[i]); | |||
p->viscol += (*p->width)(p, p->buf[i]); | |||
} | |||
} | |||
vis = vend; | |||
} | } | ||
/* | |||
* If there was trailing white space, it was not printed; | |||
* so reset the cursor position accordingly. | |||
*/ | |||
vis -= vbl; | |||
p->col = 0; | p->col = 0; | ||
p->overstep = 0; | p->overstep = 0; | ||
if ( ! (TERMP_NOBREAK & p->flags)) { | if ( ! (TERMP_NOBREAK & p->flags)) { | ||
putchar('\n'); | p->viscol = 0; | ||
(*p->endline)(p); | |||
return; | return; | ||
} | } | ||
if (TERMP_HANG & p->flags) { | if (TERMP_HANG & p->flags) { | ||
/* We need one blank after the tag. */ | /* We need one blank after the tag. */ | ||
p->overstep = /* LINTED */ | p->overstep = (int)(vis - maxvis + (*p->width)(p, ' ')); | ||
vis - maxvis + 1; | |||
/* | /* | ||
* Behave exactly the same way as groff: | * Behave exactly the same way as groff: | ||
|
|
||
*/ | */ | ||
if (p->overstep >= -1) { | if (p->overstep >= -1) { | ||
assert((int)maxvis + p->overstep >= 0); | assert((int)maxvis + p->overstep >= 0); | ||
/* LINTED */ | maxvis += (size_t)p->overstep; | ||
maxvis += p->overstep; | |||
} else | } else | ||
p->overstep = 0; | p->overstep = 0; | ||
|
|
||
return; | return; | ||
/* Right-pad. */ | /* Right-pad. */ | ||
if (maxvis > vis + /* LINTED */ | if (maxvis > vis + | ||
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) | ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) { | ||
for ( ; vis < maxvis; vis++) | p->viscol += maxvis - vis; | ||
putchar(' '); | (*p->advance)(p, maxvis - vis); | ||
else { /* ...or newline break. */ | vis += (maxvis - vis); | ||
putchar('\n'); | } else { /* ...or newline break. */ | ||
for (i = 0; i < (int)p->rmargin; i++) | (*p->endline)(p); | ||
putchar(' '); | p->viscol = p->rmargin; | ||
(*p->advance)(p, p->rmargin); | |||
} | } | ||
} | } | ||
|
|
||
{ | { | ||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
if (0 == p->col) { | if (0 == p->col && 0 == p->viscol) { | ||
p->flags &= ~TERMP_NOLPAD; | p->flags &= ~TERMP_NOLPAD; | ||
return; | return; | ||
} | } | ||
|
|
||
{ | { | ||
term_newln(p); | term_newln(p); | ||
putchar('\n'); | p->viscol = 0; | ||
(*p->endline)(p); | |||
} | } | ||
static void | static void | ||
numbered(struct termp *p, const char *word, size_t len) | |||
{ | |||
char c; | |||
if ('\0' != (c = mchars_num2char(word, len))) | |||
encode(p, &c, 1); | |||
} | |||
static void | |||
spec(struct termp *p, const char *word, size_t len) | spec(struct termp *p, const char *word, size_t len) | ||
{ | { | ||
const char *rhs; | const char *rhs; | ||
size_t sz; | size_t sz; | ||
rhs = chars_a2ascii(p->symtab, word, len, &sz); | rhs = mchars_spec2str(p->symtab, word, len, &sz); | ||
if (rhs) | if (rhs) | ||
encode(p, rhs, sz); | encode(p, rhs, sz); | ||
else if (1 == len) | |||
encode(p, word, len); | |||
} | } | ||
|
|
||
const char *rhs; | const char *rhs; | ||
size_t sz; | size_t sz; | ||
rhs = chars_a2res(p->symtab, word, len, &sz); | rhs = mchars_res2str(p->symtab, word, len, &sz); | ||
if (rhs) | if (rhs) | ||
encode(p, rhs, sz); | encode(p, rhs, sz); | ||
} | } | ||
|
|
||
void | void | ||
term_word(struct termp *p, const char *word) | term_word(struct termp *p, const char *word) | ||
{ | { | ||
const char *sv, *seq; | const char *seq; | ||
int sz; | int sz; | ||
size_t ssz; | size_t ssz; | ||
enum roffdeco deco; | enum mandoc_esc esc; | ||
sv = word; | if ( ! (TERMP_NOSPACE & p->flags)) { | ||
if ( ! (TERMP_KEEP & p->flags)) { | |||
if (TERMP_PREKEEP & p->flags) | |||
p->flags |= TERMP_KEEP; | |||
bufferc(p, ' '); | |||
if (TERMP_SENTENCE & p->flags) | |||
bufferc(p, ' '); | |||
} else | |||
bufferc(p, ASCII_NBRSP); | |||
} | |||
if (word[0] && '\0' == word[1]) | |||
switch (word[0]) { | |||
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)) | |||
bufferc(p, ' '); | |||
if ( ! (p->flags & TERMP_NONOSPACE)) | if ( ! (p->flags & TERMP_NONOSPACE)) | ||
p->flags &= ~TERMP_NOSPACE; | p->flags &= ~TERMP_NOSPACE; | ||
else | |||
p->flags |= TERMP_NOSPACE; | |||
/* FIXME: use strcspn. */ | p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM); | ||
while (*word) { | while ('\0' != *word) { | ||
if ('\\' != *word) { | if ((ssz = strcspn(word, "\\")) > 0) | ||
encode(p, word, 1); | encode(p, word, ssz); | ||
word++; | |||
word += (int)ssz; | |||
if ('\\' != *word) | |||
continue; | continue; | ||
} | |||
seq = ++word; | word++; | ||
sz = a2roffdeco(&deco, &seq, &ssz); | esc = mandoc_escape(&word, &seq, &sz); | ||
if (ESCAPE_ERROR == esc) | |||
break; | |||
switch (deco) { | switch (esc) { | ||
case (DECO_RESERVED): | case (ESCAPE_NUMBERED): | ||
res(p, seq, ssz); | numbered(p, seq, sz); | ||
break; | break; | ||
case (DECO_SPECIAL): | case (ESCAPE_PREDEF): | ||
spec(p, seq, ssz); | res(p, seq, sz); | ||
break; | break; | ||
case (DECO_BOLD): | case (ESCAPE_SPECIAL): | ||
spec(p, seq, sz); | |||
break; | |||
case (ESCAPE_FONTBOLD): | |||
term_fontrepl(p, TERMFONT_BOLD); | term_fontrepl(p, TERMFONT_BOLD); | ||
break; | break; | ||
case (DECO_ITALIC): | case (ESCAPE_FONTITALIC): | ||
term_fontrepl(p, TERMFONT_UNDER); | term_fontrepl(p, TERMFONT_UNDER); | ||
break; | break; | ||
case (DECO_ROMAN): | case (ESCAPE_FONTROMAN): | ||
term_fontrepl(p, TERMFONT_NONE); | term_fontrepl(p, TERMFONT_NONE); | ||
break; | break; | ||
case (DECO_PREVIOUS): | case (ESCAPE_FONTPREV): | ||
term_fontlast(p); | term_fontlast(p); | ||
break; | break; | ||
default: | case (ESCAPE_NOSPACE): | ||
if ('\0' == *word) | |||
p->flags |= TERMP_NOSPACE; | |||
break; | break; | ||
} | |||
word += sz; | |||
if (DECO_NOSPACE == deco && '\0' == *word) | |||
p->flags |= TERMP_NOSPACE; | |||
} | |||
/* | |||
* Note that we don't process the pipe: the parser sees it as | |||
* punctuation, but we don't in terms of typography. | |||
*/ | |||
if (sv[0] && 0 == sv[1]) | |||
switch (sv[0]) { | |||
case('('): | |||
/* FALLTHROUGH */ | |||
case('['): | |||
p->flags |= TERMP_NOSPACE; | |||
break; | |||
default: | default: | ||
break; | break; | ||
} | } | ||
} | |||
} | } | ||
static void | static void | ||
adjbuf(struct termp *p, size_t sz) | adjbuf(struct termp *p, int sz) | ||
{ | { | ||
if (0 == p->maxcols) | if (0 == p->maxcols) | ||
|
|
||
while (sz >= p->maxcols) | while (sz >= p->maxcols) | ||
p->maxcols <<= 2; | p->maxcols <<= 2; | ||
p->buf = realloc(p->buf, p->maxcols); | p->buf = mandoc_realloc | ||
if (NULL == p->buf) { | (p->buf, sizeof(int) * (size_t)p->maxcols); | ||
perror(NULL); | |||
exit(EXIT_FAILURE); | |||
} | |||
} | } | ||
static void | static void | ||
buffera(struct termp *p, const char *word, size_t sz) | |||
{ | |||
if (p->col + sz >= p->maxcols) | |||
adjbuf(p, p->col + sz); | |||
memcpy(&p->buf[(int)p->col], word, sz); | |||
p->col += sz; | |||
} | |||
static void | |||
bufferc(struct termp *p, char c) | bufferc(struct termp *p, char c) | ||
{ | { | ||
if (p->col + 1 >= p->maxcols) | if (p->col + 1 >= p->maxcols) | ||
adjbuf(p, p->col + 1); | adjbuf(p, p->col + 1); | ||
p->buf[(int)p->col++] = c; | p->buf[p->col++] = c; | ||
} | } | ||
|
|
||
encode(struct termp *p, const char *word, size_t sz) | encode(struct termp *p, const char *word, size_t sz) | ||
{ | { | ||
enum termfont f; | enum termfont f; | ||
int i; | int i, len; | ||
/* LINTED */ | |||
len = sz; | |||
/* | /* | ||
* Encode and buffer a string of characters. If the current | * Encode and buffer a string of characters. If the current | ||
* font mode is unset, buffer directly, else encode then buffer | * font mode is unset, buffer directly, else encode then buffer | ||
|
|
||
*/ | */ | ||
if (TERMFONT_NONE == (f = term_fonttop(p))) { | if (TERMFONT_NONE == (f = term_fonttop(p))) { | ||
buffera(p, word, sz); | if (p->col + len >= p->maxcols) | ||
adjbuf(p, p->col + len); | |||
for (i = 0; i < len; i++) | |||
p->buf[p->col++] = word[i]; | |||
return; | return; | ||
} | } | ||
for (i = 0; i < (int)sz; i++) { | /* Pre-buffer, assuming worst-case. */ | ||
if ( ! isgraph((u_char)word[i])) { | |||
bufferc(p, word[i]); | if (p->col + 1 + (len * 3) >= p->maxcols) | ||
adjbuf(p, p->col + 1 + (len * 3)); | |||
for (i = 0; i < len; i++) { | |||
if ( ! isgraph((unsigned char)word[i])) { | |||
p->buf[p->col++] = word[i]; | |||
continue; | continue; | ||
} | } | ||
if (TERMFONT_UNDER == f) | if (TERMFONT_UNDER == f) | ||
bufferc(p, '_'); | p->buf[p->col++] = '_'; | ||
else | else | ||
bufferc(p, word[i]); | p->buf[p->col++] = word[i]; | ||
bufferc(p, 8); | p->buf[p->col++] = 8; | ||
bufferc(p, word[i]); | p->buf[p->col++] = word[i]; | ||
} | } | ||
} | } | ||
size_t | size_t | ||
term_vspan(const struct roffsu *su) | term_len(const struct termp *p, size_t sz) | ||
{ | { | ||
return((*p->width)(p, ' ') * sz); | |||
} | |||
size_t | |||
term_strlen(const struct termp *p, const char *cp) | |||
{ | |||
size_t sz, rsz, i; | |||
int ssz; | |||
const char *seq, *rhs; | |||
static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' }; | |||
/* | |||
* Account for escaped sequences within string length | |||
* calculations. This follows the logic in term_word() as we | |||
* must calculate the width of produced strings. | |||
*/ | |||
sz = 0; | |||
while ('\0' != *cp) { | |||
rsz = strcspn(cp, rej); | |||
for (i = 0; i < rsz; i++) | |||
sz += (*p->width)(p, *cp++); | |||
switch (*cp) { | |||
case ('\\'): | |||
cp++; | |||
switch (mandoc_escape(&cp, &seq, &ssz)) { | |||
case (ESCAPE_ERROR): | |||
return(sz); | |||
case (ESCAPE_PREDEF): | |||
rhs = mchars_res2str | |||
(p->symtab, seq, ssz, &rsz); | |||
break; | |||
case (ESCAPE_SPECIAL): | |||
rhs = mchars_spec2str | |||
(p->symtab, seq, ssz, &rsz); | |||
if (ssz != 1 || rhs) | |||
break; | |||
rhs = seq; | |||
rsz = ssz; | |||
break; | |||
default: | |||
rhs = NULL; | |||
break; | |||
} | |||
if (NULL == rhs) | |||
break; | |||
for (i = 0; i < rsz; i++) | |||
sz += (*p->width)(p, *rhs++); | |||
break; | |||
case (ASCII_NBRSP): | |||
sz += (*p->width)(p, ' '); | |||
cp++; | |||
break; | |||
case (ASCII_HYPH): | |||
sz += (*p->width)(p, '-'); | |||
cp++; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
return(sz); | |||
} | |||
/* ARGSUSED */ | |||
size_t | |||
term_vspan(const struct termp *p, const struct roffsu *su) | |||
{ | |||
double r; | double r; | ||
switch (su->unit) { | switch (su->unit) { | ||
|
|
||
r); | r); | ||
} | } | ||
size_t | size_t | ||
term_hspan(const struct roffsu *su) | term_hspan(const struct termp *p, const struct roffsu *su) | ||
{ | { | ||
double r; | double v; | ||
/* XXX: CM, IN, and PT are approximations. */ | v = ((*p->hspan)(p, su)); | ||
if (v < 0.0) | |||
switch (su->unit) { | v = 0.0; | ||
case (SCALE_CM): | return((size_t) /* LINTED */ | ||
r = 4 * su->scale; | v); | ||
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); | |||
} | } | ||