source: tags/ms_r16q3/ARBDB/admatch.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.0 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : admatch.cxx                                       //
4//   Purpose   : functions related to string match/replace         //
5//                                                                 //
6//   ReCoded for POSIX ERE                                         //
7//            by Ralf Westram (coder@reallysoft.de) in April 2009  //
8//   Institute of Microbiology (Technical University Munich)       //
9//   http://www.arb-home.de/                                       //
10//                                                                 //
11// =============================================================== //
12
13#include "gb_local.h"
14
15#include <arb_strbuf.h>
16#include <arb_match.h>
17
18#include <cctype>
19
20// ------------------------
21//      string matcher
22
23enum string_matcher_type {
24    SM_INVALID = -1,
25    SM_ANY     = 0,             // matches any string
26    SM_WILDCARDED,              // match with wildcards (GBS_string_matches)
27    SM_REGEXPR,                 // match using regexpr
28};
29
30struct GBS_string_matcher {
31    string_matcher_type  type;
32    GB_CASE              case_flag;
33    char                *wildexpr;
34    GBS_regex           *regexpr;
35};
36
37GBS_string_matcher *GBS_compile_matcher(const char *search_expr, GB_CASE case_flag) {
38    /* returns a valid string matcher (to be used with GBS_string_matches_regexp)
39     * or NULL (in which case an error was exported)
40     */
41
42    GBS_string_matcher *matcher = ARB_alloc<GBS_string_matcher>(1);
43    GB_ERROR            error   = 0;
44
45    matcher->type      = SM_INVALID;
46    matcher->case_flag = case_flag;
47    matcher->wildexpr  = NULL;
48    matcher->regexpr   = NULL;
49
50    if (search_expr[0] == '/') {
51        const char *end = strchr(search_expr, 0)-1;
52        if (end>search_expr && end[0] == '/') {
53            GB_CASE     expr_attached_case;
54            const char *unwrapped_expr = GBS_unwrap_regexpr(search_expr, &expr_attached_case, &error);
55
56            if (unwrapped_expr) {
57                if (expr_attached_case != GB_MIND_CASE) error = "format '/../i' not allowed here";
58                else {
59                    matcher->regexpr = GBS_compile_regexpr(unwrapped_expr, case_flag, &error);
60                    if (matcher->regexpr) {
61                        matcher->type = SM_REGEXPR;
62                    }
63                }
64            }
65        }
66    }
67
68    if (matcher->regexpr == NULL && !error) {
69        if (strcmp(search_expr, "*") == 0) {
70            matcher->type = SM_ANY;
71        }
72        else {
73            matcher->type     = SM_WILDCARDED;
74            matcher->wildexpr = ARB_strdup(search_expr);
75        }
76    }
77
78    if (matcher->type == SM_INVALID) {
79        error = GBS_global_string("Failed to create GBS_string_matcher from '%s'", search_expr);
80    }
81
82    if (error) {
83        GBS_free_matcher(matcher);
84        matcher = 0;
85        GB_export_error(error);
86    }
87    return matcher;
88}
89
90void GBS_free_matcher(GBS_string_matcher *matcher) {
91    free(matcher->wildexpr);
92    if (matcher->regexpr) GBS_free_regexpr(matcher->regexpr);
93    free(matcher);
94}
95
96// -------------------------
97//      wildcard search
98
99GB_CSTR GBS_find_string(GB_CSTR cont, GB_CSTR substr, int match_mode) {
100    /* search a substring in another string
101     * match_mode == 0     -> exact match
102     * match_mode == 1     -> a==A
103     * match_mode == 2     -> a==a && a==?
104     * match_mode == else  -> a==A && a==?
105     */
106    const char *p1, *p2;
107    char        b;
108
109    switch (match_mode) {
110
111        case 0: // exact match
112            for (p1 = cont, p2 = substr; *p1;) {
113                if (!(b = *p2)) {
114                    return (char *)cont;
115                }
116                else {
117                    if (b == *p1) {
118                        p1++;
119                        p2++;
120                    }
121                    else {
122                        p2 = substr;
123                        p1 = (++cont);
124                    }
125                }
126            }
127            if (!*p2)   return (char *)cont;
128            break;
129
130        case 1: // a==A
131            for (p1 = cont, p2 = substr; *p1;) {
132                if (!(b = *p2)) {
133                    return (char *)cont;
134                }
135                else {
136                    if (toupper(*p1) == toupper(b)) {
137                        p1++;
138                        p2++;
139                    }
140                    else {
141                        p2 = substr;
142                        p1 = (++cont);
143                    }
144                }
145            }
146            if (!*p2) return (char *)cont;
147            break;
148        case 2: // a==a && a==?
149            for (p1 = cont, p2 = substr; *p1;) {
150                if (!(b = *p2)) {
151                    return (char *)cont;
152                }
153                else {
154                    if (b == *p1 || (b=='?')) {
155                        p1++;
156                        p2++;
157                    }
158                    else {
159                        p2 = substr;
160                        p1 = (++cont);
161                    }
162                }
163            }
164            if (!*p2) return (char *)cont;
165            break;
166
167        default: // a==A && a==?
168            for (p1 = cont, p2 = substr; *p1;) {
169                if (!(b = *p2)) {
170                    return (char *)cont;
171                }
172                else {
173                    if (toupper(*p1) == toupper(b) || (b=='?')) {
174                        p1++;
175                        p2++;
176                    }
177                    else {
178                        p2 = substr;
179                        p1 = (++cont);
180                    }
181                }
182            }
183            if (!*p2) return (char *)cont;
184            break;
185    }
186    return 0;
187}
188
189bool GBS_string_matches(const char *str, const char *search, GB_CASE case_sens)
190/* Wildcards in 'search' string:
191 *      ?   one character
192 *      *   several characters
193 *
194 * if 'case_sens' == GB_IGNORE_CASE -> change all letters to uppercase
195 *
196 * returns true if strings are equal, false otherwise
197 */
198{
199    const char *p1, *p2;
200    char        a, b, *d;
201    long        i;
202    char        fsbuf[256];
203
204    p1 = str;
205    p2 = search;
206    while (1) {
207        a = *p1;
208        b = *p2;
209        if (b == '*') {
210            if (!p2[1]) break; // '*' also matches nothing
211            i = 0;
212            d = fsbuf;
213            for (p2++; (b=*p2)&&(b!='*');) {
214                *(d++) = b;
215                p2++;
216                i++;
217                if (i > 250) break;
218            }
219            if (*p2 != '*') {
220                p1 += strlen(p1)-i;     // check the end of the string
221                if (p1 < str) return false;
222                p2 -= i;
223            }
224            else {
225                *= 0;
226                p1  = GBS_find_string(p1, fsbuf, 2+(case_sens == GB_IGNORE_CASE)); // match with '?' wildcard
227                if (!p1) return false;
228                p1 += i;
229            }
230            continue;
231        }
232
233        if (!a) return !b;
234        if (a != b) {
235            if (b != '?') {
236                if (!b) return !a;
237                if (case_sens == GB_IGNORE_CASE) {
238                    a = toupper(a);
239                    b = toupper(b);
240                    if (a != b) return false;
241                }
242                else {
243                    return false;
244                }
245            }
246        }
247        p1++;
248        p2++;
249    }
250    return true;
251}
252
253bool GBS_string_matches_regexp(const char *str, const GBS_string_matcher *expr) {
254    /* Wildcard or regular expression match
255     * Returns true if match
256     *
257     * Use GBS_compile_matcher() and GBS_free_matcher() to maintain 'expr'
258     */
259    bool matches = false;
260
261    switch (expr->type) {
262        case SM_ANY: {
263            matches = true;
264            break;
265        }
266        case SM_WILDCARDED: {
267            matches = GBS_string_matches(str, expr->wildexpr, expr->case_flag);
268            break;
269        }
270        case SM_REGEXPR: {
271            matches = GBS_regmatch_compiled(str, expr->regexpr, NULL) != NULL;
272            break;
273        }
274        case SM_INVALID: {
275            gb_assert(0);
276            break;
277        }
278    }
279
280    return matches;
281}
282
283// -----------------------------------
284//      Search replace tool (SRT)
285
286#define GBS_SET   ((char)1)
287#define GBS_SEP   ((char)2)
288#define GBS_MWILD ((char)3)
289#define GBS_WILD  ((char)4)
290
291__ATTR__USERESULT_TODO static GB_ERROR gbs_build_replace_string(GBS_strstruct *strstruct,
292                                                                char *bar, char *wildcards, long max_wildcard,
293                                                                char **mwildcards, long max_mwildcard, GBDATA *gb_container)
294{
295    int wildcardcnt  = 0;
296    int mwildcardcnt = 0;
297
298    char *p = bar;
299    char  c;
300    while ((c=*(p++))) {
301        switch (c) {
302            case GBS_MWILD:
303            case GBS_WILD: {
304                char d = *(p++);
305                if (gb_container && (d=='(')) { // if a gbcont then replace till ')'
306                    char *klz = gbs_search_second_bracket(p);
307                    if (klz) {          // reference found: $(gbd)
308                        int separator = 0;
309                        *klz = 0;
310                        char *psym = strpbrk(p, "#|:");
311                        if (psym) {
312                            separator = *psym;
313                            *psym = 0;
314                        }
315
316                        GBDATA *gb_entry = *p
317                            ? GB_search(gb_container, p, GB_FIND)
318                            : gb_container;
319
320                        if (psym) *psym = separator;
321
322                        char *entry = (gb_entry && gb_entry != gb_container)
323                            ? GB_read_as_string(gb_entry)
324                            : ARB_strdup("");
325
326                        if (entry) {
327                            char *h;
328                            switch (separator) {
329                                case ':':
330                                    h = GBS_string_eval(entry, psym+1, gb_container);
331                                    if (!h) return GB_await_error();
332
333                                    GBS_strcat(strstruct, h);
334                                    free(h);
335                                    break;
336
337                                case '|':
338                                    h = GB_command_interpreter(GB_get_root(gb_container), entry, psym+1, gb_container, 0);
339                                    if (!h) return GB_await_error();
340
341                                    GBS_strcat(strstruct, h);
342                                    free(h);
343                                    break;
344
345                                case '#':
346                                    if (!gb_entry) {
347                                        GBS_strcat(strstruct, psym+1);
348                                        break;
349                                    }
350                                    // fall-through
351                                default:
352                                    GBS_strcat(strstruct, entry);
353                                    break;
354                            }
355                            free(entry);
356                        }
357                        *klz = ')';
358                        p = klz+1;
359                        break;
360                    }
361                    c = '*';
362                    GBS_chrcat(strstruct, c);
363                    GBS_chrcat(strstruct, d);
364                }
365                else {
366                    int wildcard_num = d - '1';
367                    if (c == GBS_WILD) {
368                        c = '?';
369                        if ((wildcard_num<0)||(wildcard_num>9)) {
370                            p--;            // use this character
371                            wildcard_num = wildcardcnt++;
372                        }
373                        if (wildcard_num>=max_wildcard) {
374                            GBS_chrcat(strstruct, c);
375                        }
376                        else {
377                            GBS_chrcat(strstruct, wildcards[wildcard_num]);
378                        }
379                    }
380                    else {
381                        c = '*';
382                        if ((wildcard_num<0)||(wildcard_num>9)) {
383                            p--;            // use this character
384                            wildcard_num = mwildcardcnt++;
385                        }
386                        if (wildcard_num>=max_mwildcard) {
387                            GBS_chrcat(strstruct, c);
388                        }
389                        else {
390                            GBS_strcat(strstruct, mwildcards[wildcard_num]);
391                        }
392                    }
393                }
394                break;
395            }
396            default:
397                GBS_chrcat(strstruct, c);
398                break;
399        }
400    }
401    return 0;
402}
403
404static char *gbs_compress_command(const char *com) {
405    /* Prepare SRT.
406     *
407     * Replaces all
408     *   '=' by GBS_SET
409     *   ':' by GBS_SEP
410     *   '?' by GBS_WILD if followed by a number or '?'
411     *   '*' by GBS_MWILD  or '('
412     * \ is the escape character
413     */
414    char *result, *s, *d;
415    int   ch;
416
417    s = d = result = ARB_strdup(com);
418    while ((ch = *(s++))) {
419        switch (ch) {
420            case '=':   *(d++) = GBS_SET; break;
421            case ':':   *(d++) = GBS_SEP; break;
422            case '?':
423                ch = *s; // @@@ unused
424                *(d++) = GBS_WILD;
425                break;
426            case '*':
427                ch = *s; // @@@ unused
428                *(d++) = GBS_MWILD;
429                break;
430            case '\\':
431                ch = *(s++); if (!ch) { s--; break; };
432                switch (ch) {
433                    case 'n':   *(d++) = '\n'; break;
434                    case 't':   *(d++) = '\t'; break;
435                    case '0':   *(d++) = '\0'; break;
436                    default:    *(d++) = ch; break;
437                }
438                break;
439            default:
440                *(d++) = ch;
441        }
442    }
443    *d = 0;
444    return result;
445}
446
447
448char *GBS_string_eval(const char *insource, const char *icommand, GBDATA *gb_container)
449     /* GBS_string_eval replaces substrings in source
450      * Syntax: command = "oliver=olli:peter=peti"
451      *
452      * Returns a heapcopy of result of replacement.
453      *
454      *         * is a wildcard for any number of character
455      *         ? is a wildcard for exactly one character
456      *
457      * To reference to the wildcards on the left side of the '='
458      * use ? and *, to reference in a different order use:
459      *         *0 to reference to the first occurrence of *
460      *         *1          second
461      *         ...
462      *         *9
463      *
464      * if the last and first characters of the search string are no '*' wildcards then
465      * the replace is repeated as many times as possible
466      * '\' is the escape character: e.g. \n is newline; '\\' is '\'; '\=' is '='; ....
467      *
468      * eg:
469      * print first three characters of first word and the whole second word:
470      *
471      *         *(arb_key)          is the value of the a database entry arb key
472      *         *(arb_key#string)   value of the database entry or 'string' if the entry does not exist
473      *         *(arb_key\:SRT)     runs SRT recursively on the value of the database entry
474      *         *([arb_key]|ACI)    runs the ACI command interpreter on the value of the database entry (or on an empty string)
475      *
476      * If an error occurs it returns NULL - in this case the error was exported.
477      */
478{
479    GB_CSTR  source;            // pointer into the current string when parsed
480    char    *search;            // pointer into the current command when parsed
481    GB_CSTR  p;                 // short live pointer
482    char     c;
483    GB_CSTR  already_transferred; // point into 'in' string to non parsed position
484
485    char      wildcard[40];
486    char     *mwildcard[10];
487    GB_ERROR  error;
488
489    long i;
490    long max_wildcard;
491    long max_mwildcard;
492
493    char *start_of_wildcard;
494    char  what_wild_card;
495
496    GB_CSTR start_match;
497
498    char *doppelpunkt;
499
500    char *bar;
501    char *in;
502    char *nextdp;
503    GBS_strstruct *strstruct;
504    char *command;
505
506    if (!icommand || !icommand[0]) return ARB_strdup(insource);
507
508    command = gbs_compress_command(icommand);
509    in = ARB_strdup(insource);               // copy insource to allow to destroy it
510
511    for (doppelpunkt = command; doppelpunkt; doppelpunkt = nextdp) {    // loop over command string
512        // in is in , strstruct is out
513        max_wildcard = 0;
514        max_mwildcard = 0;
515        nextdp = strchr(doppelpunkt, GBS_SEP);
516        if (nextdp) {
517            *(nextdp++) = 0;
518        }
519        if (!doppelpunkt[0]) {                      // empty command -> search next
520            continue;
521        }
522
523        bar = strchr(doppelpunkt+1, GBS_SET);               // Parse the command string !!!!
524        if (bar) {
525            *(bar++) = 0;
526        }
527        else {
528            GB_export_errorf("SRT ERROR: no '=' found in command '%s' (position > %zi)", icommand, doppelpunkt-command+1);
529            free(command);
530            free(in);
531            return 0;
532        }
533
534        already_transferred = in;
535        strstruct = GBS_stropen(1000);          // create output stream
536
537        if ((!*in) && doppelpunkt[0] == GBS_MWILD && doppelpunkt[1] == 0) {     // empty string -> pars myself
538            // * matches empty string !!!!
539            mwildcard[max_mwildcard++] = ARB_strdup("");
540            gbs_build_replace_string(strstruct, bar, wildcard, max_wildcard, mwildcard, max_mwildcard, gb_container);
541            goto gbs_pars_unsuccessfull;    // successfull search
542        }
543
544        for (source = in; *source;) {               // loop over string
545            search = doppelpunkt;
546
547            start_match = 0;                // match string for '*'
548            while ((c = *(search++))) {             // search matching command
549                switch (c) {
550                    case GBS_MWILD:
551                        if (!start_match) start_match = source;
552
553                        start_of_wildcard = search;
554                        if (!(c = *(search++))) {       // last character is a wildcard -> that was it
555                            mwildcard[max_mwildcard++] = ARB_strdup(source);
556                            source += strlen(source);
557                            goto gbs_pars_successfull;      // successfull search and end wildcard
558                        }
559                        while ((c=*(search++)) && c!=GBS_MWILD && c!=GBS_WILD) ;     // search the next wildcardstring
560                        search--;                   // back one character
561                        *search = 0;
562                        what_wild_card = c;
563                        p = GBS_find_string(source, start_of_wildcard, 0);
564                        if (!p) {                   // string not found -> unsuccessful search
565                            goto gbs_pars_unsuccessfull;
566                        }
567                        c = *p;                     // set wildcard // @@@ unused
568                        mwildcard[max_mwildcard++] = ARB_strpartdup(source, p-1);
569                        source = p + strlen(start_of_wildcard);         // we parsed it
570                        *search = what_wild_card;
571                        break;
572
573                    case GBS_WILD:
574                        if (!source[0]) {
575                            goto gbs_pars_unsuccessfull;
576                        }
577                        if (!start_match) start_match = source;
578                        wildcard[max_wildcard++] = *(source++);
579                        break;
580                    default:
581                        if (start_match) {
582                            if (c != *(source++)) {
583                                goto gbs_pars_unsuccessfull;
584                            }
585                            break;
586                        }
587                        else {
588                            char *buf1;
589                            buf1 = search-1;
590                            while ((c=*(search++)) && c!=GBS_MWILD && c!=GBS_WILD) ;     // search the next wildcardstring
591                            search--;                   // back one character
592                            *search = 0;
593                            what_wild_card = c;
594                            p = GBS_find_string(source, buf1, 0);
595                            if (!p) {                   // string not found -> unsuccessful search
596                                goto gbs_pars_unsuccessfull;
597                            }
598                            start_match = p;
599                            source = p + strlen(buf1);          // we parsed it
600                            *search = what_wild_card;
601                        }
602                        break;
603                }
604            }
605
606        gbs_pars_successfull :
607            /* now we got
608             * source:                  pointer to end of match
609             * start_match:             pointer to start of match
610             * in:                      pointer to the entire string
611             * already_transferred:     pointer to the start of the unparsed string
612             * bar:                     the replace string
613             */
614
615            // now look for the replace string
616            GBS_strncat(strstruct, already_transferred, start_match-already_transferred); // cat old data
617            error               = gbs_build_replace_string(strstruct, bar, wildcard, max_wildcard, // do the command
618                                             mwildcard, max_mwildcard, gb_container);
619            already_transferred = source;
620
621            for (i = 0; i < max_mwildcard; i++) {
622                freenull(mwildcard[i]);
623            }
624            max_wildcard  = 0;
625            max_mwildcard = 0;
626
627            if (error) {
628                GBS_strforget(strstruct);
629                free(command);
630                free(in);
631                GB_export_error(error);
632                return 0;
633            }
634        }
635    gbs_pars_unsuccessfull :
636        GBS_strcat(strstruct, already_transferred); // cat the rest data
637
638        for (i = 0; i < max_mwildcard; i++) {
639            freenull(mwildcard[i]);
640        }
641        max_wildcard  = 0;
642        max_mwildcard = 0;
643
644        freeset(in, GBS_strclose(strstruct));
645    }
646    free(command);
647    return in;
648}
649
Note: See TracBrowser for help on using the repository browser.