UTF-8 support in canvas (panels, graph) (#10319)

This commit is contained in:
Luca Di Bartolomeo 2018-06-12 13:05:47 +02:00 committed by radare
parent f08603c6c7
commit 83e85ee07f
3 changed files with 280 additions and 174 deletions

View File

@ -10,157 +10,54 @@
R_API void r_cons_canvas_free(RConsCanvas *c) {
if (c) {
free (c->b);
if (c->b) {
int y;
for (y = 0; y < c->h; y++) {
free (c->b[y]);
}
free (c->b);
}
free (c->bsize);
free (c->blen);
free (c->attrs);
free (c);
}
}
R_API void r_cons_canvas_clear(RConsCanvas *c) {
int y;
if (c && c->b) {
memset (c->b, '\n', c->blen);
c->b[c->blen] = 0;
int y;
for (y = 0; y < c->h; y++) {
c->b[y * c->w] = '\n';
memset (c->b[y], '\n', c->bsize[y]);
}
/*//XXX tofix*/
if (c->attrs) {
c->attrslen = 0;
memset (c->attrs, 0, sizeof (*c->attrs) * c->blen);
memset (c->attrs, 0, sizeof (*c->attrs) * (c->w + 1) * c->h);
}
}
}
R_API RConsCanvas *r_cons_canvas_new(int w, int h) {
if (w < 1 || h < 1) {
return NULL;
}
RConsCanvas *c = R_NEW0 (RConsCanvas);
if (!c) {
return NULL;
}
c->color = 0;
c->sx = 0;
c->sy = 0;
c->blen = (w + 1) * h;
c->b = malloc (c->blen + 1);
if (!c->b) {
free (c);
return NULL;
}
c->attrslen = 0;
c->attrs = calloc (sizeof (*c->attrs), c->blen + 1);
if (!c->attrs) {
free (c->b);
free (c);
return NULL;
}
c->attr = Color_RESET;
c->w = w;
c->h = h;
c->x = c->y = 0;
r_cons_canvas_clear (c);
return c;
}
R_API bool r_cons_canvas_gotoxy(RConsCanvas *c, int x, int y) {
bool ret = true;
if (!c) {
return 0;
}
x += c->sx;
y += c->sy;
if (x > c->w * 2) {
return false;
}
if (y > c->h * 2) {
return false;
}
if (x >= c->w) {
c->x = c->w;
ret = false;
}
if (y >= c->h) {
c->y = c->h;
ret = false;
}
if (x < 0) {
//c->x = 0;
ret = false;
}
if (y < 0) {
c->y = 0;
ret = false;
}
if (x < c->w && x >= 0) {
c->x = x;
}
if (y < c->h && y >= 0) {
c->y = y;
}
return ret;
}
static bool is_ansi_seq(const char *s) {
#if 0
/* check utf8 length */
if (((*s & 0xc0) == 0x80)) {
return false;
}
#endif
static bool _is_ansi_seq(const char *s) {
return s && s[0] == 033 && s[1] == '[';
}
static int get_piece(const char *p, char *chr) {
static int _get_piece(const char *p, char *chr) {
const char *q = p;
if (!p) {
return 0;
}
while (p && *p && *p != '\n' && !is_ansi_seq (p)) {
while (p && *p && *p != '\n' && ! _is_ansi_seq (p)) {
p++;
}
// XXX: this is wrong because is_ansi_seq requires skipping until JmH
#if 0
while (p && *p && *p != '\n') {
if (is_ansi_seq (p)) {
p += 2;
while (*p && *p != 'J' && *p != 'm' && *p != 'H') {
p++;
}
p++;
}
}
#endif
if (chr) {
*chr = *p;
}
return p - q;
}
static char *prefixline(RConsCanvas *c, int *left) {
if (!c) {
return NULL;
}
int x, len;
char *p;
int b_len = c->w * c->h;
int yxw = c->y * c->w;
if (b_len < yxw) {
return NULL;
}
p = c->b + yxw;
len = b_len - yxw - 1;
for (x = 0; (p[x] && x < c->x) && x < len; x++) {
if (p[x] == '\n') {
p[x] = ' ';
}
}
if (left) {
*left = c->w - c->x;
}
return p + x;
}
//XXX todo
static const char **attr_at(RConsCanvas *c, int loc) {
int i, j, delta;
if (!c->color || c->attrslen == 0) {
@ -197,6 +94,7 @@ static const char **attr_at(RConsCanvas *c, int loc) {
return NULL;
}
//XXX todo
static void sort_attrs(RConsCanvas *c) {
int i, j;
RConsCanvasAttr value;
@ -209,10 +107,10 @@ static void sort_attrs(RConsCanvas *c) {
}
}
static void stamp_attr(RConsCanvas *c, int length) {
//XXX todo
static void stamp_attr(RConsCanvas *c, int loc, int length) {
int i;
const char **s;
const int loc = c->x + (c->y * c->w);
s = attr_at (c, loc);
if (s) {
@ -233,6 +131,7 @@ static void stamp_attr(RConsCanvas *c, int length) {
}
}
//XXX todo
/* check for ANSI sequences and use them as attr */
static const char *set_attr(RConsCanvas *c, const char *s) {
if (!c || !s) {
@ -240,7 +139,7 @@ static const char *set_attr(RConsCanvas *c, const char *s) {
}
const char *p = s;
while (is_ansi_seq (p)) {
while (_is_ansi_seq (p)) {
p += 2;
while (*p && *p != 'J' && *p != 'm' && *p != 'H') {
p++;
@ -261,47 +160,210 @@ static const char *set_attr(RConsCanvas *c, const char *s) {
return p;
}
R_API void r_cons_canvas_write(RConsCanvas *c, const char *s) {
char *p, ch;
int orig_x, x;
int left, slen, attr_len, piece_len;
R_API bool r_cons_canvas_gotoxy(RConsCanvas *c, int x, int y) {
bool ret = true;
if (!c) {
return 0;
}
y += c->sy;
x += c->sx;
if (x > c->blen[y] * 2) {
return false;
}
if (y > c->h * 2) {
return false;
}
if (x >= c->blen[y]) {
c->x = c->blen[y];
ret = false;
}
if (y >= c->h) {
c->y = c->h;
ret = false;
}
if (x < 0) {
//c->x = 0;
ret = false;
}
if (y < 0) {
c->y = 0;
ret = false;
}
if (x < c->blen[y] && x >= 0) {
c->x = x;
}
if (y < c->h && y >= 0) {
c->y = y;
}
return ret;
}
R_API RConsCanvas *r_cons_canvas_new(int w, int h) {
if (w < 1 || h < 1) {
return NULL;
}
RConsCanvas *c = R_NEW0 (RConsCanvas);
if (!c) {
return NULL;
}
c->bsize = NULL;
c->blen = NULL;
int i = 0;
c->color = 0;
c->sx = 0;
c->sy = 0;
c->b = malloc (sizeof *c->b * h);
if (!c->b) {
goto beach;
}
c->blen = malloc (sizeof *c->blen * h);
if (!c->blen) {
goto beach;
}
c->bsize = malloc (sizeof *c->bsize * h);
if (!c->bsize) {
goto beach;
}
for (i = 0; i < h; i++) {
c->b[i] = malloc (w + 1);
c->blen[i] = w;
c->bsize[i] = w + 1;
if (!c->b[i]) {
goto beach;
}
}
c->w = w;
c->h = h;
c->x = c->y = 0;
c->attrslen = 0;
c->attrs = calloc (sizeof (*c->attrs), (c->w + 1) * c->h);
if (!c->attrs) {
goto beach;
}
c->attr = Color_RESET;
r_cons_canvas_clear (c);
return c;
beach: {
int j;
for (j = 0; j < i; j++) {
free (c->b[j]);
}
free (c->bsize);
free (c->blen);
free (c->b);
free (c);
return NULL;
}
}
static int utf8len_fixed(const char *s, int n) {
int i = 0, j = 0;
while (s[i] && n > 0) {
if ((s[i] & 0xc0) != 0x80) {
j++;
}
n--;
i++;
}
return j;
}
static int bytes_utf8len(const char *s, int n) {
int i = 0;
while (s[i] && n > 0) {
if ((s[i] & 0xc0) != 0x80) {
n--;
}
i++;
}
return i;
}
static int expand_line (RConsCanvas *c, int real_len, int utf8_len) {
int buf_utf8_len = bytes_utf8len (c->b[c->y] + c->x, utf8_len);
int goback = R_MAX (0, (buf_utf8_len - real_len));
int padding = (real_len - utf8_len) - goback;
if (padding) {
if (padding > 0 && c->blen[c->y] + padding > c->bsize[c->y]) {
int newsize = R_MAX (c->bsize[c->y] * 1.5, c->blen[c->y] + padding);
char * newline = realloc (c->b[c->y], sizeof (*c->b[c->y])*(newsize));
if (!newline) {
r_cons_canvas_free (c);
return false;
}
memset (newline + c->bsize[c->y], 0, newsize - c->bsize[c->y]);
c->b[c->y] = newline;
c->bsize[c->y] = newsize;
}
int size = c->blen[c->y] - c->x - utf8_len;
char *start = c->b[c->y] + c->x + utf8_len;
char *tmp = malloc (size);
if (!tmp) {
return false;
}
memcpy (tmp, start, size);
memcpy (start + padding, tmp, size);
free (tmp);
c->blen[c->y] += padding;
}
return true;
}
R_API void r_cons_canvas_write(RConsCanvas *c, const char *s) {
if (!c || !s || !*s) {
return;
}
char ch;
int left, slen, attr_len, piece_len;
int orig_x = c->x, attr_x = c->x;
int x_padding = c->x - utf8len_fixed (c->b[c->y], c->x);
c->x += x_padding;
/* split the string into pieces of non-ANSI chars and print them normally,
** using the ANSI chars to set the attr of the canvas */
orig_x = c->x;
r_cons_break_push (NULL, NULL);
do {
const char *s_part = set_attr (c, s);
ch = 0;
piece_len = get_piece (s_part, &ch);
piece_len = _get_piece (s_part, &ch);
if (piece_len == 0 && ch == '\0' && s_part == s) {
break;
}
left = 0;
p = prefixline (c, &left);
left = c->blen[c->y] - c->x;
slen = R_MIN (left, piece_len);
attr_len = slen <= 0 && s_part != s? 1: slen;
if (attr_len > 0) {
stamp_attr (c, attr_len);
if (attr_len > 0 && attr_x < c->blen[c->y]) {
stamp_attr (c, c->y*c->w + attr_x, attr_len);
}
// XXX this is a bug if we scroll in the middle of \033
x = c->x - c->sx;
if (G (x, c->y - c->sy)) {
memcpy (p, s_part, slen);
int real_len = r_str_nlen (s_part, slen);
int utf8_len = utf8len_fixed (s_part, slen);
if (!expand_line (c, real_len, utf8_len)) {
return;
}
if (G (c->x - c->sx, c->y - c->sy)) {
memcpy (c->b[c->y] + c->x, s_part, slen);
}
s = s_part;
if (ch == '\n') {
c->y++;
c->x = orig_x;
s++;
if (*s == '\0') {
if (*s == '\0' || c->y >= c->h) {
break;
}
x_padding = utf8len_fixed (c->b[c->y], orig_x);
c->x = 2*orig_x - x_padding;
attr_x = orig_x;
} else {
c->x += slen;
attr_x += utf8_len;
}
s += piece_len;
} while (*s && !r_cons_is_breaked ());
@ -310,43 +372,55 @@ R_API void r_cons_canvas_write(RConsCanvas *c, const char *s) {
}
R_API char *r_cons_canvas_to_string(RConsCanvas *c) {
int x, y, olen = 0;
int x, y, olen = 0, attr_x = 0;
char *o;
const char *b;
const char **atr;
int is_first = true;
if (!c) {
return NULL;
}
b = c->b;
o = calloc (1, (c->w * (c->h + 1)) * (CONS_MAX_ATTR_SZ));
for (y = 0; y < c->h; y++) {
olen += c->blen[y] + 1;
}
o = calloc (1, olen * (CONS_MAX_ATTR_SZ));
if (!o) {
return NULL;
}
olen = 0;
for (y = 0; y < c->h; y++) {
if (!is_first) {
o[olen++] = '\n';
}
is_first = false;
for (x = 0; x < c->w; x++) {
const int p = x + (y * c->w);
atr = attr_at (c, p);
if (atr && *atr) {
strcat (o, *atr);
olen += strlen (*atr);
attr_x = 0;
for (x = 0; x < c->blen[y]; x++) {
if ((c->b[y][x] & 0xc0) != 0x80) {
atr = attr_at (c, y*c->w + attr_x);
if (atr && *atr) {
int len = strlen (*atr);
memcpy (o + olen, *atr, len);
olen += len;
}
attr_x++;
}
if (!b[p] || b[p] == '\n') {
break;
if (!c->b[y][x] || c->b[y][x] == '\n') {
o[olen++] = ' ';
continue;
}
const char *rune = r_cons_get_rune ((const ut8)b[p]);
const char *rune = r_cons_get_rune ((const ut8)c->b[y][x]);
if (rune) {
strcpy (o + olen, rune);
olen += strlen (rune);
} else {
o[olen++] = b[p];
o[olen++] = c->b[y][x];
}
}
while (o[olen - 1] == ' ') {
o[--olen] = '\0';
}
}
o[olen] = '\0';
return o;
@ -372,26 +446,59 @@ R_API void r_cons_canvas_print(RConsCanvas *c) {
}
R_API int r_cons_canvas_resize(RConsCanvas *c, int w, int h) {
void *newbuf = NULL;
const int blen = (w + 1) * h;
char *b = NULL;
if (!c || w < 0) {
return false;
}
b = realloc (c->b, blen + 1);
if (!b) {
void *newattrs = NULL;
int *newblen = realloc (c->blen, sizeof *c->blen * h);
if (!newblen) {
r_cons_canvas_free (c);
return false;
}
c->b = b;
newbuf = realloc (c->attrs, sizeof (*c->attrs) * blen + 1);
if (!newbuf) {
free (c->b);
free (c->attrs);
c->blen = newblen;
int *newbsize = realloc (c->bsize, sizeof *c->bsize * h);
if (!newbsize) {
r_cons_canvas_free (c);
return false;
}
c->attrs = newbuf;
c->blen = blen;
c->b = b;
c->bsize = newbsize;
char **newb = realloc (c->b, sizeof *c->b * h);
if (!newb) {
r_cons_canvas_free (c);
return false;
}
c->b = newb;
int i;
char *newline = NULL;
for (i = 0; i < h; i++) {
if (i < c->h) {
newline = realloc (c->b[i], sizeof *c->b[i] * (w + 1));
} else {
newline = malloc ((w + 1));
}
c->blen[i] = w;
c->bsize[i] = w + 1;
if (!newline) {
int j;
for (j = 0; j <= i; j++) {
free (c->b[i]);
}
free (c->bsize);
free (c->attrs);
free (c->blen);
free (c->bsize);
free (c->b);
free (c);
return false;
}
c->b[i] = newline;
}
newattrs = realloc (c->attrs, sizeof (*c->attrs) * (w + 1) * h);
if (!newattrs) {
r_cons_canvas_free (c);
return false;
}
c->attrs = newattrs;
c->w = w;
c->h = h;
c->x = 0;

View File

@ -33,7 +33,7 @@
#define PANEL_CMD_STACK "px 256@r:SP"
#define PANEL_CMD_REGISTERS "dr="
#define PANEL_CMD_REGISTERREFS "drr"
#define PANEL_CMD_DISASSEMBLY "pd $r @e:scr.utf8=0"
#define PANEL_CMD_DISASSEMBLY "pd $r"
static const int layoutMaxCount = 2;
@ -1034,9 +1034,7 @@ R_API void r_panels_free(RPanels *panels) {
}
free (panels->panel);
if (panels->can) {
free (panels->can->b);
free (panels->can->attrs);
free (panels->can);
r_cons_canvas_free (panels->can);
}
free (panels);
}
@ -1067,12 +1065,12 @@ R_API int r_core_visual_panels(RCore *core, RPanels *panels) {
core->print->cur_enabled = false;
core->print->col = 0;
have_utf8 = r_config_get_i (core->config, "scr.utf8");
r_config_set_i (core->config, "asm.comments", 0);
r_config_set_i (core->config, "asm.bytes", 1);
r_config_set_i (core->config, "scr.utf8", 1);
asm_comments = r_config_get_i (core->config, "asm.comments");
asm_bytes = r_config_get_i (core->config, "asm.bytes");
have_utf8 = r_config_get_i (core->config, "scr.utf8");
repeat:
core->panels = panels;

View File

@ -305,8 +305,9 @@ typedef struct r_cons_canvas_t {
int h;
int x;
int y;
char *b;
int blen;
char **b;
int *blen;
int *bsize;
const char * attr;//The current attr (inserted on each write)
RConsCanvasAttr * attrs;// all the different attributes
int attrslen;