1 /* radare - LGPL - Copyright 2019-2021 - pancake */
2 
3 #include <r_util/r_table.h>
4 #include "r_cons.h"
5 
6 // cant do that without globals because RList doesnt have void *user :(
7 static int Gnth = 0;
8 static RListComparator Gcmp = NULL;
9 
sortString(const void * a,const void * b)10 static int sortString(const void *a, const void *b) {
11 	return strcmp (a, b);
12 }
13 
sortNumber(const void * a,const void * b)14 static int sortNumber(const void *a, const void *b) {
15 	return r_num_get (NULL, a) - r_num_get (NULL, b);
16 }
17 
18 // maybe just index by name instead of exposing those symbols as global
19 static RTableColumnType r_table_type_string = { "string", sortString };
20 static RTableColumnType r_table_type_number = { "number", sortNumber };
21 static RTableColumnType r_table_type_bool = { "bool", sortNumber };
22 
r_table_type(const char * name)23 R_API RTableColumnType *r_table_type (const char *name) {
24 	if (!strcmp (name, "bool")) {
25 		return &r_table_type_bool;
26 	}
27 	if (!strcmp (name, "boolean")) {
28 		return &r_table_type_bool;
29 	}
30 	if (!strcmp (name, "string")) {
31 		return &r_table_type_string;
32 	}
33 	if (!strcmp (name, "number")) {
34 		return &r_table_type_number;
35 	}
36 	return NULL;
37 }
38 
39 // TODO: unused for now, maybe good to call after filter :?
__table_adjust(RTable * t)40 static void __table_adjust(RTable *t) {
41 	RListIter *iter, *iter2;
42 	RTableColumn *col;
43 	RTableRow  *row;
44 	r_list_foreach (t->cols, iter, col) {
45 		int itemLength = r_str_len_utf8_ansi (col->name) + 1;
46 		col->width = itemLength;
47 	}
48 	r_list_foreach (t->rows, iter, row) {
49 		const char *item;
50 		int ncol = 0;
51 		r_list_foreach (row->items, iter2, item) {
52 			int itemLength = r_str_len_utf8_ansi (item) + 1;
53 			RTableColumn *c = r_list_get_n (t->cols, ncol);
54 			if (c) {
55 				c->width = R_MAX (c->width, itemLength);
56 			}
57 			ncol ++;
58 		}
59 	}
60 }
61 
r_table_row_free(void * _row)62 R_API void r_table_row_free(void *_row) {
63 	RTableRow *row = _row;
64 	r_list_free (row->items);
65 	free (row);
66 }
67 
r_table_column_free(void * _col)68 R_API void r_table_column_free(void *_col) {
69 	RTableColumn *col = _col;
70 	free (col->name);
71 	free (col);
72 }
73 
r_table_row_clone(RTableRow * row)74 R_API RTableRow *r_table_row_clone(RTableRow *row) {
75 	RTableRow *r = r_table_row_new (r_list_newf (free));
76 	RListIter *iter;
77 	char *word;
78 	r_list_foreach (row->items, iter, word) {
79 		r_list_append (r->items, strdup (word));
80 	}
81 	return r;
82 }
83 
r_table_column_clone(RTableColumn * col)84 R_API RTableColumn *r_table_column_clone(RTableColumn *col) {
85 	RTableColumn *c = R_NEW0 (RTableColumn);
86 	if (!c) {
87 		return NULL;
88 	}
89 	memcpy (c, col, sizeof (*c));
90 	c->name = strdup (c->name);
91 	return c;
92 }
93 
r_table_new(const char * name)94 R_API RTable *r_table_new(const char *name) {
95 	RTable *t = R_NEW0 (RTable);
96 	if (t) {
97 		t->showHeader = true;
98 		t->name = strdup (name);
99 		t->cols = r_list_newf (r_table_column_free);
100 		t->rows = r_list_newf (r_table_row_free);
101 		t->showSum = false;
102 	}
103 	return t;
104 }
105 
r_table_free(RTable * t)106 R_API void r_table_free(RTable *t) {
107 	if (!t) {
108 		return;
109 	}
110 	r_list_free (t->cols);
111 	r_list_free (t->rows);
112 	free (t->name);
113 	free (t);
114 }
115 
r_table_add_column(RTable * t,RTableColumnType * type,const char * name,int maxWidth)116 R_API void r_table_add_column(RTable *t, RTableColumnType *type, const char *name, int maxWidth) {
117 	RTableColumn *c = R_NEW0 (RTableColumn);
118 	if (c) {
119 		c->name = strdup (name);
120 		c->maxWidth = maxWidth;
121 		c->type = type;
122 		int itemLength = r_str_len_utf8_ansi (name) + 1;
123 		c->width = itemLength;
124 		r_list_append (t->cols, c);
125 		c->total = -1;
126 	}
127 }
128 
r_table_row_new(RList * items)129 R_API RTableRow *r_table_row_new(RList *items) {
130 	RTableRow *row = R_NEW (RTableRow);
131 	row->items = items;
132 	return row;
133 }
134 
__addRow(RTable * t,RList * items,const char * arg,int col)135 static bool __addRow(RTable *t, RList *items, const char *arg, int col) {
136 	int itemLength = r_str_len_utf8_ansi (arg) + 1;
137 	RTableColumn *c = r_list_get_n (t->cols, col);
138 	if (c) {
139 		c->width = R_MAX (c->width, itemLength);
140 		r_list_append (items, strdup (arg));
141 		return true;
142 	}
143 	return false;
144 }
145 
r_table_add_row_list(RTable * t,RList * items)146 R_API void r_table_add_row_list(RTable *t, RList *items) {
147 	r_return_if_fail (t && items);
148 	RTableRow *row = r_table_row_new (items);
149 	r_list_append (t->rows, row);
150 	// throw warning if not enough columns defined in header
151 	t->totalCols = R_MAX (t->totalCols, r_list_length (items));
152 }
153 
r_table_set_columnsf(RTable * t,const char * fmt,...)154 R_API void r_table_set_columnsf(RTable *t, const char *fmt, ...) {
155 	va_list ap;
156 	va_start (ap, fmt);
157 	RTableColumnType *typeString = r_table_type ("string");
158 	RTableColumnType *typeNumber = r_table_type ("number");
159 	RTableColumnType *typeBool = r_table_type ("bool");
160 	const char *name;
161 	const char *f = fmt;
162 	for (;*f;f++) {
163 		name = va_arg (ap, const char *);
164 		if (!name) {
165 			break;
166 		}
167 		switch (*f) {
168 		case 'b':
169 			r_table_add_column (t, typeBool, name, 0);
170 			break;
171 		case 's':
172 		case 'z':
173 			r_table_add_column (t, typeString, name, 0);
174 			break;
175 		case 'i':
176 		case 'd':
177 		case 'n':
178 		case 'x':
179 		case 'X':
180 			r_table_add_column (t, typeNumber, name, 0);
181 			break;
182 		default:
183 			eprintf ("Invalid format string char '%c', use 's' or 'n'\n", *f);
184 			break;
185 		}
186 	}
187 	va_end(ap);
188 }
189 
r_table_add_rowf(RTable * t,const char * fmt,...)190 R_API void r_table_add_rowf(RTable *t, const char *fmt, ...) {
191 	va_list ap;
192 	va_start (ap, fmt);
193 	RList *list = r_list_newf (free);
194 	const char *f = fmt;
195 	const char *arg = NULL;
196 	for (; *f; f++) {
197 		switch (*f) {
198 		case 's':
199 		case 'z':
200 			arg = va_arg (ap, const char *);
201 			r_list_append (list, strdup (r_str_get (arg)));
202 			break;
203 		case 'b':
204 			r_list_append (list, r_str_new (r_str_bool (va_arg (ap, int))));
205 			break;
206 		case 'i':
207 		case 'd':
208 			r_list_append (list, r_str_newf ("%d", va_arg (ap, int)));
209 			break;
210 		case 'n':
211 			r_list_append (list, r_str_newf ("%"PFMT64d, va_arg (ap, ut64)));
212 			break;
213 		case 'u':
214 			r_list_append (list, r_num_units (NULL, 32, va_arg (ap, ut64)));
215 			break;
216 		case 'x':
217 		case 'X':
218 			{
219 				ut64 n = va_arg (ap, ut64);
220 				if (n == UT64_MAX) {
221 					if (*f == 'X') {
222 						r_list_append (list, strdup ("----------"));
223 					} else {
224 						r_list_append (list, strdup ("-1"));
225 					}
226 				} else {
227 					if (*f == 'X') {
228 						r_list_append (list, r_str_newf ("0x%08"PFMT64x, n));
229 					} else {
230 						r_list_append (list, r_str_newf ("0x%"PFMT64x, n));
231 					}
232 				}
233 			}
234 			break;
235 		default:
236 			eprintf ("Invalid format string char '%c', use 's' or 'n'\n", *f);
237 			break;
238 		}
239 	}
240 	va_end (ap);
241 	r_table_add_row_list (t, list);
242 }
243 
r_table_add_row(RTable * t,const char * name,...)244 R_API void r_table_add_row(RTable *t, const char *name, ...) {
245 	va_list ap;
246 	va_start (ap, name);
247 	int col = 0;
248 	RList *items = r_list_newf (free);
249 	__addRow (t, items, name, col++);
250 	for (;;) {
251 		const char *arg = va_arg (ap, const char *);
252 		if (!arg) {
253 			break;
254 		}
255 		__addRow (t, items, arg, col);
256 		// TODO: assert if number of columns doesnt match t->cols
257 		col++;
258 	}
259 	va_end (ap);
260 	RTableRow *row = r_table_row_new (items);
261 	r_list_append (t->rows, row);
262 	// throw warning if not enough columns defined in header
263 	t->totalCols = R_MAX (t->totalCols, r_list_length (items));
264 }
265 
266 // import / export
267 
__strbuf_append_col_aligned_fancy(RTable * t,RStrBuf * sb,RTableColumn * col,char * str)268 static int __strbuf_append_col_aligned_fancy(RTable *t, RStrBuf *sb, RTableColumn *col, char *str) {
269 	RCons *cons = (RCons *) t->cons;
270 	const char *v_line = (cons && (cons->use_utf8 ||  cons->use_utf8_curvy)) ? RUNE_LINE_VERT : "|";
271 	int ll = r_strbuf_length (sb);
272 	switch (col->align) {
273 	case R_TABLE_ALIGN_LEFT:
274 		r_strbuf_appendf (sb, "%s %-*s ", v_line, col->width, str);
275 		break;
276 	case R_TABLE_ALIGN_RIGHT:
277 		r_strbuf_appendf (sb, "%s %*s ", v_line, col->width, str);
278 		break;
279 	case R_TABLE_ALIGN_CENTER:
280 		{
281 			int len = r_str_len_utf8 (str);
282 			int pad = (col->width - len) / 2;
283 			int left = col->width - (pad * 2 + len);
284 			r_strbuf_appendf (sb, "%s %-*s ", v_line, pad, " ");
285 			r_strbuf_appendf (sb, "%-*s ", pad + left, str);
286 			break;
287 		}
288 	}
289 	return r_strbuf_length (sb) - ll;
290 }
291 
__computeTotal(RTable * t)292 static void __computeTotal(RTable *t) {
293 	RTableRow *row;
294 	RListIter *iter, *iter2;
295 	r_list_foreach (t->rows, iter, row) {
296 		char *item;
297 		int c = 0;
298 		r_list_foreach (row->items, iter2, item) {
299 			RTableColumn *col = r_list_get_n (t->cols, c);
300 			if (!r_str_cmp (col->type->name, "number", r_str_ansi_len ("number")) && r_str_isnumber (item)) {
301 				if (col->total < 0) {
302 					col->total = 0;
303 				}
304 				col->total += sdb_atoi(item);
305 			}
306 			c++;
307 		}
308 	}
309 }
310 
r_table_tofancystring(RTable * t)311 R_API char *r_table_tofancystring(RTable *t) {
312 	if (r_list_length (t->cols) == 0) {
313 		return strdup ("");
314 	}
315 	RStrBuf *sb = r_strbuf_new ("");
316 	RTableRow *row;
317 	RTableColumn *col;
318 	RCons *cons = (RCons *)t->cons;
319 	RListIter *iter, *iter2;
320 	bool useUtf8 = (cons && cons->use_utf8);
321 	bool useUtf8Curvy = (cons && cons->use_utf8_curvy);
322 	const char *v_line = useUtf8 || useUtf8Curvy ? RUNE_LINE_VERT : "|";
323 	const char *h_line = useUtf8 || useUtf8Curvy ? RUNE_LINE_HORIZ : "-";
324 	const char *l_intersect = useUtf8 || useUtf8Curvy ? RUNE_LINE_VERT : ")";
325 	const char *r_intersect = useUtf8 || useUtf8Curvy ? RUNE_LINE_VERT : "(";
326 	const char *tl_corner = useUtf8 ? (useUtf8Curvy ? RUNE_CURVE_CORNER_TL : RUNE_CORNER_TL) : ".";
327 	const char *tr_corner = useUtf8 ? (useUtf8Curvy ? RUNE_CURVE_CORNER_TR : RUNE_CORNER_TR) : ".";
328 	const char *bl_corner = useUtf8 ? (useUtf8Curvy ? RUNE_CURVE_CORNER_BL : RUNE_CORNER_BL) : "`";
329 	const char *br_corner = useUtf8 ? (useUtf8Curvy ? RUNE_CURVE_CORNER_BR : RUNE_CORNER_BR) : "'";
330 	__table_adjust (t);
331 
332 	r_list_foreach (t->cols, iter, col) {
333 		__strbuf_append_col_aligned_fancy (t, sb, col, col->name);
334 	}
335 	int len = r_str_len_utf8_ansi (r_strbuf_get (sb)) - 1;
336 	int maxlen = len;
337 	char *h_line_str = r_str_repeat (h_line, maxlen);
338 	{
339 		char *s = r_str_newf ("%s%s%s\n", tl_corner, h_line_str, tr_corner);
340 		r_strbuf_prepend (sb, s);
341 		free (s);
342 	}
343 
344 	r_strbuf_appendf (sb, "%s\n%s%s%s\n", v_line, l_intersect, h_line_str, r_intersect);
345 	r_list_foreach (t->rows, iter, row) {
346 		char *item;
347 		int c = 0;
348 		r_list_foreach (row->items, iter2, item) {
349 			RTableColumn *col = r_list_get_n (t->cols, c);
350 			if (col) {
351 				int l = __strbuf_append_col_aligned_fancy (t, sb, col, item);
352 				len = R_MAX (len, l);
353 			}
354 			c++;
355 		}
356 		r_strbuf_appendf (sb, "%s\n", v_line);
357 	}
358 
359 	if (t->showSum) {
360 		char tmp[64];
361 		__computeTotal (t);
362 		r_strbuf_appendf (sb, "%s%s%s\n", l_intersect, h_line_str, r_intersect);
363 		r_list_foreach (t->cols, iter, col) {
364 			char *num = col->total == -1 ? "" : sdb_itoa (col->total, tmp, 10);
365 			int l = __strbuf_append_col_aligned_fancy (t, sb, col, num);
366 			len = R_MAX (len, l);
367 		}
368 		r_strbuf_appendf (sb, "%s\n", v_line);
369 	}
370 	r_strbuf_appendf (sb, "%s%s%s\n", bl_corner, h_line_str, br_corner);
371 	free (h_line_str);
372 	return r_strbuf_drain (sb);
373 }
374 
__strbuf_append_col_aligned(RStrBuf * sb,RTableColumn * col,const char * str,bool nopad)375 static int __strbuf_append_col_aligned(RStrBuf *sb, RTableColumn *col, const char *str, bool nopad) {
376 	int ll = r_strbuf_length (sb);
377 	if (nopad) {
378 		r_strbuf_appendf (sb, "%s", str);
379 	} else {
380 		char *pad = "";
381 		int padlen = 0;
382 		int len1 = r_str_len_utf8 (str);
383 		int len2 = r_str_len_utf8_ansi (str);
384 		if (len1 > len2) {
385 			if (len2 < col->width) {
386 				padlen = col->width - len2;
387 			}
388 		}
389 		switch (col->align) {
390 		case R_TABLE_ALIGN_LEFT:
391 			pad = r_str_repeat (" ", padlen);
392 			r_strbuf_appendf (sb, "%-*s%s", col->width, str, pad);
393 			free (pad);
394 			break;
395 		case R_TABLE_ALIGN_RIGHT:
396 			pad = r_str_repeat (" ", padlen);
397 			r_strbuf_appendf (sb, "%s%*s ", pad, col->width, str);
398 			free (pad);
399 			break;
400 		case R_TABLE_ALIGN_CENTER:
401 			{
402 				int pad = (col->width - len2) / 2;
403 				int left = col->width - (pad * 2 + len2);
404 				r_strbuf_appendf (sb, "%-*s", pad, " ");
405 				r_strbuf_appendf (sb, "%-*s ", pad + left, str);
406 				break;
407 			}
408 		}
409 	}
410 	return r_strbuf_length (sb) - ll;
411 }
412 
r_table_tostring(RTable * t)413 R_API char *r_table_tostring(RTable *t) {
414 	if (!t) { // guard
415 		return strdup ("");
416 	}
417 	if (t->showR2) {
418 		return r_table_tor2cmds (t);
419 	}
420 	if (t->showSQL) {
421 		return r_table_tosql (t);
422 	}
423 	if (t->showCSV) {
424 		return r_table_tocsv (t);
425 	}
426 	if (t->showJSON) {
427 		char *s = r_table_tojson (t);
428 		char *q = r_str_newf ("%s\n", s);;
429 		free (s);
430 		return q;
431 	}
432 	if (t->showFancy) {
433 		return r_table_tofancystring (t);
434 	}
435 	return r_table_tosimplestring (t);
436 }
437 
r_table_tosimplestring(RTable * t)438 R_API char *r_table_tosimplestring(RTable *t) {
439 	RStrBuf *sb = r_strbuf_new ("");
440 	RTableRow *row;
441 	RTableColumn *col;
442 	RListIter *iter, *iter2;
443 	RCons *cons = (RCons *) t->cons;
444 	const char *h_line = (cons && (cons->use_utf8 || cons->use_utf8_curvy)) ? RUNE_LONG_LINE_HORIZ : "-";
445 	__table_adjust (t);
446 	int maxlen = 0;
447 	if (t->showHeader) {
448 		r_list_foreach (t->cols, iter, col) {
449 			bool nopad = !iter->n;
450 			int ll = __strbuf_append_col_aligned (sb, col, col->name, nopad);
451 			maxlen = R_MAX (maxlen, ll);
452 		}
453 		int len = r_str_len_utf8_ansi (r_strbuf_get (sb));
454 		char *l = r_str_repeat (h_line, R_MAX (maxlen, len));
455 		if (l) {
456 			r_strbuf_appendf (sb, "\n%s\n", l);
457 			free (l);
458 		}
459 	}
460 	r_list_foreach (t->rows, iter, row) {
461 		char *item;
462 		int c = 0;
463 		r_list_foreach (row->items, iter2, item) {
464 			bool nopad = !iter2->n;
465 			RTableColumn *col = r_list_get_n (t->cols, c);
466 			if (col) {
467 				(void)__strbuf_append_col_aligned (sb, col, item, nopad);
468 			}
469 			c++;
470 		}
471 		r_strbuf_append (sb, "\n");
472 	}
473 	if (t->showSum) {
474 		char tmp[64];
475 		__computeTotal (t);
476 		if (maxlen > 0) {
477 			char *l = r_str_repeat (h_line, maxlen);
478 			if (l) {
479 				r_strbuf_appendf (sb, "\n%s\n", l);
480 				free (l);
481 			}
482 		}
483 		r_list_foreach (t->cols, iter, col) {
484 			bool nopad = !iter->n;
485 			(void)__strbuf_append_col_aligned (sb, col, sdb_itoa (col->total, tmp, 10), nopad);
486 		}
487 	}
488 	return r_strbuf_drain (sb);
489 }
490 
r_table_tor2cmds(RTable * t)491 R_API char *r_table_tor2cmds(RTable *t) {
492 	RStrBuf *sb = r_strbuf_new ("");
493 	RTableRow *row;
494 	RTableColumn *col;
495 	RListIter *iter, *iter2;
496 
497 	r_strbuf_appendf (sb, ",h ");
498 	r_list_foreach (t->cols, iter, col) {
499 		char fmt = col->type == &r_table_type_string? 's': 'x';
500 		r_strbuf_appendf (sb, "%c", fmt);
501 	}
502 	r_list_foreach (t->cols, iter, col) {
503 		r_strbuf_appendf (sb, " %s",  col->name);
504 	}
505 	r_strbuf_append (sb, "\n");
506 
507 	r_list_foreach (t->rows, iter, row) {
508 		char *item;
509 		int c = 0;
510 		r_strbuf_appendf (sb, ",r");
511 		r_list_foreach (row->items, iter2, item) {
512 			RTableColumn *col = r_list_get_n (t->cols, c);
513 			if (col) {
514 				r_strbuf_appendf (sb, " %s", item);
515 			}
516 			c++;
517 		}
518 		r_strbuf_append (sb, "\n");
519 	}
520 	return r_strbuf_drain (sb);
521 }
522 
r_table_tosql(RTable * t)523 R_API char *r_table_tosql(RTable *t) {
524 	r_return_val_if_fail (t, NULL);
525 	RStrBuf *sb = r_strbuf_new ("");
526 	RTableRow *row;
527 	RTableColumn *col;
528 	RListIter *iter, *iter2;
529 
530 	const char *table_name = R_STR_ISEMPTY (t->name)? "r2": t->name;
531 	r_strbuf_appendf (sb, "CREATE TABLE %s (", table_name);
532 	bool primary_key = true;
533 	r_list_foreach (t->cols, iter, col) {
534 		const char *type = col->type == &r_table_type_string? "VARCHAR": "NUMERIC(20)";
535 		const char *comma = iter->n? ", ": "";
536 		const char *pkey = primary_key? " PRIMARY KEY": "";
537 		char *s = r_str_escape_sql (col->name);
538 		r_strbuf_appendf (sb, "%s %s%s%s", s, type, pkey, comma);
539 		free (s);
540 		primary_key = false;
541 	}
542 	r_strbuf_appendf (sb, ");\n");
543 
544 	r_list_foreach (t->rows, iter, row) {
545 		const char *item;
546 		int c = 0;
547 		r_strbuf_appendf (sb, "INSERT INTO %s (", table_name);
548 		r_list_foreach (t->cols, iter2, col) {
549 			const char *comma = iter2->n? ", ": "";
550 			char *s = r_str_escape_sql (col->name);
551 			r_strbuf_appendf (sb, "%s%s", s, comma);
552 			free (s);
553 		}
554 		r_strbuf_append (sb, ") VALUES (");
555 		r_list_foreach (row->items, iter2, item) {
556 			RTableColumn *col = r_list_get_n (t->cols, c);
557 			if (col) {
558 				const char *comma = iter2->n? ", ": "";
559 				if (col->type == &r_table_type_string) {
560 					char *s = r_str_escape_sql (item);
561 					r_strbuf_appendf (sb, "'%s'%s", s, comma);
562 					free (s);
563 				} else {
564 					r_strbuf_appendf (sb, "%s%s", item, comma);
565 				}
566 			}
567 			c++;
568 		}
569 		r_strbuf_append (sb, ");\n");
570 	}
571 	return r_strbuf_drain (sb);
572 }
573 
r_table_tocsv(RTable * t)574 R_API char *r_table_tocsv(RTable *t) {
575 	RStrBuf *sb = r_strbuf_new ("");
576 	RTableRow *row;
577 	RTableColumn *col;
578 	RListIter *iter, *iter2;
579 	if (t->showHeader) {
580 		const char *comma = "";
581 		r_list_foreach (t->cols, iter, col) {
582 			if (strchr (col->name, ',')) {
583 				// TODO. escaped string?
584 				r_strbuf_appendf (sb, "%s\"%s\"", comma, col->name);
585 			} else {
586 				r_strbuf_appendf (sb, "%s%s", comma, col->name);
587 			}
588 			comma = ",";
589 		}
590 		r_strbuf_append (sb, "\n");
591 	}
592 	r_list_foreach (t->rows, iter, row) {
593 		char *item;
594 		int c = 0;
595 		const char *comma = "";
596 		r_list_foreach (row->items, iter2, item) {
597 			RTableColumn *col = r_list_get_n (t->cols, c);
598 			if (col) {
599 				if (strchr (col->name, ',')) {
600 					r_strbuf_appendf (sb, "%s\"%s\"", comma, col->name);
601 				} else {
602 					r_strbuf_appendf (sb, "%s%s", comma, item);
603 				}
604 				comma = ",";
605 			}
606 			c++;
607 		}
608 		r_strbuf_append (sb, "\n");
609 	}
610 	return r_strbuf_drain (sb);
611 }
612 
r_table_tojson(RTable * t)613 R_API char *r_table_tojson(RTable *t) {
614 	PJ *pj = pj_new ();
615 	RTableRow *row;
616 	RListIter *iter, *iter2;
617 	pj_a (pj);
618 	r_list_foreach (t->rows, iter, row) {
619 		char *item;
620 		int c = 0;
621 		pj_o (pj);
622 		r_list_foreach (row->items, iter2, item) {
623 			RTableColumn *col = r_list_get_n (t->cols, c);
624 			if (col) {
625 				if (col->type == &r_table_type_number) {
626 					ut64 n = r_num_get (NULL, item);
627 					if (n) {
628 						pj_kn (pj, col->name, n);
629 					} else if (*item && *item != '0') {
630 						pj_ks (pj, col->name, item);
631 					}
632 				} else {
633 					if (*item) {
634 						pj_ks (pj, col->name, item);
635 					}
636 				}
637 			}
638 			c++;
639 		}
640 		pj_end (pj);
641 	}
642 	pj_end (pj);
643 	return pj_drain (pj);
644 }
645 
r_table_filter(RTable * t,int nth,int op,const char * un)646 R_API void r_table_filter(RTable *t, int nth, int op, const char *un) {
647 	r_return_if_fail (t && un);
648 	RTableRow *row;
649 	RListIter *iter, *iter2;
650 	ut64 uv = r_num_math (NULL, un);
651 	ut64 sum = 0;
652 	int page = 0, page_items = 0;
653 	size_t lrow = 0;
654 	if (op == 't') {
655 		size_t ll = r_list_length (t->rows);
656 		if (ll > uv) {
657 			uv = ll - uv;
658 		}
659 	}
660 	if (op == 'p') {
661 		sscanf (un, "%d/%d", &page, &page_items);
662 		if (page < 1) {
663 			page = 1;
664 		}
665 		if (!ST32_MUL_OVFCHK (page, page_items)) {
666 			lrow = page_items * (page - 1);
667 			uv = page_items * (page);
668 		}
669 	}
670 	size_t nrow = 0;
671 	r_list_foreach_safe (t->rows, iter, iter2, row) {
672 		const char *nn = r_list_get_n (row->items, nth);
673 		ut64 nv = r_num_math (NULL, nn);
674 		bool match = true;
675 		switch (op) {
676 		case 'p':
677 			nrow++;
678 			if (nrow < lrow) {
679 				match = false;
680 			}
681 			if (nrow > uv) {
682 				match = false;
683 			}
684 			break;
685 		case 't':
686 			nrow++;
687 			if (nrow < uv) {
688 				match = false;
689 			}
690 			break;
691 		case 'h':
692 			nrow++;
693 			if (nrow > uv) {
694 				match = false;
695 			}
696 			break;
697 		case '+':
698 			// "sum"
699 			sum += nv;
700 			match = false;
701 			break;
702 		case '>':
703 			match = (nv > uv);
704 			break;
705 		case '<':
706 			match = (nv < uv);
707 			break;
708 		case '=':
709 			if (nv == 0) {
710 				match = !strcmp (nn, un);
711 			} else {
712 				match = (nv == uv);
713 			}
714 			break;
715 		case '!':
716 			if (nv == 0) {
717 				match = strcmp (nn, un);
718 			} else {
719 				match = (nv != uv);
720 			}
721 			break;
722 		case '~':
723 			match = strstr (nn, un) != NULL;
724 			break;
725 		case 's':
726 			match = strlen (nn) == atoi (un);
727 			break;
728 		case 'l':
729 			match = strlen (nn) > atoi (un);
730 			break;
731 		case 'L':
732 			match = strlen (nn) < atoi (un);
733 			break;
734 		case '\0':
735 			break;
736 		}
737 		if (!match) {
738 			r_list_delete (t->rows, iter);
739 		}
740 	}
741 	if (op == '+') {
742 		r_table_add_rowf (t, "u", sum);
743 	}
744 }
745 
cmp(const void * _a,const void * _b)746 static int cmp(const void *_a, const void *_b) {
747 	RTableRow *a = (RTableRow*)_a;
748 	RTableRow *b = (RTableRow*)_b;
749 	const char *wa = r_list_get_n (a->items, Gnth);
750 	const char *wb = r_list_get_n (b->items, Gnth);
751 	int res = Gcmp (wa, wb);
752 	return res;
753 }
754 
r_table_sort(RTable * t,int nth,bool dec)755 R_API void r_table_sort(RTable *t, int nth, bool dec) {
756 	RTableColumn *col = r_list_get_n (t->cols, nth);
757 	if (col) {
758 		Gnth = nth;
759 		if (col->type && col->type->cmp) {
760 			Gcmp = col->type->cmp;
761 			t->rows->sorted = false; //force sorting
762 			r_list_sort (t->rows, cmp);
763 			if (dec) {
764 				r_list_reverse (t->rows);
765 			}
766 		}
767 		Gnth = 0;
768 		Gcmp = NULL;
769 	}
770 }
771 
cmplen(const void * _a,const void * _b)772 static int cmplen(const void *_a, const void *_b) {
773 	RTableRow *a = (RTableRow*)_a;
774 	RTableRow *b = (RTableRow*)_b;
775 	const char *wa = r_list_get_n (a->items, Gnth);
776 	const char *wb = r_list_get_n (b->items, Gnth);
777 	int res = strlen (wa) - strlen (wb);
778 	return res;
779 }
780 
r_table_sortlen(RTable * t,int nth,bool dec)781 R_API void r_table_sortlen(RTable *t, int nth, bool dec) {
782 	RTableColumn *col = r_list_get_n (t->cols, nth);
783 	if (col) {
784 		Gnth = nth;
785 		t->rows->sorted = false; //force sorting
786 		r_list_sort (t->rows, cmplen);
787 		if (dec) {
788 			r_list_reverse (t->rows);
789 		}
790 		Gnth = 0;
791 	}
792 }
793 
r_rows_cmp(RList * lhs,RList * rhs,RList * cols,int nth)794 static int r_rows_cmp(RList *lhs, RList *rhs, RList *cols, int nth) {
795 	RListIter *iter_lhs;
796 	RListIter *iter_rhs;
797 	RListIter *iter_col;
798 	RTableColumn *item_col;
799 
800 	void *item_lhs;
801 	void *item_rhs;
802 	int tmp;
803 	int i = 0;
804 
805 	for (iter_lhs = lhs->head, iter_rhs = rhs->head, iter_col = cols->head;
806 		iter_lhs && iter_rhs && iter_col;
807 		iter_lhs = iter_lhs->n, iter_rhs = iter_rhs->n, iter_col = iter_col->n) {
808 
809 		item_lhs = iter_lhs->data;
810 		item_rhs = iter_rhs->data;
811 		item_col = iter_col->data;
812 
813 		if (nth == -1 || i == nth) {
814 			tmp = item_col->type->cmp (item_lhs, item_rhs);
815 
816 			if (tmp) {
817 				return tmp;
818 			}
819 		}
820 
821 		i++;
822 	}
823 
824 	if (iter_lhs) {
825 		return 1;
826 	}
827 
828 	if (iter_rhs) {
829 		return -1;
830 	}
831 
832 	return 0;
833 }
834 
r_table_uniq(RTable * t)835 R_API void r_table_uniq(RTable *t) {
836 	r_table_group (t, -1, NULL);
837 }
838 
r_table_group(RTable * t,int nth,RTableSelector fcn)839 R_API void r_table_group(RTable *t, int nth, RTableSelector fcn) {
840 	RListIter *iter;
841 	RListIter *tmp;
842 	RTableRow *row;
843 
844 	RListIter *iter_inner;
845 	RTableRow *uniq_row;
846 
847 	RList *rows = t->rows;
848 
849 	r_list_foreach_safe (rows, iter, tmp, row) {
850 		for (iter_inner = rows->head;
851 			iter_inner && iter_inner != iter;
852 			iter_inner = iter_inner->n) {
853 
854 			uniq_row = iter_inner->data;
855 
856 			if (!r_rows_cmp (uniq_row->items, row->items, t->cols, nth)) {
857 				if (fcn) {
858 					fcn (uniq_row, row, nth);
859 				}
860 				r_list_delete (rows, iter);
861 				break;
862 			}
863 		}
864 	}
865 }
866 
r_table_column_nth(RTable * t,const char * name)867 R_API int r_table_column_nth(RTable *t, const char *name) {
868 	RListIter *iter;
869 	RTableColumn *col;
870 	int n = 0;
871 	r_list_foreach (t->cols, iter, col) {
872 		if (!strcmp (name, col->name)) {
873 			return n;
874 		}
875 		n++;
876 	}
877 	return -1;
878 }
879 
__resolveOperation(const char * op)880 static int __resolveOperation(const char *op) {
881 	if (!strcmp (op, "gt")) {
882 		return '>';
883 	}
884 	if (!strcmp (op, "lt")) {
885 		return '<';
886 	}
887 	if (!strcmp (op, "eq")) {
888 		return '=';
889 	}
890 	if (!strcmp (op, "ne")) {
891 		return '!';
892 	}
893 	return -1;
894 }
895 
__table_column_free(void * _col)896 static void __table_column_free(void *_col) {
897 	RTableColumn *col = (RTableColumn*)_col;
898 	free (col);
899 }
900 
r_table_columns(RTable * t,RList * col_names)901 R_API void r_table_columns(RTable *t, RList *col_names) {
902 	// 1 bool per OLD column to indicate whether it should be freed (masked out)
903 	bool *free_cols = malloc (sizeof (bool) * r_list_length (t->cols));
904 	if (!free_cols) {
905 		return;
906 	}
907 	size_t i;
908 	for (i = 0; i < r_list_length (t->cols); i++) {
909 		free_cols[i] = true;
910 	}
911 
912 	// 1 value per NEW column to indicate from which OLD column to take the info from and whether to dup it
913 	struct col_source {
914 		int oldcol;
915 		bool dup;
916 	} *col_sources = calloc (r_list_length (col_names), sizeof (struct col_source));
917 	if (!col_sources) {
918 		free (free_cols);
919 		return;
920 	}
921 
922 	// First create the plan which new columns to take from which old, which ones to dup or free.
923 	RListIter *it;
924 	const char *col_name;
925 	size_t new_count = 0;
926 	r_list_foreach (col_names, it, col_name) {
927 		int fc = r_table_column_nth (t, col_name);
928 		if (fc < 0) {
929 			continue;
930 		}
931 		col_sources[new_count].oldcol = fc;
932 		col_sources[new_count].dup = !free_cols[fc]; // if we already used the same old column for another new column before, we must dup it for all following!
933 		free_cols[fc] = false;
934 		new_count++;
935 	}
936 
937 	RTableRow *row;
938 	r_list_foreach (t->rows, it, row) {
939 		RList *old_items = row->items;
940 		RList *new_items = r_list_newf (free);
941 		for (i = 0; i < new_count; i++) {
942 			char *item = r_list_get_n (old_items, col_sources[i].oldcol);
943 			if (!item) {
944 				continue;
945 			}
946 			if (col_sources[i].dup) {
947 				item = strdup (item);
948 			}
949 			r_list_append (new_items, item);
950 		}
951 		row->items = new_items;
952 
953 		// Free dropped items
954 		char *item;
955 		i = 0;
956 		RListIter *fit;
957 		r_list_foreach (old_items, fit, item) {
958 			if (free_cols[i]) {
959 				free (item);
960 			}
961 			i++;
962 		}
963 		old_items->free = NULL;
964 		r_list_free (old_items);
965 	}
966 
967 	RList *old_cols = t->cols;
968 	RList *new_cols = r_list_newf (r_table_column_free);
969 	for (i = 0; i < new_count; i++) {
970 		RTableColumn *col = r_list_get_n (old_cols, col_sources[i].oldcol);
971 		if (!col) {
972 			continue;
973 		}
974 		if (col_sources[i].dup) {
975 			col = r_table_column_clone (col);
976 		}
977 		r_list_append (new_cols, col);
978 	}
979 	t->cols = new_cols;
980 
981 	// Free dropped columns
982 	RTableColumn *col;
983 	i = 0;
984 	r_list_foreach (old_cols, it, col) {
985 		if (free_cols[i]) {
986 			r_table_column_free (col);
987 		}
988 		i++;
989 	}
990 	old_cols->free = NULL;
991 	r_list_free (old_cols);
992 
993 	free (col_sources);
994 	free (free_cols);
995 }
996 
r_table_filter_columns(RTable * t,RList * list)997 R_API void r_table_filter_columns(RTable *t, RList *list) {
998 	const char *col;
999 	RListIter *iter;
1000 	RList *cols = t->cols;
1001 	t->cols = r_list_newf (__table_column_free);
1002 	r_list_foreach (list, iter, col) {
1003 		int ncol = r_table_column_nth (t, col);
1004 		if (ncol != -1) {
1005 			RTableColumn *c = r_list_get_n (cols, ncol);
1006 			if (c) {
1007 				r_table_add_column (t, c->type, col, 0);
1008 			}
1009 		}
1010 	}
1011 }
1012 
r_table_help(void)1013 R_API const char *r_table_help(void) {
1014 	return \
1015 		"RTableQuery> comma separated. 'c' stands for column name.\n"
1016 		" c/sort/inc     sort rows by given colname\n"
1017 		" c/sortlen/inc  sort rows by strlen()\n"
1018 		" c/cols/c1/c2   only show selected columns\n"
1019 		" c/gt/0x800     grep rows matching col0 > 0x800\n"
1020 		" c/lt/0x800     grep rows matching col0 < 0x800\n"
1021 		" c/eq/0x800     grep rows matching col0 == 0x800\n"
1022 		" c/ne/0x800     grep rows matching col0 != 0x800\n"
1023 		" */uniq         get the first row of each that col0 is unique\n"
1024 		" */head/10      same as | head -n 10\n"
1025 		" */tail/10      same as | tail -n 10\n"
1026 		" */page/1/10    show the first 10 rows (/page/2/10 will show the 2nd)\n"
1027 		" c/str/warn     grep rows matching col(name).str(warn)\n"
1028 		" c/strlen/3     grep rows matching strlen(col) == X\n"
1029 		" c/minlen/3     grep rows matching strlen(col) > X\n"
1030 		" c/maxlen/3     grep rows matching strlen(col) < X\n"
1031 		" c/sum          sum all the values of given column\n"
1032 		" :r2            .tostring() == .tor2()         # supports import/export\n"
1033 		" :csv           .tostring() == .tocsv()        # supports import/export\n"
1034 		" :fancy         .tostring() == .tofancystring()\n"
1035 		" :json          .tostring() == .tojson()\n"
1036 		" :simple        simple table output without lines\n"
1037 		" :sql           .tostring() == .tosql() # export table contents in SQL statements\n"
1038 		" :quiet         do not print column names header\n";
1039 }
1040 
__table_special(RTable * t,const char * columnName)1041 static bool __table_special(RTable *t, const char *columnName) {
1042 	if (*columnName != ':') {
1043 		return false;
1044 	}
1045 	if (!strcmp (columnName, ":quiet")) {
1046 		t->showHeader = true;
1047 	} else if (!strcmp (columnName, ":fancy")) {
1048 		t->showFancy = true;
1049 	} else if (!strcmp (columnName, ":sql")) {
1050 		t->showSQL = true;
1051 	} else if (!strcmp (columnName, ":simple")) {
1052 		t->showFancy = false;
1053 	} else if (!strcmp (columnName, ":r2")) {
1054 		t->showR2 = true;
1055 	} else if (!strcmp (columnName, ":csv")) {
1056 		t->showCSV = true;
1057 	} else if (!strcmp (columnName, ":json")) {
1058 		t->showJSON = true;
1059 	} else {
1060 		return false;
1061 	}
1062 	return true;
1063 }
1064 
r_table_query(RTable * t,const char * q)1065 R_API bool r_table_query(RTable *t, const char *q) {
1066 	r_return_val_if_fail (t, false);
1067 	q = r_str_trim_head_ro (q);
1068 	// TODO support parenthesis and (or)||
1069 	// split by "&&" (or comma) -> run .filter on each
1070 	// addr/gt/200,addr/lt/400,addr/sort/dec,offset/sort/inc
1071 	if (!q || !*q) {
1072 		__table_adjust (t);
1073 		return true;
1074 	}
1075 	if (*q == '?') {
1076 		const char *th = r_table_help ();
1077 		eprintf ("%s\n", th);
1078 		return false;
1079 	}
1080 
1081 	RListIter *iter;
1082 	char *qq = strdup (q);
1083 	RList *queries = r_str_split_list (qq, ",",  0);
1084 	char *query;
1085 	r_list_foreach (queries, iter, query) {
1086 		RList *q = r_str_split_list (query, "/", 2);
1087 		const char *columnName = r_list_get_n (q, 0);
1088 		const char *operation = r_list_get_n (q, 1);
1089 		const char *operand = r_list_get_n (q, 2);
1090 		if (__table_special (t, columnName)) {
1091 			continue;
1092 		}
1093 		int col = r_table_column_nth (t, columnName);
1094 		if (col == -1) {
1095 			if (columnName == NULL && strcmp (operation, "uniq")) {
1096 				eprintf ("Invalid column name (%s) for (%s)\n", columnName, query);
1097 			} else if (columnName) {
1098 				if (*columnName == '[') {
1099 					col = atoi (columnName + 1);
1100 				}
1101 			}
1102 		}
1103 		if (!operation) {
1104 			break;
1105 		}
1106 		if (!strcmp (operation, "sort")) {
1107 			r_table_sort (t, col, operand && !strcmp (operand, "dec"));
1108 		} else if (!strcmp (operation, "uniq")) {
1109 			r_table_group (t, col, NULL);
1110 		} else if (!strcmp (operation, "sortlen")) {
1111 			r_table_sortlen (t, col, operand && !strcmp (operand, "dec"));
1112 		} else if (!strcmp (operation, "join")) {
1113 			// TODO: implement join operation with other command's tables
1114 		} else if (!strcmp (operation, "sum")) {
1115 			char *op = strdup (r_str_get (operand));
1116 			RList *list = r_str_split_list (op, "/", 0);
1117 			r_list_prepend (list, strdup (columnName));
1118 			r_table_columns (t, list); // select/reorder columns
1119 			r_list_free (list);
1120 			r_table_filter (t, 0, '+', op);
1121 			free (op);
1122 		} else if (!strcmp (operation, "strlen")) {
1123 			if (operand) {
1124 				r_table_filter (t, col, 's', operand);
1125 			}
1126 		} else if (!strcmp (operation, "minlen")) {
1127 			if (operand) {
1128 				r_table_filter (t, col, 'l', operand);
1129 			}
1130 		} else if (!strcmp (operation, "maxlen")) {
1131 			if (operand) {
1132 				r_table_filter (t, col, 'L', operand);
1133 			}
1134 		} else if (!strcmp (operation, "page")) {
1135 			if (operand) {
1136 				r_table_filter (t, col, 'p', operand);
1137 			}
1138 		} else if (!strcmp (operation, "tail")) {
1139 			if (operand) {
1140 				r_table_filter (t, col, 't', operand);
1141 			}
1142 		} else if (!strcmp (operation, "head")) {
1143 			if (operand) {
1144 				r_table_filter (t, col, 'h', operand);
1145 			}
1146 		} else if (!strcmp (operation, "str")) {
1147 			if (operand) {
1148 				r_table_filter (t, col, '~', operand);
1149 			}
1150 		} else if (!strcmp (operation, "cols")) {
1151 			char *op = strdup (r_str_get (operand));
1152 			RList *list = r_str_split_list (op, "/", 0);
1153 			r_list_prepend (list, strdup (columnName));
1154 			r_table_columns (t, list); // select/reorder columns
1155 			r_list_free (list);
1156 			free (op);
1157 		// TODO	r_table_filter_columns (t, q);
1158 		} else {
1159 			int op = __resolveOperation (operation);
1160 			if (op == -1) {
1161 				eprintf ("Invalid operation (%s)\n", operation);
1162 			} else {
1163 				r_table_filter (t, col, op, operand);
1164 			}
1165 		}
1166 		r_list_free (q);
1167 	}
1168 	r_list_free (queries);
1169 	free (qq);
1170 	__table_adjust (t);
1171 	return true;
1172 }
1173 
r_table_align(RTable * t,int nth,int align)1174 R_API bool r_table_align(RTable *t, int nth, int align) {
1175 	RTableColumn *col = r_list_get_n (t->cols, nth);
1176 	if (col) {
1177 		col->align = align;
1178 		return true;
1179 	}
1180 	return false;
1181 }
1182 
r_table_hide_header(RTable * t)1183 R_API void r_table_hide_header (RTable *t) {
1184 	t->showHeader = false;
1185 }
1186 
r_table_visual_list(RTable * table,RList * list,ut64 seek,ut64 len,int width,bool va)1187 R_API void r_table_visual_list(RTable *table, RList *list, ut64 seek, ut64 len, int width, bool va) {
1188 	ut64 mul, min = -1, max = -1;
1189 	RListIter *iter;
1190 	RListInfo *info;
1191 	RCons *cons = (RCons *) table->cons;
1192 	table->showHeader = false;
1193 	const char *h_line = cons->use_utf8 ? RUNE_LONG_LINE_HORIZ : "-";
1194 	const char *block = cons->use_utf8 ? UTF_BLOCK : "#";
1195 	int j, i;
1196 	width -= 80;
1197 	if (width < 1) {
1198 		width = 30;
1199 	}
1200 
1201 	r_table_set_columnsf (table, "sssssss", "No.", "offset", "blocks", "offset", "perms", "extra", "name");
1202 	r_list_foreach (list, iter, info) {
1203 		if (min == -1 || info->pitv.addr < min) {
1204 			min = info->pitv.addr;
1205 		}
1206 		if (max == -1 || info->pitv.addr + info->pitv.size > max) {
1207 			max = info->pitv.addr + info->pitv.size;
1208 		}
1209 	}
1210 	mul = (max - min) / width;
1211 	if (min != -1 && mul > 0) {
1212 		i = 0;
1213 		r_list_foreach (list, iter, info) {
1214 			RStrBuf *buf = r_strbuf_new ("");
1215 			for (j = 0; j < width; j++) {
1216 				ut64 pos = min + j * mul;
1217 				ut64 npos = min + (j + 1) * mul;
1218 				const char *arg = (info->pitv.addr < npos && (info->pitv.addr + info->pitv.size) > pos)
1219 					? block: h_line;
1220 				r_strbuf_append (buf, arg);
1221 			}
1222 			char *b = r_strbuf_drain (buf);
1223 			if (va) {
1224 				r_table_add_rowf (table, "sssssss",
1225 					sdb_fmt ("%d%c", i, r_itv_contain (info->vitv, seek) ? '*' : ' '),
1226 					sdb_fmt ("0x%"PFMT64x, info->vitv.addr),
1227 					b,
1228 					sdb_fmt ("0x%"PFMT64x, r_itv_end (info->vitv)),
1229 					(info->perm != -1)? r_str_rwx_i (info->perm) : "",
1230 					(info->extra)?info->extra : "",
1231 					(info->name)?info->name :"");
1232 			} else {
1233 				r_table_add_rowf (table, "sssssss", sdb_fmt ("%d%c", i, r_itv_contain (info->pitv, seek) ? '*' : ' '),
1234 					sdb_fmt ("0x%"PFMT64x, info->pitv.addr), b,
1235 					sdb_fmt ("0x%"PFMT64x, r_itv_end (info->pitv)),
1236 					(info->perm != -1)? r_str_rwx_i (info->perm) : "",(info->extra)?info->extra : "", (info->name)?info->name :"");
1237 			}
1238 			free (b);
1239 			i++;
1240 		}
1241 		RStrBuf *buf = r_strbuf_new ("");
1242 		/* current seek */
1243 		if (i > 0 && len != 0) {
1244 			if (seek == UT64_MAX) {
1245 				seek = 0;
1246 			}
1247 			for (j = 0; j < width; j++) {
1248 				r_strbuf_append (buf,((j * mul) + min >= seek &&
1249 						     (j * mul) + min <= seek + len) ? "^" : h_line);
1250 			}
1251 			r_table_add_rowf (table, "sssssss", "=>", sdb_fmt ("0x%08"PFMT64x, seek),
1252 					r_strbuf_drain (buf), sdb_fmt ("0x%08"PFMT64x, seek + len), "", "", "");
1253 		} else {
1254 			r_strbuf_free (buf);
1255 		}
1256 	}
1257 }
1258 
r_table_clone(const RTable * t)1259 R_API RTable *r_table_clone(const RTable *t) {
1260 	RTable *o = r_table_new (t->name);
1261 	RTableColumn *col;
1262 	RTableRow *row;
1263 	RListIter *iter;
1264 	r_list_foreach (t->cols, iter, col) {
1265 		r_list_append (o->rows, r_table_column_clone (col));
1266 	}
1267 	r_list_foreach (t->rows, iter, row) {
1268 		r_list_append (o->rows, r_table_row_clone (row));
1269 	}
1270 	return o;
1271 }
1272 
1273 #if 0
1274 // TODO: to be implemented
1275 
1276 R_API RTable *r_table_push(RTable *t) {
1277 	// TODO: implement
1278 	return NULL;
1279 }
1280 
1281 R_API RTable *r_table_pop(RTable *t) {
1282 	// TODO: implement
1283 	return NULL;
1284 }
1285 
1286 R_API void r_table_fromjson(RTable *t, const char *csv) {
1287 	//  TODO
1288 }
1289 
1290 R_API void r_table_fromcsv(RTable *t, const char *csv) {
1291 	//  TODO
1292 }
1293 
1294 R_API char *r_table_tohtml(RTable *t) {
1295 	// TODO
1296 	return NULL;
1297 }
1298 
1299 R_API void r_table_transpose(RTable *t) {
1300 	// When the music stops rows will be cols and cols... rows!
1301 }
1302 
1303 R_API void r_table_format(RTable *t, int nth, RTableColumnType *type) {
1304 	// change the format of a specific column
1305 	// change imm base, decimal precission, ...
1306 }
1307 
1308 // to compute sum result of all the elements in a column
1309 R_API ut64 r_table_reduce(RTable *t, int nth) {
1310 	// When the music stops rows will be cols and cols... rows!
1311 	return 0;
1312 }
1313 #endif
1314