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
|
File diff suppressed because it is too large
Load Diff
@ -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])
|
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
|
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 @@
|
|||||||
|
SUBDIRS=libmdb
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in new issue