Initial implementation of the RTable API with filter, sorting and query APIs ##util

This commit is contained in:
radare 2019-08-25 22:43:34 +02:00 committed by GitHub
parent e4bb767d54
commit ff36c12ea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 511 additions and 12 deletions

View File

@ -47,6 +47,7 @@ int gettimeofday (struct timeval* p, void* tz);
#include "r_util/r_mem.h"
#include "r_util/r_name.h"
#include "r_util/r_num.h"
#include "r_util/r_table.h"
#include "r_util/r_graph.h"
#include "r_util/r_panels.h"
#include "r_util/r_pool.h"

View File

@ -21,6 +21,7 @@ R_API bool r_strbuf_setf(RStrBuf *sb, const char *fmt, ...);
R_API bool r_strbuf_vsetf(RStrBuf *sb, const char *fmt, va_list ap);
R_API bool r_strbuf_append(RStrBuf *sb, const char *s);
R_API bool r_strbuf_append_n(RStrBuf *sb, const char *s, int l);
R_API bool r_strbuf_prepend(RStrBuf *sb, const char *s);
R_API bool r_strbuf_appendf(RStrBuf *sb, const char *fmt, ...);
R_API bool r_strbuf_vappendf(RStrBuf *sb, const char *fmt, va_list ap);
R_API char *r_strbuf_get(RStrBuf *sb);

View File

@ -0,0 +1,61 @@
#ifndef R_UTIL_TABLE_H
#define R_UTIL_TABLE_H
#include <r_util.h>
typedef struct {
const char *name;
RListComparator cmp;
} RTableColumnType;
extern RTableColumnType r_table_type_string;
extern RTableColumnType r_table_type_number;
typedef struct {
char *name;
RTableColumnType *type;
int align; // left, right, center (TODO: unused)
int width; // computed
int maxWidth;
bool forceUppercase;
} RTableColumn;
typedef struct {
// TODO: use RVector
RList *items;
} RTableRow;
typedef struct {
RList *rows;
RList *cols;
int totalCols;
bool showHeader;
bool adjustedCols;
} RTable;
R_API void r_table_row_free(void *_row);
R_API void r_table_column_free(void *_col);
R_API RTable *r_table_new();
R_API void r_table_free(RTable *t);
R_API void r_table_add_column(RTable *t, RTableColumnType *type, const char *name, int maxWidth);
R_API RTableRow *r_table_row_new(RList *items);
R_API void r_table_add_row(RTable *t, const char *name, ...);
R_API char *r_table_tofancystring(RTable *t);
R_API char *r_table_tostring(RTable *t);
R_API char *r_table_tocsv(RTable *t);
R_API char *r_table_tojson(RTable *t);
R_API void r_table_filter(RTable *t, int nth, int op, const char *un);
R_API void r_table_sort(RTable *t, int nth, bool inc);
R_API void r_table_query(RTable *t, const char *q);
R_API RTable *r_table_clone(RTable *t);
R_API RTable *r_table_push(RTable *t);
R_API RTable *r_table_pop(RTable *t);
R_API void r_table_fromjson(RTable *t, const char *csv);
R_API void r_table_fromcsv(RTable *t, const char *csv);
R_API char *r_table_tohtml(RTable *t);
R_API void r_table_transpose(RTable *t);
R_API void r_table_format(RTable *t, int nth, RTableColumnType *type);
R_API ut64 r_table_reduce(RTable *t, int nth);
R_API void r_table_columns(RTable *t, const char *name, ...);
#endif

View File

@ -17,7 +17,7 @@ OBJS+=list.o flist.o chmod.o graph.o event.o alloc.o donut.o
OBJS+=regex/regcomp.o regex/regerror.o regex/regexec.o uleb128.o
OBJS+=sandbox.o calc.o thread.o thread_sem.o thread_lock.o thread_cond.o
OBJS+=strpool.o bitmap.o date.o format.o pie.o print.o ctype.o
OBJS+=seven.o randomart.o zip.o debruijn.o log.o getopt.o
OBJS+=seven.o randomart.o zip.o debruijn.o log.o getopt.o table.o
OBJS+=utf8.o utf16.o utf32.o strbuf.o lib.o name.o spaces.o signal.o syscmd.o
OBJS+=diff.o bdiff.o stack.o queue.o tree.o idpool.o assert.o
OBJS+=punycode.o pkcs7.o x509.o asn1.o astr.o json_indent.o skiplist.o pj.o

View File

@ -64,11 +64,9 @@ R_API int r_list_length(const RList *list) {
/* remove all elements of a list */
R_API void r_list_purge(RList *list) {
RListIter *it;
r_return_if_fail (list);
it = list->head;
RListIter *it = list->head;
while (it) {
RListIter *next = it->n;
r_list_delete (list, it);
@ -111,11 +109,9 @@ R_API void r_list_delete(RList *list, RListIter *iter) {
}
R_API void r_list_split(RList *list, void *ptr) {
RListIter *iter;
r_return_if_fail (list);
iter = r_list_iterator (list);
RListIter *iter = r_list_iterator (list);
while (iter) {
void *item = iter->data;
if (ptr == item) {
@ -217,11 +213,9 @@ R_API RListIter *r_list_append(RList *list, void *data) {
}
R_API RListIter *r_list_prepend(RList *list, void *data) {
RListIter *item;
r_return_val_if_fail (list, NULL);
item = R_NEW0 (RListIter);
RListIter *item = R_NEW0 (RListIter);
if (!item) {
return NULL;
}
@ -293,12 +287,11 @@ R_API void *r_list_pop(RList *list) {
R_API void *r_list_pop_head(RList *list) {
void *data = NULL;
RListIter *iter;
r_return_val_if_fail (list, NULL);
if (list->head) {
iter = list->head;
RListIter *iter = list->head;
if (list->head == list->tail) {
list->head = list->tail = NULL;
} else {

View File

@ -4,6 +4,7 @@ r_util_sources = [
'assert.c',
'alloc.c',
'donut.c',
'table.c',
'getopt.c',
'base85.c',
'base91.c',

View File

@ -108,6 +108,27 @@ done:
return ret;
}
R_API bool r_strbuf_prepend(RStrBuf *sb, const char *s) {
r_return_val_if_fail (sb && s, false);
int l = strlen (s);
// fast path if no chars to append
if (l == 0) {
return true;
}
int newlen = l + sb->len;
char *ns = malloc (newlen + 1);
if (ns) {
memcpy (ns, s, l);
char *s = sb->ptr ? sb->ptr: sb->buf;
memcpy (ns + l, s, sb->len);
free (sb->ptr);
sb->ptr = ns;
sb->len = newlen;
return true;
}
return false;
}
R_API bool r_strbuf_append(RStrBuf *sb, const char *s) {
r_return_val_if_fail (sb && s, false);

421
libr/util/table.c Normal file
View File

@ -0,0 +1,421 @@
/* radare - LGPL - Copyright 2019 - pancake */
#include <r_util/r_table.h>
// cant do that without globals because RList doesnt have void *user :(
static bool Ginc = false;
static int Gnth = 0;
static RListComparator Gcmp = NULL;
static int sortString(const void *a, const void *b) {
return strcmp (a, b);
}
static int sortNumber(const void *a, const void *b) {
return r_num_get (NULL, a) - r_num_get (NULL, b);
}
// maybe just index by name instead of exposing those symbols as global
R_API RTableColumnType r_table_type_string = { "string", sortString };
R_API RTableColumnType r_table_type_number = { "number", sortNumber };
// TODO: unused for now, maybe good to call after filter :?
static void __table_adjust(RTable *t) {
RListIter *iter, *iter2;
RTableColumn *col;
RTableRow *row;
r_list_foreach (t->cols, iter, col) {
col->width = 0;
}
r_list_foreach (t->rows, iter, row) {
const char *item;
int ncol = 0;
r_list_foreach (row->items, iter2, item) {
int itemLength = r_str_len_utf8 (item);
RTableColumn *c = r_list_get_n (t->cols, ncol);
if (c) {
c->width = R_MAX (c->width, itemLength);
}
ncol ++;
}
}
}
R_API void r_table_row_free(void *_row) {
RTableRow *row = _row;
free (row);
}
R_API void r_table_column_free(void *_col) {
RTableColumn *col = _col;
free (col->name);
free (col);
}
R_API RTable *r_table_new() {
RTable *t = R_NEW0 (RTable);
t->showHeader = true;
t->cols = r_list_newf (r_table_column_free);
t->rows = r_list_newf (r_table_row_free);
return t;
}
R_API void r_table_free(RTable *t) {
r_list_free (t->cols);
r_list_free (t->rows);
free (t);
}
R_API void r_table_add_column(RTable *t, RTableColumnType *type, const char *name, int maxWidth) {
RTableColumn *c = R_NEW0 (RTableColumn);
if (c) {
c->name = strdup (name);
c->maxWidth = maxWidth;
c->type = type;
int itemLength = r_str_len_utf8 (name);
c->width = itemLength + 1;
r_list_append (t->cols, c);
}
}
R_API RTableRow *r_table_row_new(RList *items) {
RTableRow *row = R_NEW (RTableRow);
row->items = items;
return row;
}
static bool addRow (RTable *t, RList *items, const char *arg, int col) {
int itemLength = r_str_len_utf8 (arg);
RTableColumn *c = r_list_get_n (t->cols, col);
if (c) {
c->width = R_MAX (c->width, itemLength);
r_list_append (items, strdup (arg));
return true;
}
return false;
}
R_API void r_table_add_row(RTable *t, const char *name, ...) {
va_list ap;
va_start (ap, name);
int col = 0;
RList *items = r_list_newf (free);
addRow (t, items, name, col++);
for (;;) {
const char *arg = va_arg (ap, const char *);
if (!arg) {
break;
}
addRow (t, items, arg, col);
// TODO: assert if number of columns doesnt match t->cols
col++;
}
va_end (ap);
RTableRow *row = r_table_row_new (items);
r_list_append (t->rows, row);
// throw warning if not enough columns defined in header
t->totalCols = R_MAX (t->totalCols, r_list_length (items));
}
// import / export
R_API char *r_table_tofancystring(RTable *t) {
RStrBuf *sb = r_strbuf_new ("");
RTableRow *row;
RTableColumn *col;
RListIter *iter, *iter2;
r_list_foreach (t->cols, iter, col) {
r_strbuf_appendf (sb, "| %*s ", col->width, col->name);
}
int len = r_strbuf_length (sb) - 1;
{
char *s = r_str_newf (".%s.\n", r_str_pad ('-', len));
r_strbuf_prepend (sb, s);
free (s);
}
r_strbuf_appendf (sb, "|\n)%s(\n", r_str_pad ('-', len));
r_list_foreach (t->rows, iter, row) {
char *item;
int c = 0;
r_list_foreach (row->items, iter2, item) {
RTableColumn *col = r_list_get_n (t->cols, c);
if (col) {
r_strbuf_appendf (sb, "| %*s ", col->width, item);
}
c++;
}
r_strbuf_append (sb, "|\n");
}
r_strbuf_appendf (sb, "`%s'\n", r_str_pad ('-', len));
return r_strbuf_drain (sb);
}
R_API char *r_table_tostring(RTable *t) {
RStrBuf *sb = r_strbuf_new ("");
RTableRow *row;
RTableColumn *col;
RListIter *iter, *iter2;
if (t->showHeader) {
r_list_foreach (t->cols, iter, col) {
r_strbuf_appendf (sb, "%*s", col->width, col->name);
}
int len = r_strbuf_length (sb);
r_strbuf_appendf (sb, "\n%s\n", r_str_pad ('-', len));
}
r_list_foreach (t->rows, iter, row) {
char *item;
int c = 0;
r_list_foreach (row->items, iter2, item) {
RTableColumn *col = r_list_get_n (t->cols, c);
if (col) {
r_strbuf_appendf (sb, "%*s", col->width, item);
}
c++;
}
r_strbuf_append (sb, "\n");
}
return r_strbuf_drain (sb);
}
R_API char *r_table_tocsv(RTable *t) {
RStrBuf *sb = r_strbuf_new ("");
RTableRow *row;
RTableColumn *col;
RListIter *iter, *iter2;
if (t->showHeader) {
const char *comma = "";
r_list_foreach (t->cols, iter, col) {
if (strchr (col->name, ',')) {
// TODO. escaped string?
r_strbuf_appendf (sb, "%s\"%s\"", comma, col->name);
} else {
r_strbuf_appendf (sb, "%s%s", comma, col->name);
}
comma = ",";
}
r_strbuf_append (sb, "\n");
}
r_list_foreach (t->rows, iter, row) {
char *item;
int c = 0;
const char *comma = "";
r_list_foreach (row->items, iter2, item) {
RTableColumn *col = r_list_get_n (t->cols, c);
if (col) {
if (strchr (col->name, ',')) {
r_strbuf_appendf (sb, "%s\"%s\"", comma, col->name);
} else {
r_strbuf_appendf (sb, "%s%s", comma, item);
}
comma = ",";
}
c++;
}
r_strbuf_append (sb, "\n");
}
return r_strbuf_drain (sb);
}
R_API char *r_table_tojson(RTable *t) {
PJ *pj = pj_new ();
RTableRow *row;
RListIter *iter, *iter2;
pj_a (pj);
r_list_foreach (t->rows, iter, row) {
char *item;
int c = 0;
pj_o (pj);
r_list_foreach (row->items, iter2, item) {
RTableColumn *col = r_list_get_n (t->cols, c);
if (col) {
pj_ks (pj, col->name, item);
}
c++;
}
pj_end (pj);
}
pj_end (pj);
return pj_drain (pj);
}
R_API void r_table_filter(RTable *t, int nth, int op, const char *un) {
RTableRow *row;
RListIter *iter, *iter2;
ut64 uv = r_num_get (NULL, un);
r_list_foreach_safe (t->rows, iter, iter2, row) {
const char *nn = r_list_get_n (row->items, nth);
ut64 nv = r_num_get (NULL, nn);
bool match = true;
switch (op) {
case '>':
match = (nv > uv);
break;
case '<':
match = (nv < uv);
break;
case '=':
match = (nv == uv);
break;
case '!':
match = (nv == uv);
break;
case '~':
match = strstr (nn, un) != NULL;
case '\0':
break;
}
if (!match) {
r_list_delete (t->rows, iter);
}
}
}
static int cmp(const void *_a, const void *_b) {
RTableRow *a = (RTableRow*)_a;
RTableRow *b = (RTableRow*)_b;
const char *wa = r_list_get_n (a->items, Gnth);
const char *wb = r_list_get_n (b->items, Gnth);
int res = Gcmp (wa, wb);
if (Ginc) {
res = -res;
}
return res;
}
R_API void r_table_sort(RTable *t, int nth, bool inc) {
RTableColumn *col = r_list_get_n (t->cols, nth);
Ginc = inc;
Gnth = nth;
Gcmp = col->type->cmp;
r_list_sort (t->rows, cmp);
Gnth = Ginc = 0;
Gcmp = NULL;
}
static int __columnByName(RTable *t, const char *name) {
RListIter *iter;
RTableColumn *col;
int n = 0;
r_list_foreach (t->cols, iter, col) {
if (!strcmp (name, col->name)) {
return n;
}
n++;
}
return -1;
}
static int __resolveOperation(const char *op) {
if (!strcmp (op, "gt")) {
return '>';
}
if (!strcmp (op, "lt")) {
return '<';
}
if (!strcmp (op, "eq")) {
return '=';
}
if (!strcmp (op, "ne")) {
return '!';
}
return -1;
}
R_API void r_table_query(RTable *t, const char *q) {
// TODO support parenthesis and (or)||
// split by "&&" (or comma) -> run .filter on each
// addr/gt/200,addr/lt/400,addr/sort/dec,offset/sort/inc
RListIter *iter;
char *qq = strdup (q);
RList *queries = r_str_split_list (qq, ",");
char *query;
r_list_foreach (queries, iter, query) {
RList *q = r_str_split_list (query, "/");
const char *columnName = r_list_get_n (q, 0);
const char *operation = r_list_get_n (q, 1);
const char *operand = r_list_get_n (q, 2);
int col = __columnByName (t, columnName);
if (col == -1) {
if (*columnName == '[') {
col = atoi (columnName + 1);
} else {
eprintf ("Invalid column name (%s)\n", columnName);
}
}
if (!strcmp (operation, "sort")) {
r_table_sort (t, col, !strcmp (operation, "inc"));
} else {
int op = __resolveOperation (operation);
if (op == -1) {
eprintf ("Invalid operation (%s)\n", operation);
} else {
r_table_filter (t, col, op, operand);
}
}
r_list_free (q);
}
r_list_free (queries);
free (qq);
}
#if 0
// TODO: to be implemented
R_API RTable *r_table_clone(RTable *t) {
// TODO: implement
return NULL;
}
R_API RTable *r_table_push(RTable *t) {
// TODO: implement
return NULL;
}
R_API RTable *r_table_pop(RTable *t) {
// TODO: implement
return NULL;
}
R_API void r_table_fromjson(RTable *t, const char *csv) {
// TODO
}
R_API void r_table_fromcsv(RTable *t, const char *csv) {
// TODO
}
R_API char *r_table_tohtml(RTable *t) {
// TODO
return NULL;
}
R_API void r_table_transpose(RTable *t) {
// When the music stops rows will be cols and cols... rows!
}
R_API void r_table_format(RTable *t, int nth, RTableColumnType *type) {
// change the format of a specific column
// change imm base, decimal precission, ...
}
// to compute sum result of all the elements in a column
R_API ut64 r_table_reduce(RTable *t, int nth) {
// When the music stops rows will be cols and cols... rows!
return 0;
}
R_API void r_table_columns(RTable *t, const char *name, ...) {
va_list ap;
va_start (ap, fmt);
r_list_free (t->cols);
t->cols = r_list_newf (__table_column_free);
for (;;) {
const char *n = va_arg (ap, const char *);
if (!n) {
break;
}
}
va_end (ap);
}
#endif