source: tags/arb-6.0.4/ARBDB/ad_load.cxx

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