source: tags/arb-6.0-rc1/ARBDB/arbdb.cxx

Last change on this file was 11889, checked in by westram, 11 years ago
  • add missing comment to closing #endif of test-sections
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 123.3 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : arbdb.cxx                                         //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#if defined(DARWIN)
12#include <cstdio>
13#endif // DARWIN
14
15#include <rpc/types.h>
16#include <rpc/xdr.h>
17
18#include "ad_hcb.h"
19#include "gb_comm.h"
20#include "gb_compress.h"
21#include "gb_localdata.h"
22#include "gb_ta.h"
23#include "gb_ts.h"
24#include "gb_index.h"
25#include <arb_strarray.h>
26
27#include <glib.h>
28#include <glib/gprintf.h>
29
30gb_local_data *gb_local = 0;
31
32#define INIT_TYPE_NAME(t) GB_TYPES_name[t] = #t
33
34static const char *GB_TYPES_2_name(GB_TYPES type) {
35    static const char *GB_TYPES_name[GB_TYPE_MAX];
36    static bool        initialized = false;
37
38    if (!initialized) {
39        memset(GB_TYPES_name, 0, sizeof(GB_TYPES_name));
40        INIT_TYPE_NAME(GB_NONE);
41        INIT_TYPE_NAME(GB_BIT);
42        INIT_TYPE_NAME(GB_BYTE);
43        INIT_TYPE_NAME(GB_INT);
44        INIT_TYPE_NAME(GB_FLOAT);
45        INIT_TYPE_NAME(GB_POINTER);
46        INIT_TYPE_NAME(GB_BITS);
47        INIT_TYPE_NAME(GB_BYTES);
48        INIT_TYPE_NAME(GB_INTS);
49        INIT_TYPE_NAME(GB_FLOATS);
50        INIT_TYPE_NAME(GB_LINK);
51        INIT_TYPE_NAME(GB_STRING);
52        INIT_TYPE_NAME(GB_STRING_SHRT);
53        INIT_TYPE_NAME(GB_DB);
54        initialized = true;
55    }
56
57    const char *name = NULL;
58    if (type >= 0 && type<GB_TYPE_MAX) name = GB_TYPES_name[type];
59    if (!name) name = GBS_global_string("invalid-type-%i", type);
60    return name;
61}
62
63inline GB_ERROR gb_transactable_type(GB_TYPES type, GBDATA *gbd) {
64    GB_ERROR error = NULL;
65    if (GB_MAIN(gbd)->get_transaction_level() == 0) {
66        error = "No transaction running";
67    }
68    else if (GB_ARRAY_FLAGS(gbd).changed == GB_DELETED) {
69        error = "Entry has been deleted";
70    }
71    else {
72        GB_TYPES gb_type = gbd->type();
73        if (gb_type != type && (type != GB_STRING || gb_type != GB_LINK)) {
74            char *rtype    = strdup(GB_TYPES_2_name(type));
75            char *rgb_type = strdup(GB_TYPES_2_name(gb_type));
76           
77            error = GBS_global_string("type mismatch (want='%s', got='%s') in '%s'", rtype, rgb_type, GB_get_db_path(gbd));
78
79            free(rgb_type);
80            free(rtype);
81        }
82    }
83    if (error) {
84        GBK_dump_backtrace(stderr, error); // it's a bug: none of the above errors should ever happen
85        gb_assert(0);
86    }
87    return error;
88}
89
90__ATTR__USERESULT static GB_ERROR gb_security_error(GBDATA *gbd) {
91    GB_MAIN_TYPE *Main  = GB_MAIN(gbd);
92    const char   *error = GBS_global_string("Protection: Attempt to change a level-%i-'%s'-entry,\n"
93                                            "but your current security level is only %i",
94                                            GB_GET_SECURITY_WRITE(gbd),
95                                            GB_read_key_pntr(gbd),
96                                            Main->security_level);
97#if defined(DEBUG)
98    fprintf(stderr, "%s\n", error);
99#endif // DEBUG
100    return error;
101}
102
103inline GB_ERROR gb_type_writeable_to(GB_TYPES type, GBDATA *gbd) {
104    GB_ERROR error = gb_transactable_type(type, gbd);
105    if (!error) {
106        if (GB_GET_SECURITY_WRITE(gbd) > GB_MAIN(gbd)->security_level) {
107            error = gb_security_error(gbd);
108        }
109    }
110    return error;
111}
112inline GB_ERROR gb_type_readable_from(GB_TYPES type, GBDATA *gbd) {
113    return gb_transactable_type(type, gbd);
114}
115
116inline GB_ERROR error_with_dbentry(const char *action, GBDATA *gbd, GB_ERROR error) {
117    const char *path = GB_get_db_path(gbd);
118    return GBS_global_string("Can't %s '%s':\n%s", action, path, error);
119}
120
121
122#define RETURN_ERROR_IF_NOT_WRITEABLE_AS_TYPE(gbd, type)        \
123    do {                                                        \
124        GB_ERROR error = gb_type_writeable_to(type, gbd);       \
125        if (error) {                                            \
126            return error_with_dbentry("write", gbd, error);     \
127        }                                                       \
128    } while(0)
129
130#define EXPORT_ERROR_AND_RETURN_0_IF_NOT_READABLE_AS_TYPE(gbd, type)    \
131    do {                                                                \
132        GB_ERROR error = gb_type_readable_from(type, gbd);              \
133        if (error) {                                                    \
134            error = error_with_dbentry("read", gbd, error);             \
135            GB_export_error(error);                                     \
136            return 0;                                                   \
137        }                                                               \
138    } while(0)                                                          \
139
140
141#if defined(WARN_TODO)
142#warning replace GB_TEST_READ / GB_TEST_READ by new names later
143#endif
144
145#define GB_TEST_READ(gbd, type, ignored) EXPORT_ERROR_AND_RETURN_0_IF_NOT_READABLE_AS_TYPE(gbd, type)
146#define GB_TEST_WRITE(gbd, type, ignored) RETURN_ERROR_IF_NOT_WRITEABLE_AS_TYPE(gbd, type)
147
148#define GB_TEST_NON_BUFFER(x, gerror)                                   \
149    do {                                                                \
150        if (GB_is_in_buffer(x)) {                                       \
151            GBK_terminatef("%s: you are not allowed to write any data, which you get by pntr", gerror); \
152        }                                                               \
153    } while (0)
154
155
156static GB_ERROR GB_safe_atof(const char *str, double *res) {
157    GB_ERROR  error = NULL;
158    char     *end;
159    *res            = strtod(str, &end);
160    if (end == str || end[0] != 0) {
161        if (!str[0]) {
162            *res = 0.0;
163        }
164        else {
165            error = GBS_global_string("cannot convert '%s' to double", str);
166        }
167    }
168    return error;
169}
170
171double GB_atof(const char *str) {
172    // convert ASCII to double
173    double   res = 0;
174    GB_ERROR err = GB_safe_atof(str, &res);
175    if (err) {
176        // expected double in 'str'- better use GB_safe_atof()
177        GBK_terminatef("GB_safe_atof(\"%s\", ..) returns error: %s", str, err);
178    }
179    return res;
180}
181
182// ---------------------------
183//      compression tables
184
185const int gb_convert_type_2_compression_flags[] = {
186    GB_COMPRESSION_NONE,                                                                 // GB_NONE  0
187    GB_COMPRESSION_NONE,                                                                 // GB_BIT   1
188    GB_COMPRESSION_NONE,                                                                 // GB_BYTE  2
189    GB_COMPRESSION_NONE,                                                                 // GB_INT   3
190    GB_COMPRESSION_NONE,                                                                 // GB_FLOAT 4
191    GB_COMPRESSION_NONE,                                                                 // GB_??    5
192    GB_COMPRESSION_BITS,                                                                 // GB_BITS  6
193    GB_COMPRESSION_NONE,                                                                 // GB_??    7
194    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN,                                  // GB_BYTES 8
195    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_SORTBYTES,       // GB_INTS  9
196    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_SORTBYTES,       // GB_FLTS 10
197    GB_COMPRESSION_NONE,                                                                 // GB_LINK 11
198    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_DICTIONARY,      // GB_STR  12
199    GB_COMPRESSION_NONE,                                                                 // GB_STRS 13
200    GB_COMPRESSION_NONE,                                                                 // GB??    14
201    GB_COMPRESSION_NONE                                                                  // GB_DB   15
202};
203
204int gb_convert_type_2_sizeof[] = { /* contains the unit-size of data stored in DB,
205                                    * i.e. realsize = unit_size * size()
206                                    */
207    0,                                              // GB_NONE  0
208    0,                                              // GB_BIT   1
209    sizeof(char),                                   // GB_BYTE  2
210    sizeof(int),                                    // GB_INT   3
211    sizeof(float),                                  // GB_FLOAT 4
212    0,                                              // GB_??    5
213    0,                                              // GB_BITS  6
214    0,                                              // GB_??    7
215    sizeof(char),                                   // GB_BYTES 8
216    sizeof(int),                                    // GB_INTS  9
217    sizeof(float),                                  // GB_FLTS 10
218    sizeof(char),                                   // GB_LINK 11
219    sizeof(char),                                   // GB_STR  12
220    sizeof(char),                                   // GB_STRS 13
221    0,                                              // GB_??   14
222    0,                                              // GB_DB   15
223};
224
225int gb_convert_type_2_appendix_size[] = { /* contains the size of the suffix (aka terminator element)
226                                           * size is in bytes
227                                           */
228
229    0,                                              // GB_NONE  0
230    0,                                              // GB_BIT   1
231    0,                                              // GB_BYTE  2
232    0,                                              // GB_INT   3
233    0,                                              // GB_FLOAT 4
234    0,                                              // GB_??    5
235    0,                                              // GB_BITS  6
236    0,                                              // GB_??    7
237    0,                                              // GB_BYTES 8
238    0,                                              // GB_INTS  9
239    0,                                              // GB_FLTS 10
240    1,                                              // GB_LINK 11 (zero terminated)
241    1,                                              // GB_STR  12 (zero terminated)
242    1,                                              // GB_STRS 13 (zero terminated)
243    0,                                              // GB_??   14
244    0,                                              // GB_DB   15
245};
246
247
248// ---------------------------------
249//      local buffer management
250
251static void init_buffer(gb_buffer *buf, size_t initial_size) {
252    buf->size = initial_size;
253    buf->mem  = buf->size ? (char*)malloc(buf->size) : NULL;
254}
255
256static char *check_out_buffer(gb_buffer *buf) {
257    char *checkOut = buf->mem;
258
259    buf->mem  = 0;
260    buf->size = 0;
261
262    return checkOut;
263}
264
265static void alloc_buffer(gb_buffer *buf, size_t size) {
266    free(buf->mem);
267    buf->size = size;
268#if (MEMORY_TEST==1)
269    buf->mem  = (char *)malloc(buf->size);
270#else
271    buf->mem  = (char *)GB_calloc(buf->size, 1);
272#endif
273}
274
275static GB_BUFFER give_buffer(gb_buffer *buf, size_t size) {
276#if (MEMORY_TEST==1)
277    alloc_buffer(buf, size); // do NOT reuse buffer if testing memory
278#else
279    if (size >= buf->size) {
280        alloc_buffer(buf, size);
281    }
282#endif
283    return buf->mem;
284}
285
286static int is_in_buffer(gb_buffer *buf, GB_CBUFFER ptr) {
287    return ptr >= buf->mem && ptr < buf->mem+buf->size;
288}
289
290// ------------------------------
291
292GB_BUFFER GB_give_buffer(size_t size) {
293    // return a pointer to a static piece of memory at least size bytes long
294    return give_buffer(&gb_local->buf1, size);
295}
296
297GB_BUFFER GB_increase_buffer(size_t size) {
298    if (size < gb_local->buf1.size) {
299        char   *old_buffer = gb_local->buf1.mem;
300        size_t  old_size   = gb_local->buf1.size;
301
302        gb_local->buf1.mem = NULL;
303        alloc_buffer(&gb_local->buf1, size);
304        memcpy(gb_local->buf1.mem, old_buffer, old_size);
305
306        free(old_buffer);
307    }
308    return gb_local->buf1.mem;
309}
310
311NOT4PERL int GB_give_buffer_size() {
312    return gb_local->buf1.size;
313}
314
315GB_BUFFER GB_give_buffer2(long size) {
316    return give_buffer(&gb_local->buf2, size);
317}
318
319static int GB_is_in_buffer(GB_CBUFFER ptr) {
320    /* returns 1 or 2 if 'ptr' points to gb_local->buf1/buf2
321     * returns 0 otherwise
322     */
323    int buffer = 0;
324
325    if (is_in_buffer(&gb_local->buf1, ptr)) buffer = 1;
326    else if (is_in_buffer(&gb_local->buf2, ptr)) buffer = 2;
327
328    return buffer;
329}
330
331char *GB_check_out_buffer(GB_CBUFFER buffer) {
332    /* Check a piece of memory out of the buffer management
333     * after it is checked out, the user has the full control to use and free it
334     * Returns a pointer to the start of the buffer (even if 'buffer' points inside the buffer!)
335     */
336    char *old = 0;
337
338    if (is_in_buffer(&gb_local->buf1, buffer)) old = check_out_buffer(&gb_local->buf1);
339    else if (is_in_buffer(&gb_local->buf2, buffer)) old = check_out_buffer(&gb_local->buf2);
340
341    return old;
342}
343
344GB_BUFFER GB_give_other_buffer(GB_CBUFFER buffer, long size) {
345    return is_in_buffer(&gb_local->buf1, buffer)
346        ? GB_give_buffer2(size)
347        : GB_give_buffer(size);
348}
349
350static unsigned char GB_BIT_compress_data[] = {
351    0x1d, GB_CS_OK,  0, 0,
352    0x04, GB_CS_OK,  0, 1,
353    0x0a, GB_CS_OK,  0, 2,
354    0x0b, GB_CS_OK,  0, 3,
355    0x0c, GB_CS_OK,  0, 4,
356    0x1a, GB_CS_OK,  0, 5,
357    0x1b, GB_CS_OK,  0, 6,
358    0x1c, GB_CS_OK,  0, 7,
359    0xf0, GB_CS_OK,  0, 8,
360    0xf1, GB_CS_OK,  0, 9,
361    0xf2, GB_CS_OK,  0, 10,
362    0xf3, GB_CS_OK,  0, 11,
363    0xf4, GB_CS_OK,  0, 12,
364    0xf5, GB_CS_OK,  0, 13,
365    0xf6, GB_CS_OK,  0, 14,
366    0xf7, GB_CS_OK,  0, 15,
367    0xf8, GB_CS_SUB, 0, 16,
368    0xf9, GB_CS_SUB, 0, 32,
369    0xfa, GB_CS_SUB, 0, 48,
370    0xfb, GB_CS_SUB, 0, 64,
371    0xfc, GB_CS_SUB, 0, 128,
372    0xfd, GB_CS_SUB, 1, 0,
373    0xfe, GB_CS_SUB, 2, 0,
374    0xff, GB_CS_SUB, 4, 0,
375    0
376};
377
378struct gb_exitfun {
379    void (*exitfun)();
380    gb_exitfun *next;
381};
382
383void GB_atexit(void (*exitfun)()) {
384    // called when GB_shell is destroyed (use similar to atexit())
385    //
386    // Since the program does not neccessarily terminate, your code calling
387    // GB_atexit() may run multiple times. Make sure everything is completely reset by your 'exitfun'
388
389    gb_exitfun *fun = new gb_exitfun;
390    fun->exitfun    = exitfun;
391
392    fun->next          = gb_local->atgbexit;
393    gb_local->atgbexit = fun;
394}
395
396static void run_and_destroy_exit_functions(gb_exitfun *fun) {
397    if (fun) {
398        fun->exitfun();
399        run_and_destroy_exit_functions(fun->next);
400        delete fun;
401    }
402}
403
404static void GB_exit_gb() {
405    GB_shell::ensure_inside();
406
407    if (gb_local) {
408        gb_local->~gb_local_data(); // inplace-dtor
409        gbm_free_mem(gb_local, sizeof(*gb_local), 0);
410        gb_local = NULL;
411        gbm_flush_mem();
412    }
413}
414
415gb_local_data::~gb_local_data() {
416    gb_assert(openedDBs == closedDBs);
417
418    run_and_destroy_exit_functions(atgbexit);
419
420    free(bitcompress);
421    gb_free_compress_tree(bituncompress);
422    free(write_buffer);
423
424    free(check_out_buffer(&buf2));
425    free(check_out_buffer(&buf1));
426    free(open_gb_mains);
427}
428
429// -----------------
430//      GB_shell
431
432
433static GB_shell *inside_shell = NULL;
434
435GB_shell::GB_shell() {
436    if (inside_shell) GBK_terminate("only one GB_shell allowed");
437    inside_shell = this;
438}
439GB_shell::~GB_shell() {
440    gb_assert(inside_shell == this);
441    GB_exit_gb();
442    inside_shell = NULL;
443}
444void GB_shell::ensure_inside()  { if (!inside_shell) GBK_terminate("Not inside GB_shell"); }
445
446bool GB_shell::in_shell() { // used by code based on ARBDB (Kai IIRC)
447    return inside_shell;
448}
449
450struct GB_test_shell_closed {
451    ~GB_test_shell_closed() {
452        if (GB_shell::in_shell()) { // leave that call
453            inside_shell->~GB_shell(); // call dtor
454        }
455    }
456};
457static GB_test_shell_closed test;
458
459#if defined(UNIT_TESTS)
460static bool closed_open_shell_for_unit_tests() {
461    bool was_open = inside_shell;
462    if (was_open) {
463        if (gb_local) gb_local->fake_closed_DBs();
464        inside_shell->~GB_shell(); // just call dtor (not delete)
465    }
466    return was_open;
467}
468#endif
469
470void GB_init_gb() {
471    GB_shell::ensure_inside();
472    if (!gb_local) {
473        GBK_install_SIGSEGV_handler(true);          // never uninstalled
474        gbm_init_mem();
475        gb_local = (gb_local_data *)gbm_get_mem(sizeof(gb_local_data), 0);
476        ::new(gb_local) gb_local_data(); // inplace-ctor
477    }
478}
479
480int GB_open_DBs() { return gb_local ? gb_local->open_dbs() : 0; }
481
482gb_local_data::gb_local_data()
483{
484    init_buffer(&buf1, 4000);
485    init_buffer(&buf2, 4000);
486
487    write_bufsize = GBCM_BUFFER;
488    write_buffer  = (char *)malloc((size_t)write_bufsize);
489    write_ptr     = write_buffer;
490    write_free    = write_bufsize;
491
492    bituncompress = gb_build_uncompress_tree(GB_BIT_compress_data, 1, 0);
493    bitcompress   = gb_build_compress_list(GB_BIT_compress_data, 1, &(bc_size));
494
495    openedDBs = 0;
496    closedDBs = 0;
497
498    open_gb_mains = NULL;
499    open_gb_alloc = 0;
500
501    atgbexit = NULL;
502
503    iamclient                  = false;
504    search_system_folder       = false;
505    running_client_transaction = ARB_NO_TRANS;
506}
507
508void gb_local_data::announce_db_open(GB_MAIN_TYPE *Main) {
509    gb_assert(Main);
510    int idx = open_dbs();
511    if (idx >= open_gb_alloc) {
512        int            new_alloc = open_gb_alloc+10;
513        GB_MAIN_TYPE **new_mains = (GB_MAIN_TYPE**)realloc(open_gb_mains, new_alloc*sizeof(*new_mains));
514        memset(new_mains+open_gb_alloc, 0, 10*sizeof(*new_mains));
515        open_gb_alloc            = new_alloc;
516        open_gb_mains            = new_mains;
517    }
518    open_gb_mains[idx] = Main;
519    openedDBs++;
520}
521
522void gb_local_data::announce_db_close(GB_MAIN_TYPE *Main) {
523    gb_assert(Main);
524    int open = open_dbs();
525    int idx;
526    for (idx = 0; idx<open; ++idx) if (open_gb_mains[idx] == Main) break;
527
528    gb_assert(idx<open); // passed gb_main is unknown
529    if (idx<open) {
530        if (idx<(open-1)) { // not last
531            open_gb_mains[idx] = open_gb_mains[open-1];
532        }
533        closedDBs++;
534    }
535    if (closedDBs == openedDBs) {
536        GB_exit_gb(); // free most memory allocated by ARBDB library
537        // Caution: calling GB_exit_gb() frees 'this'!
538    }
539}
540
541static GBDATA *gb_remembered_db() {
542    GB_MAIN_TYPE *Main = gb_local ? gb_local->get_any_open_db() : NULL;
543    return Main ? Main->gb_main() : NULL;
544}
545
546GB_ERROR gb_unfold(GBCONTAINER *gbc, long deep, int index_pos) {
547    /*! get data from server.
548     *
549     * @param gbc container to unfold
550     * @param deep if != 0, then get subitems too.
551     * @param index_pos
552     * - >= 0, get indexed item from server
553     * - <0, get all items
554     *
555     * @return error on failure
556     */
557
558    GB_ERROR        error;
559    gb_header_list *header = GB_DATA_LIST_HEADER(gbc->d);
560
561    if (!gbc->flags2.folded_container) return 0;
562    if (index_pos> gbc->d.nheader) gb_create_header_array(gbc, index_pos + 1);
563    if (index_pos >= 0  && GB_HEADER_LIST_GBD(header[index_pos])) return 0;
564
565    if (GBCONTAINER_MAIN(gbc)->is_server()) {
566        GB_internal_error("Cannot unfold in server");
567        return 0;
568    }
569
570    do {
571        if (index_pos<0) break;
572        if (index_pos >= gbc->d.nheader) break;
573        if (header[index_pos].flags.changed >= GB_DELETED) {
574            GB_internal_error("Tried to unfold a deleted item");
575            return 0;
576        }
577        if (GB_HEADER_LIST_GBD(header[index_pos])) return 0;            // already unfolded
578    } while (0);
579
580    error = gbcm_unfold_client(gbc, deep, index_pos);
581    if (error) {
582        GB_print_error();
583        return error;
584    }
585
586    if (index_pos<0) {
587        gb_untouch_children(gbc);
588        gbc->flags2.folded_container = 0;
589    }
590    else {
591        GBDATA *gb2 = GBCONTAINER_ELEM(gbc, index_pos);
592        if (gb2) {
593            if (gb2->is_container()) {
594                gb_untouch_children_and_me(gb2->as_container());
595            }
596            else {
597                gb_untouch_me(gb2->as_entry());
598            }
599        }
600    }
601    return 0;
602}
603
604// -----------------------
605//      close database
606
607typedef void (*gb_close_callback)(GBDATA *gb_main, void *client_data);
608
609struct gb_close_callback_list {
610    gb_close_callback_list *next;
611    gb_close_callback       cb;
612    void                   *client_data;
613};
614
615#if defined(ASSERTION_USED)
616static bool atclose_cb_exists(gb_close_callback_list *gccs, gb_close_callback cb) {
617    return gccs && (gccs->cb == cb || atclose_cb_exists(gccs->next, cb));
618}
619#endif // ASSERTION_USED
620
621void GB_atclose(GBDATA *gbd, void (*fun)(GBDATA *gb_main, void *client_data), void *client_data) {
622    /*! Add a callback, which gets called directly before GB_close destroys all data.
623     * This is the recommended way to remove all callbacks from DB elements.
624     */
625
626    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
627
628    gb_assert(!atclose_cb_exists(Main->close_callbacks, fun)); // each close callback should only exist once
629
630    gb_close_callback_list *gccs = (gb_close_callback_list *)malloc(sizeof(*gccs));
631
632    gccs->next        = Main->close_callbacks;
633    gccs->cb          = fun;
634    gccs->client_data = client_data;
635
636    Main->close_callbacks = gccs;
637}
638
639static void run_close_callbacks(GBDATA *gb_main, gb_close_callback_list *gccs) {
640    while (gccs) {
641        gccs->cb(gb_main, gccs->client_data);
642        gb_close_callback_list *next = gccs->next;
643        free(gccs);
644        gccs = next;
645    }
646}
647
648static gb_triggered_callback *currently_called_back = NULL; // points to callback during callback; NULL otherwise
649static GB_MAIN_TYPE          *inside_callback_main  = NULL; // points to DB root during callback; NULL otherwise
650
651void gb_pending_callbacks::call_and_forget(GB_CB_TYPE allowedTypes) {
652#if defined(ASSERTION_USED)
653    const gb_triggered_callback *tail = get_tail();
654#endif
655
656    for (itertype cb = callbacks.begin(); cb != callbacks.end(); ++cb) {
657        currently_called_back = &*cb;
658        gb_assert(currently_called_back);
659        currently_called_back->spec(cb->gbd, allowedTypes);
660        currently_called_back = NULL;
661    }
662
663    gb_assert(tail == get_tail());
664
665    callbacks.clear();
666}
667
668void GB_MAIN_TYPE::call_pending_callbacks() {
669    inside_callback_main = this;
670
671    deleteCBs.pending.call_and_forget(GB_CB_DELETE);         // first all delete callbacks:
672    changeCBs.pending.call_and_forget(GB_CB_ALL_BUT_DELETE); // then all change callbacks:
673
674    inside_callback_main = NULL;
675}
676
677inline void GB_MAIN_TYPE::callback_group::forget_hcbs() {
678    delete hierarchy_cbs;
679    hierarchy_cbs = NULL;
680}
681
682void GB_MAIN_TYPE::forget_hierarchy_cbs() {
683    changeCBs.forget_hcbs();
684    deleteCBs.forget_hcbs();
685}
686
687void GB_close(GBDATA *gbd) {
688    GB_ERROR      error = NULL;
689    GB_MAIN_TYPE *Main  = GB_MAIN(gbd);
690
691    gb_assert(Main->get_transaction_level() <= 0); // transaction running - you can't close DB yet!
692
693    Main->forget_hierarchy_cbs();
694
695    gb_assert(Main->gb_main() == gbd);
696    run_close_callbacks(gbd, Main->close_callbacks);
697    Main->close_callbacks = 0;
698
699    if (Main->is_client()) {
700        long result            = gbcmc_close(Main->c_link);
701        if (result != 0) error = GBS_global_string("gbcmc_close returns %li", result);
702    }
703
704    gbcm_logout(Main, NULL);                        // logout default user
705   
706    if (!error) {
707        gb_assert(Main->close_callbacks == 0);
708
709        gb_delete_dummy_father(Main->dummy_father);
710        Main->root_container = NULL;
711
712        /* ARBDB applications using awars easily crash in call_pending_callbacks(),
713         * if AWARs are still bound to elements in the closed database.
714         *
715         * To unlink awars call AW_root::unlink_awars_from_DB().
716         * If that doesn't help, test Main->data (often aka as GLOBAL_gb_main)
717         */
718        Main->call_pending_callbacks();                  // do all callbacks
719        delete Main;
720    }
721
722    if (error) {
723        GB_warningf("Error in GB_close: %s", error);
724    }
725}
726
727void gb_close_unclosed_DBs() {
728    GBDATA *gb_main;
729    while ((gb_main = gb_remembered_db())) {
730        GB_close(gb_main);
731    }
732}
733
734// ------------------
735//      read data
736
737long GB_read_int(GBDATA *gbd)
738{
739    GB_TEST_READ(gbd, GB_INT, "GB_read_int");
740    return gbd->as_entry()->info.i;
741}
742
743int GB_read_byte(GBDATA *gbd)
744{
745    GB_TEST_READ(gbd, GB_BYTE, "GB_read_byte");
746    return gbd->as_entry()->info.i;
747}
748
749GBDATA *GB_read_pointer(GBDATA *gbd) {
750    GB_TEST_READ(gbd, GB_POINTER, "GB_read_pointer");
751    return gbd->as_entry()->info.ptr;
752}
753
754double GB_read_float(GBDATA *gbd) // @@@ change return type to float (that's what's stored in DB)
755{
756    XDR          xdrs;
757    static float f; // @@@ why static?
758   
759    GB_TEST_READ(gbd, GB_FLOAT, "GB_read_float");
760    xdrmem_create(&xdrs, &gbd->as_entry()->info.in.data[0], SIZOFINTERN, XDR_DECODE);
761    xdr_float(&xdrs, &f);
762    xdr_destroy(&xdrs);
763
764    gb_assert(f == f); // !nan
765
766    return (double)f;
767}
768
769long GB_read_count(GBDATA *gbd) {
770    return gbd->as_entry()->size();
771}
772
773long GB_read_memuse(GBDATA *gbd) {
774    return gbd->as_entry()->memsize();
775}
776
777#if defined(DEBUG)
778
779#define STD_LIST_NODE_NAME _List_node
780
781inline long calc_size(gb_callback_list *gbcbl) {
782    return gbcbl
783        ? sizeof(*gbcbl) + gbcbl->callbacks.size()* sizeof(std::STD_LIST_NODE_NAME<gb_callback_list::cbtype>)
784        : 0;
785}
786inline long calc_size(gb_transaction_save *gbts) {
787    return gbts
788        ? sizeof(*gbts)
789        : 0;
790}
791inline long calc_size(gb_if_entries *gbie) {
792    return gbie
793        ? sizeof(*gbie) + calc_size(GB_IF_ENTRIES_NEXT(gbie))
794        : 0;
795}
796inline long calc_size(GB_REL_IFES *gbri, int table_size) {
797    long size = 0;
798
799    gb_if_entries *ifes;
800    for (int idx = 0; idx<table_size; ++idx) {
801        for (ifes = GB_ENTRIES_ENTRY(gbri, idx);
802             ifes;
803             ifes = GB_IF_ENTRIES_NEXT(ifes))
804        {
805            size += calc_size(ifes);
806        }
807    }
808    return size;
809}
810inline long calc_size(gb_index_files *gbif) {
811    return gbif
812        ? sizeof(*gbif) + calc_size(GB_INDEX_FILES_NEXT(gbif)) + calc_size(GB_INDEX_FILES_ENTRIES(gbif), gbif->hash_table_size)
813        : 0;
814}
815inline long calc_size(gb_db_extended *gbe) {
816    return gbe
817        ? sizeof(*gbe) + calc_size(gbe->callback) + calc_size(gbe->old)
818        : 0;
819}
820inline long calc_size(GBENTRY *gbe) {
821    return gbe
822        ? sizeof(*gbe) + calc_size(gbe->ext)
823        : 0;
824}
825inline long calc_size(GBCONTAINER *gbc) {
826    return gbc
827        ? sizeof(*gbc) + calc_size(gbc->ext) + calc_size(GBCONTAINER_IFS(gbc))
828        : 0;
829}
830
831long GB_calc_structure_size(GBDATA *gbd) {
832    long size = 0;
833    if (gbd->is_container()) {
834        size = calc_size(gbd->as_container());
835    }
836    else {
837        size = calc_size(gbd->as_entry());
838    }
839    return size;
840}
841
842void GB_SizeInfo::collect(GBDATA *gbd) {
843    if (gbd->is_container()) {
844        ++containers;
845        for (GBDATA *gb_child = GB_child(gbd); gb_child; gb_child = GB_nextChild(gb_child)) {
846            collect(gb_child);
847        }
848    }
849    else {
850        ++terminals;
851        mem += GB_read_memuse(gbd);
852
853        long size;
854        switch (gbd->type()) {
855            case GB_INT:    size = sizeof(int); break;
856            case GB_FLOAT:  size = sizeof(float); break;
857            case GB_BYTE:   size = sizeof(char); break;
858            case GB_STRING: size = GB_read_count(gbd); break; // accept 0 sized data for strings
859
860            default:
861                size = GB_read_count(gbd);
862                gb_assert(size>0);                            // terminal w/o data - really?
863                break;
864        }
865        data += size;
866    }
867    structure += GB_calc_structure_size(gbd);
868}
869#endif
870
871GB_CSTR GB_read_pntr(GBDATA *gbd) {
872    GBENTRY    *gbe  = gbd->as_entry();
873    const char *data = gbe->data();
874
875    if (data) {
876        if (gbe->flags.compressed_data) {   // uncompressed data return pntr to database entry
877            char *ca = gb_read_cache(gbe);
878
879            if (!ca) {
880                size_t      size = gbe->uncompressed_size();
881                const char *da   = gb_uncompress_data(gbe, data, size);
882
883                if (da) {
884                    ca = gb_alloc_cache_index(gbe, size);
885                    memcpy(ca, da, size);
886                }
887            }
888            data = ca;
889        }
890    }
891    return data;
892}
893
894int gb_read_nr(GBDATA *gbd) {
895    return gbd->index;
896}
897
898GB_CSTR GB_read_char_pntr(GBDATA *gbd) {
899    GB_TEST_READ(gbd, GB_STRING, "GB_read_char_pntr");
900    return GB_read_pntr(gbd);
901}
902
903char *GB_read_string(GBDATA *gbd) {
904    GB_TEST_READ(gbd, GB_STRING, "GB_read_string");
905    const char *d = GB_read_pntr(gbd);
906    if (!d) return NULL;
907    return GB_memdup(d, gbd->as_entry()->size()+1);
908}
909
910size_t GB_read_string_count(GBDATA *gbd) {
911    GB_TEST_READ(gbd, GB_STRING, "GB_read_string_count");
912    return gbd->as_entry()->size();
913}
914
915GB_CSTR GB_read_link_pntr(GBDATA *gbd) {
916    GB_TEST_READ(gbd, GB_LINK, "GB_read_link_pntr");
917    return GB_read_pntr(gbd);
918}
919
920static char *GB_read_link(GBDATA *gbd) {
921    const char *d;
922    GB_TEST_READ(gbd, GB_LINK, "GB_read_link_pntr");
923    d = GB_read_pntr(gbd);
924    if (!d) return NULL;
925    return GB_memdup(d, gbd->as_entry()->size()+1);
926}
927
928long GB_read_bits_count(GBDATA *gbd) {
929    GB_TEST_READ(gbd, GB_BITS, "GB_read_bits_count");
930    return gbd->as_entry()->size();
931}
932
933GB_CSTR GB_read_bits_pntr(GBDATA *gbd, char c_0, char c_1) {
934    GB_TEST_READ(gbd, GB_BITS, "GB_read_bits_pntr");
935    GBENTRY *gbe  = gbd->as_entry();
936    long     size = gbe->size();
937    if (size) {
938        char *ca = gb_read_cache(gbe);
939        if (ca) return ca;
940
941        ca               = gb_alloc_cache_index(gbe, size+1);
942        const char *data = gbe->data();
943        char       *da   = gb_uncompress_bits(data, size, c_0, c_1);
944        if (ca) {
945            memcpy(ca, da, size+1);
946            return ca;
947        }
948        return da;
949    }
950    return 0;
951}
952
953char *GB_read_bits(GBDATA *gbd, char c_0, char c_1) {
954    GB_CSTR d = GB_read_bits_pntr(gbd, c_0, c_1);
955    return d ? GB_memdup(d, gbd->as_entry()->size()+1) : 0;
956}
957
958
959GB_CSTR GB_read_bytes_pntr(GBDATA *gbd)
960{
961    GB_TEST_READ(gbd, GB_BYTES, "GB_read_bytes_pntr");
962    return GB_read_pntr(gbd);
963}
964
965long GB_read_bytes_count(GBDATA *gbd)
966{
967    GB_TEST_READ(gbd, GB_BYTES, "GB_read_bytes_count");
968    return gbd->as_entry()->size();
969}
970
971char *GB_read_bytes(GBDATA *gbd) {
972    GB_CSTR d = GB_read_bytes_pntr(gbd);
973    return d ? GB_memdup(d, gbd->as_entry()->size()) : 0;
974}
975
976GB_CUINT4 *GB_read_ints_pntr(GBDATA *gbd)
977{
978    GB_TEST_READ(gbd, GB_INTS, "GB_read_ints_pntr");
979    GBENTRY *gbe = gbd->as_entry();
980
981    GB_UINT4 *res;
982    if (gbe->flags.compressed_data) {
983        res = (GB_UINT4 *)GB_read_pntr(gbe);
984    }
985    else {
986        res = (GB_UINT4 *)gbe->data();
987    }
988    if (!res) return NULL;
989
990    if (0x01020304U == htonl(0x01020304U)) {
991        return res;
992    }
993    else {
994        int       size = gbe->size();
995        char     *buf2 = GB_give_other_buffer((char *)res, size<<2);
996        GB_UINT4 *s    = (GB_UINT4 *)res;
997        GB_UINT4 *d    = (GB_UINT4 *)buf2;
998
999        for (long i=size; i; i--) {
1000            *(d++) = htonl(*(s++));
1001        }
1002        return (GB_UINT4 *)buf2;
1003    }
1004}
1005
1006long GB_read_ints_count(GBDATA *gbd) { // used by ../PERL_SCRIPTS/SAI/SAI.pm@read_ints_count
1007    GB_TEST_READ(gbd, GB_INTS, "GB_read_ints_count");
1008    return gbd->as_entry()->size();
1009}
1010
1011GB_UINT4 *GB_read_ints(GBDATA *gbd)
1012{
1013    GB_CUINT4 *i = GB_read_ints_pntr(gbd);
1014    if (!i) return NULL;
1015    return  (GB_UINT4 *)GB_memdup((char *)i, gbd->as_entry()->size()*sizeof(GB_UINT4));
1016}
1017
1018GB_CFLOAT *GB_read_floats_pntr(GBDATA *gbd)
1019{
1020    GB_TEST_READ(gbd, GB_FLOATS, "GB_read_floats_pntr");
1021    GBENTRY *gbe = gbd->as_entry();
1022    char    *res;
1023    if (gbe->flags.compressed_data) {
1024        res = (char *)GB_read_pntr(gbe);
1025    }
1026    else {
1027        res = (char *)gbe->data();
1028    }
1029    if (res) {
1030        long size      = gbe->size();
1031        long full_size = size*sizeof(float);
1032
1033        XDR xdrs;
1034        xdrmem_create(&xdrs, res, (int)(full_size), XDR_DECODE);
1035
1036        char  *buf2 = GB_give_other_buffer(res, full_size);
1037        float *d    = (float *)(void*)buf2;
1038        for (long i=size; i; i--) {
1039            xdr_float(&xdrs, d);
1040            d++;
1041        }
1042        xdr_destroy(&xdrs);
1043        return (float *)(void*)buf2;
1044    }
1045    return NULL;
1046}
1047
1048static long GB_read_floats_count(GBDATA *gbd)
1049{
1050    GB_TEST_READ(gbd, GB_FLOATS, "GB_read_floats_count");
1051    return gbd->as_entry()->size();
1052}
1053
1054float *GB_read_floats(GBDATA *gbd) { // @@@ only used in unittest - check usage of floats
1055    GB_CFLOAT *f;
1056    f = GB_read_floats_pntr(gbd);
1057    if (!f) return NULL;
1058    return  (float *)GB_memdup((char *)f, gbd->as_entry()->size()*sizeof(float));
1059}
1060
1061char *GB_read_as_string(GBDATA *gbd) {
1062    switch (gbd->type()) {
1063        case GB_STRING: return GB_read_string(gbd);
1064        case GB_LINK:   return GB_read_link(gbd);
1065        case GB_BYTE:   return GBS_global_string_copy("%i", GB_read_byte(gbd));
1066        case GB_INT:    return GBS_global_string_copy("%li", GB_read_int(gbd));
1067        case GB_FLOAT:  return GBS_global_string_copy("%g", GB_read_float(gbd));
1068        case GB_BITS:   return GB_read_bits(gbd, '0', '1');
1069            /* Be careful : When adding new types here, you have to make sure that
1070             * GB_write_as_string is able to write them back and that this makes sense.
1071             */
1072        default:    return NULL;
1073    }
1074}
1075
1076// ------------------------------------------------------------
1077//      array type access functions (intended for perl use)
1078
1079long GB_read_from_ints(GBDATA *gbd, long index) { // used by ../PERL_SCRIPTS/SAI/SAI.pm@read_from_ints
1080    static GBDATA    *last_gbd = 0;
1081    static long       count    = 0;
1082    static GB_CUINT4 *i        = 0;
1083
1084    if (gbd != last_gbd) {
1085        count    = GB_read_ints_count(gbd);
1086        i        = GB_read_ints_pntr(gbd);
1087        last_gbd = gbd;
1088    }
1089
1090    if (index >= 0 && index < count) {
1091        return i[index];
1092    }
1093    return -1;
1094}
1095
1096double GB_read_from_floats(GBDATA *gbd, long index) { // @@@ unused
1097    static GBDATA    *last_gbd = 0;
1098    static long       count    = 0;
1099    static GB_CFLOAT *f        = 0;
1100
1101    if (gbd != last_gbd) {
1102        count    = GB_read_floats_count(gbd);
1103        f        = GB_read_floats_pntr(gbd);
1104        last_gbd = gbd;
1105    }
1106
1107    if (index >= 0 && index < count) {
1108        return f[index];
1109    }
1110    return -1;
1111}
1112
1113// -------------------
1114//      write data
1115
1116static void gb_remove_callbacks_marked_for_deletion(GBDATA *gbd);
1117
1118static void gb_do_callbacks(GBDATA *gbd) {
1119    gb_assert(GB_MAIN(gbd)->get_transaction_level() < 0); // only use in NO_TRANSACTION_MODE!
1120
1121    while (gbd) {
1122        GBDATA *gbdn = GB_get_father(gbd);
1123        gb_callback_list *cbl = gbd->get_callbacks();
1124        if (cbl && cbl->call(gbd, GB_CB_CHANGED)) {
1125            gb_remove_callbacks_marked_for_deletion(gbd);
1126        }
1127        gbd = gbdn;
1128    }
1129}
1130
1131#define GB_DO_CALLBACKS(gbd) do { if (GB_MAIN(gbd)->get_transaction_level() < 0) gb_do_callbacks(gbd); } while (0)
1132
1133GB_ERROR GB_write_byte(GBDATA *gbd, int i)
1134{
1135    GB_TEST_WRITE(gbd, GB_BYTE, "GB_write_byte");
1136    GBENTRY *gbe = gbd->as_entry();
1137    if (gbe->info.i != i) {
1138        gb_save_extern_data_in_ts(gbe);
1139        gbe->info.i = i & 0xff;
1140        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1141        GB_DO_CALLBACKS(gbe);
1142    }
1143    return 0;
1144}
1145
1146GB_ERROR GB_write_int(GBDATA *gbd, long i) {
1147#if defined(ARB_64)
1148#if defined(WARN_TODO)
1149#warning GB_write_int should be GB_ERROR GB_write_int(GBDATA *gbd,int32_t i)
1150#endif
1151#endif
1152
1153    GB_TEST_WRITE(gbd, GB_INT, "GB_write_int");
1154    if ((long)((int32_t)i) != i) {
1155        gb_assert(0);
1156        GB_warningf("Warning: 64bit incompatibility detected\nNo data written to '%s'\n", GB_get_db_path(gbd));
1157        return "GB_INT out of range (signed, 32bit)";
1158    }
1159    GBENTRY *gbe = gbd->as_entry();
1160    if (gbe->info.i != (int32_t)i) {
1161        gb_save_extern_data_in_ts(gbe);
1162        gbe->info.i = i;
1163        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1164        GB_DO_CALLBACKS(gbe);
1165    }
1166    return 0;
1167}
1168
1169GB_ERROR GB_write_pointer(GBDATA *gbd, GBDATA *pointer) {
1170    GB_TEST_WRITE(gbd, GB_POINTER, "GB_write_pointer");
1171    GBENTRY *gbe = gbd->as_entry();
1172    if (gbe->info.ptr != pointer) {
1173        gb_save_extern_data_in_ts(gbe);
1174        gbe->info.ptr = pointer;
1175        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1176        GB_DO_CALLBACKS(gbe);
1177    }
1178    return 0;
1179}
1180
1181GB_ERROR GB_write_float(GBDATA *gbd, double f)
1182{
1183    gb_assert(f == f); // !nan
1184
1185    XDR          xdrs;
1186    static float f2;
1187
1188    GB_TEST_WRITE(gbd, GB_FLOAT, "GB_write_float");
1189
1190#if defined(WARN_TODO)
1191#warning call GB_read_float here!
1192#endif
1193    GB_TEST_READ(gbd, GB_FLOAT, "GB_read_float");
1194
1195    GBENTRY *gbe = gbd->as_entry();
1196    xdrmem_create(&xdrs, &gbe->info.in.data[0], SIZOFINTERN, XDR_DECODE);
1197    xdr_float(&xdrs, &f2);
1198    xdr_destroy(&xdrs);
1199
1200    if (f2 != f) {
1201        f2 = f;
1202        gb_save_extern_data_in_ts(gbe);
1203        xdrmem_create(&xdrs, &gbe->info.in.data[0], SIZOFINTERN, XDR_ENCODE);
1204        xdr_float(&xdrs, &f2);
1205        xdr_destroy(&xdrs);
1206        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1207        GB_DO_CALLBACKS(gbe);
1208    }
1209    xdr_destroy(&xdrs);
1210    return 0;
1211}
1212
1213GB_ERROR gb_write_compressed_pntr(GBENTRY *gbe, const char *s, long memsize, long stored_size) {
1214    gb_uncache(gbe);
1215    gb_save_extern_data_in_ts(gbe);
1216    gbe->flags.compressed_data = 1;
1217    gbe->insert_data((char *)s, stored_size, (size_t)memsize);
1218    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1219
1220    return 0;
1221}
1222
1223int gb_get_compression_mask(GB_MAIN_TYPE *Main, GBQUARK key, int gb_type) {
1224    gb_Key *ks = &Main->keys[key];
1225    int     compression_mask;
1226
1227    if (ks->gb_key_disabled) {
1228        compression_mask = 0;
1229    }
1230    else {
1231        if (!ks->gb_key) gb_load_single_key_data(Main->gb_main(), key);
1232        compression_mask = gb_convert_type_2_compression_flags[gb_type] & ks->compression_mask;
1233    }
1234
1235    return compression_mask;
1236}
1237
1238GB_ERROR GB_write_pntr(GBDATA *gbd, const char *s, size_t bytes_size, size_t stored_size)
1239{
1240    // 'bytes_size' is the size of what 's' points to.
1241    // 'stored_size' is the size-information written into the DB
1242    //
1243    // e.g. for strings : stored_size = bytes_size-1, cause stored_size is string len,
1244    //                    but bytes_size includes zero byte.
1245
1246    GBENTRY      *gbe  = gbd->as_entry();
1247    GB_MAIN_TYPE *Main = GB_MAIN(gbe);
1248    GBQUARK       key  = GB_KEY_QUARK(gbe);
1249    GB_TYPES      type = gbe->type();
1250
1251    gb_assert(implicated(type == GB_STRING, stored_size == bytes_size-1)); // size constraint for strings not fulfilled!
1252
1253    gb_uncache(gbe);
1254    gb_save_extern_data_in_ts(gbe);
1255
1256    int compression_mask = gb_get_compression_mask(Main, key, type);
1257
1258    const char *d;
1259    size_t      memsize;
1260    if (compression_mask) {
1261        d = gb_compress_data(gbe, key, s, bytes_size, &memsize, compression_mask, false);
1262    }
1263    else {
1264        d = NULL;
1265    }
1266    if (d) {
1267        gbe->flags.compressed_data = 1;
1268    }
1269    else {
1270        d = s;
1271        gbe->flags.compressed_data = 0;
1272        memsize = bytes_size;
1273    }
1274
1275    gbe->insert_data(d, stored_size, memsize);
1276    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1277    GB_DO_CALLBACKS(gbe);
1278
1279    return 0;
1280}
1281
1282GB_ERROR GB_write_string(GBDATA *gbd, const char *s)
1283{
1284    GBENTRY *gbe = gbd->as_entry();
1285    GB_TEST_WRITE(gbe, GB_STRING, "GB_write_string");
1286    GB_TEST_NON_BUFFER(s, "GB_write_string");        // compress would destroy the other buffer
1287
1288    if (!s) s = "";
1289    size_t size = strlen(s);
1290
1291    // no zero len strings allowed
1292    if (gbe->memsize() && (size == gbe->size()))
1293    {
1294        if (!strcmp(s, GB_read_pntr(gbe)))
1295            return 0;
1296    }
1297#if defined(DEBUG) && 0
1298    // check for error (in compression)
1299    {
1300        GB_ERROR error = GB_write_pntr(gbe, s, size+1, size);
1301        if (!error) {
1302            char *check = GB_read_string(gbe);
1303
1304            gb_assert(check);
1305            gb_assert(strcmp(check, s) == 0);
1306
1307            free(check);
1308        }
1309        return error;
1310    }
1311#else
1312    return GB_write_pntr(gbe, s, size+1, size);
1313#endif // DEBUG
1314}
1315
1316GB_ERROR GB_write_link(GBDATA *gbd, const char *s)
1317{
1318    GBENTRY *gbe = gbd->as_entry();
1319    GB_TEST_WRITE(gbe, GB_STRING, "GB_write_link");
1320    GB_TEST_NON_BUFFER(s, "GB_write_link");          // compress would destroy the other buffer
1321
1322    if (!s) s = "";
1323    size_t size = strlen(s);
1324
1325    // no zero len strings allowed
1326    if (gbe->memsize()  && (size == gbe->size()))
1327    {
1328        if (!strcmp(s, GB_read_pntr(gbe)))
1329            return 0;
1330    }
1331    return GB_write_pntr(gbe, s, size+1, size);
1332}
1333
1334
1335GB_ERROR GB_write_bits(GBDATA *gbd, const char *bits, long size, const char *c_0)
1336{
1337    GBENTRY *gbe = gbd->as_entry();
1338    GB_TEST_WRITE(gbe, GB_BITS, "GB_write_bits");
1339    GB_TEST_NON_BUFFER(bits, "GB_write_bits");       // compress would destroy the other buffer
1340    gb_save_extern_data_in_ts(gbe);
1341
1342    long  memsize;
1343    char *d = gb_compress_bits(bits, size, (const unsigned char *)c_0, &memsize);
1344
1345    gbe->flags.compressed_data = 1;
1346    gbe->insert_data(d, size, memsize);
1347    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1348    GB_DO_CALLBACKS(gbe);
1349    return 0;
1350}
1351
1352GB_ERROR GB_write_bytes(GBDATA *gbd, const char *s, long size)
1353{
1354    GB_TEST_WRITE(gbd, GB_BYTES, "GB_write_bytes");
1355    return GB_write_pntr(gbd, s, size, size);
1356}
1357
1358GB_ERROR GB_write_ints(GBDATA *gbd, const GB_UINT4 *i, long size)
1359{
1360
1361    GB_TEST_WRITE(gbd, GB_INTS, "GB_write_ints");
1362    GB_TEST_NON_BUFFER((char *)i, "GB_write_ints");  // compress would destroy the other buffer
1363
1364    if (0x01020304 != htonl((GB_UINT4)0x01020304)) {
1365        long      j;
1366        char     *buf2 = GB_give_other_buffer((char *)i, size<<2);
1367        GB_UINT4 *s    = (GB_UINT4 *)i;
1368        GB_UINT4 *d    = (GB_UINT4 *)buf2;
1369
1370        for (j=size; j; j--) {
1371            *(d++) = htonl(*(s++));
1372        }
1373        i = (GB_UINT4 *)buf2;
1374    }
1375    return GB_write_pntr(gbd, (char *)i, size* 4 /* sizeof(long4) */, size);
1376}
1377
1378GB_ERROR GB_write_floats(GBDATA *gbd, const float *f, long size)
1379{
1380    long fullsize = size * sizeof(float);
1381    GB_TEST_WRITE(gbd, GB_FLOATS, "GB_write_floats");
1382    GB_TEST_NON_BUFFER((char *)f, "GB_write_floats"); // compress would destroy the other buffer
1383
1384    {
1385        XDR    xdrs;
1386        long   i;
1387        char  *buf2 = GB_give_other_buffer((char *)f, fullsize);
1388        float *s    = (float *)f;
1389
1390        xdrmem_create(&xdrs, buf2, (int)fullsize, XDR_ENCODE);
1391        for (i=size; i; i--) {
1392            xdr_float(&xdrs, s);
1393            s++;
1394        }
1395        xdr_destroy (&xdrs);
1396        f = (float*)(void*)buf2;
1397    }
1398    return GB_write_pntr(gbd, (char *)f, size*sizeof(float), size);
1399}
1400
1401GB_ERROR GB_write_as_string(GBDATA *gbd, const char *val) {
1402    switch (gbd->type()) {
1403        case GB_STRING: return GB_write_string(gbd, val);
1404        case GB_LINK:   return GB_write_link(gbd, val);
1405        case GB_BYTE:   return GB_write_byte(gbd, atoi(val));
1406        case GB_INT:    return GB_write_int(gbd, atoi(val));
1407        case GB_FLOAT:  return GB_write_float(gbd, GB_atof(val));
1408        case GB_BITS:   return GB_write_bits(gbd, val, strlen(val), "0");
1409        default:    return GB_export_errorf("Error: You cannot use GB_write_as_string on this type of entry (%s)", GB_read_key_pntr(gbd));
1410    }
1411}
1412
1413// ---------------------------
1414//      security functions
1415
1416int GB_read_security_write(GBDATA *gbd) {
1417    GB_test_transaction(gbd);
1418    return GB_GET_SECURITY_WRITE(gbd);
1419}
1420int GB_read_security_read(GBDATA *gbd) {
1421    GB_test_transaction(gbd);
1422    return GB_GET_SECURITY_READ(gbd);
1423}
1424int GB_read_security_delete(GBDATA *gbd) {
1425    GB_test_transaction(gbd);
1426    return GB_GET_SECURITY_DELETE(gbd);
1427}
1428GB_ERROR GB_write_security_write(GBDATA *gbd, unsigned long level)
1429{
1430    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1431    GB_test_transaction(Main);
1432
1433    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level)
1434        return gb_security_error(gbd);
1435    if (GB_GET_SECURITY_WRITE(gbd) == level) return 0;
1436    GB_PUT_SECURITY_WRITE(gbd, level);
1437    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1438    GB_DO_CALLBACKS(gbd);
1439    return 0;
1440}
1441GB_ERROR GB_write_security_read(GBDATA *gbd, unsigned long level) // @@@ unused
1442{
1443    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1444    GB_test_transaction(Main);
1445    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level)
1446        return gb_security_error(gbd);
1447    if (GB_GET_SECURITY_READ(gbd) == level) return 0;
1448    GB_PUT_SECURITY_READ(gbd, level);
1449    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1450    GB_DO_CALLBACKS(gbd);
1451    return 0;
1452}
1453GB_ERROR GB_write_security_delete(GBDATA *gbd, unsigned long level)
1454{
1455    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1456    GB_test_transaction(Main);
1457    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level)
1458        return gb_security_error(gbd);
1459    if (GB_GET_SECURITY_DELETE(gbd) == level) return 0;
1460    GB_PUT_SECURITY_DELETE(gbd, level);
1461    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1462    GB_DO_CALLBACKS(gbd);
1463    return 0;
1464}
1465GB_ERROR GB_write_security_levels(GBDATA *gbd, unsigned long readlevel, unsigned long writelevel, unsigned long deletelevel)
1466{
1467    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1468    GB_test_transaction(Main);
1469    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level)
1470        return gb_security_error(gbd);
1471    GB_PUT_SECURITY_WRITE(gbd, writelevel);
1472    GB_PUT_SECURITY_READ(gbd, readlevel);
1473    GB_PUT_SECURITY_DELETE(gbd, deletelevel);
1474    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1475    GB_DO_CALLBACKS(gbd);
1476    return 0;
1477}
1478
1479void GB_change_my_security(GBDATA *gbd, int level) {
1480    GB_MAIN(gbd)->security_level = level<0 ? 0 : (level>7 ? 7 : level);
1481}
1482
1483// For internal use only
1484void GB_push_my_security(GBDATA *gbd)
1485{
1486    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1487    Main->pushed_security_level++;
1488    if (Main->pushed_security_level <= 1) {
1489        Main->old_security_level = Main->security_level;
1490        Main->security_level = 7;
1491    }
1492}
1493
1494void GB_pop_my_security(GBDATA *gbd) {
1495    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1496    Main->pushed_security_level--;
1497    if (Main->pushed_security_level <= 0) {
1498        Main->security_level = Main->old_security_level;
1499    }
1500}
1501
1502
1503// ------------------------
1504//      Key information
1505
1506GB_TYPES GB_read_type(GBDATA *gbd) {
1507    GB_test_transaction(gbd);
1508    return gbd->type();
1509}
1510
1511bool GB_is_container(GBDATA *gbd) {
1512    return gbd && gbd->is_container();
1513}
1514
1515char *GB_read_key(GBDATA *gbd) {
1516    return strdup(GB_read_key_pntr(gbd));
1517}
1518
1519GB_CSTR GB_read_key_pntr(GBDATA *gbd) {
1520    GB_CSTR k;
1521    GB_test_transaction(gbd);
1522    k         = GB_KEY(gbd);
1523    if (!k) k = GBS_global_string("<invalid key (quark=%i)>", GB_KEY_QUARK(gbd));
1524    return k;
1525}
1526
1527GB_CSTR gb_read_key_pntr(GBDATA *gbd) {
1528    return GB_KEY(gbd);
1529}
1530
1531
1532GBQUARK gb_find_existing_quark(GB_MAIN_TYPE *Main, const char *key) {
1533    //! @return existing quark for 'key' (-1 if key == NULL, 0 if key is unknown)
1534    return key ? GBS_read_hash(Main->key_2_index_hash, key) : -1;
1535}
1536
1537GBQUARK gb_find_or_create_quark(GB_MAIN_TYPE *Main, const char *key) {
1538    //! @return existing or newly created quark for 'key'
1539    GBQUARK quark     = gb_find_existing_quark(Main, key);
1540    if (!quark) quark = gb_create_key(Main, key, true);
1541    return quark;
1542}
1543
1544GBQUARK gb_find_or_create_NULL_quark(GB_MAIN_TYPE *Main, const char *key) {
1545    // similar to gb_find_or_create_quark,
1546    // but if 'key' is NULL, quark 0 will be returned.
1547    //
1548    // Use this function with care.
1549    //
1550    // Known good use:
1551    // - create main entry and its dummy father via gb_make_container()
1552
1553    return key ? gb_find_or_create_quark(Main, key) : 0;
1554}
1555
1556GBQUARK GB_find_existing_quark(GBDATA *gbd, const char *key) {
1557    //! @return existing quark for 'key' (-1 if key == NULL, 0 if key is unknown)
1558    return gb_find_existing_quark(GB_MAIN(gbd), key);
1559}
1560
1561GBQUARK GB_find_or_create_quark(GBDATA *gbd, const char *key) {
1562    //! @return existing or newly created quark for 'key'
1563    return gb_find_or_create_quark(GB_MAIN(gbd), key);
1564}
1565
1566
1567// ---------------------------------------------
1568
1569GBQUARK GB_get_quark(GBDATA *gbd) {
1570    return GB_KEY_QUARK(gbd);
1571}
1572
1573bool GB_has_key(GBDATA *gbd, const char *key) {
1574    GBQUARK quark = GB_find_existing_quark(gbd, key); 
1575    return quark && (quark == GB_get_quark(gbd));
1576}
1577
1578// ---------------------------------------------
1579
1580long GB_read_clock(GBDATA *gbd) {
1581    if (GB_ARRAY_FLAGS(gbd).changed) return GB_MAIN(gbd)->clock;
1582    return gbd->update_date();
1583}
1584
1585// ---------------------------------------------
1586//      Get and check the database hierarchy
1587
1588GBDATA *GB_get_father(GBDATA *gbd) {
1589    // Get the father of an entry
1590    GB_test_transaction(gbd);
1591    return gbd->get_father();
1592}
1593
1594GBDATA *GB_get_grandfather(GBDATA *gbd) {
1595    GB_test_transaction(gbd);
1596
1597    GBDATA *gb_grandpa = GB_FATHER(gbd);
1598    if (gb_grandpa) {
1599        gb_grandpa = GB_FATHER(gb_grandpa);
1600        if (gb_grandpa && !GB_FATHER(gb_grandpa)) gb_grandpa = NULL; // never return dummy_father of root container
1601    }
1602    return gb_grandpa;
1603}
1604
1605// Get the root entry (gb_main)
1606GBDATA *GB_get_root(GBDATA *gbd) { return GB_MAIN(gbd)->gb_main(); }
1607GBCONTAINER *gb_get_root(GBENTRY *gbe) { return GB_MAIN(gbe)->root_container; }
1608GBCONTAINER *gb_get_root(GBCONTAINER *gbc) { return GB_MAIN(gbc)->root_container; }
1609
1610bool GB_check_father(GBDATA *gbd, GBDATA *gb_maybefather) {
1611    // Test whether an entry is a subentry of another
1612    GBDATA *gbfather;
1613    for (gbfather = GB_get_father(gbd);
1614         gbfather;
1615         gbfather = GB_get_father(gbfather))
1616    {
1617        if (gbfather == gb_maybefather) return true;
1618    }
1619    return false;
1620}
1621
1622// --------------------------
1623//      create and rename
1624
1625GBENTRY *gb_create(GBCONTAINER *father, const char *key, GB_TYPES type) {
1626    GBENTRY *gbe = gb_make_entry(father, key, -1, 0, type);
1627    gb_touch_header(GB_FATHER(gbe));
1628    gb_touch_entry(gbe, GB_CREATED);
1629
1630    gb_assert(GB_ARRAY_FLAGS(gbe).changed < GB_DELETED); // happens sometimes -> needs debugging
1631
1632    return gbe;
1633}
1634
1635GBCONTAINER *gb_create_container(GBCONTAINER *father, const char *key) {
1636    // Create a container, do not check anything
1637    GBCONTAINER *gbc = gb_make_container(father, key, -1, 0);
1638    gb_touch_header(GB_FATHER(gbc));
1639    gb_touch_entry(gbc, GB_CREATED);
1640    return gbc;
1641}
1642
1643GBDATA *GB_create(GBDATA *father, const char *key, GB_TYPES type) {
1644    /*! Create a DB entry
1645     *
1646     * @param father container to create DB field in
1647     * @param key name of field
1648     * @param type field type
1649     *
1650     * @return
1651     * - created DB entry
1652     * - NULL on failure (error is exported then)
1653     *
1654     * @see GB_create_container()
1655     */
1656
1657    if (GB_check_key(key)) {
1658        GB_print_error();
1659        return NULL;
1660    }
1661
1662    if (type == GB_DB) {
1663        gb_assert(type != GB_DB); // you like to use GB_create_container!
1664        GB_export_error("GB_create error: can't create containers");
1665        return NULL;
1666    }
1667
1668    if (!father) {
1669        GB_internal_errorf("GB_create error in GB_create:\nno father (key = '%s')", key);
1670        return NULL;
1671    }
1672    GB_test_transaction(father);
1673    if (father->is_entry()) {
1674        GB_export_errorf("GB_create: father (%s) is not of GB_DB type (%i) (creating '%s')",
1675                         GB_read_key_pntr(father), father->type(), key);
1676        return NULL;
1677    }
1678
1679    if (type == GB_POINTER) {
1680        if (!GB_in_temporary_branch(father)) {
1681            GB_export_error("GB_create: pointers only allowed in temporary branches");
1682            return NULL;
1683        }
1684    }
1685
1686    return gb_create(father->expect_container(), key, type);
1687}
1688
1689GBDATA *GB_create_container(GBDATA *father, const char *key) {
1690    /*! Create a new DB container
1691     *
1692     * @param father parent container
1693     * @param key name of created container
1694     *
1695     * @return
1696     * - created container
1697     * - NULL on failure (error is exported then)
1698     *
1699     * @see GB_create()
1700     */
1701
1702    if (GB_check_key(key)) {
1703        GB_print_error();
1704        return NULL;
1705    }
1706
1707    if ((*key == '\0')) {
1708        GB_export_error("GB_create error: empty key");
1709        return NULL;
1710    }
1711    if (!father) {
1712        GB_internal_errorf("GB_create error in GB_create:\nno father (key = '%s')", key);
1713        return NULL;
1714    }
1715
1716    GB_test_transaction(father);
1717    return gb_create_container(father->expect_container(), key);
1718}
1719
1720// ----------------------
1721//      recompression
1722
1723#if defined(WARN_TODO)
1724#warning rename gb_set_compression into gb_recompress (misleading name)
1725#endif
1726
1727static GB_ERROR gb_set_compression(GBDATA *source) {
1728    GB_ERROR error = 0;
1729    GB_test_transaction(source);
1730
1731    switch (source->type()) {
1732        case GB_STRING: {
1733            char *str = GB_read_string(source);
1734            GB_write_string(source, "");
1735            GB_write_string(source, str);
1736            free(str);
1737            break;
1738        }
1739        case GB_BITS:
1740        case GB_BYTES:
1741        case GB_INTS:
1742        case GB_FLOATS:
1743            break;
1744        case GB_DB:
1745            for (GBDATA *gb_p = GB_child(source); gb_p && !error; gb_p = GB_nextChild(gb_p)) {
1746                error = gb_set_compression(gb_p);
1747            }
1748            break;
1749        default:
1750            break;
1751    }
1752    return error;
1753}
1754
1755bool GB_allow_compression(GBDATA *gb_main, bool allow_compression) {
1756    GB_MAIN_TYPE *Main      = GB_MAIN(gb_main);
1757    int           prev_mask = Main->compression_mask;
1758    Main->compression_mask  = allow_compression ? -1 : 0;
1759
1760    return prev_mask == 0 ? false : true;
1761}
1762
1763
1764GB_ERROR GB_delete(GBDATA*& source) {
1765    GBDATA *gb_main;
1766
1767    GB_test_transaction(source);
1768    if (GB_GET_SECURITY_DELETE(source)>GB_MAIN(source)->security_level) {
1769        return GBS_global_string("Security error: deleting entry '%s' not permitted", GB_read_key_pntr(source));
1770    }
1771
1772    gb_main = GB_get_root(source);
1773
1774    if (source->flags.compressed_data) {
1775        bool was_allowed = GB_allow_compression(gb_main, false);
1776        gb_set_compression(source); // write data w/o compression (otherwise GB_read_old_value... won't work)
1777        GB_allow_compression(gb_main, was_allowed);
1778    }
1779
1780    {
1781        GB_MAIN_TYPE *Main = GB_MAIN(source);
1782        if (Main->get_transaction_level() < 0) { // no transaction mode
1783            gb_delete_entry(source);
1784            Main->call_pending_callbacks();
1785        }
1786        else {
1787            gb_touch_entry(source, GB_DELETED);
1788        }
1789    }
1790    return 0;
1791}
1792
1793GB_ERROR gb_delete_force(GBDATA *source)    // delete always
1794{
1795    gb_touch_entry(source, GB_DELETED);
1796    return 0;
1797}
1798
1799
1800// ------------------
1801//      Copy data
1802
1803#if defined(WARN_TODO)
1804#warning replace GB_copy with GB_copy_with_protection after release
1805#endif
1806
1807GB_ERROR GB_copy(GBDATA *dest, GBDATA *source) {
1808    return GB_copy_with_protection(dest, source, false);
1809}
1810
1811GB_ERROR GB_copy_with_protection(GBDATA *dest, GBDATA *source, bool copy_all_protections) {
1812    GB_ERROR error = 0;
1813    GB_test_transaction(source);
1814
1815    GB_TYPES type = source->type();
1816    if (dest->type() != type) {
1817        return GB_export_errorf("incompatible types in GB_copy (source %s:%u != %s:%u",
1818                                GB_read_key_pntr(source), type, GB_read_key_pntr(dest), dest->type());
1819    }
1820
1821    switch (type) {
1822        case GB_INT:
1823            error = GB_write_int(dest, GB_read_int(source));
1824            break;
1825        case GB_FLOAT:
1826            error = GB_write_float(dest, GB_read_float(source));
1827            break;
1828        case GB_BYTE:
1829            error = GB_write_byte(dest, GB_read_byte(source));
1830            break;
1831        case GB_STRING:     // No local compression
1832            error = GB_write_string(dest, GB_read_char_pntr(source));
1833            break;
1834        case GB_LINK:       // No local compression
1835            error = GB_write_link(dest, GB_read_link_pntr(source));
1836            break;
1837        case GB_BITS:       // only local compressions for the following types
1838        case GB_BYTES:
1839        case GB_INTS:
1840        case GB_FLOATS: {
1841            GBENTRY *source_entry = source->as_entry();
1842            GBENTRY *dest_entry   = dest->as_entry();
1843
1844            gb_save_extern_data_in_ts(dest_entry);
1845            dest_entry->insert_data(source_entry->data(), source_entry->size(), source_entry->memsize());
1846
1847            dest->flags.compressed_data = source->flags.compressed_data;
1848            break;
1849        }
1850        case GB_DB: {
1851            if (!dest->is_container()) {
1852                GB_ERROR err = GB_export_errorf("GB_COPY Type conflict %s:%i != %s:%i",
1853                                                GB_read_key_pntr(dest), dest->type(), GB_read_key_pntr(source), GB_DB);
1854                GB_internal_error(err);
1855                return err;
1856            }
1857
1858            GBCONTAINER *destc   = dest->as_container();
1859            GBCONTAINER *sourcec = source->as_container();
1860
1861            if (sourcec->flags2.folded_container) gb_unfold(sourcec, -1, -1);
1862            if (destc->flags2.folded_container)   gb_unfold(destc, 0, -1);
1863
1864            for (GBDATA *gb_p = GB_child(sourcec); gb_p; gb_p = GB_nextChild(gb_p)) {
1865                const char *key = GB_read_key_pntr(gb_p);
1866                GBDATA     *gb_d;
1867
1868                if (gb_p->is_container()) {
1869                    gb_d = GB_create_container(destc, key);
1870                    gb_create_header_array(gb_d->as_container(), gb_p->as_container()->d.size);
1871                }
1872                else {
1873                    gb_d = GB_create(destc, key, gb_p->type());
1874                }
1875
1876                if (!gb_d) error = GB_await_error();
1877                else error       = GB_copy_with_protection(gb_d, gb_p, copy_all_protections);
1878
1879                if (error) break;
1880            }
1881
1882            destc->flags3 = sourcec->flags3;
1883            break;
1884        }
1885        default:
1886            error = GB_export_error("GB_copy-error: unhandled type");
1887    }
1888    if (error) return error;
1889
1890    gb_touch_entry(dest, GB_NORMAL_CHANGE);
1891
1892    dest->flags.security_read = source->flags.security_read;
1893    if (copy_all_protections) {
1894        dest->flags.security_write  = source->flags.security_write;
1895        dest->flags.security_delete = source->flags.security_delete;
1896    }
1897
1898    return 0;
1899}
1900
1901
1902static char *gb_stpcpy(char *dest, const char *source)
1903{
1904    while ((*dest++=*source++)) ;
1905    return dest-1; // return pointer to last copied character (which is \0)
1906}
1907
1908char* GB_get_subfields(GBDATA *gbd) {
1909    /*! Get all subfield names
1910     *
1911     * @return all subfields of 'gbd' as ';'-separated heap-copy
1912     * (first and last char of result is a ';')
1913     */
1914    GB_test_transaction(gbd);
1915
1916    char *result = 0;
1917    if (gbd->is_container()) {
1918        GBCONTAINER *gbc           = gbd->as_container();
1919        int          result_length = 0;
1920
1921        if (gbc->flags2.folded_container) {
1922            gb_unfold(gbc, -1, -1);
1923        }
1924
1925        for (GBDATA *gbp = GB_child(gbd); gbp; gbp = GB_nextChild(gbp)) {
1926            const char *key = GB_read_key_pntr(gbp);
1927            int keylen = strlen(key);
1928
1929            if (result) {
1930                char *neu_result = (char*)malloc(result_length+keylen+1+1);
1931
1932                if (neu_result) {
1933                    char *p = gb_stpcpy(neu_result, result);
1934                    p = gb_stpcpy(p, key);
1935                    *p++ = ';';
1936                    p[0] = 0;
1937
1938                    freeset(result, neu_result);
1939                    result_length += keylen+1;
1940                }
1941                else {
1942                    gb_assert(0);
1943                }
1944            }
1945            else {
1946                result = (char*)malloc(1+keylen+1+1);
1947                result[0] = ';';
1948                strcpy(result+1, key);
1949                result[keylen+1] = ';';
1950                result[keylen+2] = 0;
1951                result_length = keylen+2;
1952            }
1953        }
1954    }
1955    else {
1956        result = strdup(";");
1957    }
1958
1959    return result;
1960}
1961
1962// --------------------------
1963//      temporary entries
1964
1965GB_ERROR GB_set_temporary(GBDATA *gbd) { // goes to header: __ATTR__USERESULT
1966    /*! if the temporary flag is set, then that entry (including all subentries) will not be saved
1967     * @see GB_clear_temporary() and GB_is_temporary()
1968     */
1969
1970    GB_ERROR error = NULL;
1971    GB_test_transaction(gbd);
1972
1973    if (GB_GET_SECURITY_DELETE(gbd)>GB_MAIN(gbd)->security_level) {
1974        error = GBS_global_string("Security error in GB_set_temporary: %s", GB_read_key_pntr(gbd));
1975    }
1976    else {
1977        gbd->flags.temporary = 1;
1978        gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1979    }
1980    RETURN_ERROR(error);
1981}
1982
1983GB_ERROR GB_clear_temporary(GBDATA *gbd) { // @@@ used in ptpan branch - do not remove
1984    //! undo effect of GB_set_temporary()
1985
1986    GB_test_transaction(gbd);
1987    gbd->flags.temporary = 0;
1988    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1989    return 0;
1990}
1991
1992bool GB_is_temporary(GBDATA *gbd) {
1993    //! @see GB_set_temporary() and GB_in_temporary_branch()
1994    GB_test_transaction(gbd);
1995    return (long)gbd->flags.temporary;
1996}
1997
1998bool GB_in_temporary_branch(GBDATA *gbd) {
1999    /*! @return true, if 'gbd' is member of a temporary subtree,
2000     * i.e. if GB_is_temporary(itself or any parent)
2001     */
2002
2003    if (GB_is_temporary(gbd)) return true;
2004
2005    GBDATA *gb_parent = GB_get_father(gbd);
2006    if (!gb_parent) return false;
2007
2008    return GB_in_temporary_branch(gb_parent);
2009}
2010
2011// ---------------------
2012//      transactions
2013
2014GB_ERROR GB_MAIN_TYPE::initial_client_transaction() {
2015    // the first client transaction ever
2016    transaction_level = 1;
2017    GB_ERROR error    = gbcmc_init_transaction(root_container);
2018    if (!error) ++clock;
2019    return error;
2020}
2021
2022inline GB_ERROR GB_MAIN_TYPE::start_transaction() {
2023    gb_assert(transaction_level == 0);
2024
2025    transaction_level   = 1;
2026    aborted_transaction = 0;
2027
2028    GB_ERROR error = NULL;
2029    if (is_client()) {
2030        error = gbcmc_begin_transaction(gb_main());
2031        if (!error) {
2032            error = gb_commit_transaction_local_rek(gb_main_ref(), 0, 0); // init structures
2033            gb_untouch_children_and_me(root_container);
2034        }
2035    }
2036
2037    if (!error) {
2038        /* do all callbacks
2039         * cb that change the db are no problem, because it's the beginning of a ta
2040         */
2041        call_pending_callbacks();
2042        ++clock;
2043    }
2044    return error;
2045}
2046
2047inline GB_ERROR GB_MAIN_TYPE::begin_transaction() {
2048    if (transaction_level>0) return GBS_global_string("attempt to start a NEW transaction (at transaction level %i)", transaction_level);
2049    if (transaction_level == 0) return start_transaction();
2050    return NULL; // NO_TRANSACTION_MODE
2051}
2052
2053inline GB_ERROR GB_MAIN_TYPE::abort_transaction() {
2054    if (transaction_level<=0) {
2055        if (transaction_level<0) return "GB_abort_transaction: Attempt to abort transaction in no-transaction-mode";
2056        return "GB_abort_transaction: No transaction running";
2057    }
2058    if (transaction_level>1) {
2059        aborted_transaction = 1;
2060        return pop_transaction();
2061    }
2062
2063    gb_abort_transaction_local_rek(gb_main_ref());
2064    if (is_client()) {
2065        GB_ERROR error = gbcmc_abort_transaction(gb_main());
2066        if (error) return error;
2067    }
2068    clock--;
2069    call_pending_callbacks();
2070    transaction_level = 0;
2071    gb_untouch_children_and_me(root_container);
2072    return 0;
2073}
2074
2075inline GB_ERROR GB_MAIN_TYPE::commit_transaction() {
2076    GB_ERROR      error = 0;
2077    GB_CHANGE     flag;
2078
2079    if (!transaction_level) {
2080        return "commit_transaction: No transaction running";
2081    }
2082    if (transaction_level>1) {
2083        return GBS_global_string("attempt to commit at transaction level %i", transaction_level);
2084    }
2085    if (aborted_transaction) {
2086        aborted_transaction = 0;
2087        return abort_transaction();
2088    }
2089    if (is_server()) {
2090        char *error1 = gb_set_undo_sync(gb_main());
2091        while (1) {
2092            flag = (GB_CHANGE)GB_ARRAY_FLAGS(gb_main()).changed;
2093            if (!flag) break;           // nothing to do
2094            error = gb_commit_transaction_local_rek(gb_main_ref(), 0, 0);
2095            gb_untouch_children_and_me(root_container);
2096            if (error) break;
2097            call_pending_callbacks();
2098        }
2099        gb_disable_undo(gb_main());
2100        if (error1) {
2101            transaction_level = 0;
2102            gb_assert(error); // maybe return error1?
2103            return error; // @@@ huh? why not return error1
2104        }
2105    }
2106    else {
2107        gb_disable_undo(gb_main());
2108        while (1) {
2109            flag = (GB_CHANGE)GB_ARRAY_FLAGS(gb_main()).changed;
2110            if (!flag) break;           // nothing to do
2111
2112            error = gbcmc_begin_sendupdate(gb_main());                    if (error) break;
2113            error = gb_commit_transaction_local_rek(gb_main_ref(), 1, 0); if (error) break;
2114            error = gbcmc_end_sendupdate(gb_main());                      if (error) break;
2115
2116            gb_untouch_children_and_me(root_container);
2117            call_pending_callbacks();
2118        }
2119        if (!error) error = gbcmc_commit_transaction(gb_main());
2120
2121    }
2122    transaction_level = 0;
2123    return error;
2124}
2125
2126inline GB_ERROR GB_MAIN_TYPE::push_transaction() {
2127    if (transaction_level == 0) return start_transaction();
2128    if (transaction_level>0) ++transaction_level;
2129    // transaction<0 is NO_TRANSACTION_MODE
2130    return NULL;
2131}
2132
2133inline GB_ERROR GB_MAIN_TYPE::pop_transaction() {
2134    if (transaction_level==0) return "attempt to pop nested transaction while none running";
2135    if (transaction_level<0)  return 0;  // NO_TRANSACTION_MODE
2136    if (transaction_level==1) return commit_transaction();
2137    transaction_level--;
2138    return NULL;
2139}
2140
2141inline GB_ERROR GB_MAIN_TYPE::no_transaction() {
2142    if (is_client()) return "Tried to disable transactions in a client";
2143    transaction_level = -1;
2144    return NULL;
2145}
2146
2147GB_ERROR GB_MAIN_TYPE::send_update_to_server(GBDATA *gbd) {
2148    GB_ERROR error = NULL;
2149
2150    if (!transaction_level) error = "send_update_to_server: no transaction running";
2151    else if (is_server()) error   = "send_update_to_server: only possible from clients (not from server itself)";
2152    else {
2153        const gb_triggered_callback *chg_cbl_old = changeCBs.pending.get_tail();
2154        const gb_triggered_callback *del_cbl_old = deleteCBs.pending.get_tail();
2155
2156        error             = gbcmc_begin_sendupdate(gb_main());
2157        if (!error) error = gb_commit_transaction_local_rek(gbd, 2, 0);
2158        if (!error) error = gbcmc_end_sendupdate(gb_main());
2159
2160        if (!error &&
2161            (chg_cbl_old != changeCBs.pending.get_tail() ||
2162             del_cbl_old != deleteCBs.pending.get_tail()))
2163        {
2164            error = "send_update_to_server triggered a callback (this is not allowed)";
2165        }
2166    }
2167    return error;
2168}
2169
2170// --------------------------------------
2171//      client transaction interface
2172
2173GB_ERROR GB_push_transaction(GBDATA *gbd) {
2174    /*! start a transaction if no transaction is running.
2175     * (otherwise only trace nested transactions)
2176     *
2177     * recommended transaction usage:
2178     *
2179     * \code
2180     * GB_ERROR myFunc() {
2181     *     GB_ERROR error = GB_push_transaction(gbd);
2182     *     if (!error) {
2183     *         error = ...;
2184     *     }
2185     *     return GB_end_transaction(gbd, error);
2186     * }
2187     *
2188     * void myFunc() {
2189     *     GB_ERROR error = GB_push_transaction(gbd);
2190     *     if (!error) {
2191     *         error = ...;
2192     *     }
2193     *     GB_end_transaction_show_error(gbd, error, aw_message);
2194     * }
2195     * \endcode
2196     *
2197     * @see GB_pop_transaction(), GB_end_transaction(), GB_begin_transaction()
2198     */
2199
2200    return GB_MAIN(gbd)->push_transaction();
2201}
2202
2203GB_ERROR GB_pop_transaction(GBDATA *gbd) {
2204    //! commit a transaction started with GB_push_transaction()
2205    return GB_MAIN(gbd)->pop_transaction();
2206}
2207GB_ERROR GB_begin_transaction(GBDATA *gbd) {
2208    /*! like GB_push_transaction(),
2209     * but fails if there is already an transaction running.
2210     * @see GB_commit_transaction() and GB_abort_transaction()
2211     */
2212    return GB_MAIN(gbd)->begin_transaction();
2213}
2214GB_ERROR GB_no_transaction(GBDATA *gbd) { // goes to header: __ATTR__USERESULT
2215    return GB_MAIN(gbd)->no_transaction();
2216}
2217
2218GB_ERROR GB_abort_transaction(GBDATA *gbd) {
2219    /*! abort a running transaction,
2220     * i.e. forget all changes made to DB inside the current transaction.
2221     *
2222     * May be called instead of GB_pop_transaction() or GB_commit_transaction()
2223     *
2224     * If a nested transactions got aborted,
2225     * committing a surrounding transaction will silently abort it as well.
2226     */
2227    return GB_MAIN(gbd)->abort_transaction();
2228}
2229
2230GB_ERROR GB_commit_transaction(GBDATA *gbd) {
2231    /*! commit a transaction started with GB_begin_transaction()
2232     *
2233     * commit changes made to DB.
2234     *
2235     * in case of nested transactions, this is equal to GB_pop_transaction()
2236     */
2237    return GB_MAIN(gbd)->commit_transaction();
2238}
2239
2240GB_ERROR GB_end_transaction(GBDATA *gbd, GB_ERROR error) {
2241    /*! abort or commit transaction
2242     *
2243     * @ param error
2244     * - if NULL commit transaction
2245     * - else abort transaction
2246     *
2247     * always commits in no-transaction-mode
2248     *
2249     * @return error or transaction error
2250     * @see GB_push_transaction() for example
2251     */
2252
2253    if (GB_get_transaction_level(gbd)<0) {
2254        ASSERT_RESULT(GB_ERROR, NULL, GB_pop_transaction(gbd));
2255    }
2256    else {
2257        if (error) GB_abort_transaction(gbd);
2258        else error = GB_pop_transaction(gbd);
2259    }
2260    return error;
2261}
2262
2263void GB_end_transaction_show_error(GBDATA *gbd, GB_ERROR error, void (*error_handler)(GB_ERROR)) {
2264    //! like GB_end_transaction(), but show error using 'error_handler'
2265    error = GB_end_transaction(gbd, error);
2266    if (error) error_handler(error);
2267}
2268
2269int GB_get_transaction_level(GBDATA *gbd) {
2270    /*! @return transaction level
2271     * <0 -> in no-transaction-mode (abort is impossible)
2272     *  0 -> not in transaction
2273     *  1 -> one single transaction
2274     *  2, ... -> nested transactions
2275     */
2276    return GB_MAIN(gbd)->get_transaction_level();
2277}
2278
2279// ------------------
2280//      callbacks
2281
2282static void dummy_db_cb(GBDATA*, GB_CB_TYPE) { gb_assert(0); } // used as marker for deleted callbacks
2283DatabaseCallback TypedDatabaseCallback::MARKED_DELETED = makeDatabaseCallback(dummy_db_cb);
2284
2285GB_MAIN_TYPE *gb_get_main_during_cb() {
2286    /* if inside a callback, return the DB root of the DB element, the callback was called for.
2287     * if not inside a callback, return NULL.
2288     */
2289    return inside_callback_main;
2290}
2291
2292NOT4PERL bool GB_inside_callback(GBDATA *of_gbd, GB_CB_TYPE cbtype) {
2293    GB_MAIN_TYPE *Main   = gb_get_main_during_cb();
2294    bool          inside = false;
2295
2296    if (Main) {                 // inside a callback
2297        gb_assert(currently_called_back);
2298        if (currently_called_back->gbd == of_gbd) {
2299            GB_CB_TYPE curr_cbtype;
2300            if (Main->has_pending_delete_callback()) { // delete callbacks were not all performed yet
2301                                                       // => current callback is a delete callback
2302                curr_cbtype = GB_CB_TYPE(currently_called_back->spec.get_type() & GB_CB_DELETE);
2303            }
2304            else {
2305                gb_assert(Main->has_pending_change_callback());
2306                curr_cbtype = GB_CB_TYPE(currently_called_back->spec.get_type() & (GB_CB_ALL-GB_CB_DELETE));
2307            }
2308            gb_assert(curr_cbtype != GB_CB_NONE); // wtf!? are we inside callback or not?
2309
2310            if ((cbtype&curr_cbtype) != GB_CB_NONE) {
2311                inside = true;
2312            }
2313        }
2314    }
2315
2316    return inside;
2317}
2318
2319GBDATA *GB_get_gb_main_during_cb() {
2320    GBDATA       *gb_main = NULL;
2321    GB_MAIN_TYPE *Main    = gb_get_main_during_cb();
2322
2323    if (Main) {                 // inside callback
2324        if (!GB_inside_callback(Main->gb_main(), GB_CB_DELETE)) { // main is not deleted
2325            gb_main = Main->gb_main();
2326        }
2327    }
2328    return gb_main;
2329}
2330
2331
2332
2333static GB_CSTR gb_read_pntr_ts(GBDATA *gbd, gb_transaction_save *ts) {
2334    int         type = GB_TYPE_TS(ts);
2335    const char *data = GB_GETDATA_TS(ts);
2336    if (data) {
2337        if (ts->flags.compressed_data) {    // uncompressed data return pntr to database entry
2338            long size = GB_GETSIZE_TS(ts) * gb_convert_type_2_sizeof[type] + gb_convert_type_2_appendix_size[type];
2339            data = gb_uncompress_data(gbd, data, size);
2340        }
2341    }
2342    return data;
2343}
2344
2345// get last array value in callbacks
2346NOT4PERL const void *GB_read_old_value() {
2347    char *data;
2348
2349    if (!currently_called_back) {
2350        GB_export_error("You cannot call GB_read_old_value outside a ARBDB callback");
2351        return NULL;
2352    }
2353    if (!currently_called_back->old) {
2354        GB_export_error("No old value available in GB_read_old_value");
2355        return NULL;
2356    }
2357    data = GB_GETDATA_TS(currently_called_back->old);
2358    if (!data) return NULL;
2359
2360    return gb_read_pntr_ts(currently_called_back->gbd, currently_called_back->old);
2361}
2362// same for size
2363long GB_read_old_size() {
2364    if (!currently_called_back) {
2365        GB_export_error("You cannot call GB_read_old_size outside a ARBDB callback");
2366        return -1;
2367    }
2368    if (!currently_called_back->old) {
2369        GB_export_error("No old value available in GB_read_old_size");
2370        return -1;
2371    }
2372    return GB_GETSIZE_TS(currently_called_back->old);
2373}
2374
2375inline char *cbtype2readable(GB_CB_TYPE type) {
2376    ConstStrArray septype;
2377
2378#define appendcbtype(cbt) do {                  \
2379        if (type&cbt) {                         \
2380            type = GB_CB_TYPE(type-cbt);        \
2381            septype.put(#cbt);                  \
2382        }                                       \
2383    } while(0)
2384
2385    appendcbtype(GB_CB_DELETE);
2386    appendcbtype(GB_CB_CHANGED);
2387    appendcbtype(GB_CB_SON_CREATED);
2388
2389    gb_assert(type == GB_CB_NONE);
2390
2391    return GBT_join_names(septype, '|');
2392}
2393
2394char *TypedDatabaseCallback::get_info() const {
2395    const char *readable_fun    = GBS_funptr2readable((void*)dbcb.callee(), true);
2396    char       *readable_cbtype = cbtype2readable((GB_CB_TYPE)dbcb.inspect_CD2());
2397    char       *result          = GBS_global_string_copy("func='%s' type=%s clientdata=%p",
2398                                                         readable_fun, readable_cbtype, (void*)dbcb.inspect_CD1());
2399
2400    free(readable_cbtype);
2401
2402    return result;
2403}
2404
2405char *GB_get_callback_info(GBDATA *gbd) {
2406    // returns human-readable information about callbacks of 'gbd' or 0
2407    char *result = 0;
2408    if (gbd->ext) {
2409        gb_callback_list *cbl = gbd->get_callbacks();
2410        if (cbl) {
2411            for (gb_callback_list::itertype cb = cbl->callbacks.begin(); cb != cbl->callbacks.end(); ++cb) {
2412                char *cb_info = cb->spec.get_info();
2413                if (result) {
2414                    char *new_result = GBS_global_string_copy("%s\n%s", result, cb_info);
2415                    free(result);
2416                    free(cb_info);
2417                    result = new_result;
2418                }
2419                else {
2420                    result = cb_info;
2421                }
2422            }
2423        }
2424    }
2425
2426    return result;
2427}
2428
2429#if defined(ASSERTION_USED)
2430template<typename CB>
2431bool CallbackList<CB>::contains_unremoved_callback(const CB& like) const {
2432    for (const_itertype cb = callbacks.begin(); cb != callbacks.end(); ++cb) {
2433        if (cb->spec.is_equal_to(like.spec) && !cb->spec.is_marked_for_removal()) {
2434            return true;
2435        }
2436    }
2437    return false;
2438}
2439#endif
2440
2441inline void add_to_callback_chain(gb_callback_list*& head, const TypedDatabaseCallback& cbs) {
2442    if (!head) head = new gb_callback_list;
2443    head->add(gb_callback(cbs));
2444}
2445inline void add_to_callback_chain(gb_hierarchy_callback_list*& head, const TypedDatabaseCallback& cbs, GBDATA *gb_representative) {
2446    if (!head) head = new gb_hierarchy_callback_list;
2447    head->add(gb_hierarchy_callback(cbs, gb_representative));
2448}
2449
2450inline GB_ERROR gb_add_callback(GBDATA *gbd, const TypedDatabaseCallback& cbs) {
2451    /* Adds a callback to a DB entry.
2452     *
2453     * Be careful when writing GB_CB_DELETE callbacks, there is a severe restriction:
2454     *
2455     * - the DB element may already be freed. The pointer is still pointing to the original
2456     *   location, so you can use it to identify the DB element, but you cannot dereference
2457     *   it under all circumstances.
2458     *
2459     * ARBDB internal delete-callbacks may use gb_get_main_during_cb() to access the DB root.
2460     * See also: GB_get_gb_main_during_cb()
2461     */
2462
2463#if defined(DEBUG)
2464    if (GB_inside_callback(gbd, GB_CB_DELETE)) {
2465        printf("Warning: add_priority_callback called inside delete-callback of gbd (gbd may already be freed)\n");
2466#if defined(DEVEL_RALF)
2467        gb_assert(0); // fix callback-handling (never modify callbacks from inside delete callbacks)
2468#endif // DEVEL_RALF
2469    }
2470#endif // DEBUG
2471
2472    GB_test_transaction(gbd); // may return error
2473    gbd->create_extended();
2474    add_to_callback_chain(gbd->ext->callback, cbs);
2475    return 0;
2476}
2477
2478GB_ERROR GB_add_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback& dbcb) {
2479    return gb_add_callback(gbd, TypedDatabaseCallback(dbcb, type));
2480}
2481
2482inline void GB_MAIN_TYPE::callback_group::add_hcb(GBDATA *gb_representative, const TypedDatabaseCallback& dbcb) {
2483    add_to_callback_chain(hierarchy_cbs, dbcb, gb_representative);
2484}
2485
2486GB_ERROR GB_MAIN_TYPE::add_hierarchy_cb(GBDATA *gb_representative, const TypedDatabaseCallback& dbcb) {
2487    GB_CB_TYPE type = dbcb.get_type();
2488    if (type & GB_CB_DELETE) {
2489        deleteCBs.add_hcb(gb_representative, dbcb.with_type_changed_to(GB_CB_DELETE));
2490    }
2491    if (type & GB_CB_ALL_BUT_DELETE) {
2492        changeCBs.add_hcb(gb_representative, dbcb.with_type_changed_to(GB_CB_TYPE(type&GB_CB_ALL_BUT_DELETE)));
2493    }
2494    return NULL;
2495}
2496
2497GB_ERROR GB_add_hierarchy_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback& dbcb) { // @@@ needed to impl #418
2498    /*! bind callback to ALL entries which are at the same DB-hierarchy as 'gbd'.
2499     *
2500     * Hierarchy callbacks are triggered before normal callbacks (added by GB_add_callback or GB_ensure_callback).
2501     * Nevertheless delete callbacks take precedence over change callbacks
2502     * (i.e. a normal delete callback is triggered before a hierarchical change callback).
2503     *
2504     * Hierarchy callbacks are cannot be installed and will NOT be triggered in NO_TRANSACTION_MODE
2505     * (i.e. it will not work in ARBs property DBs)
2506     */
2507    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
2508    gb_assert(Main->get_transaction_level()>=0); // hierarchical callbacks are not supported in NO_TRANSACTION_MODE
2509    return Main->add_hierarchy_cb(gbd, TypedDatabaseCallback(dbcb, type));
2510}
2511
2512template <typename PRED>
2513inline void gb_remove_callbacks_that(GBDATA *gbd, PRED shallRemove) {
2514#if defined(ASSERTION_USED)
2515    if (GB_inside_callback(gbd, GB_CB_DELETE)) {
2516        printf("Warning: gb_remove_callback called inside delete-callback of gbd (gbd may already be freed)\n");
2517        gb_assert(0); // fix callback-handling (never modify callbacks from inside delete callbacks)
2518        return;
2519    }
2520#endif // DEBUG
2521
2522    if (gbd->ext) {
2523        gb_callback_list *cbl = gbd->get_callbacks();
2524        if (cbl) {
2525            bool prev_running = false;
2526
2527            for (gb_callback_list::itertype cb = cbl->callbacks.begin(); cb != cbl->callbacks.end(); ) {
2528                bool this_running = cb->running;
2529
2530                if (shallRemove(*cb)) {
2531                    if (prev_running || this_running) {
2532                        cb->spec.mark_for_removal();
2533                        ++cb;
2534                    }
2535                    else {
2536                        cb = cbl->callbacks.erase(cb);
2537                    }
2538                }
2539                else {
2540                    ++cb;
2541                }
2542                prev_running = this_running;
2543            }
2544        }
2545    }
2546}
2547
2548struct ShallBeDeleted {
2549    bool operator()(const gb_callback& cb) const { return cb.spec.is_marked_for_removal(); }
2550};
2551static void gb_remove_callbacks_marked_for_deletion(GBDATA *gbd) {
2552    gb_remove_callbacks_that(gbd, ShallBeDeleted());
2553}
2554
2555struct IsCallback : private TypedDatabaseCallback {
2556    IsCallback(GB_CB func_, GB_CB_TYPE type_) : TypedDatabaseCallback(makeDatabaseCallback((GB_CB)func_, (int*)NULL), type_) {}
2557    bool operator()(const gb_callback& cb) const { return sig_is_equal_to(cb.spec); }
2558};
2559struct IsSpecificCallback : private TypedDatabaseCallback {
2560    IsSpecificCallback(const TypedDatabaseCallback& cb) : TypedDatabaseCallback(cb) {}
2561    bool operator()(const gb_callback& cb) const { return is_equal_to(cb.spec); }
2562};
2563
2564void GB_remove_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback& dbcb) {
2565    // remove specific callback; 'type' and 'dbcb' have to match
2566    gb_remove_callbacks_that(gbd, IsSpecificCallback(TypedDatabaseCallback(dbcb, type)));
2567}
2568void GB_remove_all_callbacks_to(GBDATA *gbd, GB_CB_TYPE type, GB_CB func) {
2569    // removes all callbacks 'func' bound to 'gbd' with 'type'
2570    gb_remove_callbacks_that(gbd, IsCallback(func, type));
2571}
2572
2573GB_ERROR GB_ensure_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback& dbcb) {
2574    TypedDatabaseCallback newcb(dbcb, type);
2575    gb_callback_list *cbl = gbd->get_callbacks();
2576    if (cbl) {
2577        for (gb_callback_list::itertype cb = cbl->callbacks.begin(); cb != cbl->callbacks.end(); ++cb) {
2578            if (cb->spec.is_equal_to(newcb) && !cb->spec.is_marked_for_removal()) {
2579                return NULL; // already in cb list
2580            }
2581        }
2582    }
2583    return gb_add_callback(gbd, newcb);
2584}
2585
2586int GB_nsons(GBDATA *gbd) {
2587    /*! return number of child entries
2588     *
2589     * @@@ does this work in clients ?
2590     */
2591
2592    return gbd->is_container()
2593        ? gbd->as_container()->d.size
2594        : 0;
2595}
2596
2597void GB_disable_quicksave(GBDATA *gbd, const char *reason) {
2598    /*! Disable quicksaving database
2599     * @param gbd any DB node
2600     * @param reason why quicksaving is not allowed
2601     */
2602    freedup(GB_MAIN(gbd)->qs.quick_save_disabled, reason);
2603}
2604
2605GB_ERROR GB_resort_data_base(GBDATA *gb_main, GBDATA **new_order_list, long listsize) {
2606    {
2607        long client_count = GB_read_clients(gb_main);
2608        if (client_count<0)
2609            return "Sorry: this program is not the arbdb server, you cannot resort your data";
2610
2611        if (client_count>0)
2612            return GBS_global_string("There are %li clients (editors, tree programs) connected to this server.\n"
2613                                     "You need to these close clients before you can run this operation.",
2614                                     client_count);
2615    }
2616
2617    if (listsize <= 0) return 0;
2618
2619    GBCONTAINER *father = GB_FATHER(new_order_list[0]);
2620    GB_disable_quicksave(gb_main, "some entries in the database got a new order");
2621
2622    gb_header_list *hl = GB_DATA_LIST_HEADER(father->d);
2623    for (long new_index = 0; new_index< listsize; new_index++) {
2624        long old_index = new_order_list[new_index]->index;
2625
2626        if (old_index < new_index) {
2627            GB_warningf("Warning at resort database: entry exists twice: %li and %li",
2628                        old_index, new_index);
2629        }
2630        else {
2631            GBDATA *ogb = GB_HEADER_LIST_GBD(hl[old_index]);
2632            GBDATA *ngb = GB_HEADER_LIST_GBD(hl[new_index]);
2633
2634            gb_header_list h = hl[new_index];
2635            hl[new_index] = hl[old_index];
2636            hl[old_index] = h;              // Warning: Relative Pointers are incorrect !!!
2637
2638            SET_GB_HEADER_LIST_GBD(hl[old_index], ngb);
2639            SET_GB_HEADER_LIST_GBD(hl[new_index], ogb);
2640
2641            if (ngb) ngb->index = old_index;
2642            if (ogb) ogb->index = new_index;
2643        }
2644    }
2645
2646    gb_touch_entry(father, GB_NORMAL_CHANGE);
2647    return 0;
2648}
2649
2650GB_ERROR gb_resort_system_folder_to_top(GBCONTAINER *gb_main) {
2651    GBDATA *gb_system = GB_entry(gb_main, GB_SYSTEM_FOLDER);
2652    GBDATA *gb_first = GB_child(gb_main);
2653    GBDATA **new_order_list;
2654    GB_ERROR error = 0;
2655    int i, len;
2656    if (GB_read_clients(gb_main)<0) return 0; // we are not server
2657    if (!gb_system) {
2658        return GB_export_error("System databaseentry does not exist");
2659    }
2660    if (gb_first == gb_system) return 0;
2661    len = GB_number_of_subentries(gb_main);
2662    new_order_list = (GBDATA **)GB_calloc(sizeof(GBDATA *), len);
2663    new_order_list[0] = gb_system;
2664    for (i=1; i<len; i++) {
2665        new_order_list[i] = gb_first;
2666        do {
2667            gb_first = GB_nextChild(gb_first);
2668        } while (gb_first == gb_system);
2669    }
2670    error = GB_resort_data_base(gb_main, new_order_list, len);
2671    free(new_order_list);
2672    return error;
2673}
2674
2675// ------------------------------
2676//      private(?) user flags
2677
2678STATIC_ASSERT_ANNOTATED(((GB_USERFLAG_ANY+1)&GB_USERFLAG_ANY) == 0, "not all bits set in GB_USERFLAG_ANY");
2679
2680#if defined(ASSERTION_USED)
2681inline bool legal_user_bitmask(unsigned char bitmask) {
2682    return bitmask>0 && bitmask<=GB_USERFLAG_ANY;
2683}
2684#endif
2685
2686inline gb_flag_types2& get_user_flags(GBDATA *gbd) {
2687    return gbd->expect_container()->flags2;
2688}
2689
2690bool GB_user_flag(GBDATA *gbd, unsigned char user_bit) {
2691    gb_assert(legal_user_bitmask(user_bit));
2692    return get_user_flags(gbd).user_bits & user_bit;
2693}
2694void GB_set_user_flag(GBDATA *gbd, unsigned char user_bit) {
2695    gb_assert(legal_user_bitmask(user_bit));
2696    gb_flag_types2& flags  = get_user_flags(gbd);
2697    flags.user_bits       |= user_bit;
2698}
2699
2700void GB_clear_user_flag(GBDATA *gbd, unsigned char user_bit) {
2701    gb_assert(legal_user_bitmask(user_bit));
2702    gb_flag_types2& flags  = get_user_flags(gbd);
2703    flags.user_bits       &= (user_bit^GB_USERFLAG_ANY);
2704}
2705
2706// ------------------------
2707//      mark DB entries
2708
2709void GB_write_flag(GBDATA *gbd, long flag) {
2710    GBCONTAINER  *gbc  = gbd->expect_container();
2711    GB_MAIN_TYPE *Main = GB_MAIN(gbc);
2712
2713    GB_test_transaction(Main);
2714
2715    int ubit = Main->users[0]->userbit;
2716    int prev = GB_ARRAY_FLAGS(gbc).flags;
2717    gbc->flags.saved_flags = prev;
2718
2719    if (flag) {
2720        GB_ARRAY_FLAGS(gbc).flags |= ubit;
2721    }
2722    else {
2723        GB_ARRAY_FLAGS(gbc).flags &= ~ubit;
2724    }
2725    if (prev != (int)GB_ARRAY_FLAGS(gbc).flags) {
2726        gb_touch_entry(gbc, GB_NORMAL_CHANGE);
2727        gb_touch_header(GB_FATHER(gbc));
2728        GB_DO_CALLBACKS(gbc);
2729    }
2730}
2731
2732int GB_read_flag(GBDATA *gbd) {
2733    GB_test_transaction(gbd);
2734    if (GB_ARRAY_FLAGS(gbd).flags & GB_MAIN(gbd)->users[0]->userbit) return 1;
2735    else return 0;
2736}
2737
2738void GB_touch(GBDATA *gbd) {
2739    GB_test_transaction(gbd);
2740    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
2741    GB_DO_CALLBACKS(gbd);
2742}
2743
2744
2745char GB_type_2_char(GB_TYPES type) {
2746    const char *type2char = "-bcif-B-CIFlSS-%";
2747    return type2char[type];
2748}
2749
2750GB_ERROR GB_print_debug_information(void */*dummy_AW_root*/, GBDATA *gb_main) {
2751    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
2752    GB_push_transaction(gb_main);
2753    for (int i=0; i<Main->keycnt; i++) {
2754        if (Main->keys[i].key) {
2755            printf("%3i %20s    nref %i\n", i, Main->keys[i].key, (int)Main->keys[i].nref);
2756        }
2757        else {
2758            printf("    %3i unused key, next free key = %li\n", i, Main->keys[i].next_free_key);
2759        }
2760    }
2761    gbm_debug_mem();
2762    GB_pop_transaction(gb_main);
2763    return 0;
2764}
2765
2766static int GB_info_deep = 15;
2767
2768
2769static int gb_info(GBDATA *gbd, int deep) {
2770    if (gbd==NULL) { printf("NULL\n"); return -1; }
2771    GB_push_transaction(gbd);
2772
2773    GB_TYPES type = gbd->type();
2774
2775    if (deep) {
2776        printf("    ");
2777    }
2778
2779    printf("(GBDATA*)0x%lx (GBCONTAINER*)0x%lx ", (long)gbd, (long)gbd);
2780
2781    if (gbd->rel_father==0) { printf("father=NULL\n"); return -1; }
2782
2783    GBCONTAINER  *gbc;
2784    GB_MAIN_TYPE *Main;
2785    if (type==GB_DB) { gbc = gbd->as_container(); Main = GBCONTAINER_MAIN(gbc); }
2786    else             { gbc = NULL;                Main = GB_MAIN(gbd); }
2787
2788    if (!Main) { printf("Oops - I have no main entry!!!\n"); return -1; }
2789    if (gbd==Main->dummy_father) { printf("dummy_father!\n"); return -1; }
2790
2791    printf("%10s Type '%c'  ", GB_read_key_pntr(gbd), GB_type_2_char(type));
2792
2793    switch (type) {
2794        case GB_DB: {
2795            int size = gbc->d.size;
2796            printf("Size %i nheader %i hmemsize %i", gbc->d.size, gbc->d.nheader, gbc->d.headermemsize);
2797            printf(" father=(GBDATA*)0x%lx\n", (long)GB_FATHER(gbd));
2798            if (size < GB_info_deep) {
2799                int             index;
2800                gb_header_list *header;
2801
2802                header = GB_DATA_LIST_HEADER(gbc->d);
2803                for (index = 0; index < gbc->d.nheader; index++) {
2804                    GBDATA *gb_sub = GB_HEADER_LIST_GBD(header[index]);
2805                    printf("\t\t%10s (GBDATA*)0x%lx (GBCONTAINER*)0x%lx\n", Main->keys[header[index].flags.key_quark].key, (long)gb_sub, (long)gb_sub);
2806                }
2807            }
2808            break;
2809        }
2810        default: {
2811            char *data = GB_read_as_string(gbd);
2812            if (data) { printf("%s", data); free(data); }
2813            printf(" father=(GBDATA*)0x%lx\n", (long)GB_FATHER(gbd));
2814        }
2815    }
2816
2817
2818    GB_pop_transaction(gbd);
2819
2820    return 0;
2821}
2822
2823int GB_info(GBDATA *gbd) { // unused - intended to be used in debugger
2824    return gb_info(gbd, 0);
2825}
2826
2827long GB_number_of_subentries(GBDATA *gbd) {
2828    GBCONTAINER    *gbc        = gbd->expect_container();
2829    gb_header_list *header     = GB_DATA_LIST_HEADER(gbc->d);
2830
2831    long subentries = 0;
2832    int  end        = gbc->d.nheader;
2833
2834    for (int index = 0; index<end; index++) {
2835        if (header[index].flags.changed < GB_DELETED) subentries++;
2836    }
2837    return subentries;
2838}
2839
2840// --------------------------------------------------------------------------------
2841
2842#ifdef UNIT_TESTS
2843
2844#include <test_unit.h>
2845#include <locale.h>
2846
2847void TEST_GB_atof() {
2848    // startup of ARB (gtk_only@11651) is failing on ubuntu 13.10 (in GBT_read_tree)
2849    // (failed with "Error: 'GB_safe_atof("0.0810811", ..) returns error: cannot convert '0.0810811' to double'")
2850    // Reason: LANG[UAGE] or LC_NUMERIC set to "de_DE..."
2851    //
2852    // Notes:
2853    // * gtk apparently calls 'setlocale(LC_ALL, "");', motif doesnt
2854
2855    TEST_EXPECT_SIMILAR(GB_atof("0.031"), 0.031, 0.0001); // @@@ make this fail, then fix it
2856}
2857
2858void TEST_999_strtod_replacement() {
2859    // caution: if it fails -> locale is not reset (therefore call with low priority 999)
2860    const char *old = setlocale(LC_NUMERIC, "de_DE.UTF-8");
2861    {
2862        // TEST_EXPECT_SIMILAR__BROKEN(strtod("0.031", NULL), 0.031, 0.0001);
2863        TEST_EXPECT_SIMILAR(g_ascii_strtod("0.031", NULL), 0.031, 0.0001);
2864    }
2865    setlocale(LC_NUMERIC, old);
2866}
2867
2868static void test_another_shell() { delete new GB_shell; }
2869static void test_opendb() { GB_close(GB_open("no.arb", "c")); }
2870
2871void TEST_GB_shell() {
2872    {
2873        GB_shell *shell = new GB_shell;
2874        TEST_EXPECT_SEGFAULT(test_another_shell);
2875        test_opendb(); // no SEGV here
2876        delete shell;
2877    }
2878
2879    TEST_EXPECT_SEGFAULT(test_opendb); // should be impossible to open db w/o shell
2880}
2881
2882void TEST_GB_number_of_subentries() {
2883    GB_shell  shell;
2884    GBDATA   *gb_main = GB_open("no.arb", "c");
2885
2886    {
2887        GB_transaction ta(gb_main);
2888
2889        GBDATA   *gb_cont = GB_create_container(gb_main, "container");
2890        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 0);
2891
2892        TEST_EXPECT_RESULT__NOERROREXPORTED(GB_create(gb_cont, "entry", GB_STRING));
2893        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 1);
2894
2895        {
2896            GBDATA *gb_entry;
2897            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_entry = GB_create(gb_cont, "entry", GB_STRING));
2898            TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 2);
2899
2900            TEST_EXPECT_NO_ERROR(GB_delete(gb_entry));
2901            TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 1);
2902        }
2903
2904        TEST_EXPECT_RESULT__NOERROREXPORTED(GB_create(gb_cont, "entry", GB_STRING));
2905        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 2);
2906    }
2907
2908    // TEST_REJECT(true); // @@@ fail while GB_shell open
2909
2910    GB_close(gb_main);
2911}
2912
2913static void test_count_cb(GBDATA *, int *counter) {
2914    fprintf(stderr, "test_count_cb: var.add=%p old.val=%i ", counter, *counter);
2915    (*counter)++;
2916    fprintf(stderr, "new.val=%i\n", *counter);
2917    fflush(stderr);
2918}
2919
2920static void remove_self_cb(GBDATA *gbe, GB_CB_TYPE cbtype) {
2921    GB_remove_callback(gbe, cbtype, makeDatabaseCallback(remove_self_cb));
2922}
2923
2924static void re_add_self_cb(GBDATA *gbe, int *calledCounter, GB_CB_TYPE cbtype) {
2925    ++(*calledCounter);
2926
2927    DatabaseCallback dbcb = makeDatabaseCallback(re_add_self_cb, calledCounter);
2928    GB_remove_callback(gbe, cbtype, dbcb);
2929
2930    GB_ERROR error = GB_add_callback(gbe, cbtype, dbcb);
2931    gb_assert(!error);
2932}
2933
2934void TEST_db_callbacks_ta_nota() {
2935    GB_shell shell;
2936
2937    enum TAmode {
2938        NO_TA   = 1, // no transaction mode
2939        WITH_TA = 2, // transaction mode
2940
2941        BOTH_TA_MODES = (NO_TA|WITH_TA)
2942    };
2943
2944    for (TAmode ta_mode = NO_TA; ta_mode <= WITH_TA; ta_mode = TAmode(ta_mode+1)) {
2945        GBDATA   *gb_main = GB_open("no.arb", "c");
2946        GB_ERROR  error;
2947
2948        TEST_ANNOTATE(ta_mode == NO_TA ? "NO_TA" : "WITH_TA");
2949        if (ta_mode == NO_TA) {
2950            error = GB_no_transaction(gb_main); TEST_EXPECT_NO_ERROR(error);
2951        }
2952
2953        // create some DB entries
2954        GBDATA *gbc;
2955        GBDATA *gbe1;
2956        GBDATA *gbe2;
2957        GBDATA *gbe3;
2958        {
2959            GB_transaction ta(gb_main);
2960
2961            gbc  = GB_create_container(gb_main, "cont");
2962            gbe1 = GB_create(gbc, "entry", GB_STRING);
2963            gbe2 = GB_create(gb_main, "entry", GB_INT);
2964        }
2965
2966        // counters to detect called callbacks
2967        int e1_changed    = 0;
2968        int e2_changed    = 0;
2969        int c_changed     = 0;
2970        int c_son_created = 0;
2971
2972        int e1_deleted = 0;
2973        int e2_deleted = 0;
2974        int e3_deleted = 0;
2975        int c_deleted  = 0;
2976
2977#define CHCB_COUNTERS_EXPECTATION(e1c,e2c,cc,csc)       \
2978        that(e1_changed).is_equal_to(e1c),              \
2979            that(e2_changed).is_equal_to(e2c),          \
2980            that(c_changed).is_equal_to(cc),            \
2981            that(c_son_created).is_equal_to(csc)
2982
2983#define DLCB_COUNTERS_EXPECTATION(e1d,e2d,e3d,cd)       \
2984        that(e1_deleted).is_equal_to(e1d),              \
2985            that(e2_deleted).is_equal_to(e2d),          \
2986            that(e3_deleted).is_equal_to(e3d),          \
2987            that(c_deleted).is_equal_to(cd)
2988
2989#define TEST_EXPECT_CHCB_COUNTERS(e1c,e2c,cc,csc,tam) do{ if (ta_mode & (tam)) TEST_EXPECTATION(all().of(CHCB_COUNTERS_EXPECTATION(e1c,e2c,cc,csc))); }while(0)
2990#define TEST_EXPECT_CHCB___WANTED(e1c,e2c,cc,csc,tam) do{ if (ta_mode & (tam)) TEST_EXPECTATION__WANTED(all().of(CHCB_COUNTERS_EXPECTATION(e1c,e2c,cc,csc))); }while(0)
2991
2992#define TEST_EXPECT_DLCB_COUNTERS(e1d,e2d,e3d,cd,tam) do{ if (ta_mode & (tam)) TEST_EXPECTATION(all().of(DLCB_COUNTERS_EXPECTATION(e1d,e2d,e3d,cd))); }while(0)
2993#define TEST_EXPECT_DLCB___WANTED(e1d,e2d,e3d,cd,tam) do{ if (ta_mode & (tam)) TEST_EXPECTATION__WANTED(all().of(DLCB_COUNTERS_EXPECTATION(e1d,e2d,e3d,cd))); }while(0)
2994
2995#define TEST_EXPECT_COUNTER(tam,cnt,expected)             do{ if (ta_mode & (tam)) TEST_EXPECT_EQUAL(cnt, expected); }while(0)
2996#define TEST_EXPECT_COUNTER__BROKEN(tam,cnt,expected,got) do{ if (ta_mode & (tam)) TEST_EXPECT_EQUAL__BROKEN(cnt, expected, got); }while(0)
2997
2998#define RESET_CHCB_COUNTERS()   do{ e1_changed = e2_changed = c_changed = c_son_created = 0; }while(0)
2999#define RESET_DLCB_COUNTERS()   do{ e1_deleted = e2_deleted = e3_deleted = c_deleted = 0; }while(0)
3000#define RESET_ALL_CB_COUNTERS() do{ RESET_CHCB_COUNTERS(); RESET_DLCB_COUNTERS(); }while(0)
3001
3002        // install some DB callbacks
3003        {
3004            GB_transaction ta(gb_main);
3005            GB_add_callback(gbe1, GB_CB_CHANGED,     makeDatabaseCallback(test_count_cb, &e1_changed));
3006            GB_add_callback(gbe2, GB_CB_CHANGED,     makeDatabaseCallback(test_count_cb, &e2_changed));
3007            GB_add_callback(gbc,  GB_CB_CHANGED,     makeDatabaseCallback(test_count_cb, &c_changed));
3008            GB_add_callback(gbc,  GB_CB_SON_CREATED, makeDatabaseCallback(test_count_cb, &c_son_created));
3009        }
3010
3011        // check callbacks were not called yet
3012        TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, BOTH_TA_MODES);
3013
3014        // trigger callbacks
3015        {
3016            GB_transaction ta(gb_main);
3017
3018            error = GB_write_string(gbe1, "hi"); TEST_EXPECT_NO_ERROR(error);
3019            error = GB_write_int(gbe2, 666);     TEST_EXPECT_NO_ERROR(error);
3020
3021            TEST_EXPECT_CHCB_COUNTERS(1, 1, 1, 0, NO_TA);   // callbacks triggered instantly in NO_TA mode
3022            TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, WITH_TA); // callbacks delayed until transaction is committed
3023
3024        } // [Note: callbacks happen here in ta_mode]
3025
3026        // test that GB_CB_SON_CREATED is not triggered here:
3027        TEST_EXPECT_CHCB_COUNTERS(1, 1, 1, 0, NO_TA);
3028        TEST_EXPECT_CHCB_COUNTERS(1, 1, 1, 0, WITH_TA);
3029
3030        // really create a son
3031        RESET_CHCB_COUNTERS();
3032        {
3033            GB_transaction ta(gb_main);
3034            gbe3 = GB_create(gbc, "e3", GB_STRING);
3035        }
3036        TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, NO_TA); // broken
3037        TEST_EXPECT_CHCB___WANTED(0, 0, 1, 1, NO_TA);
3038        TEST_EXPECT_CHCB_COUNTERS(0, 0, 1, 1, WITH_TA);
3039
3040        // change that son
3041        RESET_CHCB_COUNTERS();
3042        {
3043            GB_transaction ta(gb_main);
3044            error = GB_write_string(gbe3, "bla"); TEST_EXPECT_NO_ERROR(error);
3045        }
3046        TEST_EXPECT_CHCB_COUNTERS(0, 0, 1, 0, BOTH_TA_MODES);
3047
3048
3049        // test delete callbacks
3050        RESET_CHCB_COUNTERS();
3051        {
3052            GB_transaction ta(gb_main);
3053
3054            GB_add_callback(gbe1, GB_CB_DELETE, makeDatabaseCallback(test_count_cb, &e1_deleted));
3055            GB_add_callback(gbe2, GB_CB_DELETE, makeDatabaseCallback(test_count_cb, &e2_deleted));
3056            GB_add_callback(gbe3, GB_CB_DELETE, makeDatabaseCallback(test_count_cb, &e3_deleted));
3057            GB_add_callback(gbc,  GB_CB_DELETE, makeDatabaseCallback(test_count_cb, &c_deleted));
3058        }
3059        TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, BOTH_TA_MODES); // adding callbacks does not trigger existing change-callbacks
3060        {
3061            GB_transaction ta(gb_main);
3062
3063            error = GB_delete(gbe3); TEST_EXPECT_NO_ERROR(error);
3064            error = GB_delete(gbe2); TEST_EXPECT_NO_ERROR(error);
3065
3066            TEST_EXPECT_DLCB_COUNTERS(0, 1, 1, 0, NO_TA);
3067            TEST_EXPECT_DLCB_COUNTERS(0, 0, 0, 0, WITH_TA);
3068        }
3069
3070        TEST_EXPECT_CHCB_COUNTERS(0, 0, 1, 0, WITH_TA); // container changed by deleting a son (gbe3); no longer triggers via GB_SON_CHANGED
3071        TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, NO_TA);   // change is not triggered in NO_TA mode (error?)
3072        TEST_EXPECT_CHCB___WANTED(0, 0, 1, 1, NO_TA);
3073
3074        TEST_EXPECT_DLCB_COUNTERS(0, 1, 1, 0, BOTH_TA_MODES);
3075
3076        RESET_ALL_CB_COUNTERS();
3077        {
3078            GB_transaction ta(gb_main);
3079            error = GB_delete(gbc);  TEST_EXPECT_NO_ERROR(error); // delete the container containing gbe1 and gbe3 (gbe3 alreay deleted)
3080        }
3081        TEST_EXPECT_CHCB_COUNTERS(0, 0, 0, 0, BOTH_TA_MODES); // deleting the container does not trigger any change callbacks
3082        TEST_EXPECT_DLCB_COUNTERS(1, 0, 0, 1, BOTH_TA_MODES); // deleting the container does also trigger the delete callback for gbe1
3083
3084        // --------------------------------------------------------------------------------
3085        // document that a callback now can be removed while it is running
3086        // (in NO_TA mode; always worked in WITH_TA mode)
3087        {
3088            GBDATA *gbe;
3089            {
3090                GB_transaction ta(gb_main);
3091                gbe = GB_create(gb_main, "new_e1", GB_INT); // recreate
3092                GB_add_callback(gbe, GB_CB_CHANGED, makeDatabaseCallback(remove_self_cb));
3093            }
3094            { GB_transaction ta(gb_main); GB_touch(gbe); }
3095        }
3096
3097        // test that a callback may remove and re-add itself
3098        {
3099            GBDATA *gbn1;
3100            GBDATA *gbn2;
3101
3102            int counter1 = 0;
3103            int counter2 = 0;
3104
3105            {
3106                GB_transaction ta(gb_main);
3107                gbn1 = GB_create(gb_main, "new_e1", GB_INT);
3108                gbn2 = GB_create(gb_main, "new_e2", GB_INT);
3109                GB_add_callback(gbn1, GB_CB_CHANGED, makeDatabaseCallback(re_add_self_cb, &counter1));
3110            }
3111
3112            TEST_EXPECT_COUNTER(NO_TA,         counter1, 0); // no callback triggered (trigger happens BEFORE call to GB_add_callback in NO_TA mode!)
3113            TEST_EXPECT_COUNTER(WITH_TA,       counter1, 1); // callback gets triggered by GB_create
3114            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter2, 0);
3115
3116            counter1 = 0; counter2 = 0;
3117
3118            // test no callback is triggered by just adding a callback
3119            {
3120                GB_transaction ta(gb_main);
3121                GB_add_callback(gbn2, GB_CB_CHANGED, makeDatabaseCallback(re_add_self_cb, &counter2));
3122            }
3123            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter1, 0);
3124            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter2, 0);
3125
3126            { GB_transaction ta(gb_main); GB_touch(gbn1); }
3127            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter1, 1);
3128            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter2, 0);
3129
3130            { GB_transaction ta(gb_main); GB_touch(gbn2); }
3131            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter1, 1);
3132            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter2, 1);
3133
3134            { GB_transaction ta(gb_main);  }
3135            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter1, 1);
3136            TEST_EXPECT_COUNTER(BOTH_TA_MODES, counter2, 1);
3137        }
3138
3139        GB_close(gb_main);
3140    }
3141}
3142
3143// -----------------------
3144//      test callbacks
3145
3146struct calledWith {
3147    GBDATA     *gbd;
3148    GB_CB_TYPE  type;
3149    int         time_called;
3150
3151    static int timer;
3152
3153    calledWith(GBDATA *gbd_, GB_CB_TYPE type_) : gbd(gbd_), type(type_), time_called(++timer) {}
3154    calledWith(const calledWith& other) : gbd(other.gbd), type(other.type), time_called(other.time_called) {}
3155    DECLARE_ASSIGNMENT_OPERATOR(calledWith);
3156};
3157
3158int calledWith::timer = 0;
3159
3160class callback_trace {
3161    typedef std::list<calledWith> calledList;
3162    typedef calledList::iterator  calledIter;
3163
3164    calledList called;
3165
3166    calledIter find(GBDATA *gbd) {
3167        calledIter c = called.begin();
3168        while (c != called.end()) {
3169            if (c->gbd == gbd) break;
3170            ++c;
3171        }
3172        return c;
3173    }
3174    calledIter find(GB_CB_TYPE exp_type) {
3175        calledIter c = called.begin();
3176        while (c != called.end()) {
3177            if (c->type&exp_type) break;
3178            ++c;
3179        }
3180        return c;
3181    }
3182    calledIter find(GBDATA *gbd, GB_CB_TYPE exp_type) {
3183        calledIter c = called.begin();
3184        while (c != called.end()) {
3185            if (c->gbd == gbd && (c->type&exp_type)) break;
3186            ++c;
3187        }
3188        return c;
3189    }
3190
3191    bool removed(calledIter c) {
3192        if (c == called.end()) return false;
3193        called.erase(c);
3194        return true;
3195    }
3196
3197public:
3198    callback_trace() { called.clear(); }
3199
3200    void set_called_by(GBDATA *gbd, GB_CB_TYPE type) { called.push_back(calledWith(gbd, type)); }
3201
3202    bool was_called_by(GBDATA *gbd) { return removed(find(gbd)); }
3203    bool was_called_by(GB_CB_TYPE exp_type) { return removed(find(exp_type)); }
3204    bool was_called_by(GBDATA *gbd, GB_CB_TYPE exp_type) { return removed(find(gbd, exp_type)); }
3205
3206    int call_time(GBDATA *gbd, GB_CB_TYPE exp_type) {
3207        calledIter found = find(gbd, exp_type);
3208        if (found == called.end()) return -1;
3209
3210        int t = found->time_called;
3211        removed(found);
3212        return t;
3213    }
3214
3215    bool was_not_called() const { return called.empty(); }
3216    bool was_called() const { return !was_not_called(); }
3217};
3218
3219static void some_cb(GBDATA *gbd, callback_trace *trace, GB_CB_TYPE cbtype) {
3220    trace->set_called_by(gbd, cbtype);
3221}
3222
3223#define TRACESTRUCT(ELEM,FLAVOR)           trace_##ELEM##_##FLAVOR
3224#define HIERARCHY_TRACESTRUCT(ELEM,FLAVOR) traceHier_##ELEM##_##FLAVOR
3225
3226#define ADD_CHANGED_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_callback(elem, GB_CB_CHANGED,     makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,changed))))
3227#define ADD_DELETED_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_callback(elem, GB_CB_DELETE,      makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,deleted))))
3228#define ADD_NWCHILD_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_callback(elem, GB_CB_SON_CREATED, makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,newchild))))
3229
3230#define ADD_CHANGED_HIERARCHY_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_hierarchy_callback(elem, GB_CB_CHANGED,     makeDatabaseCallback(some_cb, &HIERARCHY_TRACESTRUCT(elem,changed))))
3231#define ADD_DELETED_HIERARCHY_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_hierarchy_callback(elem, GB_CB_DELETE,      makeDatabaseCallback(some_cb, &HIERARCHY_TRACESTRUCT(elem,deleted))))
3232#define ADD_NWCHILD_HIERARCHY_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_add_hierarchy_callback(elem, GB_CB_SON_CREATED, makeDatabaseCallback(some_cb, &HIERARCHY_TRACESTRUCT(elem,newchild))))
3233
3234#define ENSURE_CHANGED_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_ensure_callback(elem, GB_CB_CHANGED,     makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,changed))))
3235#define ENSURE_DELETED_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_ensure_callback(elem, GB_CB_DELETE,      makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,deleted))))
3236#define ENSURE_NWCHILD_CALLBACK(elem) TEST_EXPECT_NO_ERROR(GB_ensure_callback(elem, GB_CB_SON_CREATED, makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,newchild))))
3237
3238#define REMOVE_CHANGED_CALLBACK(elem) GB_remove_callback(elem, GB_CB_CHANGED,     makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,changed)))
3239#define REMOVE_DELETED_CALLBACK(elem) GB_remove_callback(elem, GB_CB_DELETE,      makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,deleted)))
3240#define REMOVE_NWCHILD_CALLBACK(elem) GB_remove_callback(elem, GB_CB_SON_CREATED, makeDatabaseCallback(some_cb, &TRACESTRUCT(elem,newchild)))
3241
3242#define INIT_CHANGED_CALLBACK(elem) callback_trace TRACESTRUCT(elem,changed);  ADD_CHANGED_CALLBACK(elem)
3243#define INIT_DELETED_CALLBACK(elem) callback_trace TRACESTRUCT(elem,deleted);  ADD_DELETED_CALLBACK(elem)
3244#define INIT_NWCHILD_CALLBACK(elem) callback_trace TRACESTRUCT(elem,newchild); ADD_NWCHILD_CALLBACK(elem)
3245
3246#define INIT_CHANGED_HIERARCHY_CALLBACK(elem) callback_trace HIERARCHY_TRACESTRUCT(elem,changed);  ADD_CHANGED_HIERARCHY_CALLBACK(elem)
3247#define INIT_DELETED_HIERARCHY_CALLBACK(elem) callback_trace HIERARCHY_TRACESTRUCT(elem,deleted);  ADD_DELETED_HIERARCHY_CALLBACK(elem)
3248#define INIT_NWCHILD_HIERARCHY_CALLBACK(elem) callback_trace HIERARCHY_TRACESTRUCT(elem,newchild); ADD_NWCHILD_HIERARCHY_CALLBACK(elem)
3249
3250#define ENSURE_ENTRY_CALLBACKS(entry)    ENSURE_CHANGED_CALLBACK(entry); ENSURE_DELETED_CALLBACK(entry)
3251#define ENSURE_CONTAINER_CALLBACKS(cont) ENSURE_CHANGED_CALLBACK(cont);  ENSURE_NWCHILD_CALLBACK(cont); ENSURE_DELETED_CALLBACK(cont)
3252
3253#define REMOVE_ENTRY_CALLBACKS(entry)    REMOVE_CHANGED_CALLBACK(entry); REMOVE_DELETED_CALLBACK(entry)
3254#define REMOVE_CONTAINER_CALLBACKS(cont) REMOVE_CHANGED_CALLBACK(cont);  REMOVE_NWCHILD_CALLBACK(cont); REMOVE_DELETED_CALLBACK(cont)
3255
3256#define INIT_ENTRY_CALLBACKS(entry)    INIT_CHANGED_CALLBACK(entry); INIT_DELETED_CALLBACK(entry)
3257#define INIT_CONTAINER_CALLBACKS(cont) INIT_CHANGED_CALLBACK(cont);  INIT_NWCHILD_CALLBACK(cont); INIT_DELETED_CALLBACK(cont)
3258
3259#define TRIGGER_CHANGE(gbd) do {                \
3260        GB_initial_transaction ta(gb_main);     \
3261        if (ta.ok()) GB_touch(gbd);             \
3262        TEST_EXPECT_NO_ERROR(ta.close(NULL));   \
3263    } while(0)
3264
3265#define TRIGGER_2_CHANGES(gbd1, gbd2) do {      \
3266        GB_initial_transaction ta(gb_main);     \
3267        if (ta.ok()) {                          \
3268            GB_touch(gbd1);                     \
3269            GB_touch(gbd2);                     \
3270        }                                       \
3271        TEST_EXPECT_NO_ERROR(ta.close(NULL));   \
3272    } while(0)
3273
3274#define TRIGGER_DELETE(gbd) do {                \
3275        GB_initial_transaction ta(gb_main);     \
3276        GB_ERROR error = NULL;                  \
3277        if (ta.ok()) error = GB_delete(gbd);    \
3278        TEST_EXPECT_NO_ERROR(ta.close(error));  \
3279    } while(0)
3280
3281#define TEST_EXPECT_NO_CALLBACK_TRIGGERED()                     \
3282    TEST_EXPECT(trace_top_deleted.was_not_called());            \
3283    TEST_EXPECT(trace_son_deleted.was_not_called());            \
3284    TEST_EXPECT(trace_grandson_deleted.was_not_called());       \
3285    TEST_EXPECT(trace_ograndson_deleted.was_not_called());      \
3286    TEST_EXPECT(trace_cont_top_deleted.was_not_called());       \
3287    TEST_EXPECT(trace_cont_son_deleted.was_not_called());       \
3288    TEST_EXPECT(trace_top_changed.was_not_called());            \
3289    TEST_EXPECT(trace_son_changed.was_not_called());            \
3290    TEST_EXPECT(trace_grandson_changed.was_not_called());       \
3291    TEST_EXPECT(trace_ograndson_changed.was_not_called());      \
3292    TEST_EXPECT(trace_cont_top_changed.was_not_called());       \
3293    TEST_EXPECT(trace_cont_son_changed.was_not_called());       \
3294    TEST_EXPECT(trace_cont_top_newchild.was_not_called());      \
3295    TEST_EXPECT(trace_cont_son_newchild.was_not_called());      \
3296
3297
3298#define TEST_EXPECT_CB_TRIGGERED(TRACE,GBD,TYPE)         TEST_EXPECT(TRACE.was_called_by(GBD, TYPE))
3299#define TEST_EXPECT_CB_TRIGGERED_AT(TRACE,GBD,TYPE,TIME) TEST_EXPECT_EQUAL(TRACE.call_time(GBD, TYPE), TIME)
3300
3301#define TEST_EXPECT_CHANGE_TRIGGERED(TRACE,GBD) TEST_EXPECT_CB_TRIGGERED(TRACE, GBD, GB_CB_CHANGED)
3302#define TEST_EXPECT_DELETE_TRIGGERED(TRACE,GBD) TEST_EXPECT_CB_TRIGGERED(TRACE, GBD, GB_CB_DELETE)
3303#define TEST_EXPECT_NCHILD_TRIGGERED(TRACE,GBD) TEST_EXPECT_CB_TRIGGERED(TRACE, GBD, GB_CB_SON_CREATED)
3304
3305#define TEST_EXPECT_CHANGE_TRIGGERED_AT(TRACE,GBD,TIME) TEST_EXPECT_CB_TRIGGERED_AT(TRACE, GBD, GB_CB_CHANGED, TIME)
3306#define TEST_EXPECT_DELETE_TRIGGERED_AT(TRACE,GBD,TIME) TEST_EXPECT_CB_TRIGGERED_AT(TRACE, GBD, GB_CB_DELETE, TIME)
3307
3308void TEST_db_callbacks() {
3309    GB_shell  shell;
3310    GBDATA   *gb_main = GB_open("new.arb", "c");
3311
3312    // create some data
3313    GB_begin_transaction(gb_main);
3314
3315    GBDATA *cont_top = GB_create_container(gb_main,  "cont_top"); TEST_REJECT_NULL(cont_top);
3316    GBDATA *cont_son = GB_create_container(cont_top, "cont_son"); TEST_REJECT_NULL(cont_son);
3317
3318    GBDATA *top       = GB_create(gb_main,  "top",       GB_STRING); TEST_REJECT_NULL(top);
3319    GBDATA *son       = GB_create(cont_top, "son",       GB_INT);    TEST_REJECT_NULL(son);
3320    GBDATA *grandson  = GB_create(cont_son, "grandson",  GB_STRING); TEST_REJECT_NULL(grandson);
3321    GBDATA *ograndson = GB_create(cont_son, "ograndson", GB_STRING); TEST_REJECT_NULL(ograndson);
3322
3323    GB_commit_transaction(gb_main);
3324
3325    // install callbacks
3326    GB_begin_transaction(gb_main);
3327    INIT_CONTAINER_CALLBACKS(cont_top);
3328    INIT_CONTAINER_CALLBACKS(cont_son);
3329    INIT_ENTRY_CALLBACKS(top);
3330    INIT_ENTRY_CALLBACKS(son);
3331    INIT_ENTRY_CALLBACKS(grandson);
3332    INIT_ENTRY_CALLBACKS(ograndson);
3333    GB_commit_transaction(gb_main);
3334
3335    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3336
3337    // trigger change callbacks via change
3338    GB_begin_transaction(gb_main);
3339    GB_write_string(top, "hello world");
3340    GB_commit_transaction(gb_main);
3341    TEST_EXPECT_CHANGE_TRIGGERED(trace_top_changed, top);
3342    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3343
3344    GB_begin_transaction(gb_main);
3345    GB_write_string(top, "hello world"); // no change
3346    GB_commit_transaction(gb_main);
3347    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3348
3349#if 0
3350    // code is wrong (cannot set terminal entry to "marked")
3351    GB_begin_transaction(gb_main);
3352    GB_write_flag(son, 1);                                  // only change "mark"
3353    GB_commit_transaction(gb_main);
3354    TEST_EXPECT_CHANGE_TRIGGERED(trace_son_changed, son);
3355    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3356    TEST_EXPECT_TRIGGER__UNWANTED(trace_cont_top_newchild); // @@@ modifying son should not trigger newchild callback
3357    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3358#else
3359    // @@@ add test code similar to wrong section above
3360#endif
3361
3362    GB_begin_transaction(gb_main);
3363    GB_touch(grandson);
3364    GB_commit_transaction(gb_main);
3365    TEST_EXPECT_CHANGE_TRIGGERED(trace_grandson_changed, grandson);
3366    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_son_changed, cont_son);
3367    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3368    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3369
3370    // trigger change- and soncreate-callbacks via create
3371
3372    GB_begin_transaction(gb_main);
3373    GBDATA *son2 = GB_create(cont_top, "son2", GB_INT); TEST_REJECT_NULL(son2);
3374    GB_commit_transaction(gb_main);
3375    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3376    TEST_EXPECT_NCHILD_TRIGGERED(trace_cont_top_newchild, cont_top);
3377    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3378
3379    GB_begin_transaction(gb_main);
3380    GBDATA *grandson2 = GB_create(cont_son, "grandson2", GB_STRING); TEST_REJECT_NULL(grandson2);
3381    GB_commit_transaction(gb_main);
3382    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_son_changed, cont_son);
3383    TEST_EXPECT_NCHILD_TRIGGERED(trace_cont_son_newchild, cont_son);
3384    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3385    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3386
3387    // trigger callbacks via delete
3388
3389    TRIGGER_DELETE(son2);
3390    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3391    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3392
3393    TRIGGER_DELETE(grandson2);
3394    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3395    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_son_changed, cont_son);
3396    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3397
3398    TEST_EXPECT_NO_ERROR(GB_request_undo_type(gb_main, GB_UNDO_UNDO));
3399
3400    TRIGGER_DELETE(top);
3401    TEST_EXPECT_DELETE_TRIGGERED(trace_top_deleted, top);
3402    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3403
3404    TRIGGER_DELETE(grandson);
3405    TEST_EXPECT_DELETE_TRIGGERED(trace_grandson_deleted, grandson);
3406    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3407    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_son_changed, cont_son);
3408    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3409
3410    TRIGGER_DELETE(cont_son);
3411    TEST_EXPECT_DELETE_TRIGGERED(trace_ograndson_deleted, ograndson);
3412    TEST_EXPECT_DELETE_TRIGGERED(trace_cont_son_deleted, cont_son);
3413    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3414    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3415
3416    // trigger callbacks by undoing last 3 delete transactions
3417
3418    TEST_EXPECT_NO_ERROR(GB_undo(gb_main, GB_UNDO_UNDO)); // undo delete of cont_son
3419    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3420    TEST_EXPECT_NCHILD_TRIGGERED(trace_cont_top_newchild, cont_top);
3421    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3422
3423    TEST_EXPECT_NO_ERROR(GB_undo(gb_main, GB_UNDO_UNDO)); // undo delete of grandson
3424    // cont_son callbacks are not triggered (they are not restored by undo)
3425    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3426    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3427
3428    TEST_EXPECT_NO_ERROR(GB_undo(gb_main, GB_UNDO_UNDO)); // undo delete of top
3429    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3430
3431    // reinstall callbacks that were removed by deletes
3432
3433    GB_begin_transaction(gb_main);
3434    ENSURE_CONTAINER_CALLBACKS(cont_top);
3435    ENSURE_CONTAINER_CALLBACKS(cont_son);
3436    ENSURE_ENTRY_CALLBACKS(top);
3437    ENSURE_ENTRY_CALLBACKS(son);
3438    ENSURE_ENTRY_CALLBACKS(grandson);
3439    GB_commit_transaction(gb_main);
3440    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3441
3442    // trigger callbacks which will be removed
3443
3444    TRIGGER_CHANGE(son);
3445    TEST_EXPECT_CHANGE_TRIGGERED(trace_son_changed, son);
3446    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3447    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3448
3449    GB_begin_transaction(gb_main);
3450    GBDATA *son3 = GB_create(cont_top, "son3", GB_INT); TEST_REJECT_NULL(son3);
3451    GB_commit_transaction(gb_main);
3452    TEST_EXPECT_CHANGE_TRIGGERED(trace_cont_top_changed, cont_top);
3453    TEST_EXPECT_NCHILD_TRIGGERED(trace_cont_top_newchild, cont_top);
3454    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3455
3456    // test remove callback
3457
3458    GB_begin_transaction(gb_main);
3459    REMOVE_ENTRY_CALLBACKS(son);
3460    REMOVE_CONTAINER_CALLBACKS(cont_top);
3461    GB_commit_transaction(gb_main);
3462    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3463
3464    // "trigger" removed callbacks
3465
3466    TRIGGER_CHANGE(son);
3467    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3468
3469    GB_begin_transaction(gb_main);
3470    GBDATA *son4 = GB_create(cont_top, "son4", GB_INT); TEST_REJECT_NULL(son4);
3471    GB_commit_transaction(gb_main);
3472    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3473
3474    GB_close(gb_main);
3475}
3476
3477void TEST_POSTCOND_arbdb() {
3478    GB_ERROR error = NULL;
3479    if (GB_have_error()) {
3480        error = GB_await_error(); // clears the error (to make further tests succeed)
3481    }
3482
3483    bool unclosed_GB_shell = closed_open_shell_for_unit_tests();
3484
3485    TEST_REJECT(error);             // your test finished with an exported error
3486    TEST_REJECT(unclosed_GB_shell); // your test finished w/o destroying GB_shell
3487}
3488
3489#define TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED()                   \
3490    TEST_EXPECT(traceHier_anyGrandson_changed.was_not_called());        \
3491    TEST_EXPECT(traceHier_anyGrandson_deleted.was_not_called());        \
3492    TEST_EXPECT(traceHier_anySonContainer_newchild.was_not_called())
3493
3494#undef TEST_EXPECT_NO_CALLBACK_TRIGGERED
3495
3496#define TEST_EXPECT_NO_CALLBACK_TRIGGERED()                             \
3497    TEST_EXPECT(trace_anotherGrandson_changed.was_not_called());        \
3498    TEST_EXPECT(trace_elimGrandson2_deleted.was_not_called())
3499
3500void TEST_hierarchy_callbacks() {
3501    GB_shell  shell;
3502    GBDATA   *gb_main = GB_open("new.arb", "c");
3503
3504    // create some data
3505    GB_begin_transaction(gb_main);
3506
3507    GBDATA *cont_top1 = GB_create_container(gb_main, "cont_top"); TEST_REJECT_NULL(cont_top1);
3508    GBDATA *cont_top2 = GB_create_container(gb_main, "cont_top"); TEST_REJECT_NULL(cont_top2);
3509
3510    GBDATA *cont_son11 = GB_create_container(cont_top1, "cont_son"); TEST_REJECT_NULL(cont_son11);
3511    GBDATA *cont_son21 = GB_create_container(cont_top2, "cont_son"); TEST_REJECT_NULL(cont_son21);
3512    GBDATA *cont_son22 = GB_create_container(cont_top2, "cont_son"); TEST_REJECT_NULL(cont_son22);
3513
3514    GBDATA *top1 = GB_create(gb_main, "top", GB_STRING); TEST_REJECT_NULL(top1);
3515    GBDATA *top2 = GB_create(gb_main, "top", GB_STRING); TEST_REJECT_NULL(top2);
3516
3517    GBDATA *son11 = GB_create(cont_top1, "son", GB_INT); TEST_REJECT_NULL(son11);
3518    GBDATA *son12 = GB_create(cont_top1, "son", GB_INT); TEST_REJECT_NULL(son12);
3519    GBDATA *son21 = GB_create(cont_top2, "son", GB_INT); TEST_REJECT_NULL(son21);
3520
3521    GBDATA *grandson111 = GB_create(cont_son11, "grandson", GB_STRING); TEST_REJECT_NULL(grandson111);
3522    GBDATA *grandson112 = GB_create(cont_son11, "grandson", GB_STRING); TEST_REJECT_NULL(grandson112);
3523    GBDATA *grandson211 = GB_create(cont_son21, "grandson", GB_STRING); TEST_REJECT_NULL(grandson211);
3524    GBDATA *grandson221 = GB_create(cont_son22, "grandson", GB_STRING); TEST_REJECT_NULL(grandson221);
3525    GBDATA *grandson222 = GB_create(cont_son22, "grandson", GB_STRING); TEST_REJECT_NULL(grandson222);
3526
3527    // create some entries at uncommon locations (compared to entries created above)
3528    GBDATA *ctop_top = GB_create          (cont_top2, "top", GB_STRING);      TEST_REJECT_NULL(ctop_top);
3529    GBDATA *top_son  = GB_create          (gb_main,   "son", GB_INT);         TEST_REJECT_NULL(top_son);
3530    GBDATA *cson     = GB_create_container(gb_main,   "cont_son");            TEST_REJECT_NULL(cson);
3531    GBDATA *cson_gs  = GB_create          (cson,      "grandson", GB_STRING); TEST_REJECT_NULL(cson_gs);
3532
3533    GB_commit_transaction(gb_main);
3534
3535    // test gb_hierarchy_location
3536    {
3537        gb_hierarchy_location loc_top(top1);
3538        gb_hierarchy_location loc_son(son11);
3539        gb_hierarchy_location loc_grandson(grandson222);
3540
3541        TEST_EXPECT(loc_top.matches(top1));
3542        TEST_EXPECT(loc_top.matches(top2));
3543        TEST_EXPECT(!loc_top.matches(cont_top1));
3544        TEST_EXPECT(!loc_top.matches(son12));
3545        TEST_EXPECT(!loc_top.matches(cont_son22));
3546        TEST_EXPECT(!loc_top.matches(ctop_top));
3547
3548        TEST_EXPECT(loc_son.matches(son11));
3549        TEST_EXPECT(loc_son.matches(son21));
3550        TEST_EXPECT(!loc_son.matches(top1));
3551        TEST_EXPECT(!loc_son.matches(grandson111));
3552        TEST_EXPECT(!loc_son.matches(cont_son22));
3553        TEST_EXPECT(!loc_son.matches(top_son));
3554
3555        TEST_EXPECT(loc_grandson.matches(grandson222));
3556        TEST_EXPECT(loc_grandson.matches(grandson111));
3557        TEST_EXPECT(!loc_grandson.matches(son11));
3558        TEST_EXPECT(!loc_grandson.matches(top1));
3559        TEST_EXPECT(!loc_grandson.matches(cont_son22));
3560        TEST_EXPECT(!loc_grandson.matches(cson_gs));
3561
3562        gb_hierarchy_location loc_ctop_top(ctop_top);
3563        TEST_EXPECT(loc_ctop_top.matches(ctop_top));
3564        TEST_EXPECT(!loc_ctop_top.matches(top1));
3565
3566        gb_hierarchy_location loc_top_son(top_son);
3567        TEST_EXPECT(loc_top_son.matches(top_son));
3568        TEST_EXPECT(!loc_top_son.matches(son11));
3569
3570        gb_hierarchy_location loc_gs(cson_gs);
3571        TEST_EXPECT(loc_gs.matches(cson_gs));
3572        TEST_EXPECT(!loc_gs.matches(grandson211));
3573
3574        gb_hierarchy_location loc_root(gb_main);
3575        TEST_EXPECT(loc_root.matches(gb_main));
3576        TEST_EXPECT(!loc_root.matches(cont_top1));
3577        TEST_EXPECT(!loc_root.matches(cont_son11));
3578        TEST_EXPECT(!loc_root.matches(top1));
3579        TEST_EXPECT(!loc_root.matches(son11));
3580        TEST_EXPECT(!loc_root.matches(grandson211));
3581    }
3582
3583
3584    // instanciate callback_trace data and install hierarchy callbacks
3585    GBDATA *anySon = son11;
3586
3587    GBDATA *anySonContainer     = cont_son11;
3588    GBDATA *anotherSonContainer = cont_son22;
3589
3590    GBDATA *anyGrandson     = grandson221;
3591    GBDATA *anotherGrandson = grandson112;
3592    GBDATA *elimGrandson    = grandson222;
3593    GBDATA *elimGrandson2   = grandson111;
3594    GBDATA *newGrandson     = NULL;
3595
3596    INIT_CHANGED_HIERARCHY_CALLBACK(anyGrandson);
3597    INIT_DELETED_HIERARCHY_CALLBACK(anyGrandson);
3598    INIT_NWCHILD_HIERARCHY_CALLBACK(anySonContainer);
3599
3600    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3601
3602    // trigger change-callback using same DB entry
3603    TRIGGER_CHANGE(anyGrandson);
3604    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anyGrandson);
3605    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3606
3607    // trigger change-callback using another DB entry (same hierarchy)
3608    TRIGGER_CHANGE(anotherGrandson);
3609    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anotherGrandson);
3610    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3611
3612    // check nothing is triggered by an element at different hierarchy
3613    TRIGGER_CHANGE(anySon);
3614    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3615
3616    // trigger change-callback using both DB entries (in two TAs)
3617    TRIGGER_CHANGE(anyGrandson);
3618    TRIGGER_CHANGE(anotherGrandson);
3619    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anyGrandson);
3620    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anotherGrandson);
3621    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3622
3623    // trigger change-callback using both DB entries (in one TA)
3624    TRIGGER_2_CHANGES(anyGrandson, anotherGrandson);
3625    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anyGrandson);
3626    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, anotherGrandson);
3627    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3628
3629    // trigger son-created-callback
3630    {
3631        GB_initial_transaction ta(gb_main);
3632        if (ta.ok()) {
3633            GBDATA *someson = GB_create(anySonContainer, "someson", GB_STRING); TEST_REJECT_NULL(someson);
3634        }
3635        TEST_EXPECT_NO_ERROR(ta.close(NULL));
3636    }
3637    TEST_EXPECT_NCHILD_TRIGGERED(traceHier_anySonContainer_newchild, anySonContainer);
3638    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3639
3640    // trigger 2 son-created-callbacks (for 2 containers) and one change-callback (for a newly created son)
3641    {
3642        GB_initial_transaction ta(gb_main);
3643        if (ta.ok()) {
3644            newGrandson     = GB_create(anotherSonContainer, "grandson", GB_STRING); TEST_REJECT_NULL(newGrandson);
3645            GBDATA *someson = GB_create(anySonContainer,     "someson",  GB_STRING); TEST_REJECT_NULL(someson);
3646        }
3647        TEST_EXPECT_NO_ERROR(ta.close(NULL));
3648    }
3649    TEST_EXPECT_CHANGE_TRIGGERED(traceHier_anyGrandson_changed, newGrandson);
3650    TEST_EXPECT_NCHILD_TRIGGERED(traceHier_anySonContainer_newchild, anotherSonContainer);
3651    TEST_EXPECT_NCHILD_TRIGGERED(traceHier_anySonContainer_newchild, anySonContainer);
3652    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3653
3654    // trigger delete-callback
3655    {
3656        GB_initial_transaction ta(gb_main);
3657        TEST_EXPECT_NO_ERROR(GB_delete(elimGrandson));
3658        TEST_EXPECT_NO_ERROR(ta.close(NULL));
3659    }
3660    TEST_EXPECT_DELETE_TRIGGERED(traceHier_anyGrandson_deleted, elimGrandson);
3661    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3662
3663    // bind normal (non-hierarchical) callbacks to entries which trigger hierarchical callbacks and ..
3664    calledWith::timer = 0;
3665    GB_begin_transaction(gb_main);
3666    INIT_CHANGED_CALLBACK(anotherGrandson);
3667    INIT_DELETED_CALLBACK(elimGrandson2);
3668    GB_commit_transaction(gb_main);
3669
3670    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3671
3672    {
3673        GB_initial_transaction ta(gb_main);
3674        if (ta.ok()) {
3675            GB_touch(anotherGrandson);
3676            GB_touch(elimGrandson2);
3677            TEST_EXPECT_NO_ERROR(GB_delete(elimGrandson2));
3678        }
3679    }
3680
3681    // .. test call-order (delete before change, hierarchical before normal):
3682    TEST_EXPECT_DELETE_TRIGGERED_AT(traceHier_anyGrandson_deleted, elimGrandson2,   1);
3683    TEST_EXPECT_DELETE_TRIGGERED_AT(trace_elimGrandson2_deleted,   elimGrandson2,   2);
3684    TEST_EXPECT_CHANGE_TRIGGERED_AT(traceHier_anyGrandson_changed, anotherGrandson, 3);
3685    TEST_EXPECT_CHANGE_TRIGGERED_AT(trace_anotherGrandson_changed, anotherGrandson, 4);
3686
3687    TEST_EXPECT_NO_HIERARCHY_CALLBACK_TRIGGERED();
3688    TEST_EXPECT_NO_CALLBACK_TRIGGERED();
3689
3690    // cleanup
3691    GB_close(gb_main);
3692}
3693
3694#endif // UNIT_TESTS
3695
3696
Note: See TracBrowser for help on using the repository browser.