source: branches/profile/ARBDB/ad_load.cxx

Last change on this file was 12797, checked in by westram, 10 years ago
  • gb_read_bin only increments counter nearly up to filesize
    • set counter to 100% afterwards
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.3 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : ad_load.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <cctype>
12#include <ctime>
13#include <netinet/in.h>
14#include <sys/stat.h>
15
16#include <arbdbt.h>
17#include <arb_str.h>
18#include <arb_file.h>
19#include <arb_defs.h>
20#include <arb_progress.h>
21
22#include "gb_key.h"
23#include "gb_localdata.h"
24#include "gb_map.h"
25#include "gb_load.h"
26#include "ad_io_inline.h"
27
28static int gb_verbose_mode = 0;
29void GB_set_verbose() {
30    gb_verbose_mode = 1;
31}
32
33// ---------------------------------------------------
34//      helper code to read ascii file in portions
35
36#ifdef UNIT_TESTS // UT_DIFF
37#define READING_BUFFER_SIZE 100
38#else
39#define READING_BUFFER_SIZE (1024*32)
40#endif
41
42struct ReadingBuffer {
43    char          *data;
44    ReadingBuffer *next;
45    int            read_bytes;
46};
47
48static ReadingBuffer *unused_reading_buffers = 0;
49
50#if defined(DEBUG)
51// #define CHECK_RELEASED_BUFFERS
52#endif // DEBUG
53
54#if defined(CHECK_RELEASED_BUFFERS)
55static int is_a_unused_reading_buffer(ReadingBuffer *rb) {
56    if (unused_reading_buffers) {
57        ReadingBuffer *check = unused_reading_buffers;
58        for (; check; check = check->next) {
59            if (check == rb) return 1;
60        }
61    }
62    return 0;
63}
64#endif // CHECK_RELEASED_BUFFERS
65
66static ReadingBuffer *allocate_ReadingBuffer() {
67    ReadingBuffer *rb = (ReadingBuffer*)malloc(sizeof(*rb)+READING_BUFFER_SIZE);
68    rb->data          = ((char*)rb)+sizeof(*rb);
69    rb->next          = 0;
70    rb->read_bytes    = 0;
71    return rb;
72}
73
74static void release_ReadingBuffers(ReadingBuffer *rb) {
75    ReadingBuffer *last = rb;
76
77#if defined(CHECK_RELEASED_BUFFERS)
78    gb_assert(!is_a_unused_reading_buffer(rb));
79#endif // CHECK_RELEASED_BUFFERS
80    while (last->next) {
81        last = last->next;
82#if defined(CHECK_RELEASED_BUFFERS)
83        gb_assert(!is_a_unused_reading_buffer(last));
84#endif // CHECK_RELEASED_BUFFERS
85    }
86    gb_assert(last && !last->next);
87    last->next              = unused_reading_buffers;
88    unused_reading_buffers  = rb;
89}
90static void free_ReadingBuffer(ReadingBuffer *rb) {
91    if (rb) {
92        if (rb->next) free_ReadingBuffer(rb->next);
93        free(rb);
94    }
95}
96
97static ReadingBuffer *read_another_block(FILE *in) {
98    ReadingBuffer *buf = 0;
99    if (unused_reading_buffers) {
100        buf                    = unused_reading_buffers;
101        unused_reading_buffers = buf->next;
102        buf->next              = 0;
103        buf->read_bytes        = 0;
104    }
105    else {
106        buf = allocate_ReadingBuffer();
107    }
108
109    buf->read_bytes = fread(buf->data, 1, READING_BUFFER_SIZE, in);
110
111    return buf;
112}
113
114// ---------------
115//      Reader
116
117struct Reader {
118    FILE          *in;
119    ReadingBuffer *first;                           // allocated
120    GB_ERROR       error;
121
122    ReadingBuffer *current;                         // only reference
123    size_t         current_offset;                  // into 'current->data'
124
125    char   *current_line;
126    int     current_line_allocated;                 // whether 'current_line' was allocated
127    size_t  current_line_size;                      // size of 'current_line' (valid if current_line_allocated == 1)
128    size_t  line_number;
129
130};
131
132typedef unsigned long ReaderPos; // absolute position (relative to ReadingBuffer 'first')
133#define NOPOS (-1UL)
134
135
136static Reader *openReader(FILE *in) {
137    Reader *r = (Reader*)malloc(sizeof(*r));
138
139    gb_assert(unused_reading_buffers == 0);
140
141    r->in    = in;
142    r->error = 0;
143    r->first = read_another_block(r->in);
144
145    r->current_offset = 0;
146    r->current        = r->first;
147
148    r->current_line           = 0;
149    r->current_line_allocated = 0;
150    r->line_number            = 0;
151
152    return r;
153}
154
155static void freeCurrentLine(Reader *r) {
156    if (r->current_line_allocated && r->current_line) {
157        free(r->current_line);
158        r->current_line_allocated = 0;
159    }
160}
161
162static GB_ERROR closeReader(Reader *r) {
163    GB_ERROR error = r->error;
164
165    free_ReadingBuffer(r->first);
166    free_ReadingBuffer(unused_reading_buffers);
167    unused_reading_buffers = 0;
168
169    freeCurrentLine(r);
170    free(r);
171
172    return error;
173}
174
175static void releasePreviousBuffers(Reader *r) {
176    /* Release all buffers before current position.
177     * Warning: This invalidates all offsets!
178     */
179    ReadingBuffer *last_rel  = 0;
180    ReadingBuffer *old_first = r->first;
181
182    while (r->first != r->current) {
183        last_rel = r->first;
184        r->first = r->first->next;
185    }
186
187    if (last_rel) {
188        last_rel->next = 0;     // avoid to release 'current'
189        release_ReadingBuffers(old_first);
190        r->first       = r->current;
191    }
192}
193
194static char *getPointer(const Reader *r) {
195    return r->current->data + r->current_offset;
196}
197
198static ReaderPos getPosition(const Reader *r) {
199    ReaderPos      p = 0;
200    ReadingBuffer *b = r->first;
201    while (b != r->current) {
202        gb_assert(b);
203        p += b->read_bytes;
204        b  = b->next;
205    }
206    p += r->current_offset;
207    return p;
208}
209
210static int gotoNextBuffer(Reader *r) {
211    if (!r->current->next) {
212        if (r->current->read_bytes < READING_BUFFER_SIZE) { // eof
213            return 0;
214        }
215        r->current->next = read_another_block(r->in);
216    }
217
218    r->current        = r->current->next;
219    r->current_offset = 0;
220
221    return r->current != 0;
222}
223
224static int movePosition(Reader *r, int offset) {
225    int rest = r->current->read_bytes - r->current_offset - 1;
226
227    gb_assert(offset >= 0);     // not implemented for negative offsets
228    if (rest >= offset) {
229        r->current_offset += offset;
230        return 1;
231    }
232
233    if (gotoNextBuffer(r)) {
234        offset -= rest+1;
235        return offset == 0 ? 1 : movePosition(r, offset);
236    }
237
238    // in last buffer and position behind end -> position after last element
239    // (Note: last buffer cannot be full - only empty)
240    r->current_offset = r->current->read_bytes;
241    return 0;
242}
243
244static int gotoChar(Reader *r, char lookfor) {
245    const char *data  = r->current->data + r->current_offset;
246    size_t      size  = r->current->read_bytes-r->current_offset;
247    const char *found = (const char *)memchr(data, lookfor, size);
248
249    if (found) {
250        r->current_offset += (found-data);
251        return 1;
252    }
253
254    if (gotoNextBuffer(r)) {
255        return gotoChar(r, lookfor);
256    }
257
258    // in last buffer and char not found -> position after last element
259    // (Note: last buffer cannot be full - only empty)
260    r->current_offset = r->current->read_bytes;
261    return 0;
262}
263
264static char *getLine(Reader *r) {
265    releasePreviousBuffers(r);
266
267    {
268        ReaderPos      start        = getPosition(r);
269        ReadingBuffer *start_buffer = r->current;
270        char          *start_ptr    = getPointer(r);
271        int            eol_found    = gotoChar(r, '\n');
272
273        // now current position is on EOL or EOF
274
275        ReaderPos      eol        = getPosition(r);
276        ReadingBuffer *eol_buffer = r->current;
277        char          *eol_ptr    = getPointer(r);
278
279        movePosition(r, 1);
280
281        if (start_buffer == eol_buffer) { // start and eol in one ReadingBuffer -> no copy
282            freeCurrentLine(r);
283            r->current_line           = start_ptr;
284            r->current_line_allocated = 0;
285            eol_ptr[0]                = 0; // eos
286        }
287        else { // otherwise build a copy of the string
288            size_t  line_length = eol-start+1;
289            char   *bp;
290            size_t  len;
291
292            if (r->current_line_allocated == 0 || r->current_line_size < line_length) { // need alloc
293                freeCurrentLine(r);
294                r->current_line           = (char*)malloc(line_length);
295                r->current_line_size      = line_length;
296                r->current_line_allocated = 1;
297
298                gb_assert(r->current_line);
299            }
300
301            // copy contents of first buffer
302            bp            = r->current_line;
303            len           = start_buffer->read_bytes - (start_ptr-start_buffer->data);
304            memcpy(bp, start_ptr, len);
305            bp           += len;
306            start_buffer  = start_buffer->next;
307
308            // copy contents of middle buffers
309            while (start_buffer != eol_buffer) {
310                memcpy(bp, start_buffer->data, start_buffer->read_bytes);
311                bp           += start_buffer->read_bytes;
312                start_buffer  = start_buffer->next;
313            }
314
315            // copy contents from last buffer
316            len        = eol_ptr-start_buffer->data;
317            memcpy(bp, start_buffer->data, len);
318            bp        += len;
319            bp[0]  = 0; // eos
320        }
321
322        if (!eol_found && r->current_line[0] == 0)
323            return 0;               // report "eof seen"
324
325    }
326
327    ++r->line_number;
328    return r->current_line;
329}
330
331/* ----------------------------------------
332 * ASCII format
333 *
334 * Versions:
335 *
336 * V0  - 20.6.95
337 * V1  Full save
338 * V2  Differential save
339 * V3  May read from stdin. Skipped support for old ASCII format.
340 */
341
342static const char *getToken(char **line) {
343    char *token  = *line;
344    (*line)     += strcspn(*line, " \t");
345
346    if ((*line)[0]) {                               // sth follows behind token
347        (*line)[0]  = 0;                            // terminate token
348        (*line)++;
349        (*line)    += strspn(*line, " \t");         // goto next token
350    }
351
352    return token;
353}
354
355static GB_ERROR set_protection_level(GB_MAIN_TYPE *Main, GBDATA *gbd, const char *p) {
356    int      secr, secw, secd, lu;
357    GB_ERROR error = 0;
358
359    secr = secw = secd = 0;
360    lu   = 0;
361
362    if (p && p[0] == ':') {
363        long i;
364
365        secd = p[1]; A_TO_I(secd);
366        secw = p[2]; A_TO_I(secw);
367        secr = p[3]; A_TO_I(secr);
368
369        if (secd<0 || secd>7) error = GBS_global_string("Illegal protection level %i", secd);
370        else if (secw<0 || secw>7) error = GBS_global_string("Illegal protection level %i", secw);
371        else if (secr<0 || secr>7) error = GBS_global_string("Illegal protection level %i", secr);
372
373        lu = atoi(p+4);
374
375        for (i=Main->last_updated; i<=lu; ++i) {
376            gb_assert(i<ALLOWED_DATES);
377            Main->dates[i]     = strdup("unknown date");
378            Main->last_updated = lu+1;
379        }
380    }
381
382    if (!error) {
383        gbd->flags.security_delete = secd;
384        gbd->flags.security_write  = secw;
385        gbd->flags.security_read   = secr;
386        gbd->flags2.last_updated   = lu;
387    }
388
389    return error;
390}
391
392static GB_ERROR gb_parse_ascii_rek(Reader *r, GBCONTAINER *gb_parent, const char *parent_name) {
393    // if parent_name == 0 -> we are parsing at root-level
394    GB_ERROR      error = 0;
395    int           done  = 0;
396    GB_MAIN_TYPE *Main  = GBCONTAINER_MAIN(gb_parent);
397
398    while (!error && !done) {
399        char *line = getLine(r);
400        if (!line) break;
401
402    rest :
403        line += strspn(line, " \t"); // goto first non-whitespace
404
405        if (line[0]) {          // not empty
406            if (line[0] == '/' && line[1] == '*') { // comment
407                char *eoc = strstr(line+2, "*/");
408                if (eoc) {
409                    line = eoc+2;
410                    goto rest;
411                }
412                error = "expected '*/'";
413            }
414            else { // real content
415                const char *name = getToken(&line);
416
417                if (name[0] == '%' && name[1] == ')' && !name[2]) { // close container
418                    if (!parent_name) {
419                        error = "Unexpected '%)' (not allowed outside container)";
420                    }
421                    else {
422                        if (line[0] == '/' && line[1] == '*') { // comment at container-end
423                            char *eoc  = strstr(line+2, "*/");
424                            if (!eoc) {
425                                error = "expected '*/'"; 
426                            }
427                            else {
428                                line += 2;
429                                *eoc  = 0;
430                                if (strcmp(line, parent_name) != 0) {
431                                    fprintf(stderr,
432                                            "Warning: comment at end of container ('%s') does not match name of container ('%s').\n"
433                                            "         (might be harmless if you've edited the file and did not care about these comments)\n",
434                                            line, parent_name); 
435                                }
436                                line = eoc+2;
437                            }
438                        }
439                        done = 1;
440                    }
441                }
442                else {
443                    const char *protection = NULL;
444                    if (line[0] == ':') {           // protection level
445                        protection = getToken(&line);
446                    }
447
448                    bool readAsString = true;
449                    if (line[0] == '%') {
450                        readAsString = false;
451
452                        const char *type = getToken(&line);
453
454                        if (type[1] == 0 || type[2] != 0) {
455                            error = GBS_global_string("Syntax error in type '%s' (expected %% and 1 more character)", type);
456                        }
457                        else {
458                            if (type[1] == '%' || type[1] == '$') {   // container
459                                if (line[0] == '(' && line[1] == '%') {
460                                    char        *cont_name = strdup(name);
461                                    GBCONTAINER *gbc       = gb_make_container(gb_parent, cont_name, -1, 0);
462
463                                    char *protection_copy = nulldup(protection);
464                                    bool  marked          = type[1] == '$';
465                                    error                 = gb_parse_ascii_rek(r, gbc, cont_name);
466                                    // CAUTION: most buffer variables are invalidated NOW!!!
467
468                                    if (!error) error = set_protection_level(Main, gbc, protection_copy);
469                                    if (marked) GB_write_flag(gbc, 1);
470
471                                    free(protection_copy);
472                                    free(cont_name);
473                                }
474                                else {
475                                    error = "Expected '(%' after '%%'";
476                                }
477                            }
478                            else {
479                                GB_TYPES gb_type = GB_NONE;
480
481                                switch (type[1]) {
482                                    case 'i': gb_type = GB_INT; break;
483                                    case 'l': gb_type = GB_LINK; break;
484                                    case 'y': gb_type = GB_BYTE; break;
485                                    case 'f': gb_type = GB_FLOAT; break;
486                                    case 'I': gb_type = GB_BITS; break;
487
488                                    case 'Y': gb_type = GB_BYTES; break;
489                                    case 'N': gb_type = GB_INTS; break;
490                                    case 'F': gb_type = GB_FLOATS; break;
491                                    case 's': gb_type = GB_STRING; readAsString = true; break; // special case (string starts with '%')
492
493                                    default:
494                                        error = GBS_global_string("Unknown type '%s'", type);
495                                        break;
496                                }
497
498                                if (!error && !readAsString) {
499                                    gb_assert(gb_type != GB_NONE);
500
501                                    GBENTRY *gb_new    = gb_make_entry(gb_parent, name, -1, 0, gb_type);
502                                    if (!gb_new) error = GB_await_error();
503                                    else {
504                                        switch (type[1]) {
505                                            case 'i': error = GB_write_int(gb_new, atoi(line)); break;
506                                            case 'l': error = GB_write_link(gb_new, line); break;
507                                            case 'y': error = GB_write_byte(gb_new, atoi(line)); break;
508                                            case 'f': error = GB_write_float(gb_new, GB_atof(line)); break;
509                                            case 'I': {
510                                                int len = strlen(line);
511                                                gb_assert(line[0] == '\"');
512                                                gb_assert(line[len-1] == '\"');
513                                                error   = GB_write_bits(gb_new, line+1, len-2, "-"); break;
514                                            }
515
516                                            case 'Y': if (gb_ascii_2_bin(line, gb_new)) error = "syntax error in byte-array"; break;
517                                            case 'N': if (gb_ascii_2_bin(line, gb_new)) error = "syntax error in int-array"; break;
518                                            case 'F': if (gb_ascii_2_bin(line, gb_new)) error = "syntax error in float-array"; break;
519
520                                            default: gb_assert(0);  // forgot a case ?
521                                        }
522
523                                        if (!error) error = set_protection_level(Main, gb_new, protection);
524                                    }
525                                }
526                            }
527                        }
528                    }
529                   
530                    if (readAsString) {
531                        if (line[0] != '\"') {
532                            error = GBS_global_string("Unexpected content '%s'", line);
533                        }
534                        else {                      // string entry
535                            char *string_start = line+1;
536                            char *end          = GBS_fconvert_string(string_start);
537
538                            if (!end) error = "Cannot convert string (contains zero char)";
539                            else {
540                                GBDATA *gb_string     = gb_make_entry(gb_parent, name, -1, 0, GB_STRING);
541                                if (!gb_string) error = GB_await_error();
542                                else {
543                                    error             = GB_write_string(gb_string, string_start);
544                                    if (!error) error = set_protection_level(Main, gb_string, protection);
545                                }
546                            }
547                        }
548                    }
549                }
550            }
551        }
552    }
553
554    return error;
555}
556
557static GB_ERROR gb_parse_ascii(Reader *r, GBCONTAINER *gb_parent) {
558    GB_ERROR error = gb_parse_ascii_rek(r, gb_parent, 0);
559    if (error) {
560        error = GBS_global_string("%s in line %zu", error, r->line_number);
561    }
562    return error;
563}
564
565static GB_ERROR gb_read_ascii(const char *path, GBCONTAINER *gbc) {
566    /* This loads an ACSII database
567     * if path == "-" -> read from stdin
568     */
569
570    FILE     *in         = 0;
571    GB_ERROR  error      = 0;
572    int       close_file = 0;
573
574    if (strcmp(path, "-") == 0) {
575        in = stdin;
576    }
577    else {
578        in              = fopen(path, "rt");
579        if (!in) error  = GBS_global_string("Can't open '%s'", path);
580        else close_file = 1;
581    }
582
583    if (!error) {
584        Reader *r = openReader(in);
585
586        GB_search(gbc, GB_SYSTEM_FOLDER, GB_CREATE_CONTAINER); // Switch to Version 3
587
588        error = gb_parse_ascii(r, gbc);
589
590        GB_ERROR cl_error = closeReader(r);
591        if (!error) error = cl_error;
592    }
593
594    if (close_file) fclose(in);
595    return error;
596}
597
598// --------------------------
599//      Read binary files
600
601static long gb_recover_corrupt_file(GBCONTAINER *gbc, FILE *in, GB_ERROR recovery_reason, bool loading_quick_save) {
602    // search pattern dx xx xx xx string 0
603    // returns 0 if recovery was able to resync
604    static FILE *old_in = 0;
605    static unsigned char *file = 0;
606    static long size = 0;
607    if (!GBCONTAINER_MAIN(gbc)->allow_corrupt_file_recovery) {
608        if (!recovery_reason) { recovery_reason = GB_await_error(); }
609        char       *reason         = strdup(recovery_reason);
610        const char *located_reason = GBS_global_string("%s (inside '%s')", reason, GB_get_db_path(gbc));
611
612        if (loading_quick_save) {
613            GB_export_error(located_reason);
614        }
615        else {
616            GB_export_errorf("%s\n"
617                             "(parts of your database might be recoverable using 'arb_repair yourDB.arb newName.arb')\n",
618                             located_reason);
619        }
620        free(reason);
621        return -1;
622    }
623    long pos = ftell(in);
624    if (old_in != in) {
625        file = (unsigned char *)GB_map_FILE(in, 0);
626        old_in = in;
627        size = GB_size_of_FILE(in);
628    }
629    for (; pos<size-10; pos ++) {
630        if ((file[pos] & 0xf0) == (GB_STRING_SHRT<<4)) {
631            long s;
632            int c;
633            for (s = pos + 4; s<size && file[s]; s++) {
634                c = file[s];
635                if (! (isalnum(c) || isspace(c) || strchr("._;:,", c))) break;
636            }
637            if (s< size && s > pos+11 && !file[s]) {    // we found something
638                gb_local->search_system_folder = true;
639                return fseek(in, pos, 0);
640            }
641        }
642    }
643    return -1;          // no short string found
644}
645
646// ----------------------------------------
647// #define DEBUG_READ
648#if defined(DEBUG_READ)
649
650static void DEBUG_DUMP_INDENTED(long deep, const char *s) {
651    printf("%*s%s\n", (int)deep, "", s);
652}
653
654#else
655#define DEBUG_DUMP_INDENTED(d, s)
656#endif // DEBUG_READ
657// ----------------------------------------
658
659static long gb_read_bin_rek_V2(FILE *in, GBCONTAINER *gbc_dest, long nitems, long version, 
660                               long reversed, long deep, arb_progress& progress) {
661    GB_MAIN_TYPE *Main = GB_MAIN(gbc_dest);
662
663    DEBUG_DUMP_INDENTED(deep, GBS_global_string("Reading container with %li items", nitems));
664
665    progress.inc_to(ftell(in));
666    if (progress.aborted()) {
667        GB_export_error(progress.error_if_aborted());
668        return -1;
669    }
670
671    gb_create_header_array(gbc_dest, (int)nitems);
672    gb_header_list *header = GB_DATA_LIST_HEADER(gbc_dest->d);
673    if (deep == 0 && GBCONTAINER_MAIN(gbc_dest)->allow_corrupt_file_recovery) {
674        GB_warning("Now reading to end of file (trying to recover data from broken database)");
675        nitems = 10000000; // read forever at highest level
676    }
677
678    bool is_quicksave = version != 1;
679
680    for (long item = 0; item<nitems; item++) {
681        long type = getc(in);
682        DEBUG_DUMP_INDENTED(deep, GBS_global_string("Item #%li/%li type=%02lx (filepos=%08lx)", item+1, nitems, type, ftell(in)-1));
683        if (type == EOF) {
684            if (GBCONTAINER_MAIN(gbc_dest)->allow_corrupt_file_recovery) {
685                GB_export_error("End of file reached (no error)");
686            }
687            else {
688                GB_export_error("Unexpected end of file seen");
689            }
690            return -1;
691        }
692        if (!type) {
693            int func;
694            if (version == 1) { // master file
695                if (gb_recover_corrupt_file(gbc_dest, in, "Unknown DB type 0 (DB version=1)", is_quicksave)) return -1;
696                continue;
697            }
698            func = getc(in);
699            switch (func) {
700                case 1: {    //  delete entry
701                    int index = (int)gb_get_number(in);
702                    if (index >= gbc_dest->d.nheader) {
703                        gb_create_header_array(gbc_dest, index+1);
704                        header = GB_DATA_LIST_HEADER(gbc_dest->d);
705                    }
706
707                    GBDATA *gb2 = GB_HEADER_LIST_GBD(header[index]);
708                    if (gb2) {
709                        gb_delete_entry(gb2);
710                    }
711                    else {
712                        header[index].flags.set_change(GB_DELETED);
713                    }
714
715                    break;
716                }
717                default:
718                    if (gb_recover_corrupt_file(gbc_dest, in, GBS_global_string("Unknown func=%i", func), is_quicksave)) return -1;
719                    continue;
720            }
721            continue;
722        }
723
724        long    security = getc(in);
725        long    type2    = (type>>4)&0xf;
726        GBQUARK key      = (GBQUARK)gb_get_number(in);
727
728        if (key >= Main->keycnt || !Main->keys[key].key) {
729            const char *reason = GBS_global_string("database entry with unknown field quark %i", key);
730            if (gb_recover_corrupt_file(gbc_dest, in, reason, is_quicksave)) return -1;
731            continue;
732        }
733
734        DEBUG_DUMP_INDENTED(deep, GBS_global_string("key='%s' type2=%li", Main->keys[key].key, type2));
735
736        GBENTRY     *gbe = NULL;
737        GBCONTAINER *gbc = NULL;
738        GBDATA      *gbd = NULL;
739
740        {
741            int index;
742            if (version == 2) {
743                index = (int)gb_get_number(in);
744                if (index >= gbc_dest->d.nheader) {
745                    gb_create_header_array(gbc_dest, index+1);
746                    header = GB_DATA_LIST_HEADER(gbc_dest->d);
747                }
748
749                if (index >= 0 && (gbd = GB_HEADER_LIST_GBD(header[index]))!=NULL) {
750                    if ((gbd->type() == GB_DB) != (type2 == GB_DB)) {
751                        GB_internal_error("Type changed, you may loose data");
752                        gb_delete_entry(gbd);
753                        SET_GB_HEADER_LIST_GBD(header[index], NULL);
754                    }
755                    else {
756                        if (type2 == GB_DB) {
757                            gbc = gbd->as_container();
758                        }
759                        else {
760                            gbe = gbd->as_entry();
761                            gbe->free_data();
762                        }
763                    }
764                }
765            }
766            else index = -1;
767
768            if (!gbd) {
769                if (type2 == (long)GB_DB) {
770                    gbd = gbc = gb_make_container(gbc_dest, NULL, index, key);
771                }
772                else {
773                    gbd = gbe = gb_make_entry(gbc_dest, NULL, index, key, (GB_TYPES)type2);
774                    gbe->index_check_out();
775                }
776            }
777        }
778
779        gb_assert(implicated(gbe, gbe == gbd));
780        gb_assert(implicated(gbc, gbc == gbd));
781        gb_assert(implicated(gbd, contradicted(gbe, gbc)));
782
783        if (version == 2) {
784            gbd->create_extended();
785            gbd->touch_creation_and_update(Main->clock);
786            header[gbd->index].flags.ever_changed = 1;
787        }
788        else {
789            Main->keys[key].nref_last_saved++;
790        }
791
792        gbd->flags.security_delete     = type >> 1;
793        gbd->flags.security_write      = ((type&1) << 2) + (security >> 6);
794        gbd->flags.security_read       = security >> 3;
795        gbd->flags.compressed_data     = security >> 2;
796        header[gbd->index].flags.flags = (int)((security >> 1) & 1);
797        gbd->flags.unused              = security >> 0;
798        gbd->flags2.last_updated       = getc(in);
799
800        switch (type2) {
801            case GB_INT:
802                {
803                    GB_UINT4 buffer;
804                    if (!fread((char*)&buffer, sizeof(GB_UINT4), 1, in)) {
805                        GB_export_error("File too short, seems truncated");
806                        return -1;
807                    }
808                    gbe->info.i = ntohl(buffer);
809                    break;
810                }
811
812            case GB_FLOAT:
813                if (!fread((char*)&gbe->info.i, sizeof(float), 1, in)) {
814                    GB_export_error("File too short, seems truncated");
815                    return -1;
816                }
817                break;
818            case GB_STRING_SHRT: {
819                long  i    = GB_give_buffer_size();
820                char *buff = GB_give_buffer(GBTUM_SHORT_STRING_SIZE+2);
821                char *p    = buff;
822
823                long size = 0;
824                while (1) {
825                    for (; size<i; size++) {
826                        if (!(*(p++) = getc(in))) goto shrtstring_fully_loaded;
827                    }
828                    i = i*3/2;
829                    buff = GB_increase_buffer(i);
830                    p = buff + size;
831                }
832                  shrtstring_fully_loaded :
833                gbe->insert_data(buff, size, size+1);
834                break;
835            }
836            case GB_STRING:
837            case GB_LINK:
838            case GB_BITS:
839            case GB_BYTES:
840            case GB_INTS:
841            case GB_FLOATS: {
842                long size    = gb_get_number(in);
843                long memsize = gb_get_number(in);
844
845                DEBUG_DUMP_INDENTED(deep, GBS_global_string("size=%li memsize=%li", size, memsize));
846
847                GBENTRY_memory storage(gbe, size, memsize);
848
849                long i = fread(storage, 1, (size_t)memsize, in);
850                if (i!=memsize) {
851                    gb_read_bin_error(in, gbe, "Unexpected EOF found");
852                    return -1;
853                }
854                break;
855            }
856            case GB_DB: {
857                long size = gb_get_number(in);
858                // gbc->d.size  is automatically incremented
859                if (gb_read_bin_rek_V2(in, gbc, size, version, reversed, deep+1, progress)) {
860                    if (!GBCONTAINER_MAIN(gbc_dest)->allow_corrupt_file_recovery) {
861                        return -1;
862                    }
863                }
864                break;
865            }
866            case GB_BYTE:
867                gbe->info.i = getc(in);
868                break;
869            default:
870                gb_read_bin_error(in, gbd, "Unknown type");
871                if (gb_recover_corrupt_file(gbc_dest, in, NULL, is_quicksave)) {
872                    return GBCONTAINER_MAIN(gbc_dest)->allow_corrupt_file_recovery
873                        ? 0                         // loading stopped
874                        : -1;
875                }
876
877                continue;
878        }
879    }
880    return 0;
881}
882
883static GBDATA *gb_search_system_folder_rek(GBDATA *gbd) {
884    GBDATA *gb2;
885    GBDATA *gb_result = 0;
886    for (gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {
887        int type = GB_read_type(gb2);
888        if (type != GB_DB) continue;
889        if (!strcmp(GB_SYSTEM_FOLDER, GB_read_key_pntr(gb2))) {
890            gb_result = gb2;
891            break;
892        }
893    }
894    return gb_result;
895}
896
897
898static void gb_search_system_folder(GBDATA *gb_main) {
899    /* Search a system folder within the database tree
900     * and copy it to main level
901     */
902    GBDATA   *gb_oldsystem;
903    GB_ERROR  error;
904    GBDATA   *gb_system = GB_entry(gb_main, GB_SYSTEM_FOLDER);
905    if (gb_system) return;
906
907    GB_warning("Searching system information");
908    gb_oldsystem = gb_search_system_folder_rek(gb_main);
909    if (!gb_oldsystem) {
910        GB_warning("!!!!! not found (bad)");
911        return;
912    }
913    gb_system = GB_search(gb_main, GB_SYSTEM_FOLDER, GB_CREATE_CONTAINER);
914    error = GB_copy(gb_system, gb_oldsystem);
915    if (!error) error = GB_delete(gb_oldsystem);
916    if (error) GB_warning(error);
917    GB_warning("***** found (good)");
918}
919
920inline bool read_keyword(const char *expected_keyword, FILE *in, GBCONTAINER *gbc) {
921    gb_assert(strlen(expected_keyword) == 4); // mandatory
922
923    long val         = gb_read_in_uint32(in, 0);
924    bool as_expected = strncmp((char*)&val, expected_keyword, 4) == 0;
925   
926    if (!as_expected) {
927        gb_read_bin_error(in, gbc, GBS_global_string("keyword '%s' not found", expected_keyword));
928    }
929    return as_expected;
930}
931
932static long gb_read_bin(FILE *in, GBCONTAINER *gbc, bool allowed_to_load_diff, arb_progress& progress) {
933    int   c = 1;
934    long  i;
935    long  error;
936    long  j, k;
937    long  version;
938    long  nodecnt;
939    long  first_free_key;
940    char *buffer, *p;
941
942    GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(gbc);
943
944    while (c && c != EOF) {
945        c = getc(in);
946    }
947    if (c==EOF) {
948        gb_read_bin_error(in, gbc, "First zero not found");
949        return 1;
950    }
951
952    if (!read_keyword("vers", in, gbc)) return 1;
953
954    // detect byte order in ARB file
955    i = gb_read_in_uint32(in, 0);
956    bool reversed;
957    switch (i) {
958        case 0x01020304: reversed = false; break;
959        case 0x04030201: reversed = true; break;
960        default:
961            gb_read_bin_error(in, gbc, "keyword '^A^B^C^D' not found");
962            return 1;
963    }
964
965    version = gb_read_in_uint32(in, reversed);
966    if (version == 0) {
967        gb_read_bin_error(in, gbc, "ARB Database version 0 no longer supported (rev [9647])");
968        return 1;
969    }
970    if (version>2) {
971        gb_read_bin_error(in, gbc, "ARB Database version > '2'");
972        return 1;
973    }
974
975    if (version == 2 && !allowed_to_load_diff) {
976        GB_export_error("This is not a primary arb file, please select the master"
977                        " file xxx.arb");
978        return 1;
979    }
980
981    if (!read_keyword("keys", in, gbc)) return 1;
982
983    if (!Main->key_2_index_hash) Main->key_2_index_hash = GBS_create_hash(ALLOWED_KEYS, GB_MIND_CASE);
984
985    first_free_key = 0;
986    Main->free_all_keys();
987
988    buffer = GB_give_buffer(256);
989    while (1) {         // read keys
990        long nrefs = 0;
991        if (version) {
992            nrefs = gb_get_number(in);
993        }
994        p = buffer;
995        for (k=0; ; k++) {
996            c = getc(in);
997            if (!c) break;
998            if (c==EOF) {
999                gb_read_bin_error(in, gbc, "unexpected EOF while reading keys");
1000                return 1;
1001            }
1002            *(p++) = c;
1003        }
1004        *p = 0;
1005
1006        if (k > GB_KEY_LEN_MAX) {
1007            printf("Warning: Key '%s' exceeds maximum keylength (%i)\n"
1008                   "         Please do NOT create such long keys!\n",
1009                   buffer, GB_KEY_LEN_MAX);
1010        }
1011        if (p == buffer) break;
1012
1013        if (*buffer == 1) {                                                   // empty key
1014            long index = gb_create_key(Main, 0, false);
1015
1016            Main->keys[index].key           = 0;
1017            Main->keys[index].nref          = 0;
1018            Main->keys[index].next_free_key = first_free_key;
1019
1020            first_free_key = index;
1021        }
1022        else {
1023            long index = gb_create_key(Main, buffer, false);
1024
1025            Main->keys[index].nref = nrefs;
1026        }
1027    }
1028
1029    Main->first_free_key = first_free_key;
1030
1031    if (!read_keyword("time", in, gbc)) return 1;
1032   
1033    for (j=0; j<(ALLOWED_DATES-1); j++) {         // read times
1034        p = buffer;
1035        for (k=0; k<256; k++) {
1036            c = getc(in);
1037            if (!c) break;
1038            if (c==EOF) {
1039                gb_read_bin_error(in, gbc, "unexpected EOF while reading times");
1040                return 1;
1041            }
1042            *(p++) = c;
1043        }
1044        *p = 0;
1045        if (p == buffer) break;
1046        freedup(Main->dates[j], buffer);
1047    }
1048    if (j>=(ALLOWED_DATES-1)) {
1049        gb_read_bin_error(in, gbc, "too many date entries");
1050        return 1;
1051    }
1052    Main->last_updated = (unsigned int)j;
1053
1054    if (!read_keyword("data", in, gbc)) return 1;
1055
1056    nodecnt = gb_read_in_uint32(in, reversed);
1057    GB_give_buffer(256);
1058
1059    if (version==1) {                                     // teste auf map file falls version == 1
1060        long          mode = GB_mode_of_link(Main->path); // old master
1061        GB_CSTR       map_path;
1062        unsigned long time_of_db;
1063
1064        if (S_ISLNK(mode)) {
1065            char *path2 = GB_follow_unix_link(Main->path);
1066            map_path    = gb_mapfile_name(path2);
1067            time_of_db  = GB_time_of_file(path2);
1068            free(path2);
1069        }
1070        else {
1071            map_path = gb_mapfile_name(Main->path);
1072            time_of_db  = GB_time_of_file(Main->path);
1073        }
1074
1075        bool          mapped          = false;
1076        GB_ERROR      map_fail_reason = NULL;
1077        gb_map_header mheader;
1078
1079        switch (gb_is_valid_mapfile(map_path, &mheader, 0)) {
1080            case -1: map_fail_reason = GBS_global_string("no FastLoad File '%s' found", map_path); break;
1081            case  0: map_fail_reason = GB_await_error(); break;
1082            case  1: {
1083                unsigned long time_of_map = GB_time_of_file(map_path);
1084
1085                if (time_of_map != time_of_db) { // timestamp of mapfile differs
1086                    unsigned long diff = time_of_map>time_of_db ? time_of_map-time_of_db : time_of_db-time_of_map;
1087                    fprintf(stderr, "Warning: modification times of DB and fastload file differ (DB=%lu fastload=%lu diff=%lu)\n",
1088                            time_of_db, time_of_map, diff);
1089
1090                    if (diff>5) {
1091                        map_fail_reason = "modification times of DB and fastload file differ (too much)";
1092                    }
1093                    else {
1094                        fprintf(stderr, "(accepting modification time difference of %lu seconds)\n", diff);
1095                    }
1096                }
1097
1098                if (!map_fail_reason) {
1099                    if (gb_main_array[mheader.main_idx]==NULL) {
1100                        GBCONTAINER *new_gbc = (GBCONTAINER*)gb_map_mapfile(map_path);
1101
1102                        if (new_gbc) {
1103                            GBCONTAINER *father  = GB_FATHER(gbc);
1104                            GB_MAIN_IDX  new_idx = mheader.main_idx;
1105                            GB_MAIN_IDX  old_idx = father->main_idx;
1106
1107                            long gbc_index = gbc->index;
1108
1109                            GB_commit_transaction((GBDATA*)gbc);
1110
1111                            gb_assert(new_gbc->main_idx == new_idx);
1112                            gb_assert((new_idx % GB_MAIN_ARRAY_SIZE) == new_idx);
1113
1114                            gb_main_array[new_idx] = Main;
1115
1116                            gbm_free_mem(Main->root_container, sizeof(GBCONTAINER), GB_QUARK_2_GBMINDEX(Main, 0));
1117
1118                            Main->root_container = new_gbc;
1119                            father->main_idx     = new_idx;
1120
1121                            SET_GBCONTAINER_ELEM(father, gbc_index, NULL); // unlink old main-entry
1122
1123                            gbc = new_gbc;
1124                            SET_GB_FATHER(gbc, father);
1125                       
1126                            SET_GBCONTAINER_ELEM(father, gbc->index, (GBDATA*)gbc); // link new main-entry
1127
1128                            gb_main_array[old_idx]    = NULL;
1129
1130                            GB_begin_transaction((GBDATA*)gbc);
1131                            mapped = true;
1132                        }
1133                    }
1134                    else {
1135                        map_fail_reason = GBS_global_string("FastLoad-File index conflict (%s, %i)", map_path, mheader.main_idx);
1136                    }
1137                }
1138           
1139                break;
1140            }
1141            default: gb_assert(0); break;
1142        }
1143
1144        gb_assert(mapped || map_fail_reason);
1145
1146        if (mapped) return 0; // succeded loading mapfile -> no need to load normal DB file
1147        GB_informationf("ARB: %s => loading entire DB", map_fail_reason);
1148    }
1149
1150    // load binary DB file
1151
1152    switch (version) {
1153        case 2: // quick save file
1154            for (i=1; i < Main->keycnt; i++) {
1155                if (Main->keys[i].key) {
1156                    Main->keys[i].nref_last_saved = Main->keys[i].nref;
1157                }
1158            }
1159
1160            if (Main->clock<=0) Main->clock++;
1161            // fall-through
1162        case 1: // master arb file
1163            error = gb_read_bin_rek_V2(in, gbc, nodecnt, version, reversed, 0, progress);
1164            break;
1165        default:
1166            GB_internal_errorf("Sorry: This ARB Version does not support database format V%li", version);
1167            error = 1;
1168    }
1169
1170    if (gb_local->search_system_folder) {
1171        gb_search_system_folder(gbc);
1172    }
1173
1174    switch (version) {
1175        case 2:
1176        case 1:
1177            for (i=1; i < Main->keycnt; i++) {
1178                if (Main->keys[i].key) {
1179                    Main->keys[i].nref = Main->keys[i].nref_last_saved;
1180                }
1181            }
1182            break;
1183        default:
1184            break;
1185    }
1186
1187    return error;
1188}
1189
1190// ----------------------
1191//      OPEN DATABASE
1192
1193static long gb_next_main_idx_for_mapfile = 0;
1194
1195void GB_set_next_main_idx(long idx) {
1196    gb_next_main_idx_for_mapfile = idx;
1197}
1198
1199GB_MAIN_IDX gb_make_main_idx(GB_MAIN_TYPE *Main) {
1200    static int initialized = 0;
1201    GB_MAIN_IDX idx;
1202
1203    if (!initialized) {
1204        for (idx=0; idx<GB_MAIN_ARRAY_SIZE; idx++) gb_main_array[idx] = NULL;
1205        initialized = 1;
1206    }
1207    if (gb_next_main_idx_for_mapfile<=0) {
1208        while (1) {                                 // search for unused array index
1209            idx = (short)GB_random(GB_MAIN_ARRAY_SIZE);
1210            if (gb_main_array[idx]==NULL) break;
1211        }
1212    }
1213    else {
1214        idx = (short)gb_next_main_idx_for_mapfile;
1215        gb_next_main_idx_for_mapfile = 0;
1216    }
1217
1218    gb_assert((idx%GB_MAIN_ARRAY_SIZE) == idx);
1219
1220    gb_main_array[idx] = Main;
1221    return idx;
1222}
1223
1224void GB_MAIN_TYPE::release_main_idx() { 
1225    if (dummy_father) {
1226        GB_MAIN_IDX idx = dummy_father->main_idx;
1227        gb_assert(gb_main_array[idx] == this);
1228        gb_main_array[idx] = NULL;
1229    }
1230}
1231
1232GB_ERROR GB_MAIN_TYPE::login_remote(const char *db_path, const char *opent) {
1233    GB_ERROR error = NULL;
1234
1235    i_am_server = false;
1236    c_link = gbcmc_open(db_path);
1237    if (!c_link) {
1238        error = GBS_global_string("There is no ARBDB server '%s', please start one or add a filename", db_path);
1239    }
1240    else {
1241        root_container->server_id = 0;
1242
1243        remote_hash = GBS_create_numhash(GB_REMOTE_HASH_SIZE);
1244        error       = initial_client_transaction();
1245
1246        if (!error) {
1247            root_container->flags2.folded_container = 1;
1248
1249            if (strchr(opent, 't')) error      = gb_unfold(root_container, 0, -2);  // tiny
1250            else if (strchr(opent, 'm')) error = gb_unfold(root_container, 1, -2);  // medium (no sequence)
1251            else if (strchr(opent, 'b')) error = gb_unfold(root_container, 2, -2);  // big (no tree)
1252            else if (strchr(opent, 'h')) error = gb_unfold(root_container, -1, -2); // huge (all)
1253            else error                         = gb_unfold(root_container, 0, -2);  // tiny
1254        }
1255    }
1256    return error;
1257}
1258
1259inline bool is_binary_db_id(int id) {
1260    return (id == 0x56430176)
1261        || (id == GBTUM_MAGIC_NUMBER)
1262        || (id == GBTUM_MAGIC_REVERSED);
1263}
1264
1265static GBDATA *GB_login(const char *cpath, const char *opent, const char *user) {
1266    /*! open an ARB database
1267     *
1268     * @param cpath arb database. May be
1269     * - full path of database. (loads DB plus newest quicksave, if any quicksaves exist)
1270     * - full path of a quicksave file (loads DB with specific quicksave)
1271     * - ":" connects to a running ARB DB server (e.g. ARB_NT, arb_db_server)
1272     * @param opent contains one or more of the following characters:
1273     * - 'r' read
1274     * - 'w' write (w/o 'r' it overwrites existing database)
1275     * - 'c' create (if not found)
1276     * - 's'     read only ???
1277     * - 'D' looks for default in $ARBHOME/lib/arb_default if file is not found in $ARB_PROP
1278     *       (only works combined with mode 'c')
1279     * - memory usage:
1280     *   - 't' small memory usage
1281     *   - 'm' medium
1282     *   - 'b' big
1283     *   - 'h' huge
1284     * - 'R' allow corrupt file recovery + opening quicks with no master
1285     * - 'N' assume new database format (does not check whether to convert old->new compression)
1286     * @param user username
1287     *
1288     * @return DB root node (or NULL, in which case an error has been exported)
1289     *
1290     * @see GB_open() and GBT_open()
1291     */
1292    GBCONTAINER   *gbc;
1293    GB_MAIN_TYPE  *Main;
1294    gb_open_types  opentype;
1295    GB_CSTR        quickFile           = NULL;
1296    int            ignoreMissingMaster = 0;
1297    int            loadedQuickIndex    = -1;
1298    GB_ERROR       error               = 0;
1299    char          *path                = strdup(cpath);
1300    bool           dbCreated           = false;
1301
1302    gb_assert(strchr(opent, 'd') == NULL); // mode 'd' is deprecated. You have to use 'D' and store your defaults inside ARBHOME/lib/arb_default
1303
1304    opentype = (!opent || strchr(opent, 'w')) ? gb_open_all : gb_open_read_only_all;
1305
1306    if (strchr(path, ':')) {
1307        ; // remote access
1308    }
1309    else if (GBS_string_matches(path, "*.quick?", GB_MIND_CASE)) {
1310        char *ext = gb_findExtension(path);
1311        gb_assert(ext!=0);
1312        if (isdigit(ext[6]))
1313        {
1314            loadedQuickIndex = atoi(ext+6);
1315            strcpy(ext, ".arb");
1316            quickFile = gb_oldQuicksaveName(path, loadedQuickIndex);
1317            if (strchr(opent, 'R'))     ignoreMissingMaster = 1;
1318        }
1319    }
1320    else if (GBS_string_matches(path, "*.a??", GB_MIND_CASE)) {
1321
1322        char *extension = gb_findExtension(path);
1323
1324        if (isdigit(extension[2]) && isdigit(extension[3]))
1325        {
1326            loadedQuickIndex = atoi(extension+2);
1327            strcpy(extension, ".arb");
1328            quickFile = gb_quicksaveName(path, loadedQuickIndex);
1329            if (strchr(opent, 'R'))     ignoreMissingMaster = 1;
1330        }
1331        else {
1332            char *base = strdup(path);
1333            char *ext = gb_findExtension(base);
1334            {
1335                gb_scandir dir;
1336                ext[0] = 0;
1337                gb_scan_directory(base, &dir);
1338
1339                loadedQuickIndex = dir.highest_quick_index;
1340
1341                if (dir.highest_quick_index!=dir.newest_quick_index)
1342                {
1343                    GB_warning("The QuickSave-File with the highest index-number\n"
1344                               "is not the NEWEST of your QuickSave-Files.\n"
1345                               "If you didn't restore old QuickSave-File from a backup\n"
1346                               "please inform your system-administrator - \n"
1347                               "this may be a serious bug and you may loose your data.");
1348                }
1349
1350                switch (dir.type)
1351                {
1352                    case GB_SCAN_NO_QUICK:
1353                        break;
1354                    case GB_SCAN_NEW_QUICK:
1355                        quickFile = gb_quicksaveName(path, dir.highest_quick_index);
1356                        break;
1357                    case GB_SCAN_OLD_QUICK:
1358                        quickFile = gb_oldQuicksaveName(path, dir.newest_quick_index);
1359
1360                        break;
1361                }
1362            }
1363
1364            free(base);
1365        }
1366    }
1367
1368    if (gb_verbose_mode) {
1369        GB_informationf("ARB: Loading '%s'%s%s", path, quickFile ? " + Changes-File " : "", quickFile ? quickFile : "");
1370    }
1371
1372    error = GB_install_pid(1);
1373    if (error) {
1374        GB_export_error(error);
1375        return 0;
1376    }
1377
1378    GB_init_gb();
1379
1380    Main = new GB_MAIN_TYPE(path);
1381    Main->mark_as_server();
1382
1383    if (strchr(opent, 'R')) Main->allow_corrupt_file_recovery = 1;
1384
1385    ASSERT_RESULT(long, 0, gb_create_key(Main, "main", false));
1386
1387    Main->dummy_father            = gb_make_container(NULL, NULL, -1, 0); // create "main"
1388    Main->dummy_father->main_idx  = gb_make_main_idx(Main);
1389    Main->dummy_father->server_id = GBTUM_MAGIC_NUMBER;
1390    gbc                           = gb_make_container(Main->dummy_father, NULL, -1, 0); // create "main"
1391
1392    Main->root_container = gbc;
1393    gbcm_login(gbc, user);
1394    Main->opentype = opentype;
1395    Main->security_level = 7;
1396
1397    if (path && (strchr(opent, 'r'))) {
1398        if (strchr(path, ':')) {
1399            error = Main->login_remote(path, opent);
1400        }
1401        else {
1402            int read_from_stdin = strcmp(path, "-") == 0;
1403
1404            GB_ULONG time_of_main_file = 0; long i;
1405
1406            Main->mark_as_server();
1407            GB_begin_transaction(gbc);
1408            Main->clock      = 0;                   // start clock
1409
1410            FILE *input = read_from_stdin ? stdin : fopen(path, "rb");
1411            if (!input && ignoreMissingMaster) {
1412                goto load_quick_save_file_only;
1413            }
1414
1415            if (!input) {
1416                if (strchr(opent, 'c')) {
1417                    GB_disable_quicksave(gbc, "Database Created");
1418
1419                    if (strchr(opent, 'D')) { // use default settings
1420                        GB_clear_error(); // with default-files gb_scan_directory (used above) has created an error, cause the path was a fake path
1421                       
1422                        gb_assert(!ARB_strBeginsWith(path, ".arb_prop/")); // do no longer pass path-prefix [deprecated!] 
1423                        char *found_path = GB_property_file(false, path);
1424
1425                        if (!found_path) {
1426                            fprintf(stderr, "file %s not found\n", path);
1427                            dbCreated = true;
1428                        }
1429                        else {
1430                            freeset(path, found_path);
1431                            // cppcheck-suppress deallocuse (false positive; path is reassigned to non-NULL above)
1432                            input = fopen(path, "rb");
1433                        }
1434                    }
1435                    else {
1436                        dbCreated = true;
1437                    }
1438
1439                    if (dbCreated) printf(" database %s created\n", path);
1440                }
1441                else {
1442                    error = GBS_global_string("Database '%s' not found", path);
1443                    gbc   = 0;
1444                }
1445            }
1446            if (input) {
1447                if (strchr(opent, 'D')) { // we are loading properties -> be verboose
1448                    fprintf(stderr, "Using properties from '%s'\n", path);
1449                }
1450                time_of_main_file = GB_time_of_file(path);
1451
1452                i = (input != stdin) ? gb_read_in_uint32(input, 0) : 0;
1453
1454                if (is_binary_db_id(i)) {
1455                    {
1456                        arb_progress progress("Loading database", GB_size_of_FILE(input));
1457                        i = gb_read_bin(input, gbc, false, progress);     // read or map whole db
1458                        progress.done();
1459                    }
1460                    gbc = Main->root_container;
1461                    fclose(input);
1462
1463                    if (i) {
1464                        if (Main->allow_corrupt_file_recovery) {
1465                            GB_print_error();
1466                            GB_clear_error();
1467                        }
1468                        else {
1469                            gbc   = 0;
1470                            error = GBS_global_string("Failed to load database '%s'\n"
1471                                                      "Reason: %s",
1472                                                      path,
1473                                                      GB_await_error());
1474                        }
1475                    }
1476
1477                    if (gbc && quickFile) {
1478                        long     err;
1479                        GB_ERROR err_msg;
1480                    load_quick_save_file_only :
1481                        err     = 0;
1482                        err_msg = 0;
1483
1484                        input = fopen(quickFile, "rb");
1485
1486                        if (input) {
1487                            GB_ULONG time_of_quick_file = GB_time_of_file(quickFile);
1488                            if (time_of_main_file && time_of_quick_file < time_of_main_file) {
1489                                const char *warning = GBS_global_string("Your main database file '%s' is newer than\n"
1490                                                                        "   the changes file '%s'\n"
1491                                                                        "   That is very strange and happens only if files where\n"
1492                                                                        "   moved/copied by hand\n"
1493                                                                        "   Your file '%s' may be an old relict,\n"
1494                                                                        "   if you ran into problems now,delete it",
1495                                                                        path, quickFile, quickFile);
1496                                GB_warning(warning);
1497                            }
1498                            i = gb_read_in_uint32(input, 0);
1499                            if (is_binary_db_id(i)) {
1500                                {
1501                                    arb_progress progress("Loading quicksave", GB_size_of_FILE(input) / 1024);
1502                                    err = gb_read_bin(input, gbc, true, progress);
1503                                    progress.done();
1504                                }
1505                                fclose (input);
1506
1507                                if (err) {
1508                                    err_msg = GBS_global_string("Loading failed (file corrupt?)\n"
1509                                                                "[Fail-Reason: '%s']",
1510                                                                GB_await_error());
1511                                }
1512                            }
1513                            else {
1514                                err_msg = "Wrong file format (not a quicksave file)";
1515                                err     = 1;
1516                            }
1517                        }
1518                        else {
1519                            err_msg = "Can't open file";
1520                            err     = 1;
1521                        }
1522
1523                        if (err) {
1524                            error = GBS_global_string("I cannot load your quick file '%s'\n"
1525                                                      "Reason: %s\n"
1526                                                      "\n"
1527                                                      "Note: you MAY restore an older version by running arb with:\n"
1528                                                      "      arb <name of quicksave-file>",
1529                                                      quickFile, err_msg);
1530
1531                            if (!Main->allow_corrupt_file_recovery) {
1532                                gbc = 0;
1533                            }
1534                            else {
1535                                GB_export_error(error);
1536                                GB_print_error();
1537                                GB_clear_error();
1538                                error = 0;
1539                                GB_disable_quicksave(gbc, "Couldn't load last quicksave (your latest changes are NOT included)");
1540                            }
1541                        }
1542                    }
1543                    Main->qs.last_index = loadedQuickIndex; // determines which # will be saved next
1544                }
1545                else {
1546                    if (input != stdin) fclose(input);
1547                    error = gb_read_ascii(path, gbc);
1548                    GB_disable_quicksave(gbc, "Sorry, I cannot save differences to ascii files\n"
1549                                         "  Save whole database in binary mode first");
1550                }
1551            }
1552        }
1553    }
1554    else {
1555        GB_disable_quicksave(gbc, "Database not part of this process");
1556        Main->mark_as_server();
1557        GB_begin_transaction(gbc);
1558    }
1559
1560    gb_assert(error || gbc);
1561
1562    if (error) {
1563        gbcm_logout(Main, user);
1564        gb_delete_dummy_father(Main->dummy_father);
1565        gbc = NULL;
1566        delete Main;
1567
1568        GB_export_error(error);
1569    }
1570    else {
1571        GB_commit_transaction(gbc);
1572        {
1573            GB_begin_transaction(gbc); // New Transaction, should be quicksaveable
1574            if (!strchr(opent, 'N')) {               // new format
1575                gb_convert_V2_to_V3(gbc); // Compression conversion
1576            }
1577            error = gb_load_key_data_and_dictionaries(Main);
1578            if (!error) error = gb_resort_system_folder_to_top(Main->root_container);
1579            // @@@ handle error
1580            GB_commit_transaction(gbc);
1581        }
1582        Main->security_level = 0;
1583        gbl_install_standard_commands(gbc);
1584
1585        if (Main->is_server()) {
1586            GBT_install_message_handler(gbc);
1587        }
1588        if (gb_verbose_mode && !dbCreated) GB_informationf("ARB: Loading '%s' done\n", path);
1589    }
1590    free(path);
1591    return gbc;
1592}
1593
1594GBDATA *GB_open(const char *path, const char *opent)
1595{
1596    const char *user;
1597    user = GB_getenvUSER();
1598    return GB_login(path, opent, user);
1599}
1600
1601GB_ERROR GBT_check_arb_file(const char *name) { // goes to header: __ATTR__USERESULT
1602    /*! Checks whether a file is an arb file (or ':')
1603     *
1604     * @result NULL if it is an arb file
1605     */
1606
1607    GB_ERROR error = NULL;
1608    if (!strchr(name, ':'))  { // don't check remote DB
1609        if (GB_is_regularfile(name)) {
1610            FILE *in = fopen(name, "rb");
1611            if (!in) {
1612                error = GBS_global_string("Cannot find file '%s'", name);
1613            }
1614            else {
1615                long i = gb_read_in_uint32(in, 0);
1616
1617                if (!is_binary_db_id(i)) {
1618                    rewind(in);
1619                    char buffer[100];
1620                    if (!fgets(buffer, 50, in)) {
1621                        error = GB_IO_error("reading", name);
1622                    }
1623                    else {
1624                        bool is_ascii = strncmp(buffer, "/*ARBDB AS", 10) == 0;
1625                        if (!is_ascii) error = GBS_global_string("'%s' is not an arb file", name);
1626                    }
1627                }
1628                fclose(in);
1629            }
1630        }
1631        else {
1632            error = GBS_global_string("'%s' is no file", name);
1633        }
1634    }
1635    return error;
1636}
1637
1638
1639// --------------------------------------------------------------------------------
1640
1641#ifdef UNIT_TESTS
1642
1643#include <test_unit.h>
1644
1645void TEST_io_number() {
1646    struct data {
1647        long  val;
1648        short size_expd;
1649    };
1650
1651    data DATA[] = {
1652        { 0x0,      1 },
1653        { 0x1,      1 },
1654        { 0x7f,     1 },
1655                   
1656        { 0x80,     2 },
1657        { 0x81,     2 },
1658        { 0xff,     2 },
1659        { 0x100,    2 },
1660        { 0x1234,   2 },
1661        { 0x3fff,   2 },
1662                   
1663        { 0x4000,   3 },
1664        { 0x4001,   3 },
1665        { 0xffff,   3 },
1666        { 0x1fffff, 3 },
1667
1668        { 0x200000, 4 },
1669        { 0x7fffff, 4 },
1670        { 0x800002, 4 },
1671        { 0xffffff, 4 },
1672        { 0xfffffff, 4 },
1673
1674#if defined(ARB_64)
1675        // the following entries are negative on 32bit systems; see ad_io_inline.h@bit-hell
1676        { 0x10000000, 5 },
1677        { 0x7fffffff, 5 },
1678        { 0x80000000, 5 },
1679        { 0x80808080, 5 },
1680        { 0xffffffff, 5 },
1681#endif
1682    };
1683
1684    const char *numbers   = "numbers.test";
1685    long        writeSize = 0;
1686    long        readSize  = 0;
1687
1688    {
1689        FILE *out = fopen(numbers, "wb");
1690        TEST_REJECT_NULL(out);
1691
1692        long lastPos = 0;
1693        for (size_t i = 0; i<ARRAY_ELEMS(DATA); ++i) {
1694            data& d = DATA[i];
1695            TEST_ANNOTATE(GBS_global_string("val=0x%lx", d.val));
1696            gb_put_number(d.val, out);
1697
1698            long pos           = ftell(out);
1699            long bytes_written = pos-lastPos;
1700            TEST_EXPECT_EQUAL(bytes_written, d.size_expd);
1701
1702            writeSize += bytes_written;
1703
1704            lastPos = pos;
1705        }
1706        TEST_ANNOTATE(NULL);
1707
1708        fclose(out);
1709    }
1710
1711    {
1712        FILE *in = fopen(numbers, "rb");
1713        TEST_REJECT_NULL(in);
1714
1715        long lastPos = 0;
1716
1717        for (size_t i = 0; i<ARRAY_ELEMS(DATA); ++i) {
1718            data& d = DATA[i];
1719            TEST_ANNOTATE(GBS_global_string("val=0x%lx", d.val));
1720
1721            long val = gb_get_number(in);
1722            TEST_EXPECT_EQUAL(val, d.val);
1723
1724            long pos        = ftell(in);
1725            long bytes_read = pos-lastPos;
1726            TEST_EXPECT_EQUAL(bytes_read, d.size_expd);
1727
1728            readSize += bytes_read;
1729
1730            lastPos = pos;
1731        }
1732        TEST_ANNOTATE(NULL);
1733
1734        fclose(in);
1735    }
1736
1737    TEST_EXPECT_EQUAL(GB_size_of_file(numbers), writeSize);
1738    TEST_EXPECT_EQUAL(writeSize, readSize);
1739
1740    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(GB_unlink(numbers));
1741}
1742
1743void TEST_GBDATA_size() {
1744    // protect GBDATA/GBENTRY/GBCONTAINER against unwanted changes
1745    GBENTRY fakeEntry;
1746
1747#if defined(ARB_64)
1748
1749    TEST_EXPECT_EQUAL(sizeof(GBDATA), 40);
1750    TEST_EXPECT_EQUAL(sizeof(fakeEntry.info), 24);  // @@@ SIZOFINTERN may be increased (to use unused space); that change would need new mapfile version
1751    TEST_EXPECT_EQUAL(sizeof(fakeEntry.cache_index), 4);
1752    // 40+24+4 = 68 (where/what are the missing 4 bytes?; they seem to be at the end of the struct; maybe padding-bytes)
1753
1754    TEST_EXPECT_EQUAL(sizeof(GBENTRY), 72);
1755    TEST_EXPECT_EQUAL(sizeof(GBCONTAINER), 104);
1756
1757#else // !defined(ARB_64)
1758
1759    TEST_EXPECT_EQUAL(sizeof(GBDATA), 24);
1760    TEST_EXPECT_EQUAL(sizeof(fakeEntry.info), 12);
1761    TEST_EXPECT_EQUAL(sizeof(fakeEntry.cache_index), 4);
1762    // 24+12+4 = 40 (here nothing is missing)
1763
1764    TEST_EXPECT_EQUAL(sizeof(GBENTRY), 40);
1765    TEST_EXPECT_EQUAL(sizeof(GBCONTAINER), 60);
1766
1767#endif
1768}
1769TEST_PUBLISH(TEST_GBDATA_size);
1770
1771#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.