source: tags/svn.1.5.4/ARBDB/ad_load.cxx

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