/* This file is part of GDBM test suite. Copyright (C) 2022-2023 Free Software Foundation, Inc. GDBM is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GDBM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GDBM. If not, see . */ #include "autoconf.h" #include #include #include #include #include #include #include #include #include "gdbm.h" #if __STDC_VERSION__ < 199901L # if __GNUC__ >= 2 # define __func__ __FUNCTION__ # else # define __func__ "" # endif #endif char a_name[] = "a.gdbm"; char b_name[] = "b.gdbm"; char orig_name[] = "orig.gdbm"; char bin_dumpname[] = "a.bin.dump"; char ascii_dumpname[] = "a.ascii.dump"; void db_perror (char const *fmt, ...) { int en = errno; va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, ": %s", gdbm_strerror (gdbm_errno)); if (gdbm_check_syserr (gdbm_errno)) fprintf (stderr, ": %s", strerror (en)); fputc ('\n', stderr); } GDBM_FILE createdb (char const *name) { int rc; char *cmdstub = "num2word 1:2000 | gtload -clear "; char *cmd; size_t cmdlen; GDBM_FILE db; cmdlen = strlen (cmdstub) + strlen (name); cmd = malloc (cmdlen + 1); assert (cmd != NULL); strcat (strcpy (cmd, cmdstub), name); rc = system (cmd); if (rc == -1) { perror (cmd); exit (1); } else if (WIFEXITED (rc)) { rc = WEXITSTATUS (rc); if (rc) { fprintf (stderr, "%s: terminated with code %d\n", cmd, rc); exit (1); } } else if (WIFSIGNALED (rc)) { fprintf (stderr, "%s: terminated on signal %d", cmd, WTERMSIG (rc)); if (WCOREDUMP (rc)) fprintf (stderr, " (core dumped)"); fputc ('\n', stderr); exit (1); } else { fprintf (stderr, "%s: terminated with unrecognized status: %d", cmd, rc); exit (1); } db = gdbm_open (name, 0, GDBM_READER, 0, NULL); if (db == NULL) { db_perror ("can't open %s", name); exit (1); } return db; } int db_cmp (GDBM_FILE a, GDBM_FILE b) { datum key, adata, bdata; gdbm_count_t an, bn; if (gdbm_count (a, &an)) { fprintf (stderr, "a: gdbm_count: %s\n", gdbm_db_strerror (a)); return -1; } if (gdbm_count (b, &bn)) { fprintf (stderr, "b: gdbm_count: %s\n", gdbm_db_strerror (b)); return -1; } if (an != bn) { fprintf (stderr, "key counts differ: a=%lu, b=%lu\n", (unsigned long)an, (unsigned long)bn); return -1; } key = gdbm_firstkey (a); while (key.dptr) { datum nextkey; adata = gdbm_fetch (a, key); if (adata.dptr == NULL) { fprintf (stderr, "a: can't get %*.*s: %s\n", key.dsize, key.dsize, key.dptr, gdbm_db_strerror (a)); free (key.dptr); return -1; } bdata = gdbm_fetch (b, key); if (bdata.dptr == NULL) { fprintf (stderr, "b: can't get %*.*s: %s\n", key.dsize, key.dsize, key.dptr, gdbm_db_strerror (b)); free (key.dptr); free (adata.dptr); return -1; } if (adata.dsize != bdata.dsize || memcmp (adata.dptr, bdata.dptr, adata.dsize)) { fprintf (stderr, "data differ for %*.*s\n", key.dsize, key.dsize, key.dptr); free (key.dptr); free (adata.dptr); free (bdata.dptr); return -1; } free (adata.dptr); free (bdata.dptr); nextkey = gdbm_nextkey (a, key); free (key.dptr); key = nextkey; } return 0; } /* * Load binary dump into a non-existing (NULL) database. This should * fail with GDBM_NO_DBNAME. */ void test_bindump_0 (GDBM_FILE dbf) { GDBM_FILE b = NULL; if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) { if (gdbm_errno != GDBM_NO_DBNAME) { db_perror ("%s: loading binary dump to non-existing database failed with unexpected error", __func__); exit (1); } } else { fprintf (stderr, "%s: loading binary dump to non-existing database succeeded when it should not\n", __func__); exit (1); } } /* * Load binary dump into an existing empty database. */ void test_bindump_1 (GDBM_FILE dbf) { GDBM_FILE b; /* * Create an empty database and retry loading into it. */ b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); if (b == NULL) { db_perror ("%s: can't open %s", __func__, b_name); exit (1); } if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) { db_perror ("%s: failed to load database from binary dump", __func__); exit (1); } /* Compare two databases. */ if (db_cmp (dbf, b)) { fprintf (stderr, "%s: databases differ\n", __func__); exit (1); } gdbm_close (b); } /* * Load binary dump to a non-empty database. * When tried with GDBM_INSERT replace parameter, it is expected to * fail with GDBM_CANNOT_REPLACE. * When run with GDBM_REPLACE, it should succeed. */ void test_bindump_2 (GDBM_FILE dbf) { GDBM_FILE b; datum key, dat; b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); if (b == NULL) { db_perror ("%s: can't open %s", __func__, b_name); exit (1); } key.dptr = "99"; key.dsize = 2; dat.dptr = "99"; dat.dsize = 2; if (gdbm_store (b, key, dat, 0)) { db_perror ("%s: can't store datum", __func__); exit (1); } gdbm_sync (b); if (gdbm_load (&b, bin_dumpname, GDBM_INSERT, 0, NULL)) { if (gdbm_errno != GDBM_CANNOT_REPLACE) { db_perror ("%s: expected GDBM_CANNOT_REPLACE, but got", __func__); exit (1); } } if (gdbm_load (&b, bin_dumpname, GDBM_REPLACE, 0, NULL)) { db_perror ("%s: failed to load from ASCII dump", __func__); exit (1); } if (db_cmp (dbf, b)) { fprintf (stderr, "%s: databases differ\n", __func__); exit (1); } gdbm_close (b); } /* * Test dumping to and restoring from binary dump format. */ void test_bindump (GDBM_FILE dbf) { /* * Create a binary dump. */ if (gdbm_dump (dbf, bin_dumpname, GDBM_DUMP_FMT_BINARY, GDBM_NEWDB, 0600)) { fprintf (stderr, "%s: failed to dump: %s\n", __func__, gdbm_db_strerror (dbf)); exit (1); } test_bindump_0 (dbf); test_bindump_1 (dbf); test_bindump_2 (dbf); } /* * Load ASCII dump into a non-existing (NULL) database. */ void test_asciidump_0 (GDBM_FILE dbf) { GDBM_FILE b = NULL; /* * Loading into a non-existing database should create it. */ if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) { db_perror ("%s: can't load from ascii dump", __func__); exit (1); } /* * The loaded database should have the same name as the original. */ if (access (a_name, F_OK)) { fprintf (stderr, "%s: %s: %s\n", __func__, a_name, strerror (errno)); exit (1); } /* Compare the two databases. */ if (db_cmp (dbf, b)) { fprintf (stderr, "%s: databases differ\n", __func__); exit (1); } gdbm_close (b); /* Cleanup */ unlink (a_name); } /* * Load ASCII dump into an existing empty database. */ void test_asciidump_1 (GDBM_FILE dbf) { GDBM_FILE b; /* * Now try loading into an existing database. */ b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); if (b == NULL) { db_perror ("%s: can't open %s", __func__, b_name); exit (1); } if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) { db_perror ("%s: can't load from ascii dump", __func__); exit (1); } /* * Just in case: check if a_name was not created on disk. */ if (access (a_name, F_OK) == 0) { fprintf (stderr, "%s: %s exists when it should not\n", __func__, a_name); exit (1); } if (db_cmp (dbf, b)) { fprintf (stderr, "%s: databases differ\n", __func__); exit (1); } gdbm_close (b); } /* * Load ASCII dump to a non-empty database. * When tried with GDBM_INSERT replace parameter, it is expected to * fail with GDBM_CANNOT_REPLACE. * When run with GDBM_REPLACE, it should succeed. */ void test_asciidump_2 (GDBM_FILE dbf) { GDBM_FILE b; datum key, dat; b = gdbm_open (b_name, 0, GDBM_NEWDB, 0644, NULL); if (b == NULL) { db_perror ("%s: can't open %s", __func__, b_name); exit (1); } key.dptr = "99"; key.dsize = 2; dat.dptr = "99"; dat.dsize = 2; if (gdbm_store (b, key, dat, 0)) { db_perror ("%s: can't store datum", __func__); exit (1); } gdbm_sync (b); if (gdbm_load (&b, ascii_dumpname, GDBM_INSERT, GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) { if (gdbm_errno != GDBM_CANNOT_REPLACE) { db_perror ("%s: expected GDBM_CANNOT_REPLACE, but got", __func__); exit (1); } } if (gdbm_load (&b, ascii_dumpname, GDBM_REPLACE, GDBM_META_MASK_MODE|GDBM_META_MASK_OWNER, NULL)) { db_perror ("%s: failed to load from ASCII dump", __func__); exit (1); } if (db_cmp (dbf, b)) { fprintf (stderr, "%s: databases differ\n", __func__); exit (1); } gdbm_close (b); } /* * Test dumping to and restoring from ASCII dump format. */ void test_asciidump (GDBM_FILE dbf) { /* * Create a dump in ASCII format. */ if (gdbm_dump (dbf, ascii_dumpname, GDBM_DUMP_FMT_ASCII, GDBM_NEWDB, 0600)) { fprintf (stderr, "%s: failed to dump: %s\n", __func__, gdbm_db_strerror (dbf)); exit (1); } /* * Rename the original database. */ if (unlink (orig_name) && errno != ENOENT) { fprintf (stderr, "%s: failed to remove %s: %s\n", __func__, orig_name, strerror (errno)); exit (1); } if (rename (a_name, orig_name)) { fprintf (stderr, "%s: can't rename %s to %s: %s\n", __func__, a_name, orig_name, strerror (errno)); exit (1); } test_asciidump_0 (dbf); test_asciidump_1 (dbf); test_asciidump_2 (dbf); } int main (int argc, char **argv) { GDBM_FILE db; db = createdb (a_name); test_bindump (db); test_asciidump (db); unlink (a_name); unlink (b_name); unlink (orig_name); unlink (bin_dumpname); unlink (ascii_dumpname); return 0; }