ARB
ad_save_load.cxx
Go to the documentation of this file.
1 // =============================================================== //
2 // //
3 // File : ad_save_load.cxx //
4 // Purpose : //
5 // //
6 // Institute of Microbiology (Technical University Munich) //
7 // http://www.arb-home.de/ //
8 // //
9 // =============================================================== //
10 
11 #include <unistd.h>
12 #include <sys/stat.h>
13 #include <netinet/in.h>
14 
15 #include <arb_file.h>
16 #include <arb_diff.h>
17 #include <arb_zfile.h>
18 #include <arb_defs.h>
19 #include <arb_strbuf.h>
20 
21 #include "gb_key.h"
22 #include "gb_map.h"
23 #include "gb_load.h"
24 #include "ad_io_inline.h"
25 #include <arb_misc.h>
26 
28 
29 char *gb_findExtension(char *path) {
30  char *punkt = strrchr(path, '.');
31  if (punkt) {
32  char *slash = strchr(punkt, '/');
33  if (slash) punkt = NULp; // slash after '.' -> no extension
34  }
35  return punkt;
36 }
37 
38 
39 /* CAUTION!!!
40  *
41  * The following functions (gb_quicksaveName, gb_oldQuicksaveName, gb_mapfile_name, gb_overwriteName)
42  * use static buffers for the created filenames.
43  *
44  * So you have to make sure, to use only one instance of every of these
45  * functions or to dup the string before using the second instance
46  */
47 
48 inline char *STATIC_BUFFER(SmartCharPtr& strvar, int minlen) {
49  gb_assert(minlen > 0);
50  if (strvar.isNull() || (strlen(&*strvar) < (size_t)(minlen-1))) {
51  strvar = ARB_calloc<char>(minlen);
52  }
53  return &*strvar;
54 }
55 
57  static SmartCharPtr Qname;
58 
59  size_t len = strlen(path);
60  char *qname = STATIC_BUFFER(Qname, len+15);
61  strcpy(qname, path);
62 
63  char *ext = gb_findExtension(qname);
64  if (!ext) ext = qname + len;
65 
66  if (nr==-1) sprintf(ext, ".arb.quick?");
67  else sprintf(ext, ".arb.quick%i", nr);
68 
69  return qname;
70 }
71 
73  static SmartCharPtr Qname;
74 
75  char *qname = STATIC_BUFFER(Qname, strlen(path)+4);
76  strcpy(qname, path);
77 
78  char *ext = gb_findExtension(qname);
79  if (!ext) ext = qname + strlen(qname);
80 
81  if (nr==-1) sprintf(ext, ".a??");
82  else sprintf(ext, ".a%02i", nr);
83 
84  return qname;
85 }
86 
88  static SmartCharPtr Mapname;
89 
90  char *mapname = STATIC_BUFFER(Mapname, strlen(path)+4+1);
91  strcpy(mapname, path);
92 
93  char *ext = gb_findExtension(mapname);
94  if (!ext) ext = mapname + strlen(mapname);
95 
96  strcpy(ext, ".ARM");
97 
98  return mapname;
99 }
100 
102  GB_MAIN_TYPE *Main = GB_MAIN(gb_main);
103  return gb_mapfile_name(Main->path);
104 }
105 
107  static SmartCharPtr Oname;
108 
109  int len = strlen(path);
110  char *oname = STATIC_BUFFER(Oname, len+2);
111 
112  strcpy(oname, path);
113  strcpy(oname+len, "~"); // append ~
114 
115  return oname;
116 }
117 
119  static SmartCharPtr Refname;
120 
121  size_t len = strlen(path);
122  char *refname = STATIC_BUFFER(Refname, len+4+1);
123  memcpy(refname, path, len+1);
124 
125  const char *ext = gb_findExtension(refname);
126  size_t ext_offset = ext ? (size_t)(ext-refname) : len;
127 
128  strcpy(refname+ext_offset, ".ARF");
129 
130  return refname;
131 }
132 
133 static char *gb_full_path(const char *path) {
134  char *res = NULp;
135 
136  if (path[0] == '/') res = ARB_strdup(path);
137  else {
138  const char *cwd = GB_getcwd();
139 
140  if (path[0] == 0) res = ARB_strdup(cwd);
141  else res = GBS_global_string_copy("%s/%s", cwd, path);
142  }
143  return res;
144 }
145 
146 static GB_ERROR gb_delete_reference(const char *master) {
147  GB_ERROR error = NULp;
148  char *fullmaster = gb_full_path(master);
149  const char *fullref = gb_reffile_name(fullmaster);
150 
151  GB_unlink_or_warn(fullref, &error);
152 
153  free(fullmaster);
154  return error;
155 }
156 
157 static GB_ERROR gb_create_reference(const char *master) {
158  char *fullmaster = gb_full_path(master);
159  const char *fullref = gb_reffile_name(fullmaster);
160  GB_ERROR error = NULp;
161  FILE *out = fopen(fullref, "w");
162 
163  if (out) {
164  fprintf(out, "***** The following files may be a link to %s ********\n", fullmaster);
165  fclose(out);
166  error = GB_failedTo_error("create reference file", NULp, GB_set_mode_of_file(fullref, 00666));
167  }
168  else {
169  error = GBS_global_string("Cannot create reference file '%s'\n"
170  "Your database was saved, but you should check "
171  "write permissions in the destination directory!", fullref);
172  }
173 
174  free(fullmaster);
175  return error;
176 }
177 
178 static GB_ERROR gb_add_reference(const char *master, const char *changes) {
179  char *fullmaster = gb_full_path(master);
180  char *fullchanges = gb_full_path(changes);
181  const char *fullref = gb_reffile_name(fullmaster);
182  GB_ERROR error = NULp;
183  FILE *out = fopen(fullref, "a");
184 
185  if (out) {
186  fprintf(out, "%s\n", fullchanges);
187  fclose(out);
188  error = GB_failedTo_error("append to reference files", NULp, GB_set_mode_of_file(fullref, 00666));
189  }
190  else {
191  error = GBS_global_string("Cannot add your file '%s'\n"
192  "to the list of references of '%s'.\n"
193  "Please ask the owner of that file not to delete it\n"
194  "or save the entire database (that's recommended!)",
195  fullchanges, fullref);
196  }
197 
198  free(fullchanges);
199  free(fullmaster);
200 
201  return error;
202 }
203 
204 static GB_ERROR gb_remove_quick_saved(GB_MAIN_TYPE *Main, const char *path) {
205  int i;
206  GB_ERROR error = NULp;
207 
208  for (i=0; i<GB_MAX_QUICK_SAVE_INDEX && !error; i++) GB_unlink_or_warn(gb_quicksaveName(path, i), &error);
209  for (i=0; i<10 && !error; i++) GB_unlink_or_warn(gb_oldQuicksaveName(path, i), &error);
210  if (Main) Main->qs.last_index = -1;
211 
212  RETURN_ERROR(error);
213 }
214 
215 static GB_ERROR gb_remove_all_but_main(GB_MAIN_TYPE *Main, const char *path) {
216  GB_ERROR error = gb_remove_quick_saved(Main, path);
217  if (!error) GB_unlink_or_warn(gb_mapfile_name(path), &error); // delete old mapfile
218 
219  RETURN_ERROR(error);
220 }
221 
223  int cnt = 0;
224  int i;
225  char *path = Main->path;
226  GB_ERROR error = NULp;
227 
228  for (i=0; i <= GB_MAX_QUICK_SAVE_INDEX; i++) {
229  GB_CSTR qsave = gb_quicksaveName(path, i);
230  if (GB_is_regularfile(qsave)) cnt++;
231  }
232 
233  for (i=0; cnt>GB_MAX_QUICK_SAVES && i <= GB_MAX_QUICK_SAVE_INDEX && !error; i++) {
234  GB_CSTR qsave = gb_quicksaveName(path, i);
235  if (GB_is_regularfile(qsave)) {
236  if (GB_unlink(qsave)<0) error = GB_await_error();
237  else cnt--;
238  }
239  }
240 
241  return error;
242 }
243 
245  /* After hundred quicksaves rename the quicksave-files to a00...a09
246  * to keep MS-DOS-compatibility (8.3)
247  * Note: This has to be called just before saving.
248  */
249 
251  if (!error) {
252  const char *path = Main->path;
253  int i;
254  int j;
255 
256  for (i=0, j=0; i <= GB_MAX_QUICK_SAVE_INDEX; i++) {
257  GB_CSTR qsave = gb_quicksaveName(path, i);
258 
259  if (GB_is_regularfile(qsave)) {
260  if (i!=j) { // otherwise the filename is correct
261  char *qdup = ARB_strdup(qsave);
262  GB_CSTR qnew = gb_quicksaveName(path, j);
263 
264  if (error) GB_warning(error);
265  error = GB_move_file(qdup, qnew);
266  free(qdup);
267  }
268 
269  j++;
270  }
271  }
272 
274  Main->qs.last_index = j-1;
275  }
276 
277  return error;
278 }
279 
280 // ------------------------
281 // Ascii to Binary
282 
283 long gb_ascii_2_bin(const char *source, GBENTRY *gbe) {
284  const char *s = source;
285  char c = *(s++);
286 
287  A_TO_I(c);
288  gbe->flags.compressed_data = c;
289 
290  long size;
291  if (*s == ':') {
292  size = 0;
293  s++;
294  }
295  else {
296  long i, k;
297  for (i = 0, k = 8; k && (c = *(s++)); k--) {
298  A_TO_I(c);
299  i = (i<<4)+c;
300  }
301  size = i;
302  }
303  source = s;
304 
305  long len = 0;
306  while ((c = *(s++))) {
307  if ((c == '.') || (c=='-')) {
308  len++;
309  continue;
310  }
311  if ((c == ':') || (c=='=')) {
312  len += 2;
313  continue;
314  }
315  if (!(c = *(s++))) {
316  return 1;
317  };
318  len++;
319  }
320 
321  GBENTRY_memory storage(gbe, size, len);
322 
323  char *d = storage;
324  s = source;
325 
326  while ((c = *(s++))) {
327  if (c == '.') {
328  *(d++)=0;
329  continue;
330  }
331  if (c == ':') {
332  *(d++)=0;
333  *(d++)=0;
334  continue;
335  }
336  if (c == '-') {
337  *(d++) = 0xff;
338  continue;
339  }
340  if (c == '=') {
341  *(d++) = 0xff;
342  *(d++) = 0xff;
343  continue;
344  }
345  A_TO_I(c);
346  long i = c << 4;
347  c = *(s++);
348  A_TO_I(c);
349  *(d++) = (char)(i + c);
350  }
351 
352  return 0;
353 }
354 // ------------------------
355 // Binary to Ascii
356 
357 #define GB_PUT(c, out) do { if (c>=10) c+='A'-10; else c += '0'; *(out++) = (char)c; } while (0)
358 
360  signed char *s, *out, c, mo;
361  unsigned long i;
362  int j;
363  char *buffer;
364  int k;
365 
366  const char *source = gbe->data();
367  long len = gbe->memsize();
368  long xtended = gbe->size();
369  int compressed = gbe->flags.compressed_data;
370 
371  buffer = GB_give_buffer(len * 2 + 10);
372  out = (signed char *)buffer;
373  s = (signed char *)source, mo = -1;
374 
375  GB_PUT(compressed, out);
376  if (!xtended) {
377  *(out++) = ':';
378  }
379  else {
380  for (i = 0xf0000000, j=28; j>=0; j-=4, i=i>>4) {
381  k = (int)((xtended & i)>>j);
382  GB_PUT(k, out);
383  }
384  }
385  for (i = len; i; i--) {
386  if (!(c = *(s++))) {
387  if ((i > 1) && !*s) {
388  *(out++) = ':';
389  s ++;
390  i--;
391  continue;
392  }
393  *(out++) = '.';
394  continue;
395  }
396  if (c == mo) {
397  if ((i > 1) && (*s == -1)) {
398  *(out++) = '=';
399  s ++;
400  i--;
401  continue;
402  }
403  *(out++) = '-';
404  continue;
405  }
406  j = ((unsigned char) c) >> 4;
407  GB_PUT(j, out);
408  j = c & 15;
409  GB_PUT(j, out);
410  }
411  *(out++) = 0;
412  return buffer;
413 }
414 
415 // -------------------------
416 // Write Ascii File
417 
418 #define GB_PUT_OUT(c, out) do { if (c>=10) c+='A'-10; else c += '0'; putc(c, out); } while (0)
419 
420 static bool gb_write_childs(FILE *out, GBCONTAINER *gbc, GBCONTAINER*& gb_writeFrom, GBCONTAINER *gb_writeTill, int indent);
421 
422 static bool gb_write_one_child(FILE *out, GBDATA *gb, GBCONTAINER*& gb_writeFrom, GBCONTAINER *gb_writeTill, int indent) {
428  {
429  const char *key = GB_KEY(gb);
430  if (!strcmp(key, GB_SYSTEM_FOLDER)) return true; // do not save system folder
431 
432  if (!gb_writeFrom) {
433  for (int i=indent; i--;) putc('\t', out);
434  fprintf(out, "%s\t", key);
435  const int klen = strlen(key);
436  if (klen < 16) {
437  putc('\t', out);
438  if (klen < 8) putc('\t', out);
439  }
440  }
441  }
442 
443  if (!gb_writeFrom) {
444  if (gb->flags.security_delete ||
445  gb->flags.security_write ||
446  gb->flags.security_read ||
447  gb->flags2.last_updated)
448  {
449  putc(':', out);
450  char c;
451  c= gb->flags.security_delete; GB_PUT_OUT(c, out);
452  c= gb->flags.security_write; GB_PUT_OUT(c, out);
453  c= gb->flags.security_read; GB_PUT_OUT(c, out);
454  fprintf(out, "%u\t", gb->flags2.last_updated);
455  }
456  else {
457  putc('\t', out);
458  }
459  }
460 
461  if (gb->is_container()) {
462  if (!gb_writeFrom) {
463  fprintf(out, "%%%c (%%\n", GB_read_flag(gb) ? '$' : '%');
464  bool closeTags = gb_write_childs(out, gb->as_container(), gb_writeFrom, gb_writeTill, indent+1);
465  if (!closeTags) return false;
466  }
467  if (gb_writeFrom && gb_writeFrom == gb->as_container()) gb_writeFrom = NULp;
468  if (gb_writeTill && gb_writeTill == gb->as_container()) {
469  return false;
470  }
471  if (!gb_writeFrom) {
472  for (int i=indent+1; i--;) putc('\t', out);
473  fprintf(out, "%%) /*%s*/\n\n", GB_KEY(gb));
474  }
475  }
476  else {
477  GBENTRY *gbe = gb->as_entry();
478  switch (gbe->type()) {
479  case GB_STRING: {
480  GB_CSTR strng = GB_read_char_pntr(gbe);
481  if (!strng) {
482  strng = "<entry was broken - replaced during ASCIIsave/arb_repair>";
483  GB_warningf("- replaced broken DB entry '%s' (data lost)\n", GB_get_db_path(gbe));
484  }
485  if (*strng == '%') {
486  putc('%', out);
487  putc('s', out);
488  putc('\t', out);
489  }
490  GBS_fwrite_string(strng, out);
491  putc('\n', out);
492  break;
493  }
494  case GB_OBSOLETE: {
495  GB_warningf("- deleted obsolete type GB_LINK for entry '%s'\n", GB_get_db_path(gbe));
496  break;
497  }
498  case GB_INT:
499  fprintf(out, "%%i %li\n", GB_read_int(gbe));
500  break;
501  case GB_FLOAT:
502  fprintf(out, "%%f %s\n", ARB_float_2_ascii(GB_read_float(gbe)));
503  break;
504  case GB_BITS:
505  fprintf(out, "%%I\t\"%s\"\n",
506  GB_read_bits_pntr(gbe, '-', '+'));
507  break;
508  case GB_BYTES: {
509  const char *s = gb_bin_2_ascii(gbe);
510  fprintf(out, "%%Y\t%s\n", s);
511  break;
512  }
513  case GB_INTS: {
514  const char *s = gb_bin_2_ascii(gbe);
515  fprintf(out, "%%N\t%s\n", s);
516  break;
517  }
518  case GB_FLOATS: {
519  const char *s = gb_bin_2_ascii(gbe);
520  fprintf(out, "%%F\t%s\n", s);
521  break;
522  }
523  case GB_DB:
524  gb_assert(0);
525  break;
526  case GB_BYTE:
527  fprintf(out, "%%y %i\n", GB_read_byte(gbe));
528  break;
529  default:
530  fprintf(stderr,
531  "ARBDB ERROR Key \'%s\' is of unknown type\n",
532  GB_KEY(gbe));
533  fprintf(out, "%%%% (%% %%) /* unknown type */\n");
534  break;
535  }
536  }
537  return true;
538 }
539 
540 static bool gb_write_childs(FILE *out, GBCONTAINER *gbc, GBCONTAINER*& gb_writeFrom, GBCONTAINER *gb_writeTill, int indent) {
551  gb_assert(!gb_writeFrom || !gb_writeTill); // not supported (yet)
552 
553  for (GBDATA *gb = GB_child(gbc); gb; gb = GB_nextChild(gb)) {
554  if (gb->flags.temporary) continue;
555 
556  bool closeTags = gb_write_one_child(out, gb, gb_writeFrom, gb_writeTill, indent);
557  if (!closeTags) return false;
558  }
559 
560  return true;
561 }
562 
563 // -------------------------
564 // Read Binary File
565 
566 long gb_read_bin_error(FILE *in, GBDATA *gbd, const char *text) {
567  long p = (long)ftell(in);
568  GB_export_errorf("%s in reading GB_file (loc %li=%lX) reading %s\n",
569  text, p, p, GB_KEY(gbd));
570  GB_print_error();
571  return 0;
572 }
573 
574 // --------------------------
575 // Write Binary File
576 
577 static int gb_is_writeable(gb_header_list *header, GBDATA *gbd, long version, long diff_save) {
578  /* Test whether to write any data to disc.
579  *
580  * version 1 write only latest data
581  * version 2 write deleted entries too (which are not already stored to master file!)
582  *
583  * try to avoid to access gbd (to keep it swapped out)
584  */
585  if (version == 2 && header->flags.changed==GB_DELETED) return 1; // save delete flag
586  if (!gbd) return 0;
587  if (diff_save) {
588  if (!header->flags.ever_changed) return 0;
589  if (!gbd->ext || (gbd->ext->update_date<diff_save && gbd->ext->creation_date < diff_save))
590  return 0;
591  }
592  if (gbd->flags.temporary) return 0;
593  return 1;
594 }
595 
596 static int gb_write_bin_sub_containers(FILE *out, GBCONTAINER *gbc, long version, long diff_save, int is_root);
597 
598 static bool seen_corrupt_data = false;
599 
600 static long gb_write_bin_rek(FILE *out, GBDATA *gbd, long version, long diff_save, long index_of_master_file) {
601  int i;
602  GBCONTAINER *gbc = NULp;
603  GBENTRY *gbe = NULp;
604  long size = 0;
605  GB_TYPES type = gbd->type();
606 
607  if (type == GB_DB) {
608  gbc = gbd->as_container();
609  }
610  else {
611  gbe = gbd->as_entry();
612  if (type == GB_STRING || type == GB_STRING_SHRT) {
613  size = gbe->size();
614  if (!gbe->flags.compressed_data && size < GBTUM_SHORT_STRING_SIZE) {
615  const char *data = gbe->data();
616  size_t len = strlen(data); // w/o zero-byte!
617 
618  if ((long)len == size) {
619  type = GB_STRING_SHRT;
620  }
621  else {
622  // string contains zero-byte inside data or misses trailing zero-byte
623  type = GB_STRING; // fallback to safer type
624  seen_corrupt_data = true;
625  GB_warningf("Corrupted entry detected:\n"
626  "entry: '%s'\n"
627  "data: '%s'",
628  GB_get_db_path(gbe),
629  data);
630  }
631  }
632  else {
633  type = GB_STRING;
634  }
635  }
636  }
637 
638  i = (type<<4)
639  + (gbd->flags.security_delete<<1)
640  + (gbd->flags.security_write>>2);
641  putc(i, out);
642 
643  i = ((gbd->flags.security_write &3) << 6)
644  + (gbd->flags.security_read<<3)
645  + (gbd->flags.compressed_data<<2)
646  + ((GB_ARRAY_FLAGS(gbd).flags&1)<<1)
647  + (gbd->flags.unused);
648  putc(i, out);
649 
650  gb_put_number(GB_ARRAY_FLAGS(gbd).key_quark, out);
651 
652  if (diff_save) {
653  gb_put_number(index_of_master_file, out);
654  }
655 
656  i = gbd->flags2.last_updated;
657  putc(i, out);
658 
659  if (type == GB_STRING_SHRT) {
660  const char *data = gbe->data();
661  gb_assert((long)strlen(data) == size);
662 
663  i = fwrite(data, size+1, 1, out);
664  return i <= 0 ? -1 : 0;
665  }
666 
667  switch (type) {
668  case GB_DB:
669  i = gb_write_bin_sub_containers(out, gbc, version, diff_save, 0);
670  return i;
671 
672  case GB_INT: {
673  GB_UINT4 buffer = (GB_UINT4)htonl(gbe->info.i);
674  if (!fwrite((char *)&buffer, sizeof(float), 1, out)) return -1;
675  return 0;
676  }
677  case GB_FLOAT:
678  if (!fwrite((char *)&gbe->info.i, sizeof(float), 1, out)) return -1;
679  return 0;
680  case GB_BITS:
681  case GB_BYTES:
682  case GB_INTS:
683  case GB_FLOATS:
684  size = gbe->size();
685  // fall-through
686  case GB_STRING: {
687  long memsize = gbe->memsize();
688  gb_put_number(size, out);
689  gb_put_number(memsize, out);
690  i = fwrite(gbe->data(), (size_t)memsize, 1, out);
691  if (memsize && !i) return -1;
692  return 0;
693  }
694  case GB_BYTE:
695  putc((int)(gbe->info.i), out);
696  return 0;
697  default:
698  gb_assert(0); // unknown type
699  return -1;
700  }
701 }
702 
703 static int gb_write_bin_sub_containers(FILE *out, GBCONTAINER *gbc, long version, long diff_save, int is_root) {
704  gb_header_list *header;
705  uint32_t i, index;
706 
707  header = GB_DATA_LIST_HEADER(gbc->d);
708  gb_assert(gbc->d.nheader >= 0);
709  for (i=0, index = 0; index < (uint32_t)gbc->d.nheader; index++) {
710  if (gb_is_writeable(&(header[index]), GB_HEADER_LIST_GBD(header[index]), version, diff_save)) i++;
711  }
712 
713  if (!is_root) {
714  gb_put_number(i, out);
715  }
716  else {
717  gb_write_out_uint32(i, out);
718  }
719 
720  uint32_t counter = 0;
721  for (index = 0; index < (uint32_t)gbc->d.nheader; index++) {
722  GBDATA *h_gbd;
723 
724  if (header[index].flags.changed == GB_DELETED_IN_MASTER) { // count deleted items in master, because of index renaming
725  counter ++;
726  continue;
727  }
728 
729  h_gbd = GB_HEADER_LIST_GBD(header[index]);
730 
731  if (!gb_is_writeable(&(header[index]), h_gbd, version, diff_save)) {
732  if (version <= 1 && header[index].flags.changed == GB_DELETED) {
733  header[index].flags.changed = GB_DELETED_IN_MASTER; // mark deleted in master
734  }
735  continue;
736  }
737 
738  if (h_gbd) {
739  i = (int)gb_write_bin_rek(out, h_gbd, version, diff_save, index-counter);
740  if (i) return i;
741  }
742  else {
743  if (header[index].flags.changed == GB_DELETED) {
744  putc(0, out);
745  putc(1, out);
746  gb_put_number(index - counter, out);
747  }
748  }
749  }
750  return 0;
751 }
752 
753 static int gb_write_bin(FILE *out, GBCONTAINER *gbc, uint32_t version) {
754  /* version 1 write master arb file
755  * version 2 write slave arb file (aka quick save file)
756  */
757 
759 
760  int diff_save = 0;
761  GB_MAIN_TYPE *Main = GBCONTAINER_MAIN(gbc);
762 
764  fprintf(out, "\n this is the binary version of the gbtum data file version %li\n", (long)version);
765  putc(0, out);
766  fwrite("vers", 4, 1, out);
767  gb_write_out_uint32(0x01020304, out);
768  gb_write_out_uint32(version, out);
769  fwrite("keys", 4, 1, out);
770 
771  for (long i=1; i<Main->keycnt; i++) {
772  gb_Key &KEY = Main->keys[i];
773  if (KEY.nref>0) {
774  gb_put_number(KEY.nref, out);
775  fputs(KEY.key, out);
776  }
777  else {
778  putc(0, out); // 0 nref
779  putc(1, out); // empty key
780  }
781  putc(0, out);
782 
783  }
784  putc(0, out);
785  putc(0, out);
786  fwrite("time", 4, 1, out); {
787  unsigned int k;
788  for (k=0; k<Main->last_updated; k++) {
789  fprintf(out, "%s", Main->dates[k]);
790  putc(0, out);
791  }
792  }
793  putc(0, out);
794  fwrite("data", 4, 1, out);
795  if (version == 2) diff_save = (int)Main->last_main_saved_transaction+1;
796 
797  return gb_write_bin_sub_containers(out, gbc, version, diff_save, 1);
798 }
799 
800 // ----------------------
801 // save database
802 
803 GB_ERROR GB_save(GBDATA *gb, const char *path, const char *savetype) {
809  if (path && !strchr(savetype, 'S')) { // 'S' dumps to stdout -> do not change path
810  freedup(GB_MAIN(gb)->path, path);
811  }
812  return GB_save_as(gb, path, savetype);
813 }
814 
816  GB_ERROR error = NULp;
817  char *parent;
818  GB_split_full_path(path, &parent, NULp, NULp, NULp);
819  if (parent) {
820  if (!GB_is_directory(parent)) error = GB_create_directory(parent);
821  free(parent);
822  }
823  return error;
824 }
825 
826 GB_ERROR GB_create_directory(const char *path) {
827  GB_ERROR error = NULp;
828  if (!GB_is_directory(path)) {
829  error = GB_create_parent_directory(path);
830  if (!error) {
831  int res = mkdir(path, ACCESSPERMS);
832  if (res) error = GB_IO_error("creating directory", path);
833  }
834  error = GB_failedTo_error("GB_create_directory", path, error);
835  }
836  return error;
837 }
838 
839 GB_ERROR GB_save_in_arbprop(GBDATA *gb, const char *path, const char *savetype) {
847  char *fullname = ARB_strdup(GB_path_in_arbprop(path ? path : GB_MAIN(gb)->path));
849  if (!error) error = GB_save_as(gb, fullname, savetype);
850  free(fullname);
851 
852  return error;
853 }
854 
855 GB_ERROR GB_MAIN_TYPE::check_saveable(const char *new_path, const char *flags) const {
856  /* Check wether file can be stored at destination
857  * 'f' in flags means 'force' => ignores main->disabled_path
858  * 'q' in flags means 'quick save'
859  * 'n' in flags means destination must be empty
860  */
861 
862  GB_ERROR error = NULp;
863  if (is_client()) {
864  error = "You cannot save a remote database,\nplease use save button in master program";
865  }
866  else if (opentype == gb_open_read_only_all) {
867  error = "Database is read only";
868  }
869  else if (strchr(new_path, ':')) {
870  error = "Your database name may not contain a ':' character\nChoose a different name";
871  }
872  else {
873  char *fullpath = gb_full_path(new_path);
874  if (disabled_path && !strchr(flags, 'f')) {
876  error = GBS_global_string("You are not allowed to save your database in this directory,\n"
877  "Please select 'save as' and save your data to a different location");
878  }
879  }
880 
881  if (!error) {
882  // check whether destination directory exists
883  char *lslash = strrchr(fullpath, '/');
884  if (lslash) {
885  lslash[0] = 0;
886  if (!GB_is_directory(fullpath)) {
887  error = GBS_global_string("Directory '%s' doesn't exist", fullpath);
888  }
889  lslash[0] = '/';
890  }
891  }
892  free(fullpath);
893  }
894 
895  if (!error && !strchr(flags, 'q')) {
896  long mode = GB_mode_of_link(new_path);
897  if (mode >= 0 && !(mode & S_IWUSR)) { // no write access -> looks like a master file
898  error = GBS_global_string("Your selected file '%s'\n"
899  "already exists and is write protected!\n"
900  "This happens e.g. if your file is a MASTER ARB FILE which is\n"
901  "used by multiple quicksaved databases.\n"
902  "If you want to save it nevertheless, delete it first, but\n"
903  "note that doing this will render all these quicksaves useless!",
904  new_path);
905  }
906  }
907 
908  if (!error && strchr(flags, 'n') && GB_time_of_file(new_path)) {
909  error = GBS_global_string("Your destination file '%s' already exists.\n"
910  "Delete it manually!", new_path);
911  }
912 
913 #if (MEMORY_TEST==1)
914  if (!error && strchr(flags, 'm')) {
915  error = "It's impossible to save mapfiles (ARBDB is MEMORY_TEST mode 1)";
916  }
917 #endif
918 
919  return error;
920 }
921 
922 static GB_ERROR protect_corruption_error(const char *savepath) {
923  GB_ERROR error = NULp;
925  if (!strstr(savepath, "CORRUPTED")) {
926  error = "Severe error: Corrupted data detected during save\n"
927  "ARB did NOT save your database!\n"
928  "Advices:\n" //|
929  "* If your previous (quick)save was not long ago, your savest\n"
930  " option is to drop the changes since then, by reloading the not\n"
931  " corrupted database and redo your changes. If you can reproduce\n"
932  " the bug that corrupted the entries, please report it!\n"
933  "* If that is no option (because too much work would be lost)\n"
934  " you can force saving the corrupted database by adding the text\n"
935  " 'CORRUPTED' to the database name. After doing that, do NOT\n"
936  " quit ARB, instead try to find and fix all corrupted entries\n"
937  " that were listed below. Manually enter their original values\n"
938  " (in case you want to lookup or copy&paste some values, you may\n"
939  " open the last saved version of this database using\n"
940  " 'Start second database').\n"
941  " Saving the database again will show all remaining unfixed\n"
942  " entries. If no more corrupted entries show up, you can safely\n"
943  " continue to work with that database.";
944  }
945  else {
946  GB_warning("Warning: Saved corrupt database");
947  }
948  seen_corrupt_data = false;
949  return error;
950 }
951 
952 #define SUPPORTED_COMPRESSION_FLAGS "zBx"
953 
954 const char *GB_get_supported_compression_flags(bool verboose) {
955  if (verboose) {
956  GBS_strstruct doc(50);
957  for (int f = 0; SUPPORTED_COMPRESSION_FLAGS[f]; ++f) {
958  if (f) doc.cat(", ");
959  switch (SUPPORTED_COMPRESSION_FLAGS[f]) {
960  // Note: before changing produced format, see callers (esp. AWT_insert_DBcompression_selector)
961  case 'z': doc.cat("z=gzip"); break;
962  case 'B': doc.cat("B=bzip2"); break;
963  case 'x': doc.cat("x=xz"); break;
964  default:
965  gb_assert(0); // undocumented flag
966  break;
967  }
968  }
969  return GBS_static_string(doc.get_data());
970  }
972 }
973 
974 
975 class ArbDBWriter : virtual Noncopyable {
976  GB_MAIN_TYPE *Main;
977 
978  GB_ERROR error;
979 
980  FILE *out;
981  bool saveASCII;
982  bool saveMapfile;
983 
984  char *given_path;
985  const char *as_path;
986  char *sec_path;
987  char *mappath;
988  char *sec_mappath;
989 
990  struct Levels {
991  int security;
992  int transaction;
993 
994  void readFrom(GB_MAIN_TYPE *main) {
995  security = main->security_level;
996  transaction = main->get_transaction_level();
997  }
998  void writeTo(GB_MAIN_TYPE *main) {
999  main->security_level = security;
1000  if (main->transaction_level>0) {
1002  }
1003  if (transaction) {
1005  }
1006  gb_assert(main->transaction_level == transaction || main->transaction_level == -1);
1007  }
1008 
1009  };
1010 
1011  Levels save; // wanted levels during save (i.e. inside saveFromTill/finishSave)
1012 
1013  enum {
1014  CONSTRUCTED,
1015  STARTED,
1016  FINISHED,
1017  } state;
1018 
1019  bool dump_to_stdout;
1020  bool outOfOrderSave;
1021  bool deleteQuickAllowed;
1022 
1023 public:
1025  : Main(Main_),
1026  error(NULp),
1027  out(NULp),
1028  saveASCII(false),
1029  saveMapfile(false),
1030  given_path(NULp),
1031  sec_path(NULp),
1032  mappath(NULp),
1033  sec_mappath(NULp),
1034  state(CONSTRUCTED),
1035  dump_to_stdout(false),
1036  outOfOrderSave(false),
1037  deleteQuickAllowed(false)
1038  {}
1040  gb_assert(state == FINISHED); // you have to call finishSave()! (even in error-case)
1041 
1042  free(sec_mappath);
1043  free(mappath);
1044  free(sec_path);
1045  free(given_path);
1046  }
1047 
1048  GB_ERROR startSaveAs(const char *given_path_, const char *savetype) {
1049  gb_assert(!error);
1050  gb_assert(state == CONSTRUCTED);
1051  state = STARTED;
1052 
1053  given_path = nulldup(given_path_);
1054  as_path = given_path;
1055 
1056  if (strchr(savetype, 'a')) saveASCII = true;
1057  else if (strchr(savetype, 'b')) saveASCII = false;
1058  else error = GBS_global_string("Invalid savetype '%s' (expected 'a' or 'b')", savetype);
1059 
1060  if (!error) {
1061  if (!as_path) as_path = Main->path;
1062  if (!as_path || !as_path[0]) error = "Please specify a savename";
1063  else error = Main->check_saveable(as_path, savetype);
1064  }
1065 
1066  FileCompressionMode compressMode = ZFILE_UNCOMPRESSED;
1067  if (!error) {
1068  struct {
1069  char flag;
1070  FileCompressionMode mode;
1071  } supported[] = {
1072  { 'z', ZFILE_GZIP },
1073  { 'B', ZFILE_BZIP2 },
1074  { 'x', ZFILE_XZ },
1075  // Please document new flags in GB_save_as() below.
1076  };
1077 
1078  STATIC_ASSERT(ARRAY_ELEMS(supported) == ZFILE_REAL_CMODES); // (after adding a new FileCompressionMode it should be supported here)
1079 
1080  for (size_t comp = 0; !error && comp<ARRAY_ELEMS(supported); ++comp) {
1081  if (strchr(savetype, supported[comp].flag)) {
1082  if (compressMode == ZFILE_UNCOMPRESSED) {
1083  compressMode = supported[comp].mode;
1084  }
1085  else {
1086  error = "Multiple compression modes specified";
1087  }
1088  }
1089  gb_assert(strchr(SUPPORTED_COMPRESSION_FLAGS, supported[comp].flag)); // flag gets not tested -> add to SUPPORTED_COMPRESSION_FLAGS
1090  }
1091  }
1092 
1093  if (!error && Main->transaction_level>1) {
1094  error = "Save only allowed with transaction_level <= 1";
1095  }
1096 
1097  save.readFrom(Main); // values for error case
1098  if (!error) {
1099  // unless saving to a fifo, we append ~ to the file name we write to,
1100  // and move that file if and when everything has gone well
1101  sec_path = ARB_strdup(GB_is_fifo(as_path) ? as_path : gb_overwriteName(as_path));
1102  dump_to_stdout = strchr(savetype, 'S');
1103  out = dump_to_stdout ? stdout : ARB_zfopen(sec_path, "w", compressMode, error, false);
1104 
1105  if (!out) {
1106  gb_assert(error);
1107  error = GBS_global_string("While saving database '%s': %s", sec_path, error);
1108  }
1109  else {
1110  save.security = 7;
1111  save.transaction = 1;
1112 
1113  seen_corrupt_data = false;
1114 
1115  outOfOrderSave = strchr(savetype, 'f');
1116  deleteQuickAllowed = !outOfOrderSave && !dump_to_stdout;
1117  {
1118  if (saveASCII) {
1119  fprintf(out, "/*ARBDB ASCII*/\n");
1120  }
1121  else {
1122  saveMapfile = strchr(savetype, 'm');
1123  }
1124  }
1125  }
1126  }
1127 
1128  return error;
1129  }
1130 
1131 private:
1132  void writeContainerOrChilds(GBCONTAINER *top, GBCONTAINER *from, GBCONTAINER *till) {
1133  if (top == Main->root_container) {
1134  gb_write_childs(out, top, from, till, 0);
1135  }
1136  else {
1137  int root_indent = 0;
1138  {
1139  GBCONTAINER *stepUp = top;
1140  while (stepUp != Main->root_container) {
1141  stepUp = stepUp->get_father();
1142  ++root_indent;
1143  gb_assert(stepUp); // fails if 'top' is not member of 'Main'
1144  }
1145  --root_indent; // use 0 for direct childs of root_container
1146  }
1147  gb_assert(root_indent>=0);
1148  gb_write_one_child(out, top, from, till, root_indent);
1149  }
1150  }
1151 public:
1152 
1154  gb_assert(!error);
1155  gb_assert(state == STARTED);
1156 
1157  Levels org; org.readFrom(Main);
1158  save.writeTo(Main); // set save transaction-state and security_level
1159 
1160  if (saveASCII) {
1161  if (gb_from == gb_till) {
1162  writeContainerOrChilds(gb_from, NULp, NULp);
1163  }
1164  else {
1165  bool from_is_ancestor = false;
1166  bool till_is_ancestor = false;
1167 
1168  GBCONTAINER *gb_from_ancestor = gb_from->get_father();
1169  GBCONTAINER *gb_till_ancestor = gb_till->get_father();
1170 
1171  while (gb_from_ancestor || gb_till_ancestor) {
1172  if (gb_from_ancestor && gb_from_ancestor == gb_till) till_is_ancestor = true;
1173  if (gb_till_ancestor && gb_till_ancestor == gb_from) from_is_ancestor = true;
1174 
1175  if (gb_from_ancestor) gb_from_ancestor = gb_from_ancestor->get_father();
1176  if (gb_till_ancestor) gb_till_ancestor = gb_till_ancestor->get_father();
1177  }
1178 
1179  if (from_is_ancestor) {
1180  writeContainerOrChilds(gb_from, NULp, gb_till);
1181  }
1182  else if (till_is_ancestor) {
1183  writeContainerOrChilds(gb_till, gb_from, NULp);
1184  }
1185  else {
1186  error = "Invalid use (one container has to be an ancestor of the other)";
1187  }
1188  }
1189  }
1190  else { // save binary (performed in finishSave)
1191  if (gb_from != Main->root_container || gb_till != Main->root_container) {
1192  error = "streamed saving only supported for ascii database format";
1193  }
1194  }
1195 
1196  org.writeTo(Main); // restore original transaction-state and security_level
1197  return error;
1198  }
1200  gb_assert(state == STARTED);
1201  state = FINISHED;
1202 
1203  Levels org; org.readFrom(Main);
1204  save.writeTo(Main); // set save transaction-state and security_level
1205 
1206  if (out) {
1207  int result = 0;
1208  if (!error) {
1209  if (saveASCII) {
1210  freedup(Main->qs.quick_save_disabled, "Database saved in ASCII mode");
1211  if (deleteQuickAllowed) error = gb_remove_all_but_main(Main, as_path);
1212  }
1213  else {
1214  mappath = ARB_strdup(gb_mapfile_name(as_path));
1215  if (saveMapfile) {
1216  // it's necessary to save the mapfile FIRST,
1217  // cause this re-orders all GB_CONTAINERs containing NULp-entries in their header
1218  sec_mappath = ARB_strdup(gb_overwriteName(mappath));
1219  if (!error) error = gb_save_mapfile(Main, sec_mappath);
1220  }
1221  else GB_unlink_or_warn(mappath, &error); // delete old mapfile
1222  if (!error) result |= gb_write_bin(out, Main->root_container, 1);
1223  }
1224  }
1225 
1226  // org.writeTo(Main); // Note: was originally done here
1227 
1228  if (result != 0 && !error) {
1229  error = GB_IO_error("writing", sec_path);
1230  if (!dump_to_stdout) {
1231  GB_ERROR close_error = ARB_zfclose(out);
1232  if (close_error) error = GBS_global_string("%s\n(close reports: %s)", error, close_error);
1233  }
1234  }
1235  else {
1236  if (!dump_to_stdout) {
1237  GB_ERROR close_error = ARB_zfclose(out);
1238  if (!error) error = close_error;
1239  }
1240  }
1241 
1242  if (!error && seen_corrupt_data) {
1243  error = protect_corruption_error(as_path);
1244  }
1245 
1246  if (!error && !saveASCII) {
1247  if (!outOfOrderSave) freenull(Main->qs.quick_save_disabled); // delete reason, why quicksaving was disallowed
1248  if (deleteQuickAllowed) error = gb_remove_quick_saved(Main, as_path);
1249  }
1250 
1251  if (!dump_to_stdout) {
1252  if (error) {
1253  if (sec_mappath) GB_unlink_or_warn(sec_mappath, NULp);
1254  GB_unlink_or_warn(sec_path, NULp);
1255  }
1256  else {
1257  bool unlinkMapfiles = false;
1258  if (strcmp(sec_path, as_path) != 0) {
1259  error = GB_move_file(sec_path, as_path);
1260  }
1261 
1262  if (error) {
1263  unlinkMapfiles = true;
1264  }
1265  else if (sec_mappath) {
1266  error = GB_move_file(sec_mappath, mappath);
1267  if (!error) error = GB_set_mode_of_file(mappath, GB_mode_of_file(as_path)); // set mapfile to same mode ...
1268  if (!error) error = GB_set_time_of_file(mappath, GB_time_of_file(as_path)); // ... and same time as DB file
1269  if (error) {
1270  GB_warningf("Error: %s\n[Falling back to non-fastload-save]", error);
1271  error = NULp;
1272  unlinkMapfiles = true;
1273  }
1274  }
1275 
1276  if (unlinkMapfiles) {
1277  GB_unlink_or_warn(sec_mappath, NULp);
1278  GB_unlink_or_warn(mappath, NULp);
1279  }
1280 
1281  if (!error) {
1282  error = !Main->qs.quick_save_disabled
1283  ? gb_create_reference(as_path)
1284  : gb_delete_reference(as_path);
1285  }
1286  }
1287  }
1288 
1289  if (!error && !outOfOrderSave) {
1292  Main->last_saved_time = GB_time_of_day();
1293  }
1294  }
1295 
1296  org.writeTo(Main); // restore original transaction-state and security_level
1297  return error;
1298  }
1299 };
1300 
1301 GB_ERROR GB_MAIN_TYPE::save_as(const char *as_path, const char *savetype) {
1302  ArbDBWriter dbwriter(this);
1303  GB_ERROR error = dbwriter.startSaveAs(as_path, savetype);
1304  if (!error) error = dbwriter.saveFromTill(root_container, root_container);
1305  error = dbwriter.finishSave();
1306  return error;
1307 }
1308 
1309 // AISC_MKPT_PROMOTE:class ArbDBWriter;
1310 
1311 GB_ERROR GB_start_streamed_save_as(GBDATA *gbd, const char *path, const char *savetype, ArbDBWriter*& writer) {
1319  gb_assert(!writer);
1320  writer = new ArbDBWriter(GB_MAIN(gbd));
1321  return writer->startSaveAs(path, savetype);
1322 }
1340  GB_ERROR error;
1341  if (from->is_container() && till->is_container()) {
1342  error = writer->saveFromTill(from->as_container(), till->as_container());
1343  }
1344  else {
1345  error = "Invalid use of GB_stream_save_part: need to pass 2 containers";
1346  }
1347  return error;
1348 }
1353  GB_ERROR error = writer->finishSave();
1354 
1355  delete writer;
1356  writer = NULp;
1357 
1358  return error;
1359 }
1360 
1361 
1362 GB_ERROR GB_save_as(GBDATA *gbd, const char *path, const char *savetype) {
1381  GB_ERROR error = NULp;
1382 
1383  gb_assert(savetype);
1384 
1385  if (!gbd) {
1386  error = "got no DB";
1387  }
1388  else {
1389  error = GB_MAIN(gbd)->save_as(path, savetype);
1390  }
1391 
1392  RETURN_ERROR(error);
1393 }
1394 
1395 GB_ERROR GB_MAIN_TYPE::check_quick_save() const {
1396  /* is quick save allowed?
1397  * return NULp if allowed, error why disallowed otherwise.
1398  */
1399 
1400  if (qs.quick_save_disabled) {
1401  return GBS_global_string("Save Changes Disabled, because\n"
1402  " '%s'\n"
1403  " Save whole database using binary mode first",
1405  }
1406  return NULp;
1407 }
1408 
1410  GB_ERROR error = NULp;
1411 
1412  if (GB_unlink(filename)<0) error = GB_await_error();
1413  else error = gb_remove_all_but_main(NULp, filename);
1414 
1415  return error;
1416 }
1417 
1419  GB_ERROR error = NULp;
1420  if (!as_path || !strlen(as_path)) {
1421  error = "Please specify a file name";
1422  }
1423  else if (strcmp(as_path, path) == 0) { // same name (no rename)
1424  error = save_quick(as_path);
1425  }
1426  else {
1427  error = check_quick_saveable(as_path, "bn");
1428 
1429  if (!error) {
1430  FILE *fmaster = fopen(path, "r"); // old master !!!!
1431  if (!fmaster) { // Oh no, where is my old master
1432  error = GBS_global_string("Save Changes is missing master ARB file '%s',\n"
1433  " save database first", path);
1434  }
1435  else {
1436  fclose(fmaster);
1437  }
1438  }
1439  if (!error) {
1440  if (GB_unlink(as_path)<0) { // delete old file
1441  error = GBS_global_string("File '%s' already exists and could not be deleted\n"
1442  "(Reason: %s)",
1443  as_path, GB_await_error());
1444  }
1445  }
1446  if (!error) {
1447  char *org_master = S_ISLNK(GB_mode_of_link(path))
1449  : ARB_strdup(path);
1450 
1451  error = gb_remove_all_but_main(this, as_path);
1452  if (!error) {
1453  long mode = GB_mode_of_file(org_master);
1454  if (mode & S_IWUSR) {
1455  GB_ERROR sm_error = GB_set_mode_of_file(org_master, mode & ~(S_IWUSR | S_IWGRP | S_IWOTH));
1456  if (sm_error) {
1457  GB_warningf("%s\n"
1458  "Ask the owner to remove write permissions from that master file.\n"
1459  "NEVER delete or change it, otherwise your quicksaves will be rendered useless!",
1460  sm_error);
1461  }
1462  }
1463  char *full_path_of_source;
1464  if (strchr(as_path, '/') || strchr(org_master, '/')) {
1465  // dest or source in different directory
1466  full_path_of_source = gb_full_path(org_master);
1467  }
1468  else {
1469  full_path_of_source = ARB_strdup(org_master);
1470  }
1471 
1472  error = GB_symlink(full_path_of_source, as_path);
1473  if (!error) {
1474  if ((uid_t)GB_getuid_of_file(full_path_of_source) != getuid()) {
1475  GB_warningf("**** WARNING ******\n"
1476  " You are using the file '%s' \n"
1477  " as reference for your saved changes.\n"
1478  " That file is owned by ANOTHER USER.\n"
1479  " If that user deletes or overwrites that file, your saved\n"
1480  " changes will get useless (=they will be lost)!\n"
1481  " You should only 'save changes as' if you understand what that means.\n"
1482  " Otherwise use 'Save whole database as' NOW!", full_path_of_source);
1483  }
1484 
1485  GB_ERROR warning = gb_add_reference(full_path_of_source, as_path);
1486  if (warning) GB_warning(warning);
1487 
1488  freedup(path, as_path); // Symlink created -> rename allowed
1489 
1490  qs.last_index = -1; // Start with new quicks (next index will be 0)
1491  error = save_quick(as_path);
1492  }
1493  free(full_path_of_source);
1494  }
1495  free(org_master);
1496  }
1497  }
1498 
1499  RETURN_ERROR(error);
1500 }
1501 
1502 GB_ERROR GB_save_quick_as(GBDATA *gbd, const char *path) {
1503  return GB_MAIN(gbd)->save_quick_as(path);
1504 }
1505 
1506 GB_ERROR GB_MAIN_TYPE::save_quick(const char *refpath) {
1507  GB_ERROR error = check_quick_saveable(refpath, "q");
1508 
1509  if (!error && refpath && strcmp(refpath, path) != 0) {
1510  error = GBS_global_string("master file rename '%s'!= '%s',\n"
1511  "save database first", refpath, path);
1512  }
1513  if (!error) {
1514  FILE *fmaster = fopen(path, "r");
1515 
1516  if (!fmaster) {
1517  error = GBS_global_string("Quick save is missing master ARB file '%s',\n"
1518  "save database first", refpath);
1519  }
1520  else {
1521  fclose(fmaster);
1522  }
1523  }
1524  if (!error && is_client()) error = "You cannot save a remote database";
1525  if (!error) {
1526  qs.last_index++;
1528 
1529  GB_CSTR qck_path = gb_quicksaveName(path, qs.last_index);
1530  GB_CSTR sec_path = gb_overwriteName(qck_path);
1531 
1532  FILE *out = fopen(sec_path, "w");
1533  if (!out) error = GBS_global_string("Cannot save file to '%s'", sec_path);
1534  else {
1535  long erg;
1536  {
1537  const int org_security_level = security_level;
1538  int org_transaction_level = get_transaction_level();
1539 
1540  if (!org_transaction_level) transaction_level = 1;
1541  else {
1542  if (org_transaction_level> 0) {
1545  }
1546  }
1547 
1548  security_level = 7;
1549  seen_corrupt_data = false;
1550 
1551  erg = gb_write_bin(out, root_container, 2);
1552 
1553  security_level = org_security_level;
1554  transaction_level = org_transaction_level;
1555  }
1556 
1557  erg |= fclose(out);
1558 
1559  if (erg!=0) error = GBS_global_string("Cannot write to '%s'", sec_path);
1560  else {
1561  if (seen_corrupt_data) {
1562  gb_assert(!error);
1563  error = protect_corruption_error(qck_path);
1564  }
1565  if (!error) error = GB_move_file(sec_path, qck_path);
1566  if (error) GB_unlink_or_warn(sec_path, NULp);
1567  }
1568  }
1569 
1570  if (error) qs.last_index--; // undo index increment
1571  else {
1574 
1575  error = deleteSuperfluousQuicksaves(this);
1576  }
1577  }
1578 
1579  RETURN_ERROR(error);
1580 }
1581 
1582 GB_ERROR GB_save_quick(GBDATA *gbd, const char *refpath) {
1583  return GB_MAIN(gbd)->save_quick(refpath);
1584 }
1585 
1586 
1587 void GB_disable_path(GBDATA *gbd, const char *path) {
1588  // disable directories for save
1589  freeset(GB_MAIN(gbd)->disabled_path, path ? GBS_eval_env(path) : NULp);
1590 }
1591 
1593  return GB_MAIN(gb_main)->last_saved_transaction;
1594 }
1595 
1597  return GB_MAIN(gb_main)->last_saved_time;
1598 }
1599 
1600 // --------------------------------------------------------------------------------
1601 
1602 #ifdef UNIT_TESTS
1603 
1604 #include <test_unit.h>
1605 
1606 // #define TEST_AUTO_UPDATE // uncomment to auto-update binary and quicksave testfiles (needed once after changing ascii testfile or modify_db())
1607 
1608 #if defined(TEST_AUTO_UPDATE)
1609 #define TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(src,dest) TEST_COPY_FILE(src,dest)
1610 #else
1611 #define TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(src,dest) TEST_EXPECT_TEXTFILES_EQUAL(src,dest)
1612 #endif
1613 
1614 #define SAVE_AND_COMPARE(gbd, save_as, savetype, compare_with) \
1615  TEST_EXPECT_NO_ERROR(GB_save_as(gbd, save_as, savetype)); \
1616  TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(save_as, compare_with)
1617 
1618 #define SAVE_AND_COMPARE__BROKEN(gbd, save_as, savetype, compare_with) \
1619  TEST_EXPECT_NO_ERROR(GB_save_as(gbd, save_as, savetype)); \
1620  TEST_EXPECT_FILES_EQUAL__BROKEN(save_as, compare_with)
1621 
1622 static GB_ERROR modify_db(GBDATA *gb_main) {
1623  GB_transaction ta(gb_main);
1624 
1625  GB_ERROR error = NULp;
1626  GBDATA *gb_container = GB_create_container(gb_main, "container");
1627  if (!gb_container) error = GB_await_error();
1628  else {
1629  GBDATA *gb_entry = GB_create(gb_container, "str", GB_STRING);
1630  if (!gb_entry) error = GB_await_error();
1631  else error = GB_write_string(gb_entry, "text");
1632  // else error = GB_write_string(gb_entry, "bla"); // provoke error in file compare
1633  }
1634  return error;
1635 }
1636 
1637 #define TEST_loadsave_CLEANUP() TEST_EXPECT_ZERO(system("rm -f [abr]2[ab]*.* master.* slave.* renamed.* fast.* fast2a.* TEST_loadsave.ARF"))
1638 
1639 void TEST_SLOW_loadsave() {
1640  GB_shell shell;
1641  TEST_loadsave_CLEANUP();
1642 
1643  // test non-existing DB
1644  TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_open("nonexisting.arb", "r"), "'nonexisting.arb' not found");
1645  TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_open("nonexisting.arb", "rw"), "'nonexisting.arb' not found");
1646  {
1647  GBDATA *gb_new;
1648  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_new = GB_open("nonexisting.arb", "w")); // create new DB
1649  GB_close(gb_new);
1650  }
1651 
1652  // the following DBs have to be provided in directory ../UNIT_TESTER/run
1653  const char *bin_db = "TEST_loadsave.arb";
1654  const char *asc_db = "TEST_loadsave_ascii.arb";
1655 
1656  GBDATA *gb_asc;
1657  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_asc = GB_open(asc_db, "rw"));
1658 
1659 #if defined(TEST_AUTO_UPDATE)
1660  TEST_EXPECT_NO_ERROR(GB_save_as(gb_asc, bin_db, "b"));
1661 #endif // TEST_AUTO_UPDATE
1662 
1663  GBDATA *gb_bin;
1664  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_bin = GB_open(bin_db, "rw"));
1665 
1666  // test ASCII / BINARY compatibility
1667  SAVE_AND_COMPARE(gb_asc, "a2a.arb", "a", asc_db);
1668  SAVE_AND_COMPARE(gb_asc, "a2b.arb", "b", bin_db);
1669  SAVE_AND_COMPARE(gb_bin, "b2a.arb", "a", asc_db);
1670  SAVE_AND_COMPARE(gb_bin, "b2b.arb", "b", bin_db);
1671 
1672  // test extra database stream compression
1673  const char *compFlag = SUPPORTED_COMPRESSION_FLAGS;
1674 
1675  int successful_compressed_saves = 0;
1676  for (int c = 0; compFlag[c]; ++c) {
1677  for (char dbtype = 'a'; dbtype<='b'; ++dbtype) {
1678  TEST_ANNOTATE(GBS_global_string("dbtype=%c compFlag=%c", dbtype, compFlag[c]));
1679  char *zipd_db = GBS_global_string_copy("a2%c_%c.arb", dbtype, compFlag[c]);
1680 
1681  char savetype[] = "??";
1682  savetype[0] = dbtype;
1683  savetype[1] = compFlag[c];
1684 
1685  GB_ERROR error = GB_save_as(gb_asc, zipd_db, savetype);
1686  if (error && strstr(error, "failed with exitcode=127")) {
1687  fprintf(stderr, "Assuming compression utility for flag '%c' is not installed\n", compFlag[c]);
1688  }
1689  else {
1690  TEST_EXPECT_NO_ERROR(error);
1691 
1692  // reopen saved database, save again + compare
1693  {
1694  GBDATA *gb_reloaded = GB_open(zipd_db, "rw");
1695  TEST_REJECT_NULL(gb_reloaded); // reading compressed database failed
1696 
1697  SAVE_AND_COMPARE(gb_reloaded, "r2b.arb", "b", bin_db); // check binary content
1698  SAVE_AND_COMPARE(gb_reloaded, "r2a.arb", "a", asc_db); // check ascii content
1699 
1700  GB_close(gb_reloaded);
1701  }
1702  successful_compressed_saves++;
1703  }
1704 
1705  free(zipd_db);
1706  }
1707  }
1708 
1709  TEST_EXPECT(successful_compressed_saves>=2); // at least gzip and bzip2 should be installed
1710 
1711 #if (MEMORY_TEST == 0)
1712  {
1713  GBDATA *gb_nomap;
1714  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_nomap = GB_open(bin_db, "rw"));
1715  TEST_EXPECT_NO_ERROR(GB_save_as(gb_nomap, "fast.arb", "bm"));
1716  TEST_EXPECT(GB_is_regularfile("fast.ARM")); // assert map file has been saved
1717  TEST_EXPECT_EQUAL(GB_time_of_file("fast.ARM"), GB_time_of_file("fast.arb"));
1718  GB_close(gb_nomap);
1719  }
1720  {
1721  // open DB with mapfile
1722  GBDATA *gb_map;
1723  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_map = GB_open("fast.arb", "rw"));
1724  // SAVE_AND_COMPARE(gb_map, "fast2b.arb", "b", bin_db); // fails now (because 3 keys have different key-ref-counts)
1725  // Surprise: these three keys are 'tmp', 'message' and 'pending'
1726  // (key-ref-counts include temporary entries, but after saving they vanish and remain wrong)
1727  SAVE_AND_COMPARE(gb_map, "fast2a.arb", "a", asc_db); // using ascii avoids that problem (no keys stored there)
1728 
1729  GB_close(gb_map);
1730  }
1731  {
1732  // test alloc/free (no real test, just call it for code coverage)
1733  char *small_block = (char*)gbm_get_mem(30, 5);
1734  gbm_free_mem(small_block, 30, 5);
1735 
1736  char *big_block = (char*)gbm_get_mem(3000, 6);
1737  gbm_free_mem(big_block, 3000, 6);
1738  }
1739 #endif
1740 
1741  {
1742  // test opening saved DBs
1743  GBDATA *gb_a2b = GB_open("a2b.arb", "rw"); TEST_REJECT_NULL(gb_a2b);
1744  GBDATA *gb_b2b = GB_open("b2b.arb", "rw"); TEST_REJECT_NULL(gb_b2b);
1745 
1746  // modify ..
1747  TEST_EXPECT_NO_ERROR(modify_db(gb_a2b));
1748  TEST_EXPECT_NO_ERROR(modify_db(gb_b2b));
1749 
1750  // .. and quicksave
1751  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_a2b, "a2b.arb"));
1752  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_b2b, "b2b.arb"));
1753 
1754 #if defined(TEST_AUTO_UPDATE)
1755  TEST_COPY_FILE("a2b.a00", "TEST_loadsave_quick.a00");
1756 #endif // TEST_AUTO_UPDATE
1757 
1758  TEST_EXPECT_FILES_EQUAL("TEST_loadsave_quick.a00", "a2b.a00");
1759  TEST_EXPECT_FILES_EQUAL("a2b.a00", "b2b.a00");
1760 
1761  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_a2b, "a2b.arb"));
1762 
1763  // check wether quicksave can be disabled
1764  GB_disable_quicksave(gb_a2b, "test it");
1765 
1766  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick(gb_a2b, "a2b.arb"), "Save Changes Disabled");
1767  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_a2b, "a2b.arb"), "Save Changes Disabled");
1768 
1769  const char *mod_db = "a2b_modified.arb";
1770  TEST_EXPECT_NO_ERROR(GB_save_as(gb_a2b, mod_db, "a")); // save modified DB (now ascii to avoid key-ref-problem)
1771  // test loading quicksave
1772  {
1773  GBDATA *gb_quickload = GB_open("a2b.arb", "rw"); // load DB which has a quicksave
1774  SAVE_AND_COMPARE(gb_quickload, "a2b_quickloaded.arb", "a", mod_db); // use ascii version (binary has key-ref-diffs)
1775  GB_close(gb_quickload);
1776  }
1777 
1778  {
1779  // check master/slave DBs
1780  TEST_EXPECT_NO_ERROR(GB_save_as(gb_b2b, "master.arb", "b"));
1781 
1782  GBDATA *gb_master = GB_open("master.arb", "rw"); TEST_REJECT_NULL(gb_master);
1783  TEST_EXPECT_NO_ERROR(modify_db(gb_master));
1784 
1785  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_master, "master.arb"));
1786  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_master, "master.arb"));
1787 
1788  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick(gb_master, "renamed.arb"), "master file rename"); // quicksave with wrong name
1789 
1790  // check if master gets protected by creating slave-DB
1791  TEST_EXPECT_NO_ERROR(GB_save_as(gb_master, "master.arb", "b")); // overwrite
1792  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_master, "slave.arb")); // create slave -> master now protected
1793  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_master, "master.arb", "b"), "already exists and is write protected"); // overwrite should fail now
1794 
1795  {
1796  GBDATA *gb_slave = GB_open("slave.arb", "rw"); TEST_REJECT_NULL(gb_slave); // load slave DB
1797  TEST_EXPECT_NO_ERROR(modify_db(gb_slave));
1798  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_slave, "slave.arb"));
1799  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_slave, "renamed.arb"));
1800  GB_close(gb_slave);
1801  }
1802  GB_close(gb_master);
1803  }
1804 
1805  // test various error conditions:
1806 
1807  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, "", "b"), "specify a savename"); // empty name
1808  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, "b2b.arb", "bzB"), "Multiple compression modes");
1809 
1810  TEST_EXPECT_NO_ERROR(GB_set_mode_of_file(mod_db, 0444)); // write-protect
1811  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, mod_db, "b"), "already exists and is write protected"); // try to overwrite write-protected DB
1812 
1813  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_b2b, NULp), "specify a file name"); // no name
1814  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_b2b, ""), "specify a file name"); // empty name
1815 
1816  GB_close(gb_b2b);
1817  GB_close(gb_a2b);
1818  }
1819 
1820  GB_close(gb_asc);
1821  GB_close(gb_bin);
1822 
1823  TEST_loadsave_CLEANUP();
1824 }
1825 
1826 #define TEST_quicksave_CLEANUP() TEST_EXPECT_ZERO(system("rm -f min_bin.a[0-9]* min_bin.ARF"))
1827 
1828 inline bool quicksave_exists(int i) {
1829  const char *qsformat = "min_bin.a%02i";
1830  return GB_is_regularfile(GBS_global_string(qsformat, (i)));
1831 }
1832 inline bool quicksave_missng(int i) { return !quicksave_exists(i); }
1833 inline bool is_first_quicksave(int i) { return quicksave_exists(i) && !quicksave_exists(i-1); }
1834 inline bool is_last_quicksave(int i) { return quicksave_exists(i) && !quicksave_exists(i+1); }
1835 inline bool quicksaves_range(int from, int to) { return is_first_quicksave(from) && is_last_quicksave(to); }
1836 
1837 #define TEST_QUICK_RANGE(s,e) TEST_EXPECT(quicksaves_range(s,e))
1838 #define TEST_QUICK_GONE(i) TEST_EXPECT(quicksave_missng(i))
1839 
1840 void TEST_SLOW_quicksave_names() {
1841  // check quicksave delete and wrap around
1842  TEST_quicksave_CLEANUP();
1843  const char *bname = "min_bin.arb";
1844 
1845  GB_shell shell;
1846 
1847 #if 0
1848  {
1849  // update min_bin.arb from min_ascii.arb
1850  const char *aname = "min_ascii.arb";
1851  GBDATA *gb_ascii = GB_open(aname, "rw"); TEST_REJECT_NULL(gb_ascii);
1852 
1853  TEST_EXPECT_NO_ERROR(GB_save_as(gb_ascii, bname, "b"));
1854  GB_close(gb_ascii);
1855  }
1856 #endif
1857  GBDATA *gb_bin = GB_open(bname, "rw"); TEST_REJECT_NULL(gb_bin);
1858  for (int i = 0; i <= 100; ++i) {
1859  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_bin, bname));
1860  switch (i) {
1861  case 0: TEST_QUICK_RANGE( 0, 0); break;
1862  case 1: TEST_QUICK_RANGE( 0, 1); break;
1863  case 10: TEST_QUICK_RANGE( 1, 10); break;
1864  case 98: TEST_QUICK_RANGE(89, 98); break;
1865  case 99: TEST_QUICK_RANGE(90, 99);
1866  TEST_QUICK_GONE(0); // should not exist yet
1867  break;
1868  case 100: TEST_QUICK_RANGE(0, 9);
1869  TEST_QUICK_GONE((i-8));
1870  TEST_QUICK_GONE((i-1));
1871  TEST_QUICK_GONE(i);
1872  break;
1873  }
1874  if (i == 10) {
1875  // speed-up-hack
1876  GB_MAIN_TYPE *Main = GB_MAIN(gb_bin);
1877  i += 78; // -> 88 (afterwards run 10 times w/o checks to fake correct state)
1878  Main->qs.last_index += 78;
1879  }
1880  }
1881 
1882  GB_close(gb_bin);
1883 
1884  TEST_quicksave_CLEANUP();
1885 }
1886 
1887 void TEST_db_filenames() {
1888  TEST_EXPECT_EQUAL(gb_quicksaveName("nosuch.arb", 0), "nosuch.a00");
1889  TEST_EXPECT_EQUAL(gb_quicksaveName("nosuch", 1), "nosuch.a01");
1890 }
1891 
1892 void TEST_SLOW_corruptedEntries_saveProtection() {
1893  // see #499 and #501
1894  GB_shell shell;
1895 
1896  const char *name_NORMAL[] = {
1897  "corrupted.arb",
1898  "corrupted2.arb",
1899  };
1900  const char *name_CORRUPTED[] = {
1901  "corrupted_CORRUPTED.arb",
1902  "corrupted2_CORRUPTED.arb",
1903  };
1904 
1905  const char *quickname = "corrupted.a00";
1906  const char *quickname_CORRUPTED = "corrupted_CORRUPTED.a00";
1907  const char *quickname_unwanted = "corrupted_CORRUPTED.a01";
1908 
1909  const char **name = name_NORMAL;
1910 
1911  const char *INITIAL_VALUE = "initial value";
1912  const char *CHANGED_VALUE = "changed";
1913 
1914  GB_unlink("*~");
1915  GB_unlink(quickname_unwanted);
1916 
1917  for (int corruption = 0; corruption<=3; ++corruption) {
1918  TEST_ANNOTATE(GBS_global_string("corruption level %i", corruption));
1919 
1920  GB_unlink(name[0]);
1921 
1922  // create simple DB
1923  {
1924  GBDATA *gb_main = GB_open(name[0], "cwr");
1925  TEST_REJECT_NULL(gb_main);
1926 
1927  {
1928  GB_transaction ta(gb_main);
1929 
1930  GBDATA *gb_entry = GB_create(gb_main, "sth", GB_STRING);
1931  TEST_REJECT_NULL(gb_entry);
1932  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, INITIAL_VALUE));
1933 
1934  GBDATA *gb_other = GB_create(gb_main, "other", GB_INT);
1935  TEST_REJECT_NULL(gb_other);
1936  TEST_EXPECT_NO_ERROR(GB_write_int(gb_other, 4711));
1937  }
1938 
1939  TEST_EXPECT_NO_ERROR(GB_save(gb_main, NULp, "b"));
1940  GB_close(gb_main);
1941  }
1942 
1943  // reopen DB, change the entry, quick save + full save with different name
1944  {
1945  GBDATA *gb_main = GB_open(name[0], "wr");
1946  TEST_REJECT_NULL(gb_main);
1947 
1948  {
1949  GB_transaction ta(gb_main);
1950 
1951  GBDATA *gb_entry = GB_entry(gb_main, "sth");
1952  TEST_REJECT_NULL(gb_entry);
1953 
1954  const char *content = GB_read_char_pntr(gb_entry);
1955  TEST_EXPECT_EQUAL(content, INITIAL_VALUE);
1956 
1957  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, CHANGED_VALUE));
1958 
1959  content = GB_read_char_pntr(gb_entry);
1960  TEST_EXPECT_EQUAL(content, CHANGED_VALUE);
1961 
1962  // now corrupt the DB entry:
1963  if (corruption>0) {
1964  char *illegal_access = (char*)content;
1965  illegal_access[2] = 0;
1966 
1967  if (corruption>1) {
1968  gb_entry = GB_create(gb_main, "sth", GB_STRING);
1969  TEST_REJECT_NULL(gb_entry);
1970  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, INITIAL_VALUE));
1971 
1972  if (corruption>2) {
1973  // fill rest of string with zero bytes (similar to copying a truncated string into calloced memory)
1974  int len = strlen(CHANGED_VALUE);
1975  for (int i = 3; i<len; ++i) {
1976  illegal_access[i] = 0;
1977  }
1978  }
1979  }
1980 
1981 // #define PERFORM_DELETE
1982 #ifdef PERFORM_DELETE
1983  // delete "other"
1984  GBDATA *gb_other = GB_entry(gb_main, "other");
1985  TEST_REJECT_NULL(gb_other);
1986  TEST_EXPECT_NO_ERROR(GB_delete(gb_other));
1987 #endif
1988  }
1989  }
1990 
1991  GB_ERROR quick_error = GB_save_quick(gb_main, name[0]);
1993  if (corruption) {
1994  TEST_EXPECT_CONTAINS(quick_error, "Corrupted data detected during save");
1995  quick_error = GB_save_quick_as(gb_main, name_CORRUPTED[0]); // save with special name (as user should do)
1997  }
1998  TEST_REJECT(quick_error);
1999 
2000  GB_ERROR full_error = GB_save(gb_main, name[1], "b");
2002  if (corruption) {
2003  TEST_EXPECT_CONTAINS(full_error, "Corrupted data detected during save");
2004  full_error = GB_save(gb_main, name_CORRUPTED[1], "b"); // save with special name (as user should do)
2006  name = name_CORRUPTED; // from now on use these names (for load and save)
2007  }
2008  TEST_REJECT(full_error);
2009 
2010  GB_close(gb_main);
2011  }
2012 
2013  for (int full = 0; full<2; ++full) {
2014  TEST_ANNOTATE(GBS_global_string("corruption level %i / full=%i", corruption, full));
2015 
2016  // reopen DB (full==0 -> load quick save; ==1 -> load full save)
2017  GBDATA *gb_main = GB_open(name[full], "r");
2018  TEST_REJECT_NULL(gb_main);
2019 
2020  if (gb_main) {
2021  {
2022  GB_transaction ta(gb_main);
2023 
2024  GBDATA *gb_entry = GB_entry(gb_main, "sth");
2025  TEST_REJECT_NULL(gb_entry);
2026 
2027  const char *content = GB_read_char_pntr(gb_entry);
2028 
2029  switch (corruption) {
2030  case 0:
2031  TEST_EXPECT_EQUAL(content, CHANGED_VALUE);
2032  break;
2033  default:
2034  TEST_EXPECT_EQUAL(content, "ch");
2035  break;
2036  }
2037 
2038  // check 2nd entry
2039  gb_entry = GB_nextEntry(gb_entry);
2040  if (corruption>1) {
2041  TEST_REJECT_NULL(gb_entry);
2042 
2043  content = GB_read_char_pntr(gb_entry);
2044  TEST_EXPECT_EQUAL(content, INITIAL_VALUE);
2045  }
2046  else {
2047  TEST_REJECT(gb_entry);
2048  }
2049 
2050  // check int entry
2051  GBDATA *gb_other = GB_entry(gb_main, "other");
2052 #if defined(PERFORM_DELETE)
2053  bool deleted = corruption>0;
2054 #else // !defined(PERFORM_DELETE)
2055  bool deleted = false;
2056 #endif
2057 
2058  if (deleted) {
2059  TEST_REJECT(gb_other);
2060  }
2061  else {
2062  TEST_REJECT_NULL(gb_other);
2063  TEST_EXPECT_EQUAL(GB_read_int(gb_other), 4711);
2064  }
2065  }
2066 
2067  GB_close(gb_main);
2068  }
2069  GB_unlink(name_NORMAL[full]);
2070  GB_unlink(name_CORRUPTED[full]);
2071  }
2072  GB_unlink(quickname);
2073  GB_unlink(quickname_CORRUPTED);
2074 
2075  TEST_REJECT(GB_is_regularfile(quickname_unwanted));
2076 
2077  name = name_NORMAL; // restart with normal names
2078  }
2079 }
2080 
2081 TEST_PUBLISH(TEST_SLOW_corruptedEntries_saveProtection);
2082 
2083 #endif // UNIT_TESTS
GB_ERROR GB_begin_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2492
long last_main_saved_transaction
Definition: gb_main.h:142
void gb_put_number(long b0, FILE *out)
Definition: ad_io_inline.h:49
const char * GB_ERROR
Definition: arb_core.h:25
#define GB_SYSTEM_FOLDER
Definition: arbdb.h:27
GB_CSTR gb_mapfile_name(GB_CSTR path)
gb_quick_save qs
Definition: gb_main.h:127
string result
GBDATA * GB_open(const char *path, const char *opent)
Definition: ad_load.cxx:1363
GB_TYPES type
char * oname
Definition: readseq.c:463
GB_ERROR GB_commit_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2515
FileCompressionMode
Definition: arb_zfile.h:22
void GB_warning(const char *message)
Definition: arb_msg.cxx:484
char * path
Definition: gb_main.h:122
GB_ERROR GB_save_quick(GBDATA *gbd, const char *refpath)
GB_CSTR GB_path_in_arbprop(const char *relative_path)
Definition: adsocket.cxx:1109
#define GB_PUT_OUT(c, out)
#define SUPPORTED_COMPRESSION_FLAGS
Definition: arbdb.h:65
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:699
GBDATA * GB_child(GBDATA *father)
Definition: adquery.cxx:322
GB_MAIN_TYPE * gb_main_array[GB_MAIN_ARRAY_SIZE]
char * dates[ALLOWED_DATES]
Definition: gb_main.h:149
unsigned int security_read
Definition: gb_data.h:69
static GB_CSTR gb_reffile_name(GB_CSTR path)
Definition: arbdb.h:69
static int gb_is_writeable(gb_header_list *header, GBDATA *gbd, long version, long diff_save)
GB_ERROR GB_create_directory(const char *path)
GB_ERROR GB_write_string(GBDATA *gbd, const char *s)
Definition: arbdb.cxx:1361
static GB_ERROR gb_add_reference(const char *master, const char *changes)
gb_flag_types2 flags2
Definition: gb_data.h:135
static GB_BUFFER gb_bin_2_ascii(GBENTRY *gbe)
gb_data_base_type_union info
Definition: gb_data.h:204
long GB_mode_of_link(const char *path)
Definition: arb_file.cxx:68
GB_MAIN_TYPE * GB_MAIN(GBDATA *gbd)
Definition: gb_data.h:291
void GB_unlink_or_warn(const char *path, GB_ERROR *error)
Definition: arb_file.cxx:206
long GB_mode_of_file(const char *path)
Definition: arb_file.cxx:60
GB_ERROR GB_save_as(GBDATA *gbd, const char *path, const char *savetype)
long nref
Definition: gb_key.h:31
GBDATA * GB_nextEntry(GBDATA *entry)
Definition: adquery.cxx:339
int main(int argc, char **argv)
Definition: aisc.c:359
long
Definition: AW_awar.cxx:154
GB_ERROR GB_symlink(const char *target, const char *link)
Definition: arb_file.cxx:221
size_t memsize() const
Definition: gb_data.h:215
void GB_disable_quicksave(GBDATA *gbd, const char *reason)
Definition: arbdb.cxx:2611
long last_saved_time
Definition: gb_main.h:140
GB_ERROR GB_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:293
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
static GB_ERROR gb_create_reference(const char *master)
GB_ERROR GB_start_streamed_save_as(GBDATA *gbd, const char *path, const char *savetype, ArbDBWriter *&writer)
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
void warning(int warning_num, const char *warning_message)
Definition: util.cxx:61
long GB_getuid_of_file(const char *path)
Definition: arb_file.cxx:182
unsigned int unused
Definition: gb_data.h:71
GBDATA * GB_HEADER_LIST_GBD(gb_header_list &hl)
Definition: gb_header.h:42
static bool seen_corrupt_data
CONSTEXPR_INLINE gb_header_list * GB_DATA_LIST_HEADER(gb_data_list &dl)
Definition: gb_data.h:105
gb_header_flags & GB_ARRAY_FLAGS(GBDATA *gbd)
Definition: gb_header.h:49
void cat(const char *from)
Definition: arb_strbuf.h:156
void gb_write_out_uint32(uint32_t data, FILE *out)
Definition: ad_io_inline.h:37
int GB_unlink(const char *path)
Definition: arb_file.cxx:188
GB_TYPES type() const
Definition: gb_data.h:139
GB_MAIN_TYPE * GBCONTAINER_MAIN(GBCONTAINER *gbc)
Definition: gb_data.h:289
GB_CSTR gb_oldQuicksaveName(GB_CSTR path, int nr)
static bool gb_write_one_child(FILE *out, GBDATA *gb, GBCONTAINER *&gb_writeFrom, GBCONTAINER *gb_writeTill, int indent)
GBCONTAINER * root_container
Definition: gb_main.h:120
unsigned int last_updated
Definition: gb_main.h:139
FILE * ARB_zfopen(const char *name, const char *mode, FileCompressionMode cmode, GB_ERROR &error, bool hideStderr)
Definition: arb_zfile.cxx:42
GB_ERROR GB_set_time_of_file(const char *path, unsigned long new_time)
Definition: arb_file.cxx:50
#define ARRAY_ELEMS(array)
Definition: arb_defs.h:19
Definition: gb_key.h:28
char buffer[MESSAGE_BUFFERSIZE]
Definition: seq_search.cxx:34
gb_open_types opentype
Definition: gb_main.h:123
GB_ERROR GB_save_quick_as(GBDATA *gbd, const char *path)
unsigned long GB_ULONG
Definition: arbdb_base.h:42
const char * ARB_float_2_ascii(const float f)
Definition: arb_misc.cxx:66
GB_ERROR save_as(const char *as_path, const char *savetype)
GB_ERROR GB_delete(GBDATA *&source)
Definition: arbdb.cxx:1880
long GB_last_saved_clock(GBDATA *gb_main)
unsigned int last_updated
Definition: gb_data.h:79
GB_ERROR save_quick(const char *refpath)
static GB_CSTR gb_overwriteName(GB_CSTR path)
#define TEST_PUBLISH(testfunction)
Definition: test_unit.h:1484
unsigned int GB_UINT4
Definition: arbdb_base.h:37
#define TEST_EXPECT_CONTAINS(str, part)
Definition: test_unit.h:1301
uchar flags
Definition: probe_tree.h:38
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:353
GBDATA * GB_create_container(GBDATA *father, const char *key)
Definition: arbdb.cxx:1803
gb_flag_types flags
Definition: gb_data.h:134
#define TEST_EXPECT(cond)
Definition: test_unit.h:1312
Definition: arbdb.h:67
Definition: arbdb.h:78
void GB_warningf(const char *templat,...)
Definition: arb_msg.cxx:490
#define GB_PUT(c, out)
GBDATA * GB_create(GBDATA *father, const char *key, GB_TYPES type)
Definition: arbdb.cxx:1755
char * quick_save_disabled
Definition: gb_main.h:42
GB_BUFFER GB_give_buffer(size_t size)
Definition: arbdb.cxx:304
void GBS_fwrite_string(const char *strngi, FILE *out)
Definition: adstring.cxx:810
GB_ERROR startSaveAs(const char *given_path_, const char *savetype)
unsigned long GB_time_of_file(const char *path)
Definition: arb_file.cxx:44
GB_ERROR save_quick_as(const char *as_path)
#define false
Definition: ureadseq.h:13
#define RETURN_ERROR(err)
Definition: arb_msg.h:27
GB_ERROR GB_move_file(const char *oldpath, const char *newpath)
Definition: arb_file.cxx:284
bool is_container() const
Definition: gb_data.h:147
#define TEST_REJECT(cond)
Definition: test_unit.h:1314
#define TEST_REJECT_NULL(n)
Definition: test_unit.h:1309
static void error(const char *msg)
Definition: mkptypes.cxx:96
long gb_read_bin_error(FILE *in, GBDATA *gbd, const char *text)
bool is_client() const
Definition: gb_main.h:204
GB_ULONG GB_time_of_day(void)
Definition: adsocket.cxx:357
GB_CSTR gb_quicksaveName(GB_CSTR path, int nr)
gb_db_extended * ext
Definition: gb_data.h:132
int last_index
Definition: gb_main.h:43
unsigned int security_delete
Definition: gb_data.h:67
float GB_read_float(GBDATA *gbd)
Definition: arbdb.cxx:714
static char * gb_full_path(const char *path)
bool GB_is_fifo(const char *path)
Definition: arb_file.cxx:89
static GB_ERROR gb_delete_reference(const char *master)
GBCONTAINER * as_container() const
Definition: gb_data.h:155
void * gbm_get_mem(size_t size, long index)
Definition: gb_memory.h:130
int GB_read_flag(GBDATA *gbd)
Definition: arbdb.cxx:2760
GB_ERROR finishSave()
GB_CSTR GB_getcwd(void)
Definition: adfile.cxx:24
char * key
Definition: gb_key.h:29
GB_ERROR GB_stream_save_part(ArbDBWriter *writer, GBDATA *from, GBDATA *till)
GB_ERROR GB_set_mode_of_file(const char *path, long mode)
Definition: arb_file.cxx:231
GB_ERROR GB_write_int(GBDATA *gbd, long i)
Definition: arbdb.cxx:1220
unsigned int temporary
Definition: gb_data.h:73
GB_ERROR GB_save(GBDATA *gb, const char *path, const char *savetype)
GB_ERROR GB_delete_database(GB_CSTR filename)
static GB_ERROR gb_remove_all_but_main(GB_MAIN_TYPE *Main, const char *path)
void GB_disable_path(GBDATA *gbd, const char *path)
GB_ERROR GB_print_error()
Definition: arb_msg.cxx:335
int get_transaction_level() const
Definition: gb_main.h:177
long int flag
Definition: f2c.h:39
char * data()
Definition: gb_data.h:219
Definition: arbdb.h:72
fputs(TRACE_PREFIX, stderr)
unsigned int security_level
Definition: gb_main.h:150
GB_ERROR GB_export_errorf(const char *templat,...)
Definition: arb_msg.cxx:264
static GB_ERROR renameQuicksaves(GB_MAIN_TYPE *Main)
static GB_ERROR gb_remove_quick_saved(GB_MAIN_TYPE *Main, const char *path)
#define GBTUM_MAGIC_NUMBER
Definition: gb_local.h:25
#define gb_assert(cond)
Definition: arbdbt.h:11
unsigned int ever_changed
Definition: gb_header.h:23
const int GB_MAX_QUICK_SAVE_INDEX
Definition: adtune.cxx:23
GB_ERROR ARB_zfclose(FILE *fp)
Definition: arb_zfile.cxx:160
#define A_TO_I(c)
Definition: gb_load.h:16
bool GB_is_directory(const char *path)
Definition: arb_file.cxx:176
Definition: output.h:122
#define TEST_EXPECT_FILES_EQUAL(f1, f2)
Definition: test_unit.h:1393
GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error)
Definition: arb_msg.cxx:387
int GB_read_byte(GBDATA *gbd)
Definition: arbdb.cxx:704
char * STATIC_BUFFER(SmartCharPtr &strvar, int minlen)
const char * GBS_static_string(const char *str)
Definition: arb_msg.cxx:213
unsigned int changed
Definition: gb_header.h:22
#define GB_MAIN_ARRAY_SIZE
Definition: gb_load.h:14
static bool gb_write_childs(FILE *out, GBCONTAINER *gbc, GBCONTAINER *&gb_writeFrom, GBCONTAINER *gb_writeTill, int indent)
static GB_ERROR protect_corruption_error(const char *savepath)
static GB_ERROR deleteSuperfluousQuicksaves(GB_MAIN_TYPE *Main)
const char * GB_get_db_path(GBDATA *gbd)
Definition: adTest.cxx:14
#define TEST_EXPECT_NO_ERROR(call)
Definition: test_unit.h:1107
char * gb_findExtension(char *path)
gb_Key * keys
Definition: gb_main.h:134
GBENTRY * as_entry() const
Definition: gb_data.h:150
GB_CSTR GB_mapfile(GBDATA *gb_main)
long GB_read_clock(GBDATA *gbd)
Definition: arbdb.cxx:1688
char * GBS_eval_env(GB_CSTR p)
Definition: adstring.cxx:212
void GB_split_full_path(const char *fullpath, char **res_dir, char **res_fullname, char **res_name_only, char **res_suffix)
Definition: adsocket.cxx:1213
#define NULp
Definition: cxxforward.h:97
bool GB_is_regularfile(const char *path)
Definition: arb_file.cxx:76
gb_data_list d
Definition: gb_data.h:246
GB_CSTR GB_read_bits_pntr(GBDATA *gbd, char c_0, char c_1)
Definition: arbdb.cxx:896
size_t size() const
Definition: gb_data.h:214
#define TEST_EXPECT_ERROR_CONTAINS(call, part)
Definition: test_unit.h:1103
static int gb_write_bin(FILE *out, GBCONTAINER *gbc, uint32_t version)
long last_saved_transaction
Definition: gb_main.h:141
long update_date
Definition: gb_data.h:58
int keycnt
Definition: gb_main.h:131
gb_header_flags flags
Definition: gb_header.h:35
const char * get_data() const
Definition: arb_strbuf.h:70
const int GBTUM_SHORT_STRING_SIZE
Definition: adtune.cxx:28
GB_TYPES
Definition: arbdb.h:62
GBDATA * GB_nextChild(GBDATA *child)
Definition: adquery.cxx:326
GB_transaction ta(gb_var)
static int gb_write_bin_sub_containers(FILE *out, GBCONTAINER *gbc, long version, long diff_save, int is_root)
GB_CSTR GB_read_char_pntr(GBDATA *gbd)
Definition: arbdb.cxx:874
GBDATA * gb_main
Definition: adname.cxx:33
void gbm_free_mem(void *block, size_t size, long index)
Definition: gb_memory.h:131
Definition: arbdb.h:71
GB_ULONG GB_last_saved_time(GBDATA *gb_main)
bool GBS_string_matches(const char *str, const char *expr, GB_CASE case_sens)
Definition: admatch.cxx:193
const char * GB_KEY(GBDATA *gbd)
Definition: gb_key.h:49
ArbDBWriter(GB_MAIN_TYPE *Main_)
char * GB_follow_unix_link(const char *path)
Definition: arb_file.cxx:262
char * disabled_path
Definition: gb_main.h:124
const char * GB_get_supported_compression_flags(bool verboose)
int nheader
Definition: gb_data.h:102
const int GB_MAX_QUICK_SAVES
Definition: adtune.cxx:24
static long gb_write_bin_rek(FILE *out, GBDATA *gbd, long version, long diff_save, long index_of_master_file)
GB_ERROR gb_save_mapfile(GB_MAIN_TYPE *Main, GB_CSTR path)
Definition: admap.cxx:587
GB_ERROR saveFromTill(GBCONTAINER *gb_from, GBCONTAINER *gb_till)
GB_ERROR GB_create_parent_directory(const char *path)
#define STATIC_ASSERT(const_expression)
Definition: static_assert.h:36
GB_ERROR GB_finish_stream_save(ArbDBWriter *&writer)
const char * GB_CSTR
Definition: arbdb_base.h:25
GB_ERROR GB_save_in_arbprop(GBDATA *gb, const char *path, const char *savetype)
#define TEST_EXPECT_EQUAL(expr, want)
Definition: test_unit.h:1283
char * GB_BUFFER
Definition: arbdb_base.h:26
GBDATA * GB_entry(GBDATA *father, const char *key)
Definition: adquery.cxx:334
unsigned int security_write
Definition: gb_data.h:68
GBCONTAINER * get_father()
Definition: gb_data.h:276
unsigned int compressed_data
Definition: gb_data.h:70
long gb_ascii_2_bin(const char *source, GBENTRY *gbe)
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:195
void GB_close(GBDATA *gbd)
Definition: arbdb.cxx:625
Definition: arbdb.h:66
long creation_date
Definition: gb_data.h:57
unsigned int flags
Definition: gb_header.h:20
GB_write_int const char s
Definition: AW_awar.cxx:156