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>0) {
1094  error = "Cannot save while inside transaction";
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 = "Cannot save a remote database";
1525  if (!error && transaction_level>0) error = "Cannot save while inside transaction";
1526  if (!error) {
1527  qs.last_index++;
1529 
1530  GB_CSTR qck_path = gb_quicksaveName(path, qs.last_index);
1531  GB_CSTR sec_path = gb_overwriteName(qck_path);
1532 
1533  FILE *out = fopen(sec_path, "w");
1534  if (!out) error = GBS_global_string("Cannot save file to '%s'", sec_path);
1535  else {
1536  long erg;
1537  {
1538  const int org_security_level = security_level;
1539  int org_transaction_level = get_transaction_level();
1540 
1541  if (!org_transaction_level) transaction_level = 1;
1542  else {
1543  if (org_transaction_level> 0) {
1544  gb_assert(0); // @@@ should be impossible now
1547  }
1548  }
1549 
1550  security_level = 7;
1551  seen_corrupt_data = false;
1552 
1553  erg = gb_write_bin(out, root_container, 2);
1554 
1555  security_level = org_security_level;
1556  transaction_level = org_transaction_level;
1557  }
1558 
1559  erg |= fclose(out);
1560 
1561  if (erg!=0) error = GBS_global_string("Cannot write to '%s'", sec_path);
1562  else {
1563  if (seen_corrupt_data) {
1564  gb_assert(!error);
1565  error = protect_corruption_error(qck_path);
1566  }
1567  if (!error) error = GB_move_file(sec_path, qck_path);
1568  if (error) GB_unlink_or_warn(sec_path, NULp);
1569  }
1570  }
1571 
1572  if (error) qs.last_index--; // undo index increment
1573  else {
1576 
1577  error = deleteSuperfluousQuicksaves(this);
1578  }
1579  }
1580 
1581  RETURN_ERROR(error);
1582 }
1583 
1584 GB_ERROR GB_save_quick(GBDATA *gbd, const char *refpath) {
1585  return GB_MAIN(gbd)->save_quick(refpath);
1586 }
1587 
1588 
1589 void GB_disable_path(GBDATA *gbd, const char *path) {
1590  // disable directories for save
1591  freeset(GB_MAIN(gbd)->disabled_path, path ? GBS_eval_env(path) : NULp);
1592 }
1593 
1595  return GB_MAIN(gb_main)->last_saved_transaction;
1596 }
1597 
1599  return GB_MAIN(gb_main)->last_saved_time;
1600 }
1601 
1602 // --------------------------------------------------------------------------------
1603 
1604 #ifdef UNIT_TESTS
1605 
1606 #include <test_unit.h>
1607 
1608 // #define TEST_AUTO_UPDATE // uncomment to auto-update binary and quicksave testfiles (needed once after changing ascii testfile or modify_db())
1609 
1610 #if defined(TEST_AUTO_UPDATE)
1611 #define TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(src,dest) TEST_COPY_FILE(src,dest)
1612 #else
1613 #define TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(src,dest) TEST_EXPECT_TEXTFILES_EQUAL(src,dest)
1614 #endif
1615 
1616 #define SAVE_AND_COMPARE(gbd, save_as, savetype, compare_with) \
1617  TEST_EXPECT_NO_ERROR(GB_save_as(gbd, save_as, savetype)); \
1618  TEST_UPDATE_OR_EXPECT_TEXTFILES_EQUAL(save_as, compare_with)
1619 
1620 #define SAVE_AND_COMPARE__BROKEN(gbd, save_as, savetype, compare_with) \
1621  TEST_EXPECT_NO_ERROR(GB_save_as(gbd, save_as, savetype)); \
1622  TEST_EXPECT_FILES_EQUAL__BROKEN(save_as, compare_with)
1623 
1624 static GB_ERROR modify_db(GBDATA *gb_main) {
1625  GB_transaction ta(gb_main);
1626 
1627  GB_ERROR error = NULp;
1628  GBDATA *gb_container = GB_create_container(gb_main, "container");
1629  if (!gb_container) error = GB_await_error();
1630  else {
1631  GBDATA *gb_entry = GB_create(gb_container, "str", GB_STRING);
1632  if (!gb_entry) error = GB_await_error();
1633  else error = GB_write_string(gb_entry, "text");
1634  // else error = GB_write_string(gb_entry, "bla"); // provoke error in file compare
1635  }
1636  return error;
1637 }
1638 
1639 #define TEST_loadsave_CLEANUP() TEST_EXPECT_ZERO(system("rm -f [abr]2[ab]*.* master.* slave.* renamed.* fast.* fast2a.* TEST_loadsave.ARF"))
1640 
1641 void TEST_SLOW_loadsave() {
1642  GB_shell shell;
1643  TEST_loadsave_CLEANUP();
1644 
1645  // test non-existing DB
1646  TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_open("nonexisting.arb", "r"), "'nonexisting.arb' not found");
1647  TEST_EXPECT_NORESULT__ERROREXPORTED_CONTAINS(GB_open("nonexisting.arb", "rw"), "'nonexisting.arb' not found");
1648  {
1649  GBDATA *gb_new;
1650  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_new = GB_open("nonexisting.arb", "w")); // create new DB
1651  GB_close(gb_new);
1652  }
1653 
1654  // the following DBs have to be provided in directory ../UNIT_TESTER/run
1655  const char *bin_db = "TEST_loadsave.arb";
1656  const char *asc_db = "TEST_loadsave_ascii.arb";
1657 
1658  GBDATA *gb_asc;
1659  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_asc = GB_open(asc_db, "rw"));
1660 
1661 #if defined(TEST_AUTO_UPDATE)
1662  TEST_EXPECT_NO_ERROR(GB_save_as(gb_asc, bin_db, "b"));
1663 #endif // TEST_AUTO_UPDATE
1664 
1665  GBDATA *gb_bin;
1666  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_bin = GB_open(bin_db, "rw"));
1667 
1668  // test ASCII / BINARY compatibility
1669  SAVE_AND_COMPARE(gb_asc, "a2a.arb", "a", asc_db);
1670  SAVE_AND_COMPARE(gb_asc, "a2b.arb", "b", bin_db);
1671  SAVE_AND_COMPARE(gb_bin, "b2a.arb", "a", asc_db);
1672  SAVE_AND_COMPARE(gb_bin, "b2b.arb", "b", bin_db);
1673 
1674  // test extra database stream compression
1675  const char *compFlag = SUPPORTED_COMPRESSION_FLAGS;
1676 
1677  int successful_compressed_saves = 0;
1678  for (int c = 0; compFlag[c]; ++c) {
1679  for (char dbtype = 'a'; dbtype<='b'; ++dbtype) {
1680  TEST_ANNOTATE(GBS_global_string("dbtype=%c compFlag=%c", dbtype, compFlag[c]));
1681  char *zipd_db = GBS_global_string_copy("a2%c_%c.arb", dbtype, compFlag[c]);
1682 
1683  char savetype[] = "??";
1684  savetype[0] = dbtype;
1685  savetype[1] = compFlag[c];
1686 
1687  GB_ERROR error = GB_save_as(gb_asc, zipd_db, savetype);
1688  if (error && strstr(error, "failed with exitcode=127")) {
1689  fprintf(stderr, "Assuming compression utility for flag '%c' is not installed\n", compFlag[c]);
1690  }
1691  else {
1692  TEST_EXPECT_NO_ERROR(error);
1693 
1694  // reopen saved database, save again + compare
1695  {
1696  GBDATA *gb_reloaded = GB_open(zipd_db, "rw");
1697  TEST_REJECT_NULL(gb_reloaded); // reading compressed database failed
1698 
1699  SAVE_AND_COMPARE(gb_reloaded, "r2b.arb", "b", bin_db); // check binary content
1700  SAVE_AND_COMPARE(gb_reloaded, "r2a.arb", "a", asc_db); // check ascii content
1701 
1702  GB_close(gb_reloaded);
1703  }
1704  successful_compressed_saves++;
1705  }
1706 
1707  free(zipd_db);
1708  }
1709  }
1710 
1711  TEST_EXPECT(successful_compressed_saves>=2); // at least gzip and bzip2 should be installed
1712 
1713 #if (MEMORY_TEST == 0)
1714  {
1715  GBDATA *gb_nomap;
1716  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_nomap = GB_open(bin_db, "rw"));
1717  TEST_EXPECT_NO_ERROR(GB_save_as(gb_nomap, "fast.arb", "bm"));
1718  TEST_EXPECT(GB_is_regularfile("fast.ARM")); // assert map file has been saved
1719  TEST_EXPECT_EQUAL(GB_time_of_file("fast.ARM"), GB_time_of_file("fast.arb"));
1720  GB_close(gb_nomap);
1721  }
1722  {
1723  // open DB with mapfile
1724  GBDATA *gb_map;
1725  TEST_EXPECT_RESULT__NOERROREXPORTED(gb_map = GB_open("fast.arb", "rw"));
1726  // SAVE_AND_COMPARE(gb_map, "fast2b.arb", "b", bin_db); // fails now (because 3 keys have different key-ref-counts)
1727  // Surprise: these three keys are 'tmp', 'message' and 'pending'
1728  // (key-ref-counts include temporary entries, but after saving they vanish and remain wrong)
1729  SAVE_AND_COMPARE(gb_map, "fast2a.arb", "a", asc_db); // using ascii avoids that problem (no keys stored there)
1730 
1731  GB_close(gb_map);
1732  }
1733  {
1734  // test alloc/free (no real test, just call it for code coverage)
1735  char *small_block = (char*)gbm_get_mem(30, 5);
1736  gbm_free_mem(small_block, 30, 5);
1737 
1738  char *big_block = (char*)gbm_get_mem(3000, 6);
1739  gbm_free_mem(big_block, 3000, 6);
1740  }
1741 #endif
1742 
1743  {
1744  // test opening saved DBs
1745  GBDATA *gb_a2b = GB_open("a2b.arb", "rw"); TEST_REJECT_NULL(gb_a2b);
1746  GBDATA *gb_b2b = GB_open("b2b.arb", "rw"); TEST_REJECT_NULL(gb_b2b);
1747 
1748  // modify ..
1749  TEST_EXPECT_NO_ERROR(modify_db(gb_a2b));
1750  TEST_EXPECT_NO_ERROR(modify_db(gb_b2b));
1751 
1752  // .. and quicksave
1753  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_a2b, "a2b.arb"));
1754  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_b2b, "b2b.arb"));
1755 
1756 #if defined(TEST_AUTO_UPDATE)
1757  TEST_COPY_FILE("a2b.a00", "TEST_loadsave_quick.a00");
1758 #endif // TEST_AUTO_UPDATE
1759 
1760  TEST_EXPECT_FILES_EQUAL("TEST_loadsave_quick.a00", "a2b.a00");
1761  TEST_EXPECT_FILES_EQUAL("a2b.a00", "b2b.a00");
1762 
1763  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_a2b, "a2b.arb"));
1764 
1765  // check wether quicksave can be disabled
1766  GB_disable_quicksave(gb_a2b, "test it");
1767 
1768  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick(gb_a2b, "a2b.arb"), "Save Changes Disabled");
1769  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_a2b, "a2b.arb"), "Save Changes Disabled");
1770 
1771  const char *mod_db = "a2b_modified.arb";
1772  TEST_EXPECT_NO_ERROR(GB_save_as(gb_a2b, mod_db, "a")); // save modified DB (now ascii to avoid key-ref-problem)
1773  // test loading quicksave
1774  {
1775  GBDATA *gb_quickload = GB_open("a2b.arb", "rw"); // load DB which has a quicksave
1776  SAVE_AND_COMPARE(gb_quickload, "a2b_quickloaded.arb", "a", mod_db); // use ascii version (binary has key-ref-diffs)
1777  GB_close(gb_quickload);
1778  }
1779 
1780  {
1781  // check master/slave DBs
1782  TEST_EXPECT_NO_ERROR(GB_save_as(gb_b2b, "master.arb", "b"));
1783 
1784  GBDATA *gb_master = GB_open("master.arb", "rw"); TEST_REJECT_NULL(gb_master);
1785  TEST_EXPECT_NO_ERROR(modify_db(gb_master));
1786 
1787  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_master, "master.arb"));
1788  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_master, "master.arb"));
1789 
1790  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick(gb_master, "renamed.arb"), "master file rename"); // quicksave with wrong name
1791 
1792  // check if master gets protected by creating slave-DB
1793  TEST_EXPECT_NO_ERROR(GB_save_as(gb_master, "master.arb", "b")); // overwrite
1794  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_master, "slave.arb")); // create slave -> master now protected
1795  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_master, "master.arb", "b"), "already exists and is write protected"); // overwrite should fail now
1796 
1797  {
1798  GBDATA *gb_slave = GB_open("slave.arb", "rw"); TEST_REJECT_NULL(gb_slave); // load slave DB
1799  TEST_EXPECT_NO_ERROR(modify_db(gb_slave));
1800  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_slave, "slave.arb"));
1801  TEST_EXPECT_NO_ERROR(GB_save_quick_as(gb_slave, "renamed.arb"));
1802  GB_close(gb_slave);
1803  }
1804  GB_close(gb_master);
1805  }
1806 
1807  // test various error conditions:
1808 
1809  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, "", "b"), "specify a savename"); // empty name
1810  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, "b2b.arb", "bzB"), "Multiple compression modes");
1811 
1812  TEST_EXPECT_NO_ERROR(GB_set_mode_of_file(mod_db, 0444)); // write-protect
1813  TEST_EXPECT_ERROR_CONTAINS(GB_save_as(gb_b2b, mod_db, "b"), "already exists and is write protected"); // try to overwrite write-protected DB
1814 
1815  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_b2b, NULp), "specify a file name"); // no name
1816  TEST_EXPECT_ERROR_CONTAINS(GB_save_quick_as(gb_b2b, ""), "specify a file name"); // empty name
1817 
1818  GB_close(gb_b2b);
1819  GB_close(gb_a2b);
1820  }
1821 
1822  GB_close(gb_asc);
1823  GB_close(gb_bin);
1824 
1825  TEST_loadsave_CLEANUP();
1826 }
1827 
1828 #define TEST_quicksave_CLEANUP() TEST_EXPECT_ZERO(system("rm -f min_bin.a[0-9]* min_bin.ARF"))
1829 
1830 inline bool quicksave_exists(int i) {
1831  const char *qsformat = "min_bin.a%02i";
1832  return GB_is_regularfile(GBS_global_string(qsformat, (i)));
1833 }
1834 inline bool quicksave_missng(int i) { return !quicksave_exists(i); }
1835 inline bool is_first_quicksave(int i) { return quicksave_exists(i) && !quicksave_exists(i-1); }
1836 inline bool is_last_quicksave(int i) { return quicksave_exists(i) && !quicksave_exists(i+1); }
1837 inline bool quicksaves_range(int from, int to) { return is_first_quicksave(from) && is_last_quicksave(to); }
1838 
1839 #define TEST_QUICK_RANGE(s,e) TEST_EXPECT(quicksaves_range(s,e))
1840 #define TEST_QUICK_GONE(i) TEST_EXPECT(quicksave_missng(i))
1841 
1842 void TEST_SLOW_quicksave_names() {
1843  // check quicksave delete and wrap around
1844  TEST_quicksave_CLEANUP();
1845  const char *bname = "min_bin.arb";
1846 
1847  GB_shell shell;
1848 
1849 #if 0
1850  {
1851  // update min_bin.arb from min_ascii.arb
1852  const char *aname = "min_ascii.arb";
1853  GBDATA *gb_ascii = GB_open(aname, "rw"); TEST_REJECT_NULL(gb_ascii);
1854 
1855  TEST_EXPECT_NO_ERROR(GB_save_as(gb_ascii, bname, "b"));
1856  GB_close(gb_ascii);
1857  }
1858 #endif
1859  GBDATA *gb_bin = GB_open(bname, "rw"); TEST_REJECT_NULL(gb_bin);
1860  for (int i = 0; i <= 100; ++i) {
1861  TEST_EXPECT_NO_ERROR(GB_save_quick(gb_bin, bname));
1862  switch (i) {
1863  case 0: TEST_QUICK_RANGE( 0, 0); break;
1864  case 1: TEST_QUICK_RANGE( 0, 1); break;
1865  case 10: TEST_QUICK_RANGE( 1, 10); break;
1866  case 98: TEST_QUICK_RANGE(89, 98); break;
1867  case 99: TEST_QUICK_RANGE(90, 99);
1868  TEST_QUICK_GONE(0); // should not exist yet
1869  break;
1870  case 100: TEST_QUICK_RANGE(0, 9);
1871  TEST_QUICK_GONE((i-8));
1872  TEST_QUICK_GONE((i-1));
1873  TEST_QUICK_GONE(i);
1874  break;
1875  }
1876  if (i == 10) {
1877  // speed-up-hack
1878  GB_MAIN_TYPE *Main = GB_MAIN(gb_bin);
1879  i += 78; // -> 88 (afterwards run 10 times w/o checks to fake correct state)
1880  Main->qs.last_index += 78;
1881  }
1882  }
1883 
1884  GB_close(gb_bin);
1885 
1886  TEST_quicksave_CLEANUP();
1887 }
1888 
1889 void TEST_db_filenames() {
1890  TEST_EXPECT_EQUAL(gb_quicksaveName("nosuch.arb", 0), "nosuch.a00");
1891  TEST_EXPECT_EQUAL(gb_quicksaveName("nosuch", 1), "nosuch.a01");
1892 }
1893 
1894 void TEST_SLOW_corruptedEntries_saveProtection() {
1895  // see #499 and #501
1896  GB_shell shell;
1897 
1898  const char *name_NORMAL[] = {
1899  "corrupted.arb",
1900  "corrupted2.arb",
1901  };
1902  const char *name_CORRUPTED[] = {
1903  "corrupted_CORRUPTED.arb",
1904  "corrupted2_CORRUPTED.arb",
1905  };
1906 
1907  const char *quickname = "corrupted.a00";
1908  const char *quickname_CORRUPTED = "corrupted_CORRUPTED.a00";
1909  const char *quickname_unwanted = "corrupted_CORRUPTED.a01";
1910 
1911  const char **name = name_NORMAL;
1912 
1913  const char *INITIAL_VALUE = "initial value";
1914  const char *CHANGED_VALUE = "changed";
1915 
1916  GB_unlink("*~");
1917  GB_unlink(quickname_unwanted);
1918 
1919  for (int corruption = 0; corruption<=3; ++corruption) {
1920  TEST_ANNOTATE(GBS_global_string("corruption level %i", corruption));
1921 
1922  GB_unlink(name[0]);
1923 
1924  // create simple DB
1925  {
1926  GBDATA *gb_main = GB_open(name[0], "cwr");
1927  TEST_REJECT_NULL(gb_main);
1928 
1929  {
1930  GB_transaction ta(gb_main);
1931 
1932  GBDATA *gb_entry = GB_create(gb_main, "sth", GB_STRING);
1933  TEST_REJECT_NULL(gb_entry);
1934  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, INITIAL_VALUE));
1935 
1936  GBDATA *gb_other = GB_create(gb_main, "other", GB_INT);
1937  TEST_REJECT_NULL(gb_other);
1938  TEST_EXPECT_NO_ERROR(GB_write_int(gb_other, 4711));
1939  }
1940 
1941  TEST_EXPECT_NO_ERROR(GB_save(gb_main, NULp, "b"));
1942  GB_close(gb_main);
1943  }
1944 
1945  // reopen DB, change the entry, quick save + full save with different name
1946  {
1947  GBDATA *gb_main = GB_open(name[0], "wr");
1948  TEST_REJECT_NULL(gb_main);
1949 
1950  {
1951  GB_transaction ta(gb_main);
1952 
1953  GBDATA *gb_entry = GB_entry(gb_main, "sth");
1954  TEST_REJECT_NULL(gb_entry);
1955 
1956  const char *content = GB_read_char_pntr(gb_entry);
1957  TEST_EXPECT_EQUAL(content, INITIAL_VALUE);
1958 
1959  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, CHANGED_VALUE));
1960 
1961  content = GB_read_char_pntr(gb_entry);
1962  TEST_EXPECT_EQUAL(content, CHANGED_VALUE);
1963 
1964  // now corrupt the DB entry:
1965  if (corruption>0) {
1966  char *illegal_access = (char*)content;
1967  illegal_access[2] = 0;
1968 
1969  if (corruption>1) {
1970  gb_entry = GB_create(gb_main, "sth", GB_STRING);
1971  TEST_REJECT_NULL(gb_entry);
1972  TEST_EXPECT_NO_ERROR(GB_write_string(gb_entry, INITIAL_VALUE));
1973 
1974  if (corruption>2) {
1975  // fill rest of string with zero bytes (similar to copying a truncated string into calloced memory)
1976  int len = strlen(CHANGED_VALUE);
1977  for (int i = 3; i<len; ++i) {
1978  illegal_access[i] = 0;
1979  }
1980  }
1981  }
1982 
1983 // #define PERFORM_DELETE
1984 #ifdef PERFORM_DELETE
1985  // delete "other"
1986  GBDATA *gb_other = GB_entry(gb_main, "other");
1987  TEST_REJECT_NULL(gb_other);
1988  TEST_EXPECT_NO_ERROR(GB_delete(gb_other));
1989 #endif
1990  }
1991  }
1992 
1993  GB_ERROR quick_error = GB_save_quick(gb_main, name[0]);
1995  if (corruption) {
1996  TEST_EXPECT_CONTAINS(quick_error, "Corrupted data detected during save");
1997  quick_error = GB_save_quick_as(gb_main, name_CORRUPTED[0]); // save with special name (as user should do)
1999  }
2000  TEST_REJECT(quick_error);
2001 
2002  GB_ERROR full_error = GB_save(gb_main, name[1], "b");
2004  if (corruption) {
2005  TEST_EXPECT_CONTAINS(full_error, "Corrupted data detected during save");
2006  full_error = GB_save(gb_main, name_CORRUPTED[1], "b"); // save with special name (as user should do)
2008  name = name_CORRUPTED; // from now on use these names (for load and save)
2009  }
2010  TEST_REJECT(full_error);
2011 
2012  GB_close(gb_main);
2013  }
2014 
2015  for (int full = 0; full<2; ++full) {
2016  TEST_ANNOTATE(GBS_global_string("corruption level %i / full=%i", corruption, full));
2017 
2018  // reopen DB (full==0 -> load quick save; ==1 -> load full save)
2019  GBDATA *gb_main = GB_open(name[full], "r");
2020  TEST_REJECT_NULL(gb_main);
2021 
2022  if (gb_main) {
2023  {
2024  GB_transaction ta(gb_main);
2025 
2026  GBDATA *gb_entry = GB_entry(gb_main, "sth");
2027  TEST_REJECT_NULL(gb_entry);
2028 
2029  const char *content = GB_read_char_pntr(gb_entry);
2030 
2031  switch (corruption) {
2032  case 0:
2033  TEST_EXPECT_EQUAL(content, CHANGED_VALUE);
2034  break;
2035  default:
2036  TEST_EXPECT_EQUAL(content, "ch");
2037  break;
2038  }
2039 
2040  // check 2nd entry
2041  gb_entry = GB_nextEntry(gb_entry);
2042  if (corruption>1) {
2043  TEST_REJECT_NULL(gb_entry);
2044 
2045  content = GB_read_char_pntr(gb_entry);
2046  TEST_EXPECT_EQUAL(content, INITIAL_VALUE);
2047  }
2048  else {
2049  TEST_REJECT(gb_entry);
2050  }
2051 
2052  // check int entry
2053  GBDATA *gb_other = GB_entry(gb_main, "other");
2054 #if defined(PERFORM_DELETE)
2055  bool deleted = corruption>0;
2056 #else // !defined(PERFORM_DELETE)
2057  bool deleted = false;
2058 #endif
2059 
2060  if (deleted) {
2061  TEST_REJECT(gb_other);
2062  }
2063  else {
2064  TEST_REJECT_NULL(gb_other);
2065  TEST_EXPECT_EQUAL(GB_read_int(gb_other), 4711);
2066  }
2067  }
2068 
2069  GB_close(gb_main);
2070  }
2071  GB_unlink(name_NORMAL[full]);
2072  GB_unlink(name_CORRUPTED[full]);
2073  }
2074  GB_unlink(quickname);
2075  GB_unlink(quickname_CORRUPTED);
2076 
2077  TEST_REJECT(GB_is_regularfile(quickname_unwanted));
2078 
2079  name = name_NORMAL; // restart with normal names
2080  }
2081 }
2082 
2083 TEST_PUBLISH(TEST_SLOW_corruptedEntries_saveProtection);
2084 
2085 #endif // UNIT_TESTS
GB_ERROR GB_begin_transaction(GBDATA *gbd)
Definition: arbdb.cxx:2516
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:2539
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:1130
#define GB_PUT_OUT(c, out)
#define SUPPORTED_COMPRESSION_FLAGS
Definition: arbdb.h:65
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:723
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:1385
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:2635
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:158
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:1904
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:1485
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:1827
gb_flag_types flags
Definition: gb_data.h:134
#define TEST_EXPECT(cond)
Definition: test_unit.h:1313
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:1779
char * quick_save_disabled
Definition: gb_main.h:42
GB_BUFFER GB_give_buffer(size_t size)
Definition: arbdb.cxx:305
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:1315
#define TEST_REJECT_NULL(n)
Definition: test_unit.h:1310
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:738
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:2784
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:1244
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:166
#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:1394
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:728
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:1712
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:1234
#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:920
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:898
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:649
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