Logo Search packages:      
Sourcecode: db version File versions

db_load.c

/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996,2007 Oracle.  All rights reserved.
 *
 * $Id: db_load.c,v 12.23 2007/05/17 17:17:42 bostic Exp $
 */

#include "db_config.h"

#include "db_int.h"
#include "dbinc/db_page.h"
#include "dbinc/db_am.h"

#ifndef lint
static const char copyright[] =
    "Copyright (c) 1996,2007 Oracle.  All rights reserved.\n";
#endif

typedef struct {              /* XXX: Globals. */
      const char *progname;         /* Program name. */
      char  *hdrbuf;          /* Input file header. */
      u_long      lineno;                 /* Input file line number. */
      u_long      origline;         /* Original file line number. */
      int   endodata;         /* Reached the end of a database. */
      int   endofile;         /* Reached the end of the input. */
      int   version;          /* Input version. */
      char  *home;                  /* Env home. */
      char  *passwd;          /* Env passwd. */
      int   private;          /* Private env. */
      u_int32_t cache;        /* Env cache size. */
} LDG;

int   db_load_badend __P((DB_ENV *));
void  db_load_badnum __P((DB_ENV *));
int   db_load_configure __P((DB_ENV *, DB *, char **, char **, int *));
int   db_load_convprintable __P((DB_ENV *, char *, char **));
int   db_load_db_init __P((DB_ENV *, char *, u_int32_t, int *));
int   db_load_dbt_rdump __P((DB_ENV *, DBT *));
int   db_load_dbt_rprint __P((DB_ENV *, DBT *));
int   db_load_dbt_rrecno __P((DB_ENV *, DBT *, int));
int   db_load_dbt_to_recno __P((DB_ENV *, DBT *, db_recno_t *));
int   db_load_env_create __P((DB_ENV **, LDG *));
int   db_load_load __P((DB_ENV *, char *, DBTYPE, char **, u_int, LDG *, int *));
int   db_load_main __P((int, char *[]));
int   db_load_rheader __P((DB_ENV *, DB *, DBTYPE *, char **, int *, int *));
int   db_load_usage __P((void));
int   db_load_version_check __P((void));

const char *progname;

#define     G(f)  ((LDG *)dbenv->app_private)->f

                              /* Flags to the load function. */
#define     LDF_NOHEADER      0x01        /* No dump header. */
#define     LDF_NOOVERWRITE   0x02        /* Don't overwrite existing rows. */
#define     LDF_PASSWORD      0x04        /* Encrypt created databases. */

int
db_load(args)
      char *args;
{
      int argc;
      char **argv;

      __db_util_arg("db_load", args, &argc, &argv);
      return (db_load_main(argc, argv) ? EXIT_FAILURE : EXIT_SUCCESS);
}

#include <stdio.h>
#define     ERROR_RETURN      ERROR

int
db_load_main(argc, argv)
      int argc;
      char *argv[];
{
      enum { NOTSET, FILEID_RESET, LSN_RESET, STANDARD_LOAD } mode;
      extern char *optarg;
      extern int optind, __db_getopt_reset;
      DBTYPE dbtype;
      DB_ENV      *dbenv;
      LDG ldg;
      u_int ldf;
      int ch, existed, exitval, ret;
      char **clist, **clp;

      if ((progname = __db_rpath(argv[0])) == NULL)
            progname = argv[0];
      else
            ++progname;

      if ((ret = db_load_version_check()) != 0)
            return (ret);

      ldg.progname = progname;
      ldg.lineno = 0;
      ldg.endodata = ldg.endofile = 0;
      ldg.version = 1;
      ldg.cache = MEGABYTE;
      ldg.hdrbuf = NULL;
      ldg.home = NULL;
      ldg.passwd = NULL;

      mode = NOTSET;
      ldf = 0;
      exitval = existed = 0;
      dbtype = DB_UNKNOWN;

      /* Allocate enough room for configuration arguments. */
      if ((clp = clist =
          (char **)calloc((size_t)argc + 1, sizeof(char *))) == NULL) {
            fprintf(stderr, "%s: %s\n", ldg.progname, strerror(ENOMEM));
            return (EXIT_FAILURE);
      }

      /*
       * There are two modes for db_load: -r and everything else.  The -r
       * option zeroes out the database LSN's or resets the file ID, it
       * doesn't really "load" a new database.  The functionality is in
       * db_load because we don't have a better place to put it, and we
       * don't want to create a new utility for just that functionality.
       */
      __db_getopt_reset = 1;
      while ((ch = getopt(argc, argv, "c:f:h:nP:r:Tt:V")) != EOF)
            switch (ch) {
            case 'c':
                  if (mode != NOTSET && mode != STANDARD_LOAD)
                        return (db_load_usage());
                  mode = STANDARD_LOAD;

                  *clp++ = optarg;
                  break;
            case 'f':
                  if (mode != NOTSET && mode != STANDARD_LOAD)
                        return (db_load_usage());
                  mode = STANDARD_LOAD;

                  if (freopen(optarg, "r", stdin) == NULL) {
                        fprintf(stderr, "%s: %s: reopen: %s\n",
                            ldg.progname, optarg, strerror(errno));
                        return (EXIT_FAILURE);
                  }
                  break;
            case 'h':
                  ldg.home = optarg;
                  break;
            case 'n':
                  if (mode != NOTSET && mode != STANDARD_LOAD)
                        return (db_load_usage());
                  mode = STANDARD_LOAD;

                  ldf |= LDF_NOOVERWRITE;
                  break;
            case 'P':
                  ldg.passwd = strdup(optarg);
                  memset(optarg, 0, strlen(optarg));
                  if (ldg.passwd == NULL) {
                        fprintf(stderr, "%s: strdup: %s\n",
                            ldg.progname, strerror(errno));
                        return (EXIT_FAILURE);
                  }
                  ldf |= LDF_PASSWORD;
                  break;
            case 'r':
                  if (mode == STANDARD_LOAD)
                        return (db_load_usage());
                  if (strcmp(optarg, "lsn") == 0)
                        mode = LSN_RESET;
                  else if (strcmp(optarg, "fileid") == 0)
                        mode = FILEID_RESET;
                  else
                        return (db_load_usage());
                  break;
            case 'T':
                  if (mode != NOTSET && mode != STANDARD_LOAD)
                        return (db_load_usage());
                  mode = STANDARD_LOAD;

                  ldf |= LDF_NOHEADER;
                  break;
            case 't':
                  if (mode != NOTSET && mode != STANDARD_LOAD)
                        return (db_load_usage());
                  mode = STANDARD_LOAD;

                  if (strcmp(optarg, "btree") == 0) {
                        dbtype = DB_BTREE;
                        break;
                  }
                  if (strcmp(optarg, "hash") == 0) {
                        dbtype = DB_HASH;
                        break;
                  }
                  if (strcmp(optarg, "recno") == 0) {
                        dbtype = DB_RECNO;
                        break;
                  }
                  if (strcmp(optarg, "queue") == 0) {
                        dbtype = DB_QUEUE;
                        break;
                  }
                  return (db_load_usage());
            case 'V':
                  printf("%s\n", db_version(NULL, NULL, NULL));
                  return (EXIT_SUCCESS);
            case '?':
            default:
                  return (db_load_usage());
            }
      argc -= optind;
      argv += optind;

      if (argc != 1)
            return (db_load_usage());

      /* Handle possible interruptions. */
      __db_util_siginit();

      /*
       * Create an environment object initialized for error reporting, and
       * then open it.
       */
      if (db_load_env_create(&dbenv, &ldg) != 0)
            goto shutdown;

      /* If we're resetting the LSNs, that's an entirely separate path. */
      switch (mode) {
      case FILEID_RESET:
            exitval = dbenv->fileid_reset(
                dbenv, argv[0], ldf & LDF_PASSWORD ? DB_ENCRYPT : 0);
            break;
      case LSN_RESET:
            exitval = dbenv->lsn_reset(
                dbenv, argv[0], ldf & LDF_PASSWORD ? DB_ENCRYPT : 0);
            break;
      case NOTSET:
      case STANDARD_LOAD:
            while (!ldg.endofile)
                  if (db_load_load(dbenv, argv[0], dbtype, clist, ldf,
                      &ldg, &existed) != 0)
                        goto shutdown;
            break;
      }

      if (0) {
shutdown:   exitval = 1;
      }
      if ((ret = dbenv->close(dbenv, 0)) != 0) {
            exitval = 1;
            fprintf(stderr,
                "%s: dbenv->close: %s\n", ldg.progname, db_strerror(ret));
      }

      /* Resend any caught signal. */
      __db_util_sigresend();
      free(clist);
      if (ldg.passwd != NULL)
            free(ldg.passwd);

      /*
       * Return 0 on success, 1 if keys existed already, and 2 on failure.
       *
       * Technically, this is wrong, because exit of anything other than
       * 0 is implementation-defined by the ANSI C standard.  I don't see
       * any good solutions that don't involve API changes.
       */
      return (exitval == 0 ? (existed == 0 ? 0 : 1) : 2);
}

/*
 * load --
 *    Load a database.
 */
int
db_load_load(dbenv, name, argtype, clist, flags, ldg, existedp)
      DB_ENV *dbenv;
      char *name, **clist;
      DBTYPE argtype;
      u_int flags;
      LDG *ldg;
      int *existedp;
{
      DB *dbp;
      DBT key, rkey, data, *readp, *writep;
      DBTYPE dbtype;
      DB_TXN *ctxn, *txn;
      db_recno_t recno, datarecno;
      u_int32_t put_flags;
      int ascii_recno, checkprint, hexkeys, keyflag, keys, resize, ret, rval;
      char *subdb;

      put_flags = LF_ISSET(LDF_NOOVERWRITE) ? DB_NOOVERWRITE : 0;
      G(endodata) = 0;

      subdb = NULL;
      ctxn = txn = NULL;
      memset(&key, 0, sizeof(DBT));
      memset(&data, 0, sizeof(DBT));
      memset(&rkey, 0, sizeof(DBT));

retry_db:
      dbtype = DB_UNKNOWN;
      keys = -1;
      hexkeys = -1;
      keyflag = -1;

      /* Create the DB object. */
      if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
            dbenv->err(dbenv, ret, "db_create");
            goto err;
      }

      /* Read the header -- if there's no header, we expect flat text. */
      if (LF_ISSET(LDF_NOHEADER)) {
            checkprint = 1;
            dbtype = argtype;
      } else {
            if (db_load_rheader(dbenv,
                dbp, &dbtype, &subdb, &checkprint, &keys) != 0)
                  goto err;
            if (G(endofile))
                  goto done;
      }

      /*
       * Apply command-line configuration changes.  (We apply command-line
       * configuration changes to all databases that are loaded, e.g., all
       * subdatabases.)
       */
      if (db_load_configure(dbenv, dbp, clist, &subdb, &keyflag))
            goto err;

      if (keys != 1) {
            if (keyflag == 1) {
                  dbp->err(dbp, EINVAL, "No keys specified in file");
                  goto err;
            }
      }
      else if (keyflag == 0) {
            dbp->err(dbp, EINVAL, "Keys specified in file");
            goto err;
      }
      else
            keyflag = 1;

      if (dbtype == DB_BTREE || dbtype == DB_HASH) {
            if (keyflag == 0)
                  dbp->err(dbp,
                      EINVAL, "Btree and Hash must specify keys");
            else
                  keyflag = 1;
      }

      if (argtype != DB_UNKNOWN) {

            if (dbtype == DB_RECNO || dbtype == DB_QUEUE)
                  if (keyflag != 1 && argtype != DB_RECNO &&
                      argtype != DB_QUEUE) {
                        dbenv->errx(dbenv,
                     "improper database type conversion specified");
                        goto err;
                  }
            dbtype = argtype;
      }

      if (dbtype == DB_UNKNOWN) {
            dbenv->errx(dbenv, "no database type specified");
            goto err;
      }

      if (keyflag == -1)
            keyflag = 0;

      /*
       * Recno keys have only been printed in hexadecimal starting
       * with db_dump format version 3 (DB 3.2).
       *
       * !!!
       * Note that version is set in db_load_rheader(), which must be called before
       * this assignment.
       */
      hexkeys = (G(version) >= 3 && keyflag == 1 && checkprint == 0);

      if (keyflag == 1 && (dbtype == DB_RECNO || dbtype == DB_QUEUE))
            ascii_recno = 1;
      else
            ascii_recno = 0;

      /* If configured with a password, encrypt databases we create. */
      if (LF_ISSET(LDF_PASSWORD) &&
          (ret = dbp->set_flags(dbp, DB_ENCRYPT)) != 0) {
            dbp->err(dbp, ret, "DB->set_flags: DB_ENCRYPT");
            goto err;
      }

#if 0
      Set application-specific btree comparison or hash functions here.
      For example:

      if ((ret = dbp->set_bt_compare(dbp, local_comparison_func)) != 0) {
            dbp->err(dbp, ret, "DB->set_bt_compare");
            goto err;
      }
      if ((ret = dbp->set_h_hash(dbp, local_hash_func)) != 0) {
            dbp->err(dbp, ret, "DB->set_h_hash");
            goto err;
      }
#endif

      /* Open the DB file. */
      if ((ret = dbp->open(dbp, NULL, name, subdb, dbtype,
          DB_CREATE | (TXN_ON(dbenv) ? DB_AUTO_COMMIT : 0),
          __db_omode("rw-rw-rw-"))) != 0) {
            dbp->err(dbp, ret, "DB->open: %s", name);
            goto err;
      }
      if (ldg->private != 0) {
            if ((ret = __db_util_cache(dbp, &ldg->cache, &resize)) != 0)
                  goto err;
            if (resize) {
                  if ((ret = dbp->close(dbp, 0)) != 0)
                        goto err;
                  dbp = NULL;
                  if ((ret = dbenv->close(dbenv, 0)) != 0)
                        goto err;
                  if ((ret = db_load_env_create(&dbenv, ldg)) != 0)
                        goto err;
                  goto retry_db;
            }
      }

      /* Initialize the key/data pair. */
      readp = writep = &key;
      if (dbtype == DB_RECNO || dbtype == DB_QUEUE) {
            key.size = sizeof(recno);
            if (keyflag) {
                  key.data = &datarecno;
                  if (checkprint) {
                        readp = &rkey;
                        goto key_data;
                  }
            } else
                  key.data = &recno;
      } else
key_data:   if ((readp->data = malloc(readp->ulen = 1024)) == NULL) {
                  dbenv->err(dbenv, ENOMEM, NULL);
                  goto err;
            }
      if ((data.data = malloc(data.ulen = 1024)) == NULL) {
            dbenv->err(dbenv, ENOMEM, NULL);
            goto err;
      }

      if (TXN_ON(dbenv) &&
          (ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0)
            goto err;

      /* Get each key/data pair and add them to the database. */
      for (recno = 1; !__db_util_interrupted(); ++recno) {
            if (!keyflag) {
                  if (checkprint) {
                        if (db_load_dbt_rprint(dbenv, &data))
                              goto err;
                  } else {
                        if (db_load_dbt_rdump(dbenv, &data))
                              goto err;
                  }
            } else {
                  if (checkprint) {
                        if (db_load_dbt_rprint(dbenv, readp))
                              goto err;
                        if (ascii_recno &&
                            db_load_dbt_to_recno(dbenv, readp, &datarecno) != 0)
                              goto err;

                        if (!G(endodata) && db_load_dbt_rprint(dbenv, &data))
                              goto odd_count;
                  } else {
                        if (ascii_recno) {
                              if (db_load_dbt_rrecno(dbenv, readp, hexkeys))
                                    goto err;
                        } else
                              if (db_load_dbt_rdump(dbenv, readp))
                                    goto err;

                        if (!G(endodata) && db_load_dbt_rdump(dbenv, &data)) {
odd_count:                    dbenv->errx(dbenv,
                                  "odd number of key/data pairs");
                              goto err;
                        }
                  }
            }
            if (G(endodata))
                  break;
retry:            if (txn != NULL)
                  if ((ret = dbenv->txn_begin(dbenv, txn, &ctxn, 0)) != 0)
                        goto err;
            switch (ret = dbp->put(dbp, ctxn, writep, &data, put_flags)) {
            case 0:
                  if (ctxn != NULL) {
                        if ((ret =
                            ctxn->commit(ctxn, DB_TXN_NOSYNC)) != 0)
                              goto err;
                        ctxn = NULL;
                  }
                  break;
            case DB_KEYEXIST:
                  *existedp = 1;
                  dbenv->errx(dbenv,
                      "%s: line %d: key already exists, not loaded:",
                      name,
                      !keyflag ? recno : recno * 2 - 1);

                  (void)dbenv->prdbt(&key,
                      checkprint, 0, stderr, __db_pr_callback, 0);
                  break;
            case DB_LOCK_DEADLOCK:
                  /* If we have a child txn, retry--else it's fatal. */
                  if (ctxn != NULL) {
                        if ((ret = ctxn->abort(ctxn)) != 0)
                              goto err;
                        ctxn = NULL;
                        goto retry;
                  }
                  /* FALLTHROUGH */
            default:
                  dbenv->err(dbenv, ret, NULL);
                  if (ctxn != NULL) {
                        (void)ctxn->abort(ctxn);
                        ctxn = NULL;
                  }
                  goto err;
            }
            if (ctxn != NULL) {
                  if ((ret = ctxn->abort(ctxn)) != 0)
                        goto err;
                  ctxn = NULL;
            }
      }
done: rval = 0;
      DB_ASSERT(dbenv, ctxn == NULL);
      if (txn != NULL && (ret = txn->commit(txn, 0)) != 0) {
            txn = NULL;
            goto err;
      }

      if (0) {
err:        rval = 1;
            DB_ASSERT(dbenv, ctxn == NULL);
            if (txn != NULL)
                  (void)txn->abort(txn);
      }

      /* Close the database. */
      if (dbp != NULL && (ret = dbp->close(dbp, 0)) != 0) {
            dbenv->err(dbenv, ret, "DB->close");
            rval = 1;
      }

      if (G(hdrbuf) != NULL)
            free(G(hdrbuf));
      G(hdrbuf) = NULL;
      /* Free allocated memory. */
      if (subdb != NULL)
            free(subdb);
      if (dbtype != DB_RECNO && dbtype != DB_QUEUE && key.data != NULL)
            free(key.data);
      if (rkey.data != NULL)
            free(rkey.data);
      free(data.data);

      return (rval);
}

/*
 * env_create --
 *    Create the environment and initialize it for error reporting.
 */
int
db_load_env_create(dbenvp, ldg)
      DB_ENV **dbenvp;
      LDG *ldg;
{
      DB_ENV *dbenv;
      int ret;

      if ((ret = db_env_create(dbenvp, 0)) != 0) {
            fprintf(stderr,
                "%s: db_env_create: %s\n", ldg->progname, db_strerror(ret));
            return (ret);
      }
      dbenv = *dbenvp;
      dbenv->set_errfile(dbenv, stderr);
      dbenv->set_errpfx(dbenv, ldg->progname);
      if (ldg->passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
          ldg->passwd, DB_ENCRYPT_AES)) != 0) {
            dbenv->err(dbenv, ret, "set_passwd");
            return (ret);
      }
      if ((ret = db_load_db_init(dbenv, ldg->home, ldg->cache, &ldg->private)) != 0)
            return (ret);
      dbenv->app_private = ldg;

      return (0);
}

/*
 * db_init --
 *    Initialize the environment.
 */
int
db_load_db_init(dbenv, home, cache, is_private)
      DB_ENV *dbenv;
      char *home;
      u_int32_t cache;
      int *is_private;
{
      u_int32_t flags;
      int ret;

      *is_private = 0;
      /* We may be loading into a live environment.  Try and join. */
      flags = DB_USE_ENVIRON |
          DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN;
      if ((ret = dbenv->open(dbenv, home, flags, 0)) == 0)
            return (0);
      if (ret == DB_VERSION_MISMATCH)
            goto err;

      /*
       * We're trying to load a database.
       *
       * An environment is required because we may be trying to look at
       * databases in directories other than the current one.  We could
       * avoid using an environment iff the -h option wasn't specified,
       * but that seems like more work than it's worth.
       *
       * No environment exists (or, at least no environment that includes
       * an mpool region exists).  Create one, but make it private so that
       * no files are actually created.
       */
      LF_CLR(DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN);
      LF_SET(DB_CREATE | DB_PRIVATE);
      *is_private = 1;
      if ((ret = dbenv->set_cachesize(dbenv, 0, cache, 1)) != 0) {
            dbenv->err(dbenv, ret, "set_cachesize");
            return (1);
      }
      if ((ret = dbenv->open(dbenv, home, flags, 0)) == 0)
            return (0);

      /* An environment is required. */
err:  dbenv->err(dbenv, ret, "DB_ENV->open");
      return (1);
}

#define     FLAG(name, value, keyword, flag)                      \
      if (strcmp(name, keyword) == 0) {                     \
            switch (*value) {                         \
            case '1':                                 \
                  if ((ret = dbp->set_flags(dbp, flag)) != 0) {   \
                        dbp->err(dbp, ret, "%s: set_flags: %s",   \
                            G(progname), name);             \
                        goto err;                     \
                  }                                   \
                  break;                                    \
            case '0':                                 \
                  break;                                    \
            default:                                  \
                  db_load_badnum(dbenv);                          \
                  goto err;                           \
            }                                         \
            continue;                                 \
      }
#define     NUMBER(name, value, keyword, func, t)                       \
      if (strcmp(name, keyword) == 0) {                     \
            if ((ret = __db_getlong(dbenv,                        \
                NULL, value, 0, LONG_MAX, &val)) != 0 ||          \
                (ret = dbp->func(dbp, (t)val)) != 0)        \
                  goto nameerr;                             \
            continue;                                 \
      }
#define     STRING(name, value, keyword, func)                    \
      if (strcmp(name, keyword) == 0) {                     \
            if ((ret = dbp->func(dbp, value[0])) != 0)            \
                  goto nameerr;                             \
            continue;                                 \
      }

/*
 * The code to check a command-line or input header argument against a list
 * of configuration options.  It's #defined because it's used in two places
 * and the two places have gotten out of sync more than once.
 */
#define     CONFIGURATION_LIST_COMPARE                            \
      NUMBER(name, value, "bt_minkey", set_bt_minkey, u_int32_t); \
        FLAG(name, value, "chksum", DB_CHKSUM);             \
      NUMBER(name, value, "db_lorder", set_lorder, int);          \
      NUMBER(name, value, "db_pagesize", set_pagesize, u_int32_t);      \
        FLAG(name, value, "duplicates", DB_DUP);                  \
        FLAG(name, value, "dupsort", DB_DUPSORT);                 \
      NUMBER(name, value, "extentsize", set_q_extentsize, u_int32_t);   \
      NUMBER(name, value, "h_ffactor", set_h_ffactor, u_int32_t); \
      NUMBER(name, value, "h_nelem", set_h_nelem, u_int32_t);           \
      NUMBER(name, value, "re_len", set_re_len, u_int32_t);       \
      STRING(name, value, "re_pad", set_re_pad);                  \
        FLAG(name, value, "recnum", DB_RECNUM);             \
        FLAG(name, value, "renumber", DB_RENUMBER)

/*
 * configure --
 *    Handle command-line configuration options.
 */
int
db_load_configure(dbenv, dbp, clp, subdbp, keysp)
      DB_ENV *dbenv;
      DB *dbp;
      char **clp, **subdbp;
      int *keysp;
{
      long val;
      int ret, savech;
      char *name, *value;

      for (; (name = *clp) != NULL; *--value = savech, ++clp) {
            if ((value = strchr(name, '=')) == NULL) {
                  dbp->errx(dbp,
                "command-line configuration uses name=value format");
                  return (1);
            }
            savech = *value;
            *value++ = '\0';

            if (strcmp(name, "database") == 0 ||
                strcmp(name, "subdatabase") == 0) {
                  if (*subdbp != NULL)
                        free(*subdbp);
                  if ((*subdbp = strdup(value)) == NULL) {
                        dbp->err(dbp, ENOMEM, NULL);
                        return (1);
                  }
                  continue;
            }
            if (strcmp(name, "keys") == 0) {
                  if (strcmp(value, "1") == 0)
                        *keysp = 1;
                  else if (strcmp(value, "0") == 0)
                        *keysp = 0;
                  else {
                        db_load_badnum(dbenv);
                        return (1);
                  }
                  continue;
            }

            CONFIGURATION_LIST_COMPARE;

            dbp->errx(dbp,
                "unknown command-line configuration keyword \"%s\"", name);
            return (1);
      }
      return (0);

nameerr:
      dbp->err(dbp, ret, "%s: %s=%s", G(progname), name, value);
err:  return (1);
}

/*
 * rheader --
 *    Read the header message.
 */
int
db_load_rheader(dbenv, dbp, dbtypep, subdbp, checkprintp, keysp)
      DB_ENV *dbenv;
      DB *dbp;
      DBTYPE *dbtypep;
      char **subdbp;
      int *checkprintp, *keysp;
{
      size_t buflen, linelen, start;
      long val;
      int ch, first, hdr, ret;
      char *buf, *name, *p, *value;

      *dbtypep = DB_UNKNOWN;
      *checkprintp = 0;
      name = NULL;

      /*
       * We start with a smallish buffer;  most headers are small.
       * We may need to realloc it for a large subdatabase name.
       */
      buflen = 4096;
      if (G(hdrbuf) == NULL) {
            hdr = 0;
            if ((buf = malloc(buflen)) == NULL)
                  goto memerr;
            G(hdrbuf) = buf;
            G(origline) = G(lineno);
      } else {
            hdr = 1;
            buf = G(hdrbuf);
            G(lineno) = G(origline);
      }

      start = 0;
      for (first = 1;; first = 0) {
            ++G(lineno);

            /* Read a line, which may be of arbitrary length, into buf. */
            linelen = 0;
            buf = &G(hdrbuf)[start];
            if (hdr == 0) {
                  for (;;) {
                        if ((ch = getchar()) == EOF) {
                              if (!first || ferror(stdin))
                                    goto badfmt;
                              G(endofile) = 1;
                              break;
                        }

                        /*
                         * If the buffer is too small, double it.
                         */
                        if (linelen + start == buflen) {
                              G(hdrbuf) =
                                  realloc(G(hdrbuf), buflen *= 2);
                              if (G(hdrbuf) == NULL)
                                    goto memerr;
                              buf = &G(hdrbuf)[start];
                        }

                        if (ch == '\n')
                              break;

                        buf[linelen++] = ch;
                  }
                  if (G(endofile) == 1)
                        break;
                  buf[linelen++] = '\0';
            } else
                  linelen = strlen(buf) + 1;
            start += linelen;

            if (name != NULL) {
                  free(name);
                  name = NULL;
            }
            /* If we don't see the expected information, it's an error. */
            if ((name = strdup(buf)) == NULL)
                  goto memerr;
            if ((p = strchr(name, '=')) == NULL)
                  goto badfmt;
            *p++ = '\0';

            value = p--;

            if (name[0] == '\0')
                  goto badfmt;

            /*
             * The only values that may be zero-length are database names.
             * In the original Berkeley DB code it was possible to create
             * zero-length database names, and the db_load code was then
             * changed to allow such databases to be be dumped and loaded.
             * [#8204]
             */
            if (strcmp(name, "database") == 0 ||
                strcmp(name, "subdatabase") == 0) {
                  if ((ret = db_load_convprintable(dbenv, value, subdbp)) != 0) {
                        dbp->err(dbp, ret, "error reading db name");
                        goto err;
                  }
                  continue;
            }

            /* No other values may be zero-length. */
            if (value[0] == '\0')
                  goto badfmt;

            if (strcmp(name, "HEADER") == 0)
                  break;
            if (strcmp(name, "VERSION") == 0) {
                  /*
                   * Version 1 didn't have a "VERSION" header line.  We
                   * only support versions 1, 2, and 3 of the dump format.
                   */
                  G(version) = atoi(value);

                  if (G(version) > 3) {
                        dbp->errx(dbp,
                            "line %lu: VERSION %d is unsupported",
                            G(lineno), G(version));
                        goto err;
                  }
                  continue;
            }
            if (strcmp(name, "format") == 0) {
                  if (strcmp(value, "bytevalue") == 0) {
                        *checkprintp = 0;
                        continue;
                  }
                  if (strcmp(value, "print") == 0) {
                        *checkprintp = 1;
                        continue;
                  }
                  goto badfmt;
            }
            if (strcmp(name, "type") == 0) {
                  if (strcmp(value, "btree") == 0) {
                        *dbtypep = DB_BTREE;
                        continue;
                  }
                  if (strcmp(value, "hash") == 0) {
                        *dbtypep = DB_HASH;
                        continue;
                  }
                  if (strcmp(value, "recno") == 0) {
                        *dbtypep = DB_RECNO;
                        continue;
                  }
                  if (strcmp(value, "queue") == 0) {
                        *dbtypep = DB_QUEUE;
                        continue;
                  }
                  dbp->errx(dbp, "line %lu: unknown type", G(lineno));
                  goto err;
            }
            if (strcmp(name, "keys") == 0) {
                  if (strcmp(value, "1") == 0)
                        *keysp = 1;
                  else if (strcmp(value, "0") == 0)
                        *keysp = 0;
                  else {
                        db_load_badnum(dbenv);
                        goto err;
                  }
                  continue;
            }

            CONFIGURATION_LIST_COMPARE;

            dbp->errx(dbp,
                "unknown input-file header configuration keyword \"%s\"",
                name);
            goto err;
      }
      ret = 0;

      if (0) {
nameerr:    dbp->err(dbp, ret, "%s: %s=%s", G(progname), name, value);
            ret = 1;
      }
      if (0) {
badfmt:           dbp->errx(dbp, "line %lu: unexpected format", G(lineno));
            ret = 1;
      }
      if (0) {
memerr:           dbp->errx(dbp, "unable to allocate memory");
err:        ret = 1;
      }
      if (name != NULL)
            free(name);
      return (ret);
}

/*
 * Macro to convert a pair of hex bytes to a decimal value.
 *
 * !!!
 * Note that this macro is side-effect safe.  This was done deliberately,
 * callers depend on it.
 */
#define     DIGITIZE(store, v1, v2) {                             \
      char _v1, _v2;                                        \
      _v1 = (v1);                                     \
      _v2 = (v2);                                     \
      if ((_v1) > 'f' || (_v2) > 'f')                             \
            return (db_load_badend(dbenv));                             \
      (store) =                                       \
      ((_v1) == '0' ? 0 :                                   \
      ((_v1) == '1' ? 1 :                                   \
      ((_v1) == '2' ? 2 :                                   \
      ((_v1) == '3' ? 3 :                                   \
      ((_v1) == '4' ? 4 :                                   \
      ((_v1) == '5' ? 5 :                                   \
      ((_v1) == '6' ? 6 :                                   \
      ((_v1) == '7' ? 7 :                                   \
      ((_v1) == '8' ? 8 :                                   \
      ((_v1) == '9' ? 9 :                                   \
      ((_v1) == 'a' ? 10 :                                  \
      ((_v1) == 'b' ? 11 :                                  \
      ((_v1) == 'c' ? 12 :                                  \
      ((_v1) == 'd' ? 13 :                                  \
      ((_v1) == 'e' ? 14 : 15))))))))))))))) << 4 |               \
      ((_v2) == '0' ? 0 :                                   \
      ((_v2) == '1' ? 1 :                                   \
      ((_v2) == '2' ? 2 :                                   \
      ((_v2) == '3' ? 3 :                                   \
      ((_v2) == '4' ? 4 :                                   \
      ((_v2) == '5' ? 5 :                                   \
      ((_v2) == '6' ? 6 :                                   \
      ((_v2) == '7' ? 7 :                                   \
      ((_v2) == '8' ? 8 :                                   \
      ((_v2) == '9' ? 9 :                                   \
      ((_v2) == 'a' ? 10 :                                  \
      ((_v2) == 'b' ? 11 :                                  \
      ((_v2) == 'c' ? 12 :                                  \
      ((_v2) == 'd' ? 13 :                                  \
      ((_v2) == 'e' ? 14 : 15)))))))))))))));                     \
}

/*
 * convprintable --
 *    Convert a printable-encoded string into a newly allocated string.
 *
 * In an ideal world, this would probably share code with dbt_rprint, but
 * that's set up to read character-by-character (to avoid large memory
 * allocations that aren't likely to be a problem here), and this has fewer
 * special cases to deal with.
 *
 * Note that despite the printable encoding, the char * interface to this
 * function (which is, not coincidentally, also used for database naming)
 * means that outstr cannot contain any nuls.
 */
int
db_load_convprintable(dbenv, instr, outstrp)
      DB_ENV *dbenv;
      char *instr, **outstrp;
{
      char *outstr;

      /*
       * Just malloc a string big enough for the whole input string;
       * the output string will be smaller (or of equal length).
       *
       * Note that we may be passed a zero-length string and need to
       * be able to duplicate it.
       */
      if ((outstr = malloc(strlen(instr) + 1)) == NULL)
            return (ENOMEM);

      *outstrp = outstr;

      for ( ; *instr != '\0'; instr++)
            if (*instr == '\\') {
                  if (*++instr == '\\') {
                        *outstr++ = '\\';
                        continue;
                  }
                  DIGITIZE(*outstr++, *instr, *++instr);
            } else
                  *outstr++ = *instr;

      *outstr = '\0';

      return (0);
}

/*
 * dbt_rprint --
 *    Read a printable line into a DBT structure.
 */
int
db_load_dbt_rprint(dbenv, dbtp)
      DB_ENV *dbenv;
      DBT *dbtp;
{
      u_int32_t len;
      u_int8_t *p;
      int c1, c2, escape, first;
      char buf[32];

      ++G(lineno);

      first = 1;
      escape = 0;
      for (p = dbtp->data, len = 0; (c1 = getchar()) != '\n';) {
            if (c1 == EOF) {
                  if (len == 0) {
                        G(endofile) = G(endodata) = 1;
                        return (0);
                  }
                  return (db_load_badend(dbenv));
            }
            if (first) {
                  first = 0;
                  if (G(version) > 1) {
                        if (c1 != ' ') {
                              buf[0] = c1;
                              if (fgets(buf + 1,
                                  sizeof(buf) - 1, stdin) == NULL ||
                                  strcmp(buf, "DATA=END\n") != 0)
                                    return (db_load_badend(dbenv));
                              G(endodata) = 1;
                              return (0);
                        }
                        continue;
                  }
            }
            if (escape) {
                  if (c1 != '\\') {
                        if ((c2 = getchar()) == EOF)
                              return (db_load_badend(dbenv));
                        DIGITIZE(c1, c1, c2);
                  }
                  escape = 0;
            } else
                  if (c1 == '\\') {
                        escape = 1;
                        continue;
                  }
            if (len >= dbtp->ulen - 10) {
                  dbtp->ulen *= 2;
                  if ((dbtp->data =
                      realloc(dbtp->data, dbtp->ulen)) == NULL) {
                        dbenv->err(dbenv, ENOMEM, NULL);
                        return (1);
                  }
                  p = (u_int8_t *)dbtp->data + len;
            }
            ++len;
            *p++ = c1;
      }
      dbtp->size = len;

      return (0);
}

/*
 * dbt_rdump --
 *    Read a byte dump line into a DBT structure.
 */
int
db_load_dbt_rdump(dbenv, dbtp)
      DB_ENV *dbenv;
      DBT *dbtp;
{
      u_int32_t len;
      u_int8_t *p;
      int c1, c2, first;
      char buf[32];

      ++G(lineno);

      first = 1;
      for (p = dbtp->data, len = 0; (c1 = getchar()) != '\n';) {
            if (c1 == EOF) {
                  if (len == 0) {
                        G(endofile) = G(endodata) = 1;
                        return (0);
                  }
                  return (db_load_badend(dbenv));
            }
            if (first) {
                  first = 0;
                  if (G(version) > 1) {
                        if (c1 != ' ') {
                              buf[0] = c1;
                              if (fgets(buf + 1,
                                  sizeof(buf) - 1, stdin) == NULL ||
                                  strcmp(buf, "DATA=END\n") != 0)
                                    return (db_load_badend(dbenv));
                              G(endodata) = 1;
                              return (0);
                        }
                        continue;
                  }
            }
            if ((c2 = getchar()) == EOF)
                  return (db_load_badend(dbenv));
            if (len >= dbtp->ulen - 10) {
                  dbtp->ulen *= 2;
                  if ((dbtp->data =
                      realloc(dbtp->data, dbtp->ulen)) == NULL) {
                        dbenv->err(dbenv, ENOMEM, NULL);
                        return (1);
                  }
                  p = (u_int8_t *)dbtp->data + len;
            }
            ++len;
            DIGITIZE(*p++, c1, c2);
      }
      dbtp->size = len;

      return (0);
}

/*
 * dbt_rrecno --
 *    Read a record number dump line into a DBT structure.
 */
int
db_load_dbt_rrecno(dbenv, dbtp, ishex)
      DB_ENV *dbenv;
      DBT *dbtp;
      int ishex;
{
      char buf[32], *p, *q;
      u_long recno;

      ++G(lineno);

      if (fgets(buf, sizeof(buf), stdin) == NULL) {
            G(endofile) = G(endodata) = 1;
            return (0);
      }

      if (strcmp(buf, "DATA=END\n") == 0) {
            G(endodata) = 1;
            return (0);
      }

      if (buf[0] != ' ')
            goto err;

      /*
       * If we're expecting a hex key, do an in-place conversion
       * of hex to straight ASCII before calling __db_getulong().
       */
      if (ishex) {
            for (p = q = buf + 1; *q != '\0' && *q != '\n';) {
                  /*
                   * 0-9 in hex are 0x30-0x39, so this is easy.
                   * We should alternate between 3's and [0-9], and
                   * if the [0-9] are something unexpected,
                   * __db_getulong will fail, so we only need to catch
                   * end-of-string conditions.
                   */
                  if (*q++ != '3')
                        goto err;
                  if (*q == '\n' || *q == '\0')
                        goto err;
                  *p++ = *q++;
            }
            *p = '\0';
      }

      if (__db_getulong(dbenv, G(progname), buf + 1, 0, 0, &recno))
            goto err;

      *((db_recno_t *)dbtp->data) = recno;
      dbtp->size = sizeof(db_recno_t);
      return (0);

err:  return (db_load_badend(dbenv));
}

int
db_load_dbt_to_recno(dbenv, dbt, recnop)
      DB_ENV *dbenv;
      DBT *dbt;
      db_recno_t *recnop;
{
      char buf[32];                       /* Large enough for 2^64. */

      memcpy(buf, dbt->data, dbt->size);
      buf[dbt->size] = '\0';

      return (__db_getulong(dbenv, G(progname), buf, 0, 0, (u_long *)recnop));
}

/*
 * badnum --
 *    Display the bad number message.
 */
void
db_load_badnum(dbenv)
      DB_ENV *dbenv;
{
      dbenv->errx(dbenv,
          "boolean name=value pairs require a value of 0 or 1");
}

/*
 * badend --
 *    Display the bad end to input message.
 */
int
db_load_badend(dbenv)
      DB_ENV *dbenv;
{
      dbenv->errx(dbenv, "unexpected end of input data or key/data pair");
      return (1);
}

/*
 * usage --
 *    Display the usage message.
 */
int
db_load_usage()
{
      (void)fprintf(stderr, "usage: %s %s\n\t%s\n", progname,
          "[-nTV] [-c name=value] [-f file]",
    "[-h home] [-P password] [-t btree | hash | recno | queue] db_file");
      (void)fprintf(stderr, "usage: %s %s\n",
          progname, "-r lsn | fileid [-h home] [-P password] db_file");
      return (EXIT_FAILURE);
}

int
db_load_version_check()
{
      int v_major, v_minor, v_patch;

      /* Make sure we're loaded with the right version of the DB library. */
      (void)db_version(&v_major, &v_minor, &v_patch);
      if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
            fprintf(stderr,
      "%s: version %d.%d doesn't match library version %d.%d\n",
                progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
                v_major, v_minor);
            return (EXIT_FAILURE);
      }
      return (0);
}

Generated by  Doxygen 1.6.0   Back to index