1 /* radare - LGPL - Copyright 2009-2020 - pancake, nibble */
2
3 #include <r_cons.h>
4 #include <r_util/r_print.h>
5 #include <sdb.h>
6
7 #define I(x) r_cons_singleton ()->x
8
strchr_ns(char * s,const char ch)9 static char *strchr_ns (char *s, const char ch) {
10 char *p = strchr (s, ch);
11 if (p && p > s) {
12 char *prev = p - 1;
13 if (*prev == '\\') {
14 memmove (prev, p, strlen (p) + 1);
15 return strchr_ns (p, ch);
16 }
17 }
18 return p;
19 }
20
21 static const char *help_detail_tilde[] = {
22 "Usage: [command]~[modifier][word,word][endmodifier][[column]][:line]\n"
23 "modifier:", "", "",
24 " &", "", "all words must match to grep the line",
25 " $[n]", "", "sort numerically / alphabetically the Nth column",
26 " $!", "", "sort in inverse order",
27 " ,", "", "token to define another keyword",
28 " +", "", "case insensitive grep (grep -i)",
29 " ^", "", "words must be placed at the beginning of line",
30 " <", "", "perform zoom operation on the buffer",
31 " !", "", "negate grep",
32 " ?", "", "count number of matching lines",
33 " ?.", "", "count number chars",
34 " ??", "", "show this help message",
35 " :s..e", "", "show lines s-e",
36 " ..", "", "internal 'less'",
37 " ...", "", "internal 'hud' (like V_)",
38 " {:", "", "human friendly indentation (yes, it's a smiley)",
39 " {:..", "", "less the output of {:",
40 " {:...", "", "hud the output of {:",
41 " {}", "", "json indentation",
42 " {}..", "", "less json indentation",
43 " {}...", "", "hud json indentation",
44 " {path}", "", "json path grep",
45 "endmodifier:", "", "",
46 " $", "", "words must be placed at the end of line",
47 "column:", "", "",
48 " [n]", "", "show only column n",
49 " [n-m]", "", "show column n to m",
50 " [n-]", "", "show all columns starting from column n",
51 " [i,j,k]", "", "show the columns i, j and k",
52 "Examples:", "", "",
53 " i~:0", "", "show first line of 'i' output",
54 " i~:-2", "", "show the second to last line of 'i' output",
55 " i~:0..3", "", "show first three lines of 'i' output",
56 " pd~mov", "", "disasm and grep for mov",
57 " pi~[0]", "", "show only opcode",
58 " i~0x400$", "", "show lines ending with 0x400",
59 NULL
60 };
61
62 /* TODO: remove globals */
63 static RList *sorted_lines = NULL;
64 static RList *unsorted_lines = NULL;
65 static int sorted_column = -1;
66
r_cons_grep_help(void)67 R_API void r_cons_grep_help(void) {
68 r_cons_cmd_help (help_detail_tilde, true);
69 }
70
71 #define R_CONS_GREP_BUFSIZE 4096
72
parse_grep_expression(const char * str)73 static void parse_grep_expression(const char *str) {
74 static char buf[R_CONS_GREP_BUFSIZE];
75 int wlen, len, is_range, num_is_parsed, fail = 0;
76 char *ptr, *optr, *ptr2, *ptr3, *end_ptr = NULL, last;
77 ut64 range_begin, range_end;
78
79 if (!str || !*str) {
80 return;
81 }
82 RCons *cons = r_cons_singleton ();
83 RConsGrep *grep = &cons->context->grep;
84 sorted_column = 0;
85 bool first = true;
86 while (*str) {
87 switch (*str) {
88 case '.':
89 if (str[1] == '.') {
90 if (str[2] == '.') {
91 grep->less = 2;
92 } else {
93 grep->less = 1;
94 }
95 return;
96 }
97 str++;
98 break;
99 case '{':
100 if (str[1] == ':') {
101 grep->human = true; // human friendly indentation ij~{:
102 grep->json = 1;
103 if (!strncmp (str, "{:...", 5)) {
104 grep->hud = true;
105 } else if (!strncmp (str, "{:..", 4)) {
106 grep->less = 1;
107 }
108 } else if (str[1] == '}') {
109 // standard json indentation
110 grep->json = 1;
111 if (!strncmp (str, "{}...", 5)) {
112 grep->hud = true;
113 } else if (!strncmp (str, "{}..", 4)) {
114 grep->less = 1;
115 }
116 } else {
117 char *jsonPath = strdup (str + 1);
118 char *jsonPathEnd = strchr (jsonPath, '}');
119 if (jsonPathEnd) {
120 *jsonPathEnd = 0;
121 free (grep->json_path);
122 grep->json_path = jsonPath;
123 grep->json = 1;
124 } else {
125 free (jsonPath);
126 }
127 return;
128 }
129 str++;
130 break;
131 case '$':
132 str++;
133 if (*str == '!') {
134 grep->sort_invert = true;
135 str++;
136 } else {
137 grep->sort_invert = false;
138 }
139 grep->sort = atoi (str);
140 while (IS_DIGIT (*str)) {
141 str++;
142 }
143 if (*str == ':') {
144 grep->sort_row = atoi (++str);
145 str++;
146 }
147 break;
148 case '&':
149 str++;
150 grep->amp = 1;
151 break;
152 case '<':
153 grep->zoom = atoi (++str);
154 //grep->zoomy = atoi (arg);
155 break;
156 case '+':
157 if (first) {
158 str++;
159 grep->icase = 1;
160 } else {
161 goto while_end;
162 }
163 break;
164 case '^':
165 str++;
166 grep->begin = 1;
167 break;
168 case '!':
169 str++;
170 grep->neg = 1;
171 break;
172 case '?':
173 str++;
174 grep->counter = 1;
175 if (*str == '.') {
176 grep->charCounter = true;
177 str++;
178 } else if (*str == '?') {
179 cons->filter = true;
180 r_cons_grep_help ();
181 return;
182 }
183 break;
184 default:
185 goto while_end;
186 }
187 first = false;
188 }
189 while_end:
190
191 len = strlen (str) - 1;
192 if (len > R_CONS_GREP_BUFSIZE - 1) {
193 eprintf ("r_cons_grep: too long!\n");
194 return;
195 }
196 if (len > 0 && str[len] == '?') {
197 grep->counter = 1;
198 r_str_ncpy (buf, str, R_MIN (len, sizeof (buf) - 1));
199 buf[len] = 0;
200 len--;
201 } else {
202 r_str_ncpy (buf, str, sizeof (buf) - 1);
203 }
204
205 ptr = buf;
206 ptr2 = strchr (ptr, '[');
207 ptr3 = strchr (ptr, ']');
208 is_range = 0;
209 num_is_parsed = 0;
210 fail = 0;
211 range_begin = range_end = -1;
212
213 if (ptr2 && ptr3) {
214 end_ptr = ptr2;
215 last = ptr3[1];
216 ptr3[1] = '\0';
217 ptr2++;
218 for (; ptr2 <= ptr3; ptr2++) {
219 if (fail) {
220 ZERO_FILL (grep->tokens);
221 grep->tokens_used = 0;
222 break;
223 }
224 switch (*ptr2) {
225 case '-':
226 is_range = 1;
227 num_is_parsed = 0;
228 range_end = -1;
229 break;
230 case ']': // fallthrough to handle ']' like ','
231 case ',':
232 for (; range_begin <= range_end; range_begin++) {
233 if (range_begin >= R_CONS_GREP_TOKENS) {
234 fail = 1;
235 break;
236 }
237 grep->tokens[range_begin] = 1;
238 grep->tokens_used = 1;
239 }
240 // case of [n-]
241 if (*ptr2 == ']' && is_range && !num_is_parsed) {
242 num_is_parsed = 1;
243 range_end = -1;
244 } else {
245 is_range = 0;
246 num_is_parsed = 0;
247 }
248 break;
249 default:
250 if (!num_is_parsed) {
251 if (is_range) {
252 range_end = r_num_get (cons->num, ptr2);
253 // check for bad value, if range_end == 0, we check if ptr2 == '0'
254 if (range_end == 0 && *ptr != '0') {
255 range_end = -1; // this allow [n- ]
256 }
257 } else {
258 range_begin = range_end = r_num_get (cons->num, ptr2);
259 }
260 num_is_parsed = 1;
261 }
262 }
263 }
264 ptr3[1] = last;
265 }
266
267 ptr2 = strchr_ns (ptr, ':'); // line number
268 grep->range_line = 2; // there is not :
269 if (ptr2 && ptr2[1] != ':' && ptr2[1] && (IS_DIGIT (ptr2[1]) || ptr2[1] == '-' || ptr2[1] == '.')) {
270 end_ptr = end_ptr ? R_MIN (end_ptr, ptr2) : ptr2;
271 char *p, *token = ptr2 + 1;
272 p = strstr (token, "..");
273 if (!p) {
274 grep->line = r_num_get (cons->num, ptr2 + 1);
275 grep->range_line = 0;
276 } else {
277 *p = '\0';
278 grep->range_line = 1;
279 if (*token) {
280 grep->f_line = r_num_get (cons->num, token);
281 } else {
282 grep->f_line = 0;
283 }
284 if (p[2]) {
285 grep->l_line = r_num_get (cons->num, p + 2);
286 } else {
287 grep->l_line = 0;
288 }
289 }
290 }
291 if (end_ptr) {
292 *end_ptr = '\0';
293 }
294
295 len = strlen (buf) - 1;
296 if (len > 1 && buf[len] == '$' && buf[len - 1] != '\\') {
297 grep->end = 1;
298 buf[len] = '\0';
299 }
300
301 free (grep->str);
302 if (*ptr) {
303 grep->str = (char *) strdup (ptr);
304 do {
305 optr = ptr;
306 ptr = strchr (ptr, ','); // grep keywords
307 if (ptr) {
308 *ptr++ = '\0';
309 }
310 wlen = strlen (optr);
311 if (!wlen) {
312 continue;
313 }
314 if (wlen >= R_CONS_GREP_WORD_SIZE - 1) {
315 eprintf ("grep string too long\n");
316 continue;
317 }
318 grep->nstrings++;
319 if (grep->nstrings > R_CONS_GREP_WORDS - 1) {
320 eprintf ("too many grep strings\n");
321 break;
322 }
323 r_str_ncpy (grep->strings[grep->nstrings - 1],
324 optr, R_CONS_GREP_WORD_SIZE);
325 } while (ptr);
326 } else {
327 grep->str = strdup (ptr);
328 grep->nstrings++;
329 grep->strings[0][0] = 0;
330 }
331 }
332
333 // Finds and returns next intgerp expression,
334 // unescapes escaped twiddles
find_next_intgrep(char * cmd,const char * quotes)335 static char *find_next_intgrep(char *cmd, const char *quotes) {
336 char *p;
337 do {
338 p = (char *)r_str_firstbut (cmd, '~', quotes);
339 if (!p) {
340 break;
341 }
342 if (p == cmd || *(p - 1) != '\\') {
343 return (char*)p;
344 }
345 //twiddle unescape
346 memmove (p - 1, p, strlen(p) + 1);
347 cmd = p + 1;
348 } while (*cmd);
349 return NULL;
350 }
351
352 /*
353 * Removes grep part from *cmd* and returns newly allocated string
354 * with reshaped grep expression.
355 *
356 * Function converts multiple twiddle expressions into internal representation.
357 * For example:
358 * converts "~str1~str2~str3~?" into "?&str1,str2,str3"
359 */
preprocess_filter_expr(char * cmd,const char * quotes)360 static char *preprocess_filter_expr(char *cmd, const char *quotes) {
361 char *p1, *p2, *ns = NULL;
362 const char *strsep = "&";
363 int len;
364 int i;
365
366 p1 = find_next_intgrep (cmd, quotes);
367 if (!p1) {
368 return NULL;
369 }
370
371 len = strlen (p1);
372 if (len > 4 && r_str_endswith (p1, "~?") && p1[len - 3] != '\\') {
373 p1[len - 2] = '\0';
374 ns = r_str_append (ns, "?");
375 }
376
377 *p1 = '\0'; // remove grep part from cmd
378
379 i = 0;
380 // parse words between '~'
381 while ((p2 = find_next_intgrep (p1 + 1, quotes))) {
382 ns = r_str_append (ns, strsep);
383 ns = r_str_appendlen (ns, p1 + 1, (int)(p2 - p1 - 1));
384 p1 = p2;
385 strsep = ",";
386 i++;
387 }
388
389 if (i > 0) {
390 ns = r_str_append (ns, ",");
391 }
392
393 ns = r_str_append (ns, p1 + 1);
394
395 return ns;
396 }
397
r_cons_grep_parsecmd(char * cmd,const char * quotestr)398 R_API void r_cons_grep_parsecmd(char *cmd, const char *quotestr) {
399 r_return_if_fail (cmd && quotestr);
400 char *ptr = preprocess_filter_expr (cmd, quotestr);
401 if (ptr) {
402 r_str_trim (cmd);
403 parse_grep_expression (ptr);
404 free (ptr);
405 }
406 }
407
r_cons_grep_strip(char * cmd,const char * quotestr)408 R_API char *r_cons_grep_strip(char *cmd, const char *quotestr) {
409 char *ptr = NULL;
410
411 if (cmd) {
412 ptr = preprocess_filter_expr (cmd, quotestr);
413 r_str_trim (cmd);
414 }
415 return ptr;
416 }
417
r_cons_grep_process(char * grep)418 R_API void r_cons_grep_process(char * grep) {
419 if (grep) {
420 parse_grep_expression (grep);
421 free (grep);
422 }
423 }
424
cmp(const void * a,const void * b)425 static int cmp(const void *a, const void *b) {
426 char *da = NULL;
427 char *db = NULL;
428 const char *ca = r_str_trim_head_ro (a);
429 const char *cb = r_str_trim_head_ro (b);
430 if (!a || !b) {
431 return (int) (size_t) ((char*) a - (char*) b);
432 }
433 if (sorted_column > 0) {
434 da = strdup (ca);
435 db = strdup (cb);
436 int colsa = r_str_word_set0 (da);
437 int colsb = r_str_word_set0 (db);
438 ca = (colsa > sorted_column)? r_str_word_get0 (da, sorted_column): "";
439 cb = (colsb > sorted_column)? r_str_word_get0 (db, sorted_column): "";
440 }
441 if (IS_DIGIT (*ca) && IS_DIGIT (*cb)) {
442 ut64 na = r_num_get (NULL, ca);
443 ut64 nb = r_num_get (NULL, cb);
444 int ret = (na > nb) - (na < nb);
445 free (da);
446 free (db);
447 return ret;
448 }
449 if (da && db) {
450 int ret = strcmp (ca, cb);
451 free (da);
452 free (db);
453 return ret;
454 }
455 free (da);
456 free (db);
457 return strcmp (a, b);
458 }
459
r_cons_grepbuf(void)460 R_API void r_cons_grepbuf(void) {
461 RCons *cons = r_cons_singleton ();
462 const char *buf = cons->context->buffer;
463 const int len = cons->context->buffer_len;
464 RConsGrep *grep = &cons->context->grep;
465 const char *in = buf;
466 int ret, total_lines = 0, buffer_len = 0, l = 0, tl = 0;
467 bool show = false;
468 if (cons->filter) {
469 cons->context->buffer_len = 0;
470 R_FREE (cons->context->buffer);
471 return;
472 }
473
474 if ((!len || !buf || buf[0] == '\0') && (grep->json || grep->less)) {
475 grep->json = 0;
476 grep->less = 0;
477 grep->hud = 0;
478 return;
479 }
480
481 if (grep->zoom) {
482 char *in = calloc (cons->context->buffer_len + 2, 4);
483 strcpy (in, cons->context->buffer);
484 char *out = r_str_scale (in, grep->zoom * 2, grep->zoomy?grep->zoomy:grep->zoom);
485 if (out) {
486 free (cons->context->buffer);
487 cons->context->buffer = out;
488 cons->context->buffer_len = strlen (out);
489 cons->context->buffer_sz = cons->context->buffer_len;
490 }
491 grep->zoom = 0;
492 grep->zoomy = 0;
493 free (in);
494 return;
495 }
496 if (grep->json) {
497 if (grep->json_path) {
498 char *u = sdb_json_get_str (cons->context->buffer, grep->json_path);
499 if (u) {
500 cons->context->buffer = u;
501 cons->context->buffer_len = strlen (u);
502 cons->context->buffer_sz = cons->context->buffer_len + 1;
503 grep->json = 0;
504 r_cons_newline ();
505 }
506 R_FREE (grep->json_path);
507 } else {
508 const char *palette[] = {
509 cons->context->pal.graph_false, // f
510 cons->context->pal.graph_true, // t
511 cons->context->pal.num, // k
512 cons->context->pal.comment, // v
513 Color_RESET,
514 NULL
515 };
516 char *bb = strdup (buf);
517 r_str_ansi_filter (bb, NULL, NULL, -1);
518 char *out = (cons->context->grep.human)
519 ? r_print_json_human (bb)
520 : r_print_json_indent (bb, I (context->color_mode), " ", palette);
521 free (bb);
522 if (!out) {
523 return;
524 }
525 free (cons->context->buffer);
526 cons->context->buffer = out;
527 cons->context->buffer_len = strlen (out);
528 cons->context->buffer_sz = cons->context->buffer_len + 1;
529 grep->json = 0;
530 if (grep->hud) {
531 grep->hud = false;
532 r_cons_hud_string (cons->context->buffer);
533 } else if (grep->less) {
534 grep->less = 0;
535 r_cons_less_str (cons->context->buffer, NULL);
536 }
537 }
538 return;
539 // cons->lines = ?? return 3;
540 }
541 if (grep->less) {
542 int less = grep->less;
543 grep->less = 0;
544 if (less == 2) {
545 char *res = r_cons_hud_string (buf);
546 if (res) {
547 r_cons_println (res);
548 free (res);
549 }
550 } else {
551 r_cons_less_str (buf, NULL);
552 cons->context->buffer_len = 0;
553 if (cons->context->buffer) {
554 cons->context->buffer[0] = 0;
555 }
556 R_FREE (cons->context->buffer);
557 }
558 return;
559 }
560 if (!cons->context->buffer) {
561 cons->context->buffer_len = len + 20;
562 cons->context->buffer = malloc (cons->context->buffer_len);
563 cons->context->buffer[0] = 0;
564 }
565 RStrBuf *ob = r_strbuf_new ("");
566 // if we modify cons->lines we should update I.context->buffer too
567 cons->lines = 0;
568 // used to count lines and change negative grep.line values
569 while ((int) (size_t) (in - buf) < len) {
570 char *p = strchr (in, '\n');
571 if (!p) {
572 break;
573 }
574 l = p - in;
575 if (l > 0) {
576 in += l + 1;
577 } else {
578 in++;
579 }
580 total_lines++;
581 }
582 if (!grep->range_line && grep->line < 0) {
583 grep->line = total_lines + grep->line;
584 }
585 if (grep->range_line == 1) {
586 if (grep->f_line < 0) {
587 grep->f_line = total_lines + grep->f_line;
588 }
589 if (grep->l_line <= 0) {
590 grep->l_line = total_lines + grep->l_line;
591 }
592 }
593 bool is_range_line_grep_only = grep->range_line != 2 && !*grep->str;
594 in = buf;
595 while ((int) (size_t) (in - buf) < len) {
596 char *p = strchr (in, '\n');
597 if (!p) {
598 break;
599 }
600 l = p - in;
601 if ((!l && is_range_line_grep_only) || l > 0) {
602 char *tline = r_str_ndup (in, l);
603 if (cons->grep_color) {
604 tl = l;
605 } else {
606 tl = r_str_ansi_filter (tline, NULL, NULL, l);
607 }
608 if (tl < 0) {
609 ret = -1;
610 } else {
611 ret = r_cons_grep_line (tline, tl);
612 if (!grep->range_line) {
613 if (grep->line == cons->lines) {
614 show = true;
615 }
616 } else if (grep->range_line == 1) {
617 if (grep->f_line == cons->lines) {
618 show = true;
619 }
620 if (grep->l_line == cons->lines) {
621 show = false;
622 }
623 } else {
624 show = true;
625 }
626 }
627 if ((!ret && is_range_line_grep_only) || ret > 0) {
628 if (show) {
629 char *str = r_str_ndup (tline, ret);
630 if (cons->grep_highlight) {
631 int i;
632 for (i = 0; i < grep->nstrings; i++) {
633 char *newstr = r_str_newf (Color_INVERT"%s"Color_RESET, grep->strings[i]);
634 if (str && newstr) {
635 if (grep->icase) {
636 str = r_str_replace_icase (str, grep->strings[i], newstr, 1, 1);
637 } else {
638 str = r_str_replace (str, grep->strings[i], newstr, 1);
639 }
640 }
641 free (newstr);
642 }
643 }
644 if (str) {
645 r_strbuf_append (ob, str);
646 r_strbuf_append (ob, "\n");
647 }
648 buffer_len += ret + 1;
649 free (str);
650 }
651 if (!grep->range_line) {
652 show = false;
653 }
654 cons->lines++;
655 } else if (ret < 0) {
656 free (tline);
657 return;
658 }
659 free (tline);
660 in += l + 1;
661 } else {
662 in++;
663 }
664 }
665
666 cons->context->buffer_len = r_strbuf_length (ob);
667 if (grep->counter) {
668 int cnt = grep->charCounter? strlen (cons->context->buffer): cons->lines;
669 if (cons->context->buffer_len < 10) {
670 cons->context->buffer_len = 10; // HACK
671 }
672 snprintf (cons->context->buffer, cons->context->buffer_len, "%d\n", cnt);
673 cons->context->buffer_len = strlen (cons->context->buffer);
674 cons->num->value = cons->lines;
675 r_strbuf_free (ob);
676 return;
677 }
678
679 const int ob_len = r_strbuf_length (ob);
680 if (ob_len >= cons->context->buffer_sz) {
681 cons->context->buffer_sz = ob_len + 1;
682 cons->context->buffer = r_strbuf_drain (ob);
683 } else {
684 memcpy (cons->context->buffer, r_strbuf_getbin (ob, NULL), ob_len);
685 cons->context->buffer[ob_len] = 0;
686 r_strbuf_free (ob);
687 }
688 cons->context->buffer_len = ob_len;
689
690 if (grep->sort != -1) {
691 #define INSERT_LINES(list)\
692 do {\
693 r_list_foreach (list, iter, str) {\
694 int len = strlen (str);\
695 memcpy (ptr, str, len);\
696 memcpy (ptr + len, "\n", 2);\
697 ptr += len + 1;\
698 nl++;\
699 }\
700 }\
701 while (false)
702
703 RListIter *iter;
704 int nl = 0;
705 char *ptr = cons->context->buffer;
706 char *str;
707 sorted_column = grep->sort;
708 r_list_sort (sorted_lines, cmp);
709 if (grep->sort_invert) {
710 r_list_reverse (sorted_lines);
711 }
712 INSERT_LINES (unsorted_lines);
713 INSERT_LINES (sorted_lines);
714 cons->lines = nl;
715 r_list_free (sorted_lines);
716 sorted_lines = NULL;
717 r_list_free (unsorted_lines);
718 unsorted_lines = NULL;
719 }
720 }
721
r_cons_grep_line(char * buf,int len)722 R_API int r_cons_grep_line(char *buf, int len) {
723 RCons *cons = r_cons_singleton ();
724 RConsGrep *grep = &cons->context->grep;
725 const char *delims = " |,;=\t";
726 char *tok = NULL;
727 bool hit = grep->neg;
728 int outlen = 0;
729 bool use_tok = false;
730 size_t i;
731
732 char *in = calloc (1, len + 1);
733 if (!in) {
734 return 0;
735 }
736 char *out = calloc (1, len + 2);
737 if (!out) {
738 free (in);
739 return 0;
740 }
741 memcpy (in, buf, len);
742
743 if (grep->nstrings > 0) {
744 int ampfail = grep->amp;
745 if (grep->icase) {
746 r_str_case (in, false);
747 }
748 for (i = 0; i < grep->nstrings; i++) {
749 char *str = grep->strings[i];
750 if (grep->icase) {
751 r_str_case (str, false);
752 }
753 const char *p = r_strstr_ansi (in, grep->strings[i]);
754 if (!p) {
755 ampfail = 0;
756 continue;
757 }
758 if (grep->begin) {
759 hit = (p == in);
760 } else {
761 hit = !grep->neg;
762 }
763 // TODO: optimize without strlen without breaking t/feat_grep (grep end)
764 if (grep->end && (strlen (grep->strings[i]) != strlen (p))) {
765 hit = 0;
766 }
767 if (!grep->amp) {
768 break;
769 }
770 }
771 if (grep->amp) {
772 hit = ampfail;
773 }
774 } else {
775 hit = 1;
776 }
777
778 if (hit) {
779 if (!grep->range_line) {
780 if (grep->line == cons->lines) {
781 use_tok = true;
782 }
783 } else if (grep->range_line == 1) {
784 use_tok = R_BETWEEN (grep->f_line, cons->lines, grep->l_line);
785 } else {
786 use_tok = true;
787 }
788 if (use_tok && grep->tokens_used) {
789 for (i = 0; i < R_CONS_GREP_TOKENS; i++) {
790 tok = strtok (i? NULL: in, delims);
791 if (tok) {
792 if (grep->tokens[i]) {
793 int toklen = strlen (tok);
794 memcpy (out + outlen, tok, toklen);
795 memcpy (out + outlen + toklen, " ", 2);
796 outlen += toklen + 1;
797 if (!(*out)) {
798 free (in);
799 free (out);
800 return -1;
801 }
802 }
803 } else {
804 if ((*out)) {
805 break;
806 }
807 free (in);
808 free (out);
809 return 0;
810 }
811 }
812 outlen = outlen > 0? outlen - 1: 0;
813 if (outlen > len) { // should never happen
814 eprintf ("r_cons_grep_line: wtf, how you reach this?\n");
815 free (in);
816 free (out);
817 return -1;
818 }
819 memcpy (buf, out, len);
820 len = outlen;
821 }
822 } else {
823 len = 0;
824 }
825 free (in);
826 free (out);
827 if (grep->sort != -1) {
828 char ch = buf[len];
829 buf[len] = 0;
830 if (!sorted_lines) {
831 sorted_lines = r_list_newf (free);
832 }
833 if (!unsorted_lines) {
834 unsorted_lines = r_list_newf (free);
835 }
836 if (cons->lines >= grep->sort_row) {
837 r_list_append (sorted_lines, strdup (buf));
838 } else {
839 r_list_append (unsorted_lines, strdup (buf));
840 }
841 buf[len] = ch;
842 }
843
844 return len;
845 }
846
r_cons_grep(const char * grep)847 R_API void r_cons_grep(const char *grep) {
848 parse_grep_expression (grep);
849 r_cons_grepbuf ();
850 }
851