rizin/librz/cons/canvas.c
Anton Kochkov 74bf3d8902
Various memory leaks (#1902)
* Fix CID 358531
* Fix CID 322803
* Fix CID 322786
* Fix CID 322126
* Fix CID 316868
* Fix CID 316734
* Fix CID 316713
* Fix CID 316522
* Fix CID 316490
* Fix CID 316468
* Fix CID 316361
2021-10-29 17:18:16 +08:00

576 lines
12 KiB
C

// SPDX-FileCopyrightText: 2013-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <math.h>
#include <rz_cons.h>
#include <rz_util/rz_assert.h>
#define USE_UTF8 (rz_cons_singleton()->use_utf8)
#define USE_UTF8_CURVY (rz_cons_singleton()->use_utf8_curvy)
#define W(y) rz_cons_canvas_write(c, y)
#define G(x, y) rz_cons_canvas_gotoxy(c, x, y)
static inline bool __isAnsiSequence(const char *s) {
return s && s[0] == 033 && s[1] == '[';
}
static int __getAnsiPiece(const char *p, char *chr) {
const char *q = p;
if (!p) {
return 0;
}
while (p && *p && *p != '\n' && !__isAnsiSequence(p)) {
p++;
}
if (chr) {
*chr = *p;
}
return p - q;
}
static void attribute_free_kv(HtUPKv *kv) {
free(kv->value);
}
static const char *__attributeAt(RzConsCanvas *c, int loc) {
if (!c->color) {
return NULL;
}
return ht_up_find(c->attrs, loc, NULL);
}
static void __stampAttribute(RzConsCanvas *c, int loc, int length) {
if (!c->color) {
return;
}
int i;
ht_up_update(c->attrs, loc, (void *)c->attr);
for (i = 1; i < length; i++) {
ht_up_delete(c->attrs, loc + i);
}
}
/* check for ANSI sequences and use them as attr */
static const char *set_attr(RzConsCanvas *c, const char *s) {
if (!c || !s) {
return NULL;
}
const char *p = s;
while (__isAnsiSequence(p)) {
p += 2;
while (*p && *p != 'J' && *p != 'm' && *p != 'H') {
p++;
}
p++;
}
const int slen = p - s;
if (slen > 0) {
RzStrBuf tmp;
rz_strbuf_init(&tmp);
rz_strbuf_append_n(&tmp, s, slen);
c->attr = rz_str_constpool_get(&c->constpool, rz_strbuf_get(&tmp));
rz_strbuf_fini(&tmp);
}
return p;
}
static int __getUtf8Length(const char *s, int n) {
int i = 0, j = 0, fullwidths = 0;
while (s[i] && n > 0) {
if ((s[i] & 0xc0) != 0x80) {
j++;
if (rz_str_char_fullwidth(s + i, n)) {
fullwidths++;
}
}
n--;
i++;
}
return j + fullwidths;
}
static int __getUtf8Length2(const char *s, int n, int left) {
int i = 0, fullwidths = 0;
while (n > -1 && i < left && s[i]) {
if (rz_str_char_fullwidth(s + i, left - i)) {
fullwidths++;
}
if ((s[i] & 0xc0) != 0x80) {
n--;
}
i++;
}
i -= fullwidths;
return n == -1 ? i - 1 : i;
}
static bool __expandLine(RzConsCanvas *c, int real_len, int utf8_len) {
if (real_len == 0) {
return true;
}
int buf_utf8_len = __getUtf8Length2(c->b[c->y] + c->x, utf8_len, c->blen[c->y] - c->x);
int goback = RZ_MAX(0, (buf_utf8_len - utf8_len));
int padding = (real_len - utf8_len) - goback;
if (padding) {
if (padding > 0 && c->blen[c->y] + padding > c->bsize[c->y]) {
int newsize = RZ_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) {
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 = RZ_MAX(c->blen[c->y] - c->x - goback, 0);
char *start = c->b[c->y] + c->x + goback;
char *tmp = malloc(size);
if (!tmp) {
return false;
}
memcpy(tmp, start, size);
if (padding < 0) {
int lap = RZ_MAX(0, c->b[c->y] - (start + padding));
memcpy(start + padding + lap, tmp + lap, size - lap);
free(tmp);
c->blen[c->y] += padding;
return true;
}
memcpy(start + padding, tmp, size);
free(tmp);
c->blen[c->y] += padding;
}
return true;
}
RZ_API void rz_cons_canvas_free(RzConsCanvas *c) {
if (!c) {
return;
}
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);
ht_up_free(c->attrs);
rz_str_constpool_fini(&c->constpool);
free(c);
}
static bool attribute_delete_cb(void *user, const ut64 key, const void *value) {
HtUP *ht = (HtUP *)user;
ht_up_delete(ht, key);
return true;
}
RZ_API void rz_cons_canvas_clear(RzConsCanvas *c) {
rz_return_if_fail(c && c->b);
int y;
for (y = 0; y < c->h; y++) {
memset(c->b[y], '\n', c->bsize[y]);
}
ht_up_foreach(c->attrs, attribute_delete_cb, c->attrs);
}
RZ_API bool rz_cons_canvas_gotoxy(RzConsCanvas *c, int x, int y) {
bool ret = true;
if (!c) {
return 0;
}
y += c->sy;
x += c->sx;
if (y > c->h * 2) {
return false;
}
if (y >= c->h) {
y = c->h - 1;
ret = false;
}
if (y < 0) {
y = 0;
ret = false;
}
if (x < 0) {
// c->x = 0;
ret = false;
}
if (x > c->blen[y] * 2) {
return false;
}
if (x >= c->blen[y]) {
c->x = c->blen[y];
ret = false;
}
if (x < c->blen[y] && x >= 0) {
c->x = x;
}
if (y < c->h) {
c->y = y;
}
return ret;
}
RZ_API RzConsCanvas *rz_cons_canvas_new(int w, int h) {
if (w < 1 || h < 1) {
return NULL;
}
RzConsCanvas *c = RZ_NEW0(RzConsCanvas);
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;
if (!rz_str_constpool_init(&c->constpool)) {
goto beach;
}
c->attrs = ht_up_new((HtUPDupValue)strdup, attribute_free_kv, NULL);
if (!c->attrs) {
goto beach;
}
c->attr = Color_RESET;
rz_cons_canvas_clear(c);
return c;
beach:
rz_str_constpool_fini(&c->constpool);
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;
}
RZ_API void rz_cons_canvas_write(RzConsCanvas *c, const char *s) {
if (!c || !s || !*s || !RZ_BETWEEN(0, c->y, c->h - 1) || !RZ_BETWEEN(0, c->x, c->w - 1)) {
return;
}
char ch;
int left, slen, attr_len, piece_len;
int orig_x = c->x, attr_x = c->x;
c->x = __getUtf8Length2(c->b[c->y], c->x, c->blen[c->y]);
/* split the string into pieces of non-ANSI chars and print them normally,
** using the ANSI chars to set the attr of the canvas */
rz_cons_break_push(NULL, NULL);
do {
const char *s_part = set_attr(c, s);
ch = 0;
piece_len = __getAnsiPiece(s_part, &ch);
if (piece_len == 0 && ch == '\0' && s_part == s) {
break;
}
left = c->blen[c->y] - c->x;
slen = piece_len;
if (piece_len > left) {
int utf8_piece_len = __getUtf8Length(s_part, piece_len);
if (utf8_piece_len > c->w - attr_x) {
slen = left;
}
}
int real_len = rz_str_nlen(s_part, slen);
int utf8_len = __getUtf8Length(s_part, slen);
if (!__expandLine(c, real_len, utf8_len)) {
break;
}
if (G(c->x - c->sx, c->y - c->sy)) {
memcpy(c->b[c->y] + c->x, s_part, slen);
}
attr_len = slen <= 0 && s_part != s ? 1 : utf8_len;
if (attr_len > 0 && attr_x < c->blen[c->y]) {
__stampAttribute(c, c->y * c->w + attr_x, attr_len);
}
s = s_part;
if (ch == '\n') {
c->attr = Color_RESET;
__stampAttribute(c, c->y * c->w + attr_x, 0);
c->y++;
s++;
if (*s == '\0' || c->y >= c->h) {
break;
}
c->x = __getUtf8Length2(c->b[c->y], orig_x, c->blen[c->y]);
attr_x = orig_x;
} else {
c->x += slen;
attr_x += utf8_len;
}
s += piece_len;
} while (*s && !rz_cons_is_breaked());
rz_cons_break_pop();
c->x = orig_x;
}
RZ_API RZ_OWN char *rz_cons_canvas_to_string(RzConsCanvas *c) {
rz_return_val_if_fail(c, NULL);
int x, y, olen = 0, attr_x = 0;
bool is_first = true;
for (y = 0; y < c->h; y++) {
olen += c->blen[y] + 1;
}
char *o = calloc(1, olen * 4 * CONS_MAX_ATTR_SZ);
if (!o) {
return NULL;
}
if (!olen) {
free(o);
return NULL;
}
olen = 0;
for (y = 0; y < c->h; y++) {
if (!is_first) {
o[olen++] = '\n';
}
is_first = false;
attr_x = 0;
for (x = 0; x < c->blen[y]; x++) {
if ((c->b[y][x] & 0xc0) != 0x80) {
const char *atr = __attributeAt(c, y * c->w + attr_x);
if (atr) {
size_t len = strlen(atr);
memcpy(o + olen, atr, len);
olen += len;
}
attr_x++;
if (rz_str_char_fullwidth(c->b[y] + x, c->blen[y] - x)) {
attr_x++;
}
}
if (!c->b[y][x] || c->b[y][x] == '\n') {
o[olen++] = ' ';
continue;
}
const char *rune = rz_cons_get_rune((const ut8)c->b[y][x]);
if (rune) {
size_t rune_len = strlen(rune);
memcpy(o + olen, rune, rune_len + 1);
olen += rune_len;
} else {
o[olen++] = c->b[y][x];
}
}
while (olen > 0 && o[olen - 1] == ' ') {
o[--olen] = '\0';
}
}
o[olen] = '\0';
return o;
}
RZ_API void rz_cons_canvas_print_region(RzConsCanvas *c) {
char *o = rz_cons_canvas_to_string(c);
if (RZ_STR_ISEMPTY(o)) {
free(o);
return;
}
rz_str_trim_tail(o);
if (RZ_STR_ISNOTEMPTY(o)) {
rz_cons_strcat(o);
}
free(o);
}
RZ_API void rz_cons_canvas_print(RzConsCanvas *c) {
char *o = rz_cons_canvas_to_string(c);
if (!o) {
return;
}
rz_cons_strcat(o);
free(o);
}
RZ_API int rz_cons_canvas_resize(RzConsCanvas *c, int w, int h) {
if (!c || w < 0 || h <= 0) {
return false;
}
int *newblen = realloc(c->blen, sizeof *c->blen * h);
if (!newblen) {
rz_cons_canvas_free(c);
return false;
}
c->blen = newblen;
int *newbsize = realloc(c->bsize, sizeof *c->bsize * h);
if (!newbsize) {
rz_cons_canvas_free(c);
return false;
}
c->bsize = newbsize;
char **newb = realloc(c->b, sizeof *c->b * h);
if (!newb) {
rz_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) {
size_t j;
for (j = 0; j <= i; j++) {
free(c->b[i]);
}
ht_up_free(c->attrs);
free(c->blen);
free(c->bsize);
free(c->b);
free(c);
return false;
}
c->b[i] = newline;
}
c->w = w;
c->h = h;
c->x = 0;
c->y = 0;
rz_cons_canvas_clear(c);
return true;
}
RZ_API void rz_cons_canvas_box(RzConsCanvas *c, int x, int y, int w, int h, const char *color) {
rz_return_if_fail(c && w && h);
if (color) {
c->attr = color;
}
if (!c->color) {
c->attr = Color_RESET;
}
char *row = malloc(w + 1);
if (!row) {
return;
}
const char *hline = USE_UTF8 ? RUNECODESTR_LINE_HORIZ : "-";
const char *vtmp = USE_UTF8 ? RUNECODESTR_LINE_VERT : "|";
const char *tl_corner = USE_UTF8 ? (USE_UTF8_CURVY ? RUNECODESTR_CURVE_CORNER_TL : RUNECODESTR_CORNER_TL) : ".";
const char *tr_corner = USE_UTF8 ? (USE_UTF8_CURVY ? RUNECODESTR_CURVE_CORNER_TR : RUNECODESTR_CORNER_TR) : ".";
const char *bl_corner = USE_UTF8 ? (USE_UTF8_CURVY ? RUNECODESTR_CURVE_CORNER_BL : RUNECODESTR_CORNER_BL) : "`";
const char *br_corner = USE_UTF8 ? (USE_UTF8_CURVY ? RUNECODESTR_CURVE_CORNER_BR : RUNECODESTR_CORNER_BR) : "'";
int i, x_mod;
int roundcorners = 0;
char *row_ptr;
RzStrBuf *vline = rz_strbuf_new(NULL);
rz_strbuf_appendf(vline, Color_RESET "%s%s", color, vtmp);
row[0] = roundcorners ? '.' : tl_corner[0];
if (w > 2) {
memset(row + 1, hline[0], w - 2);
}
if (w > 1) {
row[w - 1] = roundcorners ? '.' : tr_corner[0];
}
row[w] = 0;
row_ptr = row;
x_mod = x;
if (x < -c->sx) {
x_mod = RZ_MIN(-c->sx, x_mod + w);
row_ptr += x_mod - x;
}
if (G(x_mod, y)) {
W(row_ptr);
}
if (G(x_mod, y + h - 1)) {
row[0] = roundcorners ? '\'' : bl_corner[0];
row[w - 1] = roundcorners ? '\'' : br_corner[0];
W(row_ptr);
}
for (i = 1; i < h - 1; i++) {
if (G(x, y + i)) {
W(rz_strbuf_get(vline));
}
if (G(x + w - 1, y + i)) {
W(rz_strbuf_get(vline));
}
}
free(row);
rz_strbuf_free(vline);
if (color) {
c->attr = Color_RESET;
}
}
RZ_API void rz_cons_canvas_fill(RzConsCanvas *c, int x, int y, int w, int h, char ch) {
int i;
if (w < 0) {
return;
}
char *row = malloc(w + 1);
if (!row) {
return;
}
memset(row, ch, w);
row[w] = 0;
for (i = 0; i < h; i++) {
if (G(x, y + i)) {
W(row);
}
}
free(row);
}
RZ_API void rz_cons_canvas_line(RzConsCanvas *c, int x, int y, int x2, int y2, RzCanvasLineStyle *style) {
if (c->linemode) {
rz_cons_canvas_line_square(c, x, y, x2, y2, style);
} else {
rz_cons_canvas_line_diagonal(c, x, y, x2, y2, style);
}
}