/*
bogoQDBMupgrade.c -- convert QDBM data base from Hash to B+Tree format
Copyright (C) 2004 Stefan Bellon
This program 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 of the License, or (at
your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* additional error checks and cleanups by Matthias Andree */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <depot.h>
#include <cabin.h>
#include <villa.h>
#include <stdlib.h>
#include <unistd.h>
#include "datastore_qdbm.h"
int main(int argc, char *argv[])
{
DEPOT *dho;
VILLA *dhn;
char *new_name;
#define TACKON "-new"
const char *tackon = TACKON;
int ret;
int ksiz, dsiz;
char *key, *data;
if (argc < 2 || argc > 4) {
fprintf(stderr,
"Usage: %s <database> [<new database>]\n"
" or: %s <database> <tempfile> <backup>\n"
" <new database> defaults to <database>" TACKON "\n"
" If <backup> is given, <database> is backed up to <backup> and\n"
" upgraded in place, via <tempfile>.\n",
argv[0], argv[0]);
exit(EXIT_FAILURE);
}
dho = dpopen(argv[1], DP_OREADER, 0);
if (!dho) {
fprintf(stderr, "Couldn't open database '%s': %s\n", argv[1],
dperrmsg(dpecode));
exit(EXIT_FAILURE);
}
if (dpgetflags(dho) & 1) {
fprintf(stderr, "Database '%s' is already in B+ tree format.\n", argv[1]);
dpclose(dho);
exit(EXIT_FAILURE);
}
if (argc >= 3) {
new_name = strdup(argv[2]);
} else {
new_name = (char *)malloc(strlen(argv[1]) + strlen(tackon) + 1);
if (new_name) {
strcpy(new_name, argv[1]);
strcat(new_name, tackon);
}
}
if (!new_name) {
fprintf(stderr, "Couldn't allocate memory.\n");
dpclose(dho);
exit(EXIT_FAILURE);
}
if (strcmp(new_name, argv[1]) == 0) {
fprintf(stderr, "database and %s must be different files!\n",
argc >= 4 ? "tempfile" : "new database");
dpclose(dho);
free(new_name);
exit(EXIT_FAILURE);
}
remove(new_name); /* start with a fresh data base */
dhn = vlopen(new_name, VL_OWRITER | VL_OCREAT, cmpkey);
if (!dhn) {
fprintf(stderr, "Couldn't create database '%s': %s\n", new_name,
dperrmsg(dpecode));
dpclose(dho);
remove(new_name);
free(new_name);
exit(EXIT_FAILURE);
}
ret = dpiterinit(dho);
if (ret) {
while ((key = dpiternext(dho, &ksiz))) {
data = dpget(dho, key, ksiz, 0, -1, &dsiz);
if (data) {
ret = vlput(dhn, key, ksiz, data, dsiz, VL_DOVER);
if (!ret) {
int i;
fprintf(stderr, "Error writing key '%.*s', value '0x",
ksiz, key);
for (i = 0 ; i < dsiz ; i++)
fprintf(stderr, "%02x", data[i]);
fprintf(stderr, "': %s\n", dperrmsg(dpecode));
free(data);
free(key);
goto barf;
}
free(data);
} else {
fprintf(stderr, "Error reading value for key '%.*s': %s\n",
ksiz, key, dperrmsg(dpecode));
free(key);
goto barf;
}
free(key);
}
} else {
fprintf(stderr, "Error creating database iterator: %s\n",
dperrmsg(dpecode));
goto barf;
}
if (dpfatalerror(dho)) {
fprintf(stderr, "Error: database has fatal error state after reading.\n"
"Last error was: %s", dperrmsg(dpecode));
goto barf;
}
if (!dpclose(dho)) {
fprintf(stderr, "Error closing input database: %s\n", dperrmsg(dpecode));
goto barf;
}
if (!vlclose(dhn)) {
fprintf(stderr, "Error closing output database: %s\n", dperrmsg(dpecode));
goto barf;
}
if (argc >= 4) {
/* rename output to input, taking care the file is always
* present */
remove(argv[3]); /* ignore errors */
if (link(argv[1], argv[3])) {
fprintf(stderr, "Error: cannot link %s to %s: %s\n",
argv[1], argv[3], strerror(errno));
free(new_name);
exit(EXIT_FAILURE);
}
if (rename(new_name, argv[1])) {
fprintf(stderr, "Error: cannot rename %s to %s: %s\n",
new_name, argv[1], strerror(errno));
free(new_name);
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
/* clean up in case of error */
barf:
vlclose(dhn);
remove(new_name);
free(new_name);
dpclose(dho);
exit(EXIT_FAILURE);
}