From fb576534d8c5179832c9d56544132b8528fcf2ba Mon Sep 17 00:00:00 2001 From: brianb Date: Fri, 13 Oct 2000 21:33:04 +0000 Subject: [PATCH] > 255 column patches and multipage table defs from Tim Nelson mdb-export fixes spec file --- AUTHORS | 11 ++++++ README | 2 +- mdbtools.spec | 39 ++++++++++++++++++++ src/include/mdbtools.h | 1 + src/libmdb/backend.c | 2 +- src/libmdb/data.c | 80 +++++++++++++++++++++++++++-------------- src/libmdb/file.c | 21 +++++++++++ src/libmdb/table.c | 81 ++++++++++++++++++++++++++++++++++-------- src/util/mdb-export.c | 44 ++++++++++++++++++----- 9 files changed, 229 insertions(+), 52 deletions(-) create mode 100644 mdbtools.spec diff --git a/AUTHORS b/AUTHORS index 44fdd03..2407b2a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,9 +1,20 @@ Brian Bruns + Started MDB Tools Karl Nyberg + Lots and lots of bug fixes and functionality Georg Bauer + Lots and lots of bug fixes and functionality Carl Seutter + Backend schema export, other stuff Trevor Harrison + password field, etc... + +Brent Johnson + large MEMO fields, deleted rows, double datatype + +Tim Nelson + Multipage table defs, column totals > 255 diff --git a/README b/README index d29df6a..70f02c4 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -This is mdbtools version 0.1 +This is mdbtools version 0.2 This software is alpha so don't expect too much unless you know C and probably a little something about databases and reverse engineering file diff --git a/mdbtools.spec b/mdbtools.spec new file mode 100644 index 0000000..067840b --- /dev/null +++ b/mdbtools.spec @@ -0,0 +1,39 @@ +Summary: Several utilities for using MS-Access .mdb files. +Name: mdbtools +Version: 0.2 +Release: 1 +Copyright: GPL +Group: Development/Tools +Source: http://download.sourceforge.net/mdbtools/mdbtools-0.2.tgz + +%description +mdb-dump -- simple hex dump utility for looking at mdb files +mdb-schema -- prints DDL for the specified table +mdb-export -- export table to CSV format +mdb-tables -- a simple dump of table names to be used with shell scripts +mdb-header -- generates a C header to be used in exporting mdb data to a C prog. +mdb-parsecvs -- generates a C program given a CSV file made with mdb-export + +%prep +%setup + +%build +cd src/ +./configure +make + +%install +cd src/ +make install + +%files +%doc AUTHORS COPYING COPYING.LIB HACKERS INSTALL README TODO +/usr/local/lib/libmdb.a +/usr/local/bin/mdb-schema +/usr/local/bin/mdb-export +/usr/local/bin/mdb-tables +/usr/local/bin/mdb-header +/usr/local/bin/mdb-parsecsv +/usr/local/bin/mdb-dump +/usr/local/include/mdbtools.h + diff --git a/src/include/mdbtools.h b/src/include/mdbtools.h index 5563882..7418819 100644 --- a/src/include/mdbtools.h +++ b/src/include/mdbtools.h @@ -31,6 +31,7 @@ #define MDB_PGSIZE 2048 #define MDB_MAX_OBJ_NAME 30 +#define MDB_MAX_COLS 256 #define MDB_CATALOG_PG 18 #define MDB_MEMO_OVERHEAD 12 #define MDB_BIND_SIZE 2048 diff --git a/src/libmdb/backend.c b/src/libmdb/backend.c index d6f9877..127a9bb 100644 --- a/src/libmdb/backend.c +++ b/src/libmdb/backend.c @@ -99,7 +99,7 @@ char *mdb_postgres_types[] = "Postgres_Unknown 0x0e", "Serial"}; -char *bound_values[256]; +char *bound_values[MDB_MAX_COLS]; char *relationships[4]; MdbColumn *col; MdbCatalogEntry entry; diff --git a/src/libmdb/data.c b/src/libmdb/data.c index 540eab9..b3c8945 100644 --- a/src/libmdb/data.c +++ b/src/libmdb/data.c @@ -35,21 +35,22 @@ int mdb_find_end_of_row(MdbHandle *mdb, int row) { int row_start, row_end, i; - /* Search the previous "row start" values for the first non-deleted one. - * If we don't find one, then the end of the page is the correct value. - */ - for (i = row - 1; i >= 0; i--) { - row_start = mdb_get_int16(mdb, (10 + i * 2)); - if (!(row_start & 0x8000)) { - break; - } - } + /* Search the previous "row start" values for the first non-deleted +one. + * If we don't find one, then the end of the page is the correct value. + */ + for (i = row - 1; i >= 0; i--) { + row_start = mdb_get_int16(mdb, (10 + i * 2)); + if (!(row_start & 0x8000)) { + break; + } + } - if (i == -1) { - row_end = mdb->pg_size - 1; - } else { - row_end = row_start - 1; - } + if (i == -1) { + row_end = mdb->pg_size - 1; + } else { + row_end = row_start - 1; + } return row_end; } @@ -86,6 +87,7 @@ int num_cols, var_cols, fixed_cols; int row_start, row_end; int fixed_cols_found, var_cols_found; int col_start, len; +int num_of_jumps=0, jumps_used=0; int eod; /* end of data */ int delflag, lookupflag; int bitmask_sz; @@ -105,7 +107,8 @@ unsigned char null_mask[33]; /* 256 columns max / 8 bits per byte */ delflag ? "[delflag]" : ""); #endif if (delflag || lookupflag) { - return -1; + row_end = row_start-1; + return 0; } #if MDB_DEBUG @@ -153,12 +156,37 @@ unsigned char null_mask[33]; /* 256 columns max / 8 bits per byte */ } } + if (col_start >= 256) { + num_of_jumps++; + jumps_used++; + row_start = row_start + col_start - (col_start % 256); + } + + col_start = row_start; + while (col_start+256 < row_end-bitmask_sz-1-var_cols-num_of_jumps){ + col_start += 256; + num_of_jumps++; + } + eod = mdb->pg_buf[row_end-1-var_cols-bitmask_sz-num_of_jumps]; + + col_start = mdb->pg_buf[row_end-bitmask_sz-1-num_of_jumps]; + + /* variable columns */ for (j=0;jnum_cols;j++) { col = g_ptr_array_index(table->columns,j); if (!mdb_is_fixed_col(col) && ++var_cols_found <= var_cols) { - col_start = mdb->pg_buf[row_end-bitmask_sz-var_cols_found]; + /* col_start = mdb->pg_buf[row_end-bitmask_sz-var_cols_found]; */ + /* more code goes here but the diff is mangled */ + + if (var_cols_found == mdb->pg_buf[row_end-bitmask_sz-jumps_used-1] && + jumps_used < num_of_jumps) { + row_start += 256; + col_start -= 256; + jumps_used++; + } + if (var_cols_found==var_cols) len=eod - col_start; @@ -166,7 +194,7 @@ unsigned char null_mask[33]; /* 256 columns max / 8 bits per byte */ len=mdb->pg_buf[row_end - bitmask_sz - var_cols_found - - 1 ] - col_start; + - 1 - num_of_jumps ] - col_start; if (mdb_is_null(null_mask, j+1)) { mdb_xfer_bound_data(mdb, 0, col, 0); @@ -177,7 +205,7 @@ unsigned char null_mask[33]; /* 256 columns max / 8 bits per byte */ } } - return 0; + return 1; } int mdb_read_next_dpg(MdbTableDef *table) { @@ -219,14 +247,8 @@ int rows; if (!mdb_read_next_dpg(table)) return 0; } - /* Skip over any deleted rows. - * mdb_read_row() returns -1 on deleted rows. - * mdb_read_row() returns 0 on actual rows. - */ - while(mdb_read_row(table, - table->cur_row)) { - table->cur_row++; - } + mdb_read_row(table, + table->cur_row); table->cur_row++; return 1; @@ -236,7 +258,7 @@ void mdb_data_dump(MdbTableDef *table) MdbHandle *mdb = table->entry->mdb; int i, j, pg_num; int rows; -char *bound_values[256]; /* warning doesn't handle tables > 256 columns. Can that happen? */ +char *bound_values[MDB_MAX_COLS]; for (i=0;inum_cols;i++) { bound_values[i] = (char *) malloc(256); @@ -316,6 +338,10 @@ static char text[MDB_BIND_SIZE]; sprintf(text,"%ld",mdb_get_int32(mdb, start)); return text; break; + case MDB_FLOAT: + sprintf(text,"%f",mdb_get_double(mdb, start)); + return text; + break; case MDB_DOUBLE: sprintf(text,"%f",mdb_get_double(mdb, start)); return text; diff --git a/src/libmdb/file.c b/src/libmdb/file.c index cde9525..37f58e1 100644 --- a/src/libmdb/file.c +++ b/src/libmdb/file.c @@ -135,6 +135,27 @@ unsigned char *c; mdb->cur_pos+=4; return l; } +float mdb_get_single(MdbHandle *mdb, int offset) +{ +float f, f2; +unsigned char *c; +int i; + + if (offset <0 || offset+4 > mdb->pg_size) return -1; + + memcpy(&f, &mdb->pg_buf[offset], 4); + +#ifdef HW_BIG_ENDIAN + f2 = f; + for (i=0; icur_pos+=4; + return f; +} + double mdb_get_double(MdbHandle *mdb, int offset) { double d, d2; diff --git a/src/libmdb/table.c b/src/libmdb/table.c index 8061c67..d85f5ee 100644 --- a/src/libmdb/table.c +++ b/src/libmdb/table.c @@ -48,41 +48,92 @@ int len, i; return table; } +/* +** read the next page if offset is > pg_size +** return true if page was read +*/ +static int read_pg_if(MdbHandle *mdb, int *cur_pos, int offset) +{ + if (*cur_pos + offset >= mdb->pg_size) { + mdb_read_pg(mdb, mdb_get_int32(mdb,4)); + *cur_pos = 8 - (mdb->pg_size - (*cur_pos)); + return 1; + } + return 0; +} + GPtrArray *mdb_read_columns(MdbTableDef *table) { MdbHandle *mdb = table->entry->mdb; -MdbColumn col; +MdbColumn col, *pcol; int len, i; +unsigned char low_byte, high_byte; int cur_col, cur_name; int col_type, col_size; -int col_start, name_start; char name[MDB_MAX_OBJ_NAME+1]; int name_sz; table->columns = g_ptr_array_new(); - col_start = 43 + (table->num_pgs * 8); - name_start = col_start + (table->num_cols * 18); + cur_col = 43 + (table->num_pgs * 8); - cur_col = col_start; - cur_name = name_start; + /* new code based on patch submitted by Tim Nelson 2000.09.27 */ - for (i=0;inum_cols;i++) { + /* + ** column attributes + */ + for (i=0; inum_cols;i++) { memset(&col,'\0', sizeof(MdbColumn)); + read_pg_if(mdb, &cur_col, 0); col.col_type = mdb->pg_buf[cur_col]; - col.is_fixed = mdb->pg_buf[cur_col+13] & 0x01 ? 1 : 0; - col.col_size = mdb_get_int16(mdb,cur_col+16); - /* get the name */ - name_sz = mdb->pg_buf[cur_name]; - memcpy(col.name,&mdb->pg_buf[cur_name+1],name_sz); - col.name[name_sz]='\0'; - cur_col += 18; - cur_name += name_sz + 1; + read_pg_if(mdb, &cur_col, 13); + col.is_fixed = mdb->pg_buf[cur_col+13] & 0x01 ? 1 : 0; + + read_pg_if(mdb, &cur_col, 17); + low_byte = mdb->pg_buf[cur_col+16]; + read_pg_if(mdb, &cur_col, 18); + high_byte = mdb->pg_buf[cur_col+17]; + col.col_size += high_byte * 256 + low_byte; + mdb_append_column(table->columns, &col); + cur_col += 18; } + cur_name = cur_col; + + /* + ** column names + */ + for (i=0;inum_cols;i++) { + /* fetch the column */ + pcol = g_ptr_array_index (table->columns, i); + + /* we have reached the end of page */ + read_pg_if(mdb, &cur_name, 0); + name_sz = mdb->pg_buf[cur_name]; + + /* determine amount of name on this page */ + len = ((cur_name + name_sz) > mdb->pg_size) ? + mdb->pg_size - cur_name : + name_sz; + + if (len) { + memcpy(pcol->name, &mdb->pg_buf[cur_name+1], len); + } + /* name wrapped over page */ + if (len < name_sz) { + /* read the next pg */ + mdb_read_pg(mdb, mdb_get_int32(mdb,4)); + cur_name = 8 - (mdb->pg_size - cur_name); + /* get the rest of the name */ + memcpy(&pcol->name[len], &mdb->pg_buf[cur_name], name_sz - len); + } + pcol->name[name_sz]='\0'; + + cur_name += name_sz + 1; + } return table->columns; } diff --git a/src/util/mdb-export.c b/src/util/mdb-export.c index ee5d21b..bfa26f3 100644 --- a/src/util/mdb-export.c +++ b/src/util/mdb-export.c @@ -31,25 +31,53 @@ MdbTableDef *table; MdbColumn *col; /* doesn't handle tables > 256 columns. Can that happen? */ char *bound_values[256]; -char delimiter[] = ","; +char *delimiter = ","; char header_row = 1; char quote_text = 1; +int opt; - if (argc<2) { - fprintf(stderr,"Usage: %s \n",argv[0]); + while ((opt=getopt(argc, argv, "HQd:"))!=-1) { + switch (opt) { + case 'H': + header_row = 0; + break; + case 'Q': + quote_text = 0; + break; + case 'd': + delimiter = (char *) malloc(strlen(optarg)+1); + strcpy(delimiter, optarg); + break; + default: + break; + } + } + + /* + ** optind is now the position of the first non-option arg, + ** see getopt(3) + */ + if (argc-optind < 2) { + fprintf(stderr,"Usage: %s [options]
\n",argv[0]); + fprintf(stderr,"where options are:\n"); + fprintf(stderr," -H supress header row\n"); + fprintf(stderr," -Q don't wrap text-like fields in quotes\n"); + fprintf(stderr," -d specify a column delimiter\n"); + exit(1); + } + + mdb_init(); + + if (!(mdb = mdb_open(argv[optind]))) { exit(1); } - mdb_init(); - - mdb = mdb_open(argv[1]); - mdb_read_catalog(mdb, MDB_TABLE); for (i=0;inum_catalog;i++) { entry = g_array_index(mdb->catalog,MdbCatalogEntry,i); if (entry.object_type == MDB_TABLE && - !strcmp(entry.object_name,argv[2])) { + !strcmp(entry.object_name,argv[argc-1])) { table = mdb_read_table(&entry); mdb_read_columns(table); mdb_rewind_table(table);