RPA Toolkit
ae635e97e709b03d9d51aa02d9c8ebbfcf3c8717
[rpatk.git] / rgrep / rpagrep.c
1 /*
2  *  Regular Pattern Analyzer (RPA)
3  *  Copyright (c) 2009-2010 Martin Stoilov
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  *  Martin Stoilov <martin@rpasearch.com>
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "rpagrep.h"
28 #include "rpagreputf.h"
29 #include "rpagrepdep.h"
30
31 #define MAX_STACK 256000
32
33 rpa_buffer_t * rpa_buffer_init(rpa_buffer_t *str, char *s, unsigned int size)
34 {
35         str->s = s;
36         str->size = size;
37         return str;
38 }
39
40
41 void rpa_buffer_free(rpa_buffer_t *str)
42 {
43         if (str) {
44                 free(str->s);
45                 free(str);
46         }       
47 }
48
49
50 rpa_buffer_t * rpa_buffer_alloc(unsigned int size)
51 {
52         rpa_buffer_t * str;
53
54         str = (rpa_buffer_t *)malloc(sizeof(rpa_buffer_t));
55         if (!str)
56                 return (void*)0;
57         memset(str, 0, sizeof(*str));
58         if (!(str->s = (char *)malloc((size + 1) * sizeof(char)))) {
59                 free(str);
60                 return (void*)0;
61         }
62         memset(str->s, 0, size + 1);
63         str->size = size;
64         str->destroy = rpa_buffer_free;
65         return str;
66 }
67
68
69 int rpa_buffer_realloc(rpa_buffer_t *str, unsigned int size)
70 {
71         char *s;
72
73         s = (char *)realloc(str->s, size);
74         if (!s)
75                 return -1;
76         str->s = s;
77         str->size = size;
78         return 0;
79 }
80
81
82 void rpa_buffer_destroy(rpa_buffer_t *str)
83 {
84         if (str && str->destroy)
85                 str->destroy(str);      
86 }
87
88
89 rpa_grep_t *rpa_grep_create()
90 {
91         rpa_grep_t *pGrep;
92         
93         pGrep = (rpa_grep_t *)malloc(sizeof(*pGrep));
94         if (!pGrep)
95                 return (void *)0;
96         memset(pGrep, 0, sizeof(*pGrep));
97         pGrep->hDbex = rpa_dbex_create();
98         return pGrep;
99 }
100
101 void rpa_grep_close(rpa_grep_t *pGrep)
102 {
103         if (pGrep->hDbex)
104                 rpa_dbex_destroy(pGrep->hDbex);
105         pGrep->hDbex = 0;       
106 }
107
108
109 void rpa_grep_optimizations(rpa_grep_t *pGrep, rulong allow)
110 {
111         rpa_dbex_cfgset(pGrep->hDbex, RPA_DBEXCFG_OPTIMIZATIONS, allow);
112 }
113
114
115 void rpa_grep_destroy(rpa_grep_t *pGrep)
116 {
117         if (!pGrep)
118                 return;
119         rpa_grep_close(pGrep);
120         free(pGrep);
121 }
122
123
124 int rpa_grep_load_string_pattern(rpa_grep_t *pGrep, rpa_buffer_t *buf)
125 {
126         return rpa_grep_load_pattern(pGrep, buf);
127 }
128
129
130 int rpa_grep_load_pattern(rpa_grep_t *pGrep, rpa_buffer_t *buf)
131 {
132         int ret, line;
133         int inputsize = buf->size;
134         const char *pattern = buf->s;
135
136         if (rpa_dbex_open(pGrep->hDbex) < 0) {
137                 fprintf(stdout, "Failed to open rules database.\n");
138                 goto error;
139         }
140
141         while ((ret = rpa_dbex_load(pGrep->hDbex, pattern, inputsize)) > 0) {
142                 inputsize -= ret;
143                 pattern += ret;
144         }
145         if (ret < 0) {
146                 rpa_errinfo_t errinfo;
147                 rpa_dbex_lasterrorinfo(pGrep->hDbex, &errinfo);
148                 if (errinfo.code == RPA_E_SYNTAX_ERROR) {
149                         pattern += errinfo.offset;
150                         for (line = 1; pattern >= buf->s; --pattern) {
151                                 if (*pattern == '\n')
152                                         line += 1;
153                         }
154                         fprintf(stdout, "Line: %d, ERROR: Syntax Error.\n", line);
155                 } else {
156                         fprintf(stdout, "ERROR: Pattern Loading failed.\n");
157                 }
158                 goto error;
159         }       
160         
161         rpa_dbex_close(pGrep->hDbex);
162         pGrep->hPattern = rpa_dbex_last(pGrep->hDbex);
163         return 0;
164         
165 error:
166         rpa_dbex_close(pGrep->hDbex);
167         return -1;
168 }
169
170
171 void rpa_grep_list_patterns(rpa_grep_t *pGrep)
172 {
173         rpa_dbex_dumpproductions(pGrep->hDbex);
174 }
175
176
177 void rpa_grep_dump_pattern_records(rpa_grep_t *pGrep)
178 {
179         rpa_dbex_dumprecords(pGrep->hDbex);
180 }
181
182
183 void rpa_grep_debug_compile(rpa_grep_t *pGrep)
184 {
185         rpa_dbex_cfgset(pGrep->hDbex, RPA_DBEXCFG_DEBUG, 1);
186         rpa_dbex_compile(pGrep->hDbex);
187         rpa_dbex_cfgset(pGrep->hDbex, RPA_DBEXCFG_DEBUG, 0);
188 }
189
190
191 void rpa_grep_dump_pattern_info(rpa_grep_t *pGrep)
192 {
193         rpa_dbex_compile(pGrep->hDbex);
194         rpa_dbex_dumpinfo(pGrep->hDbex);
195 }
196
197
198 void rpa_grep_dump_alias_info(rpa_grep_t *pGrep)
199 {
200         rpa_dbex_compile(pGrep->hDbex);
201         rpa_dbex_dumpuids(pGrep->hDbex);
202 }
203
204
205 int rpa_grep_match(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
206 {
207         int ret = 0;
208         rpastat_t *hStat;
209         const char *input = buffer, *start = buffer, *end = buffer + size;
210
211         hStat = rpa_stat_create(pGrep->hDbex, 0);
212         if (!hStat)
213                 return -1;
214         rpa_stat_cachedisable(hStat, pGrep->disablecache);
215         rpa_stat_setencoding(hStat, pGrep->encoding);
216         hStat->debug = pGrep->execdebug;
217         ret = rpa_stat_match(hStat, pGrep->hPattern, input, start, end);
218         if (ret > 0) {
219                 rpa_grep_print_filename(pGrep);
220                 rpa_grep_output(pGrep, input, ret, pGrep->encoding);
221                 rpa_grep_output_utf8_string(pGrep, "\n");
222         }
223         pGrep->cachehit = hStat->cache->hit;
224         rpa_stat_destroy(hStat);
225         return 0;
226 }
227
228
229 int rpa_grep_parse(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
230 {
231         rlong ret;
232         rlong i;
233         rchar location[128];
234         rpastat_t *hStat;
235         rarray_t *records = rpa_records_create();
236         rparecord_t *prec;
237         const char *input = buffer, *start = buffer, *end = buffer + size;
238
239         hStat = rpa_stat_create(pGrep->hDbex, 0);
240         if (!hStat)
241                 return -1;
242         rpa_stat_cachedisable(hStat, pGrep->disablecache);
243         rpa_stat_setencoding(hStat, pGrep->encoding);
244         hStat->debug = pGrep->execdebug;
245         ret = rpa_stat_parse(hStat, pGrep->hPattern, input, start, end, records);
246         if (ret < 0) {
247                 rpa_errinfo_t err;
248                 rpa_stat_lasterrorinfo(hStat, &err);
249                 if (err.code) {
250                         r_snprintf(location, sizeof(location), "Parse Error: Code: %ld", err.code);
251                         rpa_grep_output_utf8_string(pGrep, location);
252                 }
253                 if (err.ruleid) {
254                         r_snprintf(location, sizeof(location), ", Rule UID: %ld", err.ruleid);
255                         rpa_grep_output_utf8_string(pGrep, location);
256                 }
257                 if (*err.name) {
258                         r_snprintf(location, sizeof(location), ", Name: %s", err.name);
259                         rpa_grep_output_utf8_string(pGrep, location);
260                 }
261                 if (err.offset) {
262                         r_snprintf(location, sizeof(location), " at Offset: %ld", err.offset);
263                         rpa_grep_output_utf8_string(pGrep, location);
264                 }
265                 rpa_grep_output_utf8_string(pGrep, "\n");
266
267         } else {
268                 if (pGrep->greptype == RPA_GREPTYPE_PARSE) {
269                         for (i = 0; i < rpa_records_length(records); i++) {
270                                 prec = (rparecord_t *)rpa_records_slot(records, i);
271                                 if (prec->type & RPA_RECORD_END) {
272                                         rpa_grep_output_utf8_string(pGrep, prec->rule);
273                                         r_snprintf(location, sizeof(location), " (%ld, %ld)", (rlong)(prec->input - input), (rlong)prec->inputsiz);
274                                         rpa_grep_output_utf8_string(pGrep, location);
275                                         rpa_grep_output_utf8_string(pGrep, ": ");
276                                         rpa_grep_output(pGrep, prec->input, prec->inputsiz, pGrep->encoding);
277                                         rpa_grep_output_utf8_string(pGrep, "\n");
278                                 }
279                         }
280                 } else if (pGrep->greptype == RPA_GREPTYPE_PARSEAST) {
281                         for (i = 0; i < rpa_records_length(records); i++) {
282                                 rpa_record_dump(records, i);
283                         }
284
285                 }
286         }
287         rpa_records_destroy(records);
288         pGrep->cachehit = hStat->cache->hit;
289         rpa_stat_destroy(hStat);
290         return 0;
291 }
292
293
294 int rpa_grep_scan(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
295 {
296         int ret = 0;
297         rpastat_t *hStat;
298         int displayed = 0;      
299         const char *matched;
300         const char *input = buffer, *start = buffer, *end = buffer + size;
301
302         hStat = rpa_stat_create(pGrep->hDbex, 0);
303         if (!hStat)
304                 return -1;
305         rpa_stat_setencoding(hStat, pGrep->encoding);
306         rpa_stat_cachedisable(hStat, pGrep->disablecache);
307         hStat->debug = pGrep->execdebug;
308         pGrep->cachehit = hStat->cache->hit;
309
310 again:
311         ret = rpa_stat_scan(hStat, pGrep->hPattern, input, start, end, &matched);
312         pGrep->cachehit += hStat->cache->hit;
313
314         if (ret > 0) {
315                 if (!displayed) {
316                         displayed = 1;
317                         rpa_grep_print_filename(pGrep);
318                 }
319                 rpa_grep_output(pGrep, matched, ret, pGrep->encoding);
320                 rpa_grep_output_utf8_string(pGrep, "\n");
321         }
322         if (ret && matched + ret < end) {
323                 input = matched + ret;
324                 goto again;
325         }
326         rpa_stat_destroy(hStat);
327         return 0;
328 }
329
330
331 int rpa_grep_scan_lines(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
332 {
333         int ret = 0;
334         rpastat_t *hStat;
335         const char *matched;
336         int displayed = 0;
337         unsigned long lines = 0;
338         const char *end = buffer + size, *lstart = buffer, *lend;
339
340         hStat = rpa_stat_create(pGrep->hDbex, 0);
341         if (!hStat)
342                 return -1;
343         rpa_stat_setencoding(hStat, pGrep->encoding);
344         hStat->debug = pGrep->execdebug;
345         
346 again:
347         if (pGrep->encoding == RPA_ENCODING_UTF16LE || pGrep->encoding == RPA_ENCODING_ICASE_UTF16LE) {
348                 for (lend = lstart; lend < end; lend += sizeof(unsigned short)) {
349                         if (*((unsigned short*)lend) == L'\n') {
350                                 ++lines;
351                                 lend += sizeof(unsigned short);
352                                 break;
353                         }
354                 }
355         } else {
356                 for (lend = lstart; lend < end; lend += sizeof(unsigned char)) {
357                         if (*((unsigned char*)lend) == '\n') {
358                                 ++lines;
359                                 lend += sizeof(unsigned char);
360                                 break;
361                         }
362                 }
363         }
364         if (!lines)
365                 return 0;
366         ret = rpa_stat_scan(hStat, pGrep->hPattern, lstart, lstart, lend, &matched);
367         if (ret > 0) {
368                 if (!displayed) {
369                         displayed = 1;
370                         rpa_grep_print_filename(pGrep);
371                 }
372                 rpa_grep_output(pGrep, lstart, lend - lstart, pGrep->encoding);
373         }
374         if (lend < end) {
375                 lstart = lend;
376                 goto again;
377         }
378         rpa_stat_destroy(hStat);
379         return 0;
380 }
381
382
383 void rpa_grep_scan_buffer(rpa_grep_t *pGrep, rpa_buffer_t *buf)
384 {
385         const char *input;
386         unsigned long size;
387         clock_t btime, scanclocks;
388
389         if (pGrep->forceEncoding == RPA_GREP_FORCE_BYTE) {
390                 input = buf->s;
391                 size = buf->size;
392                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_BYTE : RPA_ENCODING_BYTE;
393         } else if (pGrep->forceEncoding == RPA_GREP_FORCE_UTF16) {
394                 if (buf->size >= 2 && buf->s[0] == -1 && buf->s[1] == -2) {
395                         input = buf->s + 2;
396                         size = buf->size - 2;
397                 } else {
398                         input = buf->s;
399                         size = buf->size;
400                 }
401                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_UTF16LE : RPA_ENCODING_UTF16LE;
402         } else if (buf->size >= 2 && buf->s[0] == -1 && buf->s[1] == -2) {
403                 input = buf->s + 2;
404                 size = buf->size - 2;
405                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_UTF16LE : RPA_ENCODING_UTF16LE;
406         } else {
407                 pGrep->encoding = pGrep->icase ? RPA_ENCODING_ICASE_UTF8 : RPA_ENCODING_UTF8;
408                 input = buf->s;
409                 size = buf->size;
410         }               
411
412         btime = clock();
413
414         switch (pGrep->greptype) {
415         case RPA_GREPTYPE_SCANLINES:
416                 rpa_grep_scan_lines(pGrep, input, size);
417                 break;
418         case RPA_GREPTYPE_MATCH:
419                 rpa_grep_match(pGrep, input, size);
420                 break;
421         case RPA_GREPTYPE_PARSEAST:
422         case RPA_GREPTYPE_PARSE:
423                 rpa_grep_parse(pGrep, input, size);
424                 break;
425         case RPA_GREPTYPE_SCAN:
426                 rpa_grep_scan(pGrep, input, size);
427                 break;
428         default:
429                 rpa_grep_scan(pGrep, input, size);
430                 break;
431         };
432
433         scanclocks = clock() - btime;
434         pGrep->scanmilisec += (unsigned long)(((unsigned long long)1000)*scanclocks/CLOCKS_PER_SEC);
435 }
436
437
438 rpa_buffer_t *rpa_buffer_loadfile(FILE *pFile)
439 {
440         unsigned int memchunk = 256;
441         int ret = 0, inputsize = 0;
442         rpa_buffer_t *buf;
443         
444         buf = rpa_buffer_alloc(2 * memchunk);
445         if (!buf)
446                 return (void*)0;
447         
448         do {
449                 if ((buf->size - inputsize) < memchunk) {
450                         if (rpa_buffer_realloc(buf, buf->size + memchunk) < 0) {
451                                 fprintf(stderr, "Out of memory!\n");
452                                 exit(1);
453                         }
454                 }
455                 ret = fread(&buf->s[inputsize], 1, memchunk - 1, pFile);
456                 if ((ret <= 0) && ferror(pFile)) {
457                         rpa_buffer_destroy(buf);
458                         return (void*)0;
459                 }
460                 inputsize += ret;
461                 buf->s[inputsize] = '\0';
462                 buf->size = inputsize;
463         } while (!feof(pFile)); 
464         
465         return buf;
466 }
467
468
469 int rpa_callback_output(rpastat_t * stat, const char *name, void *userdata, const char *input, unsigned int size, unsigned int reason)
470 {
471
472         return size;
473 }
474
475
476 int rpa_callback_matched_output(rpastat_t * stat, const char *name, void *userdata, const char *input, unsigned int size, unsigned int reason)
477 {
478         rpa_grep_t *pGrep = (rpa_grep_t *)userdata;
479
480         rpa_grep_output_utf8_string(pGrep, name);
481         rpa_grep_output_utf8_string(pGrep, ": ");
482         rpa_grep_output(pGrep, input, size, pGrep->encoding);
483         rpa_grep_output_utf8_string(pGrep, "\n");
484
485         return size;
486 }
487
488
489 void rpa_grep_setup_callback(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
490 {
491
492 }
493
494
495 void rpa_grep_setup_matched_callback(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
496 {
497
498 }
499
500
501 void rpa_grep_dump_pattern_tree(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
502 {
503         rpa_dbex_dumptree(pGrep->hDbex, rpa_dbex_lookup_s(pGrep->hDbex, pattern->s));
504 }
505
506
507 void rpa_grep_output(rpa_grep_t *pGrep, const char *s, unsigned long size, unsigned int encoding)
508 {
509         const unsigned char *input = (const unsigned char*)s;
510         const unsigned char *end = input + size;
511         unsigned int wc;
512         int ret;
513         
514         if (encoding == RPA_ENCODING_UTF16LE || encoding == RPA_ENCODING_ICASE_UTF16LE) {
515                 while ((ret = (int)rpa_grep_utf16_mbtowc(&wc, input, end)) != 0) {
516                         rpa_grep_output_char(wc);
517                         input += ret;
518                 }
519         } else {
520                 while ((ret = (int)rpa_grep_utf8_mbtowc(&wc, input, end)) != 0) {
521                         rpa_grep_output_char(wc);
522                         input += ret;
523                 }
524         }
525 }
526
527
528 void rpa_grep_output_utf8_string(rpa_grep_t *pGrep, const char *s)
529 {
530         rpa_grep_output(pGrep, s, strlen(s), RPA_ENCODING_UTF8);
531 }
532
533
534 void rpa_grep_output_utf16_string(rpa_grep_t *pGrep, const unsigned short *s)
535 {
536         unsigned long size = 0;
537         const unsigned short *pstr = s;
538
539         while (*pstr) {
540                 size += sizeof(unsigned short);
541                 pstr += 1;
542         }
543         rpa_grep_output(pGrep, (const char*)s, size, RPA_ENCODING_UTF16LE);
544 }