source: branches/properties/ARBDB/arbdb.cxx

Last change on this file was 18971, checked in by westram, 3 years ago
  • include rpc/types.h under OSX only (fixes compilation under SuSE Leap 15.3).

Notes:

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.0 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#include "gb_key.h"
12#include "gb_comm.h"
13#include "gb_compress.h"
14#include "gb_localdata.h"
15#include "gb_ta.h"
16#include "gb_ts.h"
17#include "gb_index.h"
18#include "adperl.h"
19
20#include <arb_misc.h>
21
22#if defined(DARWIN)
23// including types.h was never necessary under linux.
24// Under SuSE Leap 15.3 it does fail (file not found).
25// => made this OSX-only
26# include <rpc/types.h>
27# warning "Please remove the above include if it works w/o under OSX"
28#endif
29#include <rpc/xdr.h>
30
31#include <set>
32
33gb_local_data *gb_local = NULp;
34
35#define INIT_TYPE_NAME(t) GB_TYPES_name[t] = #t
36
37static const char *GB_TYPES_2_name(GB_TYPES type) {
38    static const char *GB_TYPES_name[GB_TYPE_MAX];
39    static bool        initialized = false;
40
41    if (!initialized) {
42        memset(GB_TYPES_name, 0, sizeof(GB_TYPES_name));
43        INIT_TYPE_NAME(GB_NONE);
44        INIT_TYPE_NAME(GB_BIT);
45        INIT_TYPE_NAME(GB_BYTE);
46        INIT_TYPE_NAME(GB_INT);
47        INIT_TYPE_NAME(GB_FLOAT);
48        INIT_TYPE_NAME(GB_POINTER);
49        INIT_TYPE_NAME(GB_BITS);
50        INIT_TYPE_NAME(GB_BYTES);
51        INIT_TYPE_NAME(GB_INTS);
52        INIT_TYPE_NAME(GB_FLOATS);
53        INIT_TYPE_NAME(GB_STRING);
54        INIT_TYPE_NAME(GB_STRING_SHRT);
55        INIT_TYPE_NAME(GB_DB);
56
57        GB_TYPES_name[GB_OBSOLETE] = "GB_LINK (obsolete)";
58
59        initialized = true;
60    }
61
62    const char *name = NULp;
63    if (type >= 0 && type<GB_TYPE_MAX) name = GB_TYPES_name[type];
64    if (!name) {
65        static char *unknownType = NULp;
66        freeset(unknownType, GBS_global_string_copy("<invalid-type=%i>", type));
67        name = unknownType;
68    }
69    return name;
70}
71
72const char *GB_get_type_name(GBDATA *gbd) {
73    return GB_TYPES_2_name(gbd->type());
74}
75
76inline GB_ERROR gb_transactable_type(GB_TYPES type, GBDATA *gbd) {
77    GB_ERROR error = NULp;
78    if (GB_MAIN(gbd)->get_transaction_level() == 0) {
79        error = "No transaction running";
80    }
81    else if (GB_ARRAY_FLAGS(gbd).changed == GB_DELETED) {
82        error = "Entry has been deleted";
83    }
84    else {
85        GB_TYPES gb_type = gbd->type();
86        if (gb_type != type && (type != GB_STRING || gb_type != GB_OBSOLETE)) {
87            char *rtype    = ARB_strdup(GB_TYPES_2_name(type));
88            char *rgb_type = ARB_strdup(GB_TYPES_2_name(gb_type));
89
90            error = GBS_global_string("type mismatch (want='%s', got='%s') in '%s'", rtype, rgb_type, GB_get_db_path(gbd));
91
92            free(rgb_type);
93            free(rtype);
94        }
95    }
96    if (error) {
97        GBK_dump_backtrace(stderr, error); // it's a bug: none of the above errors should ever happen
98        gb_assert(0);
99    }
100    return error;
101}
102
103__ATTR__USERESULT static GB_ERROR gb_security_error(GBDATA *gbd) {
104    GB_MAIN_TYPE *Main  = GB_MAIN(gbd);
105    const char   *error = GBS_global_string("Protection: Attempt to change a level-%i-'%s'-entry,\n"
106                                            "but your current security level is only %i",
107                                            GB_GET_SECURITY_WRITE(gbd),
108                                            GB_read_key_pntr(gbd),
109                                            Main->security_level);
110#if defined(DEBUG)
111    fprintf(stderr, "%s\n", error);
112#endif // DEBUG
113    return error;
114}
115
116inline GB_ERROR gb_type_writeable_to(GB_TYPES type, GBDATA *gbd) {
117    GB_ERROR error = gb_transactable_type(type, gbd);
118    if (!error) {
119        if (GB_GET_SECURITY_WRITE(gbd) > GB_MAIN(gbd)->security_level) {
120            error = gb_security_error(gbd);
121        }
122    }
123    return error;
124}
125inline GB_ERROR gb_type_readable_from(GB_TYPES type, GBDATA *gbd) {
126    return gb_transactable_type(type, gbd);
127}
128
129inline GB_ERROR error_with_dbentry(const char *action, GBDATA *gbd, GB_ERROR error) {
130    if (error) {
131        char       *error_copy = ARB_strdup(error);
132        const char *path       = GB_get_db_path(gbd);
133        error                  = GBS_global_string("Can't %s '%s':\n%s", action, path, error_copy);
134        free(error_copy);
135    }
136    return error;
137}
138
139
140#define RETURN_ERROR_IF_NOT_WRITEABLE_AS_TYPE(gbd,type)         \
141    do {                                                        \
142        GB_ERROR error = gb_type_writeable_to(type, gbd);       \
143        if (error) {                                            \
144            return error_with_dbentry("write", gbd, error);     \
145        }                                                       \
146    } while(0)
147
148#define EXPORT_ERROR_AND_RETURN_RES_IF_NOT_READABLE_AS_TYPE(gbd,res,type)       \
149    do {                                                                        \
150        GB_ERROR error = gb_type_readable_from(type, gbd);                      \
151        if (error) {                                                            \
152            error = error_with_dbentry("read", gbd, error);                     \
153            GB_export_error(error);                                             \
154            return res;                                                         \
155        }                                                                       \
156    } while(0)                                                                  \
157
158
159// @@@ replace GB_TEST_READ_NUM, GB_TEST_READ_PTR and GB_TEST_READ by new names
160
161#define GB_TEST_READ_NUM(gbd,type,ignored) EXPORT_ERROR_AND_RETURN_RES_IF_NOT_READABLE_AS_TYPE(gbd,0,type)
162#define GB_TEST_READ_PTR(gbd,type,ignored) EXPORT_ERROR_AND_RETURN_RES_IF_NOT_READABLE_AS_TYPE(gbd,NULp,type)
163#define GB_TEST_WRITE(gbd,type,ignored)    RETURN_ERROR_IF_NOT_WRITEABLE_AS_TYPE(gbd, type)
164
165#define GB_TEST_NON_BUFFER(x, gerror)                                   \
166    do {                                                                \
167        if (GB_is_in_buffer(x)) {                                       \
168            GBK_terminatef("%s: you are not allowed to write any data, which you get by pntr", gerror); \
169        }                                                               \
170    } while (0)
171
172
173static GB_ERROR GB_safe_atof(const char *str, float *res) {
174    GB_ERROR error = NULp;
175
176    char *end;
177    *res = strtof(str, &end);
178
179    if (end == str || end[0] != 0) {
180        if (!str[0]) {
181            *res = 0.0;
182        }
183        else {
184            error = GBS_global_string("cannot convert '%s' to float", str);
185        }
186    }
187    return error;
188}
189
190float GB_atof(const char *str) {
191    // convert ASCII to float
192    float    res = 0;
193    GB_ERROR err = GB_safe_atof(str, &res);
194    if (err) {
195        // expected float in 'str'- better use GB_safe_atof()
196        GBK_terminatef("GB_safe_atof(\"%s\", ..) returns error: %s", str, err);
197    }
198    return res;
199}
200
201// ---------------------------
202//      compression tables
203
204const int gb_convert_type_2_compression_flags[] = {
205    GB_COMPRESSION_NONE,                                                                 // GB_NONE  0
206    GB_COMPRESSION_NONE,                                                                 // GB_BIT   1
207    GB_COMPRESSION_NONE,                                                                 // GB_BYTE  2
208    GB_COMPRESSION_NONE,                                                                 // GB_INT   3
209    GB_COMPRESSION_NONE,                                                                 // GB_FLOAT 4
210    GB_COMPRESSION_NONE,                                                                 // GB_??    5
211    GB_COMPRESSION_BITS,                                                                 // GB_BITS  6
212    GB_COMPRESSION_NONE,                                                                 // GB_??    7
213    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN,                                  // GB_BYTES 8
214    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_SORTBYTES,       // GB_INTS  9
215    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_SORTBYTES,       // GB_FLTS 10
216    GB_COMPRESSION_NONE,                                                                 // GB_LINK 11
217    GB_COMPRESSION_RUNLENGTH | GB_COMPRESSION_HUFFMANN | GB_COMPRESSION_DICTIONARY,      // GB_STR  12
218    GB_COMPRESSION_NONE,                                                                 // GB_STRS 13
219    GB_COMPRESSION_NONE,                                                                 // GB??    14
220    GB_COMPRESSION_NONE                                                                  // GB_DB   15
221};
222
223int gb_convert_type_2_sizeof[] = { /* contains the unit-size of data stored in DB,
224                                    * i.e. realsize = unit_size * size()
225                                    */
226    0,                                              // GB_NONE  0
227    0,                                              // GB_BIT   1
228    sizeof(char),                                   // GB_BYTE  2
229    sizeof(int),                                    // GB_INT   3
230    sizeof(float),                                  // GB_FLOAT 4
231    0,                                              // GB_??    5
232    0,                                              // GB_BITS  6
233    0,                                              // GB_??    7
234    sizeof(char),                                   // GB_BYTES 8
235    sizeof(int),                                    // GB_INTS  9
236    sizeof(float),                                  // GB_FLTS 10
237    sizeof(char),                                   // GB_LINK 11
238    sizeof(char),                                   // GB_STR  12
239    sizeof(char),                                   // GB_STRS 13
240    0,                                              // GB_??   14
241    0,                                              // GB_DB   15
242};
243
244int gb_convert_type_2_appendix_size[] = { /* contains the size of the suffix (aka terminator element)
245                                           * size is in bytes
246                                           */
247
248    0,                                              // GB_NONE  0
249    0,                                              // GB_BIT   1
250    0,                                              // GB_BYTE  2
251    0,                                              // GB_INT   3
252    0,                                              // GB_FLOAT 4
253    0,                                              // GB_??    5
254    0,                                              // GB_BITS  6
255    0,                                              // GB_??    7
256    0,                                              // GB_BYTES 8
257    0,                                              // GB_INTS  9
258    0,                                              // GB_FLTS 10
259    1,                                              // GB_LINK 11 (zero terminated)
260    1,                                              // GB_STR  12 (zero terminated)
261    1,                                              // GB_STRS 13 (zero terminated)
262    0,                                              // GB_??   14
263    0,                                              // GB_DB   15
264};
265
266
267// ---------------------------------
268//      local buffer management
269
270static void init_buffer(gb_buffer *buf, size_t initial_size) {
271    buf->size = initial_size;
272    buf->mem  = buf->size ? ARB_alloc<char>(buf->size) : NULp;
273}
274
275static char *check_out_buffer(gb_buffer *buf) {
276    char *checkOut = buf->mem;
277
278    buf->mem  = NULp;
279    buf->size = 0;
280
281    return checkOut;
282}
283
284static void alloc_buffer(gb_buffer *buf, size_t size) {
285    free(buf->mem);
286    buf->size = size;
287#if (MEMORY_TEST==1)
288    ARB_alloc(buf->mem, buf->size);
289#else
290    ARB_calloc(buf->mem, buf->size);
291#endif
292}
293
294static GB_BUFFER give_buffer(gb_buffer *buf, size_t size) {
295#if (MEMORY_TEST==1)
296    alloc_buffer(buf, size); // do NOT reuse buffer if testing memory
297#else
298    if (size >= buf->size) {
299        alloc_buffer(buf, size);
300    }
301#endif
302    return buf->mem;
303}
304
305static int is_in_buffer(gb_buffer *buf, GB_CBUFFER ptr) {
306    return ptr >= buf->mem && ptr < buf->mem+buf->size;
307}
308
309// ------------------------------
310
311GB_BUFFER GB_give_buffer(size_t size) {
312    // return a pointer to a static piece of memory at least size bytes long
313    return give_buffer(&gb_local->buf1, size);
314}
315
316GB_BUFFER GB_increase_buffer(size_t size) {
317    if (size < gb_local->buf1.size) {
318        char   *old_buffer = gb_local->buf1.mem;
319        size_t  old_size   = gb_local->buf1.size;
320
321        gb_local->buf1.mem = NULp;
322        alloc_buffer(&gb_local->buf1, size);
323        memcpy(gb_local->buf1.mem, old_buffer, old_size);
324
325        free(old_buffer);
326    }
327    return gb_local->buf1.mem;
328}
329
330NOT4PERL int GB_give_buffer_size() {
331    return gb_local->buf1.size;
332}
333
334GB_BUFFER GB_give_buffer2(long size) {
335    return give_buffer(&gb_local->buf2, size);
336}
337
338static int GB_is_in_buffer(GB_CBUFFER ptr) {
339    /* returns 1 or 2 if 'ptr' points to gb_local->buf1/buf2
340     * returns 0 otherwise
341     */
342    int buffer = 0;
343
344    if (is_in_buffer(&gb_local->buf1, ptr)) buffer = 1;
345    else if (is_in_buffer(&gb_local->buf2, ptr)) buffer = 2;
346
347    return buffer;
348}
349
350char *GB_check_out_buffer(GB_CBUFFER buffer) {
351    /* Check a piece of memory out of the buffer management
352     * after it is checked out, the user has the full control to use and free it
353     * Returns a pointer to the start of the buffer (even if 'buffer' points inside the buffer!)
354     */
355    char *old = NULp;
356
357    if (is_in_buffer(&gb_local->buf1, buffer)) old = check_out_buffer(&gb_local->buf1);
358    else if (is_in_buffer(&gb_local->buf2, buffer)) old = check_out_buffer(&gb_local->buf2);
359
360    return old;
361}
362
363GB_BUFFER GB_give_other_buffer(GB_CBUFFER buffer, long size) {
364    return is_in_buffer(&gb_local->buf1, buffer)
365        ? GB_give_buffer2(size)
366        : GB_give_buffer(size);
367}
368
369static unsigned char GB_BIT_compress_data[] = {
370    0x1d, GB_CS_OK,  0, 0,
371    0x04, GB_CS_OK,  0, 1,
372    0x0a, GB_CS_OK,  0, 2,
373    0x0b, GB_CS_OK,  0, 3,
374    0x0c, GB_CS_OK,  0, 4,
375    0x1a, GB_CS_OK,  0, 5,
376    0x1b, GB_CS_OK,  0, 6,
377    0x1c, GB_CS_OK,  0, 7,
378    0xf0, GB_CS_OK,  0, 8,
379    0xf1, GB_CS_OK,  0, 9,
380    0xf2, GB_CS_OK,  0, 10,
381    0xf3, GB_CS_OK,  0, 11,
382    0xf4, GB_CS_OK,  0, 12,
383    0xf5, GB_CS_OK,  0, 13,
384    0xf6, GB_CS_OK,  0, 14,
385    0xf7, GB_CS_OK,  0, 15,
386    0xf8, GB_CS_SUB, 0, 16,
387    0xf9, GB_CS_SUB, 0, 32,
388    0xfa, GB_CS_SUB, 0, 48,
389    0xfb, GB_CS_SUB, 0, 64,
390    0xfc, GB_CS_SUB, 0, 128,
391    0xfd, GB_CS_SUB, 1, 0,
392    0xfe, GB_CS_SUB, 2, 0,
393    0xff, GB_CS_SUB, 4, 0,
394    0
395};
396
397struct gb_exitfun {
398    void (*exitfun)();
399    gb_exitfun *next;
400};
401
402void GB_atexit(void (*exitfun)()) {
403    // called when GB_shell is destroyed (use similar to atexit())
404    //
405    // Since the program does not necessarily terminate, your code calling
406    // GB_atexit() may run multiple times. Make sure everything is completely reset by your 'exitfun'
407
408    gb_exitfun *fun = new gb_exitfun;
409    fun->exitfun    = exitfun;
410
411    fun->next          = gb_local->atgbexit;
412    gb_local->atgbexit = fun;
413}
414
415static void run_and_destroy_exit_functions(gb_exitfun *fun) {
416    if (fun) {
417        fun->exitfun();
418        run_and_destroy_exit_functions(fun->next);
419        delete fun;
420    }
421}
422
423static void GB_exit_gb() {
424    GB_shell::ensure_inside();
425
426    if (gb_local) {
427        gb_local->~gb_local_data(); // inplace-dtor
428        gbm_free_mem(gb_local, sizeof(*gb_local), 0);
429        gb_local = NULp;
430        gbm_flush_mem();
431    }
432}
433
434gb_local_data::~gb_local_data() {
435    gb_assert(openedDBs == closedDBs);
436
437    run_and_destroy_exit_functions(atgbexit);
438
439    free(bitcompress);
440    gb_free_compress_tree(bituncompress);
441    free(write_buffer);
442
443    free(check_out_buffer(&buf2));
444    free(check_out_buffer(&buf1));
445    free(open_gb_mains);
446}
447
448// -----------------
449//      GB_shell
450
451
452static GB_shell *inside_shell = NULp;
453
454GB_shell::GB_shell() {
455    if (inside_shell) GBK_terminate("only one GB_shell allowed");
456    inside_shell = this;
457}
458GB_shell::~GB_shell() {
459    gb_assert(inside_shell == this);
460    GB_exit_gb();
461    inside_shell = NULp;
462}
463void GB_shell::ensure_inside()  { if (!inside_shell) GBK_terminate("Not inside GB_shell"); }
464
465bool GB_shell::in_shell() { // used by code based on ARBDB (Kai IIRC)
466    return inside_shell;
467}
468
469class GB_test_shell_closed {
470    SmartPtr<GB_shell> one_global_shell;
471
472public:
473    ~GB_test_shell_closed() {
474        close_global_shell();
475        if (GB_shell::in_shell()) {    // leave that call
476            inside_shell->~GB_shell(); // call dtor
477        }
478    }
479
480    void open_global_shell() {
481        // allows to workaround using a global GB_shell variable
482        // (see #508 for problem arising when such a variable used)
483        one_global_shell = new GB_shell;
484    }
485    void close_global_shell() {
486        if (one_global_shell.isSet()) {
487            gb_abort_and_close_all_DBs();
488            one_global_shell.setNull(); // =destroy
489        }
490    }
491};
492static GB_test_shell_closed shell_manager;
493
494GB_shell4perl::GB_shell4perl() {
495    shell_manager.open_global_shell();
496}
497GB_shell4perl::~GB_shell4perl() {
498    shell_manager.close_global_shell();
499}
500
501#if defined(UNIT_TESTS)
502static bool closed_open_shell_for_unit_tests() {
503    bool was_open = inside_shell;
504    if (was_open) {
505        if (gb_local) gb_local->fake_closed_DBs();
506        inside_shell->~GB_shell(); // just call dtor (not delete)
507    }
508    return was_open;
509}
510#endif
511
512void GB_init_gb() {
513    GB_shell::ensure_inside();
514    if (!gb_local) {
515        GBK_install_SIGSEGV_handler(true);          // never uninstalled
516        gbm_init_mem();
517        gb_local = (gb_local_data *)gbm_get_mem(sizeof(gb_local_data), 0);
518        ::new(gb_local) gb_local_data(); // inplace-ctor
519    }
520}
521
522int GB_open_DBs() { return gb_local ? gb_local->open_dbs() : 0; }
523
524gb_local_data::gb_local_data() {
525    init_buffer(&buf1, 4000);
526    init_buffer(&buf2, 4000);
527
528    write_bufsize = GBCM_BUFFER;
529    ARB_alloc(write_buffer, write_bufsize);
530
531    write_ptr  = write_buffer;
532    write_free = write_bufsize;
533
534    bituncompress = gb_build_uncompress_tree(GB_BIT_compress_data, 1, NULp);
535    bitcompress   = gb_build_compress_list(GB_BIT_compress_data, 1, &(bc_size));
536
537    openedDBs = 0;
538    closedDBs = 0;
539
540    open_gb_mains = NULp;
541    open_gb_alloc = 0;
542
543    atgbexit = NULp;
544
545    iamclient                  = false;
546    search_system_folder       = false;
547    running_client_transaction = ARB_NO_TRANS;
548}
549
550void gb_local_data::announce_db_open(GB_MAIN_TYPE *Main) {
551    gb_assert(Main);
552    int idx = open_dbs();
553    if (idx >= open_gb_alloc) {
554        int new_alloc = open_gb_alloc + 10;
555        ARB_recalloc(open_gb_mains, open_gb_alloc, new_alloc);
556        open_gb_alloc = new_alloc;
557    }
558    open_gb_mains[idx] = Main;
559    openedDBs++;
560}
561
562void gb_local_data::announce_db_close(GB_MAIN_TYPE *Main) {
563    gb_assert(Main);
564    int open = open_dbs();
565    int idx;
566    for (idx = 0; idx<open; ++idx) if (open_gb_mains[idx] == Main) break;
567
568    gb_assert(idx<open); // passed gb_main is unknown
569    if (idx<open) {
570        if (idx<(open-1)) { // not last
571            open_gb_mains[idx] = open_gb_mains[open-1];
572        }
573        closedDBs++;
574    }
575    if (closedDBs == openedDBs) {
576        GB_exit_gb(); // free most memory allocated by ARBDB library
577        // Caution: calling GB_exit_gb() frees 'this'!
578    }
579}
580
581static GBDATA *gb_remembered_db() {
582    GB_MAIN_TYPE *Main = gb_local ? gb_local->get_any_open_db() : NULp;
583    return Main ? Main->gb_main() : NULp;
584}
585
586GB_ERROR gb_unfold(GBCONTAINER *gbc, long deep, int index_pos) {
587    /*! get data from server.
588     *
589     * @param gbc container to unfold
590     * @param deep if != 0, then get subitems too.
591     * @param index_pos
592     * - >= 0, get indexed item from server
593     * - <0, get all items
594     *
595     * @return error on failure
596     */
597
598    GB_ERROR        error;
599    gb_header_list *header = GB_DATA_LIST_HEADER(gbc->d);
600
601    if (!gbc->flags2.folded_container) return NULp;
602    if (index_pos> gbc->d.nheader) gb_create_header_array(gbc, index_pos + 1);
603    if (index_pos >= 0  && GB_HEADER_LIST_GBD(header[index_pos])) return NULp;
604
605    if (GBCONTAINER_MAIN(gbc)->is_server()) {
606        GB_internal_error("Cannot unfold in server");
607        return NULp;
608    }
609
610    do {
611        if (index_pos<0) break;
612        if (index_pos >= gbc->d.nheader) break;
613        if (header[index_pos].flags.changed >= GB_DELETED) {
614            GB_internal_error("Tried to unfold a deleted item");
615            return NULp;
616        }
617        if (GB_HEADER_LIST_GBD(header[index_pos])) return NULp; // already unfolded
618    } while (0);
619
620    error = gbcm_unfold_client(gbc, deep, index_pos);
621    if (error) {
622        GB_print_error();
623        return error;
624    }
625
626    if (index_pos<0) {
627        gb_untouch_children(gbc);
628        gbc->flags2.folded_container = 0;
629    }
630    else {
631        GBDATA *gb2 = GBCONTAINER_ELEM(gbc, index_pos);
632        if (gb2) {
633            if (gb2->is_container()) {
634                gb_untouch_children_and_me(gb2->as_container());
635            }
636            else {
637                gb_untouch_me(gb2->as_entry());
638            }
639        }
640    }
641    return NULp;
642}
643
644// -----------------------
645//      close database
646
647static void run_close_callbacks(GBDATA *gb_main) {
648    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
649    gb_assert(Main->gb_main() == gb_main);
650    if (Main->close_callbacks) {
651        Main->close_callbacks->call(gb_main, GB_CB_DELETE);
652    }
653}
654
655void GB_close(GBDATA *gbd) {
656    GB_ERROR      error = NULp;
657    GB_MAIN_TYPE *Main  = GB_MAIN(gbd);
658
659    gb_assert(Main->get_transaction_level() <= 0); // transaction running - you can't close DB yet!
660
661    Main->forget_hierarchy_cbs();
662
663    gb_assert(Main->gb_main() == gbd);
664    run_close_callbacks(gbd);
665    Main->close_callbacks = NULp;
666
667    bool quick_exit = Main->mapped;
668    if (Main->is_client()) {
669        GBCM_ServerResult result = gbcmc_close(Main->c_link);
670        if (result != GBCM_SERVER_OK) error = GBS_global_string("close failed (with %i:%s)", result, GB_await_error());
671
672        gb_assert(!quick_exit); // client cannot be mapped
673    }
674
675    gbcm_logout(Main, NULp); // logout default user
676
677    if (!error) {
678        gb_assert(!Main->close_callbacks);
679
680#if defined(LEAKS_SANITIZED)
681        quick_exit = false;
682#endif
683
684        if (quick_exit) {
685            // fake some data to allow quick-exit
686            Main->dummy_father = NULp;
687            Main->cache.entries = NULp;
688        }
689        else {
690            // proper cleanup of DB (causes unwanted behavior described in #649)
691            gb_delete_dummy_father(Main->dummy_father);
692        }
693        Main->root_container = NULp;
694
695        /* ARBDB applications using awars easily crash in call_pending_callbacks(),
696         * if AWARs are still bound to elements in the closed database.
697         *
698         * To unlink awars call AW_root::unlink_awars_from_DB().
699         * If that doesn't help, test Main->data (often aka as gb_main)
700         */
701        Main->call_pending_callbacks(); // do all callbacks
702        delete Main;
703    }
704
705    if (error) {
706        GB_warningf("Error in GB_close: %s", error);
707    }
708}
709
710void gb_abort_and_close_all_DBs() {
711    GBDATA *gb_main;
712    while ((gb_main = gb_remembered_db())) {
713        // abort any open transactions
714        GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
715        while (Main->get_transaction_level()>0) {
716            GB_ERROR error = Main->abort_transaction();
717            if (error) {
718                fprintf(stderr, "Error in gb_abort_and_close_all_DBs: %s\n", error);
719            }
720        }
721        // and close DB
722        GB_close(gb_main);
723    }
724}
725
726// ------------------
727//      read data
728
729long GB_read_int(GBDATA *gbd) {
730    GB_TEST_READ_NUM(gbd, GB_INT, "GB_read_int");
731    return gbd->as_entry()->info.i;
732}
733
734int GB_read_byte(GBDATA *gbd) {
735    GB_TEST_READ_NUM(gbd, GB_BYTE, "GB_read_byte");
736    return gbd->as_entry()->info.i;
737}
738
739GBDATA *GB_read_pointer(GBDATA *gbd) {
740    GB_TEST_READ_PTR(gbd, GB_POINTER, "GB_read_pointer");
741    return gbd->as_entry()->info.ptr;
742}
743
744float GB_read_float(GBDATA *gbd) {
745    XDR   xdrs;
746    float f;
747
748    GB_TEST_READ_NUM(gbd, GB_FLOAT, "GB_read_float");
749    xdrmem_create(&xdrs, &gbd->as_entry()->info.in.data[0], SIZOFINTERN, XDR_DECODE);
750    xdr_float(&xdrs, &f);
751    xdr_destroy(&xdrs);
752
753    gb_assert(f == f); // !nan
754
755    return f;
756}
757
758long GB_read_count(GBDATA *gbd) {
759    return gbd->as_entry()->size();
760}
761
762long GB_read_memuse(GBDATA *gbd) {
763    return gbd->as_entry()->memsize();
764}
765
766#if defined(DEBUG)
767
768#define MIN_CBLISTNODE_SIZE 48 // minimum (found) callbacklist-elementsize
769
770#if defined(DARWIN)
771
772#define CBLISTNODE_SIZE MIN_CBLISTNODE_SIZE // assume known minimum (doesnt really matter; only used in db-browser)
773
774#else // linux:
775
776typedef std::_List_node<gb_callback_list::cbtype> CBLISTNODE_TYPE;
777const size_t CBLISTNODE_SIZE = sizeof(CBLISTNODE_TYPE);
778
779#if defined(ARB_64)
780// ignore smaller 32-bit implementations
781STATIC_ASSERT_ANNOTATED(MIN_CBLISTNODE_SIZE<=CBLISTNODE_SIZE, "MIN_CBLISTNODE_SIZE too big (smaller implementation detected)");
782#endif
783
784#endif
785
786inline long calc_size(gb_callback_list *gbcbl) {
787    return gbcbl
788        ? sizeof(*gbcbl) + gbcbl->callbacks.size()* CBLISTNODE_SIZE
789        : 0;
790}
791inline long calc_size(gb_transaction_save *gbts) {
792    return gbts
793        ? sizeof(*gbts)
794        : 0;
795}
796inline long calc_size(gb_if_entries *gbie) {
797    return gbie
798        ? sizeof(*gbie) + calc_size(GB_IF_ENTRIES_NEXT(gbie))
799        : 0;
800}
801inline long calc_size(GB_REL_IFES *gbri, int table_size) {
802    long size = 0;
803
804    gb_if_entries *ifes;
805    for (int idx = 0; idx<table_size; ++idx) {
806        for (ifes = GB_ENTRIES_ENTRY(gbri, idx);
807             ifes;
808             ifes = GB_IF_ENTRIES_NEXT(ifes))
809        {
810            size += calc_size(ifes);
811        }
812    }
813    return size;
814}
815inline long calc_size(gb_index_files *gbif) {
816    return gbif
817        ? sizeof(*gbif) + calc_size(GB_INDEX_FILES_NEXT(gbif)) + calc_size(GB_INDEX_FILES_ENTRIES(gbif), gbif->hash_table_size)
818        : 0;
819}
820inline long calc_size(gb_db_extended *gbe) {
821    return gbe
822        ? sizeof(*gbe) + calc_size(gbe->callback) + calc_size(gbe->old)
823        : 0;
824}
825inline long calc_size(GBENTRY *gbe) {
826    return gbe
827        ? sizeof(*gbe) + calc_size(gbe->ext)
828        : 0;
829}
830inline long calc_size(GBCONTAINER *gbc) {
831    return gbc
832        ? sizeof(*gbc) + calc_size(gbc->ext) + calc_size(GBCONTAINER_IFS(gbc))
833        : 0;
834}
835
836NOT4PERL long GB_calc_structure_size(GBDATA *gbd) {
837    long size = 0;
838    if (gbd->is_container()) {
839        size = calc_size(gbd->as_container());
840    }
841    else {
842        size = calc_size(gbd->as_entry());
843    }
844    return size;
845}
846
847void GB_SizeInfo::collect(GBDATA *gbd) {
848    if (gbd->is_container()) {
849        ++containers;
850        for (GBDATA *gb_child = GB_child(gbd); gb_child; gb_child = GB_nextChild(gb_child)) {
851            collect(gb_child);
852        }
853    }
854    else {
855        ++terminals;
856        mem += GB_read_memuse(gbd);
857
858        long size;
859        switch (gbd->type()) {
860            case GB_INT:     size = sizeof(int); break;
861            case GB_FLOAT:   size = sizeof(float); break;
862            case GB_BYTE:    size = sizeof(char); break;
863            case GB_POINTER: size = sizeof(GBDATA*); break;
864            case GB_STRING:  size = GB_read_count(gbd); break; // accept 0 sized data for strings
865
866            default:
867                size = GB_read_count(gbd);
868                gb_assert(size>0);                            // terminal w/o data - really?
869                break;
870        }
871        data += size;
872    }
873    structure += GB_calc_structure_size(gbd);
874}
875#endif
876
877GB_CSTR GB_read_pntr(GBDATA *gbd) {
878    GBENTRY    *gbe  = gbd->as_entry();
879    const char *data = gbe->data();
880
881    if (data) {
882        if (gbe->flags.compressed_data) {   // uncompressed data return pntr to database entry
883            char *ca = gb_read_cache(gbe);
884
885            if (!ca) {
886                size_t      size = gbe->uncompressed_size();
887                const char *da   = gb_uncompress_data(gbe, data, size);
888
889                if (da) {
890                    ca = gb_alloc_cache_index(gbe, size);
891                    memcpy(ca, da, size);
892                }
893            }
894            data = ca;
895        }
896    }
897    return data;
898}
899
900int gb_read_nr(GBDATA *gbd) {
901    return gbd->index;
902}
903
904GB_CSTR GB_read_char_pntr(GBDATA *gbd) {
905    GB_TEST_READ_PTR(gbd, GB_STRING, "GB_read_char_pntr");
906    return GB_read_pntr(gbd);
907}
908
909char *GB_read_string(GBDATA *gbd) {
910    GB_TEST_READ_PTR(gbd, GB_STRING, "GB_read_string");
911    const char *d = GB_read_pntr(gbd);
912    if (!d) return NULp;
913    return GB_memdup(d, gbd->as_entry()->size()+1);
914}
915
916size_t GB_read_string_count(GBDATA *gbd) {
917    GB_TEST_READ_NUM(gbd, GB_STRING, "GB_read_string_count");
918    return gbd->as_entry()->size();
919}
920
921long GB_read_bits_count(GBDATA *gbd) {
922    GB_TEST_READ_NUM(gbd, GB_BITS, "GB_read_bits_count");
923    return gbd->as_entry()->size();
924}
925
926GB_CSTR GB_read_bits_pntr(GBDATA *gbd, char c_0, char c_1) {
927    GB_TEST_READ_PTR(gbd, GB_BITS, "GB_read_bits_pntr");
928    GBENTRY *gbe  = gbd->as_entry();
929    long     size = gbe->size();
930    if (size) {
931        // Note: the first 2 bytes of the cached entry contain the currently used encoding (c_0 and c_1)
932
933        char *ca = gb_read_cache(gbe);
934        if (ca) {
935            // check if encoding was the same during last read
936            if (ca[0] == c_0 && ca[1] == c_1) { // yes -> reuse
937                return ca+2;
938            }
939            // no -> re-read from DB
940            GB_flush_cache(gbe);
941            ca = NULp;
942        }
943
944        const char *data = gbe->data();
945        char       *da   = gb_uncompress_bits(data, size, c_0, c_1);
946
947        if (da) {
948            ca    = gb_alloc_cache_index(gbe, size+3); // 2 byte encoding + stored data + terminal '\0'
949            ca[0] = c_0;
950            ca[1] = c_1;
951            memcpy(ca+2, da, size+1);
952            return ca+2;
953        }
954    }
955    return NULp;
956}
957
958char *GB_read_bits(GBDATA *gbd, char c_0, char c_1) {
959    GB_CSTR d = GB_read_bits_pntr(gbd, c_0, c_1);
960    return d ? GB_memdup(d, gbd->as_entry()->size()+1) : NULp;
961}
962
963
964GB_CSTR GB_read_bytes_pntr(GBDATA *gbd) {
965    GB_TEST_READ_PTR(gbd, GB_BYTES, "GB_read_bytes_pntr");
966    return GB_read_pntr(gbd);
967}
968
969long GB_read_bytes_count(GBDATA *gbd) {
970    GB_TEST_READ_NUM(gbd, GB_BYTES, "GB_read_bytes_count");
971    return gbd->as_entry()->size();
972}
973
974char *GB_read_bytes(GBDATA *gbd) {
975    GB_CSTR d = GB_read_bytes_pntr(gbd);
976    return d ? GB_memdup(d, gbd->as_entry()->size()) : NULp;
977}
978
979GB_CUINT4 *GB_read_ints_pntr(GBDATA *gbd) {
980    GB_TEST_READ_PTR(gbd, GB_INTS, "GB_read_ints_pntr");
981    GBENTRY *gbe = gbd->as_entry();
982
983    GB_UINT4 *res;
984    if (gbe->flags.compressed_data) {
985        res = (GB_UINT4 *)GB_read_pntr(gbe);
986    }
987    else {
988        res = (GB_UINT4 *)gbe->data();
989    }
990    if (!res) return NULp;
991
992    if (0x01020304U == htonl(0x01020304U)) {
993        return res;
994    }
995    else {
996        int       size = gbe->size();
997        char     *buf2 = GB_give_other_buffer((char *)res, size<<2);
998        GB_UINT4 *s    = (GB_UINT4 *)res;
999        GB_UINT4 *d    = (GB_UINT4 *)buf2;
1000
1001        for (long i=size; i; i--) {
1002            *(d++) = htonl(*(s++));
1003        }
1004        return (GB_UINT4 *)buf2;
1005    }
1006}
1007
1008long GB_read_ints_count(GBDATA *gbd) { // used by ../PERL_SCRIPTS/SAI/SAI.pm@read_ints_count
1009    GB_TEST_READ_NUM(gbd, GB_INTS, "GB_read_ints_count");
1010    return gbd->as_entry()->size();
1011}
1012
1013GB_UINT4 *GB_read_ints(GBDATA *gbd) {
1014    GB_CUINT4 *i = GB_read_ints_pntr(gbd);
1015    if (!i) return NULp;
1016    return  (GB_UINT4 *)GB_memdup((char *)i, gbd->as_entry()->size()*sizeof(GB_UINT4));
1017}
1018
1019GB_CFLOAT *GB_read_floats_pntr(GBDATA *gbd) {
1020    GB_TEST_READ_PTR(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 NULp;
1046}
1047
1048static long GB_read_floats_count(GBDATA *gbd) {
1049    GB_TEST_READ_NUM(gbd, GB_FLOATS, "GB_read_floats_count");
1050    return gbd->as_entry()->size();
1051}
1052
1053float *GB_read_floats(GBDATA *gbd) { // @@@ only used in unittest - check usage of floats
1054    GB_CFLOAT *f;
1055    f = GB_read_floats_pntr(gbd);
1056    if (!f) return NULp;
1057    return  (float *)GB_memdup((char *)f, gbd->as_entry()->size()*sizeof(float));
1058}
1059
1060char *GB_read_as_string(GBDATA *gbd) {
1061    /*! reads basic db-field types and returns content as text.
1062     * @see GB_write_autoconv_string
1063     * @see GB_readable_as_string
1064     */
1065    switch (gbd->type()) {
1066        case GB_STRING: return GB_read_string(gbd);
1067        case GB_BYTE:   return GBS_global_string_copy("%i", GB_read_byte(gbd));
1068        case GB_INT:    return GBS_global_string_copy("%li", GB_read_int(gbd));
1069        case GB_FLOAT:  return ARB_strdup(ARB_float_2_ascii(GB_read_float(gbd)));
1070        case GB_BITS:   return GB_read_bits(gbd, '0', '1');
1071            /* Be careful : When adding new types here, you have to make sure that
1072             * GB_write_autoconv_string is able to write them back and that this makes sense.
1073             */
1074        default:
1075            gb_assert(!GB_TYPE_readable_as_string(gbd->type()));
1076            return NULp;
1077    }
1078}
1079
1080bool GB_readable_as_string(GBDATA *gbd) {
1081    //! @see GB_TYPE_readable_as_string()
1082    return GB_TYPE_readable_as_string(gbd->type());
1083}
1084
1085
1086inline GB_ERROR cannot_use_fun4entry(const char *fun, GBDATA *gb_entry) {
1087    return GBS_global_string("Error: Cannot use %s() with a field of type %i (field=%s)",
1088                             fun,
1089                             GB_read_type(gb_entry),
1090                             GB_read_key_pntr(gb_entry));
1091}
1092
1093NOT4PERL uint8_t GB_read_lossless_byte(GBDATA *gbd, GB_ERROR& error) {
1094    /*! Reads an uint8_t previously written with GB_write_lossless_byte()
1095     * @param gbd    the DB field
1096     * @param error  result parameter (has to be NULp)
1097     * @result is undefined if error got set; contains read value otherwise
1098     */
1099    gb_assert(!error);
1100    gb_assert(!GB_have_error());
1101    uint8_t result;
1102    switch (gbd->type()) {
1103        case GB_BYTE:
1104            result = GB_read_byte(gbd);
1105            break;
1106
1107        case GB_INT:
1108            result = GB_read_int(gbd);
1109            break;
1110
1111        case GB_FLOAT:
1112            result = GB_read_float(gbd)+.5;
1113            break;
1114
1115        case GB_STRING:
1116            result = atoi(GB_read_char_pntr(gbd));
1117            break;
1118
1119        default:
1120            error  = cannot_use_fun4entry("GB_read_lossless_byte", gbd);
1121            result = 0;
1122            break;
1123    }
1124
1125    if (!error) error = GB_incur_error();
1126    return result;
1127}
1128NOT4PERL int32_t GB_read_lossless_int(GBDATA *gbd, GB_ERROR& error) {
1129    /*! Reads an int32_t previously written with GB_write_lossless_int()
1130     * @param gbd    the DB field
1131     * @param error  result parameter (has to be NULp)
1132     * @result is undefined if error got set; contains read value otherwise
1133     */
1134    gb_assert(!error);
1135    gb_assert(!GB_have_error());
1136    int32_t result;
1137    switch (gbd->type()) {
1138        case GB_INT:
1139            result = GB_read_int(gbd);
1140            break;
1141
1142        case GB_STRING:
1143            result = atoi(GB_read_char_pntr(gbd));
1144            break;
1145
1146        default:
1147            error  = cannot_use_fun4entry("GB_read_lossless_int", gbd);
1148            result = 0;
1149            break;
1150    }
1151
1152    if (!error) error = GB_incur_error();
1153    return result;
1154}
1155NOT4PERL float GB_read_lossless_float(GBDATA *gbd, GB_ERROR& error) {
1156    /*! Reads a float previously written with GB_write_lossless_float()
1157     * @param gbd    the DB field
1158     * @param error  result parameter (has to be NULp)
1159     * @result is undefined if error got set; contains read value otherwise
1160     */
1161    gb_assert(!error);
1162    gb_assert(!GB_have_error());
1163    float result;
1164    switch (gbd->type()) {
1165        case GB_FLOAT:
1166            result = GB_read_float(gbd);
1167            break;
1168
1169        case GB_STRING:
1170            result = GB_atof(GB_read_char_pntr(gbd));
1171            break;
1172
1173        default:
1174            error  = cannot_use_fun4entry("GB_read_lossless_float", gbd);
1175            result = 0;
1176            break;
1177    }
1178
1179    if (!error) error = GB_incur_error();
1180    return result;
1181}
1182
1183// ------------------------------------------------------------
1184//      array type access functions (intended for perl use)
1185
1186long GB_read_from_ints(GBDATA *gbd, long index) { // used by ../PERL_SCRIPTS/SAI/SAI.pm@read_from_ints
1187    static GBDATA    *last_gbd = NULp;
1188    static long       count    = 0;
1189    static GB_CUINT4 *i        = NULp;
1190
1191    if (gbd != last_gbd) {
1192        count    = GB_read_ints_count(gbd);
1193        i        = GB_read_ints_pntr(gbd);
1194        last_gbd = gbd;
1195    }
1196
1197    if (index >= 0 && index < count) {
1198        return i[index];
1199    }
1200    return -1;
1201}
1202
1203double GB_read_from_floats(GBDATA *gbd, long index) { // @@@ unused
1204    static GBDATA    *last_gbd = NULp;
1205    static long       count    = 0;
1206    static GB_CFLOAT *f        = NULp;
1207
1208    if (gbd != last_gbd) {
1209        count    = GB_read_floats_count(gbd);
1210        f        = GB_read_floats_pntr(gbd);
1211        last_gbd = gbd;
1212    }
1213
1214    if (index >= 0 && index < count) {
1215        return f[index];
1216    }
1217    return -1;
1218}
1219
1220// -------------------
1221//      write data
1222
1223static void gb_do_callbacks(GBDATA *gbd) {
1224    gb_assert(GB_MAIN(gbd)->get_transaction_level() < 0); // only use in NO_TRANSACTION_MODE!
1225
1226    while (gbd) {
1227        GBDATA *gbdn = GB_get_father(gbd);
1228        gb_callback_list *cbl = gbd->get_callbacks();
1229        if (cbl && cbl->call(gbd, GB_CB_CHANGED)) {
1230            gb_remove_callbacks_marked_for_deletion(gbd);
1231        }
1232        gbd = gbdn;
1233    }
1234}
1235
1236#define GB_DO_CALLBACKS(gbd) do { if (GB_MAIN(gbd)->get_transaction_level() < 0) gb_do_callbacks(gbd); } while (0)
1237
1238GB_ERROR GB_write_byte(GBDATA *gbd, int i) {
1239    GB_TEST_WRITE(gbd, GB_BYTE, "GB_write_byte");
1240    GBENTRY *gbe = gbd->as_entry();
1241    if (gbe->info.i != i) {
1242        gb_save_extern_data_in_ts(gbe);
1243        gbe->info.i = i & 0xff;
1244        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1245        GB_DO_CALLBACKS(gbe);
1246    }
1247    return NULp;
1248}
1249
1250GB_ERROR GB_write_int(GBDATA *gbd, long i) {
1251    // @@@ GB_write_int should be GB_ERROR GB_write_int(GBDATA *gbd,int32_t i)
1252
1253    GB_TEST_WRITE(gbd, GB_INT, "GB_write_int");
1254    if ((long)((int32_t)i) != i) {
1255        gb_assert(0);
1256        GB_warningf("Warning: 64bit incompatibility detected\nNo data written to '%s'\n", GB_get_db_path(gbd));
1257        return "GB_INT out of range (signed, 32bit)";
1258    }
1259    GBENTRY *gbe = gbd->as_entry();
1260    if (gbe->info.i != (int32_t)i) {
1261        gb_save_extern_data_in_ts(gbe);
1262        gbe->info.i = i;
1263        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1264        GB_DO_CALLBACKS(gbe);
1265    }
1266    return NULp;
1267}
1268
1269GB_ERROR GB_write_pointer(GBDATA *gbd, GBDATA *pointer) {
1270    GB_TEST_WRITE(gbd, GB_POINTER, "GB_write_pointer");
1271    GBENTRY *gbe = gbd->as_entry();
1272    if (gbe->info.ptr != pointer) {
1273        gb_save_extern_data_in_ts(gbe);
1274        gbe->info.ptr = pointer;
1275        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1276        GB_DO_CALLBACKS(gbe);
1277    }
1278    return NULp;
1279}
1280
1281GB_ERROR GB_write_float(GBDATA *gbd, float f) {
1282    gb_assert(f == f); // !nan
1283    GB_TEST_WRITE(gbd, GB_FLOAT, "GB_write_float");
1284
1285    if (GB_read_float(gbd) != f) {
1286        GBENTRY *gbe = gbd->as_entry();
1287        gb_save_extern_data_in_ts(gbe);
1288
1289        XDR xdrs;
1290        xdrmem_create(&xdrs, &gbe->info.in.data[0], SIZOFINTERN, XDR_ENCODE);
1291        xdr_float(&xdrs, &f);
1292        xdr_destroy(&xdrs);
1293
1294        gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1295        GB_DO_CALLBACKS(gbe);
1296    }
1297    return NULp;
1298}
1299
1300inline void gb_save_extern_data_in_ts__and_uncache(GBENTRY *gbe) {
1301    // order of the following commands is important:
1302    // gb_save_extern_data_in_ts may recreate a cache-entry under the following conditions:
1303    //  - gbe is indexed AND
1304    //  - gbe is stored compressed (and compression really takes place)
1305    //
1306    // Calling these commands in reversed order (as done until [15622])
1307    // had the following effects:
1308    // - writing modified data to an entry had no effect (cache still contained old value)
1309    // - after saving and reloading the database, the modified value was effective
1310    //
1311    // Happened e.g. when copying an SAI (if dictionary compression occurred for SAI/name). See #742.
1312
1313    gb_save_extern_data_in_ts(gbe); // Warning: might undo effect of gb_uncache if called afterwards
1314    gb_uncache(gbe);
1315}
1316
1317GB_ERROR gb_write_compressed_pntr(GBENTRY *gbe, const char *s, long memsize, long stored_size) {
1318    gb_save_extern_data_in_ts__and_uncache(gbe);
1319
1320    gbe->flags.compressed_data = 1;
1321
1322    gb_assert(!gbe->cache_index); // insert_data() will recreate the cache-entry (if entry is_indexed)
1323    gbe->insert_data((char *)s, stored_size, (size_t)memsize);
1324    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1325
1326    return NULp;
1327}
1328
1329int gb_get_compression_mask(GB_MAIN_TYPE *Main, GBQUARK key, int gb_type) {
1330    gb_Key *ks = &Main->keys[key];
1331    int     compression_mask;
1332
1333    if (ks->gb_key_disabled) {
1334        compression_mask = 0;
1335    }
1336    else {
1337        if (!ks->gb_key) gb_load_single_key_data(Main->gb_main(), key);
1338        compression_mask = gb_convert_type_2_compression_flags[gb_type] & ks->compression_mask;
1339    }
1340
1341    return compression_mask;
1342}
1343
1344GB_ERROR GB_write_pntr(GBDATA *gbd, const char *s, size_t bytes_size, size_t stored_size) {
1345    // 'bytes_size' is the size of what 's' points to.
1346    // 'stored_size' is the size-information written into the DB
1347    //
1348    // e.g. for strings : stored_size = bytes_size-1, cause stored_size is string len,
1349    //                    but bytes_size includes zero byte.
1350
1351    GBENTRY      *gbe  = gbd->as_entry();
1352    GB_MAIN_TYPE *Main = GB_MAIN(gbe);
1353    GBQUARK       key  = GB_KEY_QUARK(gbe);
1354    GB_TYPES      type = gbe->type();
1355
1356    gb_assert(implicated(type == GB_STRING, stored_size == bytes_size-1)); // size constraint for strings not fulfilled!
1357
1358    gb_save_extern_data_in_ts__and_uncache(gbe);
1359
1360    int compression_mask = gb_get_compression_mask(Main, key, type);
1361
1362    const char *d;
1363    size_t      memsize;
1364    if (compression_mask) {
1365        d = gb_compress_data(gbe, key, s, bytes_size, &memsize, compression_mask, false);
1366    }
1367    else {
1368        d = NULp;
1369    }
1370    if (d) {
1371        gbe->flags.compressed_data = 1;
1372    }
1373    else {
1374        d = s;
1375        gbe->flags.compressed_data = 0;
1376        memsize = bytes_size;
1377    }
1378
1379    gb_assert(!gbe->cache_index); // insert_data() will recreate the cache-entry (if entry is_indexed)
1380    gbe->insert_data(d, stored_size, memsize);
1381    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1382    GB_DO_CALLBACKS(gbe);
1383
1384    return NULp;
1385}
1386
1387GB_ERROR GB_write_string(GBDATA *gbd, const char *s) {
1388    GBENTRY *gbe = gbd->as_entry();
1389    GB_TEST_WRITE(gbe, GB_STRING, "GB_write_string");
1390    GB_TEST_NON_BUFFER(s, "GB_write_string");        // compress would destroy the other buffer
1391
1392    if (!s) s = "";
1393    size_t size = strlen(s);
1394
1395    // no zero len strings allowed
1396    if (gbe->memsize() && (size == gbe->size())) {
1397        if (!strcmp(s, GB_read_pntr(gbe))) return NULp;
1398    }
1399#if defined(DEBUG) && 0
1400    // check for error (in compression)
1401    {
1402        GB_ERROR error = GB_write_pntr(gbe, s, size+1, size);
1403        if (!error) {
1404            char *check = GB_read_string(gbe);
1405
1406            gb_assert(check);
1407            gb_assert(strcmp(check, s) == 0);
1408
1409            free(check);
1410        }
1411        return error;
1412    }
1413#else
1414    return GB_write_pntr(gbe, s, size+1, size);
1415#endif // DEBUG
1416}
1417
1418GB_ERROR GB_write_bits(GBDATA *gbd, const char *bits, long size, const char *c_0) {
1419    GBENTRY *gbe = gbd->as_entry();
1420    GB_TEST_WRITE(gbe, GB_BITS, "GB_write_bits");
1421    GB_TEST_NON_BUFFER(bits, "GB_write_bits");       // compress would destroy the other buffer
1422    gb_save_extern_data_in_ts__and_uncache(gbe);
1423
1424    long  memsize;
1425    char *d = gb_compress_bits(bits, size, (const unsigned char *)c_0, &memsize);
1426
1427    gbe->flags.compressed_data = 1;
1428    gbe->insert_data(d, size, memsize);
1429    gb_touch_entry(gbe, GB_NORMAL_CHANGE);
1430    GB_DO_CALLBACKS(gbe);
1431    return NULp;
1432}
1433
1434GB_ERROR GB_write_bytes(GBDATA *gbd, const char *s, long size) {
1435    GB_TEST_WRITE(gbd, GB_BYTES, "GB_write_bytes");
1436    return GB_write_pntr(gbd, s, size, size);
1437}
1438
1439GB_ERROR GB_write_ints(GBDATA *gbd, const GB_UINT4 *i, long size) {
1440    GB_TEST_WRITE(gbd, GB_INTS, "GB_write_ints");
1441    GB_TEST_NON_BUFFER((char *)i, "GB_write_ints");  // compress would destroy the other buffer
1442
1443    if (0x01020304 != htonl((GB_UINT4)0x01020304)) {
1444        long      j;
1445        char     *buf2 = GB_give_other_buffer((char *)i, size<<2);
1446        GB_UINT4 *s    = (GB_UINT4 *)i;
1447        GB_UINT4 *d    = (GB_UINT4 *)buf2;
1448
1449        for (j=size; j; j--) {
1450            *(d++) = htonl(*(s++));
1451        }
1452        i = (GB_UINT4 *)buf2;
1453    }
1454    return GB_write_pntr(gbd, (char *)i, size* 4 /* sizeof(long4) */, size);
1455}
1456
1457GB_ERROR GB_write_floats(GBDATA *gbd, const float *f, long size) {
1458    long fullsize = size * sizeof(float);
1459    GB_TEST_WRITE(gbd, GB_FLOATS, "GB_write_floats");
1460    GB_TEST_NON_BUFFER((char *)f, "GB_write_floats"); // compress would destroy the other buffer
1461
1462    {
1463        XDR    xdrs;
1464        long   i;
1465        char  *buf2 = GB_give_other_buffer((char *)f, fullsize);
1466        float *s    = (float *)f;
1467
1468        xdrmem_create(&xdrs, buf2, (int)fullsize, XDR_ENCODE);
1469        for (i=size; i; i--) {
1470            xdr_float(&xdrs, s);
1471            s++;
1472        }
1473        xdr_destroy (&xdrs);
1474        f = (float*)(void*)buf2;
1475    }
1476    return GB_write_pntr(gbd, (char *)f, size*sizeof(float), size);
1477}
1478
1479GB_ERROR GB_write_autoconv_string(GBDATA *gbd, const char *val) {
1480    /*! writes data to database field using automatic conversion.
1481     *  Warning: Conversion may cause silent data-loss!
1482     *           (e.g. writing "hello" to a numeric db-field results in zero content)
1483     *
1484     *  Writing back the unmodified(!) result of GB_read_as_string will not cause data loss.
1485     *
1486     *  Consider using the GB_write_lossless_...() functions below (and their counterparts GB_read_lossless_...()).
1487     *
1488     * @@@ refer to GBT_write_int_converted / GBT_write_float_converted
1489     */
1490    switch (gbd->type()) {
1491        case GB_STRING: return GB_write_string(gbd, val);
1492        case GB_BYTE:   return GB_write_byte(gbd, atoi(val));
1493        case GB_INT:    return GB_write_int(gbd, atoi(val));
1494        case GB_FLOAT:  {
1495            float f;
1496            GB_ERROR error = GB_safe_atof(val, &f);
1497            return error ? error : GB_write_float(gbd, f);
1498        }
1499        case GB_BITS:   return GB_write_bits(gbd, val, strlen(val), "0");
1500        default: return GBS_global_string("Error: You cannot use GB_write_autoconv_string on this type of entry (%s)", GB_read_key_pntr(gbd));
1501    }
1502}
1503
1504GB_ERROR GB_write_lossless_byte(GBDATA *gbd, uint8_t byte) {
1505    /*! Writes an uint8_t to a database field capable to store any value w/o loss.
1506     *  @return error otherwise
1507     *  The corresponding field filter is FIELD_FILTER_BYTE_WRITEABLE.
1508     */
1509    switch (gbd->type()) {
1510        case GB_BYTE:   return GB_write_byte(gbd, byte);
1511        case GB_INT:    return GB_write_int(gbd, byte);
1512        case GB_FLOAT:  return GB_write_float(gbd, byte);
1513        case GB_STRING: {
1514            char buffer[4];
1515            sprintf(buffer, "%u", unsigned(byte));
1516            return GB_write_string(gbd, buffer);
1517        }
1518
1519        default: return cannot_use_fun4entry("GB_write_lossless_byte", gbd);
1520    }
1521}
1522
1523GB_ERROR GB_write_lossless_int(GBDATA *gbd, int32_t i) {
1524    /*! Writes an int32_t to a database field capable to store any value w/o loss.
1525     *  @return error otherwise
1526     *  The corresponding field filter is FIELD_FILTER_INT_WRITEABLE.
1527     */
1528
1529    switch (gbd->type()) {
1530        case GB_INT:    return GB_write_int(gbd, i);
1531        case GB_STRING: {
1532            const int BUFSIZE = 30;
1533            char      buffer[BUFSIZE];
1534#if defined(ASSERTION_USED)
1535            int printed =
1536#endif
1537                sprintf(buffer, "%i", i);
1538            gb_assert(printed<BUFSIZE);
1539            return GB_write_string(gbd, buffer);
1540        }
1541
1542        default: return cannot_use_fun4entry("GB_write_lossless_int", gbd);
1543    }
1544}
1545
1546GB_ERROR GB_write_lossless_float(GBDATA *gbd, float f) {
1547    /*! Writes a float to a database field capable to store any value w/o loss.
1548     *  @return error otherwise
1549     *  The corresponding field filter is FIELD_FILTER_FLOAT_WRITEABLE.
1550     */
1551
1552    switch (gbd->type()) {
1553        case GB_FLOAT:  return GB_write_float(gbd, f);
1554        case GB_STRING: {
1555            const int BUFSIZE = 30;
1556            char      buffer[BUFSIZE];
1557#if defined(ASSERTION_USED)
1558            int printed =
1559#endif
1560                sprintf(buffer, "%e", f);
1561            gb_assert(printed<BUFSIZE);
1562            return GB_write_string(gbd, buffer);
1563        }
1564
1565        default: return cannot_use_fun4entry("GB_write_lossless_float", gbd);
1566    }
1567}
1568
1569// ---------------------------
1570//      security functions
1571
1572int GB_read_security_write(GBDATA *gbd) {
1573    GB_test_transaction(gbd);
1574    return GB_GET_SECURITY_WRITE(gbd);
1575}
1576int GB_read_security_read(GBDATA *gbd) {
1577    GB_test_transaction(gbd);
1578    return GB_GET_SECURITY_READ(gbd);
1579}
1580int GB_read_security_delete(GBDATA *gbd) {
1581    GB_test_transaction(gbd);
1582    return GB_GET_SECURITY_DELETE(gbd);
1583}
1584GB_ERROR GB_write_security_write(GBDATA *gbd, unsigned long level) {
1585    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1586    GB_test_transaction(Main);
1587
1588    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level) return gb_security_error(gbd);
1589    if (GB_GET_SECURITY_WRITE(gbd) == level) return NULp;
1590    GB_PUT_SECURITY_WRITE(gbd, level);
1591    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1592    GB_DO_CALLBACKS(gbd);
1593    return NULp;
1594}
1595GB_ERROR GB_write_security_read(GBDATA *gbd, unsigned long level) { // @@@ unused
1596    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1597    GB_test_transaction(Main);
1598    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level) return gb_security_error(gbd);
1599    if (GB_GET_SECURITY_READ(gbd) == level) return NULp;
1600    GB_PUT_SECURITY_READ(gbd, level);
1601    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1602    GB_DO_CALLBACKS(gbd);
1603    return NULp;
1604}
1605GB_ERROR GB_write_security_delete(GBDATA *gbd, unsigned long level) {
1606    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1607    GB_test_transaction(Main);
1608    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level) return gb_security_error(gbd);
1609    if (GB_GET_SECURITY_DELETE(gbd) == level) return NULp;
1610    GB_PUT_SECURITY_DELETE(gbd, level);
1611    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1612    GB_DO_CALLBACKS(gbd);
1613    return NULp;
1614}
1615GB_ERROR GB_write_security_levels(GBDATA *gbd, unsigned long readlevel, unsigned long writelevel, unsigned long deletelevel) { // @@@ unused
1616    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1617    GB_test_transaction(Main);
1618    if (GB_GET_SECURITY_WRITE(gbd)>Main->security_level) return gb_security_error(gbd);
1619    GB_PUT_SECURITY_WRITE(gbd, writelevel);
1620    GB_PUT_SECURITY_READ(gbd, readlevel);
1621    GB_PUT_SECURITY_DELETE(gbd, deletelevel);
1622    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
1623    GB_DO_CALLBACKS(gbd);
1624    return NULp;
1625}
1626
1627void GB_change_my_security(GBDATA *gbd, int level) {
1628    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
1629    Main->security_level = level<0 ? 0 : (level>7 ? 7 : level);
1630}
1631
1632int GB_securityLevel::whats_my_security() const {
1633    GB_MAIN_TYPE *Main = GB_MAIN(gbdata);
1634    return Main->security_level;
1635}
1636void GB_securityLevel::change_my_security(int level) {
1637    GB_change_my_security(gbdata, level);
1638}
1639
1640// ------------------------
1641//      Key information
1642
1643GB_TYPES GB_read_type(GBDATA *gbd) {
1644    GB_test_transaction(gbd);
1645    return gbd->type();
1646}
1647
1648bool GB_is_container(GBDATA *gbd) {
1649    return gbd && gbd->is_container();
1650}
1651
1652char *GB_read_key(GBDATA *gbd) {
1653    return ARB_strdup(GB_read_key_pntr(gbd));
1654}
1655
1656GB_CSTR GB_read_key_pntr(GBDATA *gbd) {
1657    GB_CSTR k;
1658    GB_test_transaction(gbd);
1659    k         = GB_KEY(gbd);
1660    if (!k) k = GBS_global_string("<invalid key (quark=%i)>", GB_KEY_QUARK(gbd));
1661    return k;
1662}
1663
1664GB_CSTR gb_read_key_pntr(GBDATA *gbd) {
1665    return GB_KEY(gbd);
1666}
1667
1668GBQUARK gb_find_or_create_quark(GB_MAIN_TYPE *Main, const char *key) {
1669    //! @return existing or newly created quark for 'key'
1670    GBQUARK quark = key2quark(Main, key);
1671    if (!quark) {
1672        if (!key[0]) GBK_terminate("Attempt to create quark from empty key");
1673        quark = gb_create_key(Main, key, true);
1674    }
1675    return quark;
1676}
1677
1678GBQUARK gb_find_or_create_NULL_quark(GB_MAIN_TYPE *Main, const char *key) {
1679    // similar to gb_find_or_create_quark,
1680    // but if 'key' is NULp, quark 0 will be returned.
1681    //
1682    // Use this function with care.
1683    //
1684    // Known good use:
1685    // - create main entry and its dummy father via gb_make_container()
1686
1687    return key ? gb_find_or_create_quark(Main, key) : 0;
1688}
1689
1690GBQUARK GB_find_existing_quark(GBDATA *gbd, const char *key) {
1691    //! @return existing quark for 'key' (-1 if key is NULp, 0 if key is unknown)
1692    return key2quark(GB_MAIN(gbd), key);
1693}
1694
1695GBQUARK GB_find_or_create_quark(GBDATA *gbd, const char *key) {
1696    //! @return existing or newly created quark for 'key'
1697    return gb_find_or_create_quark(GB_MAIN(gbd), key);
1698}
1699
1700
1701// ---------------------------------------------
1702
1703GBQUARK GB_get_quark(GBDATA *gbd) {
1704    return GB_KEY_QUARK(gbd);
1705}
1706
1707bool GB_has_key(GBDATA *gbd, const char *key) {
1708    GBQUARK quark = GB_find_existing_quark(gbd, key);
1709    return quark && (quark == GB_get_quark(gbd));
1710}
1711
1712// ---------------------------------------------
1713
1714long GB_read_clock(GBDATA *gbd) {
1715    if (GB_ARRAY_FLAGS(gbd).changed) return GB_MAIN(gbd)->clock;
1716    return gbd->update_date();
1717}
1718
1719// ---------------------------------------------
1720//      Get and check the database hierarchy
1721
1722GBDATA *GB_get_father(GBDATA *gbd) {
1723    // Get the father of an entry
1724    GB_test_transaction(gbd);
1725    return gbd->get_father();
1726}
1727
1728GBDATA *GB_get_grandfather(GBDATA *gbd) {
1729    GB_test_transaction(gbd);
1730
1731    GBDATA *gb_grandpa = GB_FATHER(gbd);
1732    if (gb_grandpa) {
1733        gb_grandpa = GB_FATHER(gb_grandpa);
1734        if (gb_grandpa && !GB_FATHER(gb_grandpa)) gb_grandpa = NULp; // never return dummy_father of root container
1735    }
1736    return gb_grandpa;
1737}
1738
1739// Get the root entry (gb_main)
1740GBDATA *GB_get_root(GBDATA *gbd) { return GB_MAIN(gbd)->gb_main(); }
1741GBCONTAINER *gb_get_root(GBENTRY *gbe) { return GB_MAIN(gbe)->root_container; }
1742GBCONTAINER *gb_get_root(GBCONTAINER *gbc) { return GB_MAIN(gbc)->root_container; }
1743
1744bool GB_is_ancestor_of(GBDATA *gb_ancestor, GBDATA *gb_descendant) {
1745    // Test whether 'gb_descendant' is a subentry of 'gb_ancestor'.
1746    // Note: returns false if gb_descendant == gb_ancestor!
1747
1748    GB_test_transaction(gb_descendant);
1749    if (gb_ancestor->is_container()) { // otherwise it contains nothing!
1750        for (GBDATA *gb_up = gb_descendant->get_father();
1751             gb_up;
1752             gb_up = gb_up->get_father())
1753        {
1754            if (gb_up == gb_ancestor) return true;
1755        }
1756    }
1757    return false;
1758}
1759
1760// --------------------------
1761//      create and rename
1762
1763GBENTRY *gb_create(GBCONTAINER *father, const char *key, GB_TYPES type) {
1764    GBENTRY *gbe = gb_make_entry(father, key, -1, 0, type);
1765    gb_touch_header(GB_FATHER(gbe));
1766    gb_touch_entry(gbe, GB_CREATED);
1767
1768    gb_assert(GB_ARRAY_FLAGS(gbe).changed < GB_DELETED); // happens sometimes -> needs debugging
1769
1770    return gbe;
1771}
1772
1773GBCONTAINER *gb_create_container(GBCONTAINER *father, const char *key) {
1774    // Create a container, do not check anything
1775    GBCONTAINER *gbc = gb_make_container(father, key, -1, 0);
1776    gb_touch_header(GB_FATHER(gbc));
1777    gb_touch_entry(gbc, GB_CREATED);
1778    return gbc;
1779}
1780
1781GBDATA *GB_create(GBDATA *father, const char *key, GB_TYPES type) {
1782    /*! Create a DB entry
1783     *
1784     * @param father container to create DB field in
1785     * @param key name of field
1786     * @param type field type
1787     *
1788     * @return
1789     * - created DB entry
1790     * - NULp on failure (error is exported then)
1791     *
1792     * @see GB_create_container()
1793     */
1794
1795    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
1796
1797    if (GB_ERROR keyerr = GB_check_key(key)) {
1798        GB_export_error(keyerr);
1799        return NULp;
1800    }
1801
1802    if (type == GB_DB) {
1803        gb_assert(type != GB_DB); // you like to use GB_create_container!
1804        GB_export_error("GB_create: can't create containers");
1805        return NULp;
1806    }
1807
1808    if (!father) {
1809        GB_internal_errorf("GB_create error in GB_create:\nno father (key = '%s')", key);
1810        return NULp;
1811    }
1812    GB_test_transaction(father);
1813    if (father->is_entry()) {
1814        GB_export_errorf("while creating '%s': father (%s) is not of GB_DB type (%i)",
1815                         key, GB_read_key_pntr(father), father->type());
1816        return NULp;
1817    }
1818
1819    if (type == GB_POINTER) {
1820        if (!GB_in_temporary_branch(father)) {
1821            GB_export_error("GB_create: pointers only allowed in temporary branches");
1822            return NULp;
1823        }
1824    }
1825
1826    return gb_create(father->expect_container(), key, type);
1827}
1828
1829GBDATA *GB_create_container(GBDATA *father, const char *key) {
1830    /*! Create a new DB container
1831     *
1832     * @param father parent container
1833     * @param key name of created container
1834     *
1835     * @return
1836     * - created container
1837     * - NULp on failure (error is exported then)
1838     *
1839     * @see GB_create()
1840     */
1841
1842    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
1843
1844    if (GB_ERROR keyerr = GB_check_key(key)) {
1845        GB_export_error(keyerr);
1846        return NULp;
1847    }
1848
1849    if ((*key == '\0')) {
1850        GB_export_error("GB_create error: empty key");
1851        return NULp;
1852    }
1853    if (!father) {
1854        GB_internal_errorf("GB_create error in GB_create:\nno father (key = '%s')", key);
1855        return NULp;
1856    }
1857
1858    GB_test_transaction(father);
1859    return gb_create_container(father->expect_container(), key);
1860}
1861
1862// ----------------------
1863//      recompression
1864
1865
1866static GB_ERROR gb_set_compression(GBDATA *source) {
1867    // @@@ rename gb_set_compression into gb_recompress (misleading name)
1868
1869    GB_ERROR error = NULp;
1870    GB_test_transaction(source);
1871
1872    switch (source->type()) {
1873        case GB_STRING: {
1874            char *str = GB_read_string(source);
1875            GB_write_string(source, "");
1876            GB_write_string(source, str);
1877            free(str);
1878            break;
1879        }
1880        case GB_BITS:
1881        case GB_BYTES:
1882        case GB_INTS:
1883        case GB_FLOATS:
1884            break;
1885        case GB_DB:
1886            for (GBDATA *gb_child = GB_child(source); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
1887                error = gb_set_compression(gb_child);
1888            }
1889            break;
1890        default:
1891            break;
1892    }
1893    return error;
1894}
1895
1896bool GB_allow_compression(GBDATA *gb_main, bool allow_compression) {
1897    GB_MAIN_TYPE *Main      = GB_MAIN(gb_main);
1898    int           prev_mask = Main->compression_mask;
1899    Main->compression_mask  = allow_compression ? -1 : 0;
1900
1901    return prev_mask == 0 ? false : true;
1902}
1903
1904GB_ERROR GB_test_delete_possible(GBDATA *gb_obj) {
1905    GB_test_transaction(gb_obj);
1906    int sec_obj  = GB_GET_SECURITY_DELETE(gb_obj);
1907    int sec_main = GB_MAIN(gb_obj)->security_level;
1908    if (sec_obj > sec_main) {
1909        return GBS_global_string("Security error: deleting entry '%s' not permitted\n"
1910                                 "(entry has security level %i, your security level is only %i)",
1911                                 GB_read_key_pntr(gb_obj), sec_obj, sec_main);
1912    }
1913    return NULp;
1914}
1915
1916GB_ERROR GB_delete(GBDATA*& source) {
1917    /*! delete a database entry.
1918     * MAY(!) set source to NULp.
1919     */
1920
1921    GB_ERROR error = GB_test_delete_possible(source);
1922    if (!error) {
1923        GBDATA *gb_main = GB_get_root(source);
1924
1925        if (source->flags.compressed_data) {
1926            bool was_allowed = GB_allow_compression(gb_main, false);
1927            gb_set_compression(source); // write data w/o compression (otherwise GB_read_old_value... won't work)
1928            GB_allow_compression(gb_main, was_allowed);
1929        }
1930
1931        {
1932            GB_MAIN_TYPE *Main = GB_MAIN(source);
1933            if (Main->get_transaction_level() < 0) { // no transaction mode
1934                gb_delete_entry(source);
1935                Main->call_pending_callbacks();
1936            }
1937            else {
1938                gb_touch_entry(source, GB_DELETED);
1939            }
1940        }
1941    }
1942    RETURN_ERROR(error);
1943}
1944
1945GB_ERROR gb_delete_force(GBDATA *source) {
1946    // delete always
1947    gb_touch_entry(source, GB_DELETED);
1948    return NULp;
1949}
1950
1951
1952// ------------------
1953//      Copy data
1954
1955enum CopyMode {
1956    // bit values:
1957    CM_DROP_PROTECTION    = 1,  // -> reset protection (use current default?).
1958    CM_DROP_MARKS         = 2,  // -> clear marks of copied entries (other user flags are always dropped, e.g. query flag). this only affects containers!
1959    CM_SKIP_TEMP          = 4,  // -> do not copy temporarily entries (and their subentries).
1960    CM_DROP_TEMPSTATE     = 8,  // w/o effect if CM_SKIP_TEMP; otherwise clear temporary state of copied entries if set.
1961    CM_OVERWRITE_EXISTING = 16, // -> overwrite existing sub-entries (otherwise keeps existing sub-entries + adds new entries)
1962
1963    // convenience definitions:
1964    CM_COPY_TRADITIONAL = CM_DROP_PROTECTION|CM_DROP_MARKS|CM_DROP_TEMPSTATE, // traditional behavior of GB_copy_dropProtectMarksAndTempstate
1965    CM_COPY_WITHPROTECT = CM_COPY_TRADITIONAL & ~CM_DROP_PROTECTION,          // like traditional, but dont drop protection (note: not dropping protection was the first attempt to fix copying)
1966    CM_COPY_STANDARD    = CM_SKIP_TEMP,
1967    CM_COPY_FULL        = 0,                                                  // copy everything
1968};
1969
1970static GB_ERROR gb_copy_explicit(GBDATA *dest, GBDATA *source, CopyMode mode);
1971
1972static GBDATA *gb_clone_explicit(GBCONTAINER *gb_destCont, GBDATA *gb_source, CopyMode mode) {
1973    /*! copy a clone of 'gb_source' into container 'gb_destCont' according to 'mode'.
1974     * @return pointer to copy on success or NULp (in which case an error may be exported).
1975     */
1976    GB_test_transaction(gb_source);
1977    gb_assert(!GB_have_error());                      // illegal to enter this function when an error is exported!
1978    gb_assert(!gb_destCont->flags2.folded_container); // please unfold in caller!
1979
1980    if (mode&CM_SKIP_TEMP && GB_is_temporary(gb_source)) return NULp; // skip temporaries (as GB_save does)
1981
1982    const char *key = GB_read_key_pntr(gb_source);
1983    GBDATA     *gb_clone;
1984    if (gb_source->is_container()) {
1985        gb_assert(!GB_is_ancestor_of(gb_source, gb_destCont)); // (for performance reasons this has to be guaranteed by caller)
1986
1987        if (strcmp(GB_SYSTEM_FOLDER, key) == 0) return NULp; // never ever clone system folder
1988
1989        gb_clone = GB_create_container(gb_destCont, key);
1990        if (gb_clone) gb_create_header_array(gb_clone->as_container(), gb_source->as_container()->d.size);
1991        else          GB_export_errorf("failed to clone container (Reason: %s)", GB_await_error());
1992    }
1993    else gb_clone = GB_create(gb_destCont, key, gb_source->type());
1994
1995    if (gb_clone) {
1996        // target is a basic field or an empty container -> behavior of normal copy and overlay-copy is identical
1997        mode = CopyMode(mode&~CM_OVERWRITE_EXISTING); // -> drop overwrite mode
1998
1999        GB_ERROR error = gb_copy_explicit(gb_clone, gb_source, mode);
2000        if (error) {
2001            IF_ASSERTION_USED(GB_ERROR derror =)GB_delete(gb_clone);
2002            gb_assert(!derror);
2003            gb_clone = NULp;
2004            GB_export_error(error);
2005        }
2006    }
2007
2008    gb_assert(contradicted(gb_clone, GB_have_error()));
2009    return gb_clone;
2010}
2011
2012static GB_ERROR gb_copy_explicit(GBDATA *dest, GBDATA *source, CopyMode mode) {
2013    GB_ERROR error = NULp;
2014    GB_test_transaction(source);
2015
2016    if (mode&CM_SKIP_TEMP) {
2017        if (GB_is_temporary(source)) {
2018            return "logic error: it's too late to skip copy of temporary entry"; // has to be done by caller!
2019        }
2020#if defined(ASSERTION_USED)
2021        bool overwriteTempTarget = GB_is_temporary(dest) && (mode&CM_OVERWRITE_EXISTING);
2022        gb_assert(!overwriteTempTarget); // currently is permitted. may be problematic.
2023#endif
2024    }
2025
2026    GB_TYPES type = source->type();
2027    if (dest->type() != type) {
2028        return GB_export_errorf("incompatible types in gb_copy_explicit (source %s:%u != %s:%u",
2029                                GB_read_key_pntr(source), type, GB_read_key_pntr(dest), dest->type());
2030    }
2031
2032    switch (type) {
2033        case GB_INT:
2034            error = GB_write_int(dest, GB_read_int(source));
2035            break;
2036        case GB_FLOAT:
2037            error = GB_write_float(dest, GB_read_float(source));
2038            break;
2039        case GB_BYTE:
2040            error = GB_write_byte(dest, GB_read_byte(source));
2041            break;
2042        case GB_STRING:     // No local compression
2043            error = GB_write_string(dest, GB_read_char_pntr(source));
2044            break;
2045        case GB_OBSOLETE:
2046            error = GB_set_temporary(dest); // exclude obsolete type from next save
2047            break;
2048        case GB_BITS:       // only local compressions for the following types
2049        case GB_BYTES:
2050        case GB_INTS:
2051        case GB_FLOATS: {
2052            GBENTRY *source_entry = source->as_entry();
2053            GBENTRY *dest_entry   = dest->as_entry();
2054
2055            gb_save_extern_data_in_ts(dest_entry);
2056            dest_entry->insert_data(source_entry->data(), source_entry->size(), source_entry->memsize());
2057
2058            dest->flags.compressed_data = source->flags.compressed_data;
2059            break;
2060        }
2061        case GB_DB: {
2062            if (!dest->is_container()) {
2063                GB_ERROR err = GB_export_errorf("GB_COPY Type conflict %s:%i != %s:%i",
2064                                                GB_read_key_pntr(dest), dest->type(), GB_read_key_pntr(source), GB_DB);
2065                GB_internal_error(err);
2066                return err;
2067            }
2068
2069            GBCONTAINER *destc   = dest->as_container();
2070            GBCONTAINER *sourcec = source->as_container();
2071
2072            if (!(mode&CM_DROP_MARKS)) GB_write_flag(destc, GB_read_flag(sourcec)); // preserve mark flags
2073
2074            if (sourcec->flags2.folded_container) gb_unfold(sourcec, -1, -1);
2075            if (destc->flags2.folded_container)   gb_unfold(destc, 0, -1);
2076
2077            gb_assert(!GB_is_ancestor_of(source, dest)); // (for performance reasons this has to be guaranteed by caller, use gb_copy_checked?)
2078
2079            if (mode&CM_OVERWRITE_EXISTING) { // overlay childs of destination with childs of source
2080                std::set<GBQUARK> keyHandled;
2081                for (GBDATA *gb_child = GB_child(sourcec); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
2082                    GBQUARK quark = GB_KEY_QUARK(gb_child);
2083                    if (keyHandled.find(quark) == keyHandled.end()) { // handle each quark once
2084                        GBDATA     *gb_entry = gb_child;
2085                        const char *key      = GB_KEY(gb_entry);
2086                        GBDATA     *gb_exist = GB_entry(destc, key);
2087
2088                        while (gb_entry && gb_exist && !error) {
2089                            // @@@ skip over temporary entries here?
2090
2091                            // overlay subentries pairwise:
2092                            error = gb_copy_explicit(gb_exist, gb_entry, mode);
2093                            if (!error) {
2094                                gb_exist = GB_nextEntry(gb_exist);
2095                                gb_entry = GB_nextEntry(gb_entry);
2096                            }
2097                        }
2098
2099                        while (gb_entry && !error) {
2100                            error = GB_incur_error_if(!gb_clone_explicit(destc, gb_entry, mode));
2101                            if (!error) gb_entry = GB_nextEntry(gb_entry);
2102                        }
2103
2104                        gb_assert(implicated(gb_entry, error));
2105
2106                        keyHandled.insert(quark);
2107                    }
2108                }
2109            }
2110            else { // copy all childs to destination
2111                for (GBDATA *gb_child = GB_child(sourcec); gb_child && !error; gb_child = GB_nextChild(gb_child)) {
2112                    error = GB_incur_error_if(!gb_clone_explicit(destc, gb_child, mode));
2113                }
2114            }
2115
2116            destc->flags3 = sourcec->flags3;
2117            break;
2118        }
2119        default:
2120            error = GB_export_error("error in gb_copy_explicit: unhandled type");
2121    }
2122
2123    if (!error) {
2124        gb_touch_entry(dest, GB_NORMAL_CHANGE);
2125
2126        dest->flags.security_read = source->flags.security_read; // (note: read security generally has no effect)
2127        if (!(mode&CM_DROP_PROTECTION)) { // preserve protection
2128            dest->flags.security_write  = source->flags.security_write;
2129            dest->flags.security_delete = source->flags.security_delete;
2130        }
2131
2132        if (!(mode&CM_DROP_TEMPSTATE)) { // preserve tempstate
2133            bool source_is_temp = GB_is_temporary(source);
2134            if (GB_is_temporary(dest) != source_is_temp) {
2135                error = source_is_temp ? GB_set_temporary(dest) : GB_clear_temporary(dest);
2136            }
2137        }
2138    }
2139    return error;
2140}
2141
2142inline GB_ERROR gb_copy_checked(GBDATA *dest, GBDATA *source, CopyMode mode) {
2143    /*! copies the content of 'source' into 'dest'.
2144     * if 'dest' is a container, entries existing there will not be overwritten!
2145     */
2146    if (GB_is_ancestor_of(source, dest)) {
2147        return "infinite copy not permitted (destination may not be part of source)";
2148    }
2149    return gb_copy_explicit(dest, source, mode);
2150}
2151
2152GB_ERROR GB_copy_dropProtectMarksAndTempstate(GBDATA *dest, GBDATA *source) {
2153    /*! traditional but NOT RECOMMENDED copy flavour.
2154     *
2155     * resets protection (to current or to none?),
2156     * clears all flags (including marks) and
2157     * copies temporary entries, while clearing their temporary state (i.e. they may get saved to disk later)
2158     *
2159     * Will become obsolete!
2160     */
2161    return gb_copy_checked(dest, source, CM_COPY_TRADITIONAL);
2162}
2163GB_ERROR GB_copy_dropMarksAndTempstate(GBDATA *dest, GBDATA *source) {
2164    /*! first attempt to fix traditional GB_copy_dropProtectMarksAndTempstate.
2165     * As well NOT RECOMMENDED!
2166     *
2167     * does correctly copy protection.
2168     * clears marks + temporary state (see GB_copy_dropProtectMarksAndTempstate).
2169     *
2170     * Will become obsolete!
2171     */
2172    return gb_copy_checked(dest, source, CM_COPY_WITHPROTECT);
2173}
2174GB_ERROR GB_copy_std(GBDATA *dest, GBDATA *source) {
2175    /*! recommended copy flavour: skips temp, preserves protection and marks.
2176     * deletes other flags (e.g. query flag).
2177     * sub-entries existing in 'dest' will not get overwritten.
2178     */
2179    return gb_copy_checked(dest, source, CM_COPY_STANDARD);
2180}
2181
2182GB_ERROR GB_copy_overlay(GBDATA *dest, GBDATA *source) {
2183    /*! overlays 'dest' with 'source'.
2184     * sub-entries existing in 'dest' will be overwritten!
2185     * also applies to sub-containers.
2186     */
2187    return gb_copy_checked(dest, source, CopyMode(CM_COPY_STANDARD|CM_OVERWRITE_EXISTING));
2188}
2189
2190GB_ERROR GB_copy_full(GBDATA *dest, GBDATA *source) {
2191    /*! complete copy (does also copy temporary entries)
2192     */
2193    return gb_copy_checked(dest, source, CM_COPY_FULL);
2194}
2195
2196GBDATA *GB_clone(GBDATA *gb_destCont, GBDATA *gb_source) {
2197    /*! copy a clone of 'gb_source' into container 'gb_destCont' (like GB_copy_std would do).
2198     * @return pointer to clone (on success) or NULp (in which case an error MAY be exported).
2199     */
2200
2201    gb_assert(!GB_have_error()); // illegal to enter this function when an error is exported!
2202
2203    GB_ERROR error = NULp;
2204    if (gb_destCont->is_container()) {
2205        GBCONTAINER *destc = gb_destCont->as_container();
2206        if (destc->flags2.folded_container) gb_unfold(destc, 0, -1);
2207        if (!GB_is_ancestor_of(gb_source, gb_destCont)) {
2208            return gb_clone_explicit(destc, gb_source, CM_COPY_STANDARD);
2209        }
2210        error = "GB_clone destination cannot be part of source.";
2211    }
2212    else {
2213        error = "GB_clone destination has to be a container.";
2214    }
2215    GB_export_error(error);
2216    return NULp;
2217}
2218
2219
2220static char *gb_stpcpy(char *dest, const char *source) {
2221    while ((*dest++=*source++)) ;
2222    return dest-1; // return pointer to last copied character (which is \0)
2223}
2224
2225char* GB_get_subfields(GBDATA *gbd) {
2226    /*! Get all subfield names
2227     *
2228     * @return all subfields of 'gbd' as ';'-separated heap-copy
2229     * (first and last char of result is a ';')
2230     */
2231    GB_test_transaction(gbd);
2232
2233    char *result = NULp;
2234    if (gbd->is_container()) {
2235        GBCONTAINER *gbc           = gbd->as_container();
2236        int          result_length = 0;
2237
2238        if (gbc->flags2.folded_container) {
2239            gb_unfold(gbc, -1, -1);
2240        }
2241
2242        for (GBDATA *gbp = GB_child(gbd); gbp; gbp = GB_nextChild(gbp)) {
2243            const char *key = GB_read_key_pntr(gbp);
2244            int keylen = strlen(key);
2245
2246            if (result) {
2247                char *neu_result = ARB_alloc<char>(result_length+keylen+1+1);
2248
2249                if (neu_result) {
2250                    char *p = gb_stpcpy(neu_result, result);
2251                    p = gb_stpcpy(p, key);
2252                    *p++ = ';';
2253                    p[0] = 0;
2254
2255                    freeset(result, neu_result);
2256                    result_length += keylen+1;
2257                }
2258                else {
2259                    gb_assert(0);
2260                }
2261            }
2262            else {
2263                ARB_alloc(result, 1+keylen+1+1);
2264                result[0] = ';';
2265                strcpy(result+1, key);
2266                result[keylen+1] = ';';
2267                result[keylen+2] = 0;
2268                result_length = keylen+2;
2269            }
2270        }
2271    }
2272    else {
2273        result = ARB_strdup(";");
2274    }
2275
2276    return result;
2277}
2278
2279// --------------------------
2280//      temporary entries
2281
2282GB_ERROR GB_set_temporary(GBDATA *gbd) { // goes to header: __ATTR__USERESULT
2283    /*! if the temporary flag is set, then that entry (including all subentries) will not be saved
2284     * @see GB_clear_temporary() and GB_is_temporary()
2285     */
2286
2287    GB_ERROR error = GB_test_delete_possible(gbd);
2288    if (!error) {
2289        gbd->flags.temporary = 1;
2290        gb_touch_entry(gbd, GB_NORMAL_CHANGE);
2291    }
2292    RETURN_ERROR(error);
2293}
2294
2295GB_ERROR GB_clear_temporary(GBDATA *gbd) { // @@@ used in ptpan branch - do not remove
2296    //! undo effect of GB_set_temporary()
2297
2298    GB_test_transaction(gbd);
2299    gbd->flags.temporary = 0;
2300    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
2301    return NULp;
2302}
2303
2304bool GB_is_temporary(GBDATA *gbd) {
2305    //! @see GB_set_temporary() and GB_in_temporary_branch()
2306    GB_test_transaction(gbd);
2307    return (long)gbd->flags.temporary;
2308}
2309
2310bool GB_in_temporary_branch(GBDATA *gbd) {
2311    /*! @return true, if 'gbd' is member of a temporary subtree,
2312     * i.e. if GB_is_temporary(itself or any parent)
2313     */
2314
2315    if (GB_is_temporary(gbd)) return true;
2316
2317    GBDATA *gb_parent = GB_get_father(gbd);
2318    if (!gb_parent) return false;
2319
2320    return GB_in_temporary_branch(gb_parent);
2321}
2322
2323// ---------------------
2324//      transactions
2325
2326GB_ERROR GB_MAIN_TYPE::initial_client_transaction() {
2327    // the first client transaction ever
2328    transaction_level = 1;
2329    GB_ERROR error    = gbcmc_init_transaction(root_container);
2330    if (!error) ++clock;
2331    return error;
2332}
2333
2334inline GB_ERROR GB_MAIN_TYPE::start_transaction() {
2335    gb_assert(transaction_level == 0);
2336
2337    transaction_level   = 1;
2338    aborted_transaction = 0;
2339
2340    GB_ERROR error = NULp;
2341    if (is_client()) {
2342        error = gbcmc_begin_transaction(gb_main());
2343        if (!error) {
2344            error = gb_commit_transaction_local_rek(gb_main_ref(), 0, NULp); // init structures
2345            gb_untouch_children_and_me(root_container);
2346        }
2347    }
2348
2349    if (!error) {
2350        /* do all callbacks
2351         * cb that change the db are no problem, because it's the beginning of a ta
2352         */
2353        call_pending_callbacks();
2354        ++clock;
2355    }
2356    return error;
2357}
2358
2359inline GB_ERROR GB_MAIN_TYPE::begin_transaction() {
2360    if (transaction_level>0) return GBS_global_string("attempt to start a NEW transaction (at transaction level %i)", transaction_level);
2361    if (transaction_level == 0) return start_transaction();
2362    return NULp; // NO_TRANSACTION_MODE
2363}
2364
2365inline GB_ERROR GB_MAIN_TYPE::abort_transaction() {
2366    if (transaction_level<=0) {
2367        if (transaction_level<0) return "GB_abort_transaction: Attempt to abort transaction in no-transaction-mode";
2368        return "GB_abort_transaction: No transaction running";
2369    }
2370    if (transaction_level>1) {
2371        aborted_transaction = 1;
2372        return pop_transaction();
2373    }
2374
2375    gb_abort_transaction_local_rek(gb_main_ref());
2376    if (is_client()) {
2377        GB_ERROR error = gbcmc_abort_transaction(gb_main());
2378        if (error) return error;
2379    }
2380    clock--;
2381    call_pending_callbacks();
2382    transaction_level = 0;
2383    gb_untouch_children_and_me(root_container);
2384    return NULp;
2385}
2386
2387inline GB_ERROR GB_MAIN_TYPE::commit_transaction() {
2388    GB_ERROR      error = NULp;
2389    GB_CHANGE     flag;
2390
2391    if (!transaction_level) {
2392        return "commit_transaction: No transaction running";
2393    }
2394    if (transaction_level>1) {
2395        return GBS_global_string("attempt to commit at transaction level %i", transaction_level);
2396    }
2397    if (aborted_transaction) {
2398        aborted_transaction = 0;
2399        return abort_transaction();
2400    }
2401    if (is_server()) {
2402        char *error1 = gb_set_undo_sync(gb_main());
2403
2404        int warn_deadlock = 100;
2405        int loop_count    = 0;
2406        while (1) {
2407            flag  = (GB_CHANGE)GB_ARRAY_FLAGS(gb_main()).changed;
2408            if (!flag) break;           // nothing to do
2409            error = gb_commit_transaction_local_rek(gb_main_ref(), 0, NULp);
2410            gb_untouch_children_and_me(root_container);
2411            if (error) break;
2412            call_pending_callbacks();
2413
2414            ++loop_count;
2415            if (loop_count>warn_deadlock) {
2416                fprintf(stderr, "possible deadlock in commit_transaction (callback triggered by callback?). looped %i times.\n", warn_deadlock);
2417                warn_deadlock *= 10;
2418            }
2419        }
2420        gb_disable_undo(gb_main());
2421        if (error1) {
2422            transaction_level = 0;
2423            gb_assert(error); // maybe return error1?
2424            return error; // @@@ huh? why not return error1
2425        }
2426    }
2427    else {
2428        gb_disable_undo(gb_main());
2429        while (1) {
2430            flag = (GB_CHANGE)GB_ARRAY_FLAGS(gb_main()).changed;
2431            if (!flag) break;           // nothing to do
2432
2433            error = gbcmc_begin_sendupdate(gb_main());                       if (error) break;
2434            error = gb_commit_transaction_local_rek(gb_main_ref(), 1, NULp); if (error) break;
2435            error = gbcmc_end_sendupdate(gb_main());                         if (error) break;
2436
2437            gb_untouch_children_and_me(root_container);
2438            call_pending_callbacks();
2439        }
2440        if (!error) error = gbcmc_commit_transaction(gb_main());
2441
2442    }
2443    transaction_level = 0;
2444    return error;
2445}
2446
2447inline GB_ERROR GB_MAIN_TYPE::push_transaction() {
2448    if (transaction_level == 0) return start_transaction();
2449    if (transaction_level>0) ++transaction_level;
2450    // transaction<0 is NO_TRANSACTION_MODE
2451    return NULp;
2452}
2453
2454inline GB_ERROR GB_MAIN_TYPE::pop_transaction() {
2455    if (transaction_level==0) return "attempt to pop nested transaction while none running";
2456    if (transaction_level<0)  return NULp;  // NO_TRANSACTION_MODE
2457    if (transaction_level==1) return commit_transaction();
2458    transaction_level--;
2459    return NULp;
2460}
2461
2462inline GB_ERROR GB_MAIN_TYPE::no_transaction() {
2463    if (is_client()) return "Tried to disable transactions in a client";
2464    transaction_level = -1;
2465    return NULp;
2466}
2467
2468GB_ERROR GB_MAIN_TYPE::send_update_to_server(GBDATA *gbd) {
2469    GB_ERROR error = NULp;
2470
2471    if (!transaction_level) error = "send_update_to_server: no transaction running";
2472    else if (is_server()) error   = "send_update_to_server: only possible from clients (not from server itself)";
2473    else {
2474        const gb_triggered_callback *chg_cbl_old = changeCBs.pending.get_tail();
2475        const gb_triggered_callback *del_cbl_old = deleteCBs.pending.get_tail();
2476
2477        error             = gbcmc_begin_sendupdate(gb_main());
2478        if (!error) error = gb_commit_transaction_local_rek(gbd, 2, NULp);
2479        if (!error) error = gbcmc_end_sendupdate(gb_main());
2480
2481        if (!error &&
2482            (chg_cbl_old != changeCBs.pending.get_tail() ||
2483             del_cbl_old != deleteCBs.pending.get_tail()))
2484        {
2485            error = "send_update_to_server triggered a callback (this is not allowed)";
2486        }
2487    }
2488    return error;
2489}
2490
2491// --------------------------------------
2492//      client transaction interface
2493
2494GB_ERROR GB_push_transaction(GBDATA *gbd) {
2495    /*! start a transaction if no transaction is running.
2496     * (otherwise only trace nested transactions)
2497     *
2498     * recommended transaction usage:
2499     *
2500     * \code
2501     * GB_ERROR myFunc() {
2502     *     GB_ERROR error = GB_push_transaction(gbd);
2503     *     if (!error) {
2504     *         error = ...;
2505     *     }
2506     *     return GB_end_transaction(gbd, error);
2507     * }
2508     *
2509     * void myFunc() {
2510     *     GB_ERROR error = GB_push_transaction(gbd);
2511     *     if (!error) {
2512     *         error = ...;
2513     *     }
2514     *     GB_end_transaction_show_error(gbd, error, aw_message);
2515     * }
2516     * \endcode
2517     *
2518     * @see GB_pop_transaction(), GB_end_transaction(), GB_begin_transaction()
2519     */
2520
2521    return GB_MAIN(gbd)->push_transaction();
2522}
2523
2524GB_ERROR GB_pop_transaction(GBDATA *gbd) {
2525    //! commit a transaction started with GB_push_transaction()
2526    return GB_MAIN(gbd)->pop_transaction();
2527}
2528GB_ERROR GB_begin_transaction(GBDATA *gbd) {
2529    /*! like GB_push_transaction(),
2530     * but fails if there is already an transaction running.
2531     * @see GB_commit_transaction() and GB_abort_transaction()
2532     */
2533    return GB_MAIN(gbd)->begin_transaction();
2534}
2535GB_ERROR GB_no_transaction(GBDATA *gbd) { // goes to header: __ATTR__USERESULT
2536    return GB_MAIN(gbd)->no_transaction();
2537}
2538
2539GB_ERROR GB_abort_transaction(GBDATA *gbd) {
2540    /*! abort a running transaction,
2541     * i.e. forget all changes made to DB inside the current transaction.
2542     *
2543     * May be called instead of GB_pop_transaction() or GB_commit_transaction()
2544     *
2545     * If a nested transactions got aborted,
2546     * committing a surrounding transaction will silently abort it as well.
2547     */
2548    return GB_MAIN(gbd)->abort_transaction();
2549}
2550
2551GB_ERROR GB_commit_transaction(GBDATA *gbd) {
2552    /*! commit a transaction started with GB_begin_transaction()
2553     *
2554     * commit changes made to DB.
2555     *
2556     * in case of nested transactions, this is equal to GB_pop_transaction()
2557     */
2558    return GB_MAIN(gbd)->commit_transaction();
2559}
2560
2561GB_ERROR GB_end_transaction(GBDATA *gbd, GB_ERROR error) {
2562    /*! abort or commit transaction
2563     *
2564     * @ param error
2565     * - if NULp commit transaction
2566     * - else abort transaction
2567     *
2568     * always commits in no-transaction-mode
2569     *
2570     * @return error or transaction error
2571     * @see GB_push_transaction() for example
2572     */
2573
2574    if (GB_get_transaction_level(gbd)<0) {
2575        ASSERT_RESULT(GB_ERROR, NULp, GB_pop_transaction(gbd));
2576    }
2577    else {
2578        if (error) GB_abort_transaction(gbd);
2579        else error = GB_pop_transaction(gbd);
2580    }
2581    return error;
2582}
2583
2584void GB_end_transaction_show_error(GBDATA *gbd, GB_ERROR error, void (*error_handler)(GB_ERROR)) {
2585    //! like GB_end_transaction(), but show error using 'error_handler'
2586    error = GB_end_transaction(gbd, error);
2587    if (error) error_handler(error);
2588}
2589
2590int GB_get_transaction_level(GBDATA *gbd) {
2591    /*! @return transaction level
2592     * <0 -> in no-transaction-mode (abort is impossible)
2593     *  0 -> not in transaction
2594     *  1 -> one single transaction
2595     *  2, ... -> nested transactions
2596     */
2597    return GB_MAIN(gbd)->get_transaction_level();
2598}
2599
2600GB_ERROR GB_release(GBDATA *gbd) {
2601    /*! free cached data in client.
2602     *
2603     * Warning: pointers into the freed region(s) will get invalid!
2604     */
2605    GBCONTAINER  *gbc;
2606    GBDATA       *gb;
2607    int           index;
2608    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
2609
2610    GB_test_transaction(gbd);
2611    if (Main->is_server()) return NULp;
2612    if (GB_ARRAY_FLAGS(gbd).changed && !gbd->flags2.update_in_server) {
2613        GB_ERROR error = Main->send_update_to_server(gbd);
2614        if (error) return error;
2615    }
2616    if (gbd->type() != GB_DB) {
2617        GB_ERROR error = GB_export_errorf("You cannot release non container (%s)",
2618                                          GB_read_key_pntr(gbd));
2619        GB_internal_error(error);
2620        return error;
2621    }
2622    if (gbd->flags2.folded_container) return NULp;
2623    gbc = (GBCONTAINER *)gbd;
2624
2625    for (index = 0; index < gbc->d.nheader; index++) {
2626        if ((gb = GBCONTAINER_ELEM(gbc, index))) {
2627            gb_delete_entry(gb);
2628        }
2629    }
2630
2631    gbc->flags2.folded_container = 1;
2632    Main->call_pending_callbacks();
2633    return NULp;
2634}
2635
2636int GB_nsons(GBDATA *gbd) {
2637    /*! return number of child entries
2638     *
2639     * @@@ does this work in clients ?
2640     */
2641
2642    return gbd->is_container()
2643        ? gbd->as_container()->d.size
2644        : 0;
2645}
2646
2647void GB_disable_quicksave(GBDATA *gbd, const char *reason) {
2648    /*! Disable quicksaving database
2649     * @param gbd any DB node
2650     * @param reason why quicksaving is not allowed
2651     */
2652    freedup(GB_MAIN(gbd)->qs.quick_save_disabled, reason);
2653}
2654
2655GB_ERROR GB_resort_data_base(GBDATA *gb_main, GBDATA **new_order_list, long listsize) {
2656    {
2657        long client_count = GB_read_clients(gb_main);
2658        if (client_count<0) {
2659            return "Sorry: this program is not the arbdb server, you cannot resort your data";
2660        }
2661        if (client_count>0) {
2662            // resort will do a big amount of client update callbacks => disallow clients here
2663            bool called_from_macro = GB_inside_remote_action(gb_main);
2664            if (!called_from_macro) { // accept macro clients
2665                return GBS_global_string("There are %li clients (editors, tree programs) connected to this server.\n"
2666                                         "You need to close these clients before you can run this operation.",
2667                                         client_count);
2668            }
2669        }
2670    }
2671
2672    if (listsize <= 0) return NULp;
2673
2674    GBCONTAINER *father = GB_FATHER(new_order_list[0]);
2675    GB_disable_quicksave(gb_main, "some entries in the database got a new order");
2676
2677    gb_header_list *hl = GB_DATA_LIST_HEADER(father->d);
2678    for (long new_index = 0; new_index< listsize; new_index++) {
2679        long old_index = new_order_list[new_index]->index;
2680
2681        if (old_index < new_index) {
2682            GB_warningf("Warning at resort database: entry exists twice: %li and %li",
2683                        old_index, new_index);
2684        }
2685        else {
2686            GBDATA *ogb = GB_HEADER_LIST_GBD(hl[old_index]);
2687            GBDATA *ngb = GB_HEADER_LIST_GBD(hl[new_index]);
2688
2689            gb_header_list h = hl[new_index];
2690            hl[new_index] = hl[old_index];
2691            hl[old_index] = h;              // Warning: Relative Pointers are incorrect !!!
2692
2693            SET_GB_HEADER_LIST_GBD(hl[old_index], ngb);
2694            SET_GB_HEADER_LIST_GBD(hl[new_index], ogb);
2695
2696            if (ngb) ngb->index = old_index;
2697            if (ogb) ogb->index = new_index;
2698        }
2699    }
2700
2701    gb_touch_entry(father, GB_NORMAL_CHANGE);
2702    return NULp;
2703}
2704
2705GB_ERROR gb_resort_system_folder_to_top(GBCONTAINER *gb_main) {
2706    if (GB_read_clients(gb_main)<0) {
2707        return NULp; // we are not server
2708    }
2709
2710    GBDATA *gb_system = GB_entry(gb_main, GB_SYSTEM_FOLDER);
2711    if (!gb_system) {
2712        return GB_export_error("System databaseentry does not exist");
2713    }
2714
2715    GBDATA *gb_first = GB_child(gb_main);
2716    if (gb_first == gb_system) {
2717        return NULp;
2718    }
2719
2720    int      len            = GB_number_of_subentries(gb_main);
2721    GBDATA **new_order_list = ARB_calloc<GBDATA*>(len);
2722
2723    new_order_list[0] = gb_system;
2724    for (int i=1; i<len; i++) {
2725        new_order_list[i] = gb_first;
2726        do gb_first = GB_nextChild(gb_first); while (gb_first == gb_system);
2727    }
2728
2729    GB_ERROR error = GB_resort_data_base(gb_main, new_order_list, len);
2730    free(new_order_list);
2731
2732    return error;
2733}
2734
2735// ------------------------------
2736//      private(?) user flags
2737
2738STATIC_ASSERT_ANNOTATED(((GB_USERFLAG_ANY+1)&GB_USERFLAG_ANY) == 0, "not all bits set in GB_USERFLAG_ANY");
2739
2740#if defined(ASSERTION_USED)
2741inline bool legal_user_bitmask(unsigned char bitmask) {
2742    return bitmask>0 && bitmask<=GB_USERFLAG_ANY;
2743}
2744#endif
2745
2746inline gb_flag_types2& get_user_flags(GBDATA *gbd) {
2747    return gbd->expect_container()->flags2;
2748}
2749
2750bool GB_user_flag(GBDATA *gbd, unsigned char user_bit) {
2751    gb_assert(legal_user_bitmask(user_bit));
2752    return get_user_flags(gbd).user_bits & user_bit;
2753}
2754
2755void GB_raise_user_flag(GBDATA *gbd, unsigned char user_bit) {
2756    gb_assert(legal_user_bitmask(user_bit));
2757    gb_flag_types2& flags  = get_user_flags(gbd);
2758    flags.user_bits       |= user_bit;
2759}
2760void GB_clear_user_flag(GBDATA *gbd, unsigned char user_bit) {
2761    gb_assert(legal_user_bitmask(user_bit));
2762    gb_flag_types2& flags  = get_user_flags(gbd);
2763    flags.user_bits       &= (user_bit^GB_USERFLAG_ANY);
2764}
2765void GB_write_user_flag(GBDATA *gbd, unsigned char user_bit, bool state) {
2766    (state ? GB_raise_user_flag : GB_clear_user_flag)(gbd, user_bit);
2767}
2768
2769
2770// ------------------------
2771//      mark DB entries
2772
2773void GB_write_flag(GBDATA *gbd, long flag) {
2774    GBCONTAINER  *gbc  = gbd->expect_container();
2775    GB_MAIN_TYPE *Main = GB_MAIN(gbc);
2776
2777    GB_test_transaction(Main);
2778
2779    int ubit = Main->users[0]->userbit;
2780    int prev = GB_ARRAY_FLAGS(gbc).flags;
2781    gbc->flags.saved_flags = prev;
2782
2783    if (flag) {
2784        GB_ARRAY_FLAGS(gbc).flags |= ubit;
2785    }
2786    else {
2787        GB_ARRAY_FLAGS(gbc).flags &= ~ubit;
2788    }
2789    if (prev != (int)GB_ARRAY_FLAGS(gbc).flags) {
2790        gb_touch_entry(gbc, GB_NORMAL_CHANGE);
2791        gb_touch_header(GB_FATHER(gbc));
2792        GB_DO_CALLBACKS(gbc);
2793    }
2794}
2795
2796int GB_read_flag(GBDATA *gbd) {
2797    GB_test_transaction(gbd);
2798    if (GB_ARRAY_FLAGS(gbd).flags & GB_MAIN(gbd)->users[0]->userbit) return 1;
2799    else return 0;
2800}
2801
2802void GB_touch(GBDATA *gbd) {
2803    GB_test_transaction(gbd);
2804    gb_touch_entry(gbd, GB_NORMAL_CHANGE);
2805    GB_DO_CALLBACKS(gbd);
2806}
2807
2808
2809char GB_type_2_char(GB_TYPES type) {
2810    const char *type2char = "-bcif-B-CIFlSS-%";
2811    return type2char[type];
2812}
2813
2814void GB_print_debug_information(struct Unfixed_cb_parameter *, GBDATA *gb_main) {
2815    GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
2816    GB_push_transaction(gb_main);
2817    for (int i=0; i<Main->keycnt; i++) {
2818        gb_Key& KEY = Main->keys[i];
2819        if (KEY.key) {
2820            printf("%3i %20s    nref %li\n", i, KEY.key, KEY.nref);
2821        }
2822        else {
2823            printf("    %3i unused key, next free key = %li\n", i, KEY.next_free_key);
2824        }
2825    }
2826    gbm_debug_mem();
2827    GB_pop_transaction(gb_main);
2828}
2829
2830static int GB_info_deep = 15;
2831
2832
2833static int gb_info(GBDATA *gbd, int deep) {
2834    if (!gbd) { printf("NULp\n"); return -1; }
2835    GB_push_transaction(gbd);
2836
2837    GB_TYPES type = gbd->type();
2838
2839    if (deep) {
2840        printf("    ");
2841    }
2842
2843    printf("(GBDATA*)0x%lx (GBCONTAINER*)0x%lx ", (long)gbd, (long)gbd);
2844
2845    if (gbd->rel_father==0) { printf("father=NULp\n"); return -1; }
2846
2847    GBCONTAINER  *gbc;
2848    GB_MAIN_TYPE *Main;
2849    if (type==GB_DB) { gbc = gbd->as_container(); Main = GBCONTAINER_MAIN(gbc); }
2850    else             { gbc = NULp;                Main = GB_MAIN(gbd); }
2851
2852    if (!Main) { printf("Oops - I have no main entry!!!\n"); return -1; }
2853    if (gbd==Main->dummy_father) { printf("dummy_father!\n"); return -1; }
2854
2855    printf("%10s Type '%c'  ", GB_read_key_pntr(gbd), GB_type_2_char(type));
2856
2857    switch (type) {
2858        case GB_DB: {
2859            int size = gbc->d.size;
2860            printf("Size %i nheader %i hmemsize %i", gbc->d.size, gbc->d.nheader, gbc->d.headermemsize);
2861            printf(" father=(GBDATA*)0x%lx\n", (long)GB_FATHER(gbd));
2862            if (size < GB_info_deep) {
2863                int             index;
2864                gb_header_list *header;
2865
2866                header = GB_DATA_LIST_HEADER(gbc->d);
2867                for (index = 0; index < gbc->d.nheader; index++) {
2868                    GBDATA  *gb_sub = GB_HEADER_LIST_GBD(header[index]);
2869                    GBQUARK  quark  = header[index].flags.key_quark;
2870                    printf("\t\t%10s (GBDATA*)0x%lx (GBCONTAINER*)0x%lx\n", quark2key(Main, quark), (long)gb_sub, (long)gb_sub);
2871                }
2872            }
2873            break;
2874        }
2875        default: {
2876            char *data = GB_read_as_string(gbd);
2877            if (data) { printf("%s", data); free(data); }
2878            printf(" father=(GBDATA*)0x%lx\n", (long)GB_FATHER(gbd));
2879        }
2880    }
2881
2882
2883    GB_pop_transaction(gbd);
2884
2885    return 0;
2886}
2887
2888int GB_info(GBDATA *gbd) { // unused - intended to be used in debugger
2889    return gb_info(gbd, 0);
2890}
2891
2892long GB_number_of_subentries(GBDATA *gbd) {
2893    GBCONTAINER    *gbc        = gbd->expect_container();
2894    gb_header_list *header     = GB_DATA_LIST_HEADER(gbc->d);
2895
2896    long subentries = 0;
2897    int  end        = gbc->d.nheader;
2898
2899    for (int index = 0; index<end; index++) {
2900        if (header[index].flags.changed < GB_DELETED) subentries++;
2901    }
2902    return subentries;
2903}
2904
2905// --------------------------------------------------------------------------------
2906
2907#ifdef UNIT_TESTS
2908
2909#include <arb_diff.h>
2910#include <arb_file.h>
2911#include <test_unit.h>
2912#include <locale.h>
2913#include "arbdbt.h"
2914
2915void TEST_GB_atof() {
2916    // arb depends on locale for floating-point conversion!
2917    // see ../SOURCE_TOOLS/arb_main.h@setlocale
2918    TEST_EXPECT_SIMILAR(GB_atof("0.031"), 0.031, 0.0001); // fails if LC_NUMERIC is set to "de_DE..."
2919}
2920
2921#if !defined(DARWIN)
2922// @@@ TEST_DISABLED_OSX: test fails to compile for OSX on build server
2923// @@@ re-activate test later; see missing libs http://bugs.arb-home.de/changeset/11664#file2
2924void TEST_999_strtod_replacement() {
2925    // caution: if it fails -> locale is not reset (therefore call with low priority 999)
2926    const char *old = setlocale(LC_NUMERIC, "de_DE.UTF-8");
2927    {
2928        // TEST_EXPECT_SIMILAR__BROKEN(strtod("0.031", NULp), 0.031, 0.0001);
2929        TEST_EXPECT_SIMILAR(g_ascii_strtod("0.031", NULp), 0.031, 0.0001);
2930    }
2931    setlocale(LC_NUMERIC, old);
2932}
2933#endif
2934
2935#if defined(ENABLE_CRASH_TESTS)
2936static void test_another_shell() { delete new GB_shell; }
2937#endif
2938static void test_opendb() { GB_close(GB_open("no.arb", "c")); }
2939
2940void TEST_GB_shell__crashtest() {
2941    {
2942        GB_shell *shell = new GB_shell;
2943        TEST_EXPECT_SEGFAULT(test_another_shell);
2944        test_opendb(); // no SEGV here
2945        delete shell;
2946    }
2947
2948    TEST_EXPECT_SEGFAULT(test_opendb); // should be impossible to open db w/o shell
2949}
2950
2951void TEST_GB_number_of_subentries() {
2952    GB_shell  shell;
2953    GBDATA   *gb_main = GB_open("no.arb", "c");
2954
2955    {
2956        GB_transaction ta(gb_main);
2957
2958        GBDATA   *gb_cont = GB_create_container(gb_main, "container");
2959        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 0);
2960
2961        TEST_EXPECT_RESULT__NOERROREXPORTED(GB_create(gb_cont, "entry", GB_STRING));
2962        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 1);
2963
2964        {
2965            GBDATA *gb_entry;
2966            TEST_EXPECT_RESULT__NOERROREXPORTED(gb_entry = GB_create(gb_cont, "entry", GB_STRING));
2967            TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 2);
2968
2969            TEST_EXPECT_NO_ERROR(GB_delete(gb_entry));
2970            TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 1);
2971        }
2972
2973        TEST_EXPECT_RESULT__NOERROREXPORTED(GB_create(gb_cont, "entry", GB_STRING));
2974        TEST_EXPECT_EQUAL(GB_number_of_subentries(gb_cont), 2);
2975    }
2976
2977    GB_close(gb_main);
2978}
2979
2980
2981void TEST_POSTCOND_arbdb() {
2982    GB_ERROR error             = GB_incur_error(); // clears the error (to make further tests succeed)
2983    bool     unclosed_GB_shell = closed_open_shell_for_unit_tests();
2984
2985    TEST_REJECT(error);             // your test finished with an exported error
2986    TEST_REJECT(unclosed_GB_shell); // your test finished w/o destroying GB_shell
2987}
2988
2989// #define TEST_AUTO_UPDATE // uncomment to auto-update expected results
2990
2991static void saveAndCompare(GBDATA *gb_main, const char *expectedname, bool
2992#if defined(TEST_AUTO_UPDATE)
2993                           allowAutoUpdate
2994#endif
2995    ) {
2996    const char *outputname = "copied.arb";
2997
2998    TEST_EXPECT_NO_ERROR(GB_save(gb_main, outputname, "a"));
2999
3000#if defined(TEST_AUTO_UPDATE)
3001    if (allowAutoUpdate) {
3002        TEST_COPY_FILE(outputname, expectedname);
3003    }
3004#endif
3005    TEST_EXPECT_TEXTFILE_DIFFLINES(outputname, expectedname, 0);
3006    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(outputname));
3007}
3008
3009void TEST_AFTER_SLOW_copy() { // run after TEST_SLOW_loadsave!
3010    GB_shell  shell;
3011    GBDATA   *gb_main = GB_open("TEST_loadsave_ascii.arb", "rw"); // ../UNIT_TESTER/run/TEST_loadsave_ascii.arb
3012
3013    // ---------------------------------------------------------
3014    //      1st step: copy root elements to container 'all'
3015    GBDATA *gb_all;
3016    {
3017        GB_transaction ta(gb_main);
3018
3019        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_all = GB_create_container(gb_main, "all"));
3020
3021        // move everything into new container:
3022        GB_ERROR error = NULp;
3023        for (GBDATA *gb_child = GB_child(gb_main); gb_child && !error; ) {
3024            GBDATA *gb_next_child = GB_nextChild(gb_child);
3025            if (gb_child != gb_all) { // skip target container
3026                GBDATA *gb_clone = GB_clone(gb_all, gb_child);
3027                error            = GB_incur_error_if(!gb_clone);
3028                if (gb_clone) {
3029                    gb_assert(!error);
3030                    error = GB_delete(gb_child);
3031                }
3032            }
3033            gb_child = gb_next_child;
3034        }
3035
3036        TEST_EXPECT_NO_ERROR(error);
3037    }
3038
3039    saveAndCompare(gb_main, "TEST_copy.arb", true);
3040
3041    // -----------------------------------------------------------------------------
3042    //      2nd step: copy container 'gb_all' with traditional GB_copy_dropProtectMarksAndTempstate into the fresh container 'gb_copy' (also named 'all')
3043    GBDATA *gb_copy;
3044    {
3045        GB_transaction ta(gb_main);
3046
3047        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_copy = GB_create_container(gb_main, "all"));
3048        TEST_EXPECT_NO_ERROR(GB_copy_dropProtectMarksAndTempstate(gb_copy, gb_all)); // w/o protection
3049
3050        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_all)); // do not save 'all'
3051    }
3052
3053    saveAndCompare(gb_main, "TEST_copy_noProtect.arb", true); // ../UNIT_TESTER/run/TEST_copy_noProtect.arb
3054
3055    // -----------------------------------------------------------------------------
3056    //      3rd step: copy container 'gb_all' with GB_copy_overlay to the fresh container 'gb_copy2' (also named 'all')
3057    GBDATA *gb_copy2;
3058    {
3059        GB_transaction ta(gb_main);
3060
3061        TEST_EXPECT_NO_ERROR(GB_clear_temporary(gb_all));
3062
3063        TEST_EXPECT_RESULT__NOERROREXPORTED(gb_copy2 = GB_create_container(gb_main, "all"));
3064        TEST_EXPECT_NO_ERROR(GB_copy_overlay(gb_copy2, gb_all));
3065
3066        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_copy)); // skip save of 'copy'
3067        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_all));  // skip save of 'all'
3068    }
3069
3070    saveAndCompare(gb_main, "TEST_copy.arb", false);
3071
3072    // -----------------------------------------------------------------------------
3073    //      4th step: copy container 'gb_all' with GB_copy_overlay over container 'gb_copy'
3074    //      (Note: gb_copy lacks protection; it gets restored by overlay copy)
3075    {
3076        GB_transaction ta(gb_main);
3077
3078        TEST_EXPECT_NO_ERROR(GB_clear_temporary(gb_all));
3079        TEST_EXPECT_NO_ERROR(GB_clear_temporary(gb_copy));
3080
3081        TEST_EXPECT_NO_ERROR(GB_copy_overlay(gb_copy, gb_all));
3082
3083        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_copy2)); // skip save of 'copy2'
3084        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_all));  // skip save of 'all'
3085    }
3086
3087    saveAndCompare(gb_main, "TEST_copy.arb", false);
3088
3089    // ---------------------------------------------------------------------------------
3090    //      5th step: revert DB to original state by copying 'gb_all' back to DB-root
3091    {
3092        GB_transaction ta(gb_main);
3093
3094        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_copy));  // skip save of 'copy'
3095        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_copy2)); // skip save of 'copy2'
3096
3097        // copy all entries back to root-container:
3098        GBDATA *gb_tmp = GB_search(gb_all, "tmp", GB_FIND);
3099        TEST_EXPECT_NULL(gb_tmp); // did not clone temp entries (in 1st step)
3100
3101        TEST_EXPECT_NO_ERROR(GB_clear_temporary(gb_all)); // all = permanent (to permit copy)
3102        TEST_EXPECT_NO_ERROR(GB_copy_std(gb_main, gb_all));
3103        TEST_EXPECT_NO_ERROR(GB_set_temporary(gb_all));   // all = temporary (to avoid save)
3104
3105        TEST_EXPECT_ERROR_CONTAINS(gb_copy_checked(gb_main, gb_copy, CopyMode(CM_SKIP_TEMP & ~CM_DROP_TEMPSTATE)),
3106                                   "logic error: it's too late to skip copy of temporary entry");
3107
3108        GBDATA *gb_description = GB_entry(gb_main, "description");
3109        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_clone(gb_description, gb_copy),
3110                                                     "GB_clone destination has to be a container");
3111    }
3112
3113    saveAndCompare(gb_main, "TEST_loadsave_ascii.arb", false);
3114
3115    // ----------------------------------------------
3116    //      copying container into itself fails:
3117    {
3118        GB_transaction ta(gb_main);
3119
3120        GBDATA *gb_key_data = GB_search(gb_copy, "presets/key_data", GB_FIND);
3121        GBDATA *gb_key      = GBT_find_item_rel_item_data(gb_key_data, "key_name", "full_name");
3122
3123        TEST_REJECT_NULL(gb_key);
3124        TEST_EXPECT_NO_ERROR(GB_clear_temporary(gb_copy)); // allow to copy
3125        TEST_EXPECT(GB_is_ancestor_of(gb_copy, gb_key)); // expect copy is ancestor of key
3126
3127        TEST_EXPECT_ERROR_CONTAINS(GB_copy_std(gb_key, gb_copy), "infinite copy not permitted");
3128        TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_clone(gb_key, gb_copy), "GB_clone destination cannot be part of source");
3129    }
3130
3131    GB_close(gb_main);
3132}
3133
3134#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.