root/trunk/ARBDB/adquery.cxx

Revision 8620, 52.4 KB (checked in by westram, 5 weeks ago)

merge from e4fix [8303] [8304] [8305] [8306] [8307] [8330] [8331] [8332] [8333] [8334] [8335] [8336] [8337] [8338] [8339]

  • moved variables handling selected block into new class ED4_block
  • removed ED4_species_container_changed_cb
    • removed GB_search_last_son (only used there)
  • fixed '(re)load config'
  • ED4_WinContext missing
    • in create species functions
    • in ED4_remote_set_cursor_cb
    • when changing HELIX-inputfield
  • NULL-deref in ED4_host::announce_current_species (if no species selected yet)
  • DRYed get_area_level
  • get_upper_lower_cursor_pos
    • no longer a member of ED4_cursor
    • replaced callback+cb-arg-param by terminal predicate
    • loop backwards through hierarchy when moving cursor up
    • DRYed duplicated code in loop
  • calc_world_coords now const
  • when moving cursor vertically, scroll display if target terminal is only partly visible
  • fixed scroll_into_view
  • moved visibility checks from ED4_base to ED4_window
  • set_properties -> set_property (added inverse function clr_property)
  • ED4_manager::un/fold_group moved to ED4_bracket_terminal::un/fold
  • do group-size calculation via update flags (first request; recalc during refresh)
  • when creating new group: correctly calc group size and selection status for source group(s)
  • fixed a buffer-overflow in group_species
  • display ORF terminals at cursor of MOST RECENT window (hack)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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            GBK_terminate("cant search float by value"); // @@@ search by comparing floats is nonsense - should be removed/replaced/rewritten
87            double d                      = GB_read_float(gb);
88            if (d == *(double*)val) equal = true; // (no aliasing problem here; char* -> double* ok)
89            break;
90        }
91        default: {
92            const char *err = GBS_global_string("Value search not supported for data type %i (%i)", GB_TYPE(gb), type);
93            GB_internal_error(err);
94            break;
95        }
96    }
97
98    return equal;
99}
100
101static 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) {
102    /* search an entry with a key 'key_quark' below a container 'father'
103       after position 'after'
104
105       if 'skip_over' > 0 search skips 'skip_over' entries
106
107       if (val != NULL) search for entry with value 'val':
108
109       GB_STRING/GB_LINK: compares string (case_sensitive or not)
110       GB_INT: compares values
111       GB_FLOAT: ditto (val MUST be a 'double*')
112       others: not implemented yet
113
114       Note: to search for non-char*-values use GB_find_int()
115             for other types write a new similar function
116
117             if key_quark<0 search everything
118    */
119
120    int             end, index;
121    GBCONTAINER    *gbf = (GBCONTAINER*)father;
122    gb_header_list *header;
123    GBDATA         *gb;
124
125    end  = gbf->d.nheader;
126    header = GB_DATA_LIST_HEADER(gbf->d);
127    if (after) index = (int)after->index+1; else index = 0;
128
129    if (key_quark<0) { // unspecific key quark (i.e. search all)
130        gb_assert(!val);        // search for val not possible if searching all keys!
131        if (!val) {
132            for (; index < end; index++) {
133                if (header[index].flags.key_quark != 0) {
134                    if (header[index].flags.changed >= GB_DELETED) continue;
135                    if (!(gb=GB_HEADER_LIST_GBD(header[index]))) {
136                        // @@@ DRY here versus below
137                        gb_unfold(gbf, 0, index);
138                        header = GB_DATA_LIST_HEADER(gbf->d);
139                        gb     = GB_HEADER_LIST_GBD(header[index]);
140                        if (!gb) {
141                            const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
142                            GB_internal_error(err);
143                            continue;
144                        }
145                    }
146                    if (!skip_over--) return gb;
147                }
148            }
149        }
150    }
151    else { // specific key quark
152        for (; index < end; index++) {
153            if ((key_quark == header[index].flags.key_quark)) {
154                if (header[index].flags.changed >= GB_DELETED) continue;
155                if (!(gb=GB_HEADER_LIST_GBD(header[index]))) {
156                    // @@@ DRY here versus section above
157                    gb_unfold(gbf, 0, index);
158                    header = GB_DATA_LIST_HEADER(gbf->d);
159                    gb     = GB_HEADER_LIST_GBD(header[index]);
160                    if (!gb) {
161                        const char *err = GBS_global_string("Database entry #%u is missing (in '%s')", index, GB_get_GBDATA_path(father));
162                        GB_internal_error(err);
163                        continue;
164                    }
165                }
166                if (val) {
167                    if (!gb) {
168                        GB_internal_error("Cannot unfold data");
169                        continue;
170                    }
171                    else {
172                        if (!gb_find_value_equal(gb, type, val, case_sens)) continue;
173                    }
174                }
175                if (!skip_over--) return gb;
176            }
177        }
178    }
179    return NULL;
180}
181
182GBDATA *GB_find_sub_by_quark(GBDATA *father, GBQUARK key_quark, GBDATA *after, size_t skip_over) {
183    return find_sub_by_quark(father, key_quark, GB_NONE, NULL, GB_MIND_CASE, after, skip_over);
184}
185
186static 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) {
187    return find_sub_by_quark(father, key_quark, type, val, case_sens, after, skip_over);
188}
189
190static 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) {
191    int             end, index;
192    gb_header_list *header;
193    GBCONTAINER    *gbf  = (GBCONTAINER*)father;
194    GBDATA         *gb;
195    GBDATA         *res;
196    gb_index_files *ifs  = NULL;
197    GB_MAIN_TYPE   *Main = GBCONTAINER_MAIN(gbf);
198
199    end  = gbf->d.nheader;
200    header = GB_DATA_LIST_HEADER(gbf->d);
201
202    if (after) index = (int)after->index+1; else index = 0;
203
204    // ****** look for any hash index tables ********
205    // ****** no wildcards allowed       *******
206    if (!Main->local_mode) {
207        if (gbf->flags2.folded_container) {
208            // do the query in the server
209            if (GB_ARRAY_FLAGS(gbf).changed) {
210                if (!gbf->flags2.update_in_server) {
211                    GB_update_server((GBDATA *)gbf);
212                }
213            }
214        }
215        if (gbf->d.size > GB_MAX_LOCAL_SEARCH && val) {
216            if (after) res = GBCMC_find(after,  key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
217            else       res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
218            return res;
219        }
220    }
221    if (val &&
222        (ifs=GBCONTAINER_IFS(gbf))!=NULL &&
223        (!strchr(val, '*')) &&
224        (!strchr(val, '?')))
225    {
226        for (; ifs; ifs = GB_INDEX_FILES_NEXT(ifs)) {
227            if (ifs->key != sub_key_quark) continue;
228            // ***** We found the index table *****
229            res = gb_index_find(gbf, ifs, sub_key_quark, val, case_sens, index);
230            return res;
231        }
232    }
233
234    if (after)  gb = after;
235    else gb        = NULL;
236
237    for (; index < end; index++) {
238        GBDATA *gbn = GB_HEADER_LIST_GBD(header[index]);
239
240        if (header[index].flags.changed >= GB_DELETED) continue;
241        if (!gbn) {
242            if (!Main->local_mode) {
243                if (gb) res = GBCMC_find(gb,     key, type, val, case_sens, SEARCH_CHILD_OF_NEXT);
244                else    res = GBCMC_find(father, key, type, val, case_sens, SEARCH_GRANDCHILD);
245                return res;
246            }
247            GB_internal_error("Empty item in server");
248            continue;
249        }
250        gb = gbn;
251        if (GB_TYPE(gb) != GB_DB) continue;
252        res = GB_find_subcontent_by_quark(gb, sub_key_quark, type, val, case_sens, NULL, 0);
253        if (res) return res;
254    }
255    return NULL;
256}
257
258
259static GBDATA *gb_find_internal(GBDATA *gbd, const char *key, GB_TYPES type, const char *val, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
260    GBDATA *result = NULL;
261
262    if (gbd) {
263        GBDATA      *after = NULL;
264        GBCONTAINER *gbc   = NULL;
265
266        switch (gbs) {
267            case SEARCH_NEXT_BROTHER:
268                after = gbd;
269            case SEARCH_BROTHER:
270                gbs   = SEARCH_CHILD;
271                gbc   = GB_FATHER(gbd);
272                break;
273
274            case SEARCH_CHILD:
275            case SEARCH_GRANDCHILD:
276                if (GB_TYPE(gbd) == GB_DB) gbc = (GBCONTAINER*)gbd;
277                break;
278
279            case SEARCH_CHILD_OF_NEXT:
280                after = gbd;
281                gbs   = SEARCH_GRANDCHILD;
282                gbc   = GB_FATHER(gbd);
283                break;
284        }
285
286        if (gbc) {
287            GBQUARK key_quark = GB_find_existing_quark(gbd, key);
288
289            if (key_quark) { // only search if 'key' is known to db
290                if (gbs == SEARCH_CHILD) {
291                    result = GB_find_subcontent_by_quark((GBDATA*)gbc, key_quark, type, val, case_sens, after, 0);
292                }
293                else {
294                    gb_assert(gbs == SEARCH_GRANDCHILD);
295                    result = find_sub_sub_by_quark((GBDATA*)gbc, key, key_quark, type, val, case_sens, after);
296                }
297            }
298        }
299    }
300    return result;
301}
302
303GBDATA *GB_find(GBDATA *gbd, const char *key, GB_SEARCH_TYPE gbs) {
304    // normally you should not need to use GB_find!
305    // better use one of the replacement functions
306    // (GB_find_string, GB_find_int, GB_child, GB_nextChild, GB_entry, GB_nextEntry, GB_brother)
307    return gb_find_internal(gbd, key, GB_NONE, NULL, GB_CASE_UNDEFINED, gbs);
308}
309
310GBDATA *GB_find_string(GBDATA *gbd, const char *key, const char *str, GB_CASE case_sens, GB_SEARCH_TYPE gbs) {
311    // search for a subentry of 'gbd' that has
312    // - fieldname 'key'
313    // - type GB_STRING and
314    // - content matching 'str'
315    // if 'case_sensitive' is true, content is matched case sensitive.
316    // GBS_string_matches is used to compare (supports wildcards)
317    return gb_find_internal(gbd, key, GB_STRING, str, case_sens, gbs);
318}
319NOT4PERL GBDATA *GB_find_int(GBDATA *gbd, const char *key, long val, GB_SEARCH_TYPE gbs) {
320    // search for a subentry of 'gbd' that has
321    // - fieldname 'key'
322    // - type GB_INT
323    // - and value 'val'
324    return gb_find_internal(gbd, key, GB_INT, (const char *)&val, GB_CASE_UNDEFINED, gbs);
325}
326
327// ----------------------------------------------------
328//      iterate over ALL subentries of a container
329
330GBDATA *GB_child(GBDATA *father) {
331    // return first child (or NULL if no children)
332    return GB_find(father, NULL, SEARCH_CHILD);
333}
334GBDATA *GB_nextChild(GBDATA *child) {
335    // return next child after 'child' (or NULL if no more children)
336    return GB_find(child, NULL, SEARCH_NEXT_BROTHER);
337}
338
339// ------------------------------------------------------------------------------
340//      iterate over all subentries of a container that have a specified key
341
342GBDATA *GB_entry(GBDATA *father, const char *key) { 
343    // return first child of 'father' that has fieldname 'key'
344    // (or NULL if none found)
345    return GB_find(father, key, SEARCH_CHILD);
346}
347GBDATA *GB_nextEntry(GBDATA *entry) {
348    // return next child after 'entry', that has the same fieldname
349    // (or NULL if 'entry' is last one)
350    return GB_find_sub_by_quark((GBDATA*)GB_FATHER(entry), GB_get_quark(entry), entry, 0);
351}
352GBDATA *GB_followingEntry(GBDATA *entry, size_t skip_over) {
353    // return following child after 'entry', that has the same fieldname
354    // (or NULL if no such entry)
355    // skips 'skip_over' entries (skip_over == 0 behaves like GB_nextEntry)
356    return GB_find_sub_by_quark((GBDATA*)GB_FATHER(entry), GB_get_quark(entry), entry, skip_over);
357}
358
359GBDATA *GB_brother(GBDATA *entry, const char *key) {
360    // searches (first) brother (before or after) of 'entry' which has field 'key'
361    // i.e. does same as GB_entry(GB_get_father(entry), key)
362    return GB_find(entry, key, SEARCH_BROTHER);
363}
364
365GBDATA *gb_find_by_nr(GBDATA *father, int index) {
366    /* get a subentry by its internal number:
367       Warning: This subentry must exists, otherwise internal error */
368
369    GBCONTAINER    *gbf = (GBCONTAINER*)father;
370    gb_header_list *header;
371    GBDATA         *gb;
372
373    if (GB_TYPE(father) != GB_DB) {
374        GB_internal_error("type is not GB_DB");
375        return NULL;
376    }
377    header = GB_DATA_LIST_HEADER(gbf->d);
378    if (index >= gbf->d.nheader || index <0) {
379        GB_internal_errorf("Index '%i' out of range [%i:%i[", index, 0, gbf->d.nheader);
380        return NULL;
381    }
382    if (header[index].flags.changed >= GB_DELETED || !header[index].flags.key_quark) {
383        GB_internal_error("Entry already deleted");
384        return NULL;
385    }
386    if (!(gb=GB_HEADER_LIST_GBD(header[index])))
387    {
388        gb_unfold(gbf, 0, index);
389        header = GB_DATA_LIST_HEADER(gbf->d);
390        gb = GB_HEADER_LIST_GBD(header[index]);
391        if (!gb) {
392            GB_internal_error("Could not unfold data");
393            return NULL;
394        }
395    }
396    return gb;
397}
398
399static char  gb_ctype_table[256];
400void gb_init_ctype_table() {
401    int i;
402    for (i=0; i<256; i++) {
403        if (islower(i) || isupper(i) || isdigit(i) || i=='_' || i=='@') {
404            gb_ctype_table[i] = 1;
405        }
406        else {
407            gb_ctype_table[i] = 0;
408        }
409    }
410}
411
412inline const char *first_non_key_character(const char *str) {
413    while (1) {
414        int c = *str;
415        if (!gb_ctype_table[c]) {
416            if (c == 0) break;
417            return str;
418        }
419        str++;
420    }
421    return NULL;
422}
423
424const char *GB_first_non_key_char(const char *str) {
425    return first_non_key_character(str);
426}
427
428inline GBDATA *find_or_create(GBDATA *gb_parent, const char *key, GB_TYPES create, bool internflag) {
429    gb_assert(!first_non_key_character(key));
430
431    GBDATA *gbd = GB_entry(gb_parent, key);
432    if (create) {
433        if (gbd) {
434            GB_TYPES oldType = GB_TYPE(gbd);
435            if (create != oldType) { // type mismatch
436                GB_export_errorf("Inconsistent type for field '%s' (existing=%i, expected=%i)", key, oldType, create);
437                gbd = NULL;
438            }
439        }
440        else {
441            if (create == GB_CREATE_CONTAINER) {
442                gbd = internflag ? gb_create_container(gb_parent, key) : GB_create_container(gb_parent, key);
443            }
444            else {
445                gbd = gb_create(gb_parent, key, create);
446            }
447            gb_assert(gbd || GB_have_error());
448        }
449    }
450    return gbd;
451}
452
453GBDATA *gb_search(GBDATA *gbd, const char *key, GB_TYPES create, int internflag) {
454    /* finds a hierarchical key,
455     * if create != GB_FIND(==0), then create the key
456     * force types if ! internflag
457     */
458
459    // gb_assert(!GB_have_error()); // @@@ enable later
460
461    GB_test_transaction(gbd);
462
463    if (!key) return NULL; // was allowed in the past (and returned the 1st child). now returns NULL
464   
465    if (key[0] == '/') {
466        gbd = GB_get_root(gbd);
467        key++;
468    }
469
470    if (!key[0]) {
471        return gbd;
472    }
473
474    GBDATA     *gb_result     = NULL;
475    const char *separator     = first_non_key_character(key);
476    if (!separator) gb_result = find_or_create(gbd, key, create, internflag);
477    else {
478        int  len = separator-key;
479        char firstKey[len+1];
480        memcpy(firstKey, key, len);
481        firstKey[len] = 0;
482
483        char invalid_char = 0;
484
485        switch (separator[0]) {
486            case '/': {
487                GBDATA *gb_sub = find_or_create(gbd, firstKey, create ? GB_CREATE_CONTAINER : GB_FIND, internflag);
488                if (gb_sub) {
489                    if (GB_TYPE(gb_sub) == GB_DB) {
490                        if (separator[1] == '/') {
491                            GB_export_errorf("Invalid '//' in key '%s'", key);
492                        }
493                        else {
494                            gb_result = gb_search(gb_sub, separator+1, create, internflag);
495                        }
496                    }
497                    else {
498                        GB_export_errorf("terminal entry '%s' cannot be used as container", firstKey);
499                    }
500                }
501                break;
502            }
503            case '.': {
504                if (separator[1] != '.') invalid_char = separator[0];
505                else {
506                    GBDATA *gb_parent = GB_get_father(gbd);
507                    if (gb_parent) {
508                        switch (separator[2]) {
509                            case 0:   gb_result    = gb_parent; break;
510                            case '/': gb_result    = gb_search(gb_parent, separator+3, create, internflag); break;
511                            default:
512                                GB_export_errorf("Expected '/' after '..' in key '%s'", key);
513                                break;
514                        }
515                    }
516                    else { // ".." at root-node
517                        if (create) {
518                            GB_export_error("cannot use '..' at root node");
519                        }
520                    }
521                }
522                break;
523            }
524            case '-':
525                if (separator[1] != '>') invalid_char = separator[0];
526                else {
527                    if (firstKey[0]) {
528                        GBDATA *gb_link = GB_entry(gbd, firstKey);
529                        if (!gb_link) {
530                            if (create) {
531                                GB_export_error("Cannot create links on the fly in gb_search");
532                            }
533                        }
534                        else {
535                            if (GB_TYPE(gb_link) == GB_LINK) {
536                                GBDATA *gb_target = GB_follow_link(gb_link);
537                                if (!gb_target) GB_export_errorf("Link '%s' points nowhere", firstKey);
538                                else    gb_result = gb_search(gb_target, separator+2, create, internflag);
539                            }
540                            else GB_export_errorf("'%s' exists, but is not a link", firstKey);
541                        }
542                    }
543                    else GB_export_errorf("Missing linkname before '->' in '%s'", key);
544                }
545                break;
546
547            default:
548                invalid_char = separator[0];
549                break;
550        }
551
552        if (invalid_char) {
553            gb_assert(!gb_result);
554            GB_export_errorf("Invalid char '%c' in key '%s'", invalid_char, key);
555        }
556    }
557    gb_assert(!(gb_result && GB_have_error()));
558    return gb_result;
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) { // @@@ in case GB_search returned 0, gb_int already is 0 and error is exported. just assert error is exported
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
659long GB_number_of_marked_subentries(GBDATA *gbd) {
660    GBCONTAINER    *gbc     = (GBCONTAINER *)gbd;
661    int             userbit = GBCONTAINER_MAIN(gbc)->users[0]->userbit;
662    int             index;
663    int             end     = gbc->d.nheader;
664    gb_header_list *header;
665    long            count   = 0;
666
667    header = GB_DATA_LIST_HEADER(gbc->d);
668    for (index = 0; index<end; index++) {
669        if (! (userbit & header[index].flags.flags)) continue;
670        if (header[index].flags.changed >= GB_DELETED) continue;
671        count++;
672    }
673    return count;
674}
675
676
677
678GBDATA *GB_first_marked(GBDATA *gbd, const char *keystring) {
679    GBCONTAINER *gbc       = (GBCONTAINER *)gbd;
680    GBQUARK      key_quark = GB_find_existing_quark(gbd, keystring);
681    GB_test_transaction(gbc);
682    return key_quark ? gb_search_marked(gbc, key_quark, 0, 0) : NULL;
683}
684
685
686GBDATA *GB_following_marked(GBDATA *gbd, const char *keystring, size_t skip_over) {
687    GBCONTAINER *gbc       = GB_FATHER(gbd);
688    GBQUARK      key_quark = GB_find_existing_quark(gbd, keystring);
689    GB_test_transaction(gbc);
690    return key_quark ? gb_search_marked(gbc, key_quark, (int)gbd->index+1, skip_over) : NULL;
691}
692
693GBDATA *GB_next_marked(GBDATA *gbd, const char *keystring) {
694    return GB_following_marked(gbd, keystring, 0);
695}
696
697// ----------------------------
698//      Command interpreter
699
700void gb_install_command_table(GBDATA *gb_main, struct GBL_command_table *table, size_t table_size)
701{
702    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
703    if (!Main->command_hash) Main->command_hash = GBS_create_hash(table_size, GB_IGNORE_CASE);
704
705    for (; table->command_identifier; table++) {
706        GBS_write_hash(Main->command_hash, table->command_identifier, (long)table->function);
707    }
708
709    gb_assert((GBS_hash_count_elems(Main->command_hash)+1) == table_size);
710}
711
712static char *gbs_search_second_x(const char *str) {
713    int c;
714    for (; (c=*str); str++) {
715        if (c=='\\') {      // escaped characters
716            str++;
717            if (!(c=*str)) return NULL;
718            continue;
719        }
720        if (c=='"') return (char *)str;
721    }
722    return NULL;
723}
724
725char *gbs_search_second_bracket(const char *source)
726{
727    int c;
728    int deep = 0;
729    if (*source != '(') deep --;    // first bracket
730    for (; (c=*source); source++) {
731        if (c=='\\') {      // escaped characters
732            source++;
733            if (!*source) break;
734            continue;
735        }
736        if (c=='(') deep--;
737        else if (c==')') deep++;
738        if (!deep) return (char *)source;
739        if (c=='"') {       // search the second "
740            source = gbs_search_second_x(source);
741            if (!source) return NULL;
742        }
743    }
744    if (!c) return NULL;
745    return (char *)source;
746}
747
748
749static char *gbs_search_next_separator(const char *source, const char *seps) {
750    // search the next separator
751    static char tab[256];
752    static int flag = 0;
753    int c;
754    const char *p;
755    if (!flag) {
756        flag = 1;
757        memset(tab, 0, 256);
758    }
759    for (p = seps; (c=*p); p++) tab[c] = 1; // tab[seps[x]] = 1
760    tab['('] = 1;               // exclude () pairs
761    tab['"'] = 1;               // exclude " pairs
762    tab['\\'] = 1;              // exclude \-escaped chars
763
764    for (; (c=*source); source++) {
765        if (tab[c]) {
766            if (c=='\\') {
767                source++;
768                continue;
769            }
770            if (c=='(') {
771                source = gbs_search_second_bracket(source);
772                if (!source) break;
773                continue;
774            }
775            if (c=='"') {
776                source = gbs_search_second_x(source+1);
777                if (!source) break;
778                continue;
779            }
780            for (p = seps; (c=*p); p++) tab[c] = 0;
781            return (char *)source;
782        }
783    }
784    for (p = seps; (c=*p); p++) tab[c] = 0; // clear tab
785    return NULL;
786}
787
788static void dumpStreams(const char *name, int count, const GBL *args) {
789    printf("%s=%i\n", name, count);
790    if (count > 0) {
791        int c;
792        for (c = 0; c<count; c++) {
793            printf("  %02i='%s'\n", c, args[c].str);
794        }
795    }
796}
797
798static const char *shortenLongString(const char *str, size_t wanted_len) {
799    // shortens the string 'str' to 'wanted_len' (appends '[..]' if string was shortened)
800
801    const char *result;
802    size_t      len = strlen(str);
803
804    gb_assert(wanted_len>4);
805
806    if (len>wanted_len) {
807        static char   *shortened_str;
808        static size_t  short_len = 0;
809
810        if (short_len >= wanted_len) {
811            memcpy(shortened_str, str, wanted_len-4);
812        }
813        else {
814            freeset(shortened_str, GB_strpartdup(str, str+wanted_len));
815            short_len = wanted_len;
816        }
817        strcpy(shortened_str+wanted_len-4, "[..]");
818        result = shortened_str;
819    }
820    else {
821        result = str;
822    }
823    return result;
824}
825
826#if defined(WARN_TODO)
827#warning rewrite GB_command_interpreter (error+ressource handling)
828#endif
829
830char *GB_command_interpreter(GBDATA *gb_main, const char *str, const char *commands, GBDATA *gbd, const char *default_tree_name) {
831    /* simple command interpreter returns NULL on error (which should be exported in that case)
832     * if first character is == ':' run string parser
833     * if first character is == '/' run regexpr
834     * else command interpreter
835     */
836    int           strmalloc = 0;
837    char         *buffer;
838    GB_ERROR      error;
839    int           i;
840    int           argcinput;
841    int           argcparam;
842    int           argcout;
843    char         *bracket;
844    GB_MAIN_TYPE *Main      = GB_MAIN(gb_main);
845
846    GBL morig[GBL_MAX_ARGUMENTS];
847    GBL min[GBL_MAX_ARGUMENTS];
848    GBL mout[GBL_MAX_ARGUMENTS];
849
850    GBL *orig  = & morig[0];
851    GBL *in    = & min[0];
852    GBL *out   = & mout[0];
853    int  trace = GB_get_ACISRT_trace();
854
855    if (!str) {
856        if (!gbd) {
857            GB_export_error("ACI: no input streams found");
858            return NULL;
859        }
860        str = GB_read_as_string(gbd);
861        strmalloc = 1;
862    }
863
864    if (trace) {
865        printf("GB_command_interpreter: str='%s'\n"
866               "                        command='%s'\n", str, commands);
867    }
868
869    if (!commands || !commands[0]) { // empty command -> do not modify string
870        if (!strmalloc) return strdup(str);
871        return (char *)str;
872    }
873
874    if (commands[0] == ':') { // ':' -> string parser
875        return GBS_string_eval(str, commands+1, gbd);
876    }
877
878    if (commands[0] == '/') { // regular expression
879        GB_ERROR  err    = 0;
880        char     *result = GBS_regreplace(str, commands, &err);
881
882        if (!result) {
883            if (strcmp(err, "Missing '/' between search and replace string") == 0) {
884                // if GBS_regreplace didn't find a third '/' -> silently use GBS_regmatch:
885                size_t matchlen;
886                err    = 0;
887                const char *matched = GBS_regmatch(str, commands, &matchlen, &err);
888
889                if (matched) result   = GB_strndup(matched, matchlen);
890                else if (!err) result = strdup("");
891            }
892
893            if (!result && err) result = GBS_global_string_copy("<Error: %s>", err);
894        }
895        return result;
896    }
897
898    // ********************** init *******************
899
900    gb_local->gbl.gb_main = gb_main;
901    buffer = strdup(commands);
902
903    // ********************** remove all spaces and tabs *******************
904    {
905        const char *s1;
906        char *s2;
907        s1 = commands;
908        s2 = buffer;
909        {
910            int c;
911            for (; (c = *s1); s1++) {
912                if (c=='\\') {
913                    *(s2++) = c;
914                    if (!(c=*s1)) { break; }
915                    *(s2++) = c;
916                    continue;
917                }
918
919                if (c=='"') {       // search the second "
920                    const char *hp = gbs_search_second_x(s1+1);
921                    if (!hp) {
922                        GB_export_errorf("unbalanced '\"' in '%s'", commands);
923                        return NULL;
924                    }
925                    while (s1 <= hp) *(s2++) = *(s1++);
926                    s1--;
927                    continue;
928                }
929                if (c!=' ' && c!='\t') *(s2++) = c;
930            }
931        }
932        *s2 = 0;
933    }
934
935
936
937    memset((char *)orig, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
938    memset((char *)in, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
939    memset((char *)out, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
940
941    if (strmalloc) {
942        orig[0].str = (char *)str;
943    }
944    else {
945        orig[0].str = strdup(str);
946    }
947
948    argcinput = 1;
949    argcout   = 0;
950    error     = 0;
951    {
952        char *s1, *s2;
953        s1 = buffer;
954        if (*s1 == '|') s1++;
955
956        // ** loop over all commands **
957        for (s1 = s1; s1;  s1 = s2) {
958            int separator;
959            GBL_COMMAND command;
960            s2 = gbs_search_next_separator(s1, "|;,");
961            if (s2) {
962                separator = *(s2);
963                *(s2++) = 0;
964            }
965            else {
966                separator = 0;
967            }
968            // collect the parameters
969            memset((char*)in, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
970            if (*s1 == '"') {           // copy "text" to out
971                char *end = gbs_search_second_x(s1+1);
972                if (!end) {
973                    error = "Missing second '\"'";
974                    break;
975                }
976                *end = 0;
977                out[argcout++].str = strdup(s1+1);
978            }
979            else {
980                argcparam = 0;
981                bracket = strchr(s1, '(');
982                if (bracket) {      // I got the parameter list
983                    int slen;
984                    *(bracket++) = 0;
985                    slen  = strlen(bracket);
986                    if (bracket[slen-1] != ')') {
987                        error = "Missing ')'";
988                    }
989                    else {
990                        // go through the parameters
991                        char *p1, *p2;
992                        bracket[slen-1] = 0;
993                        for (p1 = bracket; p1;  p1 = p2) {
994                            p2 = gbs_search_next_separator(p1, ";,");
995                            if (p2) {
996                                *(p2++) = 0;
997                            }
998                            if (p1[0] == '"') { // remove "" pairs
999                                int len2;
1000                                p1++;
1001                                len2 = strlen(p1)-1;
1002
1003                                if (p1[len2] != '\"') {
1004                                    error = "Missing '\"'";
1005                                }
1006                                else {
1007                                    p1[len2] = 0;
1008                                }
1009                            }
1010                            in[argcparam++].str = strdup(p1);
1011                        }
1012                    }
1013                    if (error) break;
1014                }
1015                if (!error && (bracket || *s1)) {
1016                    char *p = s1;
1017                    int c;
1018                    while ((c = *p)) {          // command to lower case
1019                        if (c>='A' && c<='Z') {
1020                            c += 'a'-'A';
1021                            *p = c;
1022                        }
1023                        p++;
1024                    }
1025
1026                    command = (GBL_COMMAND)GBS_read_hash(Main->command_hash, s1);
1027                    if (!command) {
1028                        error = GBS_global_string("Unknown command '%s'", s1);
1029                    }
1030                    else {
1031                        GBL_command_arguments args;
1032                        args.gb_ref            = gbd;
1033                        args.default_tree_name = default_tree_name;
1034                        args.command           = s1;
1035                        args.cinput            = argcinput;
1036                        args.vinput            = orig;
1037                        args.cparam            = argcparam;
1038                        args.vparam            = in;
1039                        args.coutput           = &argcout;
1040                        args.voutput           = &out;
1041
1042                        if (trace) {
1043                            printf("-----------------------\nExecution of command '%s':\n", args.command);
1044                            dumpStreams("Arguments", args.cparam, args.vparam);
1045                            dumpStreams("InputStreams", args.cinput, args.vinput);
1046                        }
1047
1048                        error = command(&args); // execute the command
1049
1050                        if (!error && trace) dumpStreams("OutputStreams", *args.coutput, *args.voutput);
1051
1052                        if (error) {
1053                            char *inputstreams = 0;
1054                            char *paramlist    = 0;
1055                            int   j;
1056
1057#define MAX_PRINT_LEN 200
1058
1059                            for (j = 0; j<args.cparam; ++j) {
1060                                const char *param       = args.vparam[j].str;
1061                                const char *param_short = shortenLongString(param, MAX_PRINT_LEN);
1062
1063                                if (!paramlist) paramlist = strdup(param_short);
1064                                else freeset(paramlist, GBS_global_string_copy("%s,%s", paramlist, param_short));
1065                            }
1066                            for (j = 0; j<args.cinput; ++j) {
1067                                const char *param       = args.vinput[j].str;
1068                                const char *param_short = shortenLongString(param, MAX_PRINT_LEN);
1069
1070                                if (!inputstreams) inputstreams = strdup(param_short);
1071                                else freeset(inputstreams, GBS_global_string_copy("%s;%s", inputstreams, param_short));
1072                            }
1073#undef MAX_PRINT_LEN
1074                            if (paramlist) {
1075                                error = GBS_global_string("while applying '%s(%s)'\nto '%s':\n%s", s1, paramlist, inputstreams, error);
1076                            }
1077                            else {
1078                                error = GBS_global_string("while applying '%s'\nto '%s':\n%s", s1, inputstreams, error);
1079                            }
1080
1081                            free(inputstreams);
1082                            free(paramlist);
1083                        }
1084                    }
1085                }
1086
1087                for (i=0; i<argcparam; i++) {   // free intermediate arguments
1088                    if (in[i].str) free(in[i].str);
1089                }
1090            }
1091
1092            if (error) break;
1093
1094            if (separator == '|') {         // swap in and out in pipes
1095                GBL *h;
1096                for (i=0; i<argcinput; i++) {
1097                    if (orig[i].str)    free(orig[i].str);
1098                }
1099                memset((char*)orig, 0, sizeof(GBL)*GBL_MAX_ARGUMENTS);
1100                argcinput = 0;
1101
1102                h = out;            // swap orig and out
1103                out = orig;
1104                orig = h;
1105
1106                argcinput = argcout;
1107                argcout = 0;
1108            }
1109
1110        }
1111    }
1112    for (i=0; i<argcinput; i++) {
1113        if (orig[i].str) free(orig[i].str);
1114    }
1115
1116    {
1117        char *s1;
1118        if (!argcout) {
1119            s1 = strdup(""); // returned '<NULL>' in the past
1120        }
1121        else if (argcout == 1) {
1122            s1 = out[0].str;
1123        }
1124        else {             // concatenate output strings
1125            GBS_strstruct *strstruct = GBS_stropen(1000);
1126            for (i=0; i<argcout; i++) {
1127                if (out[i].str) {
1128                    GBS_strcat(strstruct, out[i].str);
1129                    free(out[i].str);
1130                }
1131            }
1132            s1 = GBS_strclose(strstruct);
1133        }
1134        free(buffer);
1135
1136        if (!error) {
1137            if (trace) printf("GB_command_interpreter: result='%s'\n", s1);
1138            return s1;
1139        }
1140        free(s1);
1141    }
1142
1143    GB_export_errorf("Command '%s' failed:\nReason: %s", commands, error);
1144    return NULL;
1145}
1146// --------------------------------------------------------------------------------
1147
1148#ifdef UNIT_TESTS
1149#ifndef TEST_UNIT_H
1150#include <test_unit.h>
1151#endif
1152
1153const char *GBT_get_name(GBDATA *gb_item); 
1154
1155struct TestDB : virtual Noncopyable {
1156    GB_shell  shell;
1157    GBDATA   *gb_main;
1158    GB_ERROR  error;
1159
1160    GBDATA *gb_cont1;
1161    GBDATA *gb_cont2;
1162    GBDATA *gb_cont_empty;
1163    GBDATA *gb_cont_misc;
1164
1165    GB_ERROR create_many_items(GBDATA *gb_parent, const char **item_key_list, int item_count) {
1166        int k = 0;
1167        for (int i = 0; i<item_count && !error; i++) {
1168            const char *item_key    = item_key_list[k++];
1169            if (!item_key) { item_key = item_key_list[0]; k = 1; }
1170
1171            GBDATA *gb_child = GB_create_container(gb_parent, item_key);
1172            if (!gb_child) {
1173                error = GB_await_error();
1174            }
1175            else {
1176                if ((i%7) == 0) GB_write_flag(gb_child, 1); // mark some
1177               
1178                GBDATA *gb_name = GB_create(gb_child, "name", GB_STRING);
1179                error           = GB_write_string(gb_name, GBS_global_string("%s %i", item_key, i));
1180            }
1181        }
1182        return error;
1183    }
1184
1185    TestDB() {
1186        gb_main = GB_open("nosuch.arb", "c");
1187        error   = gb_main ? NULL : GB_await_error();
1188
1189        if (!error) {
1190            GB_transaction ta(gb_main);
1191            gb_cont1      = GB_create_container(gb_main, "container1");
1192            gb_cont2      = GB_create_container(gb_main, "container2");
1193            gb_cont_empty = GB_create_container(gb_main, "empty");
1194            gb_cont_misc  = GB_create_container(gb_main, "misc");
1195
1196            if (!gb_cont1 || !gb_cont2) error = GB_await_error();
1197
1198            const char *single_key[] = { "entry", NULL };
1199            const char *mixed_keys[] = { "item", "other", NULL };
1200
1201            if (!error) error = create_many_items(gb_cont1, single_key, 100);
1202            if (!error) error = create_many_items(gb_cont2, mixed_keys, 20);
1203        }
1204        TEST_ASSERT_NO_ERROR(error);
1205    }
1206    ~TestDB() {
1207        GB_close(gb_main);
1208    }
1209};
1210
1211void TEST_DB_search() {
1212    TestDB db;
1213    TEST_ASSERT_NO_ERROR(db.error);
1214
1215    {
1216        GB_transaction ta(db.gb_main);
1217
1218        TEST_ASSERT_EQUAL(GB_number_of_subentries(db.gb_cont1), 100);
1219        TEST_ASSERT_EQUAL(GB_number_of_subentries(db.gb_cont2), 20);
1220
1221        {
1222            GBDATA *gb_any_child = GB_child(db.gb_cont1);
1223            TEST_ASSERT(gb_any_child);
1224            TEST_ASSERT_EQUAL(gb_any_child, GB_entry(db.gb_cont1, "entry"));
1225            TEST_ASSERT_EQUAL(gb_any_child, GB_search(db.gb_main, "container1/entry", GB_FIND));
1226
1227            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "zic-zac", GB_FIND), "Invalid char '-' in key 'zic-zac'");
1228
1229            // check link-syntax
1230            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "->entry", GB_FIND), "Missing linkname before '->' in '->entry'");
1231            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(db.gb_main, "entry->bla", GB_FIND));
1232            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "container1/entry->nowhere", GB_FIND), "'entry' exists, but is not a link");
1233            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_main, "container1/nosuchentry->nowhere", GB_STRING), "Cannot create links on the fly in gb_search");
1234            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(db.gb_main, "entry->", GB_FIND)); // valid ? just deref link
1235
1236            // check ..
1237            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "..", GB_FIND), db.gb_cont1);
1238            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "../..", GB_FIND), db.gb_main);
1239            // above main entry
1240            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(gb_any_child, "../../..", GB_FIND));
1241            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(gb_any_child, "../../../impossible", GB_STRING), "cannot use '..' at root node");
1242
1243            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "", GB_FIND), gb_any_child); // return self
1244            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "/container1/", GB_FIND), db.gb_cont1); // accept trailing slash for container ..
1245            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(gb_any_child, "/container1/entry/name/", GB_FIND), "terminal entry 'name' cannot be used as container"); // .. but not for normal entries
1246
1247            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "/", GB_FIND), db.gb_main);
1248            TEST_ASSERT_EQUAL(GB_search(gb_any_child, "/container1/..", GB_FIND), db.gb_main);
1249            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(gb_any_child, "/..", GB_FIND)); // main has no parent
1250        }
1251
1252        {
1253            GBDATA *gb_child1 = GB_child(db.gb_cont2);   TEST_ASSERT(gb_child1);
1254            GBDATA *gb_child2 = GB_nextChild(gb_child1); TEST_ASSERT(gb_child2);
1255            GBDATA *gb_child3 = GB_nextChild(gb_child2); TEST_ASSERT(gb_child3);
1256            GBDATA *gb_child4 = GB_nextChild(gb_child3); TEST_ASSERT(gb_child4);
1257
1258            TEST_ASSERT_EQUAL(GB_read_key_pntr(gb_child1), "item");
1259            TEST_ASSERT_EQUAL(GB_read_key_pntr(gb_child2), "other");
1260            TEST_ASSERT_EQUAL(GB_read_key_pntr(gb_child3), "item");
1261            TEST_ASSERT_EQUAL(GB_read_key_pntr(gb_child4), "other");
1262
1263            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_entry(db.gb_cont2, "entry"));
1264            TEST_ASSERT_EQUAL(GB_entry(db.gb_cont2, "item"),  gb_child1);
1265            TEST_ASSERT_EQUAL(GB_entry(db.gb_cont2, "other"), gb_child2);
1266
1267            TEST_ASSERT_EQUAL(GB_nextEntry(gb_child1), gb_child3);
1268            TEST_ASSERT_EQUAL(GB_nextEntry(gb_child2), gb_child4);
1269
1270            // check ..
1271            TEST_ASSERT_EQUAL(GB_search(gb_child3, "../item", GB_FIND), gb_child1);
1272            TEST_ASSERT_EQUAL(GB_search(gb_child3, "../other", GB_FIND), gb_child2);
1273            TEST_ASSERT_EQUAL(GB_search(gb_child3, "../other/../item", GB_FIND), gb_child1);
1274        }
1275
1276        // ------------------------
1277        //      single entries
1278
1279        {
1280            GBDATA *gb_str      = GB_searchOrCreate_string(db.gb_cont_misc, "str", "bla");   TEST_ASSERT(gb_str);
1281            GBDATA *gb_str_same = GB_searchOrCreate_string(db.gb_cont_misc, "str", "blub");
1282
1283            TEST_ASSERT_EQUAL(gb_str, gb_str_same);
1284            TEST_ASSERT_EQUAL(GB_read_char_pntr(gb_str), "bla");
1285
1286            GBDATA *gb_int      = GB_searchOrCreate_int(db.gb_cont_misc, "int", 4711);   TEST_ASSERT(gb_int);
1287            GBDATA *gb_int_same = GB_searchOrCreate_int(db.gb_cont_misc, "int", 2012);
1288
1289            TEST_ASSERT_EQUAL(gb_int, gb_int_same);
1290            TEST_ASSERT_EQUAL(GB_read_int(gb_int), 4711);
1291
1292            GBDATA *gb_float      = GB_searchOrCreate_float(db.gb_cont_misc, "float", 0.815);   TEST_ASSERT(gb_float);
1293            GBDATA *gb_float_same = GB_searchOrCreate_float(db.gb_cont_misc, "float", 3.1415);
1294
1295            TEST_ASSERT_EQUAL(gb_float, gb_float_same);
1296            TEST_ASSERT_SIMILAR(GB_read_float(gb_float), 0.815, 0.0001);
1297
1298            TEST_ASSERT_EQUAL  (GB_read_char_pntr(GB_searchOrCreate_string(db.gb_cont_misc, "sub1/str",    "blub")), "blub");
1299            TEST_ASSERT_EQUAL  (GB_read_int      (GB_searchOrCreate_int   (db.gb_cont_misc, "sub2/int",    2012)),   2012);
1300            TEST_ASSERT_SIMILAR(GB_read_float    (GB_searchOrCreate_float (db.gb_cont_misc, "sub3/float", 3.1415)), 3.1415, 0.0001);
1301
1302            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_float (db.gb_cont_misc, "int",   0.815), "has wrong type");
1303            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_float (db.gb_cont_misc, "str",   0.815), "has wrong type");
1304            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_int   (db.gb_cont_misc, "float", 4711),  "has wrong type");
1305            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_int   (db.gb_cont_misc, "str",   4711),  "has wrong type");
1306            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "float", "bla"), "has wrong type");
1307            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "int",   "bla"), "has wrong type");
1308
1309            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "*", "bla"), "Invalid char '*' in key '*'");
1310            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "int*", "bla"), "Invalid char '*' in key 'int*'");
1311            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_searchOrCreate_string(db.gb_cont_misc, "sth_else*", "bla"), "Invalid char '*' in key 'sth_else*'");
1312
1313            GBDATA *gb_entry;
1314            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(db.gb_cont_misc, "subcont/entry", GB_FIND));
1315            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_entry = GB_search(db.gb_cont_misc, "subcont/entry", GB_INT));
1316            TEST_ASSERT_EQUAL(GB_read_int(gb_entry), 0);
1317
1318            GBDATA *gb_cont1;
1319            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_cont1 = GB_search(db.gb_cont_misc, "subcont", GB_CREATE_CONTAINER));
1320            TEST_ASSERT_EQUAL(GB_child(gb_cont1), gb_entry); // test GB_search found the container created implicitely above
1321
1322            GBDATA *gb_cont2;
1323            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_cont2 = GB_search(db.gb_cont_misc, "subcont2", GB_CREATE_CONTAINER)); // create new container
1324
1325            // -----------------------
1326            //      search values
1327
1328            GBDATA *gb_4711;
1329            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_4711 = GB_find_int(db.gb_cont_misc, "int", 4711, SEARCH_CHILD));
1330            TEST_ASSERT_EQUAL(gb_4711, gb_int);
1331
1332            TEST_ASSERT_NULL(GB_find_int(db.gb_cont_misc, "int", 2012, SEARCH_CHILD));
1333
1334            GBDATA *gb_bla;
1335            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_bla = GB_find_string(db.gb_cont_misc, "str", "bla", GB_MIND_CASE, SEARCH_CHILD));
1336            TEST_ASSERT_EQUAL(gb_bla, gb_str);
1337            TEST_ASSERT_RESULT__NOERROREXPORTED(gb_bla = GB_find_string(gb_4711, "str", "bla", GB_MIND_CASE, SEARCH_BROTHER));
1338            TEST_ASSERT_EQUAL(gb_bla, gb_str);
1339
1340            TEST_ASSERT_NULL(GB_find_string(db.gb_cont_misc, "str", "blub", GB_MIND_CASE, SEARCH_CHILD));
1341
1342            GBDATA *gb_name;
1343            TEST_ASSERT_NOTNULL                  (GB_find_string          (db.gb_cont1, "name", "entry 77",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
1344            TEST_ASSERT_NOTNULL                  (GB_find_string          (db.gb_cont1, "name", "entry 99",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
1345            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_find_string          (db.gb_cont1, "name", "entry 100", GB_MIND_CASE,   SEARCH_GRANDCHILD));
1346            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_find_string          (db.gb_cont1, "name", "ENTRY 13",  GB_MIND_CASE,   SEARCH_GRANDCHILD));
1347            TEST_ASSERT_NOTNULL                  (gb_name = GB_find_string(db.gb_cont1, "name", "ENTRY 13",  GB_IGNORE_CASE, SEARCH_GRANDCHILD));
1348
1349            GBDATA *gb_sub;
1350            TEST_ASSERT_NOTNULL(gb_sub = GB_get_father(gb_name));        TEST_ASSERT_EQUAL(GBT_get_name(gb_sub), "entry 13");
1351            TEST_ASSERT_NOTNULL(gb_sub = GB_followingEntry(gb_sub, 0));  TEST_ASSERT_EQUAL(GBT_get_name(gb_sub), "entry 14");
1352            TEST_ASSERT_NOTNULL(gb_sub = GB_followingEntry(gb_sub, 1));  TEST_ASSERT_EQUAL(GBT_get_name(gb_sub), "entry 16");
1353            TEST_ASSERT_NOTNULL(gb_sub = GB_followingEntry(gb_sub, 10)); TEST_ASSERT_EQUAL(GBT_get_name(gb_sub), "entry 27");
1354            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_followingEntry(gb_sub, -1U)); 
1355            TEST_ASSERT_NOTNULL(gb_sub = GB_brother(gb_sub, "entry"));   TEST_ASSERT_EQUAL(GBT_get_name(gb_sub), "entry 0");
1356
1357            TEST_ASSERT_EQUAL(gb_bla = GB_search(gb_4711, "/misc/str", GB_FIND), gb_str);
1358
1359            // keyless search
1360            TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_search(db.gb_cont_misc, NULL, GB_FIND));
1361
1362            // ----------------------------
1363            //      GB_get_GBDATA_path
1364
1365            TEST_ASSERT_EQUAL(GB_get_GBDATA_path(gb_int),   "/main/misc/int");
1366            TEST_ASSERT_EQUAL(GB_get_GBDATA_path(gb_str),   "/main/misc/str");
1367            TEST_ASSERT_EQUAL(GB_get_GBDATA_path(gb_entry), "/main/misc/subcont/entry");
1368            TEST_ASSERT_EQUAL(GB_get_GBDATA_path(gb_cont2),  "/main/misc/subcont2");
1369
1370            // -----------------------------------------
1371            //      search/create with changed type
1372
1373            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "str", GB_INT), "Inconsistent type for field");
1374            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "subcont", GB_STRING), "Inconsistent type for field");
1375        }
1376
1377        {
1378            // check invalid searches
1379            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/inva*lid", GB_INT), "Invalid char '*' in key 'inva*lid'");
1380            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/1 3", GB_INT), "Invalid char ' ' in key '1 3'");
1381            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub//sub", GB_INT), "Invalid '//' in key 'sub//sub'");
1382            TEST_ASSERT_NORESULT__ERROREXPORTED_CONTAINS(GB_search(db.gb_cont_misc, "sub/..sub", GB_INT), "Expected '/' after '..' in key '..sub'");
1383        }
1384
1385        // ---------------
1386        //      marks
1387
1388        TEST_ASSERT_EQUAL(GB_number_of_marked_subentries(db.gb_cont1), 15);
1389        TEST_ASSERT_EQUAL(GB_number_of_marked_subentries(db.gb_cont2), 3);
1390
1391        TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_first_marked(db.gb_cont2, "entry"));
1392
1393        GBDATA *gb_marked;
1394        TEST_ASSERT_NOTNULL(gb_marked = GB_first_marked(db.gb_cont2, "item"));
1395        TEST_ASSERT_EQUAL(GBT_get_name(gb_marked), "item 0");
1396
1397        TEST_ASSERT_NORESULT__NOERROREXPORTED(GB_following_marked(gb_marked, "item", 1)); // skip over last
1398       
1399        TEST_ASSERT_NOTNULL(gb_marked = GB_next_marked(gb_marked, "item")); // find last
1400        TEST_ASSERT_EQUAL(GBT_get_name(gb_marked), "item 14");
1401    }
1402
1403    // @@@ delete some species, then search again
1404
1405    TEST_ASSERT_NO_ERROR(db.error);
1406}
1407
1408#endif // UNIT_TESTS
1409
1410// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the browser.