/* * DbRecord_field -- * Fill in an individual field of the DbRecord. */ static int DbRecord_field( DbRecord *recordp, u_int field, void *addr, datatype type) { size_t len; char number_buf[20]; /* * The offset table is 0-based, the field numbers are 1-based. * Correct. */ --field; switch (type) { case NOTSET: /* NOTREACHED */ abort(); break; case STRING: *((u_char **)addr) = recordp->record + recordp->offset[field]; recordp->record[recordp->offset[field] + OFFSET_LEN(recordp->offset, field)] = '\0'; break; case DOUBLE: case UNSIGNED_LONG: /* This shouldn't be possible -- 2^32 is only 10 digits. */ len = OFFSET_LEN(recordp->offset, field); if (len > sizeof(number_buf) - 1) { dbenv->errx(dbenv, "record %lu field %lu: numeric field is %lu bytes and too large to copy", recordp->recno, field, (u_long)len); return (1); } memcpy(number_buf, recordp->record + recordp->offset[field], len); number_buf[len] = '\0'; if (type == DOUBLE) { if (len == 0) *(double *)addr = 0; else if (strtod_err(number_buf, (double *)addr) != 0) goto fmt_err; } else if (len == 0) *(u_long *)addr = 0; else if (strtoul_err(number_buf, (u_long *)addr) != 0) { fmt_err: dbenv->errx(dbenv, "record %lu: numeric field %u error: %s", recordp->recno, field, number_buf); return (1); } break; } return (0); }
int main(int argc, char *argv[]) { input_fmt ifmt; u_long version; int ch, ret, t_ret; char *home; /* Initialize globals. */ dbenv = NULL; db = NULL; if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; verbose = 0; ffp = NULL; /* Initialize arguments. */ home = NULL; ifmt = FORMAT_NL; version = 1; /* Process arguments. */ while ((ch = getopt(argc, argv, "F:f:h:V:v")) != EOF) switch (ch) { case 'f': /* Required argument */ if ((ffp = freopen(optarg, "r", stdin)) == NULL) { fprintf(stderr, "%s: %s\n", optarg, db_strerror(errno)); return (EXIT_FAILURE); } break; case 'F': if (strcasecmp(optarg, "excel") == 0) { ifmt = FORMAT_EXCEL; break; } return (usage()); case 'h': home = optarg; break; case 'V': if (strtoul_err(optarg, &version)) return (EXIT_FAILURE); break; case 'v': ++verbose; break; case '?': default: return (usage()); } argc -= optind; argv += optind; if (*argv != NULL || ffp == NULL) return (usage()); /* * The home directory may not exist -- try and create it. We don't * bother to distinguish between failure to create it and it already * existing, as the database environment open will fail if we aren't * successful. */ if (home == NULL) home = getenv("DB_HOME"); if (home != NULL) (void)mkdir(home, S_IRWXU); /* Create or join the database environment. */ if (csv_env_open(home, 0) != 0) return (EXIT_FAILURE); /* Load records into the database. */ ret = input_load(ifmt, version); /* Close the database environment. */ if ((t_ret = csv_env_close()) != 0 && ret == 0) ret = t_ret; return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); }
/* * DbRecord_search_field -- * Search, looking for a record by field. */ static int DbRecord_search_field(DbField *f, char *value, OPERATOR op) { #ifdef HAVE_WILDCARD_SUPPORT regex_t preq; #endif DBC *dbc; DbRecord record; DBT key, data, pkey; double value_double; u_long value_ulong; u_int32_t cursor_flags; int ret, t_ret; int (*cmp)(void *, void *, OPERATOR); void *faddr, *valuep; dbc = NULL; memset(&key, 0, sizeof(key)); memset(&pkey, 0, sizeof(pkey)); memset(&data, 0, sizeof(data)); /* * Initialize the comparison function, crack the value. Wild cards * are always strings, otherwise we follow the field type. */ if (op == WC || op == NWC) { #ifdef HAVE_WILDCARD_SUPPORT if (f->type != STRING) { dbenv->errx(dbenv, "wildcard operator only supported for string fields"); return (1); } if (regcomp(&preq, value, 0) != 0) { dbenv->errx(dbenv, "regcomp of pattern failed"); return (1); } valuep = &preq; cmp = field_cmp_re; #else dbenv->errx(dbenv, "wildcard operators not supported in this build"); return (1); #endif } else switch (f->type) { case DOUBLE: if (strtod_err(value, &value_double) != 0) return (1); cmp = field_cmp_double; valuep = &value_double; key.size = sizeof(double); break; case STRING: valuep = value; cmp = field_cmp_string; key.size = (u_int32_t)strlen(value); break; case UNSIGNED_LONG: if (strtoul_err(value, &value_ulong) != 0) return (1); cmp = field_cmp_ulong; valuep = &value_ulong; key.size = sizeof(u_long); break; default: case NOTSET: abort(); /* NOTREACHED */ } /* * Retrieve the first record that interests us. The range depends on * the operator: * * ~ beginning to end * != beginning to end * < beginning to first match * <= beginning to last match * = first match to last match * > record after last match to end * >= first match to end * * If we have a secondary, set a cursor in the secondary, else set the * cursor to the beginning of the primary. * * XXX * If the wildcard string has a leading non-magic character we should * be able to do a range search instead of a full-database search. * * Step through records to the first non-match or to the end of the * database, depending on the operation. If the comparison function * returns success for a key/data pair, print the pair. */ if (f->secondary == NULL || op == NEQ || op == WC || op == NWC) { if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0) goto err; while ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) { if ((ret = DbRecord_init(&key, &data, &record)) != 0) break; faddr = (u_int8_t *)&record + f->offset; if (cmp(faddr, valuep, op)) DbRecord_print(&record, NULL); else if (op == EQ || op == LT || op == LTEQ) break; } } else { if ((ret = f->secondary->cursor(f->secondary, NULL, &dbc, 0)) != 0) goto err; key.data = valuep; cursor_flags = op == LT || op == LTEQ ? DB_FIRST : DB_SET_RANGE; if ((ret = dbc->c_pget(dbc, &key, &pkey, &data, cursor_flags)) != 0) goto done; if (op == GT) { while ((ret = dbc->c_pget( dbc, &key, &pkey, &data, DB_NEXT)) == 0) { if ((ret = DbRecord_init(&pkey, &data, &record)) != 0) break; faddr = (u_int8_t *)&record + f->offset; if (cmp(faddr, valuep, op) != 0) break; } if (ret != 0) goto done; } do { if ((ret = DbRecord_init(&pkey, &data, &record)) != 0) break; faddr = (u_int8_t *)&record + f->offset; if (cmp(faddr, valuep, op)) DbRecord_print(&record, NULL); else if (op == EQ || op == LT || op == LTEQ) break; } while ((ret = dbc->c_pget(dbc, &key, &pkey, &data, DB_NEXT)) == 0); } done: if (ret == DB_NOTFOUND) ret = 0; err: if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0) ret = t_ret; #ifdef HAVE_WILDCARD_SUPPORT if (op == WC || op == NWC) regfree(&preq); #endif return (ret); }
/* * DbRecord_search_recno -- * Search, looking for a record by record number. */ static int DbRecord_search_recno(char *value, OPERATOR op) { DBC *dbc; DbRecord record; DBT key, data; u_int32_t recno; u_long recno_ulong; int ret; /* * XXX * This code assumes a record number (typed as u_int32_t) is the same * size as an unsigned long, and there's no reason to believe that. */ if (strtoul_err(value, &recno_ulong) != 0) return (1); memset(&key, 0, sizeof(key)); memset(&data, 0, sizeof(data)); key.data = &recno; key.size = sizeof(recno); if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0) return (ret); /* * Retrieve the first record that interests us. The range depends on * the operator: * * ~ error * != beginning to end * < beginning to first match * <= beginning to last match * = first match to last match * > record after last match to end * >= first match to end */ if (op == LT || op == LTEQ || op == NEQ || op == WC || op == NWC) recno = 1; else if (op == WC || op == NWC) { dbenv->errx(dbenv, "wildcard operator only supported for string fields"); return (1); } else { recno = recno_ulong; if (op == GT) ++recno; } if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) != 0) goto err; for (;;) { if ((ret = DbRecord_init(&key, &data, &record)) != 0) break; if (field_cmp_ulong(&record.recno, &recno_ulong, op)) DbRecord_print(&record, NULL); else if (op == LT || op == LTEQ || op == EQ) break; if ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) != 0) break; } err: return (ret == DB_NOTFOUND ? 0 : ret); }