ref: c8abc41c0e6570845d07f45c7f6ccb5be0cc8038
dir: /wb.c/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "roff.h"
#define R_F(wb) ((wb)->r_f >= 0 ? (wb)->r_f : n_f) /* current font */
#define R_S(wb) ((wb)->r_s >= 0 ? (wb)->r_s : n_s) /* current size */
#define R_M(wb) ((wb)->r_m >= 0 ? (wb)->r_m : n_m) /* current color */
void wb_init(struct wb *wb)
{
memset(wb, 0, sizeof(*wb));
sbuf_init(&wb->sbuf);
wb->f = -1;
wb->s = -1;
wb->m = -1;
wb->r_f = -1;
wb->r_s = -1;
wb->r_m = -1;
}
void wb_done(struct wb *wb)
{
sbuf_done(&wb->sbuf);
}
/* update wb->st and wb->sb */
static void wb_stsb(struct wb *wb)
{
wb->st = MIN(wb->st, wb->v - SC_HT);
wb->sb = MAX(wb->sb, wb->v);
}
/* append font and size to the buffer if needed */
static void wb_font(struct wb *wb)
{
if (wb->f != R_F(wb)) {
sbuf_printf(&wb->sbuf, "%cf(%02d", c_ec, R_F(wb));
wb->f = R_F(wb);
}
if (wb->s != R_S(wb)) {
sbuf_printf(&wb->sbuf, "%cs(%02d", c_ec, R_S(wb));
wb->s = R_S(wb);
}
if (!n_cp && wb->m != R_M(wb)) {
sbuf_printf(&wb->sbuf, "%cm[%s]", c_ec, clr_str(R_M(wb)));
wb->m = R_M(wb);
}
wb_stsb(wb);
}
void wb_hmov(struct wb *wb, int n)
{
wb->h += n;
sbuf_printf(&wb->sbuf, "%ch'%du'", c_ec, n);
}
void wb_vmov(struct wb *wb, int n)
{
wb->v += n;
sbuf_printf(&wb->sbuf, "%cv'%du'", c_ec, n);
}
void wb_els(struct wb *wb, int els)
{
if (els > wb->els_pos)
wb->els_pos = els;
if (els < wb->els_neg)
wb->els_neg = els;
sbuf_printf(&wb->sbuf, "%cx'%du'", c_ec, els);
}
void wb_etc(struct wb *wb, char *x)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cX%s", c_ec, x);
}
/* make sure nothing is appended to wb after the last wb_put() */
static void wb_prevcheck(struct wb *wb)
{
if (wb->prev_ll != sbuf_len(&wb->sbuf))
wb->prev_n = 0;
}
/* mark wb->prev_c[] as valid */
static void wb_prevok(struct wb *wb)
{
wb->prev_ll = sbuf_len(&wb->sbuf);
}
/* append c to wb->prev_c[] */
static void wb_prevput(struct wb *wb, char *c, int ll)
{
if (wb->prev_n == LEN(wb->prev_c))
wb->prev_n--;
memmove(wb->prev_l + 1, wb->prev_l, wb->prev_n * sizeof(wb->prev_l[0]));
memmove(wb->prev_h + 1, wb->prev_h, wb->prev_n * sizeof(wb->prev_h[0]));
memmove(wb->prev_c + 1, wb->prev_c, wb->prev_n * sizeof(wb->prev_c[0]));
wb->prev_l[0] = ll;
wb->prev_h[0] = wb->h;
strcpy(wb->prev_c[0], c);
wb->prev_n++;
wb_prevok(wb);
}
/* strip the last i characters from wb */
static void wb_prevpop(struct wb *wb, int i)
{
int n = wb->prev_n - i;
sbuf_cut(&wb->sbuf, wb->prev_l[i - 1]);
wb->h = wb->prev_h[i - 1];
memmove(wb->prev_l, wb->prev_l + i, n * sizeof(wb->prev_l[0]));
memmove(wb->prev_h, wb->prev_h + i, n * sizeof(wb->prev_h[0]));
memmove(wb->prev_c, wb->prev_c + i, n * sizeof(wb->prev_c[0]));
wb->prev_n = n;
wb->prev_ll = sbuf_len(&wb->sbuf);
}
/* return the i-th last character inserted via wb_put() */
static char *wb_prev(struct wb *wb, int i)
{
wb_prevcheck(wb);
return i < wb->prev_n ? wb->prev_c[i] : NULL;
}
void wb_put(struct wb *wb, char *c)
{
struct glyph *g;
int ll;
if (c[0] == '\n') {
wb->part = 0;
return;
}
if (c[0] == ' ') {
wb_hmov(wb, spacewid(R_F(wb), R_S(wb)));
return;
}
if (c[0] == '\t' || c[0] == '' ||
(c[0] == c_ni && (c[1] == '\t' || c[1] == ''))) {
sbuf_append(&wb->sbuf, c);
return;
}
g = dev_glyph(c, R_F(wb));
wb_font(wb);
wb_prevcheck(wb); /* make sure wb->prev_c[] is valid */
ll = sbuf_len(&wb->sbuf); /* sbuf length before inserting c */
if (!c[1] || c[0] == c_ec || c[0] == c_ni ||
utf8len((unsigned char) c[0]) == strlen(c)) {
if (c[0] == c_ni && c[1] == c_ec)
sbuf_printf(&wb->sbuf, "%c%c", c_ec, c_ec);
else
sbuf_append(&wb->sbuf, c);
} else {
if (c[1] && !c[2])
sbuf_printf(&wb->sbuf, "%c(%s", c_ec, c);
else
sbuf_printf(&wb->sbuf, "%cC'%s'", c_ec, c);
}
if (strcmp(c_hc, c)) {
wb_prevput(wb, c, ll);
wb->h += charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
wb->ct |= g ? g->type : 0;
wb_stsb(wb);
}
}
/* return zero if c formed a ligature with its previous character */
int wb_lig(struct wb *wb, char *c)
{
char lig[GNLEN] = "";
char *cs[LIGLEN + 2];
int i = -1;
int ligpos;
cs[0] = c;
while (wb_prev(wb, ++i))
cs[i + 1] = wb_prev(wb, i);
ligpos = font_lig(dev_font(R_F(wb)), cs, i + 1);
if (ligpos > 1) {
for (i = 0; i < ligpos - 1; i++)
strcat(lig, wb_prev(wb, ligpos - i - 2));
strcat(lig, c);
wb_prevpop(wb, ligpos - 1);
wb_put(wb, lig);
return 0;
}
return 1;
}
/* return 0 if pairwise kerning was done */
int wb_kern(struct wb *wb, char *c)
{
int val;
if (!wb_prev(wb, 0))
return 1;
val = font_kern(dev_font(R_F(wb)), wb_prev(wb, 0), c);
if (val)
wb_hmov(wb, charwid(R_F(wb), R_S(wb), val));
wb_prevok(wb); /* kerning should not prevent ligatures */
return !val;
}
int wb_part(struct wb *wb)
{
return wb->part;
}
void wb_setpart(struct wb *wb)
{
wb->part = 1;
}
void wb_drawl(struct wb *wb, int c, int h, int v)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
wb->h += h;
wb->v += v;
wb_stsb(wb);
}
void wb_drawc(struct wb *wb, int c, int r)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cD'%c %du'", c_ec, c, r);
wb->h += r;
}
void wb_drawe(struct wb *wb, int c, int h, int v)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cD'%c %du %du'", c_ec, c, h, v);
wb->h += h;
}
void wb_drawa(struct wb *wb, int c, int h1, int v1, int h2, int v2)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cD'%c %du %du %du %du'", c_ec, c, h1, v1, h2, v2);
wb->h += h1 + h2;
wb->v += v1 + v2;
wb_stsb(wb);
}
void wb_drawxbeg(struct wb *wb, int c)
{
wb_font(wb);
sbuf_printf(&wb->sbuf, "%cD'%c", c_ec, c);
}
void wb_drawxdot(struct wb *wb, int h, int v)
{
sbuf_printf(&wb->sbuf, " %du %du", h, v);
wb->h += h;
wb->v += v;
wb_stsb(wb);
}
void wb_drawxend(struct wb *wb)
{
sbuf_printf(&wb->sbuf, "'");
}
static void wb_reset(struct wb *wb)
{
wb_done(wb);
wb_init(wb);
}
static void wb_putc(struct wb *wb, int t, char *s)
{
switch (t) {
case 0:
case 'C':
wb_put(wb, s);
break;
case 'D':
ren_dcmd(wb, s);
break;
case 'f':
wb->r_f = atoi(s);
break;
case 'h':
wb_hmov(wb, atoi(s));
break;
case 'm':
wb->r_m = clr_get(s);
break;
case 's':
wb->r_s = atoi(s);
break;
case 'v':
wb_vmov(wb, atoi(s));
break;
case 'x':
wb_els(wb, atoi(s));
break;
case 'X':
wb_etc(wb, s);
break;
}
}
void wb_cat(struct wb *wb, struct wb *src)
{
char *s = sbuf_buf(&src->sbuf);
char d[ILNLEN];
int c, part;
while ((c = escread(&s, d)) >= 0)
wb_putc(wb, c, d);
part = src->part;
wb->r_s = -1;
wb->r_f = -1;
wb->r_m = -1;
wb_reset(src);
src->part = part;
}
int wb_wid(struct wb *wb)
{
return wb->h;
}
int wb_empty(struct wb *wb)
{
return sbuf_empty(&wb->sbuf);
}
/* return 1 if wb ends a sentence (.?!) */
int wb_eos(struct wb *wb)
{
int i = 0;
while (wb_prev(wb, i) && strchr("'\")]*", wb_prev(wb, i)[0]))
i++;
return wb_prev(wb, i) && strchr(".?!", wb_prev(wb, i)[0]);
}
void wb_wconf(struct wb *wb, int *ct, int *st, int *sb)
{
*ct = wb->ct;
*st = -wb->st;
*sb = -wb->sb;
}
/* skip troff requests; return 1 if read c_hc */
static int skipreqs(char **s, struct wb *w1)
{
char d[ILNLEN];
char *r = *s;
int c;
wb_reset(w1);
while ((c = escread(s, d)) > 0) {
wb_putc(w1, c, d);
r = *s;
}
if (c < 0 || !strcmp(c_hc, d))
return 1;
*s = r;
return 0;
}
static char *dashpos(char *s, int w, struct wb *w1, int flg)
{
char d[ILNLEN];
char *r = NULL;
int c;
skipreqs(&s, w1);
while ((c = escread(&s, d)) >= 0) {
wb_putc(w1, c, d);
if (wb_wid(w1) > w && (!(flg & HY_ANY) || r))
continue;
if (!c && (!strcmp("-", d) || (!strcmp("em", d) || !strcmp("hy", d))))
r = s;
}
return r;
}
static int wb_dashwid(struct wb *wb)
{
struct glyph *g = dev_glyph("hy", R_F(wb));
return charwid(R_F(wb), R_S(wb), g ? g->wid : SC_DW);
}
static char *indicatorpos(char *s, int w, struct wb *w1, int flg)
{
char d[ILNLEN];
char *r = NULL;
int c;
skipreqs(&s, w1);
while ((c = escread(&s, d)) >= 0) {
wb_putc(w1, c, d);
if (wb_wid(w1) + wb_dashwid(w1) > w && (!(flg & HY_ANY) || r))
continue;
if (!c && !strcmp(c_hc, d))
r = s;
}
return r;
}
static char *hyphpos(char *s, int w, struct wb *w1, int flg)
{
char *map[ILNLEN] = {NULL}; /* mapping from word to s */
int fits[ILNLEN] = {0}; /* fits[i] if word[0..i]- fits w */
char word[ILNLEN];
char hyph[ILNLEN];
char d[ILNLEN];
char *prev_s = s;
char *r = NULL;
char *wp = word, *we = word + sizeof(word);
int beg, end;
int i, c;
skipreqs(&s, w1);
while ((c = escread(&s, d)) >= 0 && wp + strlen(d) + 1 < we) {
wb_putc(w1, c, d);
if (c == 0) {
strcpy(wp, d);
map[wp - word] = prev_s;
wp = strchr(wp, '\0');
fits[wp - word] = wb_wid(w1) + wb_dashwid(w1) <= w;
}
prev_s = s;
}
if (strlen(word) < 4)
return NULL;
hyphenate(hyph, word);
beg = flg & HY_FIRST2 ? 3 : 2;
end = strlen(word) - (flg & HY_FINAL2 ? 2 : 1);
for (i = beg; i < end; i++)
if (map[i] && hyph[i] && (fits[i] || ((flg & HY_ANY) && !r)))
r = map[i];
return r;
}
static void dohyph(char *s, char *pos, int dash, struct wb *w1, struct wb *w2)
{
char d[ILNLEN];
int c = -1;
wb_reset(w1);
wb_reset(w2);
while (s != pos && (c = escread(&s, d)) >= 0)
wb_putc(w1, c, d);
if (dash)
wb_putc(w1, 0, "hy");
w2->r_s = w1->r_s;
w2->r_f = w1->r_f;
w2->r_m = w1->r_m;
while ((c = escread(&s, d)) >= 0)
wb_putc(w2, c, d);
}
/* hyphenate wb into w1 and w2; return zero on success */
int wb_hyph(struct wb *wb, int w, struct wb *w1, struct wb *w2, int flg)
{
char *s = sbuf_buf(&wb->sbuf);
char *dp, *hp, *p;
if (skipreqs(&s, w1))
return 1;
dp = dashpos(sbuf_buf(&wb->sbuf), w, w1, flg);
hp = indicatorpos(sbuf_buf(&wb->sbuf), w, w1, flg);
if (hp && dp)
p = flg & HY_ANY ? MIN(dp, hp) : MAX(dp, hp);
else
p = dp ? dp : hp;
if (!p && flg & HY_MASK)
p = hyphpos(sbuf_buf(&wb->sbuf), w, w1, flg);
if (p)
dohyph(sbuf_buf(&wb->sbuf), p, p != dp, w1, w2);
return !p;
}