Initial import

pull/1/head
Timothy Pearson 9 years ago
commit 2706f30ee0

@ -0,0 +1,23 @@
The keximdb driver was written by:
Martin Ellis <martin.ellis@kdemail.net>
Keximdb uses a modified copy of mdbtools. The following authors
are listed in the AUTHORS file in mdbtools CVS:
Brian Bruns <camber@ais.org>
Started MDB Tools
Karl Nyberg <knyberg@grebyn.com>
Lots and lots of bug fixes and functionality
Georg Bauer <gb@hugo.westfalen.de>
Lots and lots of bug fixes and functionality
Carl Seutter <cgseutter@worldnet.att.net>
Backend schema export, other stuff
Trevor Harrison <trevor@harrison.org>
password field, etc...
Brent Johnson <xone47@yahoo.com>
large MEMO fields, deleted rows, double datatype
Tim Nelson <vbprogie@hotmail.com>
Multipage table defs, column totals > 255
David Mansfield <mdbtools@dm.cobite.com>
Numerous patches
Jeff Smith <whydoubt@yahoo.com>
Patches too numerous to enumerate.

@ -0,0 +1,30 @@
Wed May 10 20:55:21 BST 2006 - version 1.0.1.
Apply fix for decimal number import.
-- Martin Ellis <martin.ellis@kdemail.net>
Wed Jan 18 22:03:44 GMT 2006
Portability fixes:
* make optimisation disabling work with automake.
* dumb down strftime parameter for old C libraries.
Use QDateTime instead of QDate for importing dates and times.
Remove dodgy and slow date parsing code.
-- Martin Ellis <martin.ellis@kdemail.net>
Wed Jan 18 12:56:33 GMT 2006
Fix Heisenbug - MDB import of Northwind DB crashes in
mdb_read_indices if optimisations above -O0 are used.
Pass "%FT%T" to mdb_set_date_fmt, to get something in
a format that QDate::fromString( ..., Qt:ISODate)
to prevent silly locale problems with dates.
-- Martin Ellis <martin.ellis@kdemail.net>
Fri Nov 4 19:43:16 GMT 2005
Update for improved Kexi Migration API
* Sample datbases like Northwind are now imported without a problem.
-- Martin Ellis <martin.ellis@kdemail.net>
Sat, 05 Mar 2005 22:32:08 +0000
Initial version of MDB migration driver for Kexi.
Using latest mdbtools from CVS (i.e. 5 March 2005).
-- Martin Ellis <kde@martinellis.co.uk>

@ -0,0 +1,167 @@
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, a file
`config.cache' that saves the results of its tests to speed up
reconfiguring, and a file `config.log' containing compiler output
(useful mainly for debugging `configure').
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If at some point `config.cache'
contains results you don't want to keep, you may remove or edit it.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes a while. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Type `make install' to install the programs and any data files and
documentation.
4. You can remove the program binaries and object files from the
source code directory by typing `make clean'.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. You can give `configure'
initial values for variables by setting them in the environment. Using
a Bourne-compatible shell, you can do that on the command line like
this:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the `env' program, you can do it like this:
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not supports the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' can not figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it can not guess the host type, give it the
`--host=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name with three fields:
CPU-COMPANY-SYSTEM
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are building compiler tools for cross-compiling, you can also
use the `--target=TYPE' option to select the type of system they will
produce code for and the `--build=TYPE' option to select the type of
system on which you are compiling the package.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made.
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

@ -0,0 +1,11 @@
# KexiMDB
AUTOMAKE_OPTIONS = foreign
DISTCLEANFILES = inst-apps
include admin/deps.am
#include admin/Doxyfile.am
MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS configure.files
SUBDIRS=$(TOPSUBDIRS)

@ -0,0 +1,10 @@
# KexiMDB
AUTOMAKE_OPTIONS = foreign
DISTCLEANFILES = inst-apps
include admin/deps.am
#include admin/Doxyfile.am
MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS configure.files

@ -0,0 +1,45 @@
This is keximdb, the MDB file migration driver for Kexi.
MDB files are the native database format of MS Access (and
also some other MS applications). This driver can be used
by Kexi's migration framework to convert simple Access
databases in to native Kexi databases.
Requirements:
To compile the following software is needed:
* the kdelibs development files;
* the kexi development files;
* GLib 2.0 development files; and
* g++, gcc, make, ...
These will may be packaged for your distribution with names
similar to kdelibs-devel, libkexi-devel and libglib2.0-devel.
You should not need the libkexi-devel package if you have compiled
and installed Kexi from source.
Installation:
If you downloaded this packages as a source tarball
(in a file called something like keximdb-X.Y.tar.gz),
then it can be compiled and installed as follows:
./configure --enable-debug
make
make install
Note that the --enable-debug option is recommended for now.
Further Information:
If you have any problems, feel free to contact the author
using the e-mail address below, or post your question to the
koffice-devel list, or ask on the #kexi IRC channel...
Please contact the Kexi developers if you intend to package
this software for your distribution. (Note that we already
have some packaging scripts for Debian and related distributions
in the KDE subversion repository).
More information about this package is available on Kexi's
website at:
http://www.kexi-project.org/wiki/wiki/?MDBDriver
Martin Ellis
martin.ellis@kdemail.net

@ -0,0 +1 @@
keximdb version 1.1.0

File diff suppressed because it is too large Load Diff

884
aclocal.m4 vendored

@ -0,0 +1,884 @@
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.9.6])])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is `.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[dnl Rely on autoconf to set up CDPATH properly.
AC_PREREQ([2.50])dnl
# expand $ac_aux_dir to an absolute path
am_aux_dir=`cd $ac_aux_dir && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 7
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])
AC_SUBST([$1_FALSE])
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 8
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
# NAME is "CC", "CXX", "GCJ", or "OBJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
# dependency, and given that the user is not expected to run this macro,
# just rely on AC_PROG_CC.
AC_DEFUN([_AM_DEPENDENCIES],
[AC_REQUIRE([AM_SET_DEPDIR])dnl
AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
[$1], CXX, [depcc="$CXX" am_compiler_list=],
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
# making a dummy file named `D' -- because `-MD' means `put the output
# in D'.
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
cp "$am_depcomp" conftest.dir
cd conftest.dir
# We will build objects and dependencies in a subdirectory because
# it helps to detect inapplicable dependency modes. For instance
# both Tru64's cc and ICC support -MD to output dependencies as a
# side effect of compilation, but ICC will put the dependencies in
# the current directory while Tru64 will put them in the object
# directory.
mkdir sub
am_cv_$1_dependencies_compiler_type=none
if test "$am_compiler_list" = ""; then
am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
fi
for depmode in $am_compiler_list; do
# Setup a source with many dependencies, because some compilers
# like to wrap large dependency lists on column 80 (with \), and
# we should not choose a depcomp mode which is confused by this.
#
# We need to recreate these files for each test, as the compiler may
# overwrite some of them when testing with obscure command lines.
# This happens at least with the AIX C compiler.
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
# Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
# Solaris 8's {/usr,}/bin/sh.
touch sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
case $depmode in
nosideeffect)
# after this tag, mechanisms are not by side-effect, so they'll
# only be used when explicitly requested
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
none) break ;;
esac
# We check with `-c' and `-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
# handle `-M -o', and we need to detect this.
if depmode=$depmode \
source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_$1_dependencies_compiler_type=$depmode
break
fi
fi
done
cd ..
rm -rf conftest.dir
else
am_cv_$1_dependencies_compiler_type=none
fi
])
AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
AM_CONDITIONAL([am__fastdep$1], [
test "x$enable_dependency_tracking" != xno \
&& test "$am_cv_$1_dependencies_compiler_type" = gcc3])
])
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
# This macro is AC_REQUIREd in _AM_DEPENDENCIES
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
])
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
[AC_ARG_ENABLE(dependency-tracking,
[ --disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
#serial 3
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[for mf in $CONFIG_FILES; do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named `Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# So let's grep whole file.
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running `make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# When using ansi2knr, U may be empty or an underscore; expand it
U=`sed -n 's/^U = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`AS_DIRNAME(["$file"])`
AS_MKDIR_P([$dirpart/$fdir])
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
done
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking
# is enabled. FIXME. This creates each `.P' file that we will
# need in order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
[AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
])
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 8
# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS.
AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 12
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.58])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
# test to see if srcdir already configured
if test "`cd $srcdir && pwd`" != "`pwd`" &&
test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
AM_MISSING_PROG(AUTOCONF, autoconf)
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
AM_MISSING_PROG(AUTOHEADER, autoheader)
AM_MISSING_PROG(MAKEINFO, makeinfo)
AM_PROG_INSTALL_SH
AM_PROG_INSTALL_STRIP
AC_REQUIRE([AM_PROG_MKDIR_P])dnl
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES(CC)],
[define([AC_PROG_CC],
defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
])
])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$1 | $1:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
install_sh=${install_sh-"$am_aux_dir/install-sh"}
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# AM_PROG_LEX
# -----------
# Autoconf leaves LEX=: if lex or flex can't be found. Change that to a
# "missing" invocation, for better error output.
AC_DEFUN([AM_PROG_LEX],
[AC_PREREQ(2.50)dnl
AC_REQUIRE([AM_MISSING_HAS_RUN])dnl
AC_REQUIRE([AC_PROG_LEX])dnl
if test "$LEX" = :; then
LEX=${am_missing_run}flex
fi])
# Check to see how 'make' treats includes. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# AM_MAKE_INCLUDE()
# -----------------
# Check to see how make treats includes.
AC_DEFUN([AM_MAKE_INCLUDE],
[am_make=${MAKE-make}
cat > confinc << 'END'
am__doit:
@echo done
.PHONY: am__doit
END
# If we don't find an include directive, just comment out the code.
AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
# We grep out `Entering directory' and `Leaving directory'
# messages which can occur if `w' ends up in MAKEFLAGS.
# In particular we don't look at `^make:' because GNU make might
# be invoked under some other name (usually "gmake"), in which
# case it prints its new name instead of `make'.
if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
am__include=include
am__quote=
_am_result=GNU
fi
# Now try BSD make style include.
if test "$am__include" = "#"; then
echo '.include "confinc"' > confmf
if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
am__include=.include
am__quote="\""
_am_result=BSD
fi
fi
AC_SUBST([am__include])
AC_SUBST([am__quote])
AC_MSG_RESULT([$_am_result])
rm -f confinc confmf
])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it supports --run.
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
am_missing_run="$MISSING --run "
else
am_missing_run=
AC_MSG_WARN([`missing' script is too old or missing])
fi
])
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_MKDIR_P
# ---------------
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
#
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
# created by `make install' are always world readable, even if the
# installer happens to have an overly restrictive umask (e.g. 077).
# This was a mistake. There are at least two reasons why we must not
# use `-m 0755':
# - it causes special bits like SGID to be ignored,
# - it may be too restrictive (some setups expect 775 directories).
#
# Do not use -m 0755 and let people choose whatever they expect by
# setting umask.
#
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
# Some implementations (such as Solaris 8's) are not thread-safe: if a
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
# concurrently, both version can detect that a/ is missing, but only
# one can create it and the other will error out. Consequently we
# restrict ourselves to GNU make (using the --version option ensures
# this.)
AC_DEFUN([AM_PROG_MKDIR_P],
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
# We used to keeping the `.' as first argument, in order to
# allow $(mkdir_p) to be used without argument. As in
# $(mkdir_p) $(somedir)
# where $(somedir) is conditionally defined. However this is wrong
# for two reasons:
# 1. if the package is installed by a user who cannot write `.'
# make install will fail,
# 2. the above comment should most certainly read
# $(mkdir_p) $(DESTDIR)$(somedir)
# so it does not work when $(somedir) is undefined and
# $(DESTDIR) is not.
# To support the latter case, we have to write
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
# so the `.' trick is pointless.
mkdir_p='mkdir -p --'
else
# On NextStep and OpenStep, the `mkdir' command does not
# recognize any option. It will interpret all options as
# directories to create, and then abort because `.' already
# exists.
for d in ./-p ./--version;
do
test -d $d && rmdir $d
done
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
if test -f "$ac_aux_dir/mkinstalldirs"; then
mkdir_p='$(mkinstalldirs)'
else
mkdir_p='$(install_sh) -d'
fi
fi
AC_SUBST([mkdir_p])])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 3
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# ------------------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
# _AM_SET_OPTIONS(OPTIONS)
# ----------------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Just in case
sleep 1
echo timestamp > conftest.file
# Do `set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t $srcdir/configure conftest.file`
fi
rm -f conftest.file
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT(yes)])
# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor `install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in `make install-strip', and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using `strip' when the user
# run `make install-strip'. However `strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the `STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 2
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of `v7', `ustar', or `pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility.
AM_MISSING_PROG([AMTAR], [tar])
m4_if([$1], [v7],
[am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
[m4_case([$1], [ustar],, [pax],,
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
# Do not fold the above two line into one, because Tru64 sh and
# Solaris sh will not grok spaces in the rhs of `-'.
for _am_tool in $_am_tools
do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar;
do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar <conftest.tar])
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
m4_include([acinclude.m4])

1526
config.guess vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1,413 @@
/* config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the <Carbon/Carbon.h> header file. */
#undef HAVE_CARBON_CARBON_H
/* Define if you have the CoreAudio API */
#undef HAVE_COREAUDIO
/* Define to 1 if you have the <crt_externs.h> header file. */
#undef HAVE_CRT_EXTERNS_H
/* Defines if your system has the crypt function */
#undef HAVE_CRYPT
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
*/
#undef HAVE_DIRENT_H
/* Define if you have dlfcn */
#undef HAVE_DLFCN
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <dl.h> header file. */
#undef HAVE_DL_H
/* Define to 1 if you have the `fabsl' function. */
#undef HAVE_FABSL
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `flock' function. */
#undef HAVE_FLOCK
/* Define to 1 if you have the <fnmatch.h> header file. */
#undef HAVE_FNMATCH_H
/* Define if you have getdomainname */
#undef HAVE_GETDOMAINNAME
/* Define if you have the getdomainname prototype */
#undef HAVE_GETDOMAINNAME_PROTO
/* Define if you have gethostname */
#undef HAVE_GETHOSTNAME
/* Define if you have the gethostname prototype */
#undef HAVE_GETHOSTNAME_PROTO
/* Define if you have the iconv() function. */
#undef HAVE_ICONV
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define if you have libjpeg */
#undef HAVE_LIBJPEG
/* Define if you have libpng */
#undef HAVE_LIBPNG
/* Define if you have a working libpthread (will enable threaded code) */
#undef HAVE_LIBPTHREAD
/* Define if you have libz */
#undef HAVE_LIBZ
/* Define to 1 if you have the <linux/tcp.h> header file. */
#undef HAVE_LINUX_TCP_H
/* Define to 1 if the type `long double' works and has more range or precision
than `double'. */
#undef HAVE_LONG_DOUBLE
/* Define to 1 if the type `long double' works and has more range or precision
than `double'. */
#undef HAVE_LONG_DOUBLE_WIDER
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define if your system needs _NSGetEnviron to set up the environment */
#undef HAVE_NSGETENVIRON
/* Define to 1 if you have the <paths.h> header file. */
#undef HAVE_PATHS_H
/* Define if you have res_init */
#undef HAVE_RES_INIT
/* Define if you have the res_init prototype */
#undef HAVE_RES_INIT_PROTO
/* Define to 1 if you have the `re_comp' function. */
#undef HAVE_RE_COMP
/* Define if you have setenv */
#undef HAVE_SETENV
/* Define if you have the setenv prototype */
#undef HAVE_SETENV_PROTO
/* Define if you have a STL implementation by SGI */
#undef HAVE_SGI_STL
/* Define if you have shload */
#undef HAVE_SHLOAD
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define if you have strlcat */
#undef HAVE_STRLCAT
/* Define if you have the strlcat prototype */
#undef HAVE_STRLCAT_PROTO
/* Define if you have strlcpy */
#undef HAVE_STRLCPY
/* Define if you have the strlcpy prototype */
#undef HAVE_STRLCPY_PROTO
/* Define to 1 if you have the <sysent.h> header file. */
#undef HAVE_SYSENT_H
/* Define to 1 if you have the <sys/bitypes.h> header file. */
#undef HAVE_SYS_BITYPES_H
/* Define to 1 if you have the <sys/cdefs.h> header file. */
#undef HAVE_SYS_CDEFS_H
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_NDIR_H
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define to 1 if you have the <sys/proc.h> header file. */
#undef HAVE_SYS_PROC_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have unsetenv */
#undef HAVE_UNSETENV
/* Define if you have the unsetenv prototype */
#undef HAVE_UNSETENV_PROTO
/* Define to 1 if you have the <utmp.h> header file. */
#undef HAVE_UTMP_H
/* Define to 1 if you have the `vsnprintf' function. */
#undef HAVE_VSNPRINTF
/* Define to 1 if you have the <wordexp.h> header file. */
#undef HAVE_WORDEXP_H
/* Define as const if the declaration of iconv() needs const. */
#undef ICONV_CONST
/* Suffix for lib directories */
#undef KDELIBSUFF
/* Define a safe value for MAXPATHLEN */
#undef KDEMAXPATHLEN
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* The size of `char *', as computed by sizeof. */
#undef SIZEOF_CHAR_P
/* The size of `int', as computed by sizeof. */
#undef SIZEOF_INT
/* The size of `long', as computed by sizeof. */
#undef SIZEOF_LONG
/* The size of `short', as computed by sizeof. */
#undef SIZEOF_SHORT
/* The size of `size_t', as computed by sizeof. */
#undef SIZEOF_SIZE_T
/* The size of `unsigned long', as computed by sizeof. */
#undef SIZEOF_UNSIGNED_LONG
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#undef TIME_WITH_SYS_TIME
/* Version number of package */
#undef VERSION
/* Defined if compiling without arts */
#undef WITHOUT_ARTS
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
#undef YYTEXT_POINTER
/*
* jpeg.h needs HAVE_BOOLEAN, when the system uses boolean in system
* headers and I'm too lazy to write a configure test as long as only
* unixware is related
*/
#ifdef _UNIXWARE
#define HAVE_BOOLEAN
#endif
/*
* AIX defines FD_SET in terms of bzero, but fails to include <strings.h>
* that defines bzero.
*/
#if defined(_AIX)
#include <strings.h>
#endif
#if defined(HAVE_NSGETENVIRON) && defined(HAVE_CRT_EXTERNS_H)
# include <sys/time.h>
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
#endif
#if !defined(HAVE_GETDOMAINNAME_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
int getdomainname (char *, size_t);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_GETHOSTNAME_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
int gethostname (char *, unsigned int);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_RES_INIT_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
int res_init(void);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_SETENV_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
int setenv (const char *, const char *, int);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_STRLCAT_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
unsigned long strlcat(char*, const char*, unsigned long);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_STRLCPY_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
unsigned long strlcpy(char*, const char*, unsigned long);
#ifdef __cplusplus
}
#endif
#endif
#if !defined(HAVE_UNSETENV_PROTO)
#ifdef __cplusplus
extern "C" {
#endif
void unsetenv (const char *);
#ifdef __cplusplus
}
#endif
#endif
/*
* On HP-UX, the declaration of vsnprintf() is needed every time !
*/
#if !defined(HAVE_VSNPRINTF) || defined(hpux)
#if __STDC__
#include <stdarg.h>
#include <stdlib.h>
#else
#include <varargs.h>
#endif
#ifdef __cplusplus
extern "C"
#endif
int vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
#ifdef __cplusplus
extern "C"
#endif
int snprintf(char *str, size_t n, char const *fmt, ...);
#endif
#if defined(__SVR4) && !defined(__svr4__)
#define __svr4__ 1
#endif
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* type to use in place of socklen_t if not defined */
#undef kde_socklen_t
/* type to use in place of socklen_t if not defined (deprecated, use
kde_socklen_t) */
#undef ksize_t

1658
config.sub vendored

File diff suppressed because it is too large Load Diff

@ -0,0 +1,6 @@
./admin/configure.in.min
configure.in.in
./keximdb/configure.in.in
./keximdb/src/keximdb/configure.in.bot
./keximdb/src/keximdb/configure.in.in
./keximdb/src/mdbtools/configure.in.in

@ -0,0 +1,29 @@
#MIN_CONFIG(3.3)
# Define a symbol, to know that we're compiling WITH kde. (for apps that
# can compile without KDE, optionally)
AM_CONDITIONAL(KDE_INSTALLED, test "$have_kde" = "yes")
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h sys/time.h sys/stat.h stdint.h)
AC_CHECK_HEADERS(sys/cdefs.h fnmatch.h sysent.h strings.h paths.h)
AC_CHECK_HEADERS(utmp.h sys/param.h linux/tcp.h sys/proc.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_TIME
AC_C_LONG_DOUBLE
dnl Checks for library functions.
KDE_CHECK_DLOPEN
AC_CHECK_FUNCS(socket fabsl strdup vsnprintf re_comp flock)
AC_CHECK_SETENV
AC_CHECK_UNSETENV
AC_CHECK_GETDOMAINNAME
AC_CHECK_GETHOSTNAME
AM_PROG_LEX
LFLAGS="-o${LEX_OUTPUT_ROOT}.c"
AC_SUBST(LFLAGS)

@ -0,0 +1,12 @@
Begin4
Title: keximdb
Version: 1.1.0
Entered-date: 2006-10-27
Description: MS Access (MDB) driver for Kexi, the KOffice database program
Keywords: Microsoft Access to Kexi (KexiDB) database migration driver
Author: martin.ellis@kdemail.net (Martin Ellis)
Maintained-by: martin.ellis@kdemail.net (Martin Ellis)
Primary-site: ftp.kde.org /pub/kde/stable/apps/KDE3.x/database
576 kB keximdb-1.1.0.tar.gz
Copying-policy: LGPL
End

@ -0,0 +1 @@
SUBDIRS = src

@ -0,0 +1,63 @@
#!/bin/sh
VERSION=1.1.0
DESTINATION=/tmp/keximdb-dist/$VERSION/
KDESVN=svn://anonsvn.kde.org/home/kde
CHECKOUT=keximdb-tmp
# From trunk/kdenonbeta
#BRANCH=trunk/kdenonbeta
# From release branch
BRANCH=branches/work/keximdb/koffice-1.6
#########
set -e
SVN="svn"
DATE=`date +%Y-%m-%d`
mkdir -p "$DESTINATION"
mkdir $CHECKOUT
pushd $CHECKOUT
echo "1. Checking out top-level build files"
$SVN co -N $KDESVN/$BRANCH keximdb
cd keximdb
echo "2. Checking out admin dir"
$SVN co $KDESVN/branches/KDE/3.5/kde-common/admin
echo "3. Checking out keximdb dir"
$SVN up keximdb
cd ..
echo "4. Creating build system files"
svn2dist --no-i18n keximdb keximdb -v $VERSION -b -g -m
echo "5. Cleaning unneeded files"
#Delete debian dir
rm -r keximdb-$VERSION/keximdb/debian
#Include debian dir
#mv keximdb-$VERSION/keximdb/debian keximdb-$VERSION
#Delete diffs dir
rm -r keximdb-$VERSION/keximdb/src/diffs
#Include diffs dir
#mv keximdb-$VERSION/keximdb/src/diffs keximdb-$VERSION
echo "6. Compiling LSM entry"
sed "s/@DATE@/$DATE/;s/@VERSION@/$VERSION/" \
../keximdb.lsm > keximdb-$VERSION/keximdb.lsm
printf "7. Finding size of tarball..."
tar -cf keximdb-tmp.tar keximdb-$VERSION
gzip -9 keximdb-tmp.tar
SIZE=`du -k keximdb-tmp.tar.gz | awk '{print $1}'`
echo "$SIZE kB"
sed -i "s/@SIZE@/$SIZE kB/" keximdb-$VERSION/keximdb.lsm
rm keximdb-tmp.tar.gz
echo "8. Creating tarball..."
tar -cf keximdb-$VERSION.tar keximdb-$VERSION
gzip -9 keximdb-$VERSION.tar
popd

@ -0,0 +1,57 @@
#MIN_CONFIG(3.0)
AM_INIT_AUTOMAKE(keximdb, 0.1)
AC_C_BIGENDIAN
AC_CHECK_KDEMAXPATHLEN
dnl ----------------------------------------------------------------------
dnl These checks based on those in arts configure.in.in
dnl Check for pkg-config
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
if test "$PKG_CONFIG" = "no"; then
AC_MSG_ERROR([This package requires pkg-config.])
fi
dnl Check for Glib-2.0
# GLIB_CFLAGS: cflags for compiling glib dependant sources
# GLIB_LIBADD: glib libraries (-l options)
# GLIB_LDFLAGS: flags containing path to glib libraries (-L options)
GLIB_PACKAGES="glib-2.0"
GLIB_VERSION="2.4"
AC_MSG_CHECKING(for GLib-2.0 (at least $GLIB_VERSION))
if $PKG_CONFIG --atleast-pkgconfig-version 0.15 ; then
if $PKG_CONFIG --atleast-version $GLIB_VERSION $GLIB_PACKAGES >/dev/null 2>&1 ; then
GLIB_CFLAGS="`$PKG_CONFIG --cflags $GLIB_PACKAGES`"
GLIB_LIBADD="`$PKG_CONFIG --libs-only-l --libs-only-other $GLIB_PACKAGES`"
GLIB_LDFLAGS="`$PKG_CONFIG --libs-only-L $GLIB_PACKAGES`"
AC_MSG_RESULT(yes)
fi
else
if $PKG_CONFIG --atleast-version $GLIB_VERSION $GLIB_PACKAGES >/dev/null 2>&1 ; then
GLIB_CFLAGS="`$PKG_CONFIG --cflags $GLIB_PACKAGES`"
GLIB_LIBADD="`$PKG_CONFIG --libs-only-l $GLIB_PACKAGES`"
GLIB_LDFLAGS="`$PKG_CONFIG --libs-only-L $GLIB_PACKAGES`"
AC_MSG_RESULT(yes)
AC_MSG_WARN([building keximdb with pkg-config < 0.15 is untested.])
fi
fi
if test -z "$GLIB_LIBADD"; then
AC_MSG_RESULT(not installed)
AC_ERROR([Please install glib-2.0 (see http://www.gtk.org).])
DO_NOT_COMPILE="$DO_NOT_COMPILE keximdb"
fi
# Build without optimisation. Anything higher than -O0 here causes
# a crash in mdb_read_indices on Northwind.
CFLAGS=`echo "$CFLAGS" | sed 's/ -O2 / -O0 /g'`
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBADD)
AC_SUBST(GLIB_LDFLAGS)
dnl ----------------------------------------------------------------------

@ -0,0 +1 @@
SUBDIRS=mdbtools keximdb

@ -0,0 +1,98 @@
#!/bin/bash
MDB_CVS_DIR=mdbtools.cvs
MDB_ORIG_DIR=mdbtools.old
MDB_ANSI_DIR=mdbtools.ansi
usage(){
echo Usage:
echo " convert.sh co"
echo " Checkout mdbtools from cvs into $MDB_CVS_DIR."
echo " Press enter when prompted for password."
echo " convert.sh symlink"
echo " Create symlink farm in $MDB_ORIG_DIR pointing to"
echo " mdbtools CVS checkout at $MDB_CVS_DIR."
echo " convert.sh ansi"
echo " Make a copy of $MDB_ORIG_DIR in $MDB_ANSI_DIR where"
echo " all non-ANSI comments have been deleted or ansi-fied."
echo " convert.sh update"
echo " Update keximdb copy of mdbtools from $MDB_ANSI_DIR."
echo " convert.sh patch"
echo " Apply keximdb patches from diffs dir."
echo " convert.sh changes"
echo " Show files that need to be patched by diffs."
exit 1
}
build_links(){
rm -rf $MDB_ORIG_DIR
mkdir -p $MDB_ORIG_DIR/include
ln -s ../../$MDB_CVS_DIR/include/mdbtools.h $MDB_ORIG_DIR/include
mkdir $MDB_ORIG_DIR/libmdb
for d in mdbtools/libmdb/*.c ; do
ln -s ../../$MDB_CVS_DIR/src/libmdb/`basename $d` $MDB_ORIG_DIR/libmdb
done
exit
}
build_ansi(){
# ANSI comments or nothing
# Delete //-style comments
# so that they can be compiled with -ansi (used in the KDE
# build system). It just deletes the comments, in case they
# have further /*'s inside them.
rm -rf $MDB_ANSI_DIR
cp -r $MDB_ORIG_DIR $MDB_ANSI_DIR
for d in $MDB_ANSI_DIR/libmdb/*.c ; do
#sed -i 's#^\([ \t]*\)//\(.*\)##' $d
sed -i 's#//\(.*\)##' $d
done
exit
}
update(){
for d in mdbtools/libmdb/*.c ; do
cp $MDB_ANSI_DIR/libmdb/`basename $d` mdbtools/libmdb
done
cp $MDB_ANSI_DIR/include/mdbtools.h mdbtools/include
exit
}
apply_patch(){
for d in diffs/*.diff ; do
patch -p0 < $d
done
exit
}
show_patched(){
diff -ru mdbtools.ansi/ mdbtools/ | \
grep -v Only.in.mdbtools | \
grep ^---
}
cvs_checkout(){
CVSROOT=:pserver:anonymous@mdbtools.cvs.sourceforge.net:/cvsroot/mdbtools
if (( ! `grep -c mdbtools $HOME/.cvspass` )) ; then
cvs -d$CVSROOT login
fi
cvs -z3 -d$CVSROOT co -d $MDB_CVS_DIR mdbtools
}
case "$1" in
co) cvs_checkout ;;
symlink) build_links ;;
ansi) build_ansi ;;
update) update ;;
patch) apply_patch ;;
changes) show_patched ;;
*) usage
esac
#cp $1/include/mdbtools.h mdbtools/include
#cp $1/libmdb/*.c mdbtools/libmdb
# No backends
#rm -f mdbtools/libmdb/{backend,stats,kkd,props}.c
# No mdb_table_dump, it uses backends and not used anyway
#sed -i '/^void mdb_table_dump/,/^}/d' mdbtools/libmdb/table.c

@ -0,0 +1,22 @@
kde_module_LTLIBRARIES = keximigrate_mdb.la
kde_services_DATA = keximigrate_mdb.desktop
keximigrate_mdb_la_SOURCES = mdbmigrate.cpp
AM_CPPFLAGS = \
-DMDB_NO_BACKENDS=1 -DMDB_NO_STATS=1 \
-I$(top_srcdir)/keximdb/src/mdbtools/include \
-I$(KEXIDB_INC) -I$(KEXIDB_INC)/kexidb \
$(GLIB_CFLAGS) $(all_includes)
keximigrate_mdb_la_METASOURCES = AUTO
keximigrate_mdb_la_LIBADD = \
$(LIB_QT) $(GLIB_LIBADD) -L$(KEXIDB_LIB) -lkeximigrate \
../mdbtools/libmdb/libmdb.la
keximigrate_mdb_la_LDFLAGS = \
$(all_libraries) $(GLIB_LDFLAGS) \
-module $(KDE_PLUGIN) -no-undefined
noinst_HEADERS = mdbmigrate.h

@ -0,0 +1,23 @@
if test -z "$KEXIDB_INC" -o -z "$KEXIDB_LIB"; then
if test -z "$KEXIDB_INC"; then
echo " + The KexiDB headers were not found"
fi
if test -z "$KEXIDB_LIB"; then
echo " + The KexiDB libraries were not found"
fi
echo " Required KexiDB development files were not found."
echo " If these are installed, you can use the --with-kexidb-includes"
echo " and --with-kexidb-libraries configure options."
echo " If not, these may be available as a package for your"
echo " distribution, or you can install them by installing Kexi"
echo " from source"
all_tests=bad
else
echo "KexiDB includes: $KEXIDB_INC"
echo "KexiDB libraries: $KEXIDB_LIB"
fi

@ -0,0 +1,73 @@
dnl ======================================================================
dnl Configure checks for KexiDB
dnl
dnl This ought to be simple - use kde-config to find headers and libraries
dnl and link against them:
dnl `kde-config --prefix`/include
dnl However, kde-config doesn't return the expected directory on Debian
dnl (for one), which installs kde headers to /usr/include/kde.
dnl
dnl So the plan for headers is to check:
dnl `kde-config --prefix`/include,
dnl `kde-config --prefix`/kde/include,
dnl any other place we might find them
dnl Then for libraries, check
dnl `kde-config --prefix`/include,
dnl any other place we might find them
dnl ======================================================================
# Configure overrides
AC_ARG_WITH(kexidb_includes,
AC_HELP_STRING([--with-kexidb-includes=DIR],
[use KexiDB-includes installed in this directory]),
[ac_kexidb_incdir=$withval],
ac_kexidb_incdir="")
AC_ARG_WITH(kexidb_libraries,
AC_HELP_STRING([--with-kexidb-libraries=DIR],
[use KexiDB-libs installed in this directory ]),
[ac_kexidb_libdir=$withval],
ac_kexidb_libdir="")
# Find kde-config
if test "$ac_kexidb_incdir" = "" -o "$ac_kexidb_libdir" = "" ; then
KDE_FIND_PATH(kde_config,KDE_CONFIG,
[${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin /usr/bin
/opt/kde/bin ],
[AC_MSG_ERROR([Could not find kde-config anywhere])])
kde_prefix=`$KDE_CONFIG --prefix`
fi
# Find include dir
if test "$ac_kexidb_incdir" = "" ; then
AC_MSG_CHECKING([for KexiDB headers])
kexidb_incdirs="$kde_prefix/include $kde_prefix/include/kde /usr/include /usr/include/kde /usr/local/include /opt/kde/include"
AC_FIND_FILE("kexidb/driver.h", $kexidb_incdirs, kexidb_incdir)
if test ! -r $kexidb_incdir/kexidb/driver.h; then
AC_MSG_RESULT([Could not find the required KexiDB HEADER files.])
else
AC_MSG_RESULT([$kexidb_incdir])
KEXIDB_INC=$kexidb_incdir
fi
else
KEXIDB_INC=$ac_kexidb_incdir
fi
# Find lib dir
if test "$ac_kexidb_libdir" = "" ; then
AC_MSG_CHECKING([for KexiDB libraries])
kexidb_libdirs="$kde_prefix/lib /usr/lib /usr/local/lib /opt/kde/lib"
AC_FIND_FILE(libkexidb.so, $kexidb_libdirs, kexidb_libdir)
if test ! -r $kexidb_libdir/libkexidb.so ; then
AC_MSG_RESULT([Could not find the required KexiDB LIBRARY files.])
else
AC_MSG_RESULT([$kexidb_libdir])
KEXIDB_LIB=$kexidb_libdir
fi
else
KEXIDB_LIB=$ac_kexidb_libdir
fi
AC_SUBST(KEXIDB_INC)
AC_SUBST(KEXIDB_LIB)

@ -0,0 +1,21 @@
[Desktop Entry]
Encoding=UTF-8
Name=MS Access (MDB) Migration Driver for Kexi
Name[da]=MS Access (MDB) Migrationsdriver for Kexi
Name[es]=Controlador de migración de MS Access (MDB) Migration para Kexi
Name[et]=Kexi MS Access'i (MDB) migreerumise draiver
Name[fr]=Pilote de migration MS Access (MDB) vers Kexi
Name[gl]=Controlador de Migrazón de MS Access (MDB) para Kexi
Name[ja]=Kexi のための MS Access (MDB) 移行ドライバ
Name[pt]=Controlador de Migração do MS Access (MDB) para o Kexi
Name[pt_BR]=Controlador de Migração do MS Access (MDB) para o Kexi
Name[sv]=MS Access (MDB) överföringsdrivrutin för Kexi
Name[xx]=xxMS Access (MDB) Migration Driver for Kexixx
X-KDE-Library=keximigrate_mdb
ServiceTypes=Kexi/MigrationDriver
Type=Service
InitialPreference=8
X-Kexi-MigrationDriverName=Access
X-Kexi-MigrationDriverType=File
X-Kexi-FileDBDriverMime=application/x-msaccess
X-Kexi-KexiMigrationVersion=1.1

@ -0,0 +1,484 @@
/* This file is part of the KDE project
Copyright (C) 2005,2006 Martin Ellis <martin.ellis@kdemail.net>
Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
This program 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 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include "mdbmigrate.h"
#include <qstring.h>
#include <qcstring.h>
#include <qregexp.h>
#include <qfile.h>
#include <qvariant.h>
#include <qdatetime.h>
#include <qvaluelist.h>
#include <kdebug.h>
#include <kexiutils/identifier.h>
using namespace KexiMigration;
/* This is the implementation for the MDB file import routines. */
KEXIMIGRATE_DRIVER_INFO( MDBMigrate, mdb );
static QCString isNonUnicodePropId( "source_database_has_nonunicode_encoding" );
static QCString nonUnicodePropId( "source_database_nonunicode_encoding" );
/* ************************************************************************** */
MDBMigrate::MDBMigrate(QObject *parent, const char *name,
const QStringList &args) :
KexiMigrate(parent, name, args)
{
/*! @todo invert the sense of values, then remove "Non-" from these strings */
m_properties[ isNonUnicodePropId ] = QVariant( true, 1 );
m_propertyCaptions[ isNonUnicodePropId ] =
i18n("Source Database Has Non-Unicode Encoding");
m_properties[ nonUnicodePropId ] = QVariant("");
m_propertyCaptions[ nonUnicodePropId ]
= i18n("Source Database Non-Unicode Encoding");
initBackend();
}
/* ************************************************************************** */
//! Destructor
MDBMigrate::~MDBMigrate() {
releaseBackend();
}
/* ************************************************************************** */
void MDBMigrate::initBackend() {
mdb_init();
// Date format associated with Qt::ISODate: YYYY-MM-DDTHH:MM:SS
// (where T is a literal). The following is equivalent to %FT%T, but
// backards compatible with old/Windows C libraries.
// See strftime documentation for more info.
mdb_set_date_fmt("%Y-%m-%dT%H:%M%:%S");
}
void MDBMigrate::releaseBackend() {
mdb_exit();
}
/* ************************************************************************** */
/*! Properties */
QVariant MDBMigrate::propertyValue( const QCString& propName )
{
if ( propName == isNonUnicodePropId ) {
m_properties[ isNonUnicodePropId ] = QVariant(false, 0);
// Costly, but we need this to get this property from file...
drv_connect();
drv_disconnect();
}
return KexiMigrate::propertyValue( propName );
}
/* ************************************************************************** */
/*! Connect to the db backend */
bool MDBMigrate::drv_connect() {
kdDebug() << "mdb_open:" << endl;
KexiDB::ConnectionData *data = m_migrateData->source;
// mdb_open takes a char*, not const char*, hence this nonsense.
char *filename = qstrdup(QFile::encodeName(data->fileName()));
m_mdb = mdb_open (filename, MDB_NOFLAGS);
delete [] filename;
if (!m_mdb) {
kdDebug() << "mdb_open failed." << endl;
return false;
}
// Setting source encoding
if ( !m_properties[ nonUnicodePropId ].toCString().isEmpty() ) {
QCString encoding = m_properties[ nonUnicodePropId ].toCString();
mdb_set_encoding( m_mdb, (const char*) encoding );
kdDebug() << "non-unicode encoding set to \""
<< encoding
<< "\"" << endl;
}
// Supports setting source encoding
m_properties[ isNonUnicodePropId ] = QVariant( IS_JET3(m_mdb), 1 );
return true;
}
/*! Disconnect from the db backend */
bool MDBMigrate::drv_disconnect()
{
mdb_close( m_mdb );
return true;
}
//! Get the table definition for a given table name
/*! Look up the table definition for the given table. This only returns a ptr
to the MdbTableDef - it doesn't load e.g. the column data.
Remember to mdb_free_tabledef the table definition when it's finished
with.
\return the table definition, or null if no matching table was found
*/
MdbTableDef* MDBMigrate::getTableDef(const QString& tableName)
{
MdbTableDef *tableDef = 0;
// Look through each entry in the catalogue ...
for (unsigned int i = 0; i < m_mdb->num_catalog; i++) {
MdbCatalogEntry* dbObject =
(MdbCatalogEntry*)(g_ptr_array_index (m_mdb->catalog, i));
// ... for a table with the given name
if (dbObject->object_type == MDB_TABLE) {
QString dbObjectName = QString::fromUtf8(dbObject->object_name);
if (dbObjectName.lower() == tableName.lower()) {
tableDef = mdb_read_table(dbObject);
break;
}
}
}
return tableDef;
}
/* ************************************************************************** */
/*! Get the types and properties for each column. */
bool MDBMigrate::drv_readTableSchema( const QString& originalName,
KexiDB::TableSchema& tableSchema )
{
//! Get the column meta-data
MdbTableDef *tableDef = getTableDef(originalName);
if(!tableDef) {
kdDebug() << "MDBMigrate::drv_getTableDef: couldn't find table "
<< originalName << endl;
return false;
}
mdb_read_columns(tableDef);
kdDebug() << "MDBMigrate::drv_readTableSchema: #cols = "
<< tableDef->num_cols << endl;
/*! Convert column data to Kexi TableSchema
Nice mix of terminology here, MDBTools has columns, Kexi has fields. */
MdbColumn *col;
for (unsigned int i = 0; i < tableDef->num_cols; i++) {
col = (MdbColumn*) g_ptr_array_index(tableDef->columns, i);
// Field name
QString fldName = QString::fromUtf8(col->name);
kdDebug() << "MDBMigrate::drv_readTableSchema: got column "
<< fldName << "\"" << col->name << endl;
QString fldID( KexiUtils::string2Identifier(fldName) );
// Field type
KexiDB::Field *fld =
new KexiDB::Field(fldID, type(col->col_type));
kdDebug() << "MDBMigrate::drv_readTableSchema: size "
<< col->col_size << " type " << type(col->col_type) <<endl;
fld->setCaption(fldName);
tableSchema.addField(fld);
}
getPrimaryKey(&tableSchema, tableDef);
//! Free the column meta-data - as soon as it doesn't seg fault.
//mdb_free_tabledef(tableDef);
return true;
}
/*! Get a list of tables and put into the supplied string list */
bool MDBMigrate::drv_tableNames(QStringList& tableNames)
{
// Try to read the catalogue of database objects
if (!mdb_read_catalog (m_mdb, MDB_ANY)) {
kdDebug() << "MDBMigrate::drv_tableNames: couldn't read catalogue" << endl;
return false;
}
// Add non-system tables to the list
for (unsigned int i = 0; i < m_mdb->num_catalog; i++) {
MdbCatalogEntry* dbObject =
(MdbCatalogEntry*)(g_ptr_array_index (m_mdb->catalog, i));
if (dbObject->object_type == MDB_TABLE) {
QString dbObjectName = QString::fromUtf8(dbObject->object_name);
if (!dbObjectName.startsWith("MSys")) {
kdDebug() << "MDBMigrate::drv_tableNames: " << dbObjectName << endl;
tableNames << dbObjectName;
}
}
}
return true;
}
QVariant MDBMigrate::toQVariant(const char* data, unsigned int len, int type) {
if(len == 0) {
// Null ptr => null value
return QVariant();
} else {
switch (type) {
case MDB_TEXT:
case MDB_MEMO:
return QVariant( QString::fromUtf8(data, len) );
case MDB_BOOL: //! @todo use &bool!
case MDB_BYTE:
return QString::fromUtf8(data, len).toShort();
case MDB_SDATETIME:
return QDateTime::fromString(data, Qt::ISODate);
case MDB_INT:
case MDB_LONGINT:
return QString::fromUtf8(data, len).toLongLong();
case MDB_FLOAT:
return QString::fromUtf8(data, len).toFloat();
case MDB_DOUBLE:
case MDB_MONEY: //! @todo
case MDB_NUMERIC: //! @todo
return QString::fromUtf8(data, len).toDouble();
case MDB_OLE:
case MDB_REPID:
default:
return QVariant(QString::fromUtf8(data, len));
}
}
}
/*! Copy MDB table to KexiDB database */
bool MDBMigrate::drv_copyTable( const QString& srcTable,
KexiDB::Connection *destConn,
KexiDB::TableSchema* dstTable)
{
QString kdLoc = "MDBMigrate::drv_copyTable: ";
MdbTableDef *tableDef = getTableDef(srcTable);
if(!tableDef) {
kdDebug() << kdLoc << srcTable << endl;
return false;
}
char *columnData[256];
int columnDataLength[256];
//! Bind + allocate the DB columns to columnData and columnDataLength arrays
mdb_read_columns(tableDef); // mdb_bind_column dies without this
for (unsigned int i = 0; i < tableDef->num_cols; i++) {
columnData[i] = (char*) g_malloc(MDB_BIND_SIZE);
// Columns are numbered from 1
// and why aren't these unsigned ints?
mdb_bind_column(tableDef, i + 1, columnData[i], &columnDataLength[i]);
}
//! Copy each row into vals
mdb_rewind_table(tableDef);
kdDebug() << kdLoc << "Fetching " << tableDef->num_rows << " rows" << endl;
#ifdef KEXI_MIGRATION_MAX_ROWS_TO_IMPORT
Q_ULLONG rows=0;
#endif
bool ok = true;
while(mdb_fetch_row(tableDef)) {
QValueList<QVariant> vals = QValueList<QVariant>();
// kdDebug() << kdLoc << "Copying " << tableDef->num_cols << " cols" << endl;
for (unsigned int i = 0; i < tableDef->num_cols; i++) {
MdbColumn *col = (MdbColumn*) g_ptr_array_index(tableDef->columns, i);
if (col->col_type == MDB_OLE && col->cur_value_len) {
mdb_ole_read(m_mdb, col, columnData[i], MDB_BIND_SIZE);
}
//! @todo: How to import binary data?
QVariant var = toQVariant(columnData[i], columnDataLength[i],
col->col_type);
vals << var;
}
if ( !destConn->insertRecord( *dstTable, vals ) ) {
ok = false;
break;
}
updateProgress();
#ifdef KEXI_MIGRATION_MAX_ROWS_TO_IMPORT
//! @todo this is risky when there are references between tables
if (++rows == KEXI_MIGRATION_MAX_ROWS_TO_IMPORT)
break;
#endif
}
//! Deallocate (unbind) the DB columns arrays and column meta-data
for (unsigned int i = 0; i < tableDef->num_cols; i++) {
g_free(columnData[i]);
}
// When memory leaks are better than seg. faults...
//mdb_free_tabledef(tableDef);
return ok;
}
//! Convert an MDB type to a KexiDB type, prompting user if necessary.
KexiDB::Field::Type MDBMigrate::type(int type)
{
// Field type
KexiDB::Field::Type kexiType = KexiDB::Field::InvalidType;
switch(type)
{
case MDB_BOOL:
kexiType = KexiDB::Field::Boolean;
break;
case MDB_BYTE:
kexiType = KexiDB::Field::Byte;
break;
case MDB_INT:
kexiType = KexiDB::Field::Integer;
break;
case MDB_LONGINT:
kexiType = KexiDB::Field::BigInteger;
break;
case MDB_MONEY:
//! @todo temporary simplification
kexiType = KexiDB::Field::Double;
break;
case MDB_FLOAT:
kexiType = KexiDB::Field::Float;
break;
case MDB_DOUBLE:
kexiType = KexiDB::Field::Double;
break;
case MDB_SDATETIME:
kexiType = KexiDB::Field::DateTime;
break;
case MDB_TEXT:
kexiType = KexiDB::Field::LongText;
break;
case MDB_OLE:
kexiType = KexiDB::Field::BLOB;
break;
case MDB_MEMO:
kexiType = KexiDB::Field::LongText;
break;
//! @todo temporary simplification
case MDB_NUMERIC:
kexiType = KexiDB::Field::Double;
break;
case MDB_REPID:
// ?
default:
kexiType = KexiDB::Field::InvalidType;
}
// If we don't know what it is, hope it's text. :o)
if (kexiType == KexiDB::Field::InvalidType) {
return KexiDB::Field::LongText;
}
return kexiType;
}
bool MDBMigrate::getPrimaryKey( KexiDB::TableSchema* table,
MdbTableDef* tableDef ) {
QString kdLoc = "MDBMigrate::getPrimaryKey: ";
MdbIndex *idx;
if (!tableDef) {
return false;
}
mdb_read_columns(tableDef);
mdb_read_indices(tableDef);
//! Find the PK index in the MDB file
bool foundIdx = false;
for (unsigned int i = 0; i < tableDef->num_idxs; i++) {
idx = (MdbIndex*) g_ptr_array_index (tableDef->indices, i);
QString fldName = QString::fromUtf8(idx->name);
if (!strcmp(idx->name, "PrimaryKey")) {
idx = (MdbIndex*) g_ptr_array_index (tableDef->indices, i);
foundIdx = true;
break;
}
}
if(!foundIdx) {
mdb_free_indices(tableDef->indices);
return false;
}
//! @todo: MDB index order (asc, desc)
kdDebug() << kdLoc << "num_keys " << idx->num_keys << endl;
//! Create the KexiDB IndexSchema ...
QByteArray key_col_num(idx->num_keys);
// MDBTools counts columns from 1 - subtract 1 where necessary
KexiDB::IndexSchema* p_idx = new KexiDB::IndexSchema(table);
for (unsigned int i = 0; i < idx->num_keys; i++) {
key_col_num[i] = idx->key_col_num[i];
kdDebug() << kdLoc << "key " << i + 1
<< " col " << key_col_num[i]
<< table->field(idx->key_col_num[i] - 1)->name()
<< endl;
p_idx->addField(table->field(idx->key_col_num[i] - 1));
}
kdDebug() << kdLoc << p_idx->debugString() << endl;
//! ... and add it to the table definition
// but only if the PK has only one field, so far :o(
KexiDB::Field *f;
if(idx->num_keys == 1 && (f = table->field(idx->key_col_num[0] - 1))) {
f->setPrimaryKey(true);
} else {
//! @todo: How to add a composite PK to a TableSchema?
//m_table->setPrimaryKey(p_idx);
}
mdb_free_indices(tableDef->indices);
return true;
}
bool MDBMigrate::drv_getTableSize(const QString& table, Q_ULLONG& size) {
//! Get the column meta-data, which contains the table size
MdbTableDef *tableDef = getTableDef(table);
if(!tableDef) {
kdDebug() << "MDBMigrate::drv_getTableDef: couldn't find table "
<< table << endl;
return false;
}
size = (Q_ULLONG)(tableDef->num_rows);
mdb_free_tabledef(tableDef);
return true;
}
#include "mdbmigrate.moc"

@ -0,0 +1,77 @@
/* This file is part of the KDE project
Copyright (C) 2005,2006 Martin Ellis <martin.ellis@kdemail.net>
Copyright (C) 2005 Jaroslaw Staniek <js@iidea.pl>
This program 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 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef MDBMIGRATE_H
#define MDBMIGRATE_H
#include <mdbtools.h>
#include "kexidb/keximigrate.h"
#include "kexidb/field.h"
#include "kexidb/connection.h"
namespace KexiMigration
{
class MDBMigrate : public KexiMigrate
{
Q_OBJECT
KEXIMIGRATION_DRIVER
public:
MDBMigrate(QObject *parent, const char *name, const QStringList& args = QStringList());
virtual ~MDBMigrate();
KexiDB::Field::Type type(int type);
MdbTableDef* getTableDef(const QString& tableName);
QVariant toQVariant(const char* data, unsigned int len, int type);
bool getPrimaryKey(KexiDB::TableSchema* table, MdbTableDef* tableDef);
//! Reimplemented to add support for "sourceDatabaseHasNonUnicodeEncoding" property
//! @todo this should be in Connection class but Migration framework has no such yet!
virtual QVariant propertyValue( const QCString& propName );
protected:
//Driver specific function to return table names
virtual bool drv_tableNames(QStringList& tablenames);
//Driver specific implementation to read a table schema
virtual bool drv_readTableSchema(
const QString& originalName, KexiDB::TableSchema& tableSchema);
//Driver specific connection implementation
virtual bool drv_connect();
virtual bool drv_disconnect();
virtual bool drv_copyTable(const QString& srcTable,
KexiDB::Connection *destConn,
KexiDB::TableSchema* dstTable);
virtual bool drv_progressSupported() { return true; }
virtual bool drv_getTableSize(const QString& table, Q_ULLONG& size);
private:
void initBackend();
void releaseBackend();
MdbHandle *m_mdb;
};
}
#endif

@ -0,0 +1,67 @@
AC_CHECK_HEADERS(fcntl.h wordexp.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
dnl those with the standalone portable GNU libiconv installed).
AC_ARG_WITH([libiconv-prefix],
AC_HELP_STRING([--with-libiconv-prefix=DIR], [search for libiconv in DIR/include and DIR/lib]), [
for dir in `echo "$withval" | tr : ' '`; do
if test -d $dir/include; then CPPFLAGS="$CPPFLAGS -I$dir/include"; fi
if test -d $dir/lib; then LDFLAGS="$LDFLAGS -L$dir/lib"; fi
done
])
AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
am_cv_func_iconv="no, consider installing GNU libiconv"
am_cv_lib_iconv=no
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("","");
iconv(cd,NULL,NULL,NULL,NULL);
iconv_close(cd);],
am_cv_func_iconv=yes)
if test "$am_cv_func_iconv" != yes; then
am_save_LIBS="$LIBS"
LIBS="$LIBS -liconv"
AC_TRY_LINK([#include <stdlib.h>
#include <iconv.h>],
[iconv_t cd = iconv_open("","");
iconv(cd,NULL,NULL,NULL,NULL);
iconv_close(cd);],
am_cv_lib_iconv=yes
am_cv_func_iconv=yes)
LIBS="$am_save_LIBS"
fi
])
if test "$am_cv_func_iconv" = yes; then
AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
AC_MSG_CHECKING([for iconv declaration])
AC_CACHE_VAL(am_cv_proto_iconv, [
AC_TRY_COMPILE([
#include <stdlib.h>
#include <iconv.h>
extern
#ifdef __cplusplus
"C"
#endif
#if defined(__STDC__) || defined(__cplusplus)
size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
#else
size_t iconv();
#endif
], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
AC_MSG_RESULT([$]{ac_t:-
}[$]am_cv_proto_iconv)
AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
[Define as const if the declaration of iconv() needs const.])
fi
LIBICONV=
if test "$am_cv_lib_iconv" = yes; then
LIBICONV="-liconv"
fi
AC_SUBST(LIBICONV)

@ -0,0 +1,560 @@
/* MDB Tools - A library for reading MS Access database files
* 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.
*/
#ifndef _mdbtools_h_
#define _mdbtools_h_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <glib.h>
#include <config.h>
#ifdef HAVE_ICONV
#include <iconv.h>
#endif
#define MDB_DEBUG 0
#define MDB_PGSIZE 4096
#define MDB_MAX_OBJ_NAME 256
#define MDB_MAX_COLS 256
#define MDB_MAX_IDX_COLS 10
#define MDB_CATALOG_PG 18
#define MDB_MEMO_OVERHEAD 12
#define MDB_BIND_SIZE 16384
enum {
MDB_PAGE_DB = 0,
MDB_PAGE_DATA,
MDB_PAGE_TABLE,
MDB_PAGE_INDEX,
MDB_PAGE_LEAF,
MDB_PAGE_MAP
};
enum {
MDB_VER_JET3 = 0,
MDB_VER_JET4 = 1
};
enum {
MDB_FORM = 0,
MDB_TABLE,
MDB_MACRO,
MDB_SYSTEM_TABLE,
MDB_REPORT,
MDB_QUERY,
MDB_LINKED_TABLE,
MDB_MODULE,
MDB_RELATIONSHIP,
MDB_UNKNOWN_09,
MDB_UNKNOWN_0A,
MDB_DATABASE_PROPERTY,
MDB_ANY = -1
};
enum {
MDB_BOOL = 0x01,
MDB_BYTE = 0x02,
MDB_INT = 0x03,
MDB_LONGINT = 0x04,
MDB_MONEY = 0x05,
MDB_FLOAT = 0x06,
MDB_DOUBLE = 0x07,
MDB_SDATETIME = 0x08,
MDB_TEXT = 0x0a,
MDB_OLE = 0x0b,
MDB_MEMO = 0x0c,
MDB_REPID = 0x0f,
MDB_NUMERIC = 0x10
};
/* SARG operators */
enum {
MDB_OR = 1,
MDB_AND,
MDB_NOT,
MDB_EQUAL,
MDB_GT,
MDB_LT,
MDB_GTEQ,
MDB_LTEQ,
MDB_LIKE,
MDB_ISNULL,
MDB_NOTNULL
};
typedef enum {
MDB_TABLE_SCAN,
MDB_LEAF_SCAN,
MDB_INDEX_SCAN
} MdbStrategy;
typedef enum {
MDB_NOFLAGS = 0x00,
MDB_WRITABLE = 0x01
} MdbFileFlags;
enum {
MDB_DEBUG_LIKE = 0x0001,
MDB_DEBUG_WRITE = 0x0002,
MDB_DEBUG_USAGE = 0x0004,
MDB_DEBUG_OLE = 0x0008,
MDB_DEBUG_ROW = 0x0010,
MDB_USE_INDEX = 0x0020,
MDB_NO_MEMO = 0x0040 /* don't follow memo fields */
};
#define mdb_is_logical_op(x) (x == MDB_OR || \
x == MDB_AND || \
x == MDB_NOT )
#define mdb_is_relational_op(x) (x == MDB_EQUAL || \
x == MDB_GT || \
x == MDB_LT || \
x == MDB_GTEQ || \
x == MDB_LTEQ || \
x == MDB_LIKE || \
x == MDB_ISNULL || \
x == MDB_NOTNULL )
enum {
MDB_ASC,
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)
#if !MDB_NO_BACKENDS
/* hash to store registered backends */
extern GHashTable *mdb_backends;
#endif
/* forward declarations */
typedef struct mdbindex MdbIndex;
typedef struct mdbsargtree MdbSargNode;
#if !MDB_NO_BACKENDS
typedef struct {
char *name;
unsigned char needs_length; /* or precision */
unsigned char needs_scale;
unsigned char needs_quotes;
} MdbBackendType;
typedef struct {
MdbBackendType *types_table;
} MdbBackend;
#endif
#if !MDB_NO_STATS
typedef struct {
gboolean collect;
unsigned long pg_reads;
} MdbStatistics;
#endif
typedef struct {
int fd;
gboolean writable;
char *filename;
guint32 jet_version;
guint32 db_key;
char db_passwd[14];
#if !MDB_NO_BACKENDS
MdbBackend *default_backend;
char *backend_name;
#endif
#if !MDB_NO_STATS
MdbStatistics *stats;
#endif
/* free map */
int map_sz;
unsigned char *free_map;
/* reference count */
int refs;
} MdbFile;
/* offset to row count on data pages...version dependant */
typedef struct {
ssize_t pg_size;
guint16 row_count_offset;
guint16 tab_num_rows_offset;
guint16 tab_num_cols_offset;
guint16 tab_num_idxs_offset;
guint16 tab_num_ridxs_offset;
guint16 tab_usage_map_offset;
guint16 tab_first_dpg_offset;
guint16 tab_cols_start_offset;
guint16 tab_ridx_entry_size;
guint16 col_fixed_offset;
guint16 col_size_offset;
guint16 col_num_offset;
guint16 tab_col_entry_size;
guint16 tab_free_map_offset;
guint16 tab_col_offset_var;
guint16 tab_col_offset_fixed;
guint16 tab_row_col_num_offset;
} MdbFormatConstants;
typedef struct {
MdbFile *f;
guint32 cur_pg;
guint16 row_num;
unsigned int cur_pos;
unsigned char pg_buf[MDB_PGSIZE];
unsigned char alt_pg_buf[MDB_PGSIZE];
unsigned int num_catalog;
GPtrArray *catalog;
#if !MDB_NO_BACKENDS
MdbBackend *default_backend;
char *backend_name;
#endif
MdbFormatConstants *fmt;
#if !MDB_NO_STATS
MdbStatistics *stats;
#endif
#ifdef HAVE_ICONV
char* jet3_iconv_code;
iconv_t iconv_in;
iconv_t iconv_out;
#endif
} MdbHandle;
typedef struct {
MdbHandle *mdb;
char object_name[MDB_MAX_OBJ_NAME+1];
int object_type;
unsigned long table_pg; /* misnomer since object may not be a table */
unsigned long kkd_pg;
unsigned int kkd_rowid;
int num_props;
GArray *props;
GArray *columns;
int flags;
} MdbCatalogEntry;
typedef struct {
gchar *name;
GHashTable *hash;
} MdbProperties;
typedef union {
int i;
double d;
char s[256];
} MdbAny;
typedef struct {
char name[MDB_MAX_OBJ_NAME+1];
int col_type;
int col_size;
void *bind_ptr;
int *len_ptr;
GHashTable *properties;
unsigned int num_sargs;
GPtrArray *sargs;
GPtrArray *idx_sarg_cache;
unsigned char is_fixed;
int query_order;
/* col_num is the current column order,
* does not include deletes */
int col_num;
int cur_value_start;
int cur_value_len;
/* MEMO/OLE readers */
guint32 cur_blob_pg_row;
int chunk_size;
/* numerics only */
int col_prec;
int col_scale;
MdbProperties *props;
/* info needed for handling deleted/added columns */
int fixed_offset;
unsigned int var_col_num;
/* row_col_num is the row column number order,
* including deleted columns */
int row_col_num;
} MdbColumn;
struct mdbsargtree {
int op;
MdbColumn *col;
MdbAny value;
void *parent;
MdbSargNode *left;
MdbSargNode *right;
};
typedef struct {
guint32 pg;
int start_pos;
int offset;
int len;
guint16 idx_starts[2000];
unsigned char cache_value[256];
} MdbIndexPage;
typedef int (*MdbSargTreeFunc)(MdbSargNode *, gpointer data);
#define MDB_MAX_INDEX_DEPTH 10
typedef struct {
int cur_depth;
guint32 last_leaf_found;
int clean_up_mode;
MdbIndexPage pages[MDB_MAX_INDEX_DEPTH];
} MdbIndexChain;
typedef struct {
MdbCatalogEntry *entry;
char name[MDB_MAX_OBJ_NAME+1];
unsigned int num_cols;
GPtrArray *columns;
unsigned int num_rows;
int index_start;
unsigned int num_real_idxs;
unsigned int num_idxs;
GPtrArray *indices;
guint32 first_data_pg;
guint32 cur_pg_num;
guint32 cur_phys_pg;
unsigned int cur_row;
int noskip_del; /* don't skip deleted rows */
/* object allocation map */
guint32 map_base_pg;
size_t map_sz;
unsigned char *usage_map;
/* pages with free space left */
guint32 freemap_base_pg;
size_t freemap_sz;
unsigned char *free_usage_map;
/* query planner */
MdbSargNode *sarg_tree;
MdbStrategy strategy;
MdbIndex *scan_idx;
MdbHandle *mdbidx;
MdbIndexChain *chain;
MdbProperties *props;
unsigned int num_var_cols; /* to know if row has variable columns */
/* temp table */
unsigned int is_temp_table;
GPtrArray *temp_table_pages;
} MdbTableDef;
struct mdbindex {
int index_num;
char name[MDB_MAX_OBJ_NAME+1];
unsigned char index_type;
guint32 first_pg;
int num_rows; /* number rows in index */
unsigned int num_keys;
short key_col_num[MDB_MAX_IDX_COLS];
unsigned char key_col_order[MDB_MAX_IDX_COLS];
unsigned char flags;
MdbTableDef *table;
};
typedef struct {
char name[MDB_MAX_OBJ_NAME+1];
} MdbColumnProp;
typedef struct {
void *value;
int siz;
int start;
unsigned char is_null;
unsigned char is_fixed;
int colnum;
int offset;
} MdbField;
typedef struct {
int op;
MdbAny value;
} MdbSarg;
/* mem.c */
extern void mdb_init();
extern void mdb_exit();
/* file.c */
extern ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg);
extern ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg);
extern unsigned char mdb_get_byte(void *buf, int offset);
extern int mdb_get_int16(void *buf, int offset);
extern long mdb_get_int32(void *buf, int offset);
extern long mdb_get_int32_msb(void *buf, int offset);
extern float mdb_get_single(void *buf, int offset);
extern double mdb_get_double(void *buf, int offset);
extern unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset);
extern int mdb_pg_get_int16(MdbHandle *mdb, int offset);
extern long mdb_pg_get_int32(MdbHandle *mdb, int offset);
extern float mdb_pg_get_single(MdbHandle *mdb, int offset);
extern double mdb_pg_get_double(MdbHandle *mdb, int offset);
extern void mdb_set_encoding(MdbHandle *mdb, const char *encoding_name);
extern MdbHandle *mdb_open(const char *filename, MdbFileFlags flags);
extern void mdb_close(MdbHandle *mdb);
extern MdbHandle *mdb_clone_handle(MdbHandle *mdb);
extern void mdb_swap_pgbuf(MdbHandle *mdb);
/* catalog.c */
extern void mdb_free_catalog(MdbHandle *mdb);
extern GPtrArray *mdb_read_catalog(MdbHandle *mdb, int obj_type);
extern void mdb_dump_catalog(MdbHandle *mdb, int obj_type);
extern char *mdb_get_objtype_string(int obj_type);
/* table.c */
extern MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry);
extern void mdb_free_tabledef(MdbTableDef *table);
extern MdbTableDef *mdb_read_table(MdbCatalogEntry *entry);
extern MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type);
extern void mdb_append_column(GPtrArray *columns, MdbColumn *in_col);
extern void mdb_free_columns(GPtrArray *columns);
extern GPtrArray *mdb_read_columns(MdbTableDef *table);
extern void mdb_table_dump(MdbCatalogEntry *entry);
extern guint8 read_pg_if_8(MdbHandle *mdb, int *cur_pos);
extern guint16 read_pg_if_16(MdbHandle *mdb, int *cur_pos);
extern guint32 read_pg_if_32(MdbHandle *mdb, int *cur_pos);
extern void *read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len);
extern int mdb_is_user_table(MdbCatalogEntry *entry);
extern int mdb_is_system_table(MdbCatalogEntry *entry);
/* data.c */
extern int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr);
extern void mdb_data_dump(MdbTableDef *table);
extern void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr);
extern int mdb_rewind_table(MdbTableDef *table);
extern int mdb_fetch_row(MdbTableDef *table);
extern int mdb_is_fixed_col(MdbColumn *col);
extern char *mdb_col_to_string(MdbHandle *mdb, void *buf, int start, int datatype, int size);
extern int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len);
extern int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len);
extern int mdb_find_end_of_row(MdbHandle *mdb, int row);
extern int mdb_col_fixed_size(MdbColumn *col);
extern int mdb_col_disp_size(MdbColumn *col);
extern size_t mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr);
extern size_t mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size);
extern void mdb_set_date_fmt(const char *);
extern int mdb_read_row(MdbTableDef *table, unsigned int row);
/* dump.c */
extern void buffer_dump(const void *buf, int start, size_t len);
#if !MDB_NO_BACKENDS
/* backend.c */
extern char *mdb_get_coltype_string(MdbBackend *backend, int col_type);
extern int mdb_coltype_takes_length(MdbBackend *backend, int col_type);
extern void mdb_init_backends();
extern void mdb_register_backend(MdbBackendType *backend, char *backend_name);
extern void mdb_remove_backends();
extern int mdb_set_default_backend(MdbHandle *mdb, const char *backend_name);
extern char *mdb_get_relationships(MdbHandle *mdb);
#endif
/* sargs.c */
extern int mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields);
extern int mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field);
extern void mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data);
extern int mdb_find_indexable_sargs(MdbSargNode *node, gpointer data);
extern int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg);
extern int mdb_test_string(MdbSargNode *node, char *s);
extern int mdb_test_int(MdbSargNode *node, gint32 i);
extern int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg);
/* index.c */
extern GPtrArray *mdb_read_indices(MdbTableDef *table);
extern void mdb_index_dump(MdbTableDef *table, MdbIndex *idx);
extern void mdb_index_scan_free(MdbTableDef *table);
extern int mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg);
extern int mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row);
extern void mdb_index_hash_text(char *text, char *hash);
extern void mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table);
extern int mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row);
extern void mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest);
extern void mdb_free_indices(GPtrArray *indices);
void mdb_index_page_reset(MdbIndexPage *ipg);
extern int mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg);
#if !MDB_NO_STATS
/* stats.c */
extern void mdb_stats_on(MdbHandle *mdb);
extern void mdb_stats_off(MdbHandle *mdb);
extern void mdb_dump_stats(MdbHandle *mdb);
#endif
/* like.c */
extern int mdb_like_cmp(char *s, char *r);
/* write.c */
extern int mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields);
extern guint16 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size);
extern int mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum);
extern int mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields);
extern int mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size);
extern int mdb_pg_get_freespace(MdbHandle *mdb);
extern int mdb_update_row(MdbTableDef *table);
extern void *mdb_new_data_pg(MdbCatalogEntry *entry);
/* map.c */
extern guint32 mdb_map_find_next_freepage(MdbTableDef *table, int row_size);
extern guint32 mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg);
/* props.c */
extern GPtrArray *mdb_read_props_list(gchar *kkd, int len);
extern void mdb_free_props(MdbProperties *props);
extern MdbProperties *mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len);
/* worktable.c */
extern MdbTableDef *mdb_create_temp_table(MdbHandle *mdb, char *name);
extern void mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col);
extern void mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed);
extern void mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int column);
extern void mdb_temp_columns_end(MdbTableDef *table);
/* options.c */
extern int mdb_get_option(unsigned long optnum);
extern void mdb_debug(int klass, char *fmt, ...);
/* iconv.c */
extern int mdb_unicode2ascii(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen);
extern int mdb_ascii2unicode(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen);
extern void mdb_iconv_init(MdbHandle *mdb);
extern void mdb_iconv_close(MdbHandle *mdb);
#ifdef __cplusplus
}
#endif
#endif /* _mdbtools_h_ */

@ -0,0 +1,11 @@
# Makefile.am for KexiMDB's stripped-down version of mdbtools
noinst_LTLIBRARIES = libmdb.la
libmdb_la_SOURCES = catalog.c mem.c file.c table.c data.c dump.c \
money.c index.c write.c map.c options.c iconv.c sargs.c \
like.c
libmdb_la_LIBADD=-lm
AM_CFLAGS = -DMETHOD= -DMDB_NO_BACKENDS -DMDB_NO_STATS \
$(GLIB_CFLAGS) \
-I${srcdir}/../include

@ -0,0 +1,139 @@
/* 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
char *
mdb_get_objtype_string(int obj_type)
{
static char *type_name[] = {"Form",
"Table",
"Macro",
"System Table",
"Report",
"Query",
"Linked Table",
"Module",
"Relationship",
"Unknown 0x09",
"Unknown 0x0a",
"Database"
};
if (obj_type > 11) {
return NULL;
} else {
return type_name[obj_type];
}
}
void mdb_free_catalog(MdbHandle *mdb)
{
unsigned int i;
if ((!mdb) || (!mdb->catalog)) return;
for (i=0; i<mdb->catalog->len; i++)
g_free (g_ptr_array_index(mdb->catalog, i));
g_ptr_array_free(mdb->catalog, TRUE);
mdb->catalog = NULL;
}
GPtrArray *mdb_read_catalog (MdbHandle *mdb, int objtype)
{
MdbCatalogEntry *entry, msysobj;
MdbTableDef *table;
char obj_id[256];
char obj_name[256];
char obj_type[256];
char obj_flags[256];
int type;
if (!mdb) return NULL;
if (mdb->catalog) mdb_free_catalog(mdb);
mdb->catalog = g_ptr_array_new();
mdb->num_catalog = 0;
/* dummy up a catalog entry so we may read the table def */
memset(&msysobj, 0, sizeof(MdbCatalogEntry));
msysobj.mdb = mdb;
msysobj.object_type = MDB_TABLE;
msysobj.table_pg = 2;
strcpy(msysobj.object_name, "MSysObjects");
/* mdb_table_dump(&msysobj); */
table = mdb_read_table(&msysobj);
if (!table) return NULL;
mdb_read_columns(table);
mdb_bind_column_by_name(table, "Id", obj_id, NULL);
mdb_bind_column_by_name(table, "Name", obj_name, NULL);
mdb_bind_column_by_name(table, "Type", obj_type, NULL);
mdb_bind_column_by_name(table, "Flags", obj_flags, NULL);
mdb_rewind_table(table);
while (mdb_fetch_row(table)) {
type = atoi(obj_type);
if (objtype==MDB_ANY || type == objtype) {
entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry));
entry->mdb = mdb;
strcpy(entry->object_name, obj_name);
entry->object_type = (type & 0x7F);
entry->table_pg = atol(obj_id) & 0x00FFFFFF;
entry->flags = atol(obj_flags);
mdb->num_catalog++;
g_ptr_array_add(mdb->catalog, entry);
}
}
mdb_free_tabledef(table);
return mdb->catalog;
}
void
mdb_dump_catalog(MdbHandle *mdb, 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 (obj_type==MDB_ANY || entry->object_type==obj_type) {
fprintf(stdout,"Type: %-10s Name: %-18s T pg: %04x KKD pg: %04x row: %2d\n",
mdb_get_objtype_string(entry->object_type),
entry->object_name,
(unsigned int) entry->table_pg,
(unsigned int) entry->kkd_pg,
entry->kkd_rowid);
}
}
return;
}

@ -0,0 +1,951 @@
/* 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"
#include "time.h"
#include "math.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#define OFFSET_MASK 0x1fff
char *mdb_money_to_string(MdbHandle *mdb, int start);
static int _mdb_attempt_bind(MdbHandle *mdb,
MdbColumn *col, unsigned char isnull, int offset, int len);
static char *mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale);
static char *mdb_date_to_string(MdbHandle *mdb, int start);
#ifdef MDB_COPY_OLE
static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size);
#endif
static char date_fmt[64] = "%x %X";
void mdb_set_date_fmt(const char *fmt)
{
date_fmt[63] = 0;
strncpy(date_fmt, fmt, 63);
}
void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr)
{
MdbColumn *col;
/*
** the column arrary is 0 based, so decrement to get 1 based parameter
*/
col=g_ptr_array_index(table->columns, col_num - 1);
if (bind_ptr)
col->bind_ptr = bind_ptr;
if (len_ptr)
col->len_ptr = len_ptr;
}
int
mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr)
{
unsigned int i;
int col_num = -1;
MdbColumn *col;
for (i=0;i<table->num_cols;i++) {
col=g_ptr_array_index(table->columns,i);
if (!strcmp(col->name,col_name)) {
col_num = i + 1;
if (bind_ptr)
col->bind_ptr = bind_ptr;
if (len_ptr)
col->len_ptr = len_ptr;
break;
}
}
return col_num;
}
/**
* mdb_find_pg_row
* @mdb: Database file handle
* @pg_row: Lower byte contains the row number, the upper three contain page
* @buf: Pointer for returning a pointer to the page
* @off: Pointer for returning an offset to the row
* @len: Pointer for returning the length of the row
*
* Returns: 0 on success. 1 on failure.
*/
int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len)
{
unsigned int pg = pg_row >> 8;
unsigned int row = pg_row & 0xff;
if (mdb_read_alt_pg(mdb, pg) != mdb->fmt->pg_size)
return 1;
mdb_swap_pgbuf(mdb);
mdb_find_row(mdb, row, off, len);
mdb_swap_pgbuf(mdb);
*buf = mdb->alt_pg_buf;
return 0;
}
int mdb_find_row(MdbHandle *mdb, int row, int *start, size_t *len)
{
int rco = mdb->fmt->row_count_offset;
int next_start;
if (row > 1000) return -1;
*start = mdb_get_int16(mdb->pg_buf, rco + 2 + row*2);
next_start = (row == 0) ? mdb->fmt->pg_size :
mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK;
*len = next_start - (*start & OFFSET_MASK);
return 0;
}
int
mdb_find_end_of_row(MdbHandle *mdb, int row)
{
int rco = mdb->fmt->row_count_offset;
int row_end;
#if 1
if (row > 1000) return -1;
row_end = (row == 0) ? mdb->fmt->pg_size :
mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK;
#else
/* Search the previous "row start" values for the first non-'lookupflag'
* one. If we don't find one, then the end of the page is the correct
* value.
*/
int i, row_start;
if (row > 1000) return -1;
/* if lookupflag is not set, it's good (deleteflag is ok) */
for (i = row; i > 0; i--) {
row_start = mdb_get_int16(mdb->pg_buf, (rco + i*2));
if (!(row_start & 0x8000)) {
break;
}
}
row_end = (i == 0) ? mdb->fmt->pg_size : row_start & OFFSET_MASK;
#endif
return row_end - 1;
}
int mdb_is_null(unsigned char *null_mask, int col_num)
{
int byte_num = (col_num - 1) / 8;
int bit_num = (col_num - 1) % 8;
if ((1 << bit_num) & null_mask[byte_num]) {
return 0;
} else {
return 1;
}
}
/* bool has to be handled specially because it uses the null bit to store its
** value*/
static size_t
mdb_xfer_bound_bool(MdbHandle *mdb, MdbColumn *col, int value)
{
col->cur_value_len = value;
if (col->bind_ptr) {
strcpy(col->bind_ptr, value ? "0" : "1");
}
if (col->len_ptr) {
*col->len_ptr = 1;
}
return 1;
}
static size_t
mdb_xfer_bound_ole(MdbHandle *mdb, int start, MdbColumn *col, int len)
{
size_t ret = 0;
if (len) {
col->cur_value_start = start;
col->cur_value_len = len;
} else {
col->cur_value_start = 0;
col->cur_value_len = 0;
}
#ifdef MDB_COPY_OLE
if (col->bind_ptr || col->len_ptr) {
ret = mdb_copy_ole(mdb, col->bind_ptr, start, len);
}
#else
if (col->bind_ptr) {
memcpy(col->bind_ptr, mdb->pg_buf + start, MDB_MEMO_OVERHEAD);
}
ret = MDB_MEMO_OVERHEAD;
#endif
if (col->len_ptr) {
*col->len_ptr = ret;
}
return ret;
}
static size_t
mdb_xfer_bound_data(MdbHandle *mdb, int start, MdbColumn *col, int len)
{
int ret;
if (len) {
col->cur_value_start = start;
col->cur_value_len = len;
} else {
col->cur_value_start = 0;
col->cur_value_len = 0;
}
if (col->bind_ptr) {
if (!len) {
strcpy(col->bind_ptr, "");
} else {
char *str;
if (col->col_type == MDB_NUMERIC) {
str = mdb_num_to_string(mdb, start,
col->col_type, col->col_prec,
col->col_scale);
} else {
str = mdb_col_to_string(mdb, mdb->pg_buf, start,
col->col_type, len);
}
strcpy(col->bind_ptr, str);
g_free(str);
}
ret = strlen(col->bind_ptr);
if (col->len_ptr) {
*col->len_ptr = ret;
}
return ret;
}
return 0;
}
int mdb_read_row(MdbTableDef *table, unsigned int row)
{
MdbHandle *mdb = table->entry->mdb;
MdbColumn *col;
unsigned int i;
int rc;
int row_start;
size_t row_size;
int delflag, lookupflag;
MdbField fields[256];
int num_fields;
if (table->num_rows == 0)
return 0;
mdb_find_row(mdb, row, &row_start, &row_size);
delflag = lookupflag = 0;
if (row_start & 0x8000) lookupflag++;
if (row_start & 0x4000) delflag++;
row_start &= OFFSET_MASK; /* remove flags */
#if MDB_DEBUG
fprintf(stdout,"Row %d bytes %d to %d %s %s\n",
row, row_start, row_start + row_size - 1,
lookupflag ? "[lookup]" : "",
delflag ? "[delflag]" : "");
#endif
if (!table->noskip_del && delflag) {
return 0;
}
num_fields = mdb_crack_row(table, row_start, row_start + row_size - 1,
fields);
if (!mdb_test_sargs(table, fields, num_fields)) return 0;
#if MDB_DEBUG
fprintf(stdout,"sarg test passed row %d \n", row);
#endif
#if MDB_DEBUG
buffer_dump(mdb->pg_buf, row_start, row_size);
#endif
/* take advantage of mdb_crack_row() to clean up binding */
/* use num_cols instead of num_fields -- bsb 03/04/02 */
for (i = 0; i < table->num_cols; i++) {
col = g_ptr_array_index(table->columns,fields[i].colnum);
rc = _mdb_attempt_bind(mdb, col, fields[i].is_null,
fields[i].start, fields[i].siz);
}
return 1;
}
static int _mdb_attempt_bind(MdbHandle *mdb,
MdbColumn *col,
unsigned char isnull,
int offset,
int len)
{
if (col->col_type == MDB_BOOL) {
mdb_xfer_bound_bool(mdb, col, isnull);
} else if (isnull) {
mdb_xfer_bound_data(mdb, 0, col, 0);
} else if (col->col_type == MDB_OLE) {
mdb_xfer_bound_ole(mdb, offset, col, len);
} else {
mdb_xfer_bound_data(mdb, offset, col, len);
}
return 1;
}
int mdb_read_next_dpg(MdbTableDef *table)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
int next_pg;
#ifndef SLOW_READ
next_pg = mdb_map_find_next(mdb, table->usage_map,
table->map_sz, table->cur_phys_pg);
if (next_pg >= 0) {
if (mdb_read_pg(mdb, next_pg)) {
table->cur_phys_pg = next_pg;
return table->cur_phys_pg;
} else {
return 0;
}
}
fprintf(stderr, "Warning: defaulting to brute force read\n");
#endif
/* can't do a fast read, go back to the old way */
do {
if (!mdb_read_pg(mdb, table->cur_phys_pg++))
return 0;
} while (mdb->pg_buf[0]!=0x01 || mdb_get_int32(mdb->pg_buf, 4)!=entry->table_pg);
/* fprintf(stderr,"returning new page %ld\n", table->cur_phys_pg); */
return table->cur_phys_pg;
}
int mdb_rewind_table(MdbTableDef *table)
{
table->cur_pg_num=0;
table->cur_phys_pg=0;
table->cur_row=0;
return 0;
}
int
mdb_fetch_row(MdbTableDef *table)
{
MdbHandle *mdb = table->entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
unsigned int rows;
int rc;
guint32 pg;
if (table->num_rows==0)
return 0;
/* initialize */
if (!table->cur_pg_num) {
table->cur_pg_num=1;
table->cur_row=0;
if ((!table->is_temp_table)&&(table->strategy!=MDB_INDEX_SCAN))
if (!mdb_read_next_dpg(table)) return 0;
}
do {
if (table->is_temp_table) {
GPtrArray *pages = table->temp_table_pages;
rows = mdb_get_int16(
g_ptr_array_index(pages, table->cur_pg_num-1),
fmt->row_count_offset);
if (table->cur_row >= rows) {
table->cur_row = 0;
table->cur_pg_num++;
if (table->cur_pg_num > pages->len)
return 0;
}
memcpy(mdb->pg_buf,
g_ptr_array_index(pages, table->cur_pg_num-1),
fmt->pg_size);
} else if (table->strategy==MDB_INDEX_SCAN) {
if (!mdb_index_find_next(table->mdbidx, table->scan_idx, table->chain, &pg, (guint16 *) &(table->cur_row))) {
mdb_index_scan_free(table);
return 0;
}
mdb_read_pg(mdb, pg);
} else {
rows = mdb_get_int16(mdb->pg_buf,fmt->row_count_offset);
/* if at end of page, find a new page */
if (table->cur_row >= rows) {
table->cur_row=0;
if (!mdb_read_next_dpg(table)) {
return 0;
}
}
}
/* printf("page %d row %d\n",table->cur_phys_pg, table->cur_row); */
rc = mdb_read_row(table, table->cur_row);
table->cur_row++;
} while (!rc);
return 1;
}
void mdb_data_dump(MdbTableDef *table)
{
unsigned int i;
char *bound_values[MDB_MAX_COLS];
for (i=0;i<table->num_cols;i++) {
bound_values[i] = (char *) g_malloc(256);
mdb_bind_column(table, i+1, bound_values[i], NULL);
}
mdb_rewind_table(table);
while (mdb_fetch_row(table)) {
for (i=0;i<table->num_cols;i++) {
fprintf(stdout, "column %d is %s\n", i+1, bound_values[i]);
}
}
for (i=0;i<table->num_cols;i++) {
g_free(bound_values[i]);
}
}
int mdb_is_fixed_col(MdbColumn *col)
{
return col->is_fixed;
}
#if 0
static char *mdb_data_to_hex(MdbHandle *mdb, char *text, int start, int size)
{
int i;
for (i=start; i<start+size; i++) {
sprintf(&text[(i-start)*2],"%02x", mdb->pg_buf[i]);
}
text[(i-start)*2]='\0';
return text;
}
#endif
size_t
mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr)
{
guint32 ole_len;
void *buf;
int row_start;
size_t len;
ole_len = mdb_get_int32(ole_ptr, 0);
if ((ole_len & 0x80000000)
|| (ole_len & 0x40000000)) {
/* inline or single-page fields don't have a next */
return 0;
} else {
if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
&buf, &row_start, &len)) {
return 0;
}
if (col->bind_ptr)
memcpy(col->bind_ptr, (char*)buf + row_start + 4, len - 4);
col->cur_blob_pg_row = mdb_get_int32(buf, row_start);
return len;
}
return 0;
}
size_t
mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size)
{
guint32 ole_len;
void *buf;
int row_start;
size_t len;
ole_len = mdb_get_int32(ole_ptr, 0);
mdb_debug(MDB_DEBUG_OLE,"ole len = %d ole flags = %02x",
ole_len & 0x00ffffff, ole_len >> 24);
col->chunk_size = chunk_size;
if (ole_len & 0x80000000) {
/* inline ole field, if we can satisfy it, then do it */
len = col->cur_value_len - MDB_MEMO_OVERHEAD;
if ((size_t)chunk_size >= len) {
if (col->bind_ptr)
memcpy(col->bind_ptr,
&mdb->pg_buf[col->cur_value_start +
MDB_MEMO_OVERHEAD],
len);
return len;
} else {
return 0;
}
} else if (ole_len & 0x40000000) {
col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4);
mdb_debug(MDB_DEBUG_OLE,"ole row = %d ole pg = %ld",
col->cur_blob_pg_row & 0xff,
col->cur_blob_pg_row >> 8);
if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
&buf, &row_start, &len)) {
return 0;
}
mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len);
if (col->bind_ptr) {
memcpy(col->bind_ptr, (char*)buf + row_start, len);
if (mdb_get_option(MDB_DEBUG_OLE))
buffer_dump(col->bind_ptr, 0, 16);
}
return len;
} else if ((ole_len & 0xff000000) == 0) {
col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4);
if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
&buf, &row_start, &len)) {
return 0;
}
if (col->bind_ptr)
memcpy(col->bind_ptr, (char*)buf + row_start + 4, len - 4);
col->cur_blob_pg_row = mdb_get_int32(buf, row_start);
return len;
} else {
fprintf(stderr,"Unhandled ole field flags = %02x\n", ole_len >> 24);
return 0;
}
}
#ifdef MDB_COPY_OLE
static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size)
{
guint32 ole_len;
gint32 row_start, pg_row;
size_t len;
void *buf, *pg_buf = mdb->pg_buf;
if (size<MDB_MEMO_OVERHEAD) {
return 0;
}
/* The 16 bit integer at offset 0 is the length of the memo field.
* The 32 bit integer at offset 4 contains page and row information.
*/
ole_len = mdb_get_int32(pg_buf, start);
if (ole_len & 0x80000000) {
/* inline */
len = size - MDB_MEMO_OVERHEAD;
if (dest) memcpy(dest, pg_buf + start + MDB_MEMO_OVERHEAD, len);
return len;
} else if (ole_len & 0x40000000) {
/* single page */
pg_row = mdb_get_int32(pg_buf, start+4);
mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x", pg_row >> 8);
if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) {
return 0;
}
mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d",
pg_row & 0xff, row_start, len);
if (dest)
memcpy(dest, (char*)buf + row_start, len);
return len;
} else if ((ole_len & 0xff000000) == 0) {
/* multi-page */
int cur = 0;
pg_row = mdb_get_int32(pg_buf, start+4);
do {
mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x",
pg_row >> 8);
if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) {
return 0;
}
mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d",
pg_row & 0xff, row_start, len);
if (dest)
memcpy(dest+cur, buf + row_start + 4, len - 4);
cur += len - 4;
/* find next lval page */
pg_row = mdb_get_int32(buf, row_start);
} while ((pg_row >> 8));
return cur;
} else {
fprintf(stderr, "Unhandled ole field flags = %02x\n", ole_len >> 24);
return 0;
}
}
#endif
static char *mdb_memo_to_string(MdbHandle *mdb, int start, int size)
{
guint32 memo_len;
gint32 row_start, pg_row;
size_t len;
void *buf, *pg_buf = mdb->pg_buf;
char *text = (char *) g_malloc(MDB_BIND_SIZE);
if (size<MDB_MEMO_OVERHEAD) {
strcpy(text, "");
return text;
}
#if MDB_DEBUG
buffer_dump(pg_buf, start, MDB_MEMO_OVERHEAD);
#endif
/* The 32 bit integer at offset 0 is the length of the memo field
* with some flags in the high bits.
* The 32 bit integer at offset 4 contains page and row information.
*/
memo_len = mdb_get_int32(pg_buf, start);
if (memo_len & 0x80000000) {
/* inline memo field */
mdb_unicode2ascii(mdb, (char*)pg_buf + start + MDB_MEMO_OVERHEAD,
size - MDB_MEMO_OVERHEAD, text, MDB_BIND_SIZE);
return text;
} else if (memo_len & 0x40000000) {
/* single-page memo field */
pg_row = mdb_get_int32(pg_buf, start+4);
#if MDB_DEBUG
printf("Reading LVAL page %06x\n", pg_row >> 8);
#endif
if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) {
strcpy(text, "");
return text;
}
#if MDB_DEBUG
printf("row num %d start %d len %d\n",
pg_row & 0xff, row_start, len);
buffer_dump(buf, row_start, len);
#endif
mdb_unicode2ascii(mdb, (char*)buf + row_start, len, text, MDB_BIND_SIZE);
return text;
} else if ((memo_len & 0xff000000) == 0) {
/* multi-page memo field */
guint32 tmpoff = 0;
char *tmp;
tmp = (char *) g_malloc(memo_len);
pg_row = mdb_get_int32(pg_buf, start+4);
do {
#if MDB_DEBUG
printf("Reading LVAL page %06x\n", pg_row >> 8);
#endif
if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) {
g_free(tmp);
strcpy(text, "");
return text;
}
#if MDB_DEBUG
printf("row num %d start %d len %d\n",
pg_row & 0xff, row_start, len);
#endif
if (tmpoff + len - 4 > memo_len) {
break;
}
memcpy(tmp + tmpoff, (char*)buf + row_start + 4, len - 4);
tmpoff += len - 4;
} while (( pg_row = mdb_get_int32(buf, row_start) ));
if (tmpoff < memo_len) {
fprintf(stderr, "Warning: incorrect memo length\n");
}
mdb_unicode2ascii(mdb, tmp, tmpoff, text, MDB_BIND_SIZE);
g_free(tmp);
return text;
} else {
fprintf(stderr, "Unhandled memo field flags = %02x\n", memo_len >> 24);
strcpy(text, "");
return text;
}
}
static char *
mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale)
{
char *text;
int negative;
gint32 l;
memcpy(&l, mdb->pg_buf+start+13, 4);
negative = (*(mdb->pg_buf+start) & 0x80) ? 1 : 0;
text = (char *) g_malloc(prec+2+negative);
if (negative) {
sprintf(text, "-%0*" G_GINT32_FORMAT, prec, GINT32_FROM_LE(l));
} else {
sprintf(text, "%0*" G_GINT32_FORMAT, prec, GINT32_FROM_LE(l));
}
if (scale) {
memmove(text+prec-scale+1+negative, text+prec-scale+negative, scale+1);
text[prec-scale+negative] = '.';
}
return text;
}
static int trim_trailing_zeros(char * buff)
{
char *p;
int n = strlen(buff);
/* Don't need to trim strings with no decimal portion */
if(!strchr(buff,'.'))
return 0;
/* Trim the zeros */
p = buff + n - 1;
while (p >= buff && *p == '0')
*p-- = '\0';
/* If a decimal sign is left at the end, remove it too */
if (*p == '.')
*p = '\0';
return 0;
}
/* Date/Time is stored as a double, where the whole
part is the days from 12/30/1899 and the fractional
part is the fractional part of one day. */
static char *
mdb_date_to_string(MdbHandle *mdb, int start)
{
struct tm t;
long int day, time;
int yr, q;
int *cal;
int noleap_cal[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
int leap_cal[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};
char *text = (char *) g_malloc(MDB_BIND_SIZE);
double td = mdb_get_double(mdb->pg_buf, start);
day = (long int)(td);
time = (long int)(fabs(td - day) * 86400.0 + 0.5);
t.tm_hour = time / 3600;
t.tm_min = (time / 60) % 60;
t.tm_sec = time % 60;
t.tm_year = 1 - 1900;
day += 693593; /* Days from 1/1/1 to 12/31/1899 */
t.tm_wday = (day+1) % 7;
q = day / 146097; /* 146097 days in 400 years */
t.tm_year += 400 * q;
day -= q * 146097;
q = day / 36524; /* 36524 days in 100 years */
if (q > 3) q = 3;
t.tm_year += 100 * q;
day -= q * 36524;
q = day / 1461; /* 1461 days in 4 years */
t.tm_year += 4 * q;
day -= q * 1461;
q = day / 365; /* 365 days in 1 year */
if (q > 3) q = 3;
t.tm_year += q;
day -= q * 365;
yr = t.tm_year + 1900;
cal = ((yr)%4==0 && ((yr)%100!=0 || (yr)%400==0)) ?
leap_cal : noleap_cal;
for (t.tm_mon=0; t.tm_mon<12; t.tm_mon++) {
if (day < cal[t.tm_mon+1]) break;
}
t.tm_mday = day - cal[t.tm_mon] + 1;
t.tm_yday = day;
t.tm_isdst = -1;
strftime(text, MDB_BIND_SIZE, date_fmt, &t);
return text;
}
int floor_log10(double f, int is_single)
{
unsigned int i;
double y = 10.0;
if (f < 0.0)
f = -f;
if ((f == 0.0) || (f == 1.0)) {
return 0;
} else if (f < 1.0) {
if (is_single) {
/* The intermediate value p is necessary to prevent
* promotion of the comparison to type double */
float p;
for (i=1; (p = f * y) < 1.0; i++)
y *= 10.0;
} else {
for (i=1; f * y < 1.0; i++)
y *= 10.0;
}
return -(int)i;
} else { /* (x > 1.0) */
for (i=0; f >= y; i++)
y *= 10.0;
return (int)i;
}
}
char *mdb_col_to_string(MdbHandle *mdb, void *buf, int start, int datatype, int size)
{
char *text = NULL;
float tf;
double td;
switch (datatype) {
case MDB_BOOL:
/* shouldn't happen. bools are handled specially
** by mdb_xfer_bound_bool() */
break;
case MDB_BYTE:
text = g_strdup_printf("%d", mdb_get_byte(buf, start));
break;
case MDB_INT:
text = g_strdup_printf("%ld",
(long)mdb_get_int16(buf, start));
break;
case MDB_LONGINT:
text = g_strdup_printf("%ld",
mdb_get_int32(buf, start));
break;
case MDB_FLOAT:
tf = mdb_get_single(buf, start);
text = g_strdup_printf("%.*f",
FLT_DIG - floor_log10(tf,1) - 1, tf);
trim_trailing_zeros(text);
break;
case MDB_DOUBLE:
td = mdb_get_double(buf, start);
text = g_strdup_printf("%.*f",
DBL_DIG - floor_log10(td,0) - 1, td);
trim_trailing_zeros(text);
break;
case MDB_TEXT:
if (size<0) {
text = g_strdup("");
} else {
text = (char *) g_malloc(MDB_BIND_SIZE);
mdb_unicode2ascii(mdb, (char*)buf + start,
size, text, MDB_BIND_SIZE);
}
break;
case MDB_SDATETIME:
text = mdb_date_to_string(mdb, start);
break;
case MDB_MEMO:
text = mdb_memo_to_string(mdb, start, size);
break;
case MDB_MONEY:
text = mdb_money_to_string(mdb, start);
case MDB_NUMERIC:
break;
default:
text = g_strdup("");
break;
}
return text;
}
int mdb_col_disp_size(MdbColumn *col)
{
switch (col->col_type) {
case MDB_BOOL:
return 1;
break;
case MDB_BYTE:
return 4;
break;
case MDB_INT:
return 6;
break;
case MDB_LONGINT:
return 11;
break;
case MDB_FLOAT:
return 10;
break;
case MDB_DOUBLE:
return 10;
break;
case MDB_TEXT:
return col->col_size;
break;
case MDB_SDATETIME:
return 20;
break;
case MDB_MEMO:
return 64000;
break;
case MDB_MONEY:
return 21;
break;
}
return 0;
}
int mdb_col_fixed_size(MdbColumn *col)
{
switch (col->col_type) {
case MDB_BOOL:
return 1;
break;
case MDB_BYTE:
return -1;
break;
case MDB_INT:
return 2;
break;
case MDB_LONGINT:
return 4;
break;
case MDB_FLOAT:
return 4;
break;
case MDB_DOUBLE:
return 8;
break;
case MDB_TEXT:
return -1;
break;
case MDB_SDATETIME:
return 4;
break;
case MDB_MEMO:
return -1;
break;
case MDB_MONEY:
return 8;
break;
}
return 0;
}

@ -0,0 +1,40 @@
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
void buffer_dump(const void* buf, int start, size_t len)
{
char asc[20];
int j, k;
memset(asc, 0, sizeof(asc));
k = 0;
for (j=start; (size_t)j<(start+len); j++) {
int c = ((const unsigned char *)(buf))[j];
if (k == 0) {
fprintf(stdout, "%04x ", j);
}
fprintf(stdout, "%02x ", c);
asc[k] = isprint(c) ? c : '.';
k++;
if (k == 8) {
fprintf(stdout, " ");
}
if (k == 16) {
fprintf(stdout, " %s\n", asc);
memset(asc, 0, sizeof(asc));
k = 0;
}
}
for (j=k; j<16; j++) {
fprintf(stdout, " ");
}
if (k < 8) {
fprintf(stdout, " ");
}
fprintf(stdout, " %s\n", asc);
}

@ -0,0 +1,418 @@
/* MDB Tools - A library for reading MS Access database files
* 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
/*
typedef struct {
int pg_size;
guint16 row_count_offset;
guint16 tab_num_rows_offset;
guint16 tab_num_cols_offset;
guint16 tab_num_idxs_offset;
guint16 tab_num_ridxs_offset;
guint16 tab_usage_map_offset;
guint16 tab_first_dpg_offset;
guint16 tab_cols_start_offset;
guint16 tab_ridx_entry_size;
guint16 col_fixed_offset;
guint16 col_size_offset;
guint16 col_num_offset;
guint16 tab_col_entry_size;
guint16 tab_free_map_offset;
guint16 tab_col_offset_var;
guint16 tab_col_offset_fixed;
guint16 tab_row_col_num_offset;
} MdbFormatConstants;
*/
MdbFormatConstants MdbJet4Constants = {
4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9
};
MdbFormatConstants MdbJet3Constants = {
2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5
};
static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg);
/**
* mdb_find_file:
* @filename: path to MDB (database) file
*
* Finds and returns the absolute path to an MDB file. Function will first try
* to fstat file as passed, then search through the $MDBPATH if not found.
*
* Return value: gchar pointer to absolute path. Caller is responsible for
* freeing.
**/
static char *mdb_find_file(const char *file_name)
{
struct stat status;
gchar *mdbpath, **dir, *tmpfname;
unsigned int i = 0;
/* try the provided file name first */
if (!stat(file_name, &status)) {
return g_strdup(file_name);
}
/* Now pull apart $MDBPATH and try those */
mdbpath = (gchar *) getenv("MDBPATH");
/* no path, can't find file */
if (!mdbpath || !strlen(mdbpath)) return NULL;
dir = g_strsplit(mdbpath, ":", 0);
while (dir[i]) {
if (!strlen(dir[i])) continue;
tmpfname = g_strconcat(dir[i++], "/", file_name, NULL);
if (!stat(tmpfname, &status)) {
g_strfreev(dir);
return tmpfname;
}
g_free(tmpfname);
}
g_strfreev(dir);
return NULL;
}
/**
* mdb_set_encoding:
* @mdb: Handle to MDB database file
* @encoding_name: encoding name for MDB (database) file in JET3 version.
* A copy of the string will be created.
*
* Sets encoding name for MDB (database) file in JET3 version.
* JET3 databases have no usincode support but only ANSI code page (e.g. CP1252)
* (not ISO), so you need to decide what code page strings in the MDB file are encoded in.
*
* Use this function after mdb_open()) but BEFORE any operation which reads text strings
* from the MDB file.
* "MDB_JET3_CHARSET" environment variable has priority over this setting.
*
**/
void mdb_set_encoding(MdbHandle *mdb, const char *encoding_name)
{
#ifdef HAVE_ICONV
mdb_iconv_close(mdb);
g_free(mdb->jet3_iconv_code);
mdb->jet3_iconv_code = g_strdup(encoding_name);
mdb_iconv_init(mdb);
#endif
}
/**
* mdb_open:
* @filename: path to MDB (database) file
* @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write
*
* Opens an MDB file and returns an MdbHandle to it. MDB File may be relative
* to the current directory, a full path to the file, or relative to a
* component of $MDBPATH.
*
* Return value: pointer to MdbHandle structure.
**/
MdbHandle *mdb_open(const char *filename, MdbFileFlags flags)
{
MdbHandle *mdb;
int open_flags;
mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle));
#if !MDB_NO_BACKENDS
mdb_set_default_backend(mdb, "access");
#endif
#ifdef HAVE_ICONV
mdb->jet3_iconv_code = 0;
mdb->iconv_in = (iconv_t)-1;
mdb->iconv_out = (iconv_t)-1;
#endif
/* need something to bootstrap with, reassign after page 0 is read */
mdb->fmt = &MdbJet3Constants;
mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile));
mdb->f->refs = 1;
mdb->f->fd = -1;
mdb->f->filename = mdb_find_file(filename);
if (!mdb->f->filename) {
fprintf(stderr, "Can't alloc filename\n");
mdb_close(mdb);
return NULL;
}
if (flags & MDB_WRITABLE) {
mdb->f->writable = TRUE;
open_flags = O_RDWR;
} else {
open_flags = O_RDONLY;
}
#ifdef _WIN32
open_flags |= O_BINARY;
#endif
mdb->f->fd = open(mdb->f->filename, open_flags);
if (mdb->f->fd==-1) {
fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename);
mdb_close(mdb);
return NULL;
}
if (!mdb_read_pg(mdb, 0)) {
fprintf(stderr,"Couldn't read first page.\n");
mdb_close(mdb);
return NULL;
}
if (mdb->pg_buf[0] != 0) {
mdb_close(mdb);
return NULL;
}
mdb->f->jet_version = mdb_get_int32(mdb->pg_buf, 0x14);
if (IS_JET4(mdb)) {
mdb->fmt = &MdbJet4Constants;
} else if (IS_JET3(mdb)) {
mdb->fmt = &MdbJet3Constants;
} else {
fprintf(stderr,"Unknown Jet version.\n");
mdb_close(mdb);
return NULL;
}
mdb_iconv_init(mdb);
return mdb;
}
/**
* mdb_close:
* @mdb: Handle to open MDB database file
*
* Dereferences MDB file, closes if reference count is 0, and destroys handle.
*
**/
void
mdb_close(MdbHandle *mdb)
{
if (!mdb) return;
mdb_free_catalog(mdb);
#if !MDB_NO_STATS
g_free(mdb->stats);
#endif
#if !MDB_NO_BACKENDS
g_free(mdb->backend_name);
#endif
if (mdb->f) {
if (mdb->f->refs > 1) {
mdb->f->refs--;
} else {
if (mdb->f->fd != -1) close(mdb->f->fd);
g_free(mdb->f->filename);
g_free(mdb->f);
}
}
mdb_iconv_close(mdb);
#ifdef HAVE_ICONV
g_free(mdb->jet3_iconv_code);
#endif
g_free(mdb);
}
/**
* mdb_clone_handle:
* @mdb: Handle to open MDB database file
*
* Clones an existing database handle. Cloned handle shares the file descriptor
* but has its own page buffer, page position, and similar internal variables.
*
* Return value: new handle to the database.
*/
MdbHandle *mdb_clone_handle(MdbHandle *mdb)
{
MdbHandle *newmdb;
MdbCatalogEntry *entry, *data;
unsigned int i;
newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle));
#if !MDB_NO_STATS
newmdb->stats = NULL;
#endif
newmdb->catalog = g_ptr_array_new();
for (i=0;i<mdb->num_catalog;i++) {
entry = g_ptr_array_index(mdb->catalog,i);
data = g_memdup(entry,sizeof(MdbCatalogEntry));
g_ptr_array_add(newmdb->catalog, data);
}
#if !MDB_NO_BACKENDS
newmdb->backend_name = NULL;
#endif
if (mdb->f) {
mdb->f->refs++;
}
#ifdef HAVE_ICONV
newmdb->jet3_iconv_code = g_strdup(mdb->jet3_iconv_code);
#endif
mdb_iconv_init(newmdb);
return newmdb;
}
/*
** mdb_read a wrapper for read that bails if anything is wrong
*/
ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg)
{
ssize_t len;
if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size;
len = _mdb_read_pg(mdb, mdb->pg_buf, pg);
mdb->cur_pg = pg;
/* kan - reset the cur_pos on a new page read */
mdb->cur_pos = 0; /* kan */
return len;
}
ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg)
{
ssize_t len;
len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg);
return len;
}
static ssize_t _mdb_read_pg(MdbHandle *mdb, void *pg_buf, unsigned long pg)
{
ssize_t len;
struct stat status;
off_t offset = pg * mdb->fmt->pg_size;
fstat(mdb->f->fd, &status);
if (status.st_size < offset) {
fprintf(stderr,"offset %lu is beyond EOF\n",offset);
return 0;
}
#if !MDB_NO_STATS
if (mdb->stats && mdb->stats->collect)
mdb->stats->pg_reads++;
#endif
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 (len<mdb->fmt->pg_size) {
/* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */
return 0;
}
return len;
}
void mdb_swap_pgbuf(MdbHandle *mdb)
{
char tmpbuf[MDB_PGSIZE];
memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE);
memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE);
memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE);
}
unsigned char mdb_get_byte(void *buf, int offset)
{
return ((unsigned char *)(buf))[offset];
}
unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset)
{
if (offset < 0 || (offset+1) > (int)mdb->fmt->pg_size) return -1;
mdb->cur_pos++;
return mdb->pg_buf[offset];
}
int mdb_get_int16(void *buf, int offset)
{
guint16 l;
memcpy(&l, (char*)buf + offset, 2);
return (int)GUINT16_FROM_LE(l);
}
int mdb_pg_get_int16(MdbHandle *mdb, int offset)
{
if (offset < 0 || (offset+2) > (int)mdb->fmt->pg_size) return -1;
mdb->cur_pos+=2;
return mdb_get_int16(mdb->pg_buf, offset);
}
long mdb_get_int32_msb(void *buf, int offset)
{
gint32 l;
memcpy(&l, (char*)buf + offset, 4);
return (long)GINT32_FROM_BE(l);
}
long mdb_get_int32(void *buf, int offset)
{
gint32 l;
memcpy(&l, (char*)buf + offset, 4);
return (long)GINT32_FROM_LE(l);
}
long mdb_pg_get_int32(MdbHandle *mdb, int offset)
{
if (offset <0 || (offset+4) > (int)mdb->fmt->pg_size) return -1;
mdb->cur_pos+=4;
return mdb_get_int32(mdb->pg_buf, offset);
}
float mdb_get_single(void *buf, int offset)
{
union {guint32 g; float f;} f;
memcpy(&f, (char*)buf + offset, 4);
f.g = GUINT32_FROM_LE(f.g);
return f.f;
}
float mdb_pg_get_single(MdbHandle *mdb, int offset)
{
if (offset <0 || (offset+4) > (int)mdb->fmt->pg_size) return -1;
mdb->cur_pos+=4;
return mdb_get_single(mdb->pg_buf, offset);
}
double mdb_get_double(void *buf, int offset)
{
union {guint64 g; double d;} d;
memcpy(&d, (char*)buf + offset, 8);
d.g = GUINT64_FROM_LE(d.g);
return d.d;
}
double mdb_pg_get_double(MdbHandle *mdb, int offset)
{
if (offset <0 || (offset+8) > (int)mdb->fmt->pg_size) return -1;
mdb->cur_pos+=8;
return mdb_get_double(mdb->pg_buf, offset);
}
int
mdb_set_pos(MdbHandle *mdb, int pos)
{
if (pos<0 || pos >= (int)mdb->fmt->pg_size) return 0;
mdb->cur_pos=pos;
return pos;
}
int mdb_get_pos(MdbHandle *mdb)
{
return mdb->cur_pos;
}

@ -0,0 +1,229 @@
/* MDB Tools - A library for reading MS Access database files
* 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"
#include "errno.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#ifdef _WIN32
#include <windows.h>
#endif
/*
* This function is used in reading text data from an MDB table.
*/
int
mdb_unicode2ascii(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen)
{
char *tmp = NULL;
size_t tlen = 0;
size_t len_in, len_out;
char *in_ptr, *out_ptr;
if ((!src) || (!dest) || (!dlen))
return 0;
/* Uncompress 'Unicode Compressed' string into tmp */
if (IS_JET4(mdb) && (slen>=2)
&& ((src[0]&0xff)==0xff) && ((src[1]&0xff)==0xfe)) {
unsigned int compress=1;
src += 2;
slen -= 2;
tmp = (char *)g_malloc(slen*2);
while (slen) {
if (*src == 0) {
compress = (compress) ? 0 : 1;
src++;
slen--;
} else if (compress) {
tmp[tlen++] = *src++;
tmp[tlen++] = 0;
slen--;
} else if (slen >= 2){
tmp[tlen++] = *src++;
tmp[tlen++] = *src++;
slen-=2;
}
}
}
in_ptr = (tmp) ? tmp : src;
out_ptr = dest;
len_in = (tmp) ? tlen : slen;
len_out = dlen;
#if HAVE_ICONV
while (1) {
iconv(mdb->iconv_in, &in_ptr, &len_in, &out_ptr, &len_out);
if ((!len_in) || (errno == E2BIG)) break;
/* Don't bail if impossible conversion is encountered */
in_ptr += (IS_JET4(mdb)) ? 2 : 1;
len_in -= (IS_JET4(mdb)) ? 2 : 1;
*out_ptr++ = '?';
len_out--;
}
dlen -= len_out;
#else
if (IS_JET3(mdb)) {
strncpy(out_ptr, in_ptr, len_in);
dlen = len_in;
} else {
/* rough UCS-2LE to ISO-8859-1 conversion */
unsigned int i;
for (i=0; i<len_in; i+=2)
dest[i/2] = (in_ptr[i+1] == 0) ? in_ptr[i] : '?';
dlen = len_in/2;
}
#endif
if (tmp) g_free(tmp);
dest[dlen]='\0';
return dlen;
}
/*
* This function is used in writing text data to an MDB table.
* If slen is 0, strlen will be used to calculate src's length.
*/
int
mdb_ascii2unicode(MdbHandle *mdb, char *src, size_t slen, char *dest, size_t dlen)
{
size_t len_in, len_out;
char *in_ptr, *out_ptr;
if ((!src) || (!dest) || (!dlen))
return 0;
in_ptr = src;
out_ptr = dest;
len_in = (slen) ? slen : strlen(in_ptr);
len_out = dlen;
#ifdef HAVE_ICONV
iconv(mdb->iconv_out, &in_ptr, &len_in, &out_ptr, &len_out);
dlen -= len_out;
#else
if (IS_JET3(mdb)) {
dlen = MIN(len_in, len_out);
strncpy(out_ptr, in_ptr, dlen);
} else {
unsigned int i;
slen = MIN(len_in, len_out/2);
dlen = slen*2;
for (i=0; i<slen; i++) {
out_ptr[i*2] = in_ptr[i];
out_ptr[i*2+1] = 0;
}
}
#endif
/* Unicode Compression */
if(IS_JET4(mdb) && (dlen>4)) {
unsigned char *tmp = g_malloc(dlen);
unsigned int tptr = 0, dptr = 0;
int comp = 1;
tmp[tptr++] = 0xff;
tmp[tptr++] = 0xfe;
while((dptr < dlen) && (tptr < dlen)) {
if (((dest[dptr+1]==0) && (comp==0))
|| ((dest[dptr+1]!=0) && (comp==1))) {
/* switch encoding mode */
tmp[tptr++] = 0;
comp = (comp) ? 0 : 1;
} else if (dest[dptr]==0) {
/* this string cannot be compressed */
tptr = dlen;
} else if (comp==1) {
/* encode compressed character */
tmp[tptr++] = dest[dptr];
dptr += 2;
} else if (tptr+1 < dlen) {
/* encode uncompressed character */
tmp[tptr++] = dest[dptr];
tmp[tptr++] = dest[dptr+1];
dptr += 2;
} else {
/* could not encode uncompressed character
* into single byte */
tptr = dlen;
}
}
if (tptr < dlen) {
memcpy(dest, tmp, tptr);
dlen = tptr;
}
g_free(tmp);
}
return dlen;
}
void mdb_iconv_init(MdbHandle *mdb)
{
const char *iconv_code;
/* check environment variable */
if (!(iconv_code=getenv("MDBICONV"))) {
iconv_code="UTF-8";
}
#ifdef HAVE_ICONV
if (IS_JET4(mdb)) {
mdb->iconv_out = iconv_open("UCS-2LE", iconv_code);
mdb->iconv_in = iconv_open(iconv_code, "UCS-2LE");
} else {
/* According to Microsoft Knowledge Base pages 289525 and */
/* 202427, code page info is not contained in the database */
/* check environment variable */
const char *jet3_iconv_code_from_env = getenv("MDB_JET3_CHARSET");
if (jet3_iconv_code_from_env)
mdb_set_encoding(mdb, jet3_iconv_code_from_env);
else if (!mdb->jet3_iconv_code) {
#ifdef _WIN32
// get the default from OS
char default_encoding[] = "CP ";
if (GetLocaleInfoA( MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT),
LOCALE_IDEFAULTANSICODEPAGE, default_encoding+2, sizeof(default_encoding)-2-1 ))
mdb->jet3_iconv_code = g_strdup(default_encoding);
else
#endif
mdb->jet3_iconv_code = g_strdup("CP1252");
}
mdb->iconv_out = iconv_open(mdb->jet3_iconv_code, iconv_code);
mdb->iconv_in = iconv_open(iconv_code, mdb->jet3_iconv_code);
}
#endif
}
void mdb_iconv_close(MdbHandle *mdb)
{
#ifdef HAVE_ICONV
if (mdb->iconv_out != (iconv_t)-1) iconv_close(mdb->iconv_out);
if (mdb->iconv_in != (iconv_t)-1) iconv_close(mdb->iconv_in);
#endif
}

@ -0,0 +1,880 @@
/* MDB Tools - A library for reading MS Access database file
* Copyright (C) 2000-2004 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
MdbIndexPage *mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain);
MdbIndexPage *mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg);
char idx_to_text[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0-7 0x00-0x07 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8-15 0x09-0x0f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16-23 0x10-0x17 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24-31 0x19-0x1f */
' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 32-39 0x20-0x27 */
0x00, 0x00, 0x00, 0x00, 0x00, ' ', ' ', 0x00, /* 40-47 0x29-0x2f */
'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', /* 48-55 0x30-0x37 */
'^', '_', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56-63 0x39-0x3f */
0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 64-71 0x40-0x47 */
'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 72-79 0x49-0x4f H */
's', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 80-87 0x50-0x57 P */
'|', '}', '~', '5', '6', '7', '8', '9', /* 88-95 0x59-0x5f */
0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 96-103 0x60-0x67 */
'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 014-111 0x69-0x6f h */
's', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 112-119 0x70-0x77 p */
'|', '}', '~', 0x00, 0x00, 0x00, 0x00, 0x00, /* 120-127 0x78-0x7f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128-135 0x80-0x87 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
0x00, 0x00, 0x00, 0x00, 0x00, '`', 0x00, 0x00, /* 0xc0-0xc7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
0x00, '`', 0x00, '`', '`', '`', 0x00, 0x00, /* 0xe0-0xe7 */
'f', 'f', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
0x00, 0x00, 0x00, 'r', 0x00, 0x00, 'r', 0x00, /* 0xf0-0xf7 */
0x81, 0x00, 0x00, 0x00, 'x', 0x00, 0x00, 0x00, /* 0xf8-0xff */
};
GPtrArray *
mdb_read_indices(MdbTableDef *table)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
MdbIndex *pidx;
unsigned int i, j;
int idx_num, key_num, col_num;
int cur_pos, name_sz, idx2_sz, type_offset;
int index_start_pg = mdb->cur_pg;
gchar *tmpbuf;
table->indices = g_ptr_array_new();
if (IS_JET4(mdb)) {
cur_pos = table->index_start + 52 * table->num_real_idxs;
idx2_sz = 28;
type_offset = 23;
} else {
cur_pos = table->index_start + 39 * table->num_real_idxs;
idx2_sz = 20;
type_offset = 19;
}
tmpbuf = (gchar *) g_malloc(idx2_sz);
for (i=0;i<table->num_idxs;i++) {
read_pg_if_n(mdb, tmpbuf, &cur_pos, idx2_sz);
pidx = (MdbIndex *) g_malloc0(sizeof(MdbIndex));
pidx->table = table;
pidx->index_num = mdb_get_int16(tmpbuf, 4);
pidx->index_type = tmpbuf[type_offset];
g_ptr_array_add(table->indices, pidx);
}
g_free(tmpbuf);
for (i=0;i<table->num_idxs;i++) {
pidx = g_ptr_array_index (table->indices, i);
if (IS_JET4(mdb)) {
name_sz=read_pg_if_16(mdb, &cur_pos);
} else {
name_sz=read_pg_if_8(mdb, &cur_pos);
}
tmpbuf = g_malloc(name_sz);
read_pg_if_n(mdb, tmpbuf, &cur_pos, name_sz);
mdb_unicode2ascii(mdb, tmpbuf, name_sz, pidx->name, MDB_MAX_OBJ_NAME);
g_free(tmpbuf);
}
mdb_read_alt_pg(mdb, entry->table_pg);
mdb_read_pg(mdb, index_start_pg);
cur_pos = table->index_start;
idx_num=0;
for (i=0;i<table->num_real_idxs;i++) {
if (IS_JET4(mdb)) cur_pos += 4;
do {
pidx = g_ptr_array_index (table->indices, idx_num++);
} while (pidx && pidx!=(MdbIndex*)0xbaadf00d /*(js) temp? hack*/&& pidx->index_type==2);
/* if there are more real indexes than index entries left after
removing type 2's decrement real indexes and continue. Happens
on Northwind Orders table.
*/
if (!pidx || pidx==(MdbIndex*)0xbaadf00d /*(js) temp? hack*/) {
table->num_real_idxs--;
continue;
}
pidx->num_rows = mdb_get_int32(mdb->alt_pg_buf,
fmt->tab_cols_start_offset +
(i*fmt->tab_ridx_entry_size));
key_num=0;
for (j=0;j<MDB_MAX_IDX_COLS;j++) {
col_num=read_pg_if_16(mdb,&cur_pos);
if (col_num == 0xFFFF) {
cur_pos++;
continue;
}
/* set column number to a 1 based column number and store */
pidx->key_col_num[key_num] = col_num + 1;
pidx->key_col_order[key_num] =
(read_pg_if_8(mdb, &cur_pos)) ? MDB_ASC : MDB_DESC;
key_num++;
}
pidx->num_keys = key_num;
cur_pos += 4;
pidx->first_pg = read_pg_if_32(mdb, &cur_pos);
pidx->flags = read_pg_if_8(mdb, &cur_pos);
if (IS_JET4(mdb)) cur_pos += 9;
}
return NULL;
}
void
mdb_index_hash_text(char *text, char *hash)
{
unsigned int k;
for (k=0;k<strlen(text);k++) {
int c = ((unsigned char *)(text))[k];
hash[k] = idx_to_text[c];
if (!(hash[k])) fprintf(stderr,
"No translation available for %02x %d\n", c, c);
}
hash[strlen(text)]='\0';
}
/*
* reverse the order of the column for hashing
*/
void
mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest)
{
int i, j = 0;
for (i = sz-1; i >= 0; i--) {
dest[j++] = src[i];
}
}
void
mdb_index_cache_sarg(MdbColumn *col, MdbSarg *sarg, MdbSarg *idx_sarg)
{
unsigned char *c;
switch (col->col_type) {
case MDB_TEXT:
mdb_index_hash_text(sarg->value.s, idx_sarg->value.s);
break;
case MDB_LONGINT:
idx_sarg->value.i = GUINT32_SWAP_LE_BE(sarg->value.i);
c = (unsigned char *) &(idx_sarg->value.i);
c[0] |= 0x80;
break;
case MDB_INT:
break;
default:
break;
}
}
#if 0
int
mdb_index_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSarg *sarg, int offset, int len)
{
char tmpbuf[256];
int lastchar;
switch (col->col_type) {
case MDB_BYTE:
return mdb_test_int(sarg, mdb_pg_get_byte(mdb, offset));
break;
case MDB_INT:
return mdb_test_int(sarg, mdb_pg_get_int16(mdb, offset));
break;
case MDB_LONGINT:
return mdb_test_int(sarg, mdb_pg_get_int32(mdb, offset));
break;
case MDB_TEXT:
strncpy(tmpbuf, &mdb->pg_buf[offset],255);
lastchar = len > 255 ? 255 : len;
tmpbuf[lastchar]='\0';
return mdb_test_string(sarg, tmpbuf);
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type);
break;
}
return 1;
}
#endif
int
mdb_index_test_sargs(MdbHandle *mdb, MdbIndex *idx, char *buf, int len)
{
unsigned int i, j;
MdbColumn *col;
MdbTableDef *table = idx->table;
MdbSarg *idx_sarg;
MdbSarg *sarg;
MdbField field;
MdbSargNode node;
int c_len;
for (i=0;i<idx->num_keys;i++) {
col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
/*
* This will go away eventually
*/
if (col->col_type==MDB_TEXT) {
c_len = strlen(buf);
} else {
c_len = col->col_size;
}
/*
* If we have no cached index values for this column,
* create them.
*/
if (col->num_sargs && !col->idx_sarg_cache) {
col->idx_sarg_cache = g_ptr_array_new();
for (j=0;j<col->num_sargs;j++) {
sarg = g_ptr_array_index (col->sargs, j);
idx_sarg = g_memdup(sarg,sizeof(MdbSarg));
mdb_index_cache_sarg(col, sarg, idx_sarg);
g_ptr_array_add(col->idx_sarg_cache, idx_sarg);
}
}
for (j=0;j<col->num_sargs;j++) {
sarg = g_ptr_array_index (col->idx_sarg_cache, j);
/* XXX - kludge */
node.op = sarg->op;
node.value = sarg->value;
field.value = buf;
field.siz = c_len;
field.is_null = FALSE;
if (!mdb_test_sarg(mdb, col, &node, &field)) {
/* sarg didn't match, no sense going on */
return 0;
}
}
}
return 1;
}
/*
* pack the pages bitmap
*/
int
mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg)
{
int mask_bit = 0;
int mask_pos = 0x16;
int mask_byte = 0;
int elem = 0;
int len, start, i;
start = ipg->idx_starts[elem++];
while (start) {
len = ipg->idx_starts[elem] - start;
for (i=0; i < len; i++) {
mask_bit++;
if (mask_bit==8) {
mask_bit=0;
mdb->pg_buf[mask_pos++] = mask_byte;
mask_byte = 0;
}
/* upon reaching the len, set the bit */
}
mask_byte = (1 << mask_bit) | mask_byte;
start = ipg->idx_starts[elem++];
}
/* flush the last byte if any */
mdb->pg_buf[mask_pos++] = mask_byte;
/* remember to zero the rest of the bitmap */
for (i = mask_pos; i < 0xf8; i++) {
mdb->pg_buf[mask_pos++] = 0;
}
return 0;
}
/*
* unpack the pages bitmap
*/
int
mdb_index_unpack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg)
{
int mask_bit = 0;
int mask_pos = 0x16;
int mask_byte;
int start = 0xf8;
int elem = 0;
int len = 0;
ipg->idx_starts[elem++]=start;
do {
len = 0;
do {
mask_bit++;
if (mask_bit==8) {
mask_bit=0;
mask_pos++;
}
mask_byte = mdb->pg_buf[mask_pos];
len++;
} while (mask_pos <= 0xf8 && !((1 << mask_bit) & mask_byte));
start += len;
if (mask_pos < 0xf8) ipg->idx_starts[elem++]=start;
} while (mask_pos < 0xf8);
/* if we zero the next element, so we don't pick up the last pages starts*/
ipg->idx_starts[elem]=0;
return elem;
}
/*
* find the next entry on a page (either index or leaf). Uses state information
* stored in the MdbIndexPage across calls.
*/
int
mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg)
{
if (!ipg->pg) return 0;
/* if this page has not been unpacked to it */
if (!ipg->idx_starts[0]){
mdb_index_unpack_bitmap(mdb, ipg);
}
if (ipg->idx_starts[ipg->start_pos + 1]==0) return 0;
ipg->len = ipg->idx_starts[ipg->start_pos+1] - ipg->idx_starts[ipg->start_pos];
ipg->start_pos++;
return ipg->len;
}
void mdb_index_page_reset(MdbIndexPage *ipg)
{
ipg->offset = 0xf8; /* start byte of the index entries */
ipg->start_pos=0;
ipg->len = 0;
ipg->idx_starts[0]=0;
}
void mdb_index_page_init(MdbIndexPage *ipg)
{
memset(ipg, 0, sizeof(MdbIndexPage));
mdb_index_page_reset(ipg);
}
/*
* find the next leaf page if any given a chain. Assumes any exhausted leaf
* pages at the end of the chain have been peeled off before the call.
*/
MdbIndexPage *
mdb_find_next_leaf(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
{
MdbIndexPage *ipg, *newipg;
guint32 pg;
guint passed = 0;
ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
/*
* If we are at the first page deep and it's not an index page then
* we are simply done. (there is no page to find
*/
if (mdb->pg_buf[0]==MDB_PAGE_LEAF) {
/* Indexes can have leaves at the end that don't appear
* in the upper tree, stash the last index found so
* we can follow it at the end. */
chain->last_leaf_found = ipg->pg;
return ipg;
}
/*
* apply sargs here, currently we don't
*/
do {
ipg->len = 0;
if (!mdb_index_find_next_on_page(mdb, ipg)) {
return 0;
}
pg = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 3) >> 8;
ipg->offset += ipg->len;
/*
* add to the chain and call this function
* recursively.
*/
newipg = mdb_chain_add_page(mdb, chain, pg);
newipg = mdb_find_next_leaf(mdb, idx, chain);
return newipg;
} while (!passed);
/* no more pages */
return NULL;
}
MdbIndexPage *
mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg)
{
MdbIndexPage *ipg;
chain->cur_depth++;
if (chain->cur_depth > MDB_MAX_INDEX_DEPTH) {
fprintf(stderr,"Error! maximum index depth of %d exceeded. This is probably due to a programming bug, If you are confident that your indexes really are this deep, adjust MDB_MAX_INDEX_DEPTH in mdbtools.h and recompile.\n", MDB_MAX_INDEX_DEPTH);
exit(1);
}
ipg = &(chain->pages[chain->cur_depth - 1]);
mdb_index_page_init(ipg);
ipg->pg = pg;
return ipg;
}
/*
* returns the bottom page of the IndexChain, if IndexChain is empty it
* initializes it by reading idx->first_pg (the root page)
*/
MdbIndexPage *
mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
{
MdbIndexPage *ipg;
/*
* if it's new use the root index page (idx->first_pg)
*/
if (!chain->cur_depth) {
ipg = &(chain->pages[0]);
mdb_index_page_init(ipg);
chain->cur_depth = 1;
ipg->pg = idx->first_pg;
if (!(ipg = mdb_find_next_leaf(mdb, idx, chain)))
return 0;
} else {
ipg = &(chain->pages[chain->cur_depth - 1]);
ipg->len = 0;
}
mdb_read_pg(mdb, ipg->pg);
return ipg;
}
/*
* unwind the stack and search for new leaf node
*/
MdbIndexPage *
mdb_index_unwind(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
{
MdbIndexPage *ipg;
if (chain->cur_depth==1) {
return NULL;
}
/*
* unwind the stack until we find something or reach
* the top.
*/
ipg = NULL;
while (chain->cur_depth>1 && ipg==NULL) {
chain->cur_depth--;
ipg = mdb_find_next_leaf(mdb, idx, chain);
if (ipg) mdb_index_find_next_on_page(mdb, ipg);
}
if (chain->cur_depth==1) {
return NULL;
}
return ipg;
}
/*
* the main index function.
* caller provides an index chain which is the current traversal of index
* pages from the root page to the leaf. Initially passed as blank,
* mdb_index_find_next will store it's state information here. Each invocation
* then picks up where the last one left off, allowing us to scroll through
* the index one by one.
*
* Sargs are applied here but also need to be applied on the whole row b/c
* text columns may return false positives due to hashing and non-index
* columns with sarg values can't be tested here.
*/
int
mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row)
{
MdbIndexPage *ipg;
int passed = 0;
int idx_sz;
int idx_start = 0;
MdbColumn *col;
guint32 pg_row;
ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
/*
* loop while the sargs don't match
*/
do {
ipg->len = 0;
/*
* if no more rows on this leaf, try to find a new leaf
*/
if (!mdb_index_find_next_on_page(mdb, ipg)) {
if (!chain->clean_up_mode) {
if (!(ipg = mdb_index_unwind(mdb, idx, chain)))
chain->clean_up_mode = 1;
}
if (chain->clean_up_mode) {
if (!chain->last_leaf_found) return 0;
mdb_read_pg(mdb, chain->last_leaf_found);
chain->last_leaf_found = mdb_get_int32(
mdb->pg_buf, 0x0c);
mdb_read_pg(mdb, chain->last_leaf_found);
/* reuse the chain for cleanup mode */
chain->cur_depth = 1;
ipg = &chain->pages[0];
mdb_index_page_init(ipg);
ipg->pg = chain->last_leaf_found;
if (!mdb_index_find_next_on_page(mdb, ipg))
return 0;
}
}
pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4);
*row = pg_row & 0xff;
*pg = pg_row >> 8;
col=g_ptr_array_index(idx->table->columns,idx->key_col_num[0]-1);
idx_sz = mdb_col_fixed_size(col);
/* handle compressed indexes, single key indexes only? */
if (idx->num_keys==1 && idx_sz>0 && ipg->len - 4 < idx_sz) {
memcpy(&ipg->cache_value[idx_sz - (ipg->len - 4)], &mdb->pg_buf[ipg->offset], ipg->len);
} else {
idx_start = ipg->offset + (ipg->len - 4 - idx_sz);
memcpy(ipg->cache_value, &mdb->pg_buf[idx_start], idx_sz);
}
passed = mdb_index_test_sargs(mdb, idx, (char *)(ipg->cache_value), idx_sz);
ipg->offset += ipg->len;
} while (!passed);
return ipg->len;
}
/*
* XXX - FIX ME
* This function is grossly inefficient. It scans the entire index building
* an IndexChain to a specific row. We should be checking the index pages
* for matches against the indexed fields to find the proper leaf page, but
* getting it working first and then make it fast!
*/
int
mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row)
{
MdbIndexPage *ipg;
int passed = 0;
guint32 pg_row = (pg << 8) | (row & 0xff);
guint32 datapg_row;
ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
do {
ipg->len = 0;
/*
* if no more rows on this leaf, try to find a new leaf
*/
if (!mdb_index_find_next_on_page(mdb, ipg)) {
/* back to top? We're done */
if (chain->cur_depth==1)
return 0;
/*
* unwind the stack until we find something or reach
* the top.
*/
while (chain->cur_depth>1) {
chain->cur_depth--;
if (!(ipg = mdb_find_next_leaf(mdb, idx, chain)))
return 0;
mdb_index_find_next_on_page(mdb, ipg);
}
if (chain->cur_depth==1)
return 0;
}
/* test row and pg */
datapg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4);
if (pg_row == datapg_row) {
passed = 1;
}
ipg->offset += ipg->len;
} while (!passed);
/* index chain from root to leaf should now be in "chain" */
return 1;
}
void mdb_index_walk(MdbTableDef *table, MdbIndex *idx)
{
MdbHandle *mdb = table->entry->mdb;
int cur_pos = 0;
unsigned char marker;
MdbColumn *col;
unsigned int i;
if (idx->num_keys!=1) return;
mdb_read_pg(mdb, idx->first_pg);
cur_pos = 0xf8;
for (i=0;i<idx->num_keys;i++) {
marker = mdb->pg_buf[cur_pos++];
col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
}
}
void
mdb_index_dump(MdbTableDef *table, MdbIndex *idx)
{
unsigned int i;
MdbColumn *col;
fprintf(stdout,"index number %d\n", idx->index_num);
fprintf(stdout,"index name %s\n", idx->name);
fprintf(stdout,"index first page %d\n", idx->first_pg);
fprintf(stdout,"index rows %d\n", idx->num_rows);
if (idx->index_type==1) fprintf(stdout,"index is a primary key\n");
for (i=0;i<idx->num_keys;i++) {
col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
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->flags & MDB_IDX_UNIQUE ? "Yes" : "No"
);
}
mdb_index_walk(table, idx);
}
/*
* compute_cost tries to assign a cost to a given index using the sargs
* available in this query.
*
* Indexes with no matching sargs are assigned 0
* Unique indexes are preferred over non-uniques
* Operator preference is equal, like, isnull, others
*/
int mdb_index_compute_cost(MdbTableDef *table, MdbIndex *idx)
{
unsigned int i;
MdbColumn *col;
MdbSarg *sarg = NULL;
int not_all_equal = 0;
if (!idx->num_keys) return 0;
if (idx->num_keys > 1) {
for (i=0;i<idx->num_keys;i++) {
col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
if (col->sargs) sarg = g_ptr_array_index (col->sargs, 0);
if (!sarg || sarg->op != MDB_EQUAL) not_all_equal++;
}
}
col=g_ptr_array_index(table->columns,idx->key_col_num[0]-1);
/*
* if this is the first key column and there are no sargs,
* then this index is useless.
*/
if (!col->num_sargs) return 0;
sarg = g_ptr_array_index (col->sargs, 0);
/*
* a like with a wild card first is useless as a sarg */
if (sarg->op == MDB_LIKE && sarg->value.s[0]=='%')
return 0;
/*
* this needs a lot of tweaking.
*/
if (idx->flags & MDB_IDX_UNIQUE) {
if (idx->num_keys == 1) {
switch (sarg->op) {
case MDB_EQUAL:
return 1; break;
case MDB_LIKE:
return 4; break;
case MDB_ISNULL:
return 12; break;
default:
return 8; break;
}
} else {
switch (sarg->op) {
case MDB_EQUAL:
if (not_all_equal) return 2;
else return 1;
break;
case MDB_LIKE:
return 6; break;
case MDB_ISNULL:
return 12; break;
default:
return 9; break;
}
}
} else {
if (idx->num_keys == 1) {
switch (sarg->op) {
case MDB_EQUAL:
return 2; break;
case MDB_LIKE:
return 5; break;
case MDB_ISNULL:
return 12; break;
default:
return 10; break;
}
} else {
switch (sarg->op) {
case MDB_EQUAL:
if (not_all_equal) return 3;
else return 2;
break;
case MDB_LIKE:
return 7; break;
case MDB_ISNULL:
return 12; break;
default:
return 11; break;
}
}
}
return 0;
}
/*
* choose_index runs mdb_index_compute_cost for each available index and picks
* the best.
*
* Returns strategy to use (table scan, or index scan)
*/
MdbStrategy
mdb_choose_index(MdbTableDef *table, int *choice)
{
unsigned int i;
MdbIndex *idx;
int cost = 0;
int least = 99;
*choice = -1;
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
cost = mdb_index_compute_cost(table, idx);
if (cost && cost < least) {
least = cost;
*choice = i;
}
}
/* and the winner is: *choice */
if (least==99) return MDB_TABLE_SCAN;
return MDB_INDEX_SCAN;
}
void
mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table)
{
int i;
if (mdb_get_option(MDB_USE_INDEX) && mdb_choose_index(table, &i) == MDB_INDEX_SCAN) {
table->strategy = MDB_INDEX_SCAN;
table->scan_idx = g_ptr_array_index (table->indices, i);
table->chain = g_malloc0(sizeof(MdbIndexChain));
table->mdbidx = mdb_clone_handle(mdb);
mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
}
}
void
mdb_index_scan_free(MdbTableDef *table)
{
if (table->chain) {
g_free(table->chain);
table->chain = NULL;
}
if (table->mdbidx) {
mdb_close(table->mdbidx);
table->mdbidx = NULL;
}
}
void mdb_free_indices(GPtrArray *indices)
{
unsigned int i;
if (!indices) return;
for (i=0; i<indices->len; i++)
g_free (g_ptr_array_index(indices, i));
g_ptr_array_free(indices, TRUE);
}

@ -0,0 +1,78 @@
/* 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 <stdio.h>
#include <string.h>
#include <mdbtools.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
/**
* mdb_like_cmp
* @s: String to search within.
* @r: Search pattern.
*
* Tests the string @s to see if it matches the search pattern @r. In the
* search pattern, a percent sign indicates matching on any number of
* characters, and an underscore indicates matching any single character.
*
* Returns: 1 if the string matches, 0 if the string does not match.
*/
int mdb_like_cmp(char *s, char *r)
{
unsigned int i;
int ret;
mdb_debug(MDB_DEBUG_LIKE, "comparing %s and %s", s, r);
switch (r[0]) {
case '\0':
if (s[0]=='\0') {
return 1;
} else {
return 0;
}
case '_':
/* skip one character */
return mdb_like_cmp(&s[1],&r[1]);
case '%':
/* skip any number of characters */
/* the strlen(s)+1 is important so the next call can */
/* if there are trailing characters */
for(i=0;i<strlen(s)+1;i++) {
if (mdb_like_cmp(&s[i],&r[1])) {
return 1;
}
}
return 0;
default:
for(i=0;i<strlen(r);i++) {
if (r[i]=='_' || r[i]=='%') break;
}
if (strncmp(s,r,i)) {
return 0;
} else {
mdb_debug(MDB_DEBUG_LIKE, "at pos %d comparing %s and %s", i, &s[i], &r[i]);
ret = mdb_like_cmp(&s[i],&r[i]);
mdb_debug(MDB_DEBUG_LIKE, "returning %d (%s and %s)", ret, &s[i], &r[i]);
return ret;
}
}
}

@ -0,0 +1,133 @@
/* 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
static guint32
mdb_map_find_next0(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
{
guint32 pgnum, i, usage_bitlen;
unsigned char *usage_bitmap;
pgnum = mdb_get_int32(map, 1);
usage_bitmap = map + 5;
usage_bitlen = (map_sz - 5) * 8;
i = (start_pg >= pgnum) ? start_pg-pgnum+1 : 0;
for (; i<usage_bitlen; i++) {
if (usage_bitmap[i/8] & (1 << (i%8))) {
return pgnum + i;
}
}
/* didn't find anything */
return 0;
}
static int
mdb_map_find_next1(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
{
guint32 map_ind, max_map_pgs, offset, usage_bitlen;
/*
* start_pg will tell us where to (re)start the scan
* for the next data page. each usage_map entry points to a
* 0x05 page which bitmaps (mdb->fmt->pg_size - 4) * 8 pages.
*
* map_ind gives us the starting usage_map entry
* offset gives us a page offset into the bitmap
*/
usage_bitlen = (mdb->fmt->pg_size - 4) * 8;
max_map_pgs = (map_sz - 1) / 4;
map_ind = (start_pg + 1) / usage_bitlen;
offset = (start_pg + 1) % usage_bitlen;
for (; map_ind<max_map_pgs; map_ind++) {
unsigned char *usage_bitmap;
guint32 i, map_pg;
if (!(map_pg = mdb_get_int32(map, (map_ind*4)+1))) {
continue;
}
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);
}
usage_bitmap = mdb->alt_pg_buf + 4;
for (i=offset; i<usage_bitlen; i++) {
if (usage_bitmap[i/8] & (1 << (i%8))) {
return map_ind*usage_bitlen + i;
}
}
offset = 0;
}
/* didn't find anything */
return 0;
}
guint32
mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
{
if (map[0] == 0) {
return mdb_map_find_next0(mdb, map, map_sz, start_pg);
} else if (map[0] == 1) {
return mdb_map_find_next1(mdb, map, map_sz, start_pg);
}
fprintf(stderr, "Warning: unrecognized usage map type: %d\n", map[0]);
return -1;
}
guint32
mdb_alloc_page(MdbTableDef *table)
{
printf("Allocating new page\n");
return 0;
}
guint32
mdb_map_find_next_freepage(MdbTableDef *table, int row_size)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
guint32 pgnum;
guint32 cur_pg = 0;
int free_space;
do {
pgnum = mdb_map_find_next(mdb,
table->free_usage_map,
table->freemap_sz, cur_pg);
if (!pgnum) {
/* allocate new page */
pgnum = mdb_alloc_page(table);
return pgnum;
}
cur_pg = pgnum;
mdb_read_pg(mdb, pgnum);
free_space = mdb_pg_get_freespace(mdb);
} while (free_space < row_size);
return pgnum;
}

@ -0,0 +1,56 @@
/* MDB Tools - A library for reading MS Access database files
* 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.
*/
#ifdef JAVA
#include "javadefines.h"
#else
#include "mdbtools.h"
#include <locale.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#endif /* JAVA */
/**
* mdb_init:
*
* Initializes the LibMDB library. This function should be called exactly once
* by calling program and prior to any other function.
*
**/
/* METHOD */ void mdb_init()
{
#if !MDB_NO_BACKENDS
mdb_init_backends();
#endif
}
/**
* mdb_exit:
*
* Cleans up the LibMDB library. This function should be called exactly once
* by the calling program prior to exiting (or prior to final use of LibMDB
* functions).
*
**/
/* METHOD */ void mdb_exit()
{
#if !MDB_NO_BACKENDS
mdb_remove_backends();
#endif
}

@ -0,0 +1,138 @@
/* MDB Tools - A library for reading MS Access database file
* Copyright (C) 1998-1999 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 <stdio.h>
#include "mdbtools.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#define MAXPRECISION 19
/*
** these routines are copied from the freetds project which does something
** very similiar
*/
static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier);
static int do_carry(unsigned char *product);
static char *array_to_string(unsigned char *array, int unsigned scale, int neg);
/**
* mdb_money_to_string
* @mdb: Handle to open MDB database file
* @start: Offset of the field within the current page
*
* Returns: the allocated string that has received the value.
*/
char *mdb_money_to_string(MdbHandle *mdb, int start)
{
#define num_bytes 8
int i;
int neg=0;
unsigned char multiplier[MAXPRECISION], temp[MAXPRECISION];
unsigned char product[MAXPRECISION];
unsigned char money[num_bytes];
memset(multiplier,0,MAXPRECISION);
memset(product,0,MAXPRECISION);
multiplier[0]=1;
memcpy(money, mdb->pg_buf + start, num_bytes);
/* Perform two's complement for negative numbers */
if (money[7] & 0x80) {
neg = 1;
for (i=0;i<num_bytes;i++) {
money[i] = ~money[i];
}
for (i=0; i<num_bytes; i++) {
money[i] ++;
if (money[i]!=0) break;
}
}
for (i=0;i<num_bytes;i++) {
/* product += multiplier * current byte */
multiply_byte(product, money[i], multiplier);
/* multiplier = multiplier * 256 */
memcpy(temp, multiplier, MAXPRECISION);
memset(multiplier,0,MAXPRECISION);
multiply_byte(multiplier, 256, temp);
}
return array_to_string(product, 4, neg);
}
static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier)
{
unsigned char number[3];
unsigned int i, j;
number[0]=num%10;
number[1]=(num/10)%10;
number[2]=(num/100)%10;
for (i=0;i<MAXPRECISION;i++) {
if (multiplier[i] == 0) continue;
for (j=0;j<3;j++) {
if (number[j] == 0) continue;
product[i+j] += multiplier[i]*number[j];
}
do_carry(product);
}
return 0;
}
static int do_carry(unsigned char *product)
{
unsigned int j;
for (j=0;j<MAXPRECISION-1;j++) {
if (product[j]>9) {
product[j+1]+=product[j]/10;
product[j]=product[j]%10;
}
}
if (product[j]>9) {
product[j]=product[j]%10;
}
return 0;
}
static char *array_to_string(unsigned char *array, unsigned int scale, int neg)
{
char *s;
unsigned int top, i, j=0;
for (top=MAXPRECISION;(top>0) && (top-1>scale) && !array[top-1];top--);
s = (char *) g_malloc(22);
if (neg)
s[j++] = '-';
if (top == 0) {
s[j++] = '0';
} else {
for (i=top; i>0; i--) {
if (i == scale) s[j++]='.';
s[j++]=array[i-1]+'0';
}
}
s[j]='\0';
return s;
}

@ -0,0 +1,87 @@
/* MDB Tools - A library for reading MS Access database file
* Copyright (C) 2004 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 <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <mdbtools.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#define DEBUG 1
static unsigned long opts;
static int optset;
static void load_options();
void
mdb_debug(int klass, char *fmt, ...)
{
#ifdef DEBUG
va_list ap;
if (!optset) load_options();
if (klass & opts) {
va_start(ap, fmt);
vfprintf (stdout,fmt, ap);
va_end(ap);
fprintf(stdout,"\n");
}
#endif
}
static void
load_options()
{
char *opt;
char *s;
if (!optset && (s=getenv("MDBOPTS"))) {
opt = strtok(s, ":");
do {
if (!strcmp(opt, "use_index")) opts |= MDB_USE_INDEX;
if (!strcmp(opt, "no_memo")) opts |= MDB_NO_MEMO;
if (!strcmp(opt, "debug_like")) opts |= MDB_DEBUG_LIKE;
if (!strcmp(opt, "debug_write")) opts |= MDB_DEBUG_WRITE;
if (!strcmp(opt, "debug_usage")) opts |= MDB_DEBUG_USAGE;
if (!strcmp(opt, "debug_ole")) opts |= MDB_DEBUG_OLE;
if (!strcmp(opt, "debug_row")) opts |= MDB_DEBUG_ROW;
if (!strcmp(opt, "debug_all")) {
opts |= MDB_DEBUG_LIKE;
opts |= MDB_DEBUG_WRITE;
opts |= MDB_DEBUG_USAGE;
opts |= MDB_DEBUG_OLE;
opts |= MDB_DEBUG_ROW;
}
opt = strtok(NULL,":");
} while (opt);
}
optset = 1;
}
int
mdb_get_option(unsigned long optnum)
{
if (!optset) load_options();
return ((opts & optnum) > 0);
}

@ -0,0 +1,266 @@
/* 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.
*/
/*
* code for handling searchable arguments (sargs) used primary by the sql
* engine to support where clause handling. The sargs are configured in
* a tree with AND/OR operators connecting the child nodes. NOT operations
* have only one child on the left side. Logical operators (=,<,>,etc..)
* have no children.
*
* datatype support is a bit weak at this point. To add more types create
* a mdb_test_[type]() function and invoke it from mdb_test_sarg()
*/
#include "mdbtools.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
void
mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data)
{
if (func(node, data))
return;
if (node->left) mdb_sql_walk_tree(node->left, func, data);
if (node->right) mdb_sql_walk_tree(node->right, func, data);
}
int
mdb_test_string(MdbSargNode *node, char *s)
{
int rc;
if (node->op == MDB_LIKE) {
return mdb_like_cmp(s,node->value.s);
}
rc = strncmp(node->value.s, s, 255);
switch (node->op) {
case MDB_EQUAL:
if (rc==0) return 1;
break;
case MDB_GT:
if (rc<0) return 1;
break;
case MDB_LT:
if (rc>0) return 1;
break;
case MDB_GTEQ:
if (rc<=0) return 1;
break;
case MDB_LTEQ:
if (rc>=0) return 1;
break;
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_string() for operator %d\n",node->op);
break;
}
return 0;
}
int mdb_test_int(MdbSargNode *node, gint32 i)
{
switch (node->op) {
case MDB_EQUAL:
if (node->value.i == i) return 1;
break;
case MDB_GT:
if (node->value.i < i) return 1;
break;
case MDB_LT:
if (node->value.i > i) return 1;
break;
case MDB_GTEQ:
if (node->value.i <= i) return 1;
break;
case MDB_LTEQ:
if (node->value.i >= i) return 1;
break;
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_int() for operator %d\n",node->op);
break;
}
return 0;
}
#if 0
#endif
int
mdb_find_indexable_sargs(MdbSargNode *node, gpointer data)
{
MdbSarg sarg;
if (node->op == MDB_OR || node->op == MDB_NOT) return 1;
/*
* right now all we do is look for sargs that are anded together from
* the root. Later we may put together OR ops into a range, and then
* range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes
* col1 >= 2 and col1 <= 4 for the purpose of index scans, and then
* extra rows are thrown out when the row is tested against the main
* sarg tree. range scans are generally only a bit better than table
* scanning anyway.
*
* also, later we should support the NOT operator, but it's generally
* a pretty worthless test for indexes, ie NOT col1 = 3, we are
* probably better off table scanning.
*/
if (mdb_is_relational_op(node->op) && node->col) {
sarg.op = node->op;
sarg.value = node->value;
mdb_add_sarg(node->col, &sarg);
}
return 0;
}
int
mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field)
{
char tmpbuf[256];
if (node->op == MDB_ISNULL) {
if (field->is_null) return 0;
else return 1;
} else if (node->op == MDB_NOTNULL) {
if (field->is_null) return 1;
else return 0;
}
switch (col->col_type) {
case MDB_BOOL:
return mdb_test_int(node, !field->is_null);
break;
case MDB_BYTE:
return mdb_test_int(node, (gint32)((char *)field->value)[0]);
break;
case MDB_INT:
return mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0));
break;
case MDB_LONGINT:
return mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0));
break;
case MDB_TEXT:
mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, 256);
return mdb_test_string(node, tmpbuf);
default:
fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type);
break;
}
return 1;
}
int
mdb_find_field(int col_num, MdbField *fields, int num_fields)
{
int i;
for (i=0;i<num_fields;i++) {
if (fields[i].colnum == col_num) return i;
}
return -1;
}
int
mdb_test_sarg_node(MdbHandle *mdb, MdbSargNode *node, MdbField *fields, int num_fields)
{
int elem;
MdbColumn *col;
int rc;
if (mdb_is_relational_op(node->op)) {
col = node->col;
/* for const = const expressions */
if (!col) {
return (node->value.i);
}
elem = mdb_find_field(col->col_num, fields, num_fields);
if (!mdb_test_sarg(mdb, col, node, &fields[elem]))
return 0;
} else { /* logical op */
switch (node->op) {
case MDB_NOT:
rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields);
return !rc;
break;
case MDB_AND:
if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields))
return 0;
return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
break;
case MDB_OR:
if (mdb_test_sarg_node(mdb, node->left, fields, num_fields))
return 1;
return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
break;
}
}
return 1;
}
int
mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields)
{
MdbSargNode *node;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
node = table->sarg_tree;
/* there may not be a sarg tree */
if (!node) return 1;
return mdb_test_sarg_node(mdb, node, fields, num_fields);
}
#if 0
int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len)
{
MdbSarg *sarg;
int i;
for (i=0;i<col->num_sargs;i++) {
sarg = g_ptr_array_index (col->sargs, i);
if (!mdb_test_sarg(mdb, col, sarg, offset, len)) {
/* sarg didn't match, no sense going on */
return 0;
}
}
return 1;
}
#endif
int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg)
{
MdbSarg *sarg;
if (!col->sargs) {
col->sargs = g_ptr_array_new();
}
sarg = g_memdup(in_sarg,sizeof(MdbSarg));
g_ptr_array_add(col->sargs, sarg);
col->num_sargs++;
return 1;
}
int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg)
{
MdbColumn *col;
unsigned int i;
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index (table->columns, i);
if (!strcasecmp(col->name,colname)) {
return mdb_add_sarg(col, in_sarg);
}
}
/* else didn't find the column return 0! */
return 0;
}

@ -0,0 +1,372 @@
/* 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
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;
}
unsigned char mdb_col_needs_size(int col_type)
{
if (col_type == MDB_TEXT) {
return TRUE;
} else {
return FALSE;
}
}
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;
if (table->is_temp_table) {
unsigned int i;
/* Temp table pages are being stored in memory */
for (i=0; i<table->temp_table_pages->len; i++)
g_free(g_ptr_array_index(table->temp_table_pages,i));
g_ptr_array_free(table->temp_table_pages, TRUE);
/* Temp tables use dummy entries */
g_free(table->entry);
}
mdb_free_columns(table->columns);
mdb_free_indices(table->indices);
g_free(table->usage_map);
g_free(table->free_usage_map);
g_free(table);
}
MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
{
MdbTableDef *table;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
int len, row_start, pg_row;
void *buf, *pg_buf = mdb->pg_buf;
mdb_read_pg(mdb, entry->table_pg);
if (mdb_get_byte(pg_buf, 0) != 0x02) /* not a valid table def page */
return NULL;
table = mdb_alloc_tabledef(entry);
len = mdb_get_int16(pg_buf, 8);
table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset);
table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2);
table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset);
table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset);
table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset);
/* grab a copy of the usage map */
pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset);
mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz));
table->usage_map = g_memdup((char*)buf + row_start, table->map_sz);
if (mdb_get_option(MDB_DEBUG_USAGE))
buffer_dump(buf, row_start, table->map_sz);
mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d",
pg_row >> 8, pg_row & 0xff, row_start, table->map_sz);
/* grab a copy of the free space page map */
pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset);
mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz));
table->free_usage_map = g_memdup((char*)buf + row_start, table->freemap_sz);
mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n",
pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz);
table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
return table;
}
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;
}
guint32
read_pg_if_32(MdbHandle *mdb, int *cur_pos)
{
char c[4];
read_pg_if_n(mdb, c, cur_pos, 4);
return mdb_get_int32(c, 0);
}
guint16
read_pg_if_16(MdbHandle *mdb, int *cur_pos)
{
char c[2];
read_pg_if_n(mdb, c, cur_pos, 2);
return mdb_get_int16(c, 0);
}
guint8
read_pg_if_8(MdbHandle *mdb, int *cur_pos)
{
guint8 c;
read_pg_if_n(mdb, &c, cur_pos, 1);
return c;
}
/*
* Read data into a buffer, advancing pages and setting the
* page cursor as needed. In the case that buf in NULL, pages
* are still advanced and the page cursor is still updated.
*/
void *
read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len)
{
/* Advance to page which contains the first byte */
while (*cur_pos >= mdb->fmt->pg_size) {
mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
*cur_pos -= (mdb->fmt->pg_size - 8);
}
/* Copy pages into buffer */
while (*cur_pos + len >= mdb->fmt->pg_size) {
int piece_len = mdb->fmt->pg_size - *cur_pos;
if (buf) {
memcpy(buf, mdb->pg_buf + *cur_pos, piece_len);
buf += piece_len;
}
len -= piece_len;
mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
*cur_pos = 8;
}
/* Copy into buffer from final page */
if (len && buf) {
memcpy(buf, mdb->pg_buf + *cur_pos, len);
}
*cur_pos += len;
return buf;
}
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);
}
GPtrArray *mdb_read_columns(MdbTableDef *table)
{
MdbHandle *mdb = table->entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
MdbColumn *pcol;
unsigned char *col;
unsigned int i;
int cur_pos;
size_t name_sz;
table->columns = g_ptr_array_new();
col = (unsigned char *) g_malloc(fmt->tab_col_entry_size);
cur_pos = 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 */
/*
** column attributes
*/
for (i=0;i<table->num_cols;i++) {
#ifdef MDB_DEBUG
/* printf("column %d\n", i);
buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */
#endif
read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size);
pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn));
pcol->col_type = col[0];
pcol->col_num = col[fmt->col_num_offset];
pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var);
pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset);
/* FIXME: can this be right in Jet3 and Jet4? */
if (pcol->col_type == MDB_NUMERIC) {
pcol->col_prec = col[11];
pcol->col_scale = col[12];
}
pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0;
pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed);
if (pcol->col_type != MDB_BOOL) {
pcol->col_size = mdb_get_int16(col, fmt->col_size_offset);
} else {
pcol->col_size=0;
}
g_ptr_array_add(table->columns, pcol);
}
g_free (col);
/*
** column names - ordered the same as the column attributes table
*/
for (i=0;i<table->num_cols;i++) {
char *tmp_buf;
pcol = g_ptr_array_index(table->columns, i);
if (IS_JET4(mdb)) {
name_sz = read_pg_if_16(mdb, &cur_pos);
} else if (IS_JET3(mdb)) {
name_sz = read_pg_if_8(mdb, &cur_pos);
} else {
fprintf(stderr,"Unknown MDB version\n");
continue;
}
tmp_buf = (char *) g_malloc(name_sz);
read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz);
mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME);
g_free(tmp_buf);
}
/* Sort the columns by col_num */
g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
table->index_start = cur_pos;
return table->columns;
}
#if !MDB_NO_BACKENDS
void mdb_table_dump(MdbCatalogEntry *entry)
{
MdbTableDef *table;
MdbColumn *col;
int coln;
MdbIndex *idx;
MdbHandle *mdb = entry->mdb;
unsigned int i, bitn;
guint32 pgnum;
table = mdb_read_table(entry);
fprintf(stdout,"definition page = %lu\n",entry->table_pg);
fprintf(stdout,"number of datarows = %d\n",table->num_rows);
fprintf(stdout,"number of columns = %d\n",table->num_cols);
fprintf(stdout,"number of indices = %d\n",table->num_real_idxs);
mdb_read_columns(table);
mdb_read_indices(table);
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index(table->columns,i);
fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
i, col->name,
mdb_get_coltype_string(mdb->default_backend, col->col_type),
col->col_size);
}
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
mdb_index_dump(table, idx);
}
if (table->usage_map) {
printf("pages reserved by this object\n");
printf("usage map pg %" G_GUINT32_FORMAT "\n",
table->map_base_pg);
printf("free map pg %" G_GUINT32_FORMAT "\n",
table->freemap_base_pg);
pgnum = mdb_get_int32(table->usage_map,1);
/* the first 5 bytes of the usage map mean something */
coln = 0;
for (i=5;i<table->map_sz;i++) {
for (bitn=0;bitn<8;bitn++) {
if (table->usage_map[i] & 1 << bitn) {
coln++;
printf("%6" G_GUINT32_FORMAT, pgnum);
if (coln==10) {
printf("\n");
coln = 0;
} else {
printf(" ");
}
}
pgnum++;
}
}
printf("\n");
}
}
#endif
int mdb_is_user_table(MdbCatalogEntry *entry)
{
return ((entry->object_type == MDB_TABLE)
&& !(entry->flags & 0x80000002)) ? 1 : 0;
}
int mdb_is_system_table(MdbCatalogEntry *entry)
{
return ((entry->object_type == MDB_TABLE)
&& (entry->flags & 0x80000002)) ? 1 : 0;
}

@ -0,0 +1,99 @@
/* MDB Tools - A library for reading MS Access database files
* Copyright (C) 2004 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"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
/*
* Temp table routines. These are currently used to generate mock results for
* commands like "list tables" and "describe table"
*/
void
mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed)
{
memset(tcol,0,sizeof(MdbColumn));
strcpy(tcol->name, col_name);
tcol->col_type = col_type;
if ((col_type == MDB_TEXT) || (col_type == MDB_MEMO)) {
tcol->col_size = col_size;
} else {
tcol->col_size = mdb_col_fixed_size(tcol);
}
tcol->is_fixed = is_fixed;
}
void
mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int colnum)
{
field->value = value;
field->siz = siz;
field->is_fixed = is_fixed;
field->is_null = is_null;
field->start = start;
field->colnum = colnum;
}
MdbTableDef *
mdb_create_temp_table(MdbHandle *mdb, char *name)
{
MdbCatalogEntry *entry;
MdbTableDef *table;
/* dummy up a catalog entry */
entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry));
entry->mdb = mdb;
entry->object_type = MDB_TABLE;
entry->table_pg = 0;
strcpy(entry->object_name, name);
table = mdb_alloc_tabledef(entry);
table->columns = g_ptr_array_new();
table->is_temp_table = 1;
table->temp_table_pages = g_ptr_array_new();
return table;
}
void
mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col)
{
col->col_num = table->num_cols;
if (!col->is_fixed)
col->var_col_num = table->num_var_cols++;
g_ptr_array_add(table->columns, g_memdup(col, sizeof(MdbColumn)));
table->num_cols++;
}
/*
* Should be called after setting up all temp table columns
*/
void mdb_temp_columns_end(MdbTableDef *table)
{
MdbColumn *col;
unsigned int i;
unsigned int start = 0;
for (i=0; i<table->num_cols; i++) {
col = g_ptr_array_index(table->columns, i);
if (col->is_fixed) {
col->fixed_offset = start;
start += col->col_size;
}
}
}

@ -0,0 +1,846 @@
/* 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"
#include "time.h"
#include "math.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum);
void
_mdb_put_int16(void *buf, guint32 offset, guint32 value)
{
value = GINT32_TO_LE(value);
memcpy((char*)buf + offset, &value, 2);
}
void
_mdb_put_int32(void *buf, guint32 offset, guint32 value)
{
value = GINT32_TO_LE(value);
memcpy((char*)buf + offset, &value, 4);
}
void
_mdb_put_int32_msb(void *buf, guint32 offset, guint32 value)
{
value = GINT32_TO_BE(value);
memcpy((char*)buf + offset, &value, 4);
}
ssize_t
mdb_write_pg(MdbHandle *mdb, unsigned long pg)
{
ssize_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 ((size_t)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 (len<mdb->fmt->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)
{
unsigned int i, j;
MdbIndex *idx;
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
for (j=0;j<idx->num_keys;j++) {
if (idx->key_col_num[j]==colnum) return 1;
}
}
return 0;
}
static void
mdb_crack_row4(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
{
unsigned int i;
for (i=0; i<row_var_cols+1; i++) {
var_col_offsets[i] = mdb_get_int16(mdb->pg_buf,
row_end - bitmask_sz - 3 - (i*2));
}
}
static void
mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
{
unsigned int i;
unsigned int num_jumps = 0, jumps_used = 0;
unsigned int col_ptr, row_len;
row_len = row_end - row_start + 1;
num_jumps = (row_len - 1) / 256;
col_ptr = row_end - bitmask_sz - num_jumps - 1;
/* If last jump is a dummy value, ignore it */
if ((col_ptr-row_start-row_var_cols)/256 < num_jumps)
num_jumps--;
jumps_used = 0;
for (i=0; i<row_var_cols+1; i++) {
while ((jumps_used < num_jumps)
&& (i == mdb->pg_buf[row_end-bitmask_sz-jumps_used-1])) {
jumps_used++;
}
var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256);
}
}
/**
* mdb_crack_row:
* @table: Table that the row belongs to
* @row_start: offset to start of row on current page
* @row_end: offset to end of row on current page
* @fields: pointer to MdbField array to be popluated by mdb_crack_row
*
* Cracks a row buffer apart into its component fields.
*
* A row buffer is that portion of a data page which contains the values for
* that row. Its beginning and end can be found in the row offset table.
*
* The resulting MdbField array contains pointers into the row for each field
* present. Be aware that by modifying field[]->value, you would be modifying
* the row buffer itself, not a copy.
*
* This routine is mostly used internally by mdb_fetch_row() but may have some
* applicability for advanced application programs.
*
* Return value: number of fields present.
*/
int
mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields)
{
MdbColumn *col;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
void *pg_buf = mdb->pg_buf;
unsigned int row_var_cols=0, row_cols;
unsigned char *nullmask;
unsigned int bitmask_sz;
unsigned int *var_col_offsets = NULL;
unsigned int fixed_cols_found, row_fixed_cols;
unsigned int col_count_size;
unsigned int i;
if (mdb_get_option(MDB_DEBUG_ROW)) {
buffer_dump(pg_buf, row_start, row_end - row_start + 1);
}
if (IS_JET4(mdb)) {
row_cols = mdb_get_int16(pg_buf, row_start);
col_count_size = 2;
} else {
row_cols = mdb_get_byte(pg_buf, row_start);
col_count_size = 1;
}
bitmask_sz = (row_cols + 7) / 8;
nullmask = (char*)pg_buf + row_end - bitmask_sz + 1;
/* read table of variable column locations */
row_var_cols = IS_JET4(mdb) ?
mdb_get_int16(pg_buf, row_end - bitmask_sz - 1) :
mdb_get_byte(pg_buf, row_end - bitmask_sz);
var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int));
if (table->num_var_cols > 0) {
if (IS_JET4(mdb)) {
mdb_crack_row4(mdb, row_start, row_end, bitmask_sz,
row_var_cols, var_col_offsets);
} else {
mdb_crack_row3(mdb, row_start, row_end, bitmask_sz,
row_var_cols, var_col_offsets);
}
}
fixed_cols_found = 0;
row_fixed_cols = row_cols - row_var_cols;
if (mdb_get_option(MDB_DEBUG_ROW)) {
fprintf(stdout,"bitmask_sz %d\n", bitmask_sz);
fprintf(stdout,"row_var_cols %d\n", row_var_cols);
fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols);
}
for (i=0;i<table->num_cols;i++) {
unsigned int byte_num, bit_num;
unsigned int col_start;
col = g_ptr_array_index(table->columns,i);
fields[i].colnum = i;
fields[i].is_fixed = col->is_fixed;
byte_num = col->col_num / 8;
bit_num = col->col_num % 8;
/* logic on nulls is reverse, 1 is not null, 0 is null */
fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1;
if ((fields[i].is_fixed)
&& (fixed_cols_found < row_fixed_cols)) {
col_start = col->fixed_offset + col_count_size;
fields[i].start = row_start + col_start;
fields[i].value = (char*)pg_buf + row_start + col_start;
fields[i].siz = col->col_size;
fixed_cols_found++;
/* Use col->var_col_num because a deleted column is still
* present in the variable column offsets table for the row */
} else if ((!fields[i].is_fixed)
&& (col->var_col_num < row_var_cols)) {
col_start = var_col_offsets[col->var_col_num];
fields[i].start = row_start + col_start;
fields[i].value = (char*)pg_buf + row_start + col_start;
fields[i].siz = var_col_offsets[(col->var_col_num)+1] -
col_start;
} else {
fields[i].start = 0;
fields[i].value = NULL;
fields[i].siz = 0;
fields[i].is_null = 1;
}
}
g_free(var_col_offsets);
return row_cols;
}
static int
mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields)
{
int pos = 0, bit = 0, byte = 0;
int i;
/* 'Not null' bitmap */
for (i=0; i<num_fields; i++) {
/* column is null if bit is clear (0) */
if (!fields[i].is_null) {
byte |= 1 << bit;
}
bit++;
if (bit==8) {
buffer[pos++] = byte;
bit = byte = 0;
}
}
/* if we've written any bits to the current byte, flush it */
if (bit)
buffer[pos++] = byte;
return pos;
}
/* fields must be ordered with fixed columns first, then vars, subsorted by
* column number */
static int
mdb_pack_row4(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
{
unsigned int pos = 0;
unsigned int var_cols = 0;
unsigned int i;
row_buffer[pos++] = num_fields & 0xff;
row_buffer[pos++] = (num_fields >> 8) & 0xff;
/* Fixed length columns */
for (i=0;i<num_fields;i++) {
if (fields[i].is_fixed) {
fields[i].offset = pos;
if (!fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
}
pos += fields[i].siz;
}
}
/* For tables without variable-length columns */
if (table->num_var_cols == 0) {
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
/* Variable length columns */
for (i=0;i<num_fields;i++) {
if (!fields[i].is_fixed) {
var_cols++;
fields[i].offset = pos;
if (! fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
pos += fields[i].siz;
}
}
}
/* EOD */
row_buffer[pos] = pos & 0xff;
row_buffer[pos+1] = (pos >> 8) & 0xff;
pos += 2;
/* Offsets of the variable-length columns */
for (i=num_fields; i>0; i--) {
if (!fields[i-1].is_fixed) {
row_buffer[pos++] = fields[i-1].offset & 0xff;
row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff;
}
}
/* Number of variable-length columns */
row_buffer[pos++] = var_cols & 0xff;
row_buffer[pos++] = (var_cols >> 8) & 0xff;
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
static int
mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
{
unsigned int pos = 0;
unsigned int var_cols = 0;
unsigned int i, j;
unsigned char *offset_high;
row_buffer[pos++] = num_fields;
/* Fixed length columns */
for (i=0;i<num_fields;i++) {
if (fields[i].is_fixed) {
fields[i].offset = pos;
if (!fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
}
pos += fields[i].siz;
}
}
/* For tables without variable-length columns */
if (table->num_var_cols == 0) {
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
/* Variable length columns */
for (i=0;i<num_fields;i++) {
if (!fields[i].is_fixed) {
var_cols++;
fields[i].offset = pos;
if (! fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
pos += fields[i].siz;
}
}
}
offset_high = (unsigned char *) g_malloc(var_cols+1);
offset_high[0] = (pos >> 8) & 0xff;
j = 1;
/* EOD */
row_buffer[pos] = pos & 0xff;
pos++;
/* Variable length column offsets */
for (i=num_fields; i>0; i--) {
if (!fields[i-1].is_fixed) {
row_buffer[pos++] = fields[i-1].offset & 0xff;
offset_high[j++] = (fields[i-1].offset >> 8) & 0xff;
}
}
/* Dummy jump table entry */
if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) {
row_buffer[pos++] = 0xff;
}
/* Jump table */
for (i=0; i<var_cols; i++) {
if (offset_high[i] > offset_high[i+1]) {
row_buffer[pos++] = var_cols-i;
}
}
g_free(offset_high);
row_buffer[pos++] = var_cols;
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
int
mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields)
{
if (table->is_temp_table) {
unsigned int i;
for (i=0; i<num_fields; i++) {
MdbColumn *c = g_ptr_array_index(table->columns, i);
fields[i].is_null = (fields[i].value) ? 0 : 1;
fields[i].colnum = i;
fields[i].is_fixed = c->is_fixed;
if ((c->col_type != MDB_TEXT)
&& (c->col_type != MDB_MEMO)) {
fields[i].siz = c->col_size;
}
}
}
if (IS_JET4(table->entry->mdb)) {
return mdb_pack_row4(table, row_buffer, num_fields, fields);
} else {
return mdb_pack_row3(table, row_buffer, num_fields, fields);
}
}
int
mdb_pg_get_freespace(MdbHandle *mdb)
{
int rows, free_start, free_end;
int row_count_offset = mdb->fmt->row_count_offset;
rows = mdb_get_int16(mdb->pg_buf, row_count_offset);
free_start = row_count_offset + 2 + (rows * 2);
free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2));
mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start);
return (free_end - free_start);
}
void *
mdb_new_leaf_pg(MdbCatalogEntry *entry)
{
MdbHandle *mdb = entry->mdb;
void *new_pg = g_malloc0(mdb->fmt->pg_size);
_mdb_put_int16(new_pg, 2, 0x0104);
_mdb_put_int32(new_pg, 4, entry->table_pg);
return new_pg;
}
void *
mdb_new_data_pg(MdbCatalogEntry *entry)
{
MdbFormatConstants *fmt = entry->mdb->fmt;
void *new_pg = g_malloc0(fmt->pg_size);
_mdb_put_int16(new_pg, 2, 0x0101);
_mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2);
_mdb_put_int32(new_pg, 4, entry->table_pg);
return new_pg;
}
int
mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
{
unsigned int i;
MdbIndex *idx;
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type);
if (idx->index_type==1) {
mdb_update_index(table, idx, num_fields, fields, pgnum, rownum);
}
}
return 1;
}
int
mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
table->scan_idx = idx;
table->chain = g_malloc0(sizeof(MdbIndexChain));
table->mdbidx = mdb_clone_handle(mdb);
mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
return 1;
}
int
mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
int idx_xref[16];
unsigned int i, j;
MdbIndexChain *chain;
MdbField idx_fields[10];
for (i = 0; i < idx->num_keys; i++) {
for (j = 0; j < num_fields; j++) {
if (fields[j].colnum == idx->key_col_num[i]-1) {
idx_xref[i] = j;
idx_fields[i] = fields[j];
}
}
}
/*
for (i = 0; i < idx->num_keys; i++) {
fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n",
i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum,
fields[idx_xref[i]].siz);
}
for (i = 0; i < num_fields; i++) {
fprintf(stdout, "%d (%d %d)\n",
i, fields[i].colnum,
fields[i].siz);
}
*/
chain = g_malloc0(sizeof(MdbIndexChain));
mdb_index_find_row(mdb, idx, chain, pgnum, rownum);
mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum);
return 1;
}
int
mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields)
{
int new_row_size;
unsigned char row_buffer[4096];
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
guint32 pgnum;
guint16 rownum;
if (!mdb->f->writable) {
fprintf(stderr, "File is not open for writing\n");
return 0;
}
new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(row_buffer, 0, new_row_size);
}
pgnum = mdb_map_find_next_freepage(table, new_row_size);
if (!pgnum) {
fprintf(stderr, "Unable to allocate new page.\n");
return 0;
}
rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size);
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, fmt->pg_size - 160, 160);
}
mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum);
if (!mdb_write_pg(mdb, pgnum)) {
fprintf(stderr, "write failed! exiting...\n");
exit(1);
}
mdb_update_indexes(table, num_fields, fields, pgnum, rownum);
return 1;
}
/*
* Assumes caller has verfied space is available on page and adds the new
* row to the current pg_buf.
*/
guint16
mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size)
{
void *new_pg;
int num_rows, i, pos, row_start;
size_t row_size;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
if (table->is_temp_table) {
GPtrArray *pages = table->temp_table_pages;
if (pages->len == 0) {
new_pg = mdb_new_data_pg(entry);
g_ptr_array_add(pages, new_pg);
} else {
new_pg = g_ptr_array_index(pages, pages->len - 1);
if (mdb_get_int16(new_pg, 2) < new_row_size + 2) {
new_pg = mdb_new_data_pg(entry);
g_ptr_array_add(pages, new_pg);
}
}
num_rows = mdb_get_int16(new_pg, fmt->row_count_offset);
pos = (num_rows == 0) ? fmt->pg_size :
mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2));
} else { /* is not a temp table */
new_pg = mdb_new_data_pg(entry);
num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset);
pos = fmt->pg_size;
/* copy existing rows */
for (i=0;i<num_rows;i++) {
mdb_find_row(mdb, i, &row_start, &row_size);
pos -= row_size;
memcpy((char*)new_pg + pos, mdb->pg_buf + row_start, row_size);
_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos);
}
}
/* add our new row */
pos -= new_row_size;
memcpy((char*)new_pg + pos, row_buffer, new_row_size);
/* add row to the row offset table */
_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos);
/* update number rows on this page */
num_rows++;
_mdb_put_int16(new_pg, fmt->row_count_offset, num_rows);
/* update the freespace */
_mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2));
/* copy new page over old */
if (!table->is_temp_table) {
memcpy(mdb->pg_buf, new_pg, fmt->pg_size);
g_free(new_pg);
}
return num_rows;
}
int
mdb_update_row(MdbTableDef *table)
{
int row_start, row_end;
unsigned int i;
MdbColumn *col;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbField fields[256];
unsigned char row_buffer[4096];
size_t old_row_size, new_row_size;
unsigned int num_fields;
if (!mdb->f->writable) {
fprintf(stderr, "File is not open for writing\n");
return 0;
}
mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size);
row_end = row_start + old_row_size - 1;
row_start &= 0x0FFF; /* remove flags */
mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end);
if (mdb_get_option(MDB_DEBUG_LIKE))
buffer_dump(mdb->pg_buf, row_start, old_row_size);
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index(table->columns,i);
if (col->bind_ptr && mdb_is_col_indexed(table,i)) {
fprintf(stderr, "Attempting to update column that is part of an index\n");
return 0;
}
}
num_fields = mdb_crack_row(table, row_start, row_end, fields);
if (mdb_get_option(MDB_DEBUG_WRITE)) {
for (i=0;i<num_fields;i++) {
}
}
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index(table->columns,i);
if (col->bind_ptr) {
fields[i].value = col->bind_ptr;
fields[i].siz = *(col->len_ptr);
}
}
new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
if (mdb_get_option(MDB_DEBUG_WRITE))
buffer_dump(row_buffer, 0, new_row_size);
if (new_row_size > (old_row_size + mdb_pg_get_freespace(mdb))) {
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);
return 0;
}
int
mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
int pg_size = mdb->fmt->pg_size;
int rco = mdb->fmt->row_count_offset;
void *new_pg;
guint16 num_rows;
int row_start;
size_t row_size;
int i, pos;
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, pg_size - 160, 160);
}
mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg);
new_pg = mdb_new_data_pg(entry);
num_rows = mdb_get_int16(mdb->pg_buf, rco);
_mdb_put_int16(new_pg, rco, num_rows);
pos = pg_size;
/* rows before */
for (i=0;i<row;i++) {
mdb_find_row(mdb, i, &row_start, &row_size);
pos -= row_size;
memcpy((char*)new_pg + pos, mdb->pg_buf + row_start, row_size);
_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
}
/* our row */
pos -= new_row_size;
memcpy((char*)new_pg + pos, new_row, new_row_size);
_mdb_put_int16(new_pg, rco + 2 + row*2, pos);
/* rows after */
for (i=row+1;i<num_rows;i++) {
mdb_find_row(mdb, i, &row_start, &row_size);
pos -= row_size;
memcpy((char*)new_pg + pos, mdb->pg_buf + row_start, row_size);
_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
}
/* almost done, copy page over current */
memcpy(mdb->pg_buf, new_pg, pg_size);
g_free(new_pg);
_mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb));
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, pg_size - 160, 160);
}
/* drum roll, please */
if (!mdb_write_pg(mdb, table->cur_phys_pg)) {
fprintf(stderr, "write failed! exiting...\n");
exit(1);
}
return 0;
}
static int
mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum)
/*, guint32 pgnum, guint16 rownum)
static int
mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg)
*/
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbColumn *col;
guint32 pg, pg_row;
guint16 row;
void *new_pg;
unsigned char key_hash[256];
unsigned char iflag;
int keycol;
new_pg = mdb_new_leaf_pg(entry);
/* reinitial ipg pointers to start of page */
mdb_index_page_reset(ipg);
mdb_read_pg(mdb, ipg->pg);
/* do we support this index type yet? */
if (idx->num_keys > 1) {
fprintf(stderr,"multikey indexes not yet supported, aborting\n");
return 0;
}
keycol = idx->key_col_num[0];
col = g_ptr_array_index (table->columns, keycol - 1);
if (!col->is_fixed) {
fprintf(stderr,"variable length key columns not yet supported, aborting\n");
return 0;
}
while (mdb_index_find_next_on_page(mdb, ipg)) {
/* check for compressed indexes. */
if (ipg->len < col->col_size + 1) {
fprintf(stderr,"compressed indexes not yet supported, aborting\n");
return 0;
}
pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4);
pg = pg_row >> 8;
row = pg_row & 0xff;
iflag = mdb->pg_buf[ipg->offset];
/* turn the key hash back into a value */
mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash);
key_hash[col->col_size - 1] &= 0x7f;
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, ipg->offset, ipg->len);
buffer_dump(mdb->pg_buf, ipg->offset + 1, col->col_size);
buffer_dump(key_hash, 0, col->col_size);
}
memcpy((char*)new_pg + ipg->offset, mdb->pg_buf + ipg->offset, ipg->len);
ipg->offset += ipg->len;
ipg->len = 0;
row++;
}
/* free space left */
_mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset);
mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash);
key_hash[0] |= 0x080;
if (mdb_get_option(MDB_DEBUG_WRITE)) {
printf("key_hash\n");
buffer_dump(idx_fields[0].value, 0, col->col_size);
buffer_dump(key_hash, 0, col->col_size);
printf("--------\n");
}
((char *)new_pg)[ipg->offset] = 0x7f;
memcpy((char*)new_pg + ipg->offset + 1, key_hash, col->col_size);
pg_row = (pgnum << 8) | ((rownum-1) & 0xff);
_mdb_put_int32_msb(new_pg, ipg->offset + 5, pg_row);
ipg->idx_starts[row++] = ipg->offset + ipg->len;
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size);
}
memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size);
mdb_index_pack_bitmap(mdb, ipg);
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size);
}
g_free(new_pg);
return ipg->len;
}

@ -0,0 +1 @@
keximdb
Loading…
Cancel
Save