2000-03-01 11:34:33 +08:00
/* MDB Tools - A library for reading MS Access database file
* Copyright ( C ) 2000 Brian Bruns
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 02111 - 1307 , USA .
*/
# include "mdbtools.h"
2003-01-29 07:51:06 +08:00
# ifdef DMALLOC
# include "dmalloc.h"
# endif
2002-12-27 23:09:02 +08:00
2002-02-03 10:49:08 +08:00
static gint mdb_col_comparer ( MdbColumn * a , MdbColumn * b )
{
if ( a - > col_num > b - > col_num )
return 1 ;
else if ( a - > col_num < b - > col_num )
return - 1 ;
else
return 0 ;
}
2000-03-01 11:34:33 +08:00
unsigned char mdb_col_needs_size ( int col_type )
{
if ( col_type = = MDB_TEXT ) {
return TRUE ;
} else {
return FALSE ;
}
}
2004-02-12 06:05:13 +08:00
MdbTableDef *
mdb_read_table ( MdbCatalogEntry * entry )
2000-03-05 01:31:07 +08:00
{
2004-02-12 06:05:13 +08:00
MdbTableDef * table ;
MdbHandle * mdb = entry - > mdb ;
MdbFormatConstants * fmt = mdb - > fmt ;
int len ;
int rownum , row_start , row_end ;
guint32 pg ;
2000-03-05 01:31:07 +08:00
table = mdb_alloc_tabledef ( entry ) ;
mdb_read_pg ( mdb , entry - > table_pg ) ;
2004-01-11 05:46:14 +08:00
if ( mdb - > pg_buf [ 0 ] ! = 0x02 ) return NULL ; /* not a valid table def page */
2003-04-30 01:55:09 +08:00
len = mdb_pg_get_int16 ( mdb , 8 ) ;
2000-03-05 01:31:07 +08:00
2003-04-30 01:55:09 +08:00
table - > num_rows = mdb_pg_get_int32 ( mdb , fmt - > tab_num_rows_offset ) ;
table - > num_cols = mdb_pg_get_int16 ( mdb , fmt - > tab_num_cols_offset ) ;
table - > num_idxs = mdb_pg_get_int32 ( mdb , fmt - > tab_num_idxs_offset ) ;
table - > num_real_idxs = mdb_pg_get_int32 ( mdb , fmt - > tab_num_ridxs_offset ) ;
2002-12-20 14:17:41 +08:00
/* grab a copy of the usage map */
2003-01-02 06:29:39 +08:00
rownum = mdb - > pg_buf [ fmt - > tab_usage_map_offset ] ;
2004-02-12 06:05:13 +08:00
pg = mdb_pg_get_int24 ( mdb , fmt - > tab_usage_map_offset + 1 ) ;
mdb_read_alt_pg ( mdb , pg ) ;
2002-12-20 14:17:41 +08:00
mdb_swap_pgbuf ( mdb ) ;
2003-04-30 01:55:09 +08:00
row_start = mdb_pg_get_int16 ( mdb , ( fmt - > row_count_offset + 2 ) + ( rownum * 2 ) ) ;
2003-02-10 07:19:21 +08:00
row_end = mdb_find_end_of_row ( mdb , rownum ) ;
2002-12-27 23:09:02 +08:00
table - > map_sz = row_end - row_start + 1 ;
2004-05-30 13:06:26 +08:00
table - > usage_map = g_memdup ( & mdb - > pg_buf [ row_start ] , table - > map_sz ) ;
2004-02-16 10:00:45 +08:00
if ( mdb_get_option ( MDB_DEBUG_USAGE ) )
buffer_dump ( mdb - > pg_buf , row_start , row_end ) ;
2002-12-20 14:17:41 +08:00
/* swap back */
mdb_swap_pgbuf ( mdb ) ;
2004-02-16 10:00:45 +08:00
mdb_debug ( MDB_DEBUG_USAGE , " usage map found on page %ld rownum %d start %d end %d " , mdb_pg_get_int24 ( mdb , fmt - > tab_usage_map_offset + 1 ) , rownum , row_start , row_end ) ;
2002-12-20 14:17:41 +08:00
2003-02-10 07:19:21 +08:00
/* now grab the free space page map */
2004-01-10 05:05:56 +08:00
# if 1
//mdb_swap_pgbuf(mdb);
2003-02-10 07:19:21 +08:00
rownum = mdb - > pg_buf [ fmt - > tab_free_map_offset ] ;
2003-04-30 01:55:09 +08:00
mdb_read_alt_pg ( mdb , mdb_pg_get_int24 ( mdb , fmt - > tab_free_map_offset + 1 ) ) ;
2003-02-10 07:19:21 +08:00
mdb_swap_pgbuf ( mdb ) ;
2003-04-30 01:55:09 +08:00
row_start = mdb_pg_get_int16 ( mdb , ( fmt - > row_count_offset + 2 ) + ( rownum * 2 ) ) ;
2003-02-10 07:19:21 +08:00
row_end = mdb_find_end_of_row ( mdb , rownum ) ;
table - > freemap_sz = row_end - row_start + 1 ;
2004-05-30 13:06:26 +08:00
table - > free_usage_map = g_memdup ( & mdb - > pg_buf [ row_start ] , table - > freemap_sz ) ;
2004-01-10 05:05:56 +08:00
mdb_swap_pgbuf ( mdb ) ;
2003-04-30 01:55:09 +08:00
# endif
2004-02-16 10:00:45 +08:00
mdb_debug ( MDB_DEBUG_USAGE , " free map found on page %ld rownum %d start %d end %d \n " , mdb_pg_get_int24 ( mdb , fmt - > tab_free_map_offset + 1 ) , rownum , row_start , row_end ) ;
2003-02-10 07:19:21 +08:00
2003-04-30 01:55:09 +08:00
table - > first_data_pg = mdb_pg_get_int16 ( mdb , fmt - > tab_first_dpg_offset ) ;
2000-03-05 01:31:07 +08:00
return table ;
}
2000-10-14 05:33:04 +08:00
/*
* * read the next page if offset is > pg_size
* * return true if page was read
*/
2003-01-29 07:51:06 +08:00
int
read_pg_if ( MdbHandle * mdb , int * cur_pos , int offset )
2000-10-14 05:33:04 +08:00
{
2003-01-02 06:29:39 +08:00
if ( * cur_pos + offset > = mdb - > fmt - > pg_size ) {
2003-04-30 01:55:09 +08:00
mdb_read_pg ( mdb , mdb_pg_get_int32 ( mdb , 4 ) ) ;
2003-01-02 06:29:39 +08:00
* cur_pos = 8 - ( mdb - > fmt - > pg_size - ( * cur_pos ) ) ;
2000-10-14 05:33:04 +08:00
return 1 ;
}
return 0 ;
}
2003-01-29 07:51:06 +08:00
guint32
read_pg_if_32 ( MdbHandle * mdb , int * cur_pos )
{
unsigned char c [ 4 ] ;
int i , rc = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
rc + = read_pg_if ( mdb , cur_pos , i ) ;
c [ i ] = mdb - > pg_buf [ ( * cur_pos ) + i ] ;
}
2003-04-30 01:55:09 +08:00
return mdb_get_int32 ( c , 0 ) ;
2003-01-29 07:51:06 +08:00
}
guint16
read_pg_if_16 ( MdbHandle * mdb , int * cur_pos )
{
unsigned char low_byte , high_byte ;
int rc = 0 ;
rc + = read_pg_if ( mdb , cur_pos , 0 ) ;
low_byte = mdb - > pg_buf [ * cur_pos ] ;
rc + = read_pg_if ( mdb , cur_pos , 1 ) ;
high_byte = mdb - > pg_buf [ ( * cur_pos ) + 1 ] ;
return ( high_byte * 256 + low_byte ) ;
}
guint16
read_pg_if_n ( MdbHandle * mdb , unsigned char * buf , int * cur_pos , int len )
{
if ( * cur_pos + len < mdb - > fmt - > pg_size ) {
memcpy ( buf , & mdb - > pg_buf [ * cur_pos ] , len ) ;
return 0 ;
2004-06-12 05:57:19 +08:00
} else {
int half = mdb - > fmt - > pg_size - * cur_pos ;
memcpy ( buf , & mdb - > pg_buf [ * cur_pos ] , half ) ;
mdb_read_pg ( mdb , mdb_pg_get_int32 ( mdb , 4 ) ) ;
memcpy ( buf + half , & mdb - > pg_buf [ 8 ] , len - half ) ;
* cur_pos = 8 - half ;
return 1 ;
2003-01-29 07:51:06 +08:00
}
}
2000-10-14 05:33:04 +08:00
2000-03-12 22:08:53 +08:00
GPtrArray * mdb_read_columns ( MdbTableDef * table )
2000-03-01 11:34:33 +08:00
{
2004-01-06 08:42:07 +08:00
MdbHandle * mdb = table - > entry - > mdb ;
MdbFormatConstants * fmt = mdb - > fmt ;
MdbColumn col , * pcol ;
int len , i , j ;
unsigned char low_byte , high_byte ;
int cur_col , cur_name ;
int name_sz ;
GSList * slist = NULL ;
2000-03-05 21:10:42 +08:00
2000-03-12 22:08:53 +08:00
table - > columns = g_ptr_array_new ( ) ;
2000-03-05 01:31:07 +08:00
2003-01-02 06:29:39 +08:00
cur_col = fmt - > tab_cols_start_offset +
( table - > num_real_idxs * fmt - > tab_ridx_entry_size ) ;
2000-03-01 11:34:33 +08:00
2000-10-14 05:33:04 +08:00
/* new code based on patch submitted by Tim Nelson 2000.09.27 */
2000-03-01 11:34:33 +08:00
2000-10-14 05:33:04 +08:00
/*
* * column attributes
*/
2002-02-03 10:49:08 +08:00
for ( i = 0 ; i < table - > num_cols ; i + + ) {
2002-12-11 07:35:24 +08:00
# ifdef MDB_DEBUG
/* printf("column %d\n", i);
buffer_dump ( mdb - > pg_buf , cur_col , cur_col + 18 ) ; */
# endif
2002-02-03 10:49:08 +08:00
memset ( & col , 0 , sizeof ( col ) ) ;
2000-03-05 21:10:42 +08:00
2000-10-14 05:33:04 +08:00
read_pg_if ( mdb , & cur_col , 0 ) ;
2000-03-05 21:10:42 +08:00
col . col_type = mdb - > pg_buf [ cur_col ] ;
2000-10-14 05:33:04 +08:00
2004-06-12 05:57:19 +08:00
read_pg_if ( mdb , & cur_col , fmt - > col_num_offset ) ; // col_num_offset == 1 or 5
2004-01-06 08:42:07 +08:00
col . col_num = mdb - > pg_buf [ cur_col + fmt - > col_num_offset ] ;
2004-03-05 05:25:09 +08:00
//fprintf(stdout,"----- column %d -----\n",col.col_num);
read_pg_if ( mdb , & cur_col , fmt - > tab_col_offset_var ) ; // col_var == 3 or 7
low_byte = mdb - > pg_buf [ cur_col + fmt - > tab_col_offset_var ] ;
read_pg_if ( mdb , & cur_col , fmt - > tab_col_offset_var + 1 ) ;
high_byte = mdb - > pg_buf [ cur_col + fmt - > tab_col_offset_var + 1 ] ;
col . var_col_num = high_byte * 256 + low_byte ;
//fprintf(stdout,"var column pos %d\n",col.var_col_num);
read_pg_if ( mdb , & cur_col , fmt - > tab_row_col_num_offset ) ; // col_var == 5 or 9
low_byte = mdb - > pg_buf [ cur_col + fmt - > tab_row_col_num_offset ] ;
read_pg_if ( mdb , & cur_col , fmt - > tab_row_col_num_offset + 1 ) ;
high_byte = mdb - > pg_buf [ cur_col + fmt - > tab_row_col_num_offset + 1 ] ;
col . row_col_num = high_byte * 256 + low_byte ;
//fprintf(stdout,"row column num %d\n",col.row_col_num);
2004-01-06 08:42:07 +08:00
/* FIXME: can this be right in Jet3 and Jet4? */
2002-12-11 07:35:24 +08:00
if ( col . col_type = = MDB_NUMERIC ) {
2004-01-06 08:42:07 +08:00
read_pg_if ( mdb , & cur_col , 11 ) ;
2002-12-11 07:35:24 +08:00
col . col_prec = mdb - > pg_buf [ cur_col + 11 ] ;
2004-01-06 08:42:07 +08:00
read_pg_if ( mdb , & cur_col , 12 ) ;
2002-12-11 07:35:24 +08:00
col . col_scale = mdb - > pg_buf [ cur_col + 12 ] ;
}
2000-03-01 11:34:33 +08:00
2004-01-06 08:42:07 +08:00
read_pg_if ( mdb , & cur_col , fmt - > col_fixed_offset ) ; // col_fixed_offset == 13 or 15
2003-01-02 06:29:39 +08:00
col . is_fixed = mdb - > pg_buf [ cur_col + fmt - > col_fixed_offset ] &
2004-01-06 08:42:07 +08:00
0x01 ? 1 : 0 ;
2004-03-05 05:25:09 +08:00
read_pg_if ( mdb , & cur_col , fmt - > tab_col_offset_fixed ) ; // col_fixed_offset == 13 or 15
low_byte = mdb - > pg_buf [ cur_col + fmt - > tab_col_offset_fixed ] ;
read_pg_if ( mdb , & cur_col , fmt - > tab_col_offset_fixed + 1 ) ;
high_byte = mdb - > pg_buf [ cur_col + fmt - > tab_col_offset_fixed + 1 ] ;
col . fixed_offset = high_byte * 256 + low_byte ;
//fprintf(stdout,"fixed column offset %d\n",col.fixed_offset);
//fprintf(stdout,"col type %s\n",col.is_fixed ? "fixed" : "variable");
2002-03-16 10:24:53 +08:00
if ( col . col_type ! = MDB_BOOL ) {
2004-01-06 08:42:07 +08:00
read_pg_if ( mdb , & cur_col , fmt - > col_size_offset ) ; // col_size_offset == 16 or 23
2003-01-02 06:29:39 +08:00
low_byte = mdb - > pg_buf [ cur_col + fmt - > col_size_offset ] ;
2004-01-06 08:42:07 +08:00
read_pg_if ( mdb , & cur_col , fmt - > col_size_offset + 1 ) ;
2003-01-02 06:29:39 +08:00
high_byte = mdb - > pg_buf [ cur_col + fmt - > col_size_offset + 1 ] ;
2002-03-16 10:24:53 +08:00
col . col_size + = high_byte * 256 + low_byte ;
} else
col . col_size = 0 ;
2002-02-03 10:49:08 +08:00
pcol = g_memdup ( & col , sizeof ( MdbColumn ) ) ;
slist = g_slist_insert_sorted ( slist , pcol , ( GCompareFunc ) mdb_col_comparer ) ;
2003-01-02 06:29:39 +08:00
cur_col + = fmt - > tab_col_entry_size ;
2000-03-05 21:10:42 +08:00
}
2000-10-14 05:33:04 +08:00
cur_name = cur_col ;
/*
* * column names
*/
for ( i = 0 ; i < table - > num_cols ; i + + ) {
/* fetch the column */
2002-02-03 10:49:08 +08:00
pcol = g_slist_nth_data ( slist , i ) ;
2000-10-14 05:33:04 +08:00
/* we have reached the end of page */
read_pg_if ( mdb , & cur_name , 0 ) ;
name_sz = mdb - > pg_buf [ cur_name ] ;
2003-01-02 06:29:39 +08:00
if ( IS_JET4 ( mdb ) ) {
2002-02-03 10:49:08 +08:00
/* FIX ME - for now just skip the high order byte */
cur_name + = 2 ;
/* determine amount of name on this page */
2003-01-02 06:29:39 +08:00
len = ( ( cur_name + name_sz ) > fmt - > pg_size ) ?
fmt - > pg_size - cur_name :
2002-02-03 10:49:08 +08:00
name_sz ;
/* strip high order (second) byte from unicode string */
for ( j = 0 ; j < len ; j + = 2 ) {
pcol - > name [ j / 2 ] = mdb - > pg_buf [ cur_name + j ] ;
}
/* name wrapped over page */
if ( len < name_sz ) {
/* read the next pg */
2003-04-30 01:55:09 +08:00
mdb_read_pg ( mdb , mdb_pg_get_int32 ( mdb , 4 ) ) ;
2003-01-02 06:29:39 +08:00
cur_name = 8 - ( fmt - > pg_size - cur_name ) ;
2002-02-03 10:49:08 +08:00
/* get the rest of the name */
2004-01-06 08:42:07 +08:00
for ( j = len ; j < name_sz ; j + + ) {
if ( ! ( j % 2 ) )
pcol - > name [ j / 2 ] = mdb - > pg_buf [ cur_name + j ] ;
2002-02-03 10:49:08 +08:00
}
}
2004-01-06 08:42:07 +08:00
pcol - > name [ name_sz / 2 ] = ' \0 ' ;
2002-02-03 10:49:08 +08:00
cur_name + = name_sz ;
2003-01-02 06:29:39 +08:00
} else if ( IS_JET3 ( mdb ) ) {
2002-02-03 10:49:08 +08:00
/* determine amount of name on this page */
2003-01-02 06:29:39 +08:00
len = ( ( cur_name + name_sz ) > fmt - > pg_size ) ?
fmt - > pg_size - cur_name :
2002-02-03 10:49:08 +08:00
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 */
2003-04-30 01:55:09 +08:00
mdb_read_pg ( mdb , mdb_pg_get_int32 ( mdb , 4 ) ) ;
2003-01-02 06:29:39 +08:00
cur_name = 8 - ( fmt - > pg_size - cur_name ) ;
2002-02-03 10:49:08 +08:00
/* 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 ;
} else {
fprintf ( stderr , " Unknown MDB version \n " ) ;
2000-10-14 05:33:04 +08:00
}
}
2002-02-03 10:49:08 +08:00
/* turn this list into an array */
for ( i = 0 ; i < table - > num_cols ; i + + ) {
pcol = g_slist_nth_data ( slist , i ) ;
g_ptr_array_add ( table - > columns , pcol ) ;
}
g_slist_free ( slist ) ;
2001-04-02 06:10:15 +08:00
table - > index_start = cur_name ;
2000-03-05 21:10:42 +08:00
return table - > columns ;
}
void mdb_table_dump ( MdbCatalogEntry * entry )
{
MdbTableDef * table ;
2000-03-12 22:08:53 +08:00
MdbColumn * col ;
2003-01-02 06:29:39 +08:00
int coln ;
2001-04-02 06:10:15 +08:00
MdbIndex * idx ;
2000-03-05 21:10:42 +08:00
MdbHandle * mdb = entry - > mdb ;
2002-12-20 14:17:41 +08:00
int i , bitn ;
2003-01-21 00:04:24 +08:00
guint32 pgnum ;
2000-03-05 21:10:42 +08:00
table = mdb_read_table ( entry ) ;
2003-01-13 06:59:41 +08:00
fprintf ( stdout , " definition page = %lu \n " , entry - > table_pg ) ;
2000-03-05 21:10:42 +08:00
fprintf ( stdout , " number of datarows = %d \n " , table - > num_rows ) ;
fprintf ( stdout , " number of columns = %d \n " , table - > num_cols ) ;
2001-04-02 06:10:15 +08:00
fprintf ( stdout , " number of indices = %d \n " , table - > num_real_idxs ) ;
2000-03-05 21:10:42 +08:00
2000-03-09 12:48:59 +08:00
mdb_read_columns ( table ) ;
2001-04-02 06:10:15 +08:00
mdb_read_indices ( table ) ;
2000-03-05 21:10:42 +08:00
for ( i = 0 ; i < table - > num_cols ; i + + ) {
2000-03-12 22:08:53 +08:00
col = g_ptr_array_index ( table - > columns , i ) ;
2000-03-05 21:10:42 +08:00
2000-03-09 12:48:59 +08:00
fprintf ( stdout , " column %d Name: %-20s Type: %s(%d) \n " ,
2000-03-12 22:08:53 +08:00
i , col - > name ,
2000-04-03 01:08:30 +08:00
mdb_get_coltype_string ( mdb - > default_backend , col - > col_type ) ,
2000-03-12 22:08:53 +08:00
col - > col_size ) ;
2000-03-01 11:34:33 +08:00
}
2001-04-02 06:10:15 +08:00
for ( i = 0 ; i < table - > num_idxs ; i + + ) {
idx = g_ptr_array_index ( table - > indices , i ) ;
2001-05-23 09:42:46 +08:00
mdb_index_dump ( table , idx ) ;
2001-04-02 06:10:15 +08:00
}
2002-12-20 14:17:41 +08:00
if ( table - > usage_map ) {
2003-01-21 00:04:24 +08:00
printf ( " pages reserved by this object \n " ) ;
2004-02-12 06:05:13 +08:00
printf ( " usage map pg %lu \n " , table - > map_base_pg ) ;
printf ( " free map pg %lu \n " , table - > freemap_base_pg ) ;
2003-04-30 01:55:09 +08:00
pgnum = mdb_get_int32 ( table - > usage_map , 1 ) ;
2002-12-20 14:17:41 +08:00
/* the first 5 bytes of the usage map mean something */
2003-01-02 06:29:39 +08:00
coln = 0 ;
2002-12-20 14:17:41 +08:00
for ( i = 5 ; i < table - > map_sz ; i + + ) {
for ( bitn = 0 ; bitn < 8 ; bitn + + ) {
2003-01-02 06:29:39 +08:00
if ( table - > usage_map [ i ] & 1 < < bitn ) {
coln + + ;
2003-01-21 00:04:24 +08:00
printf ( " %6lu " , ( long unsigned ) pgnum ) ;
2003-01-02 06:29:39 +08:00
if ( coln = = 10 ) {
printf ( " \n " ) ;
coln = 0 ;
}
}
2002-12-20 14:17:41 +08:00
pgnum + + ;
}
}
2004-02-12 06:05:13 +08:00
printf ( " \n " ) ;
2002-12-20 14:17:41 +08:00
}
2000-03-01 11:34:33 +08:00
}