radare2/libr/cons/grep.c

477 lines
11 KiB
C

/* radare - LGPL - Copyright 2009-2015 - pancake, nibble */
#include <r_cons.h>
#include <r_util.h>
#define sdb_json_indent r_cons_json_indent
#define sdb_json_unindent r_cons_json_unindent
#include "../../shlr/sdb/src/json/indent.c"
R_API void r_cons_grep_help() {
eprintf (
"|Usage: [command]~[modifier][word,word][endmodifier][[column]][:line]\n"
"| modifiers:\n"
"| & all words must match to grep the line\n"
"| ^ words must be placed at the beginning of line\n"
"| ! negate grep\n"
"| ? count number of matching lines\n"
"| .. internal 'less'\n"
"| {} json indentation\n"
"| {}.. less json indentation\n"
"| endmodifiers:\n"
"| $ words must be placed at the end of line\n"
"| column:\n"
"| [n] show only column n\n"
"| [n-m] show column n to m\n"
"| [n-] show all columns starting from column n\n"
"| examples:\n"
"| i~:0 show fist line of 'i' output\n"
"| pd~mov disasm and grep for mov\n"
"| pi~[0] show only opcode\n"
"| i~0x400$ show lines ending with 0x400\n"
);
}
#define R_CONS_GREP_BUFSIZE 4096
R_API void r_cons_grep(const char *str) {
int wlen, len, is_range, num_is_parsed, fail = 0;
ut64 range_begin, range_end;
RCons *cons;
char buf[R_CONS_GREP_BUFSIZE];
char *ptr, *optr, *ptr2, *ptr3;
if (!str || !*str)
return;
cons = r_cons_singleton ();
memset (&(cons->grep), 0, sizeof (cons->grep));
cons->grep.line = -1;
while (*str) {
switch (*str) {
case '.':
if (str[1] == '.') {
cons->grep.less = 1;
return;
}
str++;
break;
case '{':
if (str[1] == '}') {
cons->grep.json = 1;
if (!strncmp (str, "{}..", 4))
cons->grep.less = 1;
str++;
return;
}
str++;
break;
case '&': str++; cons->grep.amp = 1; break;
case '^': str++; cons->grep.begin = 1; break;
case '!': str++; cons->grep.neg = 1; break;
case '?': str++; cons->grep.counter = 1;
if (*str == '?') {
r_cons_grep_help ();
return;
}
break;
default: goto while_end;
}
} while_end:
len = strlen (str) - 1;
if (len > R_CONS_GREP_BUFSIZE - 1) {
eprintf("r_cons_grep: too long!\n");
return;
}
if (len > 0 && str[len] == '?') {
cons->grep.counter = 1;
strncpy (buf, str, R_MIN (len, sizeof (buf) - 1));
buf[len]=0;
len--;
} else strncpy (buf, str, sizeof (buf) - 1);
if (len > 1 && buf[len] == '$' && buf[len - 1] != '\\') {
cons->grep.end = 1;
buf[len] = 0;
}
ptr = buf;
ptr2 = strchr (ptr, '[');
ptr3 = strchr (ptr, ']');
is_range = 0;
num_is_parsed = 0;
fail = 0;
range_begin = range_end = -1;
if (ptr2 && ptr3) {
ptr2[0] = '\0';
ptr2++;
for (; ptr2 <= ptr3; ++ptr2) {
if (fail) {
memset (cons->grep.tokens, 0, R_CONS_GREP_TOKENS);
cons->grep.tokens_used = 0;
fail = 0;
break;
}
switch (*ptr2) {
case '-':
is_range = 1;
num_is_parsed = 0;
break;
case ']': // fallthrough to handle ']' like ','
case ',':
for (; range_begin <= range_end; range_begin++) {
if (range_begin >= R_CONS_GREP_TOKENS) {
fail = 1;
break;
}
cons->grep.tokens[range_begin] = 1;
cons->grep.tokens_used = 1;
}
is_range = 0;
num_is_parsed = 0;
break;
default:
if (!num_is_parsed) {
if (is_range) {
range_end = r_num_get (cons->num, ptr2);
} else {
range_begin = range_end = r_num_get (cons->num, ptr2);
}
num_is_parsed = 1;
}
}
}
}
ptr2 = strchr (ptr, ':'); // line number
if (ptr2 && ptr2[1] != ':') {
*ptr2 = '\0';
cons->grep.line = r_num_get (cons->num, ptr2 + 1);
if (cons->grep.line < 0) {
eprintf ("Negative line grep not yet implented\n");
cons->grep.line = -1;
}
}
free (cons->grep.str);
if (*ptr) {
cons->grep.str = (char *)strdup (ptr);
do {
optr = ptr;
ptr = strchr (ptr, ','); // grep keywords
if (ptr) *ptr++ = '\0';
wlen = strlen (optr);
if (wlen == 0) continue;
if (wlen >= R_CONS_GREP_WORD_SIZE - 1) {
eprintf ("grep string too long\n");
continue;
}
strncpy (cons->grep.strings[cons->grep.nstrings],
optr, R_CONS_GREP_WORD_SIZE - 1);
cons->grep.nstrings++;
if (cons->grep.nstrings > R_CONS_GREP_WORDS - 1) {
eprintf ("too many grep strings\n");
break;
}
} while (ptr);
} else {
cons->grep.str = strdup (ptr);
cons->grep.nstrings++;
cons->grep.strings[0][0] = 0;
}
}
R_API int r_cons_grepbuf(char *buf, int len) {
RCons *cons = r_cons_singleton ();
char *tline, *tbuf, *p, *out, *in = buf;
int ret, buffer_len = 0, l = 0, tl = 0;
if ((len == 0 || buf == NULL || buf[0] == '\0') &&
(cons->grep.json || cons->grep.less)) {
cons->grep.json = 0;
cons->grep.less = 0;
return 0;
}
if (cons->grep.json) {
char *out = sdb_json_indent (buf);
free (cons->buffer);
cons->buffer = out;
cons->buffer_len = strlen (out);
cons->buffer_sz = cons->buffer_len + 1;
cons->grep.json = 0;
if (cons->grep.less) {
cons->grep.less = 0;
r_cons_less_str (cons->buffer, NULL);
}
return 3;
}
if (cons->grep.less) {
cons->grep.less = 0;
r_cons_less_str (buf, NULL);
buf[0] = 0;
cons->buffer_len = 0;
if (cons->buffer)
cons->buffer[0] = 0;
free (cons->buffer);
cons->buffer = NULL;
return 0;
}
if (!cons->buffer) {
cons->buffer_len = len + 20;
cons->buffer = malloc (cons->buffer_len);
cons->buffer[0] = 0;
}
out = tbuf = calloc (1, len);
tline = malloc (len);
cons->lines = 0;
while ((int)(size_t)(in-buf) < len) {
p = strchr (in, '\n');
if (!p) {
free (tbuf);
free (tline);
return 0;
}
l = p - in;
if (l > 0) {
memcpy (tline, in, l);
tl = r_str_ansi_filter (tline, NULL, NULL, l);
if (tl < 0)
ret = -1;
else ret = r_cons_grep_line (tline, tl);
if (ret > 0) {
if (cons->grep.line == -1 ||
(cons->grep.line != -1 && cons->grep.line == cons->lines)) {
memcpy (out, tline, ret);
memcpy (out + ret, "\n", 1);
out += ret + 1;
buffer_len += ret + 1;
}
cons->lines++;
} else if (ret < 0) {
free (tbuf);
free (tline);
return 0;
}
in += l + 1;
} else in++;
}
memcpy (buf, tbuf, len);
cons->buffer_len = buffer_len;
free (tbuf);
free (tline);
if (cons->grep.counter) {
if (cons->buffer_len < 10) cons->buffer_len = 10; // HACK
snprintf (cons->buffer, cons->buffer_len, "%d\n", cons->lines);
cons->buffer_len = strlen (cons->buffer);
}
return cons->lines;
}
R_API int r_cons_grep_line(char *buf, int len) {
RCons *cons = r_cons_singleton ();
const char *delims = " |,;=\t";
char *in, *out, *tok = NULL;
int hit = cons->grep.neg;
int outlen = 0;
size_t i;
in = calloc (1, len + 1);
out = calloc (1, len + 2);
memcpy (in, buf, len);
if (cons->grep.nstrings > 0) {
int ampfail = cons->grep.amp;
for (i = 0; i < cons->grep.nstrings; i++) {
char *p = strstr (in, cons->grep.strings[i]);
if (!p) {
ampfail = 0;
continue;
}
if (cons->grep.begin)
hit = (p == in) ? 1 : 0;
else hit = !cons->grep.neg;
// TODO: optimize without strlen without breaking t/feat_grep (grep end)
if (cons->grep.end && (strlen (cons->grep.strings[i]) != strlen (p)))
hit = 0 ;
if (!cons->grep.amp)
break;
}
if (cons->grep.amp)
hit = ampfail;
} else hit = 1;
if (hit) {
if ((cons->grep.line == -1 || cons->grep.line == cons->lines) &&
cons->grep.tokens_used) {
for (i = 0; i < R_CONS_GREP_TOKENS; i++) {
tok = strtok (i ? NULL : in, delims);
if (tok) {
if (cons->grep.tokens[i]) {
int toklen = strlen (tok);
memcpy (out + outlen, tok, toklen);
memcpy (out + outlen + toklen, " ", 2);
outlen += toklen + 1;
if (!(*out)) {
free (in);
free (out);
return -1;
}
}
} else {
if (!(*out)) {
free (in);
free (out);
return -1;
} else break;
}
}
outlen = outlen > 0 ? outlen - 1 : 0;
if (outlen > len) { // should never happen
eprintf ("r_cons_grep_line: wtf, how you reach this?\n");
free (in);
free (out);
return -1;
}
memcpy (buf, out, len);
len = outlen;
}
} else len = 0;
free (in);
free (out);
return len;
}
static const char *gethtmlrgb(const char *str) {
static char buf[32];
ut8 r, g, b;
r = g = b = 0;
r_cons_rgb_parse (str, &r, &g, &b, 0);
sprintf (buf, "#%02x%02x%02x", r, g, b);
return buf;
}
static const char *gethtmlcolor(const char ptrch, const char *def) {
switch (ptrch) {
case '0': return "#000"; // BLACK
case '1': return "#f00"; // RED
case '2': return "#0f0"; // GREEN
case '3': return "#ff0"; // YELLOW
case '4': return "#00f"; // BLUE
case '5': return "#f0f"; // MAGENTA
case '6': return "#aaf"; // TURQOISE
case '7': return "#fff"; // WHITE
case '8': return "#777"; // GREY
case '9': break; // ???
}
return def;
}
// XXX: rename char *r_cons_filter_html(const char *ptr)
R_API int r_cons_html_print(const char *ptr) {
const char *str = ptr;
int esc = 0;
int len = 0;
int inv = 0;
int tmp;
int tag_font = 0;
if (!ptr)
return 0;
for (;ptr[0]; ptr = ptr + 1) {
if (0 && ptr[0] == '\n') {
printf ("<br />");
fflush (stdout);
}
if (ptr[0] == 0x1b) {
esc = 1;
tmp = (int) (size_t) (ptr-str);
if (write (1, str, tmp) != tmp)
eprintf ("r_cons_html_print: write: error\n");
if (tag_font) {
printf ("</font>");
fflush(stdout);
tag_font = 0;
}
str = ptr + 1;
continue;
}
if (esc == 1) {
// \x1b[2J
if (ptr[0] != '[') {
eprintf ("Oops invalid escape char\n");
esc = 0;
str = ptr + 1;
continue;
}
esc = 2;
continue;
} else
if (esc == 2) {
// TODO: use dword comparison here
if (ptr[0] == '2' && ptr[1] == 'J') {
printf ("<hr />\n");
fflush(stdout);
ptr++;
esc = 0;
str = ptr;
continue;
} else
if (!strncmp (ptr, "38;5;", 5)) {
char *end = strchr (ptr, 'm');
printf ("<font color='%s'>", gethtmlrgb (ptr));
fflush (stdout);
tag_font = 1;
ptr = end;
str = ptr + 1;
esc = 0;
} else
if (ptr[0] == '0' && ptr[1] == ';' && ptr[2] == '0') {
r_cons_gotoxy (0, 0);
ptr += 4;
esc = 0;
str = ptr;
continue;
} else
if (ptr[0] == '0' && ptr[1] == 'm') {
str = (++ptr) + 1;
esc = inv = 0;
continue;
// reset color
} else
if (ptr[0] == '7' && ptr[1] == 'm') {
str = (++ptr) + 1;
inv = 128;
esc = 0;
continue;
// reset color
} else
if (ptr[0] == '3' && ptr[2] == 'm') {
printf ("<font color='%s'>", gethtmlcolor (ptr[1], inv ? "#fff" : "#000"));
fflush(stdout);
tag_font = 1;
ptr = ptr + 1;
str = ptr + 2;
esc = 0;
continue;
} else
if (ptr[0] == '4' && ptr[2] == 'm') {
printf ("<font style='background-color:%s'>",
gethtmlcolor (ptr[1], inv ? "#000" : "#fff"));
fflush(stdout);
}
}
len++;
}
write (1, str, ptr - str);
return len;
}