RPA Toolkit
implemented more compiler optimizations and postfix/prefix expressions
[rpatk.git] / rpa / rpamnode.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 #include "rmem.h"
22 #include "rstring.h"
23 #include "rpamatch.h"
24 #include "rpalist.h"
25 #include "rpamatchlist.h"
26 #include "rpamnode.h"
27 #include "rpastat.h"
28 #include "rpadbex.h"
29
30
31 #define RPA_MCACHE_SET(_c_, _m_, _i_, _r_, _d_) do {(_c_)->match = (_m_); (_c_)->input = (_i_); (_c_)->ret = (_r_); (_c_)->cbdisable = (_d_);} while (0)
32 #define RPA_MCACHE_CBSET(_c_, _m_, _i_, _r_, _d_, _o_, _s_) \
33         do { \
34                 rpa_cbrecord_t *cbrec; \
35                 rpa_word_t _off_; \
36                 RPA_MCACHE_SET((_c_), (_m_), (_i_), (_r_), (_d_)); \
37                 rpa_cbset_reset(&(_c_)->cbset, 0); \
38                 if ((_s_) > 0) { \
39                         if (rpa_cbset_check_space_min(&(_c_)->cbset, (_s_) + 1) < 0) { \
40                                 RPA_MCACHE_SET((_c_), NULL, NULL, 0, 0); \
41                                 break; \
42                         } \
43                         for (_off_ = 1; _off_ <= (_s_); _off_++) {\
44                                 if ((cbrec = rpa_cbset_getslot(&(_c_)->cbset, _off_)) != 0) { \
45                                         *cbrec = rpa_cbset_getrecord(&stat->cbset, (_o_) + (_off_)); \
46                                 }\
47                         } \
48                         rpa_cbset_reset(&(_c_)->cbset, _s_); \
49                 } \
50         } while (0)
51
52 #define RPA_MAX_RECURSION 100
53 #define RPA_DLOOP_INIT(__name__, __match__, __input__) {{&(__name__).lnk, &(__name__).lnk }, (__match__), (__input__), 0, 0}
54
55
56 static unsigned int rpa_mnode_getid(rpa_class_t *cls)
57 {
58         return (RPA_MNODE_CLASSID);
59 }
60
61
62 static const char *rpa_mnode_getstr(rpa_class_t *cls)
63 {
64         if ((((rpa_mnode_t*)cls)->flags & (RPA_MATCH_OPTIONAL|RPA_MATCH_MULTIPLE)) == (RPA_MATCH_OPTIONAL|RPA_MATCH_MULTIPLE))
65                 return "*";
66         else if (((rpa_mnode_t*)cls)->flags & RPA_MATCH_OPTIONAL)
67                 return "?";
68         else if (((rpa_mnode_t*)cls)->flags & RPA_MATCH_MULTIPLE )
69                 return "+";
70         else
71                 return "x";
72
73         return "";
74 }
75
76
77 static int rpa_mnode_dump(rpa_class_t *cls, char *buffer, unsigned int size)
78 {
79         return 0;
80 }
81
82
83 static void rpa_mnode_destroy(rpa_class_t *cls)
84 {
85         rpa_mnode_t *mnode = (rpa_mnode_t*)cls;
86         r_free(mnode);
87 }
88
89
90 static rpa_class_methods_t rpa_mnode_methods = {
91         rpa_mnode_getid,
92         rpa_mnode_getstr,
93         rpa_mnode_dump,
94         rpa_mnode_destroy,
95 };
96
97
98 static rpa_class_methods_t rpa_mnode_callback_methods = {
99         rpa_mnode_getid,
100         rpa_mnode_getstr,
101         rpa_mnode_dump,
102         rpa_mnode_destroy,
103 };
104
105
106 int rpa_mnode_check_for_loop(rpa_mnode_t *mnode, rpa_match_t *loop, rpa_head_t *mhead, int reclevel)
107 {
108         int ret = 0;
109         unsigned int classid = 0;
110         rpa_match_t *match = mnode->match;
111         rpa_dloop_t dloop;
112         rpa_link_t *pos;
113
114         if (reclevel > RPA_MAX_RECURSION)
115                 return 0;
116                 
117 //      fprintf(stdout, "%s: %s\n", __FUNCTION__, match->name);
118         /* easy case */
119         if (reclevel && match == loop) {
120                 mnode->flags |= RPA_MNODE_LOOP;
121                 return 1;
122         }
123         
124         for (pos = rpa_list_last(mhead); pos; pos = rpa_list_prev(mhead, pos)) {
125                 rpa_dloop_t *pLoop = rpa_list_entry(pos, rpa_dloop_t, lnk);
126                 if (pLoop->match == match)
127                         return 0;
128         }       
129
130         rpa_list_init(&dloop.lnk);
131         dloop.match = match;
132         rpa_list_addt(mhead, &dloop.lnk);
133
134         classid = rpa_class_getid((rpa_class_t *)mnode->match);
135         if (classid & (RPA_MATCH_LIST_CLASSID|RPA_MATCH_NLIST_CLASSID)) {
136                 rpa_mnode_t *hcur;
137                 rpa_link_t *pos;
138                 rpa_head_t *head = &((rpa_match_list_t *)match)->head;
139                 switch (match->match_function_id) {
140                         case RPA_MATCHFUNC_LIST_ALT:
141                         case RPA_MATCHFUNC_NLIST_ALT:
142                         case RPA_MATCHFUNC_NLIST_BESTALT:
143                                 rpa_list_for_each(pos, head) {
144                                         hcur = rpa_list_entry(pos, rpa_mnode_t, mlink);
145                                         if (rpa_mnode_check_for_loop(hcur, loop, mhead, reclevel + 1))
146                                                 ret = 1;
147                                 }
148                         break;
149                         case RPA_MATCHFUNC_LIST:
150                                 rpa_list_for_each(pos, head) {
151                                         hcur = rpa_list_entry(pos, rpa_mnode_t, mlink);
152                                         if (rpa_mnode_check_for_loop(hcur, loop, mhead, reclevel + 1))
153                                                 ret = 1;
154                                         if ((hcur->flags & RPA_MATCH_OPTIONAL) == 0)
155                                                 break;
156                                 }
157                         break;
158                         default:
159                                 ret = 0;
160                         break;
161                 }
162         }
163         
164         rpa_list_del(&dloop.lnk);
165
166         if (ret)
167                 mnode->flags |= RPA_MNODE_LOOP; 
168         return ret;
169 }
170
171
172 rpa_mnode_t *rpa_mnode_init(
173         rpa_mnode_t *mnode,
174         rpa_match_t *match, 
175         unsigned int flags,
176         rpa_class_methods_t *vptr)
177 {
178         r_memset(mnode, 0, sizeof(*mnode));
179         rpa_class_init((rpa_class_t*)mnode, vptr);
180         mnode->match = match;
181         mnode->flags = flags;
182         rpa_list_init(&mnode->mlink);
183         return mnode;
184 }
185
186
187 rpa_mnode_t *rpa_mnode_callback_init(
188         rpa_mnode_callback_t *mnode,
189         rpa_match_t *match, 
190         unsigned int flags,
191         RPA_MATCH_CALLBACK matched_callback, 
192         void *userdata,
193         rpa_class_methods_t *vptr)
194 {
195         flags |= RPA_MNODE_CALLBACK; 
196         rpa_mnode_init((rpa_mnode_t*)mnode, match, flags, vptr);
197         mnode->matched_callback = matched_callback;
198         mnode->userdata = userdata;
199         return (rpa_mnode_t*)mnode;
200 }
201
202 rpa_mnode_t *rpa_mnode_create(
203         rpa_match_t *match, 
204         unsigned int flags)
205 {
206         rpa_mnode_t *newnode;
207
208         newnode = (rpa_mnode_t*) r_malloc(sizeof(*newnode));
209         if (!newnode)
210                 return ((void*)0);
211         return rpa_mnode_init(newnode, match, flags, &rpa_mnode_methods);
212 }
213
214
215 rpa_mnode_t *rpa_mnode_callback_create(
216         rpa_match_t *match, 
217         unsigned int flags, 
218         RPA_MATCH_CALLBACK matched_callback, 
219         void *userdata)
220 {
221         rpa_mnode_callback_t *newnode;
222
223         newnode = (rpa_mnode_callback_t*) r_malloc(sizeof(*newnode));
224         if (!newnode)
225                 return ((void*)0);
226         return rpa_mnode_callback_init(newnode, match, flags, matched_callback, userdata, &rpa_mnode_callback_methods);
227 }
228
229
230 rpa_mnode_t *rpa_mnode_callback_arg1_create(
231         rpa_match_t *match, 
232         unsigned int flags, 
233         RPA_MATCH_CALLBACK matched_callback, 
234         void *userdata,
235         rpa_word_t arg1)
236 {
237         rpa_mnode_callback_arg1_t *newnode;
238
239         newnode = (rpa_mnode_callback_arg1_t*) r_malloc(sizeof(*newnode));
240         if (!newnode)
241                 return ((void*)0);
242         newnode->arg1 = arg1;
243         return rpa_mnode_callback_init((rpa_mnode_callback_t*)newnode, match, flags, matched_callback, userdata, &rpa_mnode_callback_methods);
244         
245 }
246
247
248 rpa_mnode_t *rpa_mnode_callback_arg2_create(
249         rpa_match_t *match, 
250         unsigned int flags, 
251         RPA_MATCH_CALLBACK matched_callback, 
252         void *userdata,
253         rpa_word_t arg1,
254         rpa_word_t arg2)
255 {
256         rpa_mnode_callback_arg2_t *newnode;
257
258         newnode = (rpa_mnode_callback_arg2_t*) r_malloc(sizeof(*newnode));
259         if (!newnode)
260                 return ((void*)0);
261         newnode->base.arg1 = arg1;
262         newnode->arg2 = arg2;
263         return rpa_mnode_callback_init((rpa_mnode_callback_t*)newnode, match, flags, matched_callback, userdata, &rpa_mnode_callback_methods);
264 }
265
266
267 rpa_mnode_t *rpa_mnode_list_append(rpa_head_t *head, rpa_mnode_t *mnode)
268 {
269         rpa_list_addt(head, &mnode->mlink);
270         return mnode;
271 }
272
273
274 void rpa_mnode_cat_list(rpa_head_t *head, rpa_mnode_t *first, ...)
275 {
276         va_list args;
277         rpa_mnode_t *next;
278
279         va_start(args, first);
280         for (next = first; next; next = va_arg(args, rpa_mnode_t*)) {
281                 rpa_mnode_list_append(head, next);
282         }
283         va_end(args);
284 }
285
286
287 int rpa_mnode_exec_callback(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input, unsigned int size, unsigned int reason)
288 {
289         int ret = size;
290
291         if (input >= stat->end)
292                 return -1;
293         if (stat->cbdisable)
294                 return size;
295         if ( ((rpa_mnode_callback_t*)mnode)->matched_callback && (reason & mnode->flags))
296                 ret = ((rpa_mnode_callback_t*)mnode)->matched_callback(mnode, stat, input, size, (reason & mnode->flags));
297         return ret;
298 }
299
300
301 int rpa_mnode_record_callback(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input, unsigned int size, unsigned int reason)
302 {
303         rpa_cbrecord_t *cbrec;
304
305         if (input >= stat->end)
306                 return -1;
307         if (stat->cbdisable)
308                 return size;
309         if (mnode->flags & RPA_MNODE_SYNCRONOUS)
310                 return rpa_mnode_exec_callback(mnode, stat, input, size, reason);
311         if (((rpa_mnode_callback_t*)mnode)->matched_callback) {
312                 if ((cbrec = rpa_cbset_push(&stat->cbset)) != 0) {
313                         cbrec->mnode = mnode;
314                         cbrec->input = input;
315                         cbrec->size = size;
316                 }
317         }
318         return size;
319 }
320
321
322 int rpa_mnode_plain(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
323 {
324         int ret;
325         rpa_match_t *match = mnode->match;
326         rpa_dloop_t *pLoop = rpa_stat_current_loop(stat);
327
328         if (pLoop && pLoop->size && pLoop->input == input && !(mnode->flags & RPA_MNODE_LOOP))
329                 return 0;
330
331         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
332         if (ret <= 0)
333                 return -1;
334         return ret;
335 }
336
337
338 int rpa_mnode_optional(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
339 {
340         int ret;
341         ret = rpa_mnode_plain(mnode, stat, input);
342         if (ret < 0)
343                 return 0;
344         return ret;
345 }
346
347
348 int rpa_mnode_multiple(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
349 {
350         int ret = 0, mret;
351         
352         mret = rpa_mnode_plain(mnode, stat, input);
353         if (mret < 0)
354                 return -1;
355         ret += mret;
356         while ((mret = rpa_mnode_optional(mnode, stat, input + ret))) {
357                 ret += mret;
358         }
359         return ret;
360 }
361
362
363 int rpa_mnode_multiopt(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
364 {
365         int ret = 0, mret;
366         
367         while ((mret = rpa_mnode_optional(mnode, stat, input + ret))) {
368                 ret += mret;
369         }
370         return ret;
371 }
372
373
374 int rpa_mnode_plain_loop_detect(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
375 {
376         int ret;
377         rpa_dloop_t *pLoop;
378         rpa_match_t *match = mnode->match;
379         rpa_dloop_t loop = RPA_DLOOP_INIT(loop, match, input);
380         rpa_head_t *bucket = &stat->loophash[RPA_LOOPHASH(match)];
381         rpa_link_t *pos;
382
383         pLoop = rpa_stat_current_loop(stat);
384         if (pLoop && pLoop->match == mnode->match && pLoop->input == input) {
385                 return pLoop->size;
386         }
387
388         for (pos = rpa_list_last(bucket); pos; pos = rpa_list_prev(bucket, pos)) {
389                 pLoop = rpa_list_entry(pos, rpa_dloop_t, lnk);
390                 if (pLoop->match == match && pLoop->input == input) {
391                         pLoop->mnode = mnode;
392                         rpa_list_del(pos);
393                         rpa_list_addt(&stat->loopstack, pos);
394                         return pLoop->size;
395                 }
396         }
397         rpa_list_addt(bucket, &loop.lnk);
398         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
399         if (loop.mnode) {
400                 while (ret > loop.size) {
401                         loop.size = ret;
402                         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
403                 }
404         }
405         rpa_list_del(&loop.lnk);
406         if (loop.size)
407                 ret = loop.size;
408         if (ret <= 0)
409                 return -1;
410         return ret;
411 }
412
413
414 int rpa_mnode_callback_plain(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
415 {
416         int ret = 0;
417         rpa_match_t *match = mnode->match;
418         rpa_word_t hash = 0;
419         rpa_mcache_t *ncache = NULL;
420         rpa_mcache_t *mcache = NULL;
421         unsigned char cbdisable = stat->cbdisable;
422
423         if (mnode->flags & RPA_MNODE_NOCONNECT) {
424                 stat->cbdisable = 1;
425         }
426         hash = RPA_MCACHEHASH(match, input, stat->cbdisable);
427         mcache = &stat->mcache[hash];
428         ncache = &stat->ncache[hash];
429
430
431         rpa_mnode_exec_callback(mnode, stat, input, (unsigned int) (stat->end - input), RPA_REASON_START);
432         if (((rpa_match_nlist_t*)(mnode->match))->loopy) {
433                 ret = rpa_mnode_plain_loop_detect(mnode, stat, input);
434         } else if (ncache->match == match && ncache->input == input) {
435                 /*
436                  * Debug the cache efficiency
437                  * r_printf("HIT THE CACHE @ %d: %s, %d\n", hash, match->name, ncache->ret);
438                  */
439                 ret = -1;
440                 goto end;
441         } else if (mcache->match == match && mcache->input == input) {
442                 ret = mcache->ret;
443                 /*
444                  * Debug the cache efficiency
445                  * r_printf("HIT THE CACHE @ %d: %s, %d\n", hash, match->name, mcache->ret);
446                  */
447         } else {
448                 ret = rpa_mnode_plain(mnode, stat, input);
449                 if (stat->usecache) {
450                         if (ret > 0)
451                                 RPA_MCACHE_SET(mcache, match, input, ret, stat->cbdisable);
452                         else
453                                 RPA_MCACHE_SET(ncache, match, input, ret, stat->cbdisable);
454                 }
455         }
456         if (ret <= 0) {
457                 rpa_mnode_exec_callback(mnode, stat, input, 0, RPA_REASON_END);
458                 ret = -1;
459                 goto end;
460         }
461         ret = rpa_mnode_exec_callback(mnode, stat, input, ret, RPA_REASON_END|RPA_REASON_MATCHED);
462
463 end:
464         stat->cbdisable = cbdisable;
465         if (ret <= 0)
466                 return -1;
467         return ret;
468 }
469
470
471 int rpa_mnode_callback_optional(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
472 {
473         int ret;
474
475         ret = rpa_mnode_callback_plain(mnode, stat, input);
476         if (ret < 0) 
477                 return 0;
478         
479         return ret;
480 }
481
482
483 int rpa_mnode_callback_multiple(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
484 {
485         int ret = 0, mret;
486
487         mret = rpa_mnode_callback_plain(mnode, stat, input);
488         if (mret <= 0) 
489                 return mret;
490
491         ret += mret;
492         do {
493                 mret = rpa_mnode_callback_optional(mnode, stat, input + ret);
494                 ret += mret;
495         } while (mret);
496
497         return ret;
498 }
499
500
501 int rpa_mnode_callback_multiopt(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
502 {
503         int ret = 0, mret;
504
505         do {
506                 mret = rpa_mnode_callback_optional(mnode, stat, input + ret);
507                 ret += mret;
508         } while (mret);
509         
510         return ret;
511 }
512
513
514
515 /* Parser functionality */
516
517 int rpa_mnode_p_plain_loop_detect(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
518 {
519         int ret;
520         rpa_word_t off; 
521         rpa_dloop_t *pLoop;
522         rpa_match_t *match = mnode->match;
523         rpa_dloop_t loop = RPA_DLOOP_INIT(loop, match, input);
524         rpa_link_t *pos;
525         rpa_head_t *bucket = &stat->loophash[RPA_LOOPHASH(match)];
526
527         pLoop = rpa_stat_current_loop(stat);
528         if (pLoop && pLoop->match == mnode->match && pLoop->input == input) {
529                 return pLoop->size;
530         }
531
532         for (pos = rpa_list_last(bucket); pos; pos = rpa_list_prev(bucket, pos)) {
533                 pLoop = rpa_list_entry(pos, rpa_dloop_t, lnk);
534                 if (pLoop->match == match && pLoop->input == input) {
535                         pLoop->mnode = mnode;
536                         rpa_list_del(pos);
537                         rpa_list_addt(&stat->loopstack, pos);
538                         return pLoop->size;
539                 }
540         }
541         rpa_list_addt(bucket, &loop.lnk);
542         off = rpa_cbset_getpos(&stat->cbset);
543         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
544         if (ret <= 0)
545                 rpa_cbset_reset(&stat->cbset, off);
546         if (loop.mnode) {
547                 while (ret > loop.size) {
548                         loop.size = ret;
549                         off = rpa_cbset_getpos(&stat->cbset);
550                         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
551                         if (ret <= loop.size)
552                                 rpa_cbset_reset(&stat->cbset, off);
553                 }
554         }
555         rpa_list_del(&loop.lnk);
556         if (loop.size)
557                 ret = loop.size;
558         if (ret <= 0)
559                 return -1;
560
561         return ret;
562 }
563
564
565
566 int rpa_mnode_p_plain(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
567 {
568         int ret;
569         rpa_word_t off = rpa_cbset_getpos(&stat->cbset);
570         rpa_match_t *match = mnode->match;
571         rpa_dloop_t *pLoop = rpa_stat_current_loop(stat);
572
573         if (pLoop && pLoop->size && pLoop->input == input && !(mnode->flags & RPA_MNODE_LOOP))
574                 return 0;
575
576         ret = stat->mtable[match->match_function_id](mnode->match, stat, input);
577         if (ret <= 0) {
578                 rpa_cbset_reset(&stat->cbset, off);
579                 return -1;
580         }
581         return ret;
582 }
583
584
585 int rpa_mnode_p_optional(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
586 {
587         int ret;
588         ret = rpa_mnode_p_plain(mnode, stat, input);
589         if (ret < 0)
590                 return 0;
591         return ret;
592 }
593
594
595 int rpa_mnode_p_multiple(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
596 {
597         int ret = 0, mret;
598         
599         mret = rpa_mnode_p_plain(mnode, stat, input);
600         if (mret < 0)
601                 return -1;
602         ret += mret;
603         while ((mret = rpa_mnode_p_optional(mnode, stat, input + ret))) {
604                 ret += mret;
605         }
606         return ret;
607 }
608
609
610 int rpa_mnode_p_multiopt(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
611 {
612         int ret = 0, mret;
613         
614         while ((mret = rpa_mnode_p_optional(mnode, stat, input + ret))) {
615                 ret += mret;
616         }
617         return ret;
618 }
619
620
621 void rpa_mcache_cbset(rpa_stat_t *stat, rpa_mcache_t *_c_, rpa_match_t *_m_, const char* _i_, int _r_, rpa_word_t _o_, int _s_)
622 {
623         rpa_cbrecord_t *cbrec;
624         rpa_word_t _off_;
625         RPA_MCACHE_SET((_c_), (_m_), (_i_), (_r_), stat->cbdisable);
626         rpa_cbset_reset(&(_c_)->cbset, 0);
627         if ((_s_) > 0) {
628                 if (rpa_cbset_check_space_min(&(_c_)->cbset, (_s_) + 1) < 0) {
629                         RPA_MCACHE_SET((_c_), NULL, NULL, 0, 0);
630                         return;
631                 }
632                 for (_off_ = 1; _off_ <= (_s_); _off_++) {
633                         if ((cbrec = rpa_cbset_getslot(&(_c_)->cbset, _off_)) != 0) {
634                                 *cbrec = rpa_cbset_getrecord(&stat->cbset, (_o_) + (_off_));
635                         }
636                 }
637                 rpa_cbset_reset(&(_c_)->cbset, _s_);
638         }
639 }
640
641
642 int rpa_mnode_p_callback_plain(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
643 {
644         int ret = 0;
645         rpa_match_t *match = mnode->match;
646         rpa_word_t hash = 0;
647         rpa_mcache_t *mcache = NULL;
648         rpa_mcache_t *ncache = NULL;
649         rpa_word_t cboff, cboffset_now = (rpa_word_t)-1, cboffset_before = rpa_cbset_getpos(&stat->cbset);
650         unsigned char cbdisable = stat->cbdisable;
651
652         if (mnode->flags & RPA_MNODE_NOCONNECT) {
653                 stat->cbdisable = 1;
654         }
655         hash = RPA_MCACHEHASH(match, input, stat->cbdisable);
656         mcache = &stat->mcache[hash];
657         ncache = &stat->ncache[hash];
658
659         if (((rpa_match_nlist_t*)match)->loopy) {
660                 ret = rpa_mnode_p_plain_loop_detect(mnode, stat, input);
661         } else if (ncache->match == match && ncache->input == input) {
662                 /*
663                  * Debug the cache efficiency
664                  * r_printf("HIT THE CACHE @ %d: %s, %d\n", hash, match->name, ncache->ret);
665                  */
666                 ret = -1;
667                 goto end;
668         } else if (mcache->match == match && mcache->input == input && mcache->cbdisable == stat->cbdisable) {
669                 rpa_cbrecord_t *cbrec;
670                 rpa_word_t lastoff = rpa_cbset_getpos(&mcache->cbset);
671                 for (cboff = 1; cboff <= lastoff; cboff++) {
672                         if ((cbrec = rpa_cbset_push(&stat->cbset)) != 0) {
673                                 *cbrec = rpa_cbset_getrecord(&mcache->cbset, cboff);
674                         }
675                 }
676                 ret = mcache->ret;
677                 /*
678                  * Debug the cache efficiency
679                  * r_printf("HIT THE CACHE @ %d: %s, %d\n", hash, match->name, ret);
680                  */
681         } else {
682                 ret = rpa_mnode_p_plain(mnode, stat, input);
683                 if (stat->usecache ) {
684                         /*
685                          * We can only use the cache if the CB recording is NOT disabled,
686                          * because we insert the recorded callbacks in the cache.
687                          */
688                         if (ret > 0) {
689                                 cboffset_now = rpa_cbset_getpos(&stat->cbset);
690                                 RPA_MCACHE_CBSET(mcache, match, input, ret, stat->cbdisable, cboffset_before, cboffset_now - cboffset_before);
691                         } else {
692                                 RPA_MCACHE_SET(ncache, match, input, ret, stat->cbdisable);
693                         }
694                 }
695         }
696
697         if (ret > 0) {
698                 ret = rpa_mnode_record_callback(mnode, stat, input, ret, RPA_REASON_START|RPA_REASON_END|RPA_REASON_MATCHED);
699         }
700
701 end:
702         /*
703          * Restore the original state
704          */
705         stat->cbdisable = cbdisable;
706         if (ret <= 0) {
707                 return -1;
708         }
709         return ret;
710 }
711
712
713 int rpa_mnode_p_callback_optional(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
714 {
715         int ret;
716
717         ret = rpa_mnode_p_callback_plain(mnode, stat, input);
718         if (ret < 0) 
719                 return 0;
720         
721         return ret;
722 }
723
724
725 int rpa_mnode_p_callback_multiple(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
726 {
727         int ret = 0, mret;
728
729         mret = rpa_mnode_p_callback_plain(mnode, stat, input);
730         if (mret <= 0) 
731                 return mret;
732
733         ret += mret;
734         do {
735                 mret = rpa_mnode_p_callback_optional(mnode, stat, input + ret);
736                 ret += mret;
737         } while (mret);
738
739         return ret;
740 }
741
742
743 int rpa_mnode_p_callback_multiopt(rpa_mnode_t *mnode, rpa_stat_t *stat, const char *input)
744 {
745         int ret = 0, mret;
746
747         do {
748                 mret = rpa_mnode_p_callback_optional(mnode, stat, input + ret);
749                 ret += mret;
750         } while (mret);
751         
752         return ret;
753 }