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