diff options
Diffstat (limited to 'db-4.8.30/examples_c/csv')
-rw-r--r-- | db-4.8.30/examples_c/csv/DbRecord.c | 470 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/Makefile | 75 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/README | 408 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/code.c | 405 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/csv.h | 101 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/csv_extern.h | 37 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/db.c | 244 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/load.c | 346 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/load_main.c | 117 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/query.c | 241 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/query_main.c | 99 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/sample.csv | 8 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/sample.desc | 10 | ||||
-rw-r--r-- | db-4.8.30/examples_c/csv/util.c | 309 |
14 files changed, 2870 insertions, 0 deletions
diff --git a/db-4.8.30/examples_c/csv/DbRecord.c b/db-4.8.30/examples_c/csv/DbRecord.c new file mode 100644 index 0000000..56af9d4 --- /dev/null +++ b/db-4.8.30/examples_c/csv/DbRecord.c @@ -0,0 +1,470 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +static int DbRecord_field(DbRecord *, u_int, void *, datatype); +static int DbRecord_search_field(DbField *, char *, OPERATOR); +static int DbRecord_search_recno(char *, OPERATOR); + +/* + * DbRecord_print -- + * Display a DbRecord structure. + */ +void +DbRecord_print(DbRecord *recordp, FILE *fp) +{ + DbField *f; + void *faddr; + + if (fp == NULL) + fp = stdout; + + fprintf(fp, "Record: %lu:\n", (u_long)recordp->recno); + for (f = fieldlist; f->name != NULL; ++f) { + faddr = (u_int8_t *)recordp + f->offset; + fprintf(fp, "\t%s: ", f->name); + switch (f->type) { + case NOTSET: + /* NOTREACHED */ + abort(); + break; + case DOUBLE: + fprintf(fp, "%f\n", *(double *)faddr); + break; + case STRING: + fprintf(fp, "%s\n", *(char **)faddr); + break; + case UNSIGNED_LONG: + fprintf(fp, "%lu\n", *(u_long *)faddr); + break; + } + } +} + +/* + * DbRecord_read -- + * Read a specific record from the database. + */ +int +DbRecord_read(u_long recno_ulong, DbRecord *recordp) +{ + DBT key, data; + u_int32_t recno; + 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. + */ + recno = recno_ulong; + + /* + * Retrieve the requested record from the primary database. Have + * Berkeley DB allocate memory for us, keeps the DB handle thread + * safe. + * + * We have the Berkeley DB library allocate memory for the record, + * which we own and must eventually free. The reason is so we can + * have the string fields in the structure point into the actual + * record, rather than allocating structure local memory to hold them + * and copying them out of the record. + */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + key.data = &recno; + key.size = sizeof(recno); + data.flags = DB_DBT_MALLOC; + if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) + return (ret); + + if ((ret = DbRecord_init(&key, &data, recordp)) != 0) + return (ret); + + return (0); +} + +/* + * DbRecord_discard -- + * Discard a DbRecord structure. + */ +int +DbRecord_discard(DbRecord *recordp) +{ + /* Free the allocated memory. */ + free(recordp->raw); + recordp->raw = NULL; + + return (0); +} + +/* + * DbRecord_init -- + * Fill in a DbRecord from the database key/data pair. + */ +int +DbRecord_init(const DBT *key, const DBT *data, DbRecord *recordp) +{ + DbField *f; + u_int32_t skip; + void *faddr; + + /* Initialize the structure (get the pre-set index values). */ + *recordp = DbRecord_base; + + /* Fill in the ID and version. */ + memcpy(&recordp->recno, key->data, sizeof(u_int32_t)); + memcpy(&recordp->version, + (u_int32_t *)data->data + 1, sizeof(u_int32_t)); + + /* Set up the record references. */ + recordp->raw = data->data; + recordp->offset = (u_int32_t *)data->data + 1; + skip = (recordp->field_count + 2) * sizeof(u_int32_t); + recordp->record = (u_int8_t *)data->data + skip; + recordp->record_len = data->size - skip; + + for (f = fieldlist; f->name != NULL; ++f) { + faddr = (u_int8_t *)recordp + f->offset; + if (DbRecord_field( + recordp, f->fieldno, faddr, f->type) != 0) + return (1); + } + return (0); +} + +/* + * 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); +} + +/* + * DbRecord_search_field_name -- + * Search, looking for a record by field name. + */ +int +DbRecord_search_field_name(char *field, char *value, OPERATOR op) +{ + DbField *f; + + for (f = fieldlist; f->name != NULL; ++f) + if (strcasecmp(field, f->name) == 0) + return (DbRecord_search_field(f, value, op)); + + /* Record numbers aren't handled as fields. */ + if (strcasecmp(field, "id") == 0) + return (DbRecord_search_recno(value, op)); + + dbenv->errx(dbenv, "unknown field name: %s", field); + return (1); +} + +/* + * DbRecord_search_field_number -- + * Search, looking for a record by field number. + */ +int +DbRecord_search_field_number(u_int32_t fieldno, char *value, OPERATOR op) +{ + DbField *f; + + for (f = fieldlist; f->name != NULL; ++f) + if (fieldno == f->fieldno) + return (DbRecord_search_field(f, value, op)); + + dbenv->errx(dbenv, "field number %lu not configured", (u_long)fieldno); + return (1); +} + +/* + * 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); +} + +/* + * 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 = 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); +} diff --git a/db-4.8.30/examples_c/csv/Makefile b/db-4.8.30/examples_c/csv/Makefile new file mode 100644 index 0000000..9d46cc7 --- /dev/null +++ b/db-4.8.30/examples_c/csv/Makefile @@ -0,0 +1,75 @@ +# $Id$ + +# Berkeley DB installation. +DB_INCLUDE=../../build_unix +LIBS= -L../../build_unix -L../../build_unix/.libs/ -ldb + +INC= -I. -I$(DB_INCLUDE) +CFLAGS= $(INC) -g -W -Wall -Wpointer-arith -Wmissing-prototypes + +PROGS= csv_code csv_load csv_query +SRCS= DbRecord.c code.c csv_local.c db.c load.c load_main.c query.c \ + query_main.c util.c + +all: csv_load csv_query + +csv_code: code.o + $(CC) -o $@ $? $(LIBS) + +LOAD_OBJS=DbRecord.o csv_local.o db.o load.o load_main.o util.o +csv_load: $(LOAD_OBJS) + $(CC) -o $@ $(LOAD_OBJS) $(LIBS) + +QUERY_OBJS=DbRecord.o csv_local.o db.o query.o query_main.o util.o +csv_query: $(QUERY_OBJS) + $(CC) -o $@ $(QUERY_OBJS) $(LIBS) + +clean distclean realclean: + rm -rf $(PROGS) TESTDIR eBay tags *.o *.core csv_local.[ch] + +tags: + rm -f tags + ctags $(SRCS) code.c + +DbRecord.o csv_local.o db.o load.o load_main.o query.o: csv_local.h csv.h +query_main.o util.o: csv_local.h csv.h + +csv_local.c csv_local.h: csv_code + ./csv_code -c csv_local.c -h csv_local.h -f sample.desc + +lint_code: + flexelint +fll \ + "-e801" \ + "-e818" \ + "-esym(534,fprintf)" \ + "-esym(534,memcpy)" \ + "-esym(534,memmove)" \ + "-esym(534,memset)" \ + "-esym(534,printf)" \ + "-wlib(1)" \ + -i$(DB_INCLUDE) "-i/usr/include" \ + code.c +lint_load: + flexelint +fll \ + "-e801" \ + "-e818" \ + "-esym(534,fprintf)" \ + "-esym(534,memcpy)" \ + "-esym(534,memmove)" \ + "-esym(534,memset)" \ + "-esym(534,printf)" \ + "-wlib(1)" \ + -i$(DB_INCLUDE) "-i/usr/include" \ + DbRecord.c csv_local.c db.c load.c load_main.c util.c +lint_query: + flexelint +fll \ + "-e801" \ + "-e818" \ + "-esym(534,fprintf)" \ + "-esym(534,memcpy)" \ + "-esym(534,memmove)" \ + "-esym(534,memset)" \ + "-esym(534,printf)" \ + "-wlib(1)" \ + -i$(DB_INCLUDE) "-i/usr/include" \ + DbRecord.c csv_local.c db.c query.c query_main.c util.c diff --git a/db-4.8.30/examples_c/csv/README b/db-4.8.30/examples_c/csv/README new file mode 100644 index 0000000..6a5fd13 --- /dev/null +++ b/db-4.8.30/examples_c/csv/README @@ -0,0 +1,408 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +The "comma-separated value" (csv) directory is a suite of three programs: + + csv_code: write "helper" code on which to build applications, + csv_load: import csv files into a Berkeley DB database, + csv_query: query databases created by csv_load. + +The goal is to allow programmers to easily build applications for using +csv databases. + +You can build the three programs, and run a sample application in this +directory. + +First, there's the sample.csv file: + + Adams,Bob,01/02/03,green,apple,37 + Carter,Denise Ann,04/05/06,blue,banana,38 + Eidel,Frank,07/08/09,red,cherry,38 + Grabel,Harriet,10/11/12,purple,date,40 + Indals,Jason,01/03/05,pink,orange,32 + Kilt,Laura,07/09/11,yellow,grape,38 + Moreno,Nancy,02/04/06,black,strawberry,38 + Octon,Patrick,08/10/12,magenta,kiwi,15 + +The fields are: + Last name, + First name, + Birthdate, + Favorite color, + Favorite fruit, + Age + +Second, there's a "description" of that csv file in sample.desc: + + version 1 { + LastName string + FirstName string + BirthDate + Color string index + Fruit string index + Age unsigned_long index + } + +The DESCRIPTION file maps one-to-one to the fields in the csv file, and +provides a data type for any field the application wants to use. (If +the application doesn't care about a field, don't specify a data type +and the csv code will ignore it.) The string "index" specifies there +should be a secondary index based on the field. + +The "field" names in the DESCRIPTION file don't have to be the same as +the ones in the csv file (and, as they may not have embedded spaces, +probably won't be). + +To build in the sample directory, on POSIX-like systems, type "make". +This first builds the program csv_code, which it then run, with the file +DESCRIPTION as an input. Running csv_code creates two additional files: +csv_local.c and csv_local.h. Those two files are then used as part of +the build process for two more programs: csv_load and csv_query. + +You can load now load the csv file into a Berkeley DB database with the +following command: + + % ./csv_load -h TESTDIR < sample.csv + +The csv_load command will create a directory and four databases: + + primary primary database + Age secondary index on Age field + Color secondary index on Color field + Fruit secondary index on Fruit field + +You can then query the database: + + % ./csv_query -h TESTDIR + Query: id=2 + Record: 2: + LastName: Carter + FirstName: Denise + Color: blue + Fruit: banana + Age: 38 + Query: color==green + Record: 1: + LastName: Adams + FirstName: Bob + Color: green + Fruit: apple + Age: 37 + +and so on. + +The csv_code process also creates source code modules that support +building your own applications based on this database. First, there +is the local csv_local.h include file: + + /* + * DO NOT EDIT: automatically built by csv_code. + * + * Record structure. + */ + typedef struct __DbRecord { + u_int32_t recno; /* Record number */ + + /* + * Management fields + */ + void *raw; /* Memory returned by DB */ + char *record; /* Raw record */ + size_t record_len; /* Raw record length */ + + u_int32_t field_count; /* Field count */ + u_int32_t version; /* Record version */ + + u_int32_t *offset; /* Offset table */ + + /* + * Indexed fields + */ + #define CSV_INDX_LASTNAME 1 + char *LastName; + + #define CSV_INDX_FIRSTNAME 2 + char *FirstName; + + #define CSV_INDX_COLOR 4 + char *Color; + + #define CSV_INDX_FRUIT 5 + char *Fruit; + + #define CSV_INDX_AGE 6 + u_long Age; + } DbRecord; + +This defines the DbRecord structure that is the primary object for this +csv file. As you can see, the intersting fields in the csv file have +mappings in this structure. + +Also, there are routines in the Dbrecord.c file your application can use +to handle DbRecord structures. When you retrieve a record from the +database the DbRecord structure will be filled in based on that record. + +Here are the helper routines: + + int + DbRecord_print(DbRecord *recordp, FILE *fp) + Display the contents of a DbRecord structure to the specified + output stream. + + int + DbRecord_init(const DBT *key, DBT *data, DbRecord *recordp) + Fill in a DbRecord from a returned database key/data pair. + + int + DbRecord_read(u_long key, DbRecord *recordp) + Read the specified record (DbRecord_init will be called + to fill in the DbRecord). + + int + DbRecord_discard(DbRecord *recordp) + Discard the DbRecord structure (must be called after the + DbRecord_read function), when the application no longer + needs the returned DbRecord. + + int + DbRecord_search_field_name(char *field, char *value, OPERATOR op) + Display the DbRecords where the field (named by field) has + the specified relationship to the value. For example: + + DbRecord_search_field_name("Age", "35", GT) + + would search for records with a "Age" field greater than + 35. + + int + DbRecord_search_field_number( + u_int32_t fieldno, char *value, OPERATOR op) + Display the DbRecords where the field (named by field) + has the specified relationship to the value. The field + number used as an argument comes from the csv_local.h + file, for example, CSV_INDX_AGE is the field index for + the "Age" field in this csv file. For example: + + DbRecord_search_field_number(CSV_INDX_AGE, 35, GT) + + would search for records with a "Age" field greater than + 35. + + Currently, the csv code only supports three types of data: + strings, unsigned longs and doubles. Others can easily be + added. + +The usage of the csv_code program is as follows: + + usage: csv_code [-v] [-c source-file] [-f input] [-h header-file] + -c output C source code file + -h output C header file + -f input file + -v verbose (defaults to off) + + -c A file to which to write the C language code. By default, + the file "csv_local.c" is used. + + -f A file to read for a description of the fields in the + csv file. By default, csv_code reads from stdin. + + -h A file to which to write the C language header structures. + By default, the file "csv_local.h" is used. + + -v The -v verbose flag outputs potentially useful debugging + information. + +There are two applications built on top of the code produced by +csv_code, csv_load and csv_query. + +The usage of the csv_load program is as follows: + + usage: csv_load [-v] [-F format] [-f csv-file] [-h home] [-V version] + -F format (currently supports "excel") + -f input file + -h database environment home directory + -v verbose (defaults to off) + + -F See "Input format" below. + + -f If an input file is specified using the -f flag, the file + is read and the records in the file are stored into the + database. By default, csv_load reads from stdin. + + -h If a database environment home directory is specified + using the -h flag, that directory is used as the + Berkeley DB directory. The default for -h is the + current working directory or the value of the DB_HOME + environment variable. + + -V Specify a version number for the input (the default is 1). + + -v The -v verbose flag outputs potentially useful debugging + information. It can be specified twice for additional + information. + +The usage of csv_query program is as follows: + + usage: csv_query [-v] [-c cmd] [-h home] + + -c A command to run, otherwise csv_query will enter + interactive mode and prompt for user input. + + -h If a database environment home directory is specified + using the -h flag, that directory is used as the + Berkeley DB directory. The default for -h is the + current working directory or the value of the DB_HOME + environment variable. + + -v The -v verbose flag outputs potentially useful debugging + information. It can be specified twice for additional + information. + +The query program currently supports the following commands: + + ? Display help screen + exit Exit program + fields Display list of field names + help Display help screen + quit Exit program + version Display database format version + field[op]value Display fields by value (=, !=, <, <=, >, >=, ~, !~) + +The "field[op]value" command allows you to specify a field and a +relationship to a value. For example, you could run the query: + + csv_query -c "price < 5" + +to list all of the records with a "price" field less than "5". + +Field names and all string comparisons are case-insensitive. + +The operators ~ and !~ do match/no-match based on the IEEE Std 1003.2 +(POSIX.2) Basic Regular Expression standard. + +As a special case, every database has the field "Id", which matches the +record number of the primary key. + +Input format: + The input to the csv_load utility is a text file, containing + lines of comma-separated fields. + + Blank lines are ignored. All non-blank lines must be comma-separated + lists of fields. + + By default: + <nul> (\000) bytes and unprintable characters are stripped, + input lines are <nl> (\012) separated, + commas cannot be escaped. + + If "-F excel" is specified: + <nul> (\000) bytes and unprintable characters are stripped, + input lines are <cr> (\015) separated, + <nl> bytes (\012) characters are stripped from the input, + commas surrounded by double-quote character (") are not + treated as field separators. + +Storage format: + Records in the primary database are stored with a 32-bit unsigned + record number as the key. + + Key/Data pair 0 is of the format: + [version] 32-bit unsigned int + [field count] 32-bit unsigned int + [raw record] byte array + + For example: + [1] + [5] + [field1,field2,field3,field4,field5] + + All other Key/Data pairs are of the format: + [version] 32-bit unsigned int + [offset to field 1] 32-bit unsigned int + [offset to field 2] 32-bit unsigned int + [offset to field 3] 32-bit unsigned int + ... 32-bit unsigned int + [offset to field N] 32-bit unsigned int + [offset past field N] 32-bit unsigned int + [raw record] byte array + + For example: + [1] + [0] + [2] + [5] + [9] + [14] + [19] + [a,ab,abc,abcd,abcde] + 012345678901234567890 << byte offsets + 0 1 2 + + So, field 3 of the data can be directly accessed by using + the "offset to field 3", and the length of the field is + the "((offset to field 4) - (offset to field 3)) - 1". + +Limits: + The csv program stores the primary key in a 32-bit unsigned + value, limiting the number of records in the database. New + records are inserted after the last existing record, that is, + new records are not inserted into gaps left by any deleted + records. This will limit the total number of records stored in + any database. + +Versioning: + Versioning is when a database supports multiple versions of the + records. This is likely to be necessary when dealing with large + applications and databases, as record fields change over time. + + The csv application suite does not currently support versions, + although all of the necessary hooks are there. + + The way versioning will work is as follows: + + The XXX.desc file needs to support multiple version layouts. + + The generated C language structure defined should be a superset + of all of the interesting fields from all of the version + layouts, regardless of which versions of the csv records those + fields exist in. + + When the csv layer is asked for a record, the record's version + will provide a lookup into a separate database of field lists. + That is, there will be another database which has key/data pairs + where the key is a version number, and the data is the field + list. At that point, it's relatively easy to map the fields + to the structure as is currently done, except that some of the + fields may not be filled in. + + To determine if a field is filled in, in the structure, the + application has to have an out-of-band value to put in that + field during DbRecord initialization. If that's a problem, the + alternative would be to add an additional field for each listed + field -- if the additional field is set to 1, the listed field + has been filled in, otherwise it hasn't. The csv code will + support the notion of required fields, so in most cases the + application won't need to check before simply using the field, + it's only if a field isn't required and may be filled in that + the check will be necessary. + +TODO: + Csv databases are not portable between machines of different + byte orders. To make them portable, all of the 32-bit unsigned + int fields currently written into the database should be + converted to a standard byte order. This would include the + version number and field count in the column-map record, and the + version and field offsets in the other records. + + Add Extended RE string matches. + + Add APIs to replace the reading of a schema file, allow users to + fill in a DbRecord structure and do a put on it. (Hard problem: + how to flag fields that aren't filled in.) + + Add a second sample file, and write the actual versioning code. diff --git a/db-4.8.30/examples_c/csv/code.c b/db-4.8.30/examples_c/csv/code.c new file mode 100644 index 0000000..a6a3878 --- /dev/null +++ b/db-4.8.30/examples_c/csv/code.c @@ -0,0 +1,405 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" + +typedef struct { + char *name; /* Field name */ + char *upper; /* Field name in upper-case */ + datatype type; /* Data type */ + int indx; /* Index */ +} FIELD; + +int code_source(void); +int code_header(void); +int desc_dump(void); +int desc_load(void); +char *type_to_string(datatype); +int usage(void); + +/* + * Globals + */ +FILE *cfp; /* C source file */ +FILE *hfp; /* C source file */ +char *progname; /* Program name */ +int verbose; /* Verbose flag */ + +u_int field_cnt; /* Count of fields */ +FIELD *fields; /* Field list */ + +int +main(int argc, char *argv[]) +{ + int ch; + char *cfile, *hfile; + + /* Initialize globals. */ + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + ++progname; + + /* Initialize arguments. */ + cfile = "csv_local.c"; /* Default header/source files */ + hfile = "csv_local.h"; + + /* Process arguments. */ + while ((ch = getopt(argc, argv, "c:f:h:v")) != EOF) + switch (ch) { + case 'c': + cfile = optarg; + break; + case 'f': + if (freopen(optarg, "r", stdin) == NULL) { + fprintf(stderr, + "%s: %s\n", optarg, strerror(errno)); + return (EXIT_FAILURE); + } + break; + case 'h': + hfile = optarg; + break; + case 'v': + ++verbose; + break; + case '?': + default: + return (usage()); + } + argc -= optind; + argv += optind; + + if (*argv != NULL) + return (usage()); + + /* Load records from the input file. */ + if (desc_load()) + return (EXIT_FAILURE); + + /* Dump records for debugging. */ + if (verbose && desc_dump()) + return (EXIT_FAILURE); + + /* Open output files. */ + if ((cfp = fopen(cfile, "w")) == NULL) { + fprintf(stderr, + "%s: %s: %s\n", progname, cfile, strerror(errno)); + return (EXIT_FAILURE); + } + if ((hfp = fopen(hfile, "w")) == NULL) { + fprintf(stderr, + "%s: %s: %s\n", progname, hfile, strerror(errno)); + return (EXIT_FAILURE); + } + + /* Build the source and header files. */ + if (code_header()) + return (EXIT_FAILURE); + if (code_source()) + return (EXIT_FAILURE); + + return (EXIT_SUCCESS); +} + +/* + * desc_load -- + * Load a description file. + */ +int +desc_load() +{ + u_int field_alloc; + int version; + char *p, *t, save_ch, buf[256]; + + field_alloc = version = 0; + while (fgets(buf, sizeof(buf), stdin) != NULL) { + if ((p = strchr(buf, '\n')) == NULL) { + fprintf(stderr, "%s: input line too long\n", progname); + return (1); + } + *p = '\0'; + + /* Skip leading whitespace. */ + for (p = buf; isspace(*p); ++p) + ; + + /* Skip empty lines or lines beginning with '#'. */ + if (*p == '\0' || *p == '#') + continue; + + /* Get a version. */ + if (!version) { + if (strncasecmp( + p, "version", sizeof("version") - 1) == 0) { + version = 1; + continue; + } + fprintf(stderr, + "%s: expected \"version\" line\n", progname); + return (1); + } + + /* + * Skip block close -- not currently useful, but when this + * code supports versioned descriptions, it will matter. + */ + if (*p == '}') { + version = 0; + continue; + } + + /* Allocate a new field structure as necessary. */ + if (field_cnt == field_alloc && + (fields = realloc(fields, field_alloc += 100)) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return (1); + } + + /* Find the end of the field name. */ + for (t = p; *t != '\0' && !isspace(*t); ++t) + ; + save_ch = *t; + *t = '\0'; + if ((fields[field_cnt].name = strdup(p)) == NULL || + (fields[field_cnt].upper = strdup(p)) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(errno)); + return (1); + } + *t = save_ch; + p = t; + + fields[field_cnt].indx = 0; + fields[field_cnt].type = NOTSET; + for (;;) { + /* Skip to the next field, if any. */ + for (; *p != '\0' && isspace(*p); ++p) + ; + if (*p == '\0') + break; + + /* Find the end of the field. */ + for (t = p; *t != '\0' && !isspace(*t); ++t) + ; + save_ch = *t; + *t = '\0'; + if (strcasecmp(p, "double") == 0) + fields[field_cnt].type = DOUBLE; + else if (strcasecmp(p, "index") == 0) + fields[field_cnt].indx = 1; + else if (strcasecmp(p, "string") == 0) + fields[field_cnt].type = STRING; + else if (strcasecmp(p, "unsigned_long") == 0) + fields[field_cnt].type = UNSIGNED_LONG; + else { + fprintf(stderr, + "%s: unknown keyword: %s\n", progname, p); + return (1); + } + *t = save_ch; + p = t; + } + + /* Create a copy of the field name that's upper-case. */ + for (p = fields[field_cnt].upper; *p != '\0'; ++p) + if (islower(*p)) + *p = (char)toupper(*p); + ++field_cnt; + } + if (ferror(stdin)) { + fprintf(stderr, "%s: stdin: %s\n", progname, strerror(errno)); + return (1); + } + return (0); +} + +/* + * desc_dump -- + * Dump a set of FIELD structures. + */ +int +desc_dump() +{ + FIELD *f; + u_int i; + + for (f = fields, i = 0; i < field_cnt; ++i, ++f) { + fprintf(stderr, "field {%s}: (", f->name); + switch (f->type) { + case NOTSET: + fprintf(stderr, "ignored"); + break; + case DOUBLE: + fprintf(stderr, "double"); + break; + case STRING: + fprintf(stderr, "string"); + break; + case UNSIGNED_LONG: + fprintf(stderr, "unsigned_long"); + break; + } + if (f->indx) + fprintf(stderr, ", indexed"); + fprintf(stderr, ")\n"); + } + return (0); +} + +/* + * code_header -- + * Print out the C #include file. + */ +int +code_header() +{ + FIELD *f; + u_int i; + + fprintf(hfp, "/*\n"); + fprintf(hfp, " * DO NOT EDIT: automatically built by %s.\n", progname); + fprintf(hfp, " *\n"); + fprintf(hfp, " * Record structure.\n"); + fprintf(hfp, " */\n"); + fprintf(hfp, "typedef struct __DbRecord {\n"); + fprintf(hfp, "\tu_int32_t\t recno;\t\t/* Record number */\n"); + fprintf(hfp, "\n"); + fprintf(hfp, "\t/*\n"); + fprintf(hfp, "\t * Management fields\n"); + fprintf(hfp, "\t */\n"); + fprintf(hfp, "\tvoid\t\t*raw;\t\t/* Memory returned by DB */\n"); + fprintf(hfp, "\tu_char\t\t*record;\t/* Raw record */\n"); + fprintf(hfp, "\tsize_t\t\t record_len;\t/* Raw record length */\n\n"); + fprintf(hfp, "\tu_int32_t\t field_count;\t/* Field count */\n"); + fprintf(hfp, "\tu_int32_t\t version;\t/* Record version */\n\n"); + fprintf(hfp, "\tu_int32_t\t*offset;\t/* Offset table */\n"); + fprintf(hfp, "\n"); + + fprintf(hfp, "\t/*\n"); + fprintf(hfp, "\t * Indexed fields\n"); + fprintf(hfp, "\t */\n"); + for (f = fields, i = 0; i < field_cnt; ++i, ++f) { + if (f->type == NOTSET) + continue; + if (i != 0) + fprintf(hfp, "\n"); + fprintf(hfp, "#define CSV_INDX_%s\t%d\n", f->upper, i + 1); + switch (f->type) { + case NOTSET: + /* NOTREACHED */ + abort(); + break; + case DOUBLE: + fprintf(hfp, "\tdouble\t\t %s;\n", f->name); + break; + case STRING: + fprintf(hfp, "\tchar\t\t*%s;\n", f->name); + break; + case UNSIGNED_LONG: + fprintf(hfp, "\tu_long\t\t %s;\n", f->name); + break; + } + } + fprintf(hfp, "} DbRecord;\n"); + + return (0); +} + +/* + * code_source -- + * Print out the C structure initialization. + */ +int +code_source() +{ + FIELD *f; + u_int i; + + fprintf(cfp, "/*\n"); + fprintf(cfp, + " * DO NOT EDIT: automatically built by %s.\n", progname); + fprintf(cfp, " *\n"); + fprintf(cfp, " * Initialized record structure.\n"); + fprintf(cfp, " */\n"); + fprintf(cfp, "\n"); + fprintf(cfp, "#include \"csv.h\"\n"); + fprintf(cfp, "#include \"csv_local.h\"\n"); + fprintf(cfp, "\n"); + fprintf(cfp, "DbRecord DbRecord_base = {\n"); + fprintf(cfp, "\t0,\t\t/* Record number */\n"); + fprintf(cfp, "\tNULL,\t\t/* Memory returned by DB */\n"); + fprintf(cfp, "\tNULL,\t\t/* Raw record */\n"); + fprintf(cfp, "\t0,\t\t/* Raw record length */\n"); + fprintf(cfp, "\t%d,\t\t/* Field count */\n", field_cnt); + fprintf(cfp, "\t0,\t\t/* Record version */\n"); + fprintf(cfp, "\tNULL,\t\t/* Offset table */\n"); + fprintf(cfp, "\n"); + for (f = fields, i = 0; i < field_cnt; ++i, ++f) { + if (f->type == NOTSET) + continue; + switch (f->type) { + case NOTSET: + abort(); + /* NOTREACHED */ + break; + case DOUBLE: + case UNSIGNED_LONG: + fprintf(cfp, "\t0,\t\t/* %s */\n", f->name); + break; + case STRING: + fprintf(cfp, "\tNULL,\t\t/* %s */\n", f->name); + break; + } + } + fprintf(cfp, "};\n"); + + fprintf(cfp, "\n"); + fprintf(cfp, "DbField fieldlist[] = {\n"); + for (f = fields, i = 0; i < field_cnt; ++i, ++f) { + if (f->type == NOTSET) + continue; + fprintf(cfp, "\t{ \"%s\",", f->name); + fprintf(cfp, " CSV_INDX_%s,", f->upper); + fprintf(cfp, "\n\t %s,", type_to_string(f->type)); + fprintf(cfp, " %d,", f->indx ? 1 : 0); + fprintf(cfp, " NULL,"); + fprintf(cfp, " FIELD_OFFSET(%s)},\n", f->name); + } + fprintf(cfp, "\t{NULL, 0, STRING, 0, NULL, 0}\n};\n"); + + return (0); +} + +char * +type_to_string(type) + datatype type; +{ + switch (type) { + case NOTSET: + return ("NOTSET"); + case DOUBLE: + return ("DOUBLE"); + case STRING: + return ("STRING"); + case UNSIGNED_LONG: + return ("UNSIGNED_LONG"); + } + + abort(); + /* NOTREACHED */ +} + +int +usage() +{ + (void)fprintf(stderr, + "usage: %s [-v] [-c source-file] [-f input] [-h header-file]\n", + progname); + exit(1); +} diff --git a/db-4.8.30/examples_c/csv/csv.h b/db-4.8.30/examples_c/csv/csv.h new file mode 100644 index 0000000..fb0a66a --- /dev/null +++ b/db-4.8.30/examples_c/csv/csv.h @@ -0,0 +1,101 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 + +#include <direct.h> +#include <db.h> + +extern int getopt(int, char * const *, const char *); +extern char *optarg; +extern int optind; +#else +#define HAVE_WILDCARD_SUPPORT 1 + +#include <regex.h> +#include <unistd.h> +#endif + +#include "db.h" + +/* + * MAP_VERSION + * This code has hooks for versioning, but does not directly support it. + * See the README file for details. + */ +#define MAP_VERSION 1 + +/* + * Supported formats. + * + * FORMAT_NL: <nl> separated + * FORMAT_EXCEL: Excel dumped flat text. + */ +typedef enum { FORMAT_EXCEL, FORMAT_NL } input_fmt; + +/* + * OFFSET_LEN + * The length of any item can be calculated from the two offset fields. + * OFFSET_OOB + * An offset that's illegal, used to detect unavailable fields. + */ +#define OFFSET_LEN(offset, indx) \ + (((offset)[(indx) + 1] - (offset)[(indx)]) - 1) + +#define OFFSET_OOB 0 + +/* + * Field comparison operators. + */ +typedef enum { EQ=1, NEQ, GT, GTEQ, LT, LTEQ, WC, NWC } OPERATOR; + +/* + * Supported data types. + */ +typedef enum { NOTSET=1, DOUBLE, STRING, UNSIGNED_LONG } datatype; + +/* + * C structure that describes the csv fields. + */ +typedef struct { + char *name; /* Field name */ + u_int32_t fieldno; /* Field index */ + datatype type; /* Data type */ + + int indx; /* Indexed */ + DB *secondary; /* Secondary index handle */ + +#define FIELD_OFFSET(field) ((size_t)(&(((DbRecord *)0)->field))) + size_t offset; /* DbRecord field offset */ +} DbField; + +/* + * Globals + */ +extern DB *db; /* Primary database */ +extern DbField fieldlist[]; /* Field list */ +extern DB_ENV *dbenv; /* Database environment */ +extern char *progname; /* Program name */ +extern int verbose; /* Program verbosity */ +#ifdef _WIN32 +#undef strcasecmp +#define strcasecmp _stricmp +#undef strncasecmp +#define strncasecmp _strnicmp +#define mkdir(d, perm) _mkdir(d) +#endif diff --git a/db-4.8.30/examples_c/csv/csv_extern.h b/db-4.8.30/examples_c/csv/csv_extern.h new file mode 100644 index 0000000..8bd0653 --- /dev/null +++ b/db-4.8.30/examples_c/csv/csv_extern.h @@ -0,0 +1,37 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +extern DbRecord DbRecord_base; /* Initialized structure. */ + +/* + * Prototypes + */ +extern int DbRecord_discard(DbRecord *); +extern int DbRecord_init(const DBT *, const DBT *, DbRecord *); +extern void DbRecord_print(DbRecord *, FILE *); +extern int DbRecord_read(u_long, DbRecord *); +extern int DbRecord_search_field_name(char *, char *, OPERATOR); +extern int DbRecord_search_field_number(u_int, char *, OPERATOR); +extern int compare_double(DB *, const DBT *, const DBT *); +extern int compare_string(DB *, const DBT *, const DBT *); +extern int compare_ulong(DB *, const DBT *, const DBT *); +extern int csv_env_close(void); +extern int csv_env_open(const char *, int); +extern int csv_secondary_close(void); +extern int csv_secondary_open(void); +extern int entry_print(void *, size_t, u_int32_t); +extern int field_cmp_double(void *, void *, OPERATOR); +extern int field_cmp_re(void *, void *, OPERATOR); +extern int field_cmp_string(void *, void *, OPERATOR); +extern int field_cmp_ulong(void *, void *, OPERATOR); +extern int input_load(input_fmt, u_long); +extern int query(char *, int *); +extern int query_interactive(void); +extern int secondary_callback(DB *, const DBT *, const DBT *, DBT *); +extern int strtod_err(char *, double *); +extern int strtoul_err(char *, u_long *); diff --git a/db-4.8.30/examples_c/csv/db.c b/db-4.8.30/examples_c/csv/db.c new file mode 100644 index 0000000..efa9208 --- /dev/null +++ b/db-4.8.30/examples_c/csv/db.c @@ -0,0 +1,244 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +static int compare_uint32(DB *, const DBT *, const DBT *); + +/* + * csv_env_init -- + * Initialize the database environment. + */ +int +csv_env_open(const char *home, int is_rdonly) +{ + int ret; + + dbenv = NULL; + db = NULL; + + /* Create a database environment handle. */ + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_env_create: %s\n", progname, db_strerror(ret)); + return (1); + } + + /* + * Configure Berkeley DB error reporting to stderr, with our program + * name as the prefix. + */ + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, progname); + + /* + * The default Berkeley DB cache size is fairly small; configure a + * 1MB cache for now. This value will require tuning in the future. + */ + if ((ret = dbenv->set_cachesize(dbenv, 0, 1048576, 1)) != 0) { + dbenv->err(dbenv, ret, "DB_ENV->set_cachesize"); + return (1); + } + + /* + * We may be working with an existing environment -- try and join it. + * If that fails, create a new database environment; for now, we only + * need a cache, no logging, locking, or transactions. + */ + if ((ret = dbenv->open(dbenv, home, + DB_JOINENV | DB_USE_ENVIRON, 0)) != 0 && + (ret = dbenv->open(dbenv, home, + DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0) { + dbenv->err(dbenv, ret, "DB_ENV->open"); + return (1); + } + + /* Create the primary database handle. */ + if ((ret = db_create(&db, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (1); + } + + /* + * Records may be relatively large -- use a large page size. + */ + if ((ret = db->set_pagesize(db, 32 * 1024)) != 0) { + dbenv->err(dbenv, ret, "DB->set_pagesize"); + return (1); + } + + /* + * The primary database uses an integer as its key; on little-endian + * machines, integers sort badly using the default Berkeley DB sort + * function (which is lexicographic). Specify a comparison function + * for the database. + */ + if ((ret = db->set_bt_compare(db, compare_uint32)) != 0) { + dbenv->err(dbenv, ret, "DB->set_bt_compare"); + return (1); + } + + /* Open the primary database. */ + if ((ret = db->open(db, NULL, + "primary", NULL, DB_BTREE, is_rdonly ? 0 : DB_CREATE, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: primary"); + return (1); + } + + /* Open the secondaries. */ + if ((ret = csv_secondary_open()) != 0) + return (1); + + return (0); +} + +/* + * csv_env_close -- + * Discard the database environment. + */ +int +csv_env_close() +{ + int ret, t_ret; + + ret = 0; + + /* Close the secondaries. */ + ret = csv_secondary_close(); + + /* Close the primary handle. */ + if (db != NULL && (t_ret = db->close(db, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->close"); + if (ret == 0) + ret = t_ret; + } + if ((t_ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, + "%s: DB_ENV->close: %s\n", progname, db_strerror(ret)); + if (ret == 0) + ret = t_ret; + } + + return (ret); +} + +/* + * csv_secondary_open -- + * Open any secondary indices. + */ +int +csv_secondary_open() +{ + DB *sdb; + DbField *f; + int ret, (*fcmp)(DB *, const DBT *, const DBT *); + + /* + * Create secondary database handles. + */ + for (f = fieldlist; f->name != NULL; ++f) { + if (f->indx == 0) + continue; + + if ((ret = db_create(&sdb, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (1); + } + sdb->app_private = f; + + /* Keys are small, use a relatively small page size. */ + if ((ret = sdb->set_pagesize(sdb, 8 * 1024)) != 0) { + dbenv->err(dbenv, ret, "DB->set_pagesize"); + return (1); + } + + /* + * Sort the database based on the underlying type. Skip + * strings, Berkeley DB defaults to lexicographic sort. + */ + switch (f->type) { + case DOUBLE: + fcmp = compare_double; + break; + case UNSIGNED_LONG: + fcmp = compare_ulong; + break; + case NOTSET: + case STRING: + default: + fcmp = NULL; + break; + } + if (fcmp != NULL && + (ret = sdb->set_bt_compare(sdb, fcmp)) != 0) { + dbenv->err(dbenv, ret, "DB->set_bt_compare"); + return (1); + } + + /* Always configure secondaries for sorted duplicates. */ + if ((ret = sdb->set_flags(sdb, DB_DUPSORT)) != 0) { + dbenv->err(dbenv, ret, "DB->set_flags"); + return (1); + } + if ((ret = sdb->set_dup_compare(sdb, compare_ulong)) != 0) { + dbenv->err(dbenv, ret, "DB->set_dup_compare"); + return (1); + } + + if ((ret = sdb->open( + sdb, NULL, f->name, NULL, DB_BTREE, DB_CREATE, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: %s", f->name); + return (1); + } + if ((ret = sdb->associate( + db, NULL, sdb, secondary_callback, DB_CREATE)) != 0) { + dbenv->err(dbenv, ret, "DB->set_associate"); + return (1); + } + f->secondary = sdb; + } + + return (0); +} + +/* + * csv_secondary_close -- + * Close any secondary indices. + */ +int +csv_secondary_close() +{ + DbField *f; + int ret, t_ret; + + ret = 0; + for (f = fieldlist; f->name != NULL; ++f) + if (f->secondary != NULL && (t_ret = + f->secondary->close(f->secondary, 0)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} + +/* + * compare_uint32 -- + * Compare two keys. + */ +static int +compare_uint32(DB *db_arg, const DBT *a_arg, const DBT *b_arg) +{ + u_int32_t a, b; + + db_arg = db_arg; /* Quiet compiler. */ + + memcpy(&a, a_arg->data, sizeof(u_int32_t)); + memcpy(&b, b_arg->data, sizeof(u_int32_t)); + return (a > b ? 1 : ((a < b) ? -1 : 0)); +} diff --git a/db-4.8.30/examples_c/csv/load.c b/db-4.8.30/examples_c/csv/load.c new file mode 100644 index 0000000..34d8cc1 --- /dev/null +++ b/db-4.8.30/examples_c/csv/load.c @@ -0,0 +1,346 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +typedef enum { GL_OK, GL_EOF, GL_FAIL } getline_status; + +static int input_field_count(const char *, size_t, u_int32_t *); +static getline_status + input_getline(char **, size_t *, size_t *); +static int input_put_alloc(u_int32_t **, size_t *, size_t, u_int32_t); +static int input_set_offset(u_int32_t *, char *, size_t, u_int32_t); + +static input_fmt ifmt; /* Input format. */ +static u_long record_count = 0; /* Input record count for errors. */ +static u_long version; /* Version we're loading. */ + +/* + * input_load -- + * Read the input file and load new records into the database. + */ +int +input_load(input_fmt ifmt_arg, u_long version_arg) +{ + getline_status gtl_status; + DBT key, data; + DBC *cursor; + u_int32_t field_count, primary_key, *put_line; + size_t input_len, len, put_len; + int is_first, ret; + char *input_line; + + field_count = 0; /* Shut the compiler up. */ + + /* ifmt and version are global to this file. */ + ifmt = ifmt_arg; + version = version_arg; + + /* + * The primary key for the database is a unique number. Find out the + * last unique number allocated in this database by opening a cursor + * and fetching the last record. + */ + if ((ret = db->cursor(db, NULL, &cursor, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->cursor"); + return (1); + } + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + if ((ret = cursor->c_get(cursor, &key, &data, DB_LAST)) != 0) + if (ret == DB_NOTFOUND) + primary_key = 0; + else { + dbenv->err(dbenv, ret, "DB->cursor: DB_LAST"); + return (1); + } + else + memcpy(&primary_key, key.data, sizeof(primary_key)); + if ((ret = cursor->c_close(cursor)) != 0) { + dbenv->err(dbenv, ret, "DBC->close"); + return (1); + } + if (verbose) + dbenv->errx(dbenv, + "maximum existing record in the database is %lu", + (u_long)primary_key); + + key.data = &primary_key; + key.size = sizeof(primary_key); + input_line = NULL; + put_line = NULL; + input_len = put_len = 0; + + /* + * See the README file for a description of the file input format. + */ + for (is_first = 1; (gtl_status = + input_getline(&input_line, &input_len, &len)) == GL_OK;) { + ++record_count; + if (verbose > 1) + dbenv->errx(dbenv, "reading %lu", (u_long)record_count); + + /* The first non-blank line of the input is a column map. */ + if (is_first) { + is_first = 0; + + /* Count the fields we're expecting in the input. */ + if (input_field_count( + input_line, len, &field_count) != 0) + return (1); + + } + + /* Allocate room for the table of offsets. */ + if (input_put_alloc( + &put_line, &put_len, len, field_count) != 0) + return (1); + + /* + * Build the offset table and create the record we're + * going to store. + */ + if (input_set_offset(put_line, + input_line, len, field_count) != 0) + return (1); + + ++primary_key; + + memcpy(put_line + (field_count + 2), input_line, len); + data.data = put_line; + data.size = (field_count + 2) * sizeof(u_int32_t) + len; + + if (verbose > 1) + (void)entry_print( + data.data, data.size, field_count); + + /* Load the key/data pair into the database. */ + if ((ret = db->put(db, NULL, &key, &data, 0)) != 0) { + dbenv->err(dbenv, ret, + "DB->put: %lu", (u_long)primary_key); + return (1); + } + } + + if (gtl_status != GL_EOF) + return (1); + + if (verbose) + dbenv->errx(dbenv, + "%lu records read from the input file into the database", + record_count); + + /* + * This program isn't transactional, limit the window for corruption. + */ + if ((ret = db->sync(db, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->sync"); + return (1); + } + + return (0); +} + +/* + * input_getline -- + * Read in a line of input into a buffer. + */ +static getline_status +input_getline(char **input_linep, size_t *input_lenp, size_t *lenp) +{ + size_t input_len, len; + int ch; + char *input_line, *p, *endp; + + input_line = *input_linep; + input_len = *input_lenp; + + p = input_line; + endp = input_line + input_len; + + for (len = 0; (ch = getchar()) != EOF;) { + if (ch == '\0') /* Strip <nul> (\000) bytes. */ + continue; + switch (ifmt) { + case FORMAT_NL: + if (ch == '\n') + goto end; + break; + case FORMAT_EXCEL: + /* Strip <nl> (\012) bytes. */ + if (ch == '\n') + continue; + /* + * <cr> (\015) bytes terminate lines. + * Skip blank lines. + */ + if (ch == '\015') { + if (len == 0) + continue; + goto end; + } + } + if (input_line == endp) { + input_len += 256; + input_len *= 2; + if ((input_line = + realloc(input_line, input_len)) == NULL) { + dbenv->err(dbenv, errno, + "unable to allocate %lu bytes for record", + (u_long)input_len); + return (GL_FAIL); + } + p = input_line; + endp = p + input_len; + } + + if (isprint(ch)) { /* Strip unprintables. */ + *p++ = (char)ch; + ++len; + } + } + +end: if (len == 0) + return (GL_EOF); + + *lenp = len; + *input_linep = input_line; + *input_lenp = input_len; + + return (GL_OK); +} + +/* + * input_field_count -- + * Count the fields in the line. + */ +static int +input_field_count(const char *line, size_t len, u_int32_t *field_countp) +{ + u_int32_t field_count; + int quoted; + + field_count = 1; + + /* + * There are N-1 separators for N fields, that is, "a,b,c" is three + * fields, with two comma separators. + */ + switch (ifmt) { + case FORMAT_EXCEL: + quoted = 0; + for (field_count = 1; len > 0; ++line, --len) + if (*line == '"') + quoted = !quoted; + else if (*line == ',' && !quoted) + ++field_count; + break; + case FORMAT_NL: + for (field_count = 1; len > 0; ++line, --len) + if (*line == ',') + ++field_count; + break; + } + *field_countp = field_count; + + if (verbose) + dbenv->errx(dbenv, + "input file made up of %lu fields", (u_int)field_count); + + return (0); +} + +/* + * input_put_alloc -- + * Allocate room for the offset table plus the input. + */ +static int +input_put_alloc(u_int32_t **put_linep, + size_t *put_lenp, size_t len, u_int32_t field_count) +{ + size_t total; + + total = (field_count + 2) * sizeof(u_int32_t) + len; + if (total > *put_lenp && + (*put_linep = realloc(*put_linep, *put_lenp += total)) == NULL) { + dbenv->err(dbenv, errno, + "unable to allocate %lu bytes for record", + (u_long)*put_lenp); + return (1); + } + return (0); +} + +/* + * input_set_offset -- + * Build an offset table and record combination. + */ +static int +input_set_offset(u_int32_t *put_line, + char *input_line, size_t len, u_int32_t field_count) +{ + u_int32_t *op; + int quoted; + char *p, *endp; + + op = put_line; + + /* The first field is the version number. */ + *op++ = version; + + /* + * Walk the input line, looking for comma separators. It's an error + * to have too many or too few fields. + */ + *op++ = 0; + quoted = 0; + for (p = input_line, endp = input_line + len;; ++p) { + if (ifmt == FORMAT_EXCEL && p < endp) { + if (*p == '"') + quoted = !quoted; + if (quoted) + continue; + } + if (*p == ',' || p == endp) { + if (field_count == 0) { + dbenv->errx(dbenv, + "record %lu: too many fields in the record", + record_count); + return (1); + } + --field_count; + + *op++ = (u_int32_t)(p - input_line) + 1; + + if (verbose > 1) + dbenv->errx(dbenv, + "offset %lu: {%.*s}", op[-1], + OFFSET_LEN(op, -2), input_line + op[-2]); + + /* + * Don't insert a new field if the input lines ends + * in a comma. + */ + if (p == endp || p + 1 == endp) + break; + } + } + *op++ = (u_int32_t)(p - input_line); + + if (field_count != 0) { + dbenv->errx(dbenv, + "record %lu: not enough fields in the record", + record_count); + return (1); + } + memcpy(op, input_line, len); + + return (0); +} diff --git a/db-4.8.30/examples_c/csv/load_main.c b/db-4.8.30/examples_c/csv/load_main.c new file mode 100644 index 0000000..4ff4dd4 --- /dev/null +++ b/db-4.8.30/examples_c/csv/load_main.c @@ -0,0 +1,117 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +static int usage(void); + +/* + * Globals + */ +DB_ENV *dbenv; /* Database environment */ +DB *db; /* Primary database */ +DB **secondary; /* Secondaries */ +int verbose; /* Program verbosity */ +char *progname; /* Program name */ + +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; + + /* 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': + if (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) + 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); +} + +/* + * usage -- + * Program usage message. + */ +static int +usage(void) +{ + (void)fprintf(stderr, + "usage: %s [-v] [-F excel] [-f csv-file] [-h home]\n", progname); + return (EXIT_FAILURE); +} diff --git a/db-4.8.30/examples_c/csv/query.c b/db-4.8.30/examples_c/csv/query.c new file mode 100644 index 0000000..c6eee8c --- /dev/null +++ b/db-4.8.30/examples_c/csv/query.c @@ -0,0 +1,241 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +static int query_by_field(char *); +static int query_fieldlist(char *); +static int query_help(char *); +static int query_usage(void); + +typedef struct _cmdtab { + char *cmd; /* Command name */ + int (*f)(char *); /* Underlying function. */ + char *help; /* Help message. */ +} CMDTAB; + +static CMDTAB cmdtab[] = { + { "?", + query_help, + "?\t\tDisplay help screen" }, + { "exit", + NULL, + "exit\t\tExit program" }, + { "fields", + query_fieldlist, + "fields\t\tDisplay list of field names" }, + { "help", + query_help, + "help\t\tDisplay help screen" }, + { "quit", + NULL, + "quit\t\tExit program" }, + { NULL, + query_by_field, + "field[op]value\tDisplay fields by value (=, !=, <, <=, >, >=, ~, !~)" }, + { NULL, NULL, NULL } +}; + +/* + * query_interactive -- + * Allow the user to interactively query the database. + */ +int +query_interactive() +{ + int done; + char *p, input[256]; + + for (;;) { + printf("Query: "); + (void)fflush(stdout); + if (fgets(input, sizeof(input), stdin) == NULL) { + printf("\n"); + if (ferror(stdin)) { + dbenv->err(dbenv, errno, + "error occurred reading from stdin"); + return (1); + } + break; + } + if ((p = strchr(input, '\n')) == NULL) { + dbenv->errx(dbenv, "input buffer too small"); + return (1); + } + *p = '\0'; + if (query(input, &done) != 0) + return (1); + if (done != 0) + break; + } + return (0); +} + +/* + * query -- + * Process a query. + */ +int +query(char *cmd, int *donep) +{ + CMDTAB *p; + + if (donep != NULL) + *donep = 0; + + for (p = cmdtab; p->cmd != NULL; ++p) + if (p->cmd != NULL && + strncasecmp(cmd, p->cmd, strlen(p->cmd)) == 0) + break; + + if (p->cmd == NULL) + return (query_by_field(cmd)); + + if (p->f == NULL) { + if (donep != NULL) + *donep = 1; + return (0); + } + + return (p->f(cmd)); +} + +/* + * query_by_field -- + * Query the primary database by field. + */ +static int +query_by_field(char *input) +{ + OPERATOR operator; + size_t len; + char *field, *op, *value; + + /* + * We expect to see "field [op] value" -- figure it out. + * + * Skip leading whitespace. + */ + while (isspace(*input)) + ++input; + + /* + * Find an operator, and it better not start the string. + */ + if ((len = strcspn(field = input, "<>!=~")) == 0) + return (query_usage()); + op = field + len; + + /* Figure out the operator, and find the start of the value. */ + switch (op[0]) { + case '~': + operator = WC; + value = op + 1; + break; + case '!': + if (op[1] == '=') { + operator = NEQ; + value = op + 2; + break; + } + if (op[1] == '~') { + operator = NWC; + value = op + 2; + break; + } + return (query_usage()); + case '<': + if (op[1] == '=') { + operator = LTEQ; + value = op + 2; + } else { + operator = LT; + value = op + 1; + } + break; + case '=': + operator = EQ; + if (op[1] == '=') + value = op + 2; + else + value = op + 1; + break; + case '>': + if (op[1] == '=') { + operator = GTEQ; + value = op + 2; + } else { + operator = GT; + value = op + 1; + } + break; + default: + return (query_usage()); + } + + /* Terminate the field name, and there better be a field name. */ + while (--op > input && isspace(*op)) + ; + if (op == input) + return (query_usage()); + op[1] = '\0'; + + /* Make sure there is a value field. */ + while (isspace(*value)) + ++value; + if (*value == '\0') + return (query_usage()); + + return (DbRecord_search_field_name(field, value, operator)); +} + +/* + * query_fieldlist -- + * Display list of field names. + */ +static int +query_fieldlist(char *input) +{ + DbField *f; + + input = input; /* Quiet compiler. */ + + for (f = fieldlist; f->name != NULL; ++f) + printf("field %3d: %s\n", f->fieldno, f->name); + return (0); +} + +/* + * query_help -- + * Query command list. + */ +static int +query_help(char *input) +{ + CMDTAB *p; + + input = input; /* Quiet compiler. */ + + printf("Query commands:\n"); + for (p = cmdtab; p->help != NULL; ++p) + printf("\t%s\n", p->help); + return (0); +} + +/* + * query_usage -- + * Query usage message. + */ +static int +query_usage(void) +{ + fprintf(stderr, "%s: query syntax error\n", progname); + return (query_help(NULL)); +} diff --git a/db-4.8.30/examples_c/csv/query_main.c b/db-4.8.30/examples_c/csv/query_main.c new file mode 100644 index 0000000..30376cd --- /dev/null +++ b/db-4.8.30/examples_c/csv/query_main.c @@ -0,0 +1,99 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +static int usage(void); + +/* + * Globals + */ +DB_ENV *dbenv; /* Database environment */ +DB *db; /* Primary database */ +int verbose; /* Program verbosity */ +char *progname; /* Program name */ + +int +main(int argc, char *argv[]) +{ + int ch, done, ret, t_ret; + char **clist, **clp, *home; + + /* Initialize globals. */ + dbenv = NULL; + db = NULL; + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + ++progname; + verbose = 0; + + /* Initialize arguments. */ + home = NULL; + ret = 0; + + /* Allocate enough room for command-list arguments. */ + if ((clp = clist = + (char **)calloc((size_t)argc + 1, sizeof(char *))) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); + return (EXIT_FAILURE); + } + + /* Process arguments. */ + while ((ch = getopt(argc, argv, "c:h:v")) != EOF) + switch (ch) { + case 'c': + *clp++ = optarg; + break; + case 'h': + home = optarg; + break; + case 'v': + ++verbose; + break; + case '?': + default: + return (usage()); + } + argc -= optind; + argv += optind; + + if (*argv != NULL) + return (usage()); + + /* Create or join the database environment. */ + if (csv_env_open(home, 1) != 0) + return (EXIT_FAILURE); + + /* Handle the queries. */ + if (clp == clist) + ret = query_interactive(); + else + for (clp = clist, done = 0; *clp != NULL && !done; ++clp) + if ((ret = query(*clp, &done)) != 0) + break; + + /* Close the database environment. */ + if ((t_ret = csv_env_close()) != 0 && ret == 0) + ret = t_ret; + + return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* + * usage -- + * Program usage message. + */ +static int +usage(void) +{ + (void)fprintf(stderr, "usage: %s [-v] [-c cmd] [-h home]\n", progname); + return (EXIT_FAILURE); +} diff --git a/db-4.8.30/examples_c/csv/sample.csv b/db-4.8.30/examples_c/csv/sample.csv new file mode 100644 index 0000000..b3f0970 --- /dev/null +++ b/db-4.8.30/examples_c/csv/sample.csv @@ -0,0 +1,8 @@ +Adams,Bob,01/02/03,green,apple,37 +Carter,Denise Ann,04/05/06,blue,banana,38 +Eidel,Frank,07/08/09,red,cherry,38 +Grabel,Harriet,10/11/12,purple,date,40 +Indals,Jason,01/03/05,pink,orange,32 +Kilt,Laura,07/09/11,yellow,grape,38 +Moreno,Nancy,02/04/06,black,strawberry,38 +Octon,Patrick,08/10/12,magenta,kiwi,15 diff --git a/db-4.8.30/examples_c/csv/sample.desc b/db-4.8.30/examples_c/csv/sample.desc new file mode 100644 index 0000000..65aa939 --- /dev/null +++ b/db-4.8.30/examples_c/csv/sample.desc @@ -0,0 +1,10 @@ +# $Id$ + +version 1 { + LastName string + FirstName string + BirthDate + Color string index + Fruit string index + Age unsigned_long index +} diff --git a/db-4.8.30/examples_c/csv/util.c b/db-4.8.30/examples_c/csv/util.c new file mode 100644 index 0000000..eed03f0 --- /dev/null +++ b/db-4.8.30/examples_c/csv/util.c @@ -0,0 +1,309 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2005-2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#include "csv.h" +#include "csv_local.h" +#include "csv_extern.h" + +/* + * entry_print -- + * Display the primary database's data item. + */ +int +entry_print(void *data, size_t len, u_int32_t field_count) +{ + u_int32_t a, *offset; + u_int i; + char *raw; + + memcpy(&a, data, sizeof(u_int32_t)); + printf("\tversion: %lu\n", (u_long)a); + + offset = (u_int32_t *)data + 1; + if (field_count == 0) { + memcpy(&a, offset++, sizeof(u_int32_t)); + printf("\tcolumn map: %lu fields: {%.*s}\n", (u_long)a, + (int)(len - 2 * sizeof(u_int32_t)), + (u_int8_t *)data + 2 * sizeof(u_int32_t)); + } else { + raw = (char *)(offset + (field_count + 1)); + for (i = 0; i < field_count; ++i) { + memcpy(&a, &offset[i], sizeof(u_int32_t)); + len = OFFSET_LEN(offset, i); + printf("\toffset %4lu: len %4lu: {%.*s}\n", + (u_long)offset[i], + (u_long)len, (int)len, raw + a); + } + } + + return (0); +} + +/* + * strtod_err -- + * strtod(3) with error checking. + */ +int +strtod_err(char *input, double *valp) +{ + double val; + char *end; + + /* + * strtoul requires setting errno to detect errors. + */ + errno = 0; + val = strtod(input, &end); + if (errno == ERANGE) { + dbenv->err(dbenv, ERANGE, "%s", input); + return (1); + } + if (input[0] == '\0' || + (end[0] != '\0' && end[0] != '\n' && !isspace(end[0]))) { + dbenv->errx(dbenv, + "%s: invalid floating point argument", input); + return (1); + } + + *valp = val; + return (0); +} + +/* + * strtoul_err -- + * strtoul(3) with error checking. + */ +int +strtoul_err(char *input, u_long *valp) +{ + u_long val; + char *end; + + /* + * strtoul requires setting errno to detect errors. + */ + errno = 0; + val = strtoul(input, &end, 10); + if (errno == ERANGE) { + dbenv->err(dbenv, ERANGE, "%s", input); + return (1); + } + if (input[0] == '\0' || + (end[0] != '\0' && end[0] != '\n' && !isspace(end[0]))) { + dbenv->errx(dbenv, "%s: invalid unsigned long argument", input); + return (1); + } + + *valp = val; + return (0); +} + +int +secondary_callback(DB *db_arg, const DBT *key, const DBT *data, DBT *result) +{ + DbField *f; + DbRecord record; + void *faddr, *addr; + + /* Populate the field. */ + if (DbRecord_init(key, data, &record) != 0) + return (-1); + + f = db_arg->app_private; + faddr = (u_int8_t *)&record + f->offset; + + /* + * If necessary, copy the field into separate memory. + * Set up the result DBT. + */ + switch (f->type) { + case STRING: + result->data = *(char **)faddr; + result->size = strlen(*(char **)faddr) + 1; + break; + case DOUBLE: + if ((addr = malloc(sizeof(double))) == NULL) + return (-1); + result->data = addr; + result->size = sizeof(double); + result->flags = DB_DBT_APPMALLOC; + memcpy(addr, faddr, sizeof(double)); + break; + case UNSIGNED_LONG: + if ((addr = malloc(sizeof(u_long))) == NULL) + return (-1); + result->data = addr; + result->size = sizeof(u_long); + result->flags = DB_DBT_APPMALLOC; + memcpy(addr, faddr, sizeof(u_long)); + break; + default: + case NOTSET: + abort(); + /* NOTREACHED */ + } + + return (0); +} + +/* + * compare_double -- + * Compare two keys. + */ +int +compare_double(DB *db_arg, const DBT *a_arg, const DBT *b_arg) +{ + double a, b; + + db_arg = db_arg; /* Quiet compiler. */ + + memcpy(&a, a_arg->data, sizeof(double)); + memcpy(&b, b_arg->data, sizeof(double)); + return (a > b ? 1 : ((a < b) ? -1 : 0)); +} + +/* + * compare_ulong -- + * Compare two keys. + */ +int +compare_ulong(DB *db_arg, const DBT *a_arg, const DBT *b_arg) +{ + u_long a, b; + + db_arg = db_arg; /* Quiet compiler. */ + + memcpy(&a, a_arg->data, sizeof(u_long)); + memcpy(&b, b_arg->data, sizeof(u_long)); + return (a > b ? 1 : ((a < b) ? -1 : 0)); +} + +/* + * field_cmp_double -- + * Compare two double. + */ +int +field_cmp_double(void *a, void *b, OPERATOR op) +{ + switch (op) { + case GT: + return (*(double *)a > *(double *)b); + case GTEQ: + return (*(double *)a >= *(double *)b); + case LT: + return (*(double *)a < *(double *)b); + case LTEQ: + return (*(double *)a <= *(double *)b); + case NEQ: + return (*(double *)a != *(double *)b); + case EQ: + return (*(double *)a == *(double *)b); + case WC: + case NWC: + break; + } + + abort(); + /* NOTREACHED */ +} + +/* + * field_cmp_re -- + * Compare against regular expression. + */ +int +field_cmp_re(void *a, void *b, OPERATOR op) +{ + op = op; /* Quiet compiler. */ + + switch (op) { +#ifdef HAVE_WILDCARD_SUPPORT + case WC: + return (regexec(b, *(char **)a, 0, NULL, 0) == 0); + case NWC: + return (regexec(b, *(char **)a, 0, NULL, 0) != 0); +#else + case WC: + case NWC: + a = a; + b = b; /* Quiet compiler. */ + /* FALLTHROUGH */ +#endif + case GT: + case GTEQ: + case LT: + case LTEQ: + case NEQ: + case EQ: + break; + } + + abort(); + /* NOTREACHED */ +} + +/* + * field_cmp_string -- + * Compare two strings. + */ +int +field_cmp_string(void *a, void *b, OPERATOR op) +{ + int v; + + v = strcasecmp(*(char **)a, b); + switch (op) { + case GT: + return (v > 0 ? 1 : 0); + case GTEQ: + return (v >= 0 ? 1 : 0); + case LT: + return (v < 0 ? 1 : 0); + case LTEQ: + return (v <= 0 ? 1 : 0); + case NEQ: + return (v ? 1 : 0); + case EQ: + return (v ? 0 : 1); + case WC: + case NWC: + break; + } + + abort(); + /* NOTREACHED */ +} + +/* + * field_cmp_ulong -- + * Compare two ulongs. + */ +int +field_cmp_ulong(void *a, void *b, OPERATOR op) +{ + switch (op) { + case GT: + return (*(u_long *)a > *(u_long *)b); + case GTEQ: + return (*(u_long *)a >= *(u_long *)b); + case LT: + return (*(u_long *)a < *(u_long *)b); + case LTEQ: + return (*(u_long *)a <= *(u_long *)b); + case NEQ: + return (*(u_long *)a != *(u_long *)b); + case EQ: + return (*(u_long *)a == *(u_long *)b); + case WC: + case NWC: + break; + } + + abort(); + /* NOTREACHED */ +} |