/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996, 1997, 1998 * Sleepycat Software. All rights reserved. */ #include "config.h" #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1996, 1997, 1998\n\ Sleepycat Software Inc. All rights reserved.\n"; static const char sccsid[] = "@(#)db_load.c 10.23 (Sleepycat) 10/4/98"; #endif #ifndef NO_SYSTEM_INCLUDES #include #include #include #include #include #include #include #include #endif #include "db_int.h" #include "db_page.h" #include "db_am.h" #include "clib_ext.h" void badnum __P((void)); void configure __P((DB_INFO *, char **)); DB_ENV *db_init __P((char *)); int dbt_rdump __P((DBT *)); int dbt_rprint __P((DBT *)); int digitize __P((int)); int main __P((int, char *[])); void onint __P((int)); void rheader __P((DBTYPE *, int *, DB_INFO *)); void siginit __P((void)); void usage __P((void)); int interrupted; const char *progname = "db_load"; /* Program name. */ int main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; DB *dbp; DBT key, data; DBTYPE argtype, dbtype; DB_ENV *dbenv; DB_INFO dbinfo; db_recno_t recno; u_int32_t db_nooverwrite; int ch, checkprint, existed, no_header, ret; char **clist, **clp, *home; /* Allocate enough room for configuration arguments. */ if ((clp = clist = (char **)calloc(argc + 1, sizeof(char *))) == NULL) err(1, NULL); dbp = NULL; home = NULL; db_nooverwrite = 0; checkprint = existed = no_header = ret = 0; argtype = dbtype = DB_UNKNOWN; while ((ch = getopt(argc, argv, "c:f:h:nTt:")) != EOF) switch (ch) { case 'c': *clp++ = optarg; break; case 'f': if (freopen(optarg, "r", stdin) == NULL) err(1, "%s", optarg); break; case 'h': home = optarg; break; case 'n': db_nooverwrite = DB_NOOVERWRITE; break; case 'T': no_header = checkprint = 1; break; case 't': if (strcmp(optarg, "btree") == 0) { argtype = DB_BTREE; break; } if (strcmp(optarg, "hash") == 0) { argtype = DB_HASH; break; } if (strcmp(optarg, "recno") == 0) { argtype = DB_RECNO; break; } usage(); /* NOTREACHED */ case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); /* * Read the header. If there isn't any header, we're expecting flat * text, set the checkprint flag appropriately. */ memset(&dbinfo, 0, sizeof(DB_INFO)); if (no_header) dbtype = argtype; else { rheader(&dbtype, &checkprint, &dbinfo); if (argtype != DB_UNKNOWN) { /* Conversion to/from recno is prohibited. */ if ((dbtype == DB_RECNO && argtype != DB_RECNO) || (argtype == DB_RECNO && dbtype != DB_RECNO)) errx(1, "databases of type recno may not be converted"); dbtype = argtype; } } if (dbtype == DB_UNKNOWN) errx(1, "no database type specified"); /* Apply command-line configuration changes. */ configure(&dbinfo, clist); /* Initialize the key/data pair. */ memset(&key, 0, sizeof(DBT)); if (dbtype == DB_RECNO) { key.data = &recno; key.size = sizeof(recno); } else if ((key.data = (void *)malloc(key.ulen = 1024)) == NULL) { errno = ENOMEM; err(1, NULL); } memset(&data, 0, sizeof(DBT)); if ((data.data = (void *)malloc(data.ulen = 1024)) == NULL) { errno = ENOMEM; err(1, NULL); } /* Initialize the environment if the user specified one. */ siginit(); dbenv = home == NULL ? NULL : db_init(home); /* Open the DB file. */ if ((errno = db_open(argv[0], dbtype, DB_CREATE, __db_omode("rwrwrw"), dbenv, &dbinfo, &dbp)) != 0) { warn("%s", argv[0]); goto err; } /* Get each key/data pair and add them to the database. */ for (recno = 1; !interrupted; ++recno) { if (dbtype == DB_RECNO) if (checkprint) { if (dbt_rprint(&data)) break; } else { if (dbt_rdump(&data)) break; } else if (checkprint) { if (dbt_rprint(&key)) break; if (dbt_rprint(&data)) goto fmt; } else { if (dbt_rdump(&key)) break; if (dbt_rdump(&data)) { fmt: warnx("odd number of key/data pairs"); goto err; } } switch (errno = dbp->put(dbp, NULL, &key, &data, db_nooverwrite)) { case 0: break; case DB_KEYEXIST: existed = 1; warnx("%s: line %d: key already exists, not loaded:", argv[0], dbtype == DB_RECNO ? recno : recno * 2 - 1); (void)__db_prdbt(&key, checkprint, stderr); break; default: warn(NULL); goto err; } } if (0) { err: ret = 1; } if (dbp != NULL && (errno = dbp->close(dbp, 0)) != 0) { ret = 1; warn(NULL); } if (dbenv != NULL && (errno = db_appexit(dbenv)) != 0) { ret = 1; warn(NULL); } if (interrupted) { (void)signal(interrupted, SIG_DFL); (void)raise(interrupted); /* NOTREACHED */ } /* Return 0 on success, 1 if keys existed already, and 2 on failure. */ return (ret == 0 ? (existed == 0 ? 0 : 1) : 2); } /* * db_init -- * Initialize the environment. */ DB_ENV * db_init(home) char *home; { DB_ENV *dbenv; if ((dbenv = (DB_ENV *)calloc(sizeof(DB_ENV), 1)) == NULL) { errno = ENOMEM; err(1, NULL); } /* * The database may be live, try and use the shared regions. * * If it works, we're done. Set the error output options so that * future errors are correctly reported. */ if ((errno = db_appinit(home, NULL, dbenv, DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_USE_ENVIRON)) == 0) { dbenv->db_errfile = stderr; dbenv->db_errpfx = progname; return (dbenv); } /* * If the db_appinit fails, assume the database isn't live, and don't * bother with an environment. */ free(dbenv); return (NULL); } #define FLAG(name, value, keyword, flag) \ if (strcmp(name, keyword) == 0) { \ switch (*value) { \ case '1': \ dbinfop->flags |= (flag); \ break; \ case '0': \ dbinfop->flags &= ~(flag); \ break; \ default: \ badnum(); \ /* NOTREACHED */ \ } \ continue; \ } #define NUMBER(name, value, keyword, field, flag) \ if (strcmp(name, keyword) == 0) { \ get_long(value, 1, LONG_MAX, &val); \ dbinfop->field = val; \ if (flag != 0) \ dbinfop->flags |= (flag); \ continue; \ } #define STRING(name, value, keyword, field, flag) \ if (strcmp(name, keyword) == 0) { \ dbinfop->field = value[0]; \ if (flag != 0) \ dbinfop->flags |= (flag); \ continue; \ } /* * configure -- * Handle command-line configuration options. */ void configure(dbinfop, clp) DB_INFO *dbinfop; char **clp; { long val; char *name, *value; for (; (name = *clp) != NULL; ++clp) { if ((value = strchr(name, '=')) == NULL) errx(1, "command-line configuration uses name=value format"); *value++ = '\0'; NUMBER(name, value, "bt_maxkey", bt_maxkey, 0); NUMBER(name, value, "bt_minkey", bt_minkey, 0); NUMBER(name, value, "db_lorder", db_lorder, 0); NUMBER(name, value, "db_pagesize", db_pagesize, 0); FLAG(name, value, "duplicates", DB_DUP); NUMBER(name, value, "h_ffactor", h_ffactor, 0); NUMBER(name, value, "h_nelem", h_nelem, 0); NUMBER(name, value, "re_len", re_len, DB_FIXEDLEN); STRING(name, value, "re_pad", re_pad, DB_PAD); FLAG(name, value, "recnum", DB_RECNUM); FLAG(name, value, "renumber", DB_RENUMBER); errx(1, "unknown command-line configuration keyword"); } } /* * rheader -- * Read the header message. */ void rheader(dbtypep, checkprintp, dbinfop) DBTYPE *dbtypep; int *checkprintp; DB_INFO *dbinfop; { long lineno, val; char name[256], value[256]; *dbtypep = DB_UNKNOWN; *checkprintp = 0; for (lineno = 1;; ++lineno) { /* If we don't see the expected information, it's an error. */ if (fscanf(stdin, "%[^=]=%s\n", name, value) != 2) errx(1, "line %lu: unexpected format", lineno); /* Check for the end of the header lines. */ if (strcmp(name, "HEADER") == 0) break; if (strcmp(name, "format") == 0) { if (strcmp(value, "bytevalue") == 0) { *checkprintp = 0; continue; } if (strcmp(value, "print") == 0) { *checkprintp = 1; continue; } errx(1, "line %d: unknown format", lineno); } 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; } errx(1, "line %d: unknown type", lineno); } NUMBER(name, value, "bt_maxkey", bt_maxkey, 0); NUMBER(name, value, "bt_minkey", bt_minkey, 0); NUMBER(name, value, "db_lorder", db_lorder, 0); NUMBER(name, value, "db_pagesize", db_pagesize, 0); FLAG(name, value, "duplicates", DB_DUP); NUMBER(name, value, "h_ffactor", h_ffactor, 0); NUMBER(name, value, "h_nelem", h_nelem, 0); NUMBER(name, value, "re_len", re_len, DB_FIXEDLEN); STRING(name, value, "re_pad", re_pad, DB_PAD); FLAG(name, value, "recnum", DB_RECNUM); FLAG(name, value, "renumber", DB_RENUMBER); errx(1, "unknown input-file header configuration keyword"); } } /* * dbt_rprint -- * Read a printable line into a DBT structure. */ int dbt_rprint(dbtp) DBT *dbtp; { u_int32_t len; u_int8_t *p; int c1, c2, escape; escape = 0; for (p = dbtp->data, len = 0; (c1 = getchar()) != '\n';) { if (c1 == EOF) { if (len == 0) return (1); err(1, "unexpected end of key/data pair"); } if (escape) { if (c1 != '\\') { if ((c2 = getchar()) == EOF) err(1, "unexpected end of key/data pair"); c1 = digitize(c1) << 4 | digitize(c2); } escape = 0; } else if (c1 == '\\') { escape = 1; continue; } if (len >= dbtp->ulen - 10) { dbtp->ulen *= 2; if ((dbtp->data = (void *)realloc(dbtp->data, dbtp->ulen)) == NULL) { errno = ENOMEM; err(1, NULL); } 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 dbt_rdump(dbtp) DBT *dbtp; { u_int32_t len; u_int8_t *p; int c1, c2; for (p = dbtp->data, len = 0; (c1 = getchar()) != '\n';) { if (c1 == EOF) { if (len == 0) return (1); err(1, "unexpected end of key/data pair"); } if ((c2 = getchar()) == EOF) err(1, "unexpected end of key/data pair"); if (len >= dbtp->ulen - 10) { dbtp->ulen *= 2; if ((dbtp->data = (void *)realloc(dbtp->data, dbtp->ulen)) == NULL) { errno = ENOMEM; err(1, NULL); } p = (u_int8_t *)dbtp->data + len; } ++len; *p++ = digitize(c1) << 4 | digitize(c2); } dbtp->size = len; return (0); } /* * digitize -- * Convert a character to an integer. */ int digitize(c) int c; { switch (c) { /* Don't depend on ASCII ordering. */ case '0': return (0); case '1': return (1); case '2': return (2); case '3': return (3); case '4': return (4); case '5': return (5); case '6': return (6); case '7': return (7); case '8': return (8); case '9': return (9); case 'a': return (10); case 'b': return (11); case 'c': return (12); case 'd': return (13); case 'e': return (14); case 'f': return (15); } err(1, "unexpected hexadecimal value"); /* NOTREACHED */ return (0); } /* * badnum -- * Display the bad number message. */ void badnum() { err(1, "boolean name=value pairs require a value of 0 or 1"); } /* * siginit -- * Initialize the set of signals for which we want to clean up. * Generally, we try not to leave the shared regions locked if * we can. */ void siginit() { #ifdef SIGHUP (void)signal(SIGHUP, onint); #endif (void)signal(SIGINT, onint); (void)signal(SIGTERM, onint); } /* * onint -- * Interrupt signal handler. */ void onint(signo) int signo; { if ((interrupted = signo) == 0) interrupted = SIGINT; } /* * usage -- * Display the usage message. */ void usage() { (void)fprintf(stderr, "%s\n\t%s\n", "usage: db_load [-nT]", "[-c name=value] [-f file] [-h home] [-t btree | hash | recno] db_file"); exit(1); }