source: tags/arb-7.0.1/ARBDB/arbdb.cxx

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