From cc362c42e9bdf7c23bc96a048627a4e01353f383 Mon Sep 17 00:00:00 2001 From: brianb Date: Wed, 1 Jan 2003 22:29:39 +0000 Subject: [PATCH] more gmdb2 work, reorged mdbhandle --- HACKING | 7 +- include/mdbtools.h | 47 +++++-- src/gmdb2/debug.c | 177 ++++++++++++++++++++++-- src/gmdb2/file.c | 17 +++ src/gmdb2/form.c | 17 +++ src/gmdb2/info.c | 25 +++- src/gmdb2/macro.c | 17 +++ src/gmdb2/main2.c | 17 +++ src/gmdb2/module.c | 17 +++ src/gmdb2/query.c | 17 +++ src/gmdb2/report.c | 17 +++ src/gmdb2/schema.c | 17 +++ src/gmdb2/sql.c | 17 +++ src/gmdb2/table.c | 17 +++ src/gmdb2/table_data.c | 17 +++ src/gmdb2/table_def.c | 17 +++ src/gmdb2/table_export.c | 17 +++ src/gmdb2/util.c | 17 +++ src/libmdb/catalog.c | 4 +- src/libmdb/data.c | 56 ++++---- src/libmdb/file.c | 111 +++++++-------- src/libmdb/index.c | 8 +- src/libmdb/kkd.c | 4 +- src/libmdb/mem.c | 19 ++- src/libmdb/table.c | 67 +++++---- src/libmdb/write.c | 292 ++++++++++++++++++++++++++++++++++++--- src/util/mdb-ver.c | 13 +- src/util/updrow.c | 6 +- 28 files changed, 893 insertions(+), 181 deletions(-) diff --git a/HACKING b/HACKING index 333e7ac..ee7abd8 100644 --- a/HACKING +++ b/HACKING @@ -193,13 +193,14 @@ with the next_pg pointer. | ???? | 1 byte | col_name_len| len of the name of the column | | ???? | n bytes | col_name | Name of the column | +-------------------------------------------------------------------------+ -| Iterate for the number of num_real_idx (30+5 = 35 bytes) | +| Iterate for the number of num_real_idx (30+9 = 39 bytes) | +-------------------------------------------------------------------------+ | Iterate 10 times for 10 possible columns (10*3 = 30 bytes) | +-------------------------------------------------------------------------+ | ???? | 2 bytes | col_num | number of a column (0xFFFF= none) | | ???? | 1 byte | col_order | 0x01 = ascendency order | +-------------------------------------------------------------------------+ +| ???? | 4 bytes | unknown | | | ???? | 4 bytes | first_dp | Data pointer of the index page | | ???? | 1 byte | flags | See flags table for indexes | +-------------------------------------------------------------------------+ @@ -362,6 +363,10 @@ Note: For boolean fixed columns, the values are in null_table[]: 0 indicates a false value 1 indicates a true value +An 0xFF stored in the var_table indicates that this column has been deleted. + +Note: A row will always have the number of fixed columns as specified in the table definition, but may have less variable columns, as rows are not updated when columns are added. + In Access 2000 (JET4) data rows are like this +------+---------+----------------------------------------------------------+ diff --git a/include/mdbtools.h b/include/mdbtools.h index 8327ca3..464d98d 100644 --- a/include/mdbtools.h +++ b/include/mdbtools.h @@ -92,6 +92,15 @@ enum { MDB_DESC }; +enum { + MDB_IDX_UNIQUE = 0x01, + MDB_IDX_IGNORENULLS = 0x02, + MDB_IDX_REQUIRED = 0x08 +}; + +#define IS_JET4(mdb) (mdb->f->jet_version==MDB_VER_JET4) +#define IS_JET3(mdb) (mdb->f->jet_version==MDB_VER_JET3) + /* hash to store registered backends */ GHashTable *mdb_backends; @@ -101,24 +110,19 @@ typedef struct { typedef struct { int fd; + gboolean writable; char *filename; - guint16 cur_pg; - guint16 row_num; - unsigned int cur_pos; - unsigned char pg_buf[MDB_PGSIZE]; - unsigned char alt_pg_buf[MDB_PGSIZE]; - int num_catalog; - GPtrArray *catalog; - int pg_size; guint32 jet_version; guint32 db_key; char db_passwd[14]; - MdbBackend *default_backend; - char *backend_name; /* free map */ int map_sz; unsigned char *free_map; - /* offset to row count on data pages...version dependant */ +} MdbFile; + +/* offset to row count on data pages...version dependant */ +typedef struct { + int pg_size; guint16 row_count_offset; guint16 tab_num_rows_offset; guint16 tab_num_cols_offset; @@ -129,9 +133,23 @@ typedef struct { guint16 tab_cols_start_offset; guint16 tab_ridx_entry_size; guint16 col_fixed_offset; - guint16 col_num_offset; guint16 col_size_offset; + guint16 col_num_offset; guint16 tab_col_entry_size; +} MdbFormatConstants; + +typedef struct { + MdbFile *f; + guint16 cur_pg; + guint16 row_num; + unsigned int cur_pos; + unsigned char pg_buf[MDB_PGSIZE]; + unsigned char alt_pg_buf[MDB_PGSIZE]; + int num_catalog; + GPtrArray *catalog; + MdbBackend *default_backend; + char *backend_name; + MdbFormatConstants *fmt; } MdbHandle; typedef struct { @@ -166,6 +184,9 @@ typedef struct { int map_sz; unsigned char *usage_map; /* */ + int idxmap_base_pg; + int idxmap_sz; + unsigned char *idx_usage_map; } MdbTableDef; typedef struct { @@ -177,6 +198,7 @@ typedef struct { int num_keys; short key_col_num[MDB_MAX_IDX_COLS]; unsigned char key_col_order[MDB_MAX_IDX_COLS]; + unsigned char flags; } MdbIndex; typedef struct { @@ -213,6 +235,7 @@ typedef struct { MdbAny value; } MdbSarg; + /* mem.c */ extern void mdb_init(); extern void mdb_exit(); diff --git a/src/gmdb2/debug.c b/src/gmdb2/debug.c index 31f28c5..55ee267 100644 --- a/src/gmdb2/debug.c +++ b/src/gmdb2/debug.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include extern GtkWidget *app; @@ -131,6 +148,7 @@ GtkTextBuffer *buffer; GtkTextIter iter; GtkWidget *entry; GtkTextView *textview; +gchar *s; if (!mdb) return; @@ -138,21 +156,33 @@ GtkTextView *textview; entry = glade_xml_get_widget (xml, "debug_entry"); textview = glade_xml_get_widget (xml, "debug_textview"); - page = atol(gtk_entry_get_text(GTK_ENTRY(entry))); + s = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + + if (!strncmp(s,"0x",2)) { + page = 0; + for (i=2;i '9' ? s[i] - 'a' + 10 : s[i] - '0'; + } + } else { + page = atol(gtk_entry_get_text(GTK_ENTRY(entry))); + } if (page>gmdb_get_max_page(mdb) || page<0) { gmdb_info_msg("Page entered is outside valid page range."); } gmdb_debug_clear(xml); - pos = lseek(mdb->fd, 0, SEEK_CUR); - lseek(mdb->fd, page * mdb->pg_size, SEEK_SET); + pos = lseek(mdb->f->fd, 0, SEEK_CUR); + lseek(mdb->f->fd, page * mdb->fmt->pg_size, SEEK_SET); - fbuf = (unsigned char *) malloc(mdb->pg_size); - tbuf = (unsigned char *) malloc( (mdb->pg_size / 16) * 80); - memset(tbuf, 0, (mdb->pg_size / 16) * 80); - length = read(mdb->fd, fbuf, mdb->pg_size); - if (lengthpg_size) { + fbuf = (unsigned char *) malloc(mdb->fmt->pg_size); + tbuf = (unsigned char *) malloc( (mdb->fmt->pg_size / 16) * 80); + memset(tbuf, 0, (mdb->fmt->pg_size / 16) * 80); + length = read(mdb->f->fd, fbuf, mdb->fmt->pg_size); + if (lengthfmt->pg_size) { } i = 0; while (ifd, &st)!=-1 ); - return st.st_size/mdb->pg_size; + assert( fstat(mdb->f->fd, &st)!=-1 ); + return st.st_size/mdb->fmt->pg_size; } gchar * gmdb_val_to_str(GMdbValStr *valstr, gint val) @@ -293,10 +323,99 @@ GtkTreeIter *node; gmdb_debug_add_item(store, node, str, offset+1, offset+3); } void +gmdb_debug_dissect_row(GtkTreeStore *store, GtkTreeIter *parent, char *fbuf, int offset, int end) +{ +gchar str[100]; +int bitmask_sz; +int num_cols, var_cols, var_cols_loc, fixed_end, eod_ptr; +int i; + + num_cols = fbuf[offset]; + snprintf(str, 100, "Num columns: %u", num_cols); + gmdb_debug_add_item(store, parent, str, offset, offset); + bitmask_sz = ((num_cols-1) / 8) + 1; + var_cols_loc = end - bitmask_sz; + var_cols = fbuf[var_cols_loc]; + fixed_end = offset + fbuf[var_cols_loc - 1] - 1; /* work even if 0 b/c of EOD */ + snprintf(str, 100, "Fixed columns"); + gmdb_debug_add_item(store, parent, str, offset + 1, fixed_end); + for (i=0;ifmt->pg_size - 1; + else + row_end = get_uint16(&fbuf[offset+10+(i-1)*2]) + & 0x0FFF - 1; + snprintf(str, 100, "Row %d", i+1); + container = gmdb_debug_add_item(store, NULL, str, row_start, row_end); + + /* usage pages have parent id of 0 (database) and do not + * follow normal row format */ + if (tdef) + gmdb_debug_dissect_row(store, container, fbuf, row_start, row_end); + } +} +void gmdb_debug_dissect_tabledef_pg(GtkTreeStore *store, char *fbuf, int offset, int len) { gchar str[100]; -guint32 i, num_idx, num_cols; +guint32 i, num_idx, num_cols, idx_entries; int newbase; GtkTreeIter *node, *container; @@ -321,8 +440,8 @@ GtkTreeIter *node, *container; snprintf(str, 100, "# of Columns: %u", get_uint16(&fbuf[offset+25])); gmdb_debug_add_item(store, NULL, str, offset+25, offset+26); - snprintf(str, 100, "# of Index Entries: %lu", - get_uint32(&fbuf[offset+27])); + idx_entries = get_uint32(&fbuf[offset+27]); + snprintf(str, 100, "# of Index Entries: %lu", idx_entries); gmdb_debug_add_item(store, NULL, str, offset+27, offset+30); num_idx = get_uint32(&fbuf[offset+31]); @@ -361,6 +480,34 @@ GtkTreeIter *node, *container; node = gmdb_debug_add_item(store, container, str, newbase + 1, newbase + namelen); newbase += namelen + 1; } + container = gmdb_debug_add_item(store, NULL, "Index definition 1", -1, -1); + for (i=0;i diff --git a/src/gmdb2/form.c b/src/gmdb2/form.c index 5471e35..390d699 100644 --- a/src/gmdb2/form.c +++ b/src/gmdb2/form.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/info.c b/src/gmdb2/info.c index c4da447..937baaf 100644 --- a/src/gmdb2/info.c +++ b/src/gmdb2/info.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" extern GtkWidget *app; @@ -26,7 +43,7 @@ struct stat st; /* connect the signals in the interface */ glade_xml_signal_autoconnect(propswin_xml); - filepath = g_strdup(mdb->filename); + filepath = g_strdup(mdb->f->filename); for (i=strlen(filepath);i>0 && filepath[i-1]!='/';i--); filename=&filepath[i]; @@ -38,14 +55,14 @@ struct stat st; gtk_label_set_text(GTK_LABEL(label), filename); label = glade_xml_get_widget (propswin_xml, "props_jetver"); - gtk_label_set_text(GTK_LABEL(label), mdb->jet_version == MDB_VER_JET3 ? "3 (Access 97)" : "4 (Access 2000/XP)"); + gtk_label_set_text(GTK_LABEL(label), mdb->f->jet_version == MDB_VER_JET3 ? "3 (Access 97)" : "4 (Access 2000/XP)"); - assert( fstat(mdb->fd, &st)!=-1 ); + assert( fstat(mdb->f->fd, &st)!=-1 ); sprintf(tmpstr, "%ld K", st.st_size/1024); label = glade_xml_get_widget (propswin_xml, "props_filesize"); gtk_label_set_text(GTK_LABEL(label), tmpstr); - sprintf(tmpstr, "%ld", st.st_size / mdb->pg_size); + sprintf(tmpstr, "%ld", st.st_size / mdb->fmt->pg_size); label = glade_xml_get_widget (propswin_xml, "props_numpages"); gtk_label_set_text(GTK_LABEL(label), tmpstr); diff --git a/src/gmdb2/macro.c b/src/gmdb2/macro.c index c03d1de..dfadeff 100644 --- a/src/gmdb2/macro.c +++ b/src/gmdb2/macro.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/main2.c b/src/gmdb2/main2.c index a40c0b7..d1d58a9 100644 --- a/src/gmdb2/main2.c +++ b/src/gmdb2/main2.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include #include #include diff --git a/src/gmdb2/module.c b/src/gmdb2/module.c index e3cd704..73f5fef 100644 --- a/src/gmdb2/module.c +++ b/src/gmdb2/module.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/query.c b/src/gmdb2/query.c index 89a142a..309e453 100644 --- a/src/gmdb2/query.c +++ b/src/gmdb2/query.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/report.c b/src/gmdb2/report.c index 62e4e87..eac7513 100644 --- a/src/gmdb2/report.c +++ b/src/gmdb2/report.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/schema.c b/src/gmdb2/schema.c index 4b5e5bf..33ae70f 100644 --- a/src/gmdb2/schema.c +++ b/src/gmdb2/schema.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" extern GtkWidget *app; diff --git a/src/gmdb2/sql.c b/src/gmdb2/sql.c index 2d032c7..6f82583 100644 --- a/src/gmdb2/sql.c +++ b/src/gmdb2/sql.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #if SQL diff --git a/src/gmdb2/table.c b/src/gmdb2/table.c index 5d27b28..4a6d410 100644 --- a/src/gmdb2/table.c +++ b/src/gmdb2/table.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include diff --git a/src/gmdb2/table_data.c b/src/gmdb2/table_data.c index 77a3e8c..501310c 100644 --- a/src/gmdb2/table_data.c +++ b/src/gmdb2/table_data.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" extern GtkWidget *app; diff --git a/src/gmdb2/table_def.c b/src/gmdb2/table_def.c index 61918dc..578d163 100644 --- a/src/gmdb2/table_def.c +++ b/src/gmdb2/table_def.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" #include "pk.xpm" diff --git a/src/gmdb2/table_export.c b/src/gmdb2/table_export.c index 2d28d81..e8c9db3 100644 --- a/src/gmdb2/table_export.c +++ b/src/gmdb2/table_export.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" extern GtkWidget *app; diff --git a/src/gmdb2/util.c b/src/gmdb2/util.c index 8e39cee..b957ee6 100644 --- a/src/gmdb2/util.c +++ b/src/gmdb2/util.c @@ -1,3 +1,20 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ #include "gmdb.h" void gmdb_info_msg(gchar *message) { diff --git a/src/libmdb/catalog.c b/src/libmdb/catalog.c index d64ca45..fe2a2c2 100644 --- a/src/libmdb/catalog.c +++ b/src/libmdb/catalog.c @@ -111,7 +111,7 @@ int i,j; if (rowid < 0 || rowid > rows) return NULL; offset = mdb_get_int16(mdb, (mdb->row_count_offset + 2) + 2 * rowid); - if (mdb->jet_version==MDB_VER_JET4) offset++; + if (IS_JET4(mdb)) offset++; /* ** ??? this happens, don't know what it means */ @@ -132,7 +132,7 @@ fprintf(stdout,"\n"); if (j<=MDB_MAX_OBJ_NAME) { entry->object_name[j++]=mdb->pg_buf[i]; } - if (mdb->jet_version==MDB_VER_JET4) i++; + if (IS_JET4(mdb)) i++; } //fprintf(stderr,"name: %s type: %d\n",entry->object_name, entry->object_type); //fprintf(stderr,"cur page: %d row; %d\n", entry->table_pg, rowid); diff --git a/src/libmdb/data.c b/src/libmdb/data.c index d21ce66..a6c9efc 100644 --- a/src/libmdb/data.c +++ b/src/libmdb/data.c @@ -46,6 +46,7 @@ MdbColumn *col; } int mdb_find_end_of_row(MdbHandle *mdb, int row) { +MdbFormatConstants *fmt = mdb->fmt; int row_end; /* Search the previous "row start" values for the first non-deleted one. @@ -53,14 +54,14 @@ int row_end; */ #if 1 if (row==0) { - row_end = mdb->pg_size - 1; + row_end = fmt->pg_size - 1; } else { - row_end = (mdb_get_int16(mdb, ((mdb->row_count_offset + 2) + (row - 1) * 2)) & 0x0FFF) - 1; + row_end = (mdb_get_int16(mdb, ((fmt->row_count_offset + 2) + (row - 1) * 2)) & 0x0FFF) - 1; } return row_end; #else for (i = row - 1; i >= 0; i--) { - row_start = mdb_get_int16(mdb, ((mdb->row_count_offset + 2) + i * 2)); + row_start = mdb_get_int16(mdb, ((fmt->row_count_offset + 2) + i * 2)); if (!(row_start & 0x8000)) { break; } @@ -68,7 +69,7 @@ int row_end; row_start &= 0x0FFF; if (i == -1) { - row_end = mdb->pg_size - 1; + row_end = fmt->pg_size - 1; } else { row_end = row_start - 1; } @@ -152,6 +153,7 @@ int ret; int mdb_read_row(MdbTableDef *table, int row) { MdbHandle *mdb = table->entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; MdbColumn *col; int i, j, rc; int num_cols, var_cols, fixed_cols; @@ -166,7 +168,7 @@ int col_ptr, deleted_columns=0; unsigned char null_mask[33]; /* 256 columns max / 8 bits per byte */ unsigned char isnull; - row_start = mdb_get_int16(mdb, (mdb->row_count_offset + 2) + (row*2)); + row_start = mdb_get_int16(mdb, (fmt->row_count_offset + 2) + (row*2)); row_end = mdb_find_end_of_row(mdb, row); delflag = lookupflag = 0; @@ -190,7 +192,7 @@ unsigned char isnull; #endif /* find out all the important stuff about the row */ - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { num_cols = mdb_get_int16(mdb, row_start); } else { num_cols = mdb->pg_buf[row_start]; @@ -205,7 +207,7 @@ unsigned char isnull; var_cols++; } bitmask_sz = (num_cols - 1) / 8 + 1; - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { eod = mdb_get_int16(mdb, row_end - 3 - var_cols*2 - bitmask_sz); } else { eod = mdb->pg_buf[row_end-1-var_cols-bitmask_sz]; @@ -219,7 +221,7 @@ unsigned char isnull; num_cols, var_cols, eod); #endif - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { col_start = 2; } else { /* data starts at 1 */ @@ -247,6 +249,7 @@ unsigned char isnull; } } + /* if fixed columns add up to more than 256, we need a jump */ if (col_start >= 256) { num_of_jumps++; jumps_used++; @@ -254,11 +257,12 @@ unsigned char isnull; } col_start = row_start; + /* */ while (col_start+256 < row_end-bitmask_sz-1-var_cols-num_of_jumps){ col_start += 256; num_of_jumps++; } - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { col_ptr = row_end - 2 - bitmask_sz - 1; eod = mdb_get_int16(mdb, col_ptr - var_cols*2); col_start = mdb_get_int16(mdb, col_ptr); @@ -298,7 +302,7 @@ unsigned char isnull; if (var_cols_found==var_cols) { len=eod - col_start; } else { - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { //next_col = mdb_get_int16(mdb, row_end - bitmask_sz - var_cols_found * 2 - 2 - 1) ; next_col = mdb->pg_buf[row_end - bitmask_sz - var_cols_found * 2 - 2] * 256 + mdb->pg_buf[row_end - bitmask_sz - var_cols_found * 2 - 2 - 1] ; @@ -398,12 +402,12 @@ unsigned char map_byte; if (!map_pg) continue; - if(mdb_read_alt_pg(mdb, map_pg) != mdb->pg_size) { + if(mdb_read_alt_pg(mdb, map_pg) != mdb->fmt->pg_size) { fprintf(stderr, "Oops! didn't get a full page at %d\n", map_pg); exit(1); } //printf("reading page %ld\n",map_pg); - for (j=4;jpg_size;j++) { + for (j=4;jfmt->pg_size;j++) { for (bitn=0;bitn<8;bitn++) { if (mdb->alt_pg_buf[j] & 1 << bitn && pgnum > table->cur_phys_pg) { table->cur_phys_pg = pgnum; @@ -458,6 +462,7 @@ int mdb_rewind_table(MdbTableDef *table) int mdb_fetch_row(MdbTableDef *table) { MdbHandle *mdb = table->entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; int rows; int rc; @@ -472,7 +477,7 @@ int rc; } do { - rows = mdb_get_int16(mdb,mdb->row_count_offset); + rows = mdb_get_int16(mdb,fmt->row_count_offset); /* if at end of page, find a new page */ if (table->cur_row >= rows) { @@ -559,7 +564,7 @@ guint16 len, cur; #if MDB_DEBUG printf("Reading LVAL page %06x\n", lval_pg); #endif - if(mdb_read_alt_pg(mdb, lval_pg) != mdb->pg_size) { + if(mdb_read_alt_pg(mdb, lval_pg) != mdb->fmt->pg_size) { /* Failed to read */ return 0; } @@ -568,7 +573,7 @@ guint16 len, cur; if (ole_row) { row_stop = mdb_get_int16(mdb, 10 + (ole_row - 1) * 2) & 0x0FFF; } else { - row_stop = mdb->pg_size - 1; + row_stop = mdb->fmt->pg_size - 1; } row_start = mdb_get_int16(mdb, 10 + ole_row * 2); #if MDB_DEBUG @@ -589,14 +594,14 @@ guint16 len, cur; mdb_swap_pgbuf(mdb); cur=0; do { - if(mdb_read_pg(mdb, lval_pg) != mdb->pg_size) { + if(mdb_read_pg(mdb, lval_pg) != mdb->fmt->pg_size) { /* Failed to read */ return 0; } if (ole_row) { row_stop = mdb_get_int16(mdb, 10 + (ole_row - 1) * 2) & 0x0FFF; } else { - row_stop = mdb->pg_size - 1; + row_stop = mdb->fmt->pg_size - 1; } row_start = mdb_get_int16(mdb, 10 + ole_row * 2); #if MDB_DEBUG @@ -622,6 +627,7 @@ guint16 len, cur; } static char *mdb_memo_to_string(MdbHandle *mdb, int start, int size) { +MdbFormatConstants *fmt = mdb->fmt; guint16 memo_len; static char text[MDB_BIND_SIZE]; guint16 memo_flags; @@ -658,24 +664,24 @@ int i; #if MDB_DEBUG printf("Reading LVAL page %06x\n", lval_pg); #endif - if(mdb_read_alt_pg(mdb, lval_pg) != mdb->pg_size) { + if(mdb_read_alt_pg(mdb, lval_pg) != fmt->pg_size) { /* Failed to read */ return ""; } /* swap the alt and regular page buffers, so we can call get_int16 */ mdb_swap_pgbuf(mdb); if (memo_row) { - row_stop = mdb_get_int16(mdb, mdb->row_count_offset + 2 + (memo_row - 1) * 2) & 0x0FFF; + row_stop = mdb_get_int16(mdb, fmt->row_count_offset + 2 + (memo_row - 1) * 2) & 0x0FFF; } else { - row_stop = mdb->pg_size - 1; + row_stop = fmt->pg_size - 1; } - row_start = mdb_get_int16(mdb, mdb->row_count_offset + 2 + memo_row * 2); + row_start = mdb_get_int16(mdb, fmt->row_count_offset + 2 + memo_row * 2); #if MDB_DEBUG printf("row num %d row start %d row stop %d\n", memo_row, row_start, row_stop); buffer_dump(mdb->pg_buf,row_start, row_start + len); #endif len = row_stop - row_start; - if (mdb->jet_version==MDB_VER_JET3) { + if (IS_JET3(mdb)) { strncpy(text, &mdb->pg_buf[row_start], len); text[len]='\0'; } else { @@ -703,14 +709,14 @@ int i; mdb_swap_pgbuf(mdb); text[0]='\0'; do { - if(mdb_read_pg(mdb, lval_pg) != mdb->pg_size) { + if(mdb_read_pg(mdb, lval_pg) != fmt->pg_size) { /* Failed to read */ return ""; } if (memo_row) { row_stop = mdb_get_int16(mdb, 10 + (memo_row - 1) * 2) & 0x0FFF; } else { - row_stop = mdb->pg_size - 1; + row_stop = fmt->pg_size - 1; } row_start = mdb_get_int16(mdb, 10 + memo_row * 2); #if MDB_DEBUG @@ -812,7 +818,7 @@ int i,j; if (size<0) { return ""; } - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { /* for (i=0;ipg_buf[start+i], mdb->pg_buf[start+i]); diff --git a/src/libmdb/file.c b/src/libmdb/file.c index 8e0b148..c82b7f3 100644 --- a/src/libmdb/file.c +++ b/src/libmdb/file.c @@ -19,20 +19,35 @@ #include "mdbtools.h" +MdbFormatConstants MdbJet4Constants = { + 4096, 0x0c, 12, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25 +}; +MdbFormatConstants MdbJet3Constants = { + 2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18 +}; + static size_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg); -MdbHandle *mdb_open(char *filename) +MdbHandle *_mdb_open(char *filename, gboolean writable) { MdbHandle *mdb; int key[] = {0x86, 0xfb, 0xec, 0x37, 0x5d, 0x44, 0x9c, 0xfa, 0xc6, 0x5e, 0x28, 0xe6, 0x13, 0xb6}; int j,pos; mdb = mdb_alloc_handle(); - mdb->filename = (char *) malloc(strlen(filename)+1); - strcpy(mdb->filename, filename); - mdb->fd = open(filename,O_RDONLY); + /* need something to bootstrap with, reassign after page 0 is read */ + mdb->fmt = &MdbJet3Constants; + mdb->f = mdb_alloc_file(); + mdb->f->filename = (char *) malloc(strlen(filename)+1); + strcpy(mdb->f->filename, filename); + if (writable) { + mdb->f->writable = TRUE; + mdb->f->fd = open(filename,O_RDWR); + } else { + mdb->f->fd = open(filename,O_RDONLY); + } - if (mdb->fd==-1) { + if (mdb->f->fd==-1) { /* fprintf(stderr,"Couldn't open file %s\n",filename); */ return NULL; } @@ -40,42 +55,16 @@ int j,pos; fprintf(stderr,"Couldn't read first page.\n"); return NULL; } - mdb->jet_version = mdb_get_int32(mdb, 0x14); - if (mdb->jet_version == MDB_VER_JET4) { - mdb->pg_size = 4096; - mdb->row_count_offset = 0x0c; - mdb->tab_num_rows_offset = 12; - mdb->tab_num_cols_offset = 45; - mdb->tab_num_idxs_offset = 47; - mdb->tab_num_ridxs_offset = 51; - mdb->tab_usage_map_offset = 55; - mdb->tab_first_dpg_offset = 56; - mdb->tab_cols_start_offset = 63; - mdb->tab_ridx_entry_size = 12; - mdb->col_fixed_offset = 15; - mdb->col_size_offset = 23; - mdb->col_num_offset = 5; - mdb->tab_col_entry_size = 25; + mdb->f->jet_version = mdb_get_int32(mdb, 0x14); + if (IS_JET4(mdb)) { + mdb->fmt = &MdbJet4Constants; } else { - mdb->pg_size = 2048; - mdb->row_count_offset = 0x08; - mdb->tab_num_rows_offset = 12; - mdb->tab_num_cols_offset = 25; - mdb->tab_num_idxs_offset = 27; - mdb->tab_num_ridxs_offset = 31; - mdb->tab_usage_map_offset = 35; - mdb->tab_first_dpg_offset = 36; - mdb->tab_cols_start_offset = 43; - mdb->tab_ridx_entry_size = 8; - mdb->col_fixed_offset = 13; - mdb->col_size_offset = 16; - mdb->col_num_offset = 1; - mdb->tab_col_entry_size = 18; + mdb->fmt = &MdbJet3Constants; } /* get the db encryption key and xor it back to clear text */ - mdb->db_key = mdb_get_int32(mdb, 0x3e); - mdb->db_key ^= 0xe15e01b9; + mdb->f->db_key = mdb_get_int32(mdb, 0x3e); + mdb->f->db_key ^= 0xe15e01b9; /* get the db password located at 0x42 bytes into the file */ @@ -83,22 +72,22 @@ int j,pos; j = mdb_get_int32(mdb,0x42+pos); j ^= key[pos]; if ( j != 0) - mdb->db_passwd[pos] = j; + mdb->f->db_passwd[pos] = j; else - mdb->db_passwd[pos] = '\0'; + mdb->f->db_passwd[pos] = '\0'; } return mdb; } +MdbHandle *mdb_open(char *filename) +{ + return _mdb_open(filename, FALSE); +} void mdb_close(MdbHandle *mdb) { - if (mdb->fd > 0) { - close(mdb->fd); - if (mdb->filename) { - free(mdb->filename); - mdb->filename = NULL; - } + if (mdb->f) { + mdb_free_file(mdb->f); } } @@ -125,21 +114,21 @@ static size_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long { size_t len; struct stat status; -off_t offset = pg * mdb->pg_size; +off_t offset = pg * mdb->fmt->pg_size; - fstat(mdb->fd, &status); + fstat(mdb->f->fd, &status); if (status.st_size < offset) { fprintf(stderr,"offset %lu is beyond EOF\n",offset); return 0; } - lseek(mdb->fd, offset, SEEK_SET); - len = read(mdb->fd,pg_buf,mdb->pg_size); + lseek(mdb->f->fd, offset, SEEK_SET); + len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size); if (len==-1) { perror("read"); return 0; } - else if (lenpg_size) { - /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */ + else if (lenfmt->pg_size) { + /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */ return 0; } return len; @@ -160,14 +149,18 @@ unsigned char c; mdb->cur_pos++; return c; } +int _mdb_get_int16(unsigned char *buf, int offset) +{ + return buf[offset+1]*256+buf[offset]; +} int mdb_get_int16(MdbHandle *mdb, int offset) { unsigned char *c; int i; - if (offset < 0 || offset+2 > mdb->pg_size) return -1; - c = &mdb->pg_buf[offset]; - i = c[1]*256+c[0]; + if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1; + + i = _mdb_get_int16(mdb->pg_buf, offset); mdb->cur_pos+=2; return i; @@ -178,7 +171,7 @@ gint32 mdb_get_int24(MdbHandle *mdb, int offset) gint32 l; unsigned char *c; - if (offset <0 || offset+3 > mdb->pg_size) return -1; + if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1; c = &mdb->pg_buf[offset]; l =c[2]; l<<=8; l+=c[1]; l<<=8; @@ -204,7 +197,7 @@ long mdb_get_int32(MdbHandle *mdb, int offset) { long l; - if (offset <0 || offset+4 > mdb->pg_size) return -1; + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; l = _mdb_get_int32(mdb->pg_buf, offset); mdb->cur_pos+=4; @@ -216,7 +209,7 @@ float f, f2; unsigned char *c; int i; - if (offset <0 || offset+4 > mdb->pg_size) return -1; + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; memcpy(&f, &mdb->pg_buf[offset], 4); @@ -237,7 +230,7 @@ double d, d2; unsigned char *c; int i; - if (offset <0 || offset+4 > mdb->pg_size) return -1; + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; memcpy(&d, &mdb->pg_buf[offset], 8); @@ -254,7 +247,7 @@ int i; } int mdb_set_pos(MdbHandle *mdb, int pos) { - if (pos<0 || pos >= mdb->pg_size) return 0; + if (pos<0 || pos >= mdb->fmt->pg_size) return 0; mdb->cur_pos=pos; return pos; diff --git a/src/libmdb/index.c b/src/libmdb/index.c index 4b95979..593f56c 100644 --- a/src/libmdb/index.c +++ b/src/libmdb/index.c @@ -106,7 +106,8 @@ int name_sz; pidx->num_keys = key_num; cur_pos += 4; pidx->first_pg = mdb_get_int32(mdb, cur_pos); - cur_pos += 5; + cur_pos += 4; + pidx->flags = mdb->pg_buf[cur_pos++]; } } void mdb_index_walk(MdbTableDef *table, MdbIndex *idx) @@ -140,10 +141,11 @@ MdbColumn *col; if (idx->index_type==1) fprintf(stdout,"index is a primary key\n"); for (i=0;inum_keys;i++) { col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); - fprintf(stdout,"Column %s(%d) Sorted %s\n", + fprintf(stdout,"Column %s(%d) Sorted %s Unique: %s\n", col->name, idx->key_col_num[i], - idx->key_col_order[i]==MDB_ASC ? "ascending" : "descending" + idx->key_col_order[i]==MDB_ASC ? "ascending" : "descending", + idx->flags & MDB_IDX_UNIQUE ? "Yes" : "No" ); } mdb_index_walk(table, idx); diff --git a/src/libmdb/kkd.c b/src/libmdb/kkd.c index a4ae56f..8a80e6a 100644 --- a/src/libmdb/kkd.c +++ b/src/libmdb/kkd.c @@ -116,10 +116,10 @@ int rowid = entry->kkd_rowid; fprintf(stdout,"number of rows = %d\n",rows); kkd_start = mdb_get_int16(mdb,10+rowid*2); fprintf(stdout,"kkd start = %d %04x\n",kkd_start,kkd_start); - kkd_end = mdb->pg_size; + kkd_end = mdb->fmt->pg_size; for (i=0;ipg_size && + if (tmp < mdb->fmt->pg_size && tmp > kkd_start && tmp < kkd_end) { kkd_end = tmp; diff --git a/src/libmdb/mem.c b/src/libmdb/mem.c index e462762..b1d9502 100644 --- a/src/libmdb/mem.c +++ b/src/libmdb/mem.c @@ -30,13 +30,28 @@ void mdb_exit() g_hash_table_destroy(mdb_backends); } +MdbFile *mdb_alloc_file() +{ +MdbHandle *f; + f = (MdbFile *) malloc(sizeof(MdbFile)); + memset(f, '\0', sizeof(MdbFile)); + + return f; +} +void mdb_free_file(MdbFile *f) +{ + if (!f) return; + if (f->fd) close(f->fd); + if (f->filename) free(f->filename); + free(f); +} + MdbHandle *mdb_alloc_handle() { MdbHandle *mdb; mdb = (MdbHandle *) malloc(sizeof(MdbHandle)); memset(mdb, '\0', sizeof(MdbHandle)); - mdb->pg_size = MDB_PGSIZE; mdb_set_default_backend(mdb, "access"); return mdb; @@ -45,8 +60,8 @@ void mdb_free_handle(MdbHandle *mdb) { if (!mdb) return; - if (mdb->filename) free(mdb->filename); if (mdb->catalog) mdb_free_catalog(mdb); + if (mdb->f) mdb_free_file(mdb->f); if (mdb->backend_name) free(mdb->backend_name); free(mdb); } diff --git a/src/libmdb/table.c b/src/libmdb/table.c index 096f926..3c70820 100644 --- a/src/libmdb/table.c +++ b/src/libmdb/table.c @@ -44,6 +44,7 @@ MdbTableDef *mdb_read_table(MdbCatalogEntry *entry) { MdbTableDef *table; MdbHandle *mdb = entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; int len, i; int rownum, row_start, row_end; @@ -52,16 +53,16 @@ int rownum, row_start, row_end; mdb_read_pg(mdb, entry->table_pg); len = mdb_get_int16(mdb,8); - table->num_rows = mdb_get_int32(mdb, mdb->tab_num_rows_offset); - table->num_cols = mdb_get_int16(mdb, mdb->tab_num_cols_offset); - table->num_idxs = mdb_get_int32(mdb, mdb->tab_num_idxs_offset); - table->num_real_idxs = mdb_get_int32(mdb, mdb->tab_num_ridxs_offset); + table->num_rows = mdb_get_int32(mdb, fmt->tab_num_rows_offset); + table->num_cols = mdb_get_int16(mdb, fmt->tab_num_cols_offset); + table->num_idxs = mdb_get_int32(mdb, fmt->tab_num_idxs_offset); + table->num_real_idxs = mdb_get_int32(mdb, fmt->tab_num_ridxs_offset); /* grab a copy of the usage map */ - rownum = mdb->pg_buf[mdb->tab_usage_map_offset]; - mdb_read_alt_pg(mdb, mdb_get_int24(mdb, mdb->tab_usage_map_offset + 1)); + rownum = mdb->pg_buf[fmt->tab_usage_map_offset]; + mdb_read_alt_pg(mdb, mdb_get_int24(mdb, fmt->tab_usage_map_offset + 1)); mdb_swap_pgbuf(mdb); - row_start = mdb_get_int16(mdb, (mdb->row_count_offset + 2) + (rownum*2)); + row_start = mdb_get_int16(mdb, (fmt->row_count_offset + 2) + (rownum*2)); row_end = mdb_find_end_of_row(mdb, rownum); table->map_sz = row_end - row_start + 1; table->usage_map = malloc(table->map_sz); @@ -72,11 +73,11 @@ int rownum, row_start, row_end; /* swap back */ mdb_swap_pgbuf(mdb); #if MDB_DEBUG_USAGE - printf ("usage map found on page %ld start %d end %d\n", mdb_get_int24(mdb, mdb->tab_usage_map_offset + 1), row_start, row_end); + printf ("usage map found on page %ld start %d end %d\n", mdb_get_int24(mdb, fmt->tab_usage_map_offset + 1), row_start, row_end); #endif - table->first_data_pg = mdb_get_int16(mdb, mdb->tab_first_dpg_offset); + table->first_data_pg = mdb_get_int16(mdb, fmt->tab_first_dpg_offset); return table; } @@ -87,9 +88,9 @@ int rownum, row_start, row_end; */ static int read_pg_if(MdbHandle *mdb, int *cur_pos, int offset) { - if (*cur_pos + offset >= mdb->pg_size) { + if (*cur_pos + offset >= mdb->fmt->pg_size) { mdb_read_pg(mdb, mdb_get_int32(mdb,4)); - *cur_pos = 8 - (mdb->pg_size - (*cur_pos)); + *cur_pos = 8 - (mdb->fmt->pg_size - (*cur_pos)); return 1; } return 0; @@ -98,6 +99,7 @@ static int read_pg_if(MdbHandle *mdb, int *cur_pos, int offset) GPtrArray *mdb_read_columns(MdbTableDef *table) { MdbHandle *mdb = table->entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; MdbColumn col, *pcol; int len, i,j; unsigned char low_byte, high_byte; @@ -109,8 +111,8 @@ GSList *slist = NULL; table->columns = g_ptr_array_new(); - cur_col = mdb->tab_cols_start_offset + - (table->num_real_idxs * mdb->tab_ridx_entry_size); + cur_col = fmt->tab_cols_start_offset + + (table->num_real_idxs * fmt->tab_ridx_entry_size); /* new code based on patch submitted by Tim Nelson 2000.09.27 */ @@ -123,7 +125,7 @@ GSList *slist = NULL; buffer_dump(mdb->pg_buf, cur_col ,cur_col + 18); */ #endif memset(&col, 0, sizeof(col)); - col.col_num = mdb->pg_buf[cur_col + mdb->col_num_offset]; + col.col_num = mdb->pg_buf[cur_col + fmt->col_num_offset]; read_pg_if(mdb, &cur_col, 0); col.col_type = mdb->pg_buf[cur_col]; @@ -134,20 +136,20 @@ GSList *slist = NULL; } read_pg_if(mdb, &cur_col, 13); - col.is_fixed = mdb->pg_buf[cur_col + mdb->col_fixed_offset] & + col.is_fixed = mdb->pg_buf[cur_col + fmt->col_fixed_offset] & 0x01 ? 1 : 0; if (col.col_type != MDB_BOOL) { read_pg_if(mdb, &cur_col, 17); - low_byte = mdb->pg_buf[cur_col + mdb->col_size_offset]; + low_byte = mdb->pg_buf[cur_col + fmt->col_size_offset]; read_pg_if(mdb, &cur_col, 18); - high_byte = mdb->pg_buf[cur_col + mdb->col_size_offset + 1]; + high_byte = mdb->pg_buf[cur_col + fmt->col_size_offset + 1]; col.col_size += high_byte * 256 + low_byte; } else col.col_size=0; pcol = g_memdup(&col, sizeof(MdbColumn)); slist = g_slist_insert_sorted(slist,pcol,(GCompareFunc)mdb_col_comparer); - cur_col += mdb->tab_col_entry_size; + cur_col += fmt->tab_col_entry_size; } cur_name = cur_col; @@ -163,12 +165,12 @@ GSList *slist = NULL; read_pg_if(mdb, &cur_name, 0); name_sz = mdb->pg_buf[cur_name]; - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { /* FIX ME - for now just skip the high order byte */ cur_name += 2; /* determine amount of name on this page */ - len = ((cur_name + name_sz) > mdb->pg_size) ? - mdb->pg_size - cur_name : + len = ((cur_name + name_sz) > fmt->pg_size) ? + fmt->pg_size - cur_name : name_sz; /* strip high order (second) byte from unicode string */ @@ -179,7 +181,7 @@ GSList *slist = NULL; 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); + cur_name = 8 - (fmt->pg_size - cur_name); if (len % 2) cur_name++; /* get the rest of the name */ for (j=0;jname[name_sz]='\0'; cur_name += name_sz; - } else if (mdb->jet_version==MDB_VER_JET3) { + } else if (IS_JET3(mdb)) { /* determine amount of name on this page */ - len = ((cur_name + name_sz) > mdb->pg_size) ? - mdb->pg_size - cur_name : + len = ((cur_name + name_sz) > fmt->pg_size) ? + fmt->pg_size - cur_name : name_sz; if (len) { @@ -202,7 +204,7 @@ GSList *slist = NULL; 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); + cur_name = 8 - (fmt->pg_size - cur_name); /* get the rest of the name */ memcpy(&pcol->name[len], &mdb->pg_buf[cur_name], name_sz - len); } @@ -229,6 +231,7 @@ void mdb_table_dump(MdbCatalogEntry *entry) { MdbTableDef *table; MdbColumn *col; +int coln; MdbIndex *idx; MdbHandle *mdb = entry->mdb; int i,bitn; @@ -258,12 +261,20 @@ int pgnum; mdb_index_dump(table, idx); } if (table->usage_map) { + printf("pages reserved by this object\n",pgnum); pgnum = _mdb_get_int32(table->usage_map,1); /* the first 5 bytes of the usage map mean something */ + coln = 0; for (i=5;imap_sz;i++) { for (bitn=0;bitn<8;bitn++) { - if (table->usage_map[i] & 1 << bitn) - printf("page %ld is reserved by this object\n",pgnum); + if (table->usage_map[i] & 1 << bitn) { + coln++; + printf("%6ld ",pgnum); + if (coln==10) { + printf("\n"); + coln = 0; + } + } pgnum++; } } diff --git a/src/libmdb/write.c b/src/libmdb/write.c index 2f2a4ab..4cf5e60 100644 --- a/src/libmdb/write.c +++ b/src/libmdb/write.c @@ -26,11 +26,57 @@ typedef struct { void *value; int siz; + int start; unsigned char is_null; unsigned char is_fixed; int colnum; + int offset; } MdbField; +void +_mdb_put_int16(unsigned char *buf, guint32 offset, guint32 value) +{ + buf[offset] = value % 256; + value /= 256; + buf[offset+1] = value % 256; +} +void +_mdb_put_int32(unsigned char *buf, guint32 offset, guint32 value) +{ + buf[offset] = value % 256; + value /= 256; + buf[offset+1] = value % 256; + value /= 256; + buf[offset+2] = value % 256; + value /= 256; + buf[offset+3] = value % 256; +} +size_t +mdb_write_pg(MdbHandle *mdb, unsigned long pg) +{ +size_t len; +struct stat status; +off_t offset = pg * mdb->fmt->pg_size; + + fstat(mdb->f->fd, &status); + /* is page beyond current size + 1 ? */ + if (status.st_size < offset + mdb->fmt->pg_size) { + fprintf(stderr,"offset %lu is beyond EOF\n",offset); + return 0; + } + lseek(mdb->f->fd, offset, SEEK_SET); + len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size); + if (len==-1) { + perror("write"); + return 0; + } else if (lenfmt->pg_size) { + /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */ + return 0; + } + mdb->cur_pos = 0; + return len; +} + static int mdb_is_col_indexed(MdbTableDef *table, int colnum) { @@ -51,15 +97,16 @@ mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; MdbColumn *col; -int var_cols, fixed_cols, num_cols, i, totcols; +int i, j; +int var_cols, fixed_cols, num_cols, totcols; +int var_cols_found, fixed_cols_found, var_entry_pos; +int col_start, next_col; unsigned char *nullmask; int bitmask_sz; int byte_num, bit_num; +int eod, len; /* end of data */ - - printf("field 0 %s\n", fields[0].value); - - if (mdb->jet_version==MDB_VER_JET4) { + if (IS_JET4(mdb)) { num_cols = mdb_get_int16(mdb, row_start); } else { num_cols = mdb->pg_buf[row_start]; @@ -72,17 +119,17 @@ int byte_num, bit_num; col = g_ptr_array_index (table->columns, i); if (mdb_is_fixed_col(col)) { fixed_cols++; - fields[totcols++].colnum = i; + fields[totcols].colnum = i; fields[totcols].siz = col->col_size; - fields[totcols].is_fixed = 1; + fields[totcols++].is_fixed = 1; } } for (i = 0; i < table->num_cols; i++) { col = g_ptr_array_index (table->columns, i); if (!mdb_is_fixed_col(col)) { var_cols++; - fields[totcols++].colnum = i; - fields[totcols].is_fixed = 0; + fields[totcols].colnum = i; + fields[totcols++].is_fixed = 0; } } @@ -97,25 +144,142 @@ int byte_num, bit_num; printf("col %d is %s\n", i, fields[i].is_null ? "null" : "not null"); } + /* find the end of data pointer */ + if (IS_JET4(mdb)) { + eod = mdb_get_int16(mdb, row_end - 3 - var_cols*2 - bitmask_sz); + } else { + eod = mdb->pg_buf[row_end-1-var_cols-bitmask_sz]; + } + printf("eod is %d\n", eod); + + if (IS_JET4(mdb)) { + col_start = 2; + } else { + /* data starts at 1 */ + col_start = 1; + } + /* actual cols on this row */ + fixed_cols_found = 0; + var_cols_found = 0; + + totcols = 0; + /* loop through fixed columns and add values to fields[] */ + for (j=0;jnum_cols;j++) { + col = g_ptr_array_index(table->columns,j); + if (mdb_is_fixed_col(col) && ++fixed_cols_found <= fixed_cols) { + fields[totcols].start = row_start + col_start; + fields[totcols++].value = &mdb->pg_buf[row_start + col_start]; + if (col->col_type != MDB_BOOL) + col_start += col->col_size; + } + } + 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) { + if (var_cols_found==var_cols) { + len=eod - col_start; + printf("len = %d eod %d col_start %d\n",len, eod, col_start); + } else { + if (IS_JET4(mdb)) { + /* position of the var table + * entry for this column */ + var_entry_pos = + row_end - + bitmask_sz - + var_cols_found * 2 - 2 - 1; + next_col = mdb_get_int16(mdb, var_entry_pos); + len = next_col - col_start; + } else { + var_entry_pos = + row_end - + bitmask_sz - + var_cols_found - 1; + len=mdb->pg_buf[var_entry_pos] - mdb->pg_buf[var_entry_pos+1]; + printf("%d %d %d %d\n", mdb->pg_buf[var_entry_pos-1],mdb->pg_buf[var_entry_pos],len, col_start); + } + } /* if found==var_cols */ + if (len<0) len+=256; + fields[totcols].start = row_start + col_start; + fields[totcols].value = &mdb->pg_buf[row_start +col_start]; + fields[totcols++].siz = len; + col_start += len; + } /* if !fixed */ + } /* for */ + return num_cols; } +/* fields must be ordered with fixed columns first, then vars, subsorted by + * column number */ int -mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, MdbField *fields) +mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int num_fields, MdbField *fields) { +int pos = 0; +int var_cols = 0; +unsigned char bit, byte; +int i; + + row_buffer[pos++] = num_fields; + for (i=0;i=num_fields - var_cols;i--) { + row_buffer[pos++] = fields[i].offset % 256; + } + + row_buffer[pos++] = var_cols; + + byte = 0; + bit = 0; + for (i=0;ifmt; int rows, free_start, free_end; - rows = mdb_get_int16(mdb, mdb->row_count_offset); - free_start = mdb->row_count_offset + 2 + (rows * 2); - free_end = mdb_get_int16(mdb, (mdb->row_count_offset + rows * 2)) -1; + rows = mdb_get_int16(mdb, fmt->row_count_offset); + free_start = fmt->row_count_offset + 2 + (rows * 2); + free_end = mdb_get_int16(mdb, (fmt->row_count_offset + rows * 2)) -1; #if MDB_DEBUG_WRITE printf("free space left on page = %d\n", free_end - free_start); #endif - return (free_end - free_start); + return (free_end - free_start + 1); } int mdb_update_row(MdbTableDef *table) @@ -125,18 +289,26 @@ int i; MdbColumn *col; MdbCatalogEntry *entry = table->entry; MdbHandle *mdb = entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; MdbField fields[256]; unsigned char row_buffer[4096]; int old_row_size, new_row_size, delta, num_fields; - fields[0].value = "hello"; - - row_start = mdb_get_int16(mdb, (mdb->row_count_offset + 2) + (table->cur_row*2)); - row_end = mdb_find_end_of_row(mdb, table->cur_row); + if (!mdb->f->writable) { + fprintf(stderr, "File is not open for writing\n"); + return 0; + } + row_start = mdb_get_int16(mdb, (fmt->row_count_offset + 2) + ((table->cur_row-1)*2)); + row_end = mdb_find_end_of_row(mdb, table->cur_row-1); old_row_size = row_end - row_start; row_start &= 0x0FFF; /* remove flags */ +#if MDB_DEBUG_WRITE + printf("page %lu row %d start %d end %d\n", table->cur_phys_pg, table->cur_row-1, row_start, row_end); + buffer_dump(mdb->pg_buf, row_start, row_end); +#endif + for (i=0;inum_cols;i++) { col = g_ptr_array_index(table->columns,i); if (col->bind_ptr && mdb_is_col_indexed(table,i)) { @@ -148,13 +320,95 @@ int old_row_size, new_row_size, delta, num_fields; #if MDB_DEBUG_WRITE for (i=0;inum_cols;i++) { + col = g_ptr_array_index(table->columns,i); + if (col->bind_ptr) { + printf("yes\n"); + fields[i].value = col->bind_ptr; + fields[i].siz = *(col->len_ptr); + } + } - new_row_size = mdb_pack_row(table, row_buffer, &fields); + new_row_size = mdb_pack_row(table, row_buffer, num_fields, &fields); +#if MDB_DEBUG_WRITE + buffer_dump(row_buffer, 0, new_row_size-1); +#endif delta = new_row_size - old_row_size; if ((mdb_pg_get_freespace(mdb) - delta) < 0) { fprintf(stderr, "No space left on this page, update will not occur\n"); return 0; } + /* do it! */ + mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size); +} +int +mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size) +{ +MdbCatalogEntry *entry = table->entry; +MdbHandle *mdb = entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; +unsigned char *new_pg; +guint16 num_rows; +int row_start, row_end, row_size; +int i, pos; + +#if MDB_DEBUG_WRITE + buffer_dump(mdb->pg_buf, 0, 39); + buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1); + printf("updating row %d on page %lu\n", row, table->cur_phys_pg); +#endif + new_pg = (unsigned char *) g_malloc0(fmt->pg_size); + g_free(new_pg); + + new_pg[0]=0x01; + new_pg[1]=0x01; + _mdb_put_int32(new_pg, 4, entry->table_pg); + + num_rows = mdb_get_int16(mdb, fmt->row_count_offset); + _mdb_put_int16(new_pg, fmt->row_count_offset, num_rows); + + pos = mdb->fmt->pg_size; + + /* rows before */ + for (i=0;irow_count_offset + 2) + (i*2)); + row_end = mdb_find_end_of_row(mdb, i); + row_size = row_end - row_start + 1; + pos -= row_size; + memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + + /* our row */ + pos -= new_row_size; + memcpy(&new_pg[pos], new_row, new_row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (row*2), pos); + + /* rows after */ + for (i=row+1;irow_count_offset + 2) + (i*2)); + row_end = mdb_find_end_of_row(mdb, i); + row_size = row_end - row_start + 1; + pos -= row_size; + memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + + /* almost done, copy page over current */ + memcpy(mdb->pg_buf, new_pg, fmt->pg_size); + + _mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb)); +#if MDB_DEBUG_WRITE + buffer_dump(mdb->pg_buf, 0, 39); + buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1); +#endif + /* drum roll, please */ + if (!mdb_write_pg(mdb, table->cur_phys_pg)) { + fprintf(stderr, "write failed! exiting...\n"); + exit(1); + } + } diff --git a/src/util/mdb-ver.c b/src/util/mdb-ver.c index 44699c7..41cb1eb 100644 --- a/src/util/mdb-ver.c +++ b/src/util/mdb-ver.c @@ -50,13 +50,12 @@ int opt; if (!(mdb = mdb_open(argv[optind]))) { exit(1); } - switch (mdb->jet_version) { - case MDB_VER_JET3: - printf("JET3\n"); - break; - case MDB_VER_JET4: - printf("JET4\n"); - break; + if (IS_JET3(mdb)) { + printf("JET3\n"); + } else if (IS_JET4(mdb)) { + printf("JET4\n"); + } else { + printf("unknown\n"); } mdb_free_handle(mdb); diff --git a/src/util/updrow.c b/src/util/updrow.c index 993155d..55a336d 100644 --- a/src/util/updrow.c +++ b/src/util/updrow.c @@ -45,7 +45,7 @@ int len; } mdb_init(); - mdb = mdb_open(argv[1]); + mdb = _mdb_open(argv[1], TRUE); tabname = argv[2]; sargname = argv[3]; updstr = strdup(argv[4]); @@ -64,8 +64,10 @@ int len; colval = strtok(NULL,"="); bind_column(table, colname, data, &len); read_to_row(table, sargname); - mdb_update_row(table); printf("current value of %s is %s, changing to %s\n", colname, data, colval); + len = strlen(colval); + strcpy(data,colval); + mdb_update_row(table); } }