source: tags/svn.1.5.4/ARBDB/adquery.cxx

Last change on this file was 8319, checked in by westram, 14 years ago
  • ignored PERL2ARB interface as referrer (to detect functions that are only used from perl)
    • moved several functions to static scope or removed them (partly reverted by [13155])
    • for some functions it's ok to be only used from perl (e.g. macro support functions). Added comments there!
  • there is still some dead code in there, e.g.
    • read-security is implemented, but unused (and unwanted)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.0 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adquery.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_aci.h"
12#include "gb_comm.h"
13#include "gb_index.h"
14#include "gb_key.h"
15#include "gb_localdata.h"
16#include "gb_ta.h"
17
18#include <arb_strbuf.h>
19
20#include <cctype>
21
22#define GB_PATH_MAX 1024
23
24static void build_GBDATA_path(GBDATA *gbd, char **buffer) {
25    GBCONTAINER *gbc = GB_FATHER(gbd);
26    const char  *key;
27
28    if (gbc) {
29        build_GBDATA_path((GBDATA*)gbc, buffer);
30        key = GB_KEY(gbd);
31        {
32            char *bp = *buffer;
33            *bp++ = '/';
34            while (*key) *bp++ = *key++;
35            *bp      = 0;
36
37            *buffer = bp;
38        }
39    }
40}
41
42#define BUFFERSIZE 1024
43
44static const char *GB_get_GBDATA_path(GBDATA *gbd) {
45    static char *orgbuffer = NULL;
46    char        *buffer;
47
48    if (!orgbuffer) orgbuffer = (char*)malloc(BUFFERSIZE);
49    buffer                    = orgbuffer;
50
51    build_GBDATA_path(gbd, &buffer);
52    assert_or_exit((buffer-orgbuffer) < BUFFERSIZE); // buffer overflow
53
54    return orgbuffer;
55}
56
57// ----------------
58//      QUERIES
59
60static bool gb_find_value_equal(GBDATA *gb, GB_TYPES type, const char *val, GB_CASE case_sens) {
61    bool equal = false;
62
63#if defined(DEBUG)
64    GB_TYPES realtype = GB_TYPE(gb);
65    gb_assert(val);
66    if (type == GB_STRING) {
67        gb_assert(realtype == GB_STRING || realtype == GB_LINK); // gb_find_internal called with wrong type
68    }
69    else {
70        gb_assert(realtype == type); // gb_find_internal called with wrong type
71    }
72#endif // DEBUG
73
74    switch (type) {
75        case GB_STRING:
76        case GB_LINK:
77            equal = GBS_string_matches(GB_read_char_pntr(gb), val, case_sens);
78            break;
79
80        case GB_INT: {
81            int i                      = GB_read_int(gb);
82            if (i == *(int*)val) equal = true;
83            break;
84        }
85        case GB_FLOAT: {
86            double d                      = GB_read_float(gb);
87            if (d == *(double*)val) equal = true; // (no aliasing problem here; char* -> double* ok)
88            break;
89        }
90        default: {
91            const char *err = GBS_global_string("Value search not supported for data type %i (%i)", GB_TYPE(gb), type);
92            GB_internal_error(err);
93            break;
94        }
95    }
96
97    return equal;
98}
99
100static GBDATA *find_sub_by_quark(GBDATA *father, GBQUARK key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after, size_t skip_over) {
101    /* search an entry with a key 'key_quark' below a container 'father'
102       after position 'after'
103
104       if 'skip_over' > 0 search skips 'skip_over' entries
105
106       if (val != NULL) search for entry with value 'val':
107
108       GB_STRING/GB_LINK: compares string (case_sensitive or not)
109       GB_INT: compares values
110       GB_FLOAT: ditto (val MUST be a 'double*')
111       others: not implemented yet
112
113       Note: to search for non-char*-values use GB_find_int()
114             for other types write a new similar function
115
116             if key_quark<0 search everything
117    */
118
119    int             end, index;
120    GBCONTAINER    *gbf = (GBCONTAINER*)father;
121    gb_header_list *header;
122    GBDATA         *gb;
123
124    end  = gbf->d.nheader;
125    header = GB_DATA_LIST_HEADER(gbf->d);
126    if (after) index = (int)after->index+1; else index = 0;
127
128    if (key_quark<0) { // unspecific key quark (i.e. search all)
129        gb_assert(!val);        // search for val not possible if searching all keys!
130        if (!val) {
131            for (; index < end; index++) {
132                if (header[index].flags.key_quark != 0) {
133                    if (header[index].flags.changed >= GB_DELETED) continue;
134                    if (!(gb=GB_HEADER_LIST_GBD(header[index]))) {
135                        gb_unfold(gbf, 0, index);
136                        header = GB_DATA_LIST_HEADER(gbf->d);
137                        gb     = GB_HEADER_LIST_GBD(header[index]);
138                        if (!gb) {
139                            const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
140                            GB_internal_error(err);
141                            continue;
142                        }
143                    }
144                    if (!skip_over--) return gb;
145                }
146            }
147        }
148    }
149    else { // specific key quark
150        for (; index < end; index++) {
151            if ((key_quark == header[index].flags.key_quark)) {
152                if (header[index].flags.changed >= GB_DELETED) continue;
153                if (!(gb=GB_HEADER_LIST_GBD(header[index])))
154                {
155                    gb_unfold(gbf, 0, index);
156                    header = GB_DATA_LIST_HEADER(gbf->d);
157                    gb = GB_HEADER_LIST_GBD(header[index]);
158                    if (!gb) {
159                        const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
160                        GB_internal_error(err);
161                        continue;
162                    }
163                }
164                if (val) {
165                    if (!gb) {
166                        GB_internal_error("Cannot unfold data");
167                        continue;
168                    }
169                    else {
170                        if (!gb_find_value_equal(gb, type, val, case_sens)) continue;
171                    }
172                }
173                if (!skip_over--) return gb;
174            }
175        }
176    }
177    return NULL;
178}
179
180GBDATA *GB_find_sub_by_quark(GBDATA *father, GBQUARK key_quark, GBDATA *after, size_t skip_over) {
181    return find_sub_by_quark(father, key_quark, GB_NONE, NULL, GB_MIND_CASE, after, skip_over);
182}
183
184static GBDATA *GB_find_subcontent_by_quark(GBDATA *father, GBQUARK key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after, size_t skip_over) {
185    return find_sub_by_quark(father, key_quark, type, val, case_sens, after, skip_over);
186}
187
188static GBDATA *find_sub_sub_by_quark(GBDATA *father, const char *key, GBQUARK sub_key_quark, GB_TYPES type, const char *val, GB_CASE case_sens, GBDATA *after) {
189    int             end, index;
190    gb_header_list *header;
191    GBCONTAINER    *gbf  = (GBCONTAINER*)father;
192    GBDATA         *gb;
193    GBDATA         *res;
194    gb_index_files *ifs  = NULL;
195    GB_MAIN_TYPE   *Main = GBCONTAINER_MAIN(gbf);
196
197    end  = gbf->d.nheader;
198    header = GB_DATA_LIST_HEADER(gbf->d);
199
200    if (after) index = (int)after->index+1; else index = 0;
201
202    // ****** look for any hash index tables ********
203    // ****** no wildcards allowed       *******
204    if (!Main->local_mode) {
205        if (gbf->flags2.folded_container) {
206            // do the query in the server
207            if (GB_ARRAY_FLAGS(gbf).changed) {
208                if (!gbf->flags2.update_in_server) {
209                    GB_update_server((GBDATA *)gbf);
210                }
211            }
212        }
213        if (gbf->d.size > GB_MAX_LOCAL_SEARCH && val) {
214            if (after) res = GBCMC_find(after,  key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
215            else       res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
216            return res;
217        }
218    }
219    if (val &&
220        (ifs=GBCONTAINER_IFS(gbf))!=NULL &&
221        (!strchr(val, '*')) &&
222        (!strchr(val, '?')))
223    {
224        for (; ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
225            if (ifs->key != sub_key_quark) continue;
226            // ***** We found the index table *****
227            res = gb_index_find(gbf, ifs, sub_key_quark, val, case_sens, index);
228            return res;
229        }
230    }
231
232    if (after)  gb = after;
233    else        gb = NULL;
234
235    for (; index < end; index++) {
236        GBDATA *gbn = GB_HEADER_LIST_GBD(header[index]);
237
238        if (header[index].flags.changed >= GB_DELETED) continue;
239        if (!gbn) {
240            if (!Main->local_mode) {
241                if (gb) res = GBCMC_find(gb,     key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
242                else    res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
243                return res;
244            }
245            GB_internal_error("Empty item in server");
246            continue;
247        }
248        gb = gbn;
249        if (GB_TYPE(gb) != GB_DB) continue;
250        res = GB_find_subcontent_by_quark(gb, sub_key_quark, type, val, case_sens, NULL, 0);
251        if (res) return res;
252    }
253    return NULL;
254}
255
256
257static GBDATA *gb_find_internal(GBDATA *gbd, const char *key, GB_TYPES type, const char *val, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
258    GBDATA *result = NULL;
259
260    if (gbd) {
261        GBDATA      *after = NULL;
262        GBCONTAINER *gbc   = NULL;
263
264        switch (gbs) {
265            case SEARCH_NEXT_BROTHER:
266                after = gbd;
267            case SEARCH_BROTHER:
268                gbs   = SEARCH_CHILD;
269                gbc   = GB_FATHER(gbd);
270                break;
271
272            case SEARCH_CHILD:
273            case SEARCH_GRANDCHILD:
274                if (GB_TYPE(gbd) == GB_DB) gbc = (GBCONTAINER*)gbd;
275                break;
276
277            case SEARCH_CHILD_OF_NEXT:
278                after = gbd;
279                gbs   = SEARCH_GRANDCHILD;
280                gbc   = GB_FATHER(gbd);
281                break;
282        }
283
284        if (gbc) {
285            GBQUARK key_quark = key ? GB_key_2_quark(gbd, key) : -1;
286
287            if (gbs == SEARCH_CHILD) {
288                result = GB_find_subcontent_by_quark((GBDATA*)gbc, key_quark, type, val, case_sens, after, 0);
289            }
290            else {
291                gb_assert(gbs == SEARCH_GRANDCHILD);
292                result = find_sub_sub_by_quark((GBDATA*)gbc, key, key_quark, type, val, case_sens, after);
293            }
294        }
295    }
296    return result;
297}
298
299GBDATA *GB_find(GBDATA *gbd, const char *key, GB_SEARCH_TYPE gbs) {
300    // normally you should not need to use GB_find!
301    // better use one of the replacement functions
302    // (GB_find_string, GB_find_int, GB_child, GB_nextChild, GB_entry, GB_nextEntry, GB_brother)
303    return gb_find_internal(gbd, key, GB_NONE, NULL, GB_CASE_UNDEFINED, gbs);
304}
305
306GBDATA *GB_find_string(GBDATA *gbd, const char *key, const char *str, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
307    // search for a subentry of 'gbd' that has
308    // - fieldname 'key'
309    // - type GB_STRING and
310    // - content matching 'str'
311    // if 'case_sensitive' is true, content is matched case sensitive.
312    // GBS_string_matches is used to compare (supports wildcards)
313    return gb_find_internal(gbd, key, GB_STRING, str, case_sens, gbs);
314}
315NOT4PERL GBDATA *GB_find_int(GBDATA *gbd, const char *key, long val, GB_SEARCH_TYPE gbs) {
316    // search for a subentry of 'gbd' that has
317    // - fieldname 'key'
318    // - type GB_INT
319    // - and value 'val'
320    return gb_find_internal(gbd, key, GB_INT, (const char *)&val, GB_CASE_UNDEFINED, gbs);
321}
322
323// ----------------------------------------------------
324//      iterate over ALL subentries of a container
325
326GBDATA *GB_child(GBDATA *father) {
327    // return first child (or NULL if no children)
328    return GB_find(father, NULL, SEARCH_CHILD);
329}
330GBDATA *GB_nextChild(GBDATA *child) {
331    // return next child after 'child' (or NULL if no more children)
332    return GB_find(child, NULL, SEARCH_NEXT_BROTHER);
333}
334
335// ------------------------------------------------------------------------------
336//      iterate over all subentries of a container that have a specified key
337
338GBDATA *GB_entry(GBDATA *father, const char *key) { 
339    // return first child of 'father' that has fieldname 'key'
340    // (or NULL if none found)
341    return GB_find(father, key, SEARCH_CHILD);
342}
343GBDATA *GB_nextEntry(GBDATA *entry) { 
344    // return next child after 'entry', that has the same fieldname
345    // (or NULL if 'entry' is last one)
346    return GB_find_sub_by_quark((GBDATA*)GB_FATHER(entry), GB_get_quark(entry), entry, 0);
347}
348GBDATA *GB_followingEntry(GBDATA *entry, size_t skip_over) {
349    // return following child after 'entry', that has the same fieldname
350    // (or NULL if no such entry)
351    // skips 'skip_over' entries (skip_over == 0 behaves like GB_nextEntry)
352    return GB_find_sub_by_quark((GBDATA*)GB_FATHER(entry), GB_get_quark(entry), entry, skip_over);
353}
354
355GBDATA *GB_brother(GBDATA *entry, const char *key) {
356    // searches (first) brother (before or after) of 'entry' which has field 'key'
357    // i.e. does same as GB_entry(GB_get_father(entry), key)
358    return GB_find(entry, key, SEARCH_BROTHER);
359}
360
361GBDATA *gb_find_by_nr(GBDATA *father, int index) {
362    /* get a subentry by its internal number:
363       Warning: This subentry must exists, otherwise internal error */
364
365    GBCONTAINER    *gbf = (GBCONTAINER*)father;
366    gb_header_list *header;
367    GBDATA         *gb;
368
369    if (GB_TYPE(father) != GB_DB) {
370        GB_internal_error("type is not GB_DB");
371        return NULL;
372    }
373    header = GB_DATA_LIST_HEADER(gbf->d);
374    if (index >= gbf->d.nheader || index <0) {
375        GB_internal_errorf("Index '%i' out of range [%i:%i[", index, 0, gbf->d.nheader);
376        return NULL;
377    }
378    if (header[index].flags.changed >= GB_DELETED || !header[index].flags.key_quark) {
379        GB_internal_error("Entry already deleted");
380        return NULL;
381    }
382    if (!(gb=GB_HEADER_LIST_GBD(header[index])))
383    {
384        gb_unfold(gbf, 0, index);
385        header = GB_DATA_LIST_HEADER(gbf->d);
386        gb = GB_HEADER_LIST_GBD(header[index]);
387        if (!gb) {
388            GB_internal_error("Could not unfold data");
389            return NULL;
390        }
391    }
392    return gb;
393}
394
395static char  gb_ctype_table[256];
396void gb_init_ctype_table() {
397    int i;
398    for (i=0; i<256; i++) {
399        if (islower(i) || isupper(i) || isdigit(i) || i=='_' || i=='@') {
400            gb_ctype_table[i] = 1;
401        }
402        else {
403            gb_ctype_table[i] = 0;
404        }
405    }
406}
407
408inline char *gb_first_non_key_character(const char *str) {
409    const char *s = str;
410    int c;
411    while (1) {
412        c = *s;
413        if (!gb_ctype_table[c]) {
414            if (c == 0) break;
415            return (char *)(s);
416        }
417        s++;
418    }
419    return NULL;
420}
421
422char *GB_first_non_key_char(const char *str) {
423    return gb_first_non_key_character(str);
424}
425
426GBDATA *gb_search(GBDATA * gbd, const char *str, GB_TYPES create, int internflag)
427{
428    /* finds a hierarchical key,
429       if create != GB_FIND(==0), then create the key
430       force types if ! internflag
431    */
432
433    char   *s1, *s2;
434    GBDATA *gbp, *gbsp;
435    int     len;
436    int     separator = 0;
437    char    buffer[GB_PATH_MAX];
438
439    GB_test_transaction(gbd);
440    if (!str) {
441        return GB_child(gbd);
442    }
443    if (*str == '/') {
444        gbd = GB_get_root(gbd);
445        str++;
446    }
447
448    if (!gb_first_non_key_character(str)) {
449        gbsp = GB_entry(gbd, str);
450        if (gbsp && create) {
451            GB_TYPES oldType = GB_TYPE(gbsp);
452            if (create != oldType) { // type mismatch
453                GB_export_errorf("Inconsistent type for field '%s' (existing=%i, expected=%i)", str, oldType, create);
454                return NULL;
455            }
456        }
457        if (!gbsp && create) {
458            if (internflag) {
459                if (create == GB_CREATE_CONTAINER) {
460                    gbsp = gb_create_container(gbd, str);
461                }
462                else {
463                    gbsp = gb_create(gbd, str, create);
464                }
465            }
466            else {
467                if (create == GB_CREATE_CONTAINER) {
468                    gbsp = GB_create_container(gbd, str);
469                }
470                else {
471                    gbsp = gb_create(gbd, str, create);
472                }
473            }
474            if (!gbsp) GB_print_error();
475        }
476        return gbsp;
477    }
478    {
479        len = strlen(str)+1;
480        if (len > GB_PATH_MAX) {
481            GB_internal_errorf("Path Length '%i' exceeded by '%s'", GB_PATH_MAX, str);
482            return NULL;
483        }
484        memcpy(buffer, str, len);
485    }
486
487    gbp = gbd;
488    for (s1 = buffer; s1; s1 = s2) {
489
490        s2 = gb_first_non_key_character(s1);
491        if (s2) {
492            separator = *s2;
493            *(s2++) = 0;
494            if (separator == '-') {
495                if ((*s2)  != '>') {
496                    GB_export_errorf("Invalid key for gb_search '%s'", str);
497                    GB_print_error();
498                    return NULL;
499                }
500                s2++;
501            }
502        }
503
504        if (strcmp("..", s1) == 0) {
505            gbsp = GB_get_father(gbp);
506        }
507        else {
508            gbsp = GB_entry(gbp, s1);
509            if (gbsp && separator == '-') { // follow link !!!
510                if (GB_TYPE(gbsp) != GB_LINK) {
511                    if (create) {
512                        GB_export_error("Cannot create links on the fly in GB_search");
513                        GB_print_error();
514                    }
515                    return NULL;
516                }
517                gbsp = GB_follow_link(gbsp);
518                separator = 0;
519                if (!gbsp) return NULL; // cannot resolve link
520            }
521            while (gbsp && create) {
522                if (s2) {                           // non terminal
523                    if (GB_DB == GB_TYPE(gbsp)) break;
524                }
525                else {                              // terminal
526                    if (create == GB_TYPE(gbsp)) break;
527                }
528                GB_internal_errorf("Inconsistent Type %u:%u '%s':'%s', repairing database", create, GB_TYPE(gbsp), str, s1);
529                GB_print_error();
530                GB_delete(gbsp);
531                gbsp = GB_entry(gbd, s1);
532            }
533        }
534        if (!gbsp) {
535            if (!create) return NULL; // read only mode
536            if (separator == '-') {
537                GB_export_error("Cannot create linked objects");
538                return NULL; // do not create linked objects
539            }
540
541            if (s2 || (create == GB_CREATE_CONTAINER)) {
542                gbsp = internflag
543                    ? gb_create_container(gbp, s1)
544                    : GB_create_container(gbp, s1);
545            }
546            else {
547                gbsp = GB_create(gbp, s1, (GB_TYPES)create);
548                if (create == GB_STRING) {
549                    GB_ERROR error = GB_write_string(gbsp, "");
550                    if (error) GB_internal_error("Couldn't write to just created string entry");
551                }
552            }
553
554            if (!gbsp) return NULL;
555        }
556        gbp = gbsp;
557    }
558    return gbp;
559}
560
561
562GBDATA *GB_search(GBDATA * gbd, const char *fieldpath, GB_TYPES create) {
563    return gb_search(gbd, fieldpath, create, 0);
564}
565
566static GBDATA *gb_expect_type(GBDATA *gbd, long expected_type, const char *fieldname) {
567    gb_assert(expected_type != GB_FIND); // impossible
568
569    long type = GB_TYPE(gbd);
570    if (type != expected_type) {
571        GB_export_errorf("Field '%s' has wrong type (found=%li, expected=%li)", fieldname, type, expected_type);
572        gbd = 0;
573    }
574    return gbd;
575}
576
577GBDATA *GB_searchOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
578    GBDATA *gb_str = GB_search(gb_container, fieldpath, GB_FIND);
579    if (!gb_str) {
580        gb_str = GB_search(gb_container, fieldpath, GB_STRING);
581        GB_ERROR error;
582
583        if (!gb_str) error = GB_await_error();
584        else error         = GB_write_string(gb_str, default_value);
585
586        if (error) {
587            gb_str = 0;
588            GB_export_error(error);
589        }
590    }
591    else {
592        gb_str = gb_expect_type(gb_str, GB_STRING, fieldpath);
593    }
594    return gb_str;
595}
596
597GBDATA *GB_searchOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value) {
598    GBDATA *gb_int = GB_search(gb_container, fieldpath, GB_FIND);
599    if (!gb_int) {
600        gb_int = GB_search(gb_container, fieldpath, GB_INT);
601        GB_ERROR error;
602
603        if (!gb_int) error = GB_await_error();
604        else error         = GB_write_int(gb_int, default_value);
605
606        if (error) {
607            gb_int = 0;
608            GB_export_error(error);
609        }
610    }
611    else {
612        gb_int = gb_expect_type(gb_int, GB_INT, fieldpath);
613    }
614    return gb_int;
615}
616
617GBDATA *GB_searchOrCreate_float(GBDATA *gb_container, const char *fieldpath, double default_value) {
618    GBDATA *gb_float = GB_search(gb_container, fieldpath, GB_FIND);
619    if (!gb_float) {
620        gb_float = GB_search(gb_container, fieldpath, GB_FLOAT);
621        GB_ERROR error;
622
623        if (!gb_float) error = GB_await_error();
624        else error           = GB_write_float(gb_float, default_value);
625
626        if (error) {
627            gb_float = 0;
628            GB_export_error(error);
629        }
630    }
631    else {
632        gb_float = gb_expect_type(gb_float, GB_FLOAT, fieldpath);
633    }
634    return gb_float;
635}
636
637static GBDATA *gb_search_marked(GBCONTAINER *gbc, GBQUARK key_quark, int firstindex, size_t skip_over) {
638    int             userbit = GBCONTAINER_MAIN(gbc)->users[0]->userbit;
639    int             index;
640    int             end     = gbc->d.nheader;
641    gb_header_list *header  = GB_DATA_LIST_HEADER(gbc->d);
642
643    for (index = firstindex; index<end; index++) {
644        GBDATA *gb;
645
646        if (! (userbit & header[index].flags.flags)) continue;
647        if ((key_quark>=0) && (header[index].flags.key_quark  != key_quark)) continue;
648        if (header[index].flags.changed >= GB_DELETED) continue;
649        if ((gb=GB_HEADER_LIST_GBD(header[index]))==NULL) {
650            gb_unfold(gbc, 0, index);
651            header = GB_DATA_LIST_HEADER(gbc->d);
652            gb = GB_HEADER_LIST_GBD(header[index]);
653        }
654        if (!skip_over--) return gb;
655    }
656    return NULL;
657}
658
659GBDATA *GB_search_last_son(GBDATA *gbd) {
660    GBCONTAINER    *gbc    = (GBCONTAINER *)gbd;
661    int             index;
662    int             end    = gbc->d.nheader;
663    GBDATA         *gb;
664    gb_header_list *header = GB_DATA_LIST_HEADER(gbc->d);
665
666    for (index = end-1; index>=0; index--) {
667        if (header[index].flags.changed >= GB_DELETED) continue;
668        if ((gb=GB_HEADER_LIST_GBD(header[index]))==NULL)
669        {
670            gb_unfold(gbc, 0, index);
671            header = GB_DATA_LIST_HEADER(gbc->d);
672            gb = GB_HEADER_LIST_GBD(header[index]);
673        }
674        return gb;
675    }
676    return NULL;
677}
678
679long GB_number_of_marked_subentries(GBDATA *gbd) {
680    GBCONTAINER    *gbc     = (GBCONTAINER *)gbd;
681    int             userbit = GBCONTAINER_MAIN(gbc)->users[0]->userbit;
682    int             index;
683    int             end     = gbc->d.nheader;
684    gb_header_list *header;
685    long            count   = 0;
686
687    header = GB_DATA_LIST_HEADER(gbc->d);
688    for (index = 0; index<end; index++) {
689        if (! (userbit & header[index].flags.flags)) continue;
690        if (header[index].flags.changed >= GB_DELETED) continue;
691        count++;
692    }
693    return count;
694}
695
696
697
698GBDATA *GB_first_marked(GBDATA *gbd, const char *keystring) {
699    GBCONTAINER *gbc = (GBCONTAINER *)gbd;
700    GBQUARK key_quark;
701    if (keystring) {
702        key_quark = GB_key_2_quark(gbd, keystring);
703    }
704    else {
705        key_quark = -1;
706    }
707    GB_test_transaction(gbd);
708    return gb_search_marked(gbc, key_quark, 0, 0);
709}
710
711
712GBDATA *GB_following_marked(GBDATA *gbd, const char *keystring, size_t skip_over) {
713    GBCONTAINER *gbc = GB_FATHER(gbd);
714    GBQUARK key_quark;
715
716    if (keystring) {
717        key_quark = GB_key_2_quark(gbd, keystring);
718    }
719    else {
720        key_quark = -1;
721    }
722    GB_test_transaction(gbd);
723    return gb_search_marked(gbc, key_quark, (int)gbd->index+1, skip_over);
724}
725
726GBDATA *GB_next_marked(GBDATA *gbd, const char *keystring) {
727    return GB_following_marked(gbd, keystring, 0);
728}
729
730// ----------------------------
731//      Command interpreter
732
733void gb_install_command_table(GBDATA *gb_main, struct GBL_command_table *table, size_t table_size)
734{
735    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
736    if (!Main->command_hash) Main->command_hash = GBS_create_hash(table_size, GB_IGNORE_CASE);
737
738    for (; table->command_identifier; table++) {
739        GBS_write_hash(Main->command_hash, table->command_identifier, (long)table->function);
740    }
741
742    gb_assert((GBS_hash_count_elems(Main->command_hash)+1) == table_size);
743}
744
745static char *gbs_search_second_x(const char *str) {
746    int c;
747    for (; (c=*str); str++) {
748        if (c=='\\') {      // escaped characters
749            str++;
750            if (!(c=*str)) return NULL;
751            continue;
752        }
753        if (c=='"') return (char *)str;
754    }
755    return NULL;
756}
757
758char *gbs_search_second_bracket(const char *source)
759{
760    int c;
761    int deep = 0;
762    if (*source != '(') deep --;    // first bracket
763    for (; (c=*source); source++) {
764        if (c=='\\') {      // escaped characters
765            source++;
766            if (!*source) break;
767            continue;
768        }
769        if (c=='(') deep--;
770        else if (c==')') deep++;
771        if (!deep) return (char *)source;
772        if (c=='"') {       // search the second "
773            source = gbs_search_second_x(source);
774            if (!source) return NULL;
775        }
776    }
777    if (!c) return NULL;
778    return (char *)source;
779}
780
781
782static char *gbs_search_next_separator(const char *source, const char *seps) {
783    // search the next separator
784    static char tab[256];
785    static int flag = 0;
786    int c;
787    const char *p;
788    if (!flag) {
789        flag = 1;
790        memset(tab, 0, 256);
791    }
792    for (p = seps; (c=*p); p++) tab[c] = 1; // tab[seps[x]] = 1
793    tab['('] = 1;               // exclude () pairs
794    tab['"'] = 1;               // exclude " pairs
795    tab['\\'] = 1;              // exclude \-escaped chars
796
797    for (; (c=*source); source++) {
798        if (tab[c]) {
799            if (c=='\\') {
800                source++;
801                continue;
802            }
803            if (c=='(') {
804                source = gbs_search_second_bracket(source);
805                if (!source) break;
806                continue;
807            }
808            if (c=='"') {
809                source = gbs_search_second_x(source+1);
810                if (!source) break;
811                continue;
812            }
813            for (p = seps; (c=*p); p++) tab[c] = 0;
814            return (char *)source;
815        }
816    }
817    for (p = seps; (c=*p); p++) tab[c] = 0; // clear tab
818    return NULL;
819}
820
821static void dumpStreams(const char *name, int count, const GBL *args) {
822    printf("%s=%i\n", name, count);
823    if (count > 0) {
824        int c;
825        for (c = 0; c<count; c++) {
826            printf("  %02i='%s'\n", c, args[c].str);
827        }
828    }
829}
830
831static const char *shortenLongString(const char *str, size_t wanted_len) {
832    // shortens the string 'str' to 'wanted_len' (appends '[..]' if string was shortened)
833
834    const char *result;
835    size_t      len = strlen(str);
836
837    gb_assert(wanted_len>4);
838
839    if (len>wanted_len) {
840        static char   *shortened_str;
841        static size_t  short_len = 0;
842
843        if (short_len >= wanted_len) {
844            memcpy(shortened_str, str, wanted_len-4);
845        }
846        else {
847            freeset(shortened_str, GB_strpartdup(str, str+wanted_len));
848            short_len = wanted_len;
849        }
850        strcpy(shortened_str+wanted_len-4, "[..]");
851        result = shortened_str;
852    }
853    else {
854        result = str;
855    }
856    return result;
857}
858
859#if defined(WARN_TODO)
860#warning rewrite GB_command_interpreter (error+ressource handling)
861#endif
862
863char *GB_command_interpreter(GBDATA *gb_main, const char *str, const char *commands, GBDATA *gbd, const char *default_tree_name) {
864    /* simple command interpreter returns NULL on error (which should be exported in that case)
865     * if first character is == ':' run string parser
866     * if first character is == '/' run regexpr
867     * else command interpreter
868     */
869    int           strmalloc = 0;
870    char         *buffer;
871    GB_ERROR      error;
872    int           i;
873    int           argcinput;
874    int           argcparam;
875    int           argcout;
876    char         *bracket;
877    GB_MAIN_TYPE *Main      = GB_MAIN(gb_main);
878
879    GBL morig[GBL_MAX_ARGUMENTS];
880    GBL min[GBL_MAX_ARGUMENTS];
881    GBL mout[GBL_MAX_ARGUMENTS];
882
883    GBL *orig  = & morig[0];
884    GBL *in    = & min[0];
885    GBL *out   = & mout[0];
886    int  trace = GB_get_ACISRT_trace();
887
888    if (!str) {
889        if (!gbd) {
890            GB_export_error("ACI: no input streams found");
891            return NULL;
892        }
893        str = GB_read_as_string(gbd);
894        strmalloc = 1;
895    }
896
897    if (trace) {
898        printf("GB_command_interpreter: str='%s'\n"
899               "                        command='%s'\n", str, commands);
900    }
901
902    if (!commands || !commands[0]) { // empty command -> do not modify string
903        if (!strmalloc) return strdup(str);
904        return (char *)str;
905    }
906
907    if (commands[0] == ':') { // ':' -> string parser
908        return GBS_string_eval(str, commands+1, gbd);
909    }
910
911    if (commands[0] == '/') { // regular expression
912        GB_ERROR  err    = 0;
913        char     *result = GBS_regreplace(str, commands, &err);
914
915        if (!result) {
916            if (strcmp(err, "Missing '/' between search and replace string") == 0) {
917                // if GBS_regreplace didn't find a third '/' -> silently use GBS_regmatch:
918                size_t matchlen;
919                err    = 0;
920                const char *matched = GBS_regmatch(str, commands, &matchlen, &err);
921
922                if (matched) result   = GB_strndup(matched, matchlen);
923                else if (!err) result = strdup("");
924            }
925
926            if (!result && err) result = GBS_global_string_copy("<Error: %s>", err);
927        }
928        return result;
929    }
930
931    // ********************** init *******************
932
933    gb_local->gbl.gb_main = gb_main;
934    buffer = strdup(commands);
935
936    // ********************** remove all spaces and tabs *******************
937    {
938        const char *s1;
939        char *s2;
940        s1 = commands;
941        s2 = buffer;
942        {
943            int c;
944            for (; (c = *s1); s1++) {
945                if (c=='\\') {
946                    *(s2++) = c;
947                    if (!(c=*s1)) { break; }
948                    *(s2++) = c;
949                    continue;
950                }
951
952                if (c=='"') {       // search the second "
953                    const char *hp = gbs_search_second_x(s1+1);
954                    if (!hp) {
955                        GB_export_errorf("unbalanced '\"' in '%s'", commands);
956                        return NULL;
957                    }
958                    while (s1 <= hp) *(s2++) = *(s1++);
959                    s1--;
960                    continue;
961                }
962                if (c!=' ' && c!='\t') *(s2++) = c;
963            }
964        }
965        *s2 = 0;
966    }
967
968
969
970    memset((char *)orig, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
971    memset((char *)in, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
972    memset((char *)out, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
973
974    if (strmalloc) {
975        orig[0].str = (char *)str;
976    }
977    else {
978        orig[0].str = strdup(str);
979    }
980
981    argcinput = 1;
982    argcout   = 0;
983    error     = 0;
984    {
985        char *s1, *s2;
986        s1 = buffer;
987        if (*s1 == '|') s1++;
988
989        // ** loop over all commands **
990        for (s1 = s1; s1;  s1 = s2) {
991            int separator;
992            GBL_COMMAND command;
993            s2 = gbs_search_next_separator(s1, "|;,");
994            if (s2) {
995                separator = *(s2);
996                *(s2++) = 0;
997            }
998            else {
999                separator = 0;
1000            }
1001            // collect the parameters
1002            memset((char*)in, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
1003            if (*s1 == '"') {           // copy "text" to out
1004                char *end = gbs_search_second_x(s1+1);
1005                if (!end) {
1006                    error = "Missing second '\"'";
1007                    break;
1008                }
1009                *end = 0;
1010                out[argcout++].str = strdup(s1+1);
1011            }
1012            else {
1013                argcparam = 0;
1014                bracket = strchr(s1, '(');
1015                if (bracket) {      // I got the parameter list
1016                    int slen;
1017                    *(bracket++) = 0;
1018                    slen  = strlen(bracket);
1019                    if (bracket[slen-1] != ')') {
1020                        error = "Missing ')'";
1021                    }
1022                    else {
1023                        // go through the parameters
1024                        char *p1, *p2;
1025                        bracket[slen-1] = 0;
1026                        for (p1 = bracket; p1;  p1 = p2) {
1027                            p2 = gbs_search_next_separator(p1, ";,");
1028                            if (p2) {
1029                                *(p2++) = 0;
1030                            }
1031                            if (p1[0] == '"') { // remove "" pairs
1032                                int len2;
1033                                p1++;
1034                                len2 = strlen(p1)-1;
1035
1036                                if (p1[len2] != '\"') {
1037                                    error = "Missing '\"'";
1038                                }
1039                                else {
1040                                    p1[len2] = 0;
1041                                }
1042                            }
1043                            in[argcparam++].str = strdup(p1);
1044                        }
1045                    }
1046                    if (error) break;
1047                }
1048                if (!error && (bracket || *s1)) {
1049                    char *p = s1;
1050                    int c;
1051                    while ((c = *p)) {          // command to lower case
1052                        if (c>='A' && c<='Z') {
1053                            c += 'a'-'A';
1054                            *p = c;
1055                        }
1056                        p++;
1057                    }
1058
1059                    command = (GBL_COMMAND)GBS_read_hash(Main->command_hash, s1);
1060                    if (!command) {
1061                        error = GBS_global_string("Unknown command '%s'", s1);
1062                    }
1063                    else {
1064                        GBL_command_arguments args;
1065                        args.gb_ref            = gbd;
1066                        args.default_tree_name = default_tree_name;
1067                        args.command           = s1;
1068                        args.cinput            = argcinput;
1069                        args.vinput            = orig;
1070                        args.cparam            = argcparam;
1071                        args.vparam            = in;
1072                        args.coutput           = &argcout;
1073                        args.voutput           = &out;
1074
1075                        if (trace) {
1076                            printf("-----------------------\nExecution of command '%s':\n", args.command);
1077                            dumpStreams("Arguments", args.cparam, args.vparam);
1078                            dumpStreams("InputStreams", args.cinput, args.vinput);
1079                        }
1080
1081                        error = command(&args); // execute the command
1082
1083                        if (!error && trace) dumpStreams("OutputStreams", *args.coutput, *args.voutput);
1084
1085                        if (error) {
1086                            char *inputstreams = 0;
1087                            char *paramlist    = 0;
1088                            int   j;
1089
1090#define MAX_PRINT_LEN 200
1091
1092                            for (j = 0; j<args.cparam; ++j) {
1093                                const char *param       = args.vparam[j].str;
1094                                const char *param_short = shortenLongString(param, MAX_PRINT_LEN);
1095
1096                                if (!paramlist) paramlist = strdup(param_short);
1097                                else freeset(paramlist, GBS_global_string_copy("%s,%s", paramlist, param_short));
1098                            }
1099                            for (j = 0; j<args.cinput; ++j) {
1100                                const char *param       = args.vinput[j].str;
1101                                const char *param_short = shortenLongString(param, MAX_PRINT_LEN);
1102
1103                                if (!inputstreams) inputstreams = strdup(param_short);
1104                                else freeset(inputstreams, GBS_global_string_copy("%s;%s", inputstreams, param_short));
1105                            }
1106#undef MAX_PRINT_LEN
1107                            if (paramlist) {
1108                                error = GBS_global_string("while applying '%s(%s)'\nto '%s':\n%s", s1, paramlist, inputstreams, error);
1109                            }
1110                            else {
1111                                error = GBS_global_string("while applying '%s'\nto '%s':\n%s", s1, inputstreams, error);
1112                            }
1113
1114                            free(inputstreams);
1115                            free(paramlist);
1116                        }
1117                    }
1118                }
1119
1120                for (i=0; i<argcparam; i++) {   // free intermediate arguments
1121                    if (in[i].str) free(in[i].str);
1122                }
1123            }
1124
1125            if (error) break;
1126
1127            if (separator == '|') {         // swap in and out in pipes
1128                GBL *h;
1129                for (i=0; i<argcinput; i++) {
1130                    if (orig[i].str)    free(orig[i].str);
1131                }
1132                memset((char*)orig, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
1133                argcinput = 0;
1134
1135                h = out;            // swap orig and out
1136                out = orig;
1137                orig = h;
1138
1139                argcinput = argcout;
1140                argcout = 0;
1141            }
1142
1143        }
1144    }
1145    for (i=0; i<argcinput; i++) {
1146        if (orig[i].str) free(orig[i].str);
1147    }
1148
1149    {
1150        char *s1;
1151        if (!argcout) {
1152            s1 = strdup(""); // returned '<NULL>' in the past
1153        }
1154        else if (argcout == 1) {
1155            s1 = out[0].str;
1156        }
1157        else {             // concatenate output strings
1158            GBS_strstruct *strstruct = GBS_stropen(1000);
1159            for (i=0; i<argcout; i++) {
1160                if (out[i].str) {
1161                    GBS_strcat(strstruct, out[i].str);
1162                    free(out[i].str);
1163                }
1164            }
1165            s1 = GBS_strclose(strstruct);
1166        }
1167        free(buffer);
1168
1169        if (!error) {
1170            if (trace) printf("GB_command_interpreter: result='%s'\n", s1);
1171            return s1;
1172        }
1173        free(s1);
1174    }
1175
1176    GB_export_errorf("Command '%s' failed:\nReason: %s", commands, error);
1177    return NULL;
1178}
1179
Note: See TracBrowser for help on using the repository browser.