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
2004-06-15 12:12:36 +08:00
static gint mdb_col_comparer ( MdbColumn * * a , MdbColumn * * b )
2002-02-03 10:49:08 +08:00
{
2004-06-15 12:12:36 +08:00
if ( ( * a ) - > col_num > ( * b ) - > col_num )
2002-02-03 10:49:08 +08:00
return 1 ;
2004-06-15 12:12:36 +08:00
else if ( ( * a ) - > col_num < ( * b ) - > col_num )
2002-02-03 10:49:08 +08:00
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-06-23 20:31:54 +08:00
MdbTableDef * mdb_alloc_tabledef ( MdbCatalogEntry * entry )
{
MdbTableDef * table ;
table = ( MdbTableDef * ) g_malloc0 ( sizeof ( MdbTableDef ) ) ;
table - > entry = entry ;
strcpy ( table - > name , entry - > object_name ) ;
return table ;
}
void mdb_free_tabledef ( MdbTableDef * table )
{
if ( ! table ) return ;
mdb_free_columns ( table - > columns ) ;
2004-06-24 12:22:40 +08:00
mdb_free_indices ( table - > indices ) ;
2004-06-23 20:31:54 +08:00
g_free ( table - > usage_map ) ;
g_free ( table - > free_usage_map ) ;
g_free ( table ) ;
}
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 ;
}
2004-07-02 20:29:09 +08:00
MdbTableDef * mdb_read_table_by_name ( MdbHandle * mdb , gchar * table_name , int obj_type )
{
unsigned int i ;
MdbCatalogEntry * entry ;
mdb_read_catalog ( mdb , obj_type ) ;
for ( i = 0 ; i < mdb - > num_catalog ; i + + ) {
entry = g_ptr_array_index ( mdb - > catalog , i ) ;
if ( ! strcasecmp ( entry - > object_name , table_name ) )
return mdb_read_table ( entry ) ;
}
return NULL ;
}
2000-03-05 01:31:07 +08:00
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
2004-06-23 20:31:54 +08:00
void mdb_append_column ( GPtrArray * columns , MdbColumn * in_col )
{
g_ptr_array_add ( columns , g_memdup ( in_col , sizeof ( MdbColumn ) ) ) ;
}
void mdb_free_columns ( GPtrArray * columns )
{
unsigned int i ;
if ( ! columns ) return ;
for ( i = 0 ; i < columns - > len ; i + + )
g_free ( g_ptr_array_index ( columns , i ) ) ;
g_ptr_array_free ( columns , TRUE ) ;
}
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 ;
2004-06-14 20:13:25 +08:00
MdbColumn * pcol ;
unsigned char * col ;
int i , cur_pos , name_sz ;
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
2004-06-14 20:13:25 +08:00
col = ( unsigned char * ) g_malloc ( fmt - > tab_col_entry_size ) ;
cur_pos = fmt - > tab_cols_start_offset +
2003-01-02 06:29:39 +08:00
( 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);
2004-06-14 20:13:25 +08:00
buffer_dump ( mdb - > pg_buf , cur_pos , cur_pos + 18 ) ; */
2002-12-11 07:35:24 +08:00
# endif
2004-06-14 20:13:25 +08:00
read_pg_if_n ( mdb , col , & cur_pos , fmt - > tab_col_entry_size ) ;
cur_pos + = fmt - > tab_col_entry_size ;
pcol = ( MdbColumn * ) g_malloc0 ( sizeof ( MdbColumn ) ) ;
2000-03-05 21:10:42 +08:00
2004-06-14 20:13:25 +08:00
pcol - > col_type = col [ 0 ] ;
2000-10-14 05:33:04 +08:00
2004-06-14 20:13:25 +08:00
// col_num_offset == 1 or 5
pcol - > col_num = col [ fmt - > col_num_offset ] ;
2004-01-06 08:42:07 +08:00
2004-06-14 20:13:25 +08:00
//fprintf(stdout,"----- column %d -----\n",pcol->col_num);
// col_var == 3 or 7
pcol - > var_col_num = mdb_get_int16 ( col , fmt - > tab_col_offset_var ) ;
//fprintf(stdout,"var column pos %d\n",pcol->var_col_num);
2004-03-05 05:25:09 +08:00
2004-06-14 20:13:25 +08:00
// col_var == 5 or 9
pcol - > row_col_num = mdb_get_int16 ( col , fmt - > tab_row_col_num_offset ) ;
//fprintf(stdout,"row column num %d\n",pcol->row_col_num);
2004-03-05 05:25:09 +08:00
2004-01-06 08:42:07 +08:00
/* FIXME: can this be right in Jet3 and Jet4? */
2004-06-14 20:13:25 +08:00
if ( pcol - > col_type = = MDB_NUMERIC ) {
pcol - > col_prec = col [ 11 ] ;
pcol - > col_scale = col [ 12 ] ;
2002-12-11 07:35:24 +08:00
}
2000-03-01 11:34:33 +08:00
2004-06-14 20:13:25 +08:00
// col_fixed_offset == 13 or 15
pcol - > is_fixed = col [ fmt - > col_fixed_offset ] & 0x01 ? 1 : 0 ;
// col_fixed_offset == 13 or 15
pcol - > fixed_offset = mdb_get_int16 ( col , fmt - > tab_col_offset_fixed ) ;
//fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset);
//fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable");
if ( pcol - > col_type ! = MDB_BOOL ) {
// col_size_offset == 16 or 23
pcol - > col_size = mdb_get_int16 ( col , fmt - > col_size_offset ) ;
} else {
pcol - > col_size = 0 ;
}
2002-02-03 10:49:08 +08:00
2004-06-15 12:12:36 +08:00
g_ptr_array_add ( table - > columns , pcol ) ;
2000-03-05 21:10:42 +08:00
}
2004-06-14 20:13:25 +08:00
g_free ( col ) ;
2000-10-14 05:33:04 +08:00
/*
2004-06-15 12:12:36 +08:00
* * column names - ordered the same as the column attributes table
2000-10-14 05:33:04 +08:00
*/
for ( i = 0 ; i < table - > num_cols ; i + + ) {
2004-06-15 12:12:36 +08:00
pcol = g_ptr_array_index ( table - > columns , i ) ;
2000-10-14 05:33:04 +08:00
2003-01-02 06:29:39 +08:00
if ( IS_JET4 ( mdb ) ) {
2004-06-14 20:13:25 +08:00
char * tmp_buf ;
name_sz = read_pg_if_16 ( mdb , & cur_pos ) ;
cur_pos + = 2 ;
tmp_buf = ( char * ) g_malloc ( name_sz ) ;
read_pg_if_n ( mdb , tmp_buf , & cur_pos , name_sz ) ;
mdb_unicode2ascii ( mdb , tmp_buf , 0 , name_sz , pcol - > name ) ;
g_free ( tmp_buf ) ;
cur_pos + = name_sz ;
2003-01-02 06:29:39 +08:00
} else if ( IS_JET3 ( mdb ) ) {
2004-06-14 20:13:25 +08:00
read_pg_if ( mdb , & cur_pos , 0 ) ;
name_sz = mdb - > pg_buf [ cur_pos ] ;
cur_pos + + ;
read_pg_if_n ( mdb , pcol - > name , & cur_pos , name_sz ) ;
2002-02-03 10:49:08 +08:00
pcol - > name [ name_sz ] = ' \0 ' ;
2004-06-14 20:13:25 +08:00
cur_pos + = name_sz ;
2002-02-03 10:49:08 +08:00
} else {
fprintf ( stderr , " Unknown MDB version \n " ) ;
2000-10-14 05:33:04 +08:00
}
}
2002-02-03 10:49:08 +08:00
2004-06-15 12:12:36 +08:00
/* Sort the columns by col_num */
g_ptr_array_sort ( table - > columns , ( GCompareFunc ) mdb_col_comparer ) ;
2002-02-03 10:49:08 +08:00
2004-06-14 20:13:25 +08:00
table - > index_start = cur_pos ;
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
}