root/trunk/ARBDB/ad_load.cxx

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