summaryrefslogtreecommitdiff
path: root/utils/tokudb_load.cc
diff options
context:
space:
mode:
authorLeif Walsh <leif@tokutek.com>2012-07-19 13:06:39 +0000
committerYoni Fogel <yoni@tokutek.com>2013-04-17 00:00:59 -0400
commit9fbea5d8c9541ccd73f3bbe1a635a74f9c5cff37 (patch)
tree0e3ec7cbeafa6028837e0410a0cceaba80eda0d9 /utils/tokudb_load.cc
parent0f39e134dd7b9e68460965abe9a099d9edf171ba (diff)
downloadmariadb-git-9fbea5d8c9541ccd73f3bbe1a635a74f9c5cff37.tar.gz
closes #5206 merge c++ changes to mainline
git-svn-id: file:///svn/toku/tokudb@45903 c7de825b-a66e-492c-adef-691d508d4ae1
Diffstat (limited to 'utils/tokudb_load.cc')
-rw-r--r--utils/tokudb_load.cc892
1 files changed, 892 insertions, 0 deletions
diff --git a/utils/tokudb_load.cc b/utils/tokudb_load.cc
new file mode 100644
index 00000000000..4a4b6756a80
--- /dev/null
+++ b/utils/tokudb_load.cc
@@ -0,0 +1,892 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
+#ident "$Id$"
+#ident "Copyright (c) 2007, 2008 Tokutek Inc. All rights reserved."
+
+#include <toku_portability.h>
+#include <toku_assert.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <db.h>
+#include "tokudb_common.h"
+
+typedef struct {
+ bool leadingspace;
+ bool plaintext;
+ bool overwritekeys;
+ bool header;
+ bool eof;
+ bool keys;
+ bool is_private;
+ char* progname;
+ char* homedir;
+ char* database;
+ char* subdatabase;
+ char** config_options;
+ int32_t version;
+ int exitcode;
+ u_int64_t linenumber;
+ DBTYPE dbtype;
+ DB* db;
+ DB_ENV* dbenv;
+ struct {
+ char* data[2];
+ } get_dbt;
+ struct {
+ char* data;
+ } read_header;
+} load_globals;
+
+load_globals g;
+#include "tokudb_common_funcs.h"
+
+static int usage (void);
+static int load_database (void);
+static int create_init_env(void);
+static int read_header (void);
+static int open_database (void);
+static int read_keys (void);
+static int apply_commandline_options(void);
+static int close_database (void);
+static int doublechararray(char** pmem, u_int64_t* size);
+
+int test_main(int argc, char *const argv[]) {
+ int ch;
+ int retval;
+ char** next_config_option;
+
+ /* Set up the globals. */
+ memset(&g, 0, sizeof(g));
+ g.leadingspace = true;
+ g.overwritekeys = true;
+ g.dbtype = DB_UNKNOWN;
+ //g.dbtype = DB_BTREE;
+ g.progname = argv[0];
+ g.header = true;
+
+ if (verify_library_version() != 0) goto error;
+
+ next_config_option = g.config_options = (char**) calloc(argc, sizeof(char*));
+ if (next_config_option == NULL) {
+ PRINT_ERROR(errno, "main: calloc\n");
+ goto error;
+ }
+ while ((ch = getopt(argc, argv, "c:f:h:nP:r:Tt:V")) != EOF) {
+ switch (ch) {
+ case ('c'): {
+ *next_config_option++ = optarg;
+ break;
+ }
+ case ('f'): {
+ if (freopen(optarg, "r", stdin) == NULL) {
+ fprintf(stderr,
+ "%s: %s: reopen: %s\n",
+ g.progname, optarg, strerror(errno));
+ goto error;
+ }
+ break;
+ }
+ case ('h'): {
+ g.homedir = optarg;
+ break;
+ }
+ case ('n'): {
+ /* g.overwritekeys = false; */
+ PRINT_ERRORX("-%c option not supported.\n", ch);
+ goto error;
+ }
+ case ('P'): {
+ /* Clear password. */
+ memset(optarg, 0, strlen(optarg));
+ PRINT_ERRORX("-%c option not supported.\n", ch);
+ goto error;
+ }
+ case ('r'): {
+ PRINT_ERRORX("-%c option not supported.\n", ch);
+ goto error;
+ }
+ case ('T'): {
+ g.plaintext = true;
+ g.leadingspace = false;
+ g.header = false;
+ break;
+ }
+ case ('t'): {
+ if (!strcmp(optarg, "btree")) {
+ g.dbtype = DB_BTREE;
+ break;
+ }
+ if (!strcmp(optarg, "hash") || !strcmp(optarg, "recno") || !strcmp(optarg, "queue")) {
+ fprintf(stderr, "%s: db type %s not supported.\n", g.progname, optarg);
+ goto error;
+ }
+ fprintf(stderr, "%s: Unrecognized db type %s.\n", g.progname, optarg);
+ goto error;
+ }
+ case ('V'): {
+ printf("%s\n", db_version(NULL, NULL, NULL));
+ goto cleanup;
+ }
+ case ('?'):
+ default: {
+ g.exitcode = usage();
+ goto cleanup;
+ }
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ g.exitcode = usage();
+ goto cleanup;
+ }
+ init_catch_signals();
+
+ g.database = argv[0];
+ if (create_init_env() != 0) goto error;
+ if (caught_any_signals()) goto cleanup;
+ while (!g.eof) {
+ if (load_database() != 0) goto error;
+ if (caught_any_signals()) goto cleanup;
+ }
+ if (false) {
+error:
+ g.exitcode = EXIT_FAILURE;
+ fprintf(stderr, "%s: Quitting out due to errors.\n", g.progname);
+ }
+cleanup:
+ if (g.dbenv && (retval = g.dbenv->close(g.dbenv, 0)) != 0) {
+ g.exitcode = EXIT_FAILURE;
+ fprintf(stderr, "%s: dbenv->close: %s\n", g.progname, db_strerror(retval));
+ }
+ if (g.config_options) toku_free(g.config_options);
+ if (g.subdatabase) toku_free(g.subdatabase);
+ if (g.read_header.data) toku_free(g.read_header.data);
+ if (g.get_dbt.data[0]) toku_free(g.get_dbt.data[0]);
+ if (g.get_dbt.data[1]) toku_free(g.get_dbt.data[1]);
+ resend_signals();
+
+ return g.exitcode;
+}
+
+int load_database()
+{
+ int retval;
+
+ /* Create a database handle. */
+ retval = db_create(&g.db, g.dbenv, 0);
+ if (retval != 0) {
+ PRINT_ERROR(retval, "db_create");
+ return EXIT_FAILURE;
+ }
+
+ if (g.header && read_header() != 0) goto error;
+ if (g.eof) goto cleanup;
+ if (caught_any_signals()) goto cleanup;
+ if (apply_commandline_options() != 0) goto error;
+ if (g.eof) goto cleanup;
+ if (caught_any_signals()) goto cleanup;
+
+ /*
+ TODO: If/when supporting encryption
+ if (g.password && (retval = db->set_flags(db, DB_ENCRYPT))) {
+ PRINT_ERROR(ret, "DB->set_flags: DB_ENCRYPT");
+ goto error;
+ }
+ */
+ if (open_database() != 0) goto error;
+ if (g.eof) goto cleanup;
+ if (caught_any_signals()) goto cleanup;
+ if (read_keys() != 0) goto error;
+ if (g.eof) goto cleanup;
+ if (caught_any_signals()) goto cleanup;
+
+ if (false) {
+error:
+ g.exitcode = EXIT_FAILURE;
+ }
+cleanup:
+
+ if (close_database() != 0) g.exitcode = EXIT_FAILURE;
+
+ return g.exitcode;
+}
+
+int usage()
+{
+ fprintf(stderr,
+ "usage: %s [-TV] [-c name=value] [-f file] [-h home] [-t btree] db_file\n",
+ g.progname);
+ return EXIT_FAILURE;
+}
+
+int create_init_env()
+{
+ int retval;
+ DB_ENV* dbenv;
+ int flags;
+ //TODO: Experiments to determine right cache size for tokudb, or maybe command line argument.
+ //int cache = 1 << 20; /* 1 megabyte */
+
+ retval = db_env_create(&dbenv, 0);
+ if (retval) {
+ fprintf(stderr, "%s: db_dbenv_create: %s\n", g.progname, db_strerror(retval));
+ goto error;
+ }
+ ///TODO: UNCOMMENT/IMPLEMENT dbenv->set_errfile(dbenv, stderr);
+ dbenv->set_errpfx(dbenv, g.progname);
+ /*
+ TODO: If/when supporting encryption
+ if (g.password && (retval = dbenv->set_encrypt(dbenv, g.password, DB_ENCRYPT_AES))) {
+ PRINT_ERROR(retval, "set_passwd");
+ goto error;
+ }
+ */
+
+ /* Open the dbenvironment. */
+ g.is_private = false;
+ flags = DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL|DB_INIT_TXN|DB_INIT_LOG; ///TODO: UNCOMMENT/IMPLEMENT | DB_USE_ENVIRON;
+ //TODO: Transactions.. SET_BITS(flags, DB_INIT_TXN);
+
+ /*
+ ///TODO: UNCOMMENT/IMPLEMENT Notes: We require DB_PRIVATE
+ if (!dbenv->open(dbenv, g.homedir, flags, 0)) goto success;
+ */
+
+ /*
+ ///TODO: UNCOMMENT/IMPLEMENT
+ retval = dbenv->set_cachesize(dbenv, 0, cache, 1);
+ if (retval) {
+ PRINT_ERROR(retval, "DB_ENV->set_cachesize");
+ goto error;
+ }
+ */
+ g.is_private = true;
+ //TODO: Do we want to support transactions/logging even in single-process mode?
+ //Maybe if the db already exists.
+ //If db does not exist.. makes sense not to log or have transactions
+ //REMOVE_BITS(flags, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN);
+ SET_BITS(flags, DB_CREATE | DB_PRIVATE);
+
+ retval = dbenv->open(dbenv, g.homedir ? g.homedir : ".", flags, 0);
+ if (retval) {
+ PRINT_ERROR(retval, "DB_ENV->open");
+ goto error;
+ }
+ g.dbenv = dbenv;
+ return EXIT_SUCCESS;
+
+error:
+ return EXIT_FAILURE;
+}
+
+#define PARSE_NUMBER(match, dbfunction) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 1, INT32_MAX, 10)) goto error; \
+ if ((retval = dbfunction(db, num)) != 0) goto printerror; \
+ continue; \
+}
+#define PARSE_UNSUPPORTEDNUMBER(match, dbfunction) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 1, INT32_MAX, 10)) goto error; \
+ PRINT_ERRORX("%s option not supported.\n", field); \
+ goto error; \
+}
+#define PARSE_IGNOREDNUMBER(match, dbfunction) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 1, INT32_MAX, 10)) goto error; \
+ PRINT_ERRORX("%s option not supported yet (ignored).\n", field); \
+ continue; \
+}
+
+#define PARSE_FLAG(match, flag) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 0, 1, 10)) { \
+ PRINT_ERRORX("%s: boolean name=value pairs require a value of 0 or 1", \
+ field); \
+ goto error; \
+ } \
+ if ((retval = db->set_flags(db, flag)) != 0) { \
+ PRINT_ERROR(retval, "set_flags: %s", field); \
+ goto error; \
+ } \
+ continue; \
+}
+
+#define PARSE_UNSUPPORTEDFLAG(match, flag) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 0, 1, 10)) { \
+ PRINT_ERRORX("%s: boolean name=value pairs require a value of 0 or 1", \
+ field); \
+ goto error; \
+ } \
+ PRINT_ERRORX("%s option not supported.\n", field); \
+ goto error; \
+}
+
+#define PARSE_IGNOREDFLAG(match, flag) \
+if (!strcmp(field, match)) { \
+ if (strtoint32(value, &num, 0, 1, 10)) { \
+ PRINT_ERRORX("%s: boolean name=value pairs require a value of 0 or 1", \
+ field); \
+ goto error; \
+ } \
+ PRINT_ERRORX("%s option not supported yet (ignored).\n", field); \
+ continue; \
+}
+
+#define PARSE_CHAR(match, dbfunction) \
+if (!strcmp(field, match)) { \
+ if (strlen(value) != 1) { \
+ PRINT_ERRORX("%s=%s: Expected 1-byte value", \
+ field, value); \
+ goto error; \
+ } \
+ if ((retval = dbfunction(db, value[0])) != 0) { \
+ goto printerror; \
+ } \
+ continue; \
+}
+
+#define PARSE_UNSUPPORTEDCHAR(match, dbfunction) \
+if (!strcmp(field, match)) { \
+ if (strlen(value) != 1) { \
+ PRINT_ERRORX("%s=%s: Expected 1-byte value", \
+ field, value); \
+ goto error; \
+ } \
+ PRINT_ERRORX("%s option not supported.\n", field); \
+ goto error; \
+}
+
+#define PARSE_COMMON_CONFIGURATIONS() \
+ PARSE_IGNOREDNUMBER( "bt_minkey", db->set_bt_minkey); \
+ PARSE_IGNOREDFLAG( "chksum", DB_CHKSUM); \
+ PARSE_IGNOREDNUMBER( "db_lorder", db->set_lorder); \
+ PARSE_IGNOREDNUMBER( "db_pagesize", db->set_pagesize); \
+ PARSE_UNSUPPORTEDNUMBER("extentsize", db->set_q_extentsize); \
+ PARSE_UNSUPPORTEDNUMBER("h_ffactor", db->set_h_ffactor); \
+ PARSE_UNSUPPORTEDNUMBER("h_nelem", db->set_h_nelem); \
+ PARSE_UNSUPPORTEDNUMBER("re_len", db->set_re_len); \
+ PARSE_UNSUPPORTEDCHAR( "re_pad", db->set_re_pad); \
+ PARSE_UNSUPPORTEDFLAG( "recnum", DB_RECNUM); \
+ PARSE_UNSUPPORTEDFLAG( "renumber", DB_RENUMBER);
+
+
+
+int read_header()
+{
+ static u_int64_t datasize = 1 << 10;
+ u_int64_t idx = 0;
+ char* field;
+ char* value;
+ int ch;
+ int32_t num;
+ int retval;
+ int r;
+
+ assert(g.header);
+
+ if (g.read_header.data == NULL && (g.read_header.data = (char*)toku_malloc(datasize * sizeof(char))) == NULL) {
+ PRINT_ERROR(errno, "read_header: malloc");
+ goto error;
+ }
+ while (!g.eof) {
+ if (caught_any_signals()) goto success;
+ g.linenumber++;
+ idx = 0;
+ /* Read a line. */
+ while (true) {
+ if ((ch = getchar()) == EOF) {
+ g.eof = true;
+ if (ferror(stdin)) goto formaterror;
+ break;
+ }
+ if (ch == '\n') break;
+
+ g.read_header.data[idx] = (char)ch;
+ idx++;
+
+ /* Ensure room exists for next character/null terminator. */
+ if (idx == datasize && doublechararray(&g.read_header.data, &datasize)) goto error;
+ }
+ if (idx == 0 && g.eof) goto success;
+ g.read_header.data[idx] = '\0';
+
+ field = g.read_header.data;
+ if ((value = strchr(g.read_header.data, '=')) == NULL) goto formaterror;
+ value[0] = '\0';
+ value++;
+
+ if (field[0] == '\0' || value[0] == '\0') goto formaterror;
+
+ if (!strcmp(field, "HEADER")) break;
+ if (!strcmp(field, "VERSION")) {
+ if (strtoint32(value, &g.version, 1, INT32_MAX, 10)) goto error;
+ if (g.version != 3) {
+ PRINT_ERRORX("line %" PRIu64 ": VERSION %d is unsupported", g.linenumber, g.version);
+ goto error;
+ }
+ continue;
+ }
+ if (!strcmp(field, "format")) {
+ if (!strcmp(value, "bytevalue")) {
+ g.plaintext = false;
+ continue;
+ }
+ if (!strcmp(value, "print")) {
+ g.plaintext = true;
+ continue;
+ }
+ goto formaterror;
+ }
+ if (!strcmp(field, "type")) {
+ if (!strcmp(value, "btree")) {
+ g.dbtype = DB_BTREE;
+ continue;
+ }
+ if (!strcmp(value, "hash") || strcmp(value, "recno") || strcmp(value, "queue")) {
+ PRINT_ERRORX("db type %s not supported.\n", value);
+ goto error;
+ }
+ PRINT_ERRORX("line %" PRIu64 ": unknown type %s", g.linenumber, value);
+ goto error;
+ }
+ if (!strcmp(field, "database") || !strcmp(field, "subdatabase")) {
+ if (g.subdatabase != NULL) {
+ toku_free(g.subdatabase);
+ g.subdatabase = NULL;
+ }
+ if ((retval = printabletocstring(value, &g.subdatabase))) {
+ PRINT_ERROR(retval, "error reading db name");
+ goto error;
+ }
+ continue;
+ }
+ if (!strcmp(field, "keys")) {
+ int32_t temp;
+ if (strtoint32(value, &temp, 0, 1, 10)) {
+ PRINT_ERROR(0,
+ "%s: boolean name=value pairs require a value of 0 or 1",
+ field);
+ goto error;
+ }
+ g.keys = (bool)temp;
+ if (!g.keys) {
+ PRINT_ERRORX("keys=0 not supported");
+ goto error;
+ }
+ continue;
+ }
+ PARSE_COMMON_CONFIGURATIONS();
+
+ PRINT_ERRORX("unknown input-file header configuration keyword \"%s\"", field);
+ goto error;
+ }
+success:
+ r = 0;
+
+ if (false) {
+formaterror:
+ r = EXIT_FAILURE;
+ PRINT_ERRORX("line %" PRIu64 ": unexpected format", g.linenumber);
+ }
+ if (false) {
+error:
+ r = EXIT_FAILURE;
+ }
+ return r;
+}
+
+int apply_commandline_options()
+{
+ int r;
+ unsigned idx;
+ char* field;
+ char* value = NULL;
+ int32_t num;
+ int retval;
+
+ for (idx = 0; g.config_options[idx]; idx++) {
+ if (value) {
+ /* Restore the field=value format. */
+ value[-1] = '=';
+ value = NULL;
+ }
+ field = g.config_options[idx];
+
+ if ((value = strchr(field, '=')) == NULL) {
+ PRINT_ERRORX("command-line configuration uses name=value format");
+ goto error;
+ }
+ value[0] = '\0';
+ value++;
+
+ if (field[0] == '\0' || value[0] == '\0') {
+ PRINT_ERRORX("command-line configuration uses name=value format");
+ goto error;
+ }
+
+ if (!strcmp(field, "database") || !strcmp(field, "subdatabase")) {
+ if (g.subdatabase != NULL) {
+ toku_free(g.subdatabase);
+ g.subdatabase = NULL;
+ }
+ if ((retval = printabletocstring(value, &g.subdatabase))) {
+ PRINT_ERROR(retval, "error reading db name");
+ goto error;
+ }
+ continue;
+ }
+ if (!strcmp(field, "keys")) {
+ int32_t temp;
+ if (strtoint32(value, &temp, 0, 1, 10)) {
+ PRINT_ERROR(0,
+ "%s: boolean name=value pairs require a value of 0 or 1",
+ field);
+ goto error;
+ }
+ g.keys = (bool)temp;
+ if (!g.keys) {
+ PRINT_ERRORX("keys=0 not supported");
+ goto error;
+ }
+ continue;
+ }
+ PARSE_COMMON_CONFIGURATIONS();
+
+ PRINT_ERRORX("unknown input-file header configuration keyword \"%s\"", field);
+ goto error;
+ }
+ if (value) {
+ /* Restore the field=value format. */
+ value[-1] = '=';
+ value = NULL;
+ }
+ r = 0;
+
+error:
+ return r;
+}
+
+int open_database()
+{
+ DB* db = g.db;
+ int retval;
+
+ int open_flags = 0;
+ //TODO: Transaction auto commit stuff
+ //if (TXN_ON(dbenv)) SET_BITS(open_flags, DB_AUTO_COMMIT);
+
+ //Try to see if it exists first.
+ retval = db->open(db, NULL, g.database, g.subdatabase, g.dbtype, open_flags, 0666);
+ if (retval == ENOENT) {
+ //Does not exist and we did not specify a type.
+ //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented.
+ /*
+ if (g.dbtype == DB_UNKNOWN) {
+ PRINT_ERRORX("no database type specified");
+ goto error;
+ }*/
+ SET_BITS(open_flags, DB_CREATE);
+ //Try creating it.
+ retval = db->open(db, NULL, g.database, g.subdatabase, g.dbtype, open_flags, 0666);
+ }
+ if (retval != 0) {
+ PRINT_ERROR(retval, "DB->open: %s", g.database);
+ goto error;
+ }
+ //TODO: Uncomment when DB_UNKNOWN + db->get_type are implemented.
+ /*
+ if ((retval = db->get_type(db, &opened_type)) != 0) {
+ PRINT_ERROR(retval, "DB->get_type");
+ goto error;
+ }
+ if (opened_type != DB_BTREE) {
+ PRINT_ERRORX("Unsupported db type %d\n", opened_type);
+ goto error;
+ }
+ if (g.dbtype != DB_UNKNOWN && opened_type != g.dbtype) {
+ PRINT_ERRORX("DBTYPE %d does not match opened DBTYPE %d.\n", g.dbtype, opened_type);
+ goto error;
+ }*/
+ return EXIT_SUCCESS;
+error:
+ fprintf(stderr, "Quitting out due to errors.\n");
+ return EXIT_FAILURE;
+}
+
+int doublechararray(char** pmem, u_int64_t* size)
+{
+ assert(pmem);
+ assert(size);
+ assert(IS_POWER_OF_2(*size));
+
+ *size <<= 1;
+ if (*size == 0) {
+ /* Overflowed u_int64_t. */
+ PRINT_ERRORX("Line %" PRIu64 ": Line too long.\n", g.linenumber);
+ goto error;
+ }
+ if ((*pmem = (char*)toku_realloc(*pmem, *size)) == NULL) {
+ PRINT_ERROR(errno, "doublechararray: realloc");
+ goto error;
+ }
+ return EXIT_SUCCESS;
+
+error:
+ return EXIT_FAILURE;
+}
+
+static int get_dbt(DBT* pdbt)
+{
+ /* Need to store a key and value. */
+ static u_int64_t datasize[2] = {1 << 10, 1 << 10};
+ static int which = 0;
+ char* datum;
+ u_int64_t idx = 0;
+ int highch;
+ int lowch;
+
+ /* *pdbt should have been memset to 0 before being called. */
+ which = 1 - which;
+ if (g.get_dbt.data[which] == NULL &&
+ (g.get_dbt.data[which] = (char*)toku_malloc(datasize[which] * sizeof(char))) == NULL) {
+ PRINT_ERROR(errno, "get_dbt: malloc");
+ goto error;
+ }
+
+ datum = g.get_dbt.data[which];
+
+ if (g.plaintext) {
+ int firstch;
+ int nextch = EOF;
+
+ for (firstch = getchar(); firstch != EOF; firstch = getchar()) {
+ switch (firstch) {
+ case ('\n'): {
+ /* Done reading this key/value. */
+ nextch = EOF;
+ break;
+ }
+ case ('\\'): {
+ /* Escaped \ or two hex digits. */
+ highch = getchar();
+ if (highch == '\\') {
+ nextch = '\\';
+ break;
+ }
+ else if (highch == EOF) {
+ g.eof = true;
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected end of file (2 hex digits per byte).\n", g.linenumber);
+ goto error;
+ }
+ else if (!isxdigit(highch)) {
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected '%c' (non-hex) input.\n", g.linenumber, highch);
+ goto error;
+ }
+
+ lowch = getchar();
+ if (lowch == EOF) {
+ g.eof = true;
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected end of file (2 hex digits per byte).\n", g.linenumber);
+ goto error;
+ }
+ else if (!isxdigit(lowch)) {
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected '%c' (non-hex) input.\n", g.linenumber, lowch);
+ goto error;
+ }
+
+ nextch = (hextoint(highch) << 4) | hextoint(lowch);
+ break;
+ }
+ default: {
+ if (isprint(firstch)) {
+ nextch = firstch;
+ break;
+ }
+ PRINT_ERRORX("Line %" PRIu64 ": Nonprintable character found.", g.linenumber);
+ goto error;
+ }
+ }
+ if (nextch == EOF) {
+ break;
+ }
+ if (idx == datasize[which]) {
+ /* Overflow, double the memory. */
+ if (doublechararray(&g.get_dbt.data[which], &datasize[which])) goto error;
+ datum = g.get_dbt.data[which];
+ }
+ datum[idx] = (char)nextch;
+ idx++;
+ }
+ if (firstch == EOF) g.eof = true;
+ }
+ else {
+ for (highch = getchar(); highch != EOF; highch = getchar()) {
+ if (highch == '\n') {
+ /* Done reading this key/value. */
+ break;
+ }
+
+ lowch = getchar();
+ if (lowch == EOF) {
+ g.eof = true;
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected end of file (2 hex digits per byte).\n", g.linenumber);
+ goto error;
+ }
+ if (!isxdigit(highch)) {
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected '%c' (non-hex) input.\n", g.linenumber, highch);
+ goto error;
+ }
+ if (!isxdigit(lowch)) {
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected '%c' (non-hex) input.\n", g.linenumber, lowch);
+ goto error;
+ }
+ if (idx == datasize[which]) {
+ /* Overflow, double the memory. */
+ if (doublechararray(&g.get_dbt.data[which], &datasize[which])) goto error;
+ datum = g.get_dbt.data[which];
+ }
+ datum[idx] = (char)((hextoint(highch) << 4) | hextoint(lowch));
+ idx++;
+ }
+ if (highch == EOF) g.eof = true;
+ }
+
+ /* Done reading. */
+ pdbt->size = idx;
+ pdbt->data = (void*)datum;
+ return EXIT_SUCCESS;
+error:
+ return EXIT_FAILURE;
+}
+
+static int insert_pair(DBT* key, DBT* data)
+{
+ DB* db = g.db;
+
+ int retval = db->put(db, NULL, key, data, g.overwritekeys ? 0 : DB_NOOVERWRITE);
+ if (retval != 0) {
+ //TODO: Check for transaction failures/etc.. retry if necessary.
+ PRINT_ERROR(retval, "DB->put");
+ if (!(retval == DB_KEYEXIST && g.overwritekeys)) goto error;
+ }
+ return EXIT_SUCCESS;
+error:
+ return EXIT_FAILURE;
+}
+
+int read_keys()
+{
+ DBT key;
+ DBT data;
+ int spacech;
+
+ char footer[sizeof("ATA=END\n")];
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+
+ //TODO: Start transaction/end transaction/abort/retry/etc
+
+ if (!g.leadingspace) {
+ assert(g.plaintext);
+ while (!g.eof) {
+ if (caught_any_signals()) goto success;
+ g.linenumber++;
+ if (get_dbt(&key) != 0) goto error;
+ if (g.eof) {
+ if (key.size == 0) {
+ //Last entry had no newline. Done.
+ break;
+ }
+ PRINT_ERRORX("Line %" PRIu64 ": Key exists but value missing.", g.linenumber);
+ goto error;
+ }
+ g.linenumber++;
+ if (get_dbt(&data) != 0) goto error;
+ if (insert_pair(&key, &data) != 0) goto error;
+ }
+ }
+ else while (!g.eof) {
+ if (caught_any_signals()) goto success;
+ g.linenumber++;
+ spacech = getchar();
+ switch (spacech) {
+ case (EOF): {
+ /* Done. */
+ g.eof = true;
+ goto success;
+ }
+ case (' '): {
+ /* Time to read a key. */
+ if (get_dbt(&key) != 0) goto error;
+ break;
+ }
+ case ('D'): {
+ if (fgets(footer, sizeof("ATA=END\n"), stdin) != NULL &&
+ (!strcmp(footer, "ATA=END") || !strcmp(footer, "ATA=END\n")))
+ {
+ goto success;
+ }
+ goto unexpectedinput;
+ }
+ default: {
+unexpectedinput:
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected input while reading key.\n", g.linenumber);
+ goto error;
+ }
+ }
+
+ if (g.eof) {
+ PRINT_ERRORX("Line %" PRIu64 ": Key exists but value missing.", g.linenumber);
+ goto error;
+ }
+ g.linenumber++;
+ spacech = getchar();
+ switch (spacech) {
+ case (EOF): {
+ g.eof = true;
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected end of file while reading value.\n", g.linenumber);
+ goto error;
+ }
+ case (' '): {
+ /* Time to read a key. */
+ if (get_dbt(&data) != 0) goto error;
+ break;
+ }
+ default: {
+ PRINT_ERRORX("Line %" PRIu64 ": Unexpected input while reading value.\n", g.linenumber);
+ goto error;
+ }
+ }
+ if (insert_pair(&key, &data) != 0) goto error;
+ }
+success:
+ return EXIT_SUCCESS;
+error:
+ return EXIT_FAILURE;
+}
+
+int close_database()
+{
+ DB* db = g.db;
+ int retval;
+
+ assert(db);
+ if ((retval = db->close(db, 0)) != 0) {
+ PRINT_ERROR(retval, "DB->close");
+ goto error;
+ }
+ return EXIT_SUCCESS;
+error:
+ return EXIT_FAILURE;
+}