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