Return to term.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.123, 2009/11/06 10:31:32 | version 1.150, 2010/06/26 15:36:37 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* | * | ||
* 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 | ||
|
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | */ | ||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <sys/types.h> | |||
#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 "mandoc.h" | |||
#include "chars.h" | #include "chars.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" | ||
/* FIXME: accomodate non-breaking, non-collapsing white-space. */ | static void spec(struct termp *, const char *, size_t); | ||
/* FIXME: accomodate non-breaking, collapsing white-space. */ | 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 adjbuf(struct termp *p, size_t); | |||
static void encode(struct termp *, const char *, size_t); | |||
static struct termp *term_alloc(enum termenc); | |||
static void term_free(struct termp *); | |||
static void do_escaped(struct termp *, const char **); | void | ||
static void do_special(struct termp *, | term_free(struct termp *p) | ||
const char *, size_t); | |||
static void do_reserved(struct termp *, | |||
const char *, size_t); | |||
static void buffer(struct termp *, char); | |||
static void encode(struct termp *, char); | |||
void * | |||
ascii_alloc(void) | |||
{ | { | ||
return(term_alloc(TERMENC_ASCII)); | if (p->buf) | ||
free(p->buf); | |||
if (p->symtab) | |||
chars_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 = calloc(1, sizeof(struct termp)); | ||
if (NULL == p) { | if (NULL == p) { | ||
perror(NULL); | perror(NULL); | ||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||
} | } | ||
p->maxrmargin = 78; | |||
p->enc = enc; | p->enc = enc; | ||
return(p); | return(p); | ||
} | } | ||
|
|
||
* 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 | ||
* broken apart by newlines getting there. A line can also be a | * broken apart by newlines getting there. A line can also be a | ||
* fragment of a columnar list. | * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does | ||
* not have a trailing newline. | |||
* | * | ||
* Specifically, a line is whatever's in p->buf of length p->col, which | * The following flags may be specified: | ||
* is zeroed after this function returns. | |||
* | * | ||
* The usage of termp:flags is as 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 | ||
* prior column has right-padded. | * prior column has right-padded. | ||
|
|
||
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 */ | ||
int j; /* temporary loop index */ | int j; /* temporary loop index */ | ||
int jhy; /* last hyphen before line overflow */ | |||
size_t maxvis, mmax; | size_t maxvis, mmax; | ||
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 = (int)(p->rmargin - p->offset) - overstep < 0 ? | maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ? | ||
/* LINTED */ | /* LINTED */ | ||
0 : p->rmargin - p->offset - overstep; | 0 : p->rmargin - p->offset - p->overstep; | ||
mmax = (int)(p->maxrmargin - p->offset) - overstep < 0 ? | mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ? | ||
/* LINTED */ | /* LINTED */ | ||
0 : p->maxrmargin - p->offset - overstep; | 0 : p->maxrmargin - p->offset - p->overstep; | ||
bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; | ||
/* | |||
* Indent the first line of a paragraph. | |||
*/ | |||
vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset; | |||
/* | /* | ||
* FIXME: if bp is zero, we still output the first word before | * FIXME: if bp is zero, we still output the first word before | ||
* breaking the line. | * breaking the line. | ||
*/ | */ | ||
vis = 0; | vis = vend = i = 0; | ||
while (i < (int)p->col) { | |||
/* | /* | ||
* If in the standard case (left-justified), then begin with our | * Handle literal tab characters. | ||
* indentation, otherwise (columns, etc.) just start spitting | */ | ||
* out text. | for (j = i; j < (int)p->col; j++) { | ||
*/ | if ('\t' != p->buf[j]) | ||
break; | |||
vend = (vis/p->tabwidth+1)*p->tabwidth; | |||
vbl += vend - vis; | |||
vis = vend; | |||
} | |||
if ( ! (p->flags & TERMP_NOLPAD)) | |||
/* LINTED */ | |||
for (j = 0; j < (int)p->offset; j++) | |||
putchar(' '); | |||
for (i = 0; i < (int)p->col; 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 | ||
|
|
||
*/ | */ | ||
/* LINTED */ | /* LINTED */ | ||
for (j = i, vsz = 0; j < (int)p->col; j++) { | for (jhy = 0; j < (int)p->col; j++) { | ||
if (j && ' ' == p->buf[j]) | if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j]) | ||
break; | break; | ||
else if (8 == p->buf[j]) | if (8 != p->buf[j]) { | ||
vsz--; | if (vend > vis && vend < bp && | ||
else | ASCII_HYPH == p->buf[j]) | ||
vsz++; | jhy = j; | ||
vend++; | |||
} else | |||
vend--; | |||
} | } | ||
/* | /* | ||
* Choose the number of blanks to prepend: no blank at the | |||
* beginning of a line, one between words -- but do not | |||
* actually write them yet. | |||
*/ | |||
vbl = (size_t)(0 == vis ? 0 : 1); | |||
/* | |||
* 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. (TODO: hyphenate) | * If so, break to the next line. | ||
* Otherwise, write the chosen number of blanks now. | |||
*/ | */ | ||
if (vis && vis + vbl + vsz > bp) { | if (vend > bp && 0 == jhy && vis > 0) { | ||
putchar('\n'); | vend -= vis; | ||
(*p->endline)(p); | |||
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 overstep width. */ | |||
/* Remove the p->overstep width. */ | |||
bp += (int)/* LINTED */ | bp += (int)/* LINTED */ | ||
overstep; | p->overstep; | ||
overstep = 0; | p->overstep = 0; | ||
} else { | |||
for (j = 0; j < (int)vbl; j++) | |||
putchar(' '); | |||
vis += vbl; | |||
} | } | ||
/* | /* | ||
* Finally, write out the word. | * Skip leading tabs, they were handled above. | ||
*/ | */ | ||
while (i < (int)p->col && '\t' == p->buf[i]) | |||
i++; | |||
/* Write out the [remaining] word. */ | |||
for ( ; i < (int)p->col; i++) { | for ( ; i < (int)p->col; i++) { | ||
if (' ' == p->buf[i]) | if (vend > bp && jhy > 0 && i > jhy) | ||
break; | break; | ||
if ('\t' == p->buf[i]) | |||
break; | |||
if (' ' == p->buf[i]) { | |||
while (' ' == p->buf[i]) { | |||
vbl++; | |||
i++; | |||
} | |||
break; | |||
} | |||
if (ASCII_NBRSP == p->buf[i]) { | |||
vbl++; | |||
continue; | |||
} | |||
/* The unit sep. is a non-breaking space. */ | /* | ||
if (31 == p->buf[i]) | * Now we definitely know there will be | ||
putchar(' '); | * 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, '-'); | |||
else | else | ||
putchar(p->buf[i]); | (*p->letter)(p, p->buf[i]); | ||
p->viscol += 1; | |||
} | } | ||
vis += vsz; | vend += vbl; | ||
vis = vend; | |||
} | } | ||
p->col = 0; | p->col = 0; | ||
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. */ | ||
overstep = /* LINTED */ | p->overstep = /* LINTED */ | ||
vis - maxvis + 1; | vis - maxvis + 1; | ||
/* | /* | ||
|
|
||
* move it one step LEFT and flag the rest of the line | * move it one step LEFT and flag the rest of the line | ||
* to be longer. | * to be longer. | ||
*/ | */ | ||
if (overstep >= -1) { | if (p->overstep >= -1) { | ||
assert((int)maxvis + overstep >= 0); | assert((int)maxvis + p->overstep >= 0); | ||
/* LINTED */ | /* LINTED */ | ||
maxvis += overstep; | maxvis += p->overstep; | ||
} else | } else | ||
overstep = 0; | p->overstep = 0; | ||
} else if (TERMP_DANGLE & p->flags) | } else if (TERMP_DANGLE & p->flags) | ||
return; | return; | ||
/* Right-pad. */ | /* Right-pad. */ | ||
if (maxvis > vis + /* LINTED */ | if (maxvis > vis + /* LINTED */ | ||
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) | ((TERMP_TWOSPACE & p->flags) ? 1 : 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 | ||
do_special(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; | ||
int i; | |||
rhs = chars_a2ascii(p->symtab, word, len, &sz); | rhs = chars_a2ascii(p->symtab, word, len, &sz); | ||
if (rhs) | |||
if (NULL == rhs) { | encode(p, rhs, sz); | ||
#if 0 | |||
fputs("Unknown special character: ", stderr); | |||
for (i = 0; i < (int)len; i++) | |||
fputc(word[i], stderr); | |||
fputc('\n', stderr); | |||
#endif | |||
return; | |||
} | |||
for (i = 0; i < (int)sz; i++) | |||
encode(p, rhs[i]); | |||
} | } | ||
static void | static void | ||
do_reserved(struct termp *p, const char *word, size_t len) | res(struct termp *p, const char *word, size_t len) | ||
{ | { | ||
const char *rhs; | const char *rhs; | ||
size_t sz; | size_t sz; | ||
int i; | |||
rhs = chars_a2res(p->symtab, word, len, &sz); | rhs = chars_a2res(p->symtab, word, len, &sz); | ||
if (rhs) | |||
if (NULL == rhs) { | encode(p, rhs, sz); | ||
#if 0 | |||
fputs("Unknown reserved word: ", stderr); | |||
for (i = 0; i < (int)len; i++) | |||
fputc(word[i], stderr); | |||
fputc('\n', stderr); | |||
#endif | |||
return; | |||
} | |||
for (i = 0; i < (int)sz; i++) | |||
encode(p, rhs[i]); | |||
} | } | ||
/* | void | ||
* Handle an escape sequence: determine its length and pass it to the | term_fontlast(struct termp *p) | ||
* escape-symbol look table. Note that we assume mdoc(3) has validated | |||
* the escape sequence (we assert upon badly-formed escape sequences). | |||
*/ | |||
static void | |||
do_escaped(struct termp *p, const char **word) | |||
{ | { | ||
int j, type, sv, t, lim; | enum termfont f; | ||
const char *wp; | |||
wp = *word; | f = p->fontl; | ||
type = 1; | p->fontl = p->fontq[p->fonti]; | ||
p->fontq[p->fonti] = f; | |||
} | |||
if ('\0' == *(++wp)) { | |||
*word = wp; | |||
return; | |||
} | |||
if ('(' == *wp) { | void | ||
wp++; | term_fontrepl(struct termp *p, enum termfont f) | ||
if ('\0' == *wp || '\0' == *(wp + 1)) { | { | ||
*word = '\0' == *wp ? wp : wp + 1; | |||
return; | |||
} | |||
do_special(p, wp, 2); | p->fontl = p->fontq[p->fonti]; | ||
*word = ++wp; | p->fontq[p->fonti] = f; | ||
return; | } | ||
} else if ('*' == *wp) { | |||
if ('\0' == *(++wp)) { | |||
*word = wp; | |||
return; | |||
} | |||
switch (*wp) { | void | ||
case ('('): | term_fontpush(struct termp *p, enum termfont f) | ||
wp++; | { | ||
if ('\0' == *wp || '\0' == *(wp + 1)) { | |||
*word = '\0' == *wp ? wp : wp + 1; | |||
return; | |||
} | |||
do_reserved(p, wp, 2); | assert(p->fonti + 1 < 10); | ||
*word = ++wp; | p->fontl = p->fontq[p->fonti]; | ||
return; | p->fontq[++p->fonti] = f; | ||
case ('['): | } | ||
type = 0; | |||
break; | |||
default: | |||
do_reserved(p, wp, 1); | |||
*word = wp; | |||
return; | |||
} | |||
} else if ('s' == *wp) { | |||
/* This closely follows mandoc_special(). */ | |||
if ('\0' == *(++wp)) { | |||
*word = wp; | |||
return; | |||
} | |||
t = 0; | const void * | ||
lim = 1; | term_fontq(struct termp *p) | ||
{ | |||
if (*wp == '\'') { | return(&p->fontq[p->fonti]); | ||
lim = 0; | } | ||
t = 1; | |||
++wp; | |||
} else if (*wp == '[') { | |||
lim = 0; | |||
t = 2; | |||
++wp; | |||
} else if (*wp == '(') { | |||
lim = 2; | |||
t = 3; | |||
++wp; | |||
} | |||
if (*wp == '+' || *wp == '-') | |||
++wp; | |||
if (*wp == '\'') { | enum termfont | ||
if (t) { | term_fonttop(struct termp *p) | ||
*word = wp; | { | ||
return; | |||
} | |||
lim = 0; | |||
t = 1; | |||
++wp; | |||
} else if (*wp == '[') { | |||
if (t) { | |||
*word = wp; | |||
return; | |||
} | |||
lim = 0; | |||
t = 2; | |||
++wp; | |||
} else if (*wp == '(') { | |||
if (t) { | |||
*word = wp; | |||
return; | |||
} | |||
lim = 2; | |||
t = 3; | |||
++wp; | |||
} | |||
if ( ! isdigit((u_char)*wp)) { | return(p->fontq[p->fonti]); | ||
*word = --wp; | } | ||
return; | |||
} | |||
for (j = 0; isdigit((u_char)*wp); j++) { | |||
if (lim && j >= lim) | |||
break; | |||
++wp; | |||
} | |||
if (t && t < 3) { | void | ||
if (1 == t && *wp != '\'') { | term_fontpopq(struct termp *p, const void *key) | ||
*word = --wp; | { | ||
return; | |||
} | |||
if (2 == t && *wp != ']') { | |||
*word = --wp; | |||
return; | |||
} | |||
++wp; | |||
} | |||
*word = --wp; | |||
return; | |||
} else if ('f' == *wp) { | while (p->fonti >= 0 && key != &p->fontq[p->fonti]) | ||
if ('\0' == *(++wp)) { | p->fonti--; | ||
*word = wp; | assert(p->fonti >= 0); | ||
return; | } | ||
} | |||
switch (*wp) { | |||
case ('3'): | |||
/* FALLTHROUGH */ | |||
case ('B'): | |||
p->metamask = p->metafont; | |||
p->metafont |= METAF_BOLD; | |||
break; | |||
case ('2'): | |||
/* FALLTHROUGH */ | |||
case ('I'): | |||
p->metamask = p->metafont; | |||
p->metafont |= METAF_UNDER; | |||
break; | |||
case ('P'): | |||
sv = p->metamask; | |||
p->metamask = p->metafont; | |||
p->metafont = sv; | |||
break; | |||
case ('1'): | |||
/* FALLTHROUGH */ | |||
case ('R'): | |||
p->metamask = p->metafont; | |||
p->metafont &= ~METAF_UNDER; | |||
p->metafont &= ~METAF_BOLD; | |||
break; | |||
default: | |||
break; | |||
} | |||
*word = wp; | void | ||
return; | term_fontpop(struct termp *p) | ||
{ | |||
} else if ('[' != *wp) { | assert(p->fonti); | ||
do_special(p, wp, 1); | p->fonti--; | ||
*word = wp; | |||
return; | |||
} | |||
wp++; | |||
for (j = 0; *wp && ']' != *wp; wp++, j++) | |||
/* Loop... */ ; | |||
if ('\0' == *wp) { | |||
*word = wp; | |||
return; | |||
} | |||
if (type) | |||
do_special(p, wp - j, (size_t)j); | |||
else | |||
do_reserved(p, wp - j, (size_t)j); | |||
*word = wp; | |||
} | } | ||
|
|
||
void | void | ||
term_word(struct termp *p, const char *word) | term_word(struct termp *p, const char *word) | ||
{ | { | ||
const char *sv; | const char *sv, *seq; | ||
int sz; | |||
size_t ssz; | |||
enum roffdeco deco; | |||
sv = word; | sv = word; | ||
|
|
||
case(')'): | case(')'): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case(']'): | case(']'): | ||
/* FALLTHROUGH */ | |||
case('}'): | |||
if ( ! (TERMP_IGNDELIM & p->flags)) | if ( ! (TERMP_IGNDELIM & p->flags)) | ||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
break; | break; | ||
|
|
||
break; | break; | ||
} | } | ||
if ( ! (TERMP_NOSPACE & p->flags)) | if ( ! (TERMP_NOSPACE & p->flags)) { | ||
buffer(p, ' '); | bufferc(p, ' '); | ||
if (TERMP_SENTENCE & p->flags) | |||
bufferc(p, ' '); | |||
} | |||
if ( ! (p->flags & TERMP_NONOSPACE)) | if ( ! (p->flags & TERMP_NONOSPACE)) | ||
p->flags &= ~TERMP_NOSPACE; | p->flags &= ~TERMP_NOSPACE; | ||
for ( ; *word; word++) | p->flags &= ~TERMP_SENTENCE; | ||
if ('\\' != *word) | |||
encode(p, *word); | |||
else | |||
do_escaped(p, &word); | |||
/* FIXME: use strcspn. */ | |||
while (*word) { | |||
if ('\\' != *word) { | |||
encode(p, word, 1); | |||
word++; | |||
continue; | |||
} | |||
seq = ++word; | |||
sz = a2roffdeco(&deco, &seq, &ssz); | |||
switch (deco) { | |||
case (DECO_RESERVED): | |||
res(p, seq, ssz); | |||
break; | |||
case (DECO_SPECIAL): | |||
spec(p, seq, ssz); | |||
break; | |||
case (DECO_BOLD): | |||
term_fontrepl(p, TERMFONT_BOLD); | |||
break; | |||
case (DECO_ITALIC): | |||
term_fontrepl(p, TERMFONT_UNDER); | |||
break; | |||
case (DECO_ROMAN): | |||
term_fontrepl(p, TERMFONT_NONE); | |||
break; | |||
case (DECO_PREVIOUS): | |||
term_fontlast(p); | |||
break; | |||
default: | |||
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]) | if (sv[0] && 0 == sv[1]) | ||
switch (sv[0]) { | switch (sv[0]) { | ||
case('('): | case('('): | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case('['): | case('['): | ||
/* FALLTHROUGH */ | |||
case('{'): | |||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
break; | break; | ||
default: | default: | ||
|
|
||
} | } | ||
/* | |||
* 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 | ||
buffer(struct termp *p, char c) | adjbuf(struct termp *p, size_t sz) | ||
{ | { | ||
size_t s; | |||
if (p->col + 1 >= p->maxcols) { | if (0 == p->maxcols) | ||
if (0 == p->maxcols) | p->maxcols = 1024; | ||
p->maxcols = 256; | while (sz >= p->maxcols) | ||
s = p->maxcols * 2; | p->maxcols <<= 2; | ||
p->buf = realloc(p->buf, s); | |||
if (NULL == p->buf) { | p->buf = realloc(p->buf, p->maxcols); | ||
perror(NULL); | if (NULL == p->buf) { | ||
exit(EXIT_FAILURE); | perror(NULL); | ||
} | exit(EXIT_FAILURE); | ||
p->maxcols = s; | |||
} | } | ||
p->buf[(int)(p->col)++] = c; | |||
} | } | ||
static void | static void | ||
encode(struct termp *p, char c) | buffera(struct termp *p, const char *word, size_t sz) | ||
{ | { | ||
if (isgraph((u_char)c)) { | if (p->col + sz >= p->maxcols) | ||
if (p->under || METAF_UNDER & p->metafont) { | adjbuf(p, p->col + sz); | ||
buffer(p, '_'); | |||
buffer(p, 8); | memcpy(&p->buf[(int)p->col], word, sz); | ||
p->col += sz; | |||
} | |||
static void | |||
bufferc(struct termp *p, char c) | |||
{ | |||
if (p->col + 1 >= p->maxcols) | |||
adjbuf(p, p->col + 1); | |||
p->buf[(int)p->col++] = c; | |||
} | |||
static void | |||
encode(struct termp *p, const char *word, size_t sz) | |||
{ | |||
enum termfont f; | |||
int i; | |||
/* | |||
* Encode and buffer a string of characters. If the current | |||
* font mode is unset, buffer directly, else encode then buffer | |||
* character by character. | |||
*/ | |||
if (TERMFONT_NONE == (f = term_fonttop(p))) { | |||
buffera(p, word, sz); | |||
return; | |||
} | |||
for (i = 0; i < (int)sz; i++) { | |||
if ( ! isgraph((u_char)word[i])) { | |||
bufferc(p, word[i]); | |||
continue; | |||
} | } | ||
if (p->bold || METAF_BOLD & p->metafont) { | |||
buffer(p, c); | if (TERMFONT_UNDER == f) | ||
buffer(p, 8); | bufferc(p, '_'); | ||
} | else | ||
bufferc(p, word[i]); | |||
bufferc(p, 8); | |||
bufferc(p, word[i]); | |||
} | } | ||
buffer(p, c); | |||
} | } | ||
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; | |||
for (sz = 0; *cp; cp++) | |||
sz += (*p->width)(p, *cp); | |||
return(sz); | |||
} | |||
size_t | |||
term_vspan(const struct termp *p, const struct roffsu *su) | |||
{ | |||
double r; | double r; | ||
switch (su->unit) { | switch (su->unit) { | ||
|
|
||
size_t | size_t | ||
term_hspan(const struct roffsu *su) | term_hspan(const struct termp *p, const struct roffsu *su) | ||
{ | { | ||
double r; | double r; | ||