1 /* MDB Tools - A library for reading MS Access database files
2 * Copyright (C) 2000 Brian Bruns
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
29 guint16 row_count_offset;
30 guint16 tab_num_rows_offset;
31 guint16 tab_num_cols_offset;
32 guint16 tab_num_idxs_offset;
33 guint16 tab_num_ridxs_offset;
34 guint16 tab_usage_map_offset;
35 guint16 tab_first_dpg_offset;
36 guint16 tab_cols_start_offset;
37 guint16 tab_ridx_entry_size;
38 guint16 col_fixed_offset;
39 guint16 col_size_offset;
40 guint16 col_num_offset;
41 guint16 tab_col_entry_size;
42 guint16 tab_free_map_offset;
43 guint16 tab_col_offset_var;
44 guint16 tab_col_offset_fixed;
45 guint16 tab_row_col_num_offset;
48 MdbFormatConstants MdbJet4Constants = {
49 4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9
51 MdbFormatConstants MdbJet3Constants = {
52 2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5 /* not sure on 5, need to check */
55 static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg);
59 * @filename: path to MDB (database) file
61 * Finds and returns the absolute path to an MDB file. Function will first try
62 * to fstat file as passed, then search through the $MDBPATH if not found.
64 * Return value: gchar pointer to absolute path. Caller is responsible for
68 static gchar *mdb_find_file(char *file_name)
71 gchar *mdbpath, **dir, *tmpfname;
74 /* try the provided file name first */
75 if (!stat(file_name, &status)) {
76 return g_strdup(file_name);
79 /* Now pull apart $MDBPATH and try those */
80 mdbpath = (gchar *) getenv("MDBPATH");
81 /* no path, can't find file */
82 if (!mdbpath || !strlen(mdbpath)) return NULL;
84 dir = g_strsplit(mdbpath, ":", 0);
86 if (!strlen(dir[i])) continue;
87 tmpfname = g_strconcat(dir[i++], "/", file_name, NULL);
88 if (!stat(tmpfname, &status)) {
99 * @filename: path to MDB (database) file
100 * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write
102 * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative
103 * to the current directory, a full path to the file, or relative to a
104 * component of $MDBPATH.
106 * Return value: pointer to MdbHandle structure.
108 MdbHandle *mdb_open(char *filename, MdbFileFlags flags)
112 mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle));
113 mdb_set_default_backend(mdb, "access");
114 /* need something to bootstrap with, reassign after page 0 is read */
115 mdb->fmt = &MdbJet3Constants;
116 mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile));
119 mdb->f->filename = (char *) mdb_find_file(filename);
120 if (!mdb->f->filename) {
121 fprintf(stderr, "Can't alloc filename\n");
125 if (flags & MDB_WRITABLE) {
126 mdb->f->writable = TRUE;
127 mdb->f->fd = open(mdb->f->filename,O_RDWR);
129 mdb->f->fd = open(mdb->f->filename,O_RDONLY);
132 if (mdb->f->fd==-1) {
133 fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename);
137 if (!mdb_read_pg(mdb, 0)) {
138 fprintf(stderr,"Couldn't read first page.\n");
142 if (mdb->pg_buf[0] != 0) {
146 mdb->f->jet_version = mdb_pg_get_int32(mdb, 0x14);
148 mdb->fmt = &MdbJet4Constants;
149 } else if (IS_JET3(mdb)) {
150 mdb->fmt = &MdbJet3Constants;
152 fprintf(stderr,"Unknown Jet version.\n");
162 * @mdb: Handle to open MDB database file
164 * Dereferences MDB file, closes if reference count is 0, and destroys handle.
168 mdb_close(MdbHandle *mdb)
171 mdb_free_catalog(mdb);
173 g_free(mdb->backend_name);
176 if (mdb->f->refs > 1) {
179 if (mdb->f->fd != -1) close(mdb->f->fd);
180 g_free(mdb->f->filename);
189 * @mdb: Handle to open MDB database file
191 * Clones an existing database handle. Cloned handle shares the file descriptor
192 * but has its own page buffer, page position, and similar internal variables.
194 * Return value: new handle to the database.
196 MdbHandle *mdb_clone_handle(MdbHandle *mdb)
199 MdbCatalogEntry *entry, *data;
202 newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle));
203 newmdb->stats = NULL;
204 newmdb->catalog = g_ptr_array_new();
205 for (i=0;i<mdb->num_catalog;i++) {
206 entry = g_ptr_array_index(mdb->catalog,i);
207 data = g_memdup(entry,sizeof(MdbCatalogEntry));
208 g_ptr_array_add(newmdb->catalog, data);
210 mdb->backend_name = NULL;
218 ** mdb_read a wrapper for read that bails if anything is wrong
220 ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg)
224 if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size;
226 len = _mdb_read_pg(mdb, mdb->pg_buf, pg);
227 //fprintf(stderr, "read page %d type %02x\n", pg, mdb->pg_buf[0]);
229 /* kan - reset the cur_pos on a new page read */
230 mdb->cur_pos = 0; /* kan */
233 ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg)
237 len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg);
240 static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg)
244 off_t offset = pg * mdb->fmt->pg_size;
246 fstat(mdb->f->fd, &status);
247 if (status.st_size < offset) {
248 fprintf(stderr,"offset %lu is beyond EOF\n",offset);
251 if (mdb->stats && mdb->stats->collect)
252 mdb->stats->pg_reads++;
254 lseek(mdb->f->fd, offset, SEEK_SET);
255 len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size);
260 else if (len<mdb->fmt->pg_size) {
261 /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */
266 void mdb_swap_pgbuf(MdbHandle *mdb)
268 char tmpbuf[MDB_PGSIZE];
270 memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE);
271 memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE);
272 memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE);
276 /* really stupid, just here for consistancy */
277 unsigned char mdb_get_byte(unsigned char *buf, int offset)
281 unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset)
283 if (offset < 0 || offset+1 > mdb->fmt->pg_size) return -1;
285 return mdb->pg_buf[offset];
288 int mdb_get_int16(unsigned char *buf, int offset)
290 return buf[offset+1]*256+buf[offset];
292 int mdb_pg_get_int16(MdbHandle *mdb, int offset)
294 if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1;
296 return mdb_get_int16(mdb->pg_buf, offset);
299 gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset)
302 if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
304 memcpy((char *)&l+1, &(mdb->pg_buf[offset]), 3);
306 printf("l=0x%08x 0x%08x\n",l,GINT32_FROM_BE(l));
308 return GINT32_FROM_BE(l);
310 gint32 mdb_get_int24(unsigned char *buf, int offset)
313 memcpy(&l, &buf[offset], 3);
314 return GINT32_FROM_LE(l);
316 gint32 mdb_pg_get_int24(MdbHandle *mdb, int offset)
318 if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
320 return mdb_get_int24(mdb->pg_buf, offset);
323 long mdb_get_int32(unsigned char *buf, int offset)
326 memcpy(&l, &buf[offset], 4);
327 return (long)GINT32_FROM_LE(l);
329 long mdb_pg_get_int32(MdbHandle *mdb, int offset)
331 if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
333 return mdb_get_int32(mdb->pg_buf, offset);
336 float mdb_get_single(unsigned char *buf, int offset)
338 union {guint32 g; float f;} f;
339 memcpy(&f, &buf[offset], 4);
340 f.g = GUINT32_FROM_LE(f.g);
343 float mdb_pg_get_single(MdbHandle *mdb, int offset)
345 if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
347 return mdb_get_single(mdb->pg_buf, offset);
350 double mdb_get_double(unsigned char *buf, int offset)
352 union {guint64 g; double d;} d;
353 memcpy(&d, &buf[offset], 8);
354 d.g = GUINT64_FROM_LE(d.g);
357 double mdb_pg_get_double(MdbHandle *mdb, int offset)
359 if (offset <0 || offset+8 > mdb->fmt->pg_size) return -1;
361 return mdb_get_double(mdb->pg_buf, offset);
366 mdb_set_pos(MdbHandle *mdb, int pos)
368 if (pos<0 || pos >= mdb->fmt->pg_size) return 0;
373 int mdb_get_pos(MdbHandle *mdb)