RPA Toolkit
Refined the rpastat_t public interface. Added more API documentation.
[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         hStat->debug = pGrep->execdebug;
216         ret = rpa_stat_match(hStat, pGrep->hPattern, pGrep->encoding, input, start, end);
217         if (ret > 0) {
218                 rpa_grep_print_filename(pGrep);
219                 rpa_grep_output(pGrep, input, ret, pGrep->encoding);
220                 rpa_grep_output_utf8_string(pGrep, "\n");
221         }
222         pGrep->cachehit = hStat->cache->hit;
223         rpa_stat_destroy(hStat);
224         return 0;
225 }
226
227
228 int rpa_grep_parse(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
229 {
230         rlong ret;
231         rlong i;
232         rchar location[128];
233         rpastat_t *hStat;
234         rarray_t *records = rpa_records_create();
235         rparecord_t *prec;
236         const char *input = buffer, *start = buffer, *end = buffer + size;
237
238         hStat = rpa_stat_create(pGrep->hDbex, 0);
239         if (!hStat)
240                 return -1;
241         rpa_stat_cachedisable(hStat, pGrep->disablecache);
242         hStat->debug = pGrep->execdebug;
243         ret = rpa_stat_parse(hStat, pGrep->hPattern, pGrep->encoding, input, start, end, records);
244         if (ret < 0) {
245                 rpa_errinfo_t err;
246                 rpa_stat_lasterrorinfo(hStat, &err);
247                 if (err.code) {
248                         r_snprintf(location, sizeof(location), "Parse Error: Code: %ld", err.code);
249                         rpa_grep_output_utf8_string(pGrep, location);
250                 }
251                 if (err.ruleid) {
252                         r_snprintf(location, sizeof(location), ", Rule UID: %ld", err.ruleid);
253                         rpa_grep_output_utf8_string(pGrep, location);
254                 }
255                 if (*err.name) {
256                         r_snprintf(location, sizeof(location), ", Name: %s", err.name);
257                         rpa_grep_output_utf8_string(pGrep, location);
258                 }
259                 if (err.offset) {
260                         r_snprintf(location, sizeof(location), " at Offset: %ld", err.offset);
261                         rpa_grep_output_utf8_string(pGrep, location);
262                 }
263                 rpa_grep_output_utf8_string(pGrep, "\n");
264
265         } else {
266                 if (pGrep->greptype == RPA_GREPTYPE_PARSE) {
267                         for (i = 0; i < rpa_records_length(records); i++) {
268                                 prec = (rparecord_t *)rpa_records_slot(records, i);
269                                 if (prec->type & RPA_RECORD_END) {
270                                         rpa_grep_output_utf8_string(pGrep, prec->rule);
271                                         r_snprintf(location, sizeof(location), " (%ld, %ld)", (rlong)(prec->input - input), (rlong)prec->inputsiz);
272                                         rpa_grep_output_utf8_string(pGrep, location);
273                                         rpa_grep_output_utf8_string(pGrep, ": ");
274                                         rpa_grep_output(pGrep, prec->input, prec->inputsiz, pGrep->encoding);
275                                         rpa_grep_output_utf8_string(pGrep, "\n");
276                                 }
277                         }
278                 } else if (pGrep->greptype == RPA_GREPTYPE_PARSEAST) {
279                         for (i = 0; i < rpa_records_length(records); i++) {
280                                 rpa_record_dump(records, i);
281                         }
282
283                 }
284         }
285         rpa_records_destroy(records);
286         pGrep->cachehit = hStat->cache->hit;
287         rpa_stat_destroy(hStat);
288         return 0;
289 }
290
291
292 int rpa_grep_scan(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
293 {
294         int ret = 0;
295         rpastat_t *hStat;
296         int displayed = 0;      
297         const char *matched;
298         const char *input = buffer, *start = buffer, *end = buffer + size;
299
300         hStat = rpa_stat_create(pGrep->hDbex, 0);
301         if (!hStat)
302                 return -1;
303         rpa_stat_cachedisable(hStat, pGrep->disablecache);
304         hStat->debug = pGrep->execdebug;
305         pGrep->cachehit = hStat->cache->hit;
306
307 again:
308         ret = rpa_stat_scan(hStat, pGrep->hPattern, pGrep->encoding, input, start, end, &matched);
309         pGrep->cachehit += hStat->cache->hit;
310
311         if (ret > 0) {
312                 if (!displayed) {
313                         displayed = 1;
314                         rpa_grep_print_filename(pGrep);
315                 }
316                 rpa_grep_output(pGrep, matched, ret, pGrep->encoding);
317                 rpa_grep_output_utf8_string(pGrep, "\n");
318         }
319         if (ret && matched + ret < end) {
320                 input = matched + ret;
321                 goto again;
322         }
323         rpa_stat_destroy(hStat);
324         return 0;
325 }
326
327
328 int rpa_grep_scan_lines(rpa_grep_t *pGrep, const char* buffer, unsigned long size)
329 {
330         int ret = 0;
331         rpastat_t *hStat;
332         const char *matched;
333         int displayed = 0;
334         unsigned long lines = 0;
335         const char *end = buffer + size, *lstart = buffer, *lend;
336
337         hStat = rpa_stat_create(pGrep->hDbex, 0);
338         if (!hStat)
339                 return -1;
340         hStat->debug = pGrep->execdebug;
341         
342 again:
343         if (pGrep->encoding == RPA_ENCODING_UTF16LE || pGrep->encoding == RPA_ENCODING_ICASE_UTF16LE) {
344                 for (lend = lstart; lend < end; lend += sizeof(unsigned short)) {
345                         if (*((unsigned short*)lend) == L'\n') {
346                                 ++lines;
347                                 lend += sizeof(unsigned short);
348                                 break;
349                         }
350                 }
351         } else {
352                 for (lend = lstart; lend < end; lend += sizeof(unsigned char)) {
353                         if (*((unsigned char*)lend) == '\n') {
354                                 ++lines;
355                                 lend += sizeof(unsigned char);
356                                 break;
357                         }
358                 }
359         }
360         if (!lines)
361                 return 0;
362         ret = rpa_stat_scan(hStat, pGrep->hPattern, pGrep->encoding, lstart, lstart, lend, &matched);
363         if (ret > 0) {
364                 if (!displayed) {
365                         displayed = 1;
366                         rpa_grep_print_filename(pGrep);
367                 }
368                 rpa_grep_output(pGrep, lstart, lend - lstart, pGrep->encoding);
369         }
370         if (lend < end) {
371                 lstart = lend;
372                 goto again;
373         }
374         rpa_stat_destroy(hStat);
375         return 0;
376 }
377
378
379 void rpa_grep_scan_buffer(rpa_grep_t *pGrep, rpa_buffer_t *buf)
380 {
381         const char *input;
382         unsigned long size;
383         clock_t btime, scanclocks;
384
385         if (pGrep->forceEncoding == RPA_GREP_FORCE_BYTE) {
386                 input = buf->s;
387                 size = buf->size;
388                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_BYTE : RPA_ENCODING_BYTE;
389         } else if (pGrep->forceEncoding == RPA_GREP_FORCE_UTF16) {
390                 if (buf->size >= 2 && buf->s[0] == -1 && buf->s[1] == -2) {
391                         input = buf->s + 2;
392                         size = buf->size - 2;
393                 } else {
394                         input = buf->s;
395                         size = buf->size;
396                 }
397                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_UTF16LE : RPA_ENCODING_UTF16LE;
398         } else if (buf->size >= 2 && buf->s[0] == -1 && buf->s[1] == -2) {
399                 input = buf->s + 2;
400                 size = buf->size - 2;
401                 pGrep->encoding =  pGrep->icase ? RPA_ENCODING_ICASE_UTF16LE : RPA_ENCODING_UTF16LE;
402         } else {
403                 pGrep->encoding = pGrep->icase ? RPA_ENCODING_ICASE_UTF8 : RPA_ENCODING_UTF8;
404                 input = buf->s;
405                 size = buf->size;
406         }               
407
408         btime = clock();
409
410         switch (pGrep->greptype) {
411         case RPA_GREPTYPE_SCANLINES:
412                 rpa_grep_scan_lines(pGrep, input, size);
413                 break;
414         case RPA_GREPTYPE_MATCH:
415                 rpa_grep_match(pGrep, input, size);
416                 break;
417         case RPA_GREPTYPE_PARSEAST:
418         case RPA_GREPTYPE_PARSE:
419                 rpa_grep_parse(pGrep, input, size);
420                 break;
421         case RPA_GREPTYPE_SCAN:
422                 rpa_grep_scan(pGrep, input, size);
423                 break;
424         default:
425                 rpa_grep_scan(pGrep, input, size);
426                 break;
427         };
428
429         scanclocks = clock() - btime;
430         pGrep->scanmilisec += (unsigned long)(((unsigned long long)1000)*scanclocks/CLOCKS_PER_SEC);
431 }
432
433
434 rpa_buffer_t *rpa_buffer_loadfile(FILE *pFile)
435 {
436         unsigned int memchunk = 256;
437         int ret = 0, inputsize = 0;
438         rpa_buffer_t *buf;
439         
440         buf = rpa_buffer_alloc(2 * memchunk);
441         if (!buf)
442                 return (void*)0;
443         
444         do {
445                 if ((buf->size - inputsize) < memchunk) {
446                         if (rpa_buffer_realloc(buf, buf->size + memchunk) < 0) {
447                                 fprintf(stderr, "Out of memory!\n");
448                                 exit(1);
449                         }
450                 }
451                 ret = fread(&buf->s[inputsize], 1, memchunk - 1, pFile);
452                 if ((ret <= 0) && ferror(pFile)) {
453                         rpa_buffer_destroy(buf);
454                         return (void*)0;
455                 }
456                 inputsize += ret;
457                 buf->s[inputsize] = '\0';
458                 buf->size = inputsize;
459         } while (!feof(pFile)); 
460         
461         return buf;
462 }
463
464
465 int rpa_callback_output(rpastat_t * stat, const char *name, void *userdata, const char *input, unsigned int size, unsigned int reason)
466 {
467
468         return size;
469 }
470
471
472 int rpa_callback_matched_output(rpastat_t * stat, const char *name, void *userdata, const char *input, unsigned int size, unsigned int reason)
473 {
474         rpa_grep_t *pGrep = (rpa_grep_t *)userdata;
475
476         rpa_grep_output_utf8_string(pGrep, name);
477         rpa_grep_output_utf8_string(pGrep, ": ");
478         rpa_grep_output(pGrep, input, size, pGrep->encoding);
479         rpa_grep_output_utf8_string(pGrep, "\n");
480
481         return size;
482 }
483
484
485 void rpa_grep_setup_callback(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
486 {
487
488 }
489
490
491 void rpa_grep_setup_matched_callback(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
492 {
493
494 }
495
496
497 void rpa_grep_dump_pattern_tree(rpa_grep_t *pGrep, rpa_buffer_t *pattern)
498 {
499         rpa_dbex_dumptree(pGrep->hDbex, rpa_dbex_lookup_s(pGrep->hDbex, pattern->s));
500 }
501
502
503 void rpa_grep_output(rpa_grep_t *pGrep, const char *s, unsigned long size, unsigned int encoding)
504 {
505         const unsigned char *input = (const unsigned char*)s;
506         const unsigned char *end = input + size;
507         unsigned int wc;
508         int ret;
509         
510         if (encoding == RPA_ENCODING_UTF16LE || encoding == RPA_ENCODING_ICASE_UTF16LE) {
511                 while ((ret = (int)rpa_grep_utf16_mbtowc(&wc, input, end)) != 0) {
512                         rpa_grep_output_char(wc);
513                         input += ret;
514                 }
515         } else {
516                 while ((ret = (int)rpa_grep_utf8_mbtowc(&wc, input, end)) != 0) {
517                         rpa_grep_output_char(wc);
518                         input += ret;
519                 }
520         }
521 }
522
523
524 void rpa_grep_output_utf8_string(rpa_grep_t *pGrep, const char *s)
525 {
526         rpa_grep_output(pGrep, s, strlen(s), RPA_ENCODING_UTF8);
527 }
528
529
530 void rpa_grep_output_utf16_string(rpa_grep_t *pGrep, const unsigned short *s)
531 {
532         unsigned long size = 0;
533         const unsigned short *pstr = s;
534
535         while (*pstr) {
536                 size += sizeof(unsigned short);
537                 pstr += 1;
538         }
539         rpa_grep_output(pGrep, (const char*)s, size, RPA_ENCODING_UTF16LE);
540 }