summaryrefslogtreecommitdiff
path: root/xbase64
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-08-02 08:43:31 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-08-02 08:43:31 +0200
commitdaf17154bf13139d9375f48525d19d6aaba08155 (patch)
treee3c08b6c49dc8a8e83f03327591310546675b43d /xbase64
Imported Upstream version 3.1.2upstream/3.1.2
Diffstat (limited to 'xbase64')
-rwxr-xr-xxbase64/Makefile.am55
-rwxr-xr-xxbase64/Makefile.in475
-rwxr-xr-xxbase64/makebcc.bat32
-rwxr-xr-xxbase64/xbase64.cpp766
-rwxr-xr-xxbase64/xbase64.h239
-rwxr-xr-xxbase64/xbcdx.cpp113
-rwxr-xr-xxbase64/xbcdx.h150
-rw-r--r--xbase64/xbconfig.h131
-rwxr-xr-xxbase64/xbconfig.in130
-rwxr-xr-xxbase64/xbdate.cpp851
-rwxr-xr-xxbase64/xbdate.h278
-rwxr-xr-xxbase64/xbdbf.cpp2671
-rwxr-xr-xxbase64/xbdbf.h533
-rwxr-xr-xxbase64/xbexp.cpp1323
-rwxr-xr-xxbase64/xbexp.h290
-rwxr-xr-xxbase64/xbexpfnc.cpp1092
-rwxr-xr-xxbase64/xbexpprc.cpp549
-rwxr-xr-xxbase64/xbfields.cpp672
-rwxr-xr-xxbase64/xbfile.cpp69
-rwxr-xr-xxbase64/xbfile.h70
-rwxr-xr-xxbase64/xbfilter.cpp231
-rwxr-xr-xxbase64/xbfilter.h75
-rwxr-xr-xxbase64/xbindex.cpp220
-rwxr-xr-xxbase64/xbindex.h137
-rwxr-xr-xxbase64/xblock.cpp580
-rwxr-xr-xxbase64/xblock.h159
-rwxr-xr-xxbase64/xbmemo.cpp1173
-rwxr-xr-xxbase64/xbmindex.h14
-rwxr-xr-xxbase64/xbndx.cpp2402
-rwxr-xr-xxbase64/xbndx.h292
-rwxr-xr-xxbase64/xbnode.cpp6
-rwxr-xr-xxbase64/xbnode.h19
-rwxr-xr-xxbase64/xbntx.cpp2604
-rwxr-xr-xxbase64/xbntx.h213
-rwxr-xr-xxbase64/xbretcod.h99
-rwxr-xr-xxbase64/xbstring.cpp1041
-rwxr-xr-xxbase64/xbstring.h145
-rwxr-xr-xxbase64/xbtypes.h99
-rwxr-xr-xxbase64/xbwincfg.h82
39 files changed, 20080 insertions, 0 deletions
diff --git a/xbase64/Makefile.am b/xbase64/Makefile.am
new file mode 100755
index 0000000..b0dff24
--- /dev/null
+++ b/xbase64/Makefile.am
@@ -0,0 +1,55 @@
+# This file is part of the xbase64 libraries
+# Copyright (C) 1998 Denis Pershin (dyp@inetlab.com)
+#
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser 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
+#
+# Contact:
+#
+# Email:
+#
+# xbase64-dev@lists.sourceforge.net
+# xbase64-users@lists.sourceforge.net
+#
+#
+
+INCLUDES = -I$(topdir)
+
+lib_LTLIBRARIES = libxbase64.la
+
+pkginclude_HEADERS = xbdbf.h xbexp.h xbndx.h xbretcod.h xbase64.h xbdate.h \
+ xbtypes.h xbstring.h xbindex.h xbntx.h xbconfig.h xbfilter.h \
+ xblock.h xbfile.h xbcdx.h xbwincfg.h xbmindex.h xbnode.h
+
+#install-data-hook:
+# (cd $(includedir); rm -f xbase64.h; ln -s xbase64/xbase64.h xbase64.h)
+
+libxbase64_la_SOURCES = xbdbf.cpp xbexp.cpp xbexpfnc.cpp xbexpprc.cpp \
+ xbfields.cpp xbmemo.cpp xbndx.cpp xbase64.cpp xbdate.cpp \
+ xbstring.cpp xbindex.cpp xbntx.cpp xbfilter.cpp xblock.cpp \
+ xbfile.cpp xbcdx.cpp xbnode.cpp
+
+EXTRA_DIST = makebcc.bat
+# makefile.g95 \
+# makebcc.bat \
+# xbase.ide
+
+
+libxbase64_la_LDFLAGS = -version-info 1:0:0
+libxbase64_la_LIBADD =
+
+MAINTAINERCLEANFILES = Makefile.in stamp-h.in
+CLEANFILES = *.obj *.BAK *.bak *.tds *.lib compout
+
diff --git a/xbase64/Makefile.in b/xbase64/Makefile.in
new file mode 100755
index 0000000..981a6b9
--- /dev/null
+++ b/xbase64/Makefile.in
@@ -0,0 +1,475 @@
+# Makefile.in generated by automake 1.6.3 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
+# This Makefile.in 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.
+
+@SET_MAKE@
+
+# This file is part of the xbase64 libraries
+# Copyright (C) 1998 Denis Pershin (dyp@inetlab.com)
+#
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser 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
+#
+# Contact:
+#
+# Email:
+#
+# xbase64-dev@lists.sourceforge.net
+# xbase64-users@lists.sourceforge.net
+#
+#
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+AMTAR = @AMTAR@
+AS = @AS@
+AWK = @AWK@
+CC = @CC@
+CXX = @CXX@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+GXXVER = @GXXVER@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+OBJDUMP = @OBJDUMP@
+PACKAGE = @PACKAGE@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RHREL = @RHREL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+XSUBDIRS = @XSUBDIRS@
+am__include = @am__include@
+am__quote = @am__quote@
+doxygen = @doxygen@
+install_sh = @install_sh@
+topdir = @topdir@
+
+INCLUDES = -I$(topdir)
+
+lib_LTLIBRARIES = libxbase64.la
+
+pkginclude_HEADERS = xbdbf.h xbexp.h xbndx.h xbretcod.h xbase64.h xbdate.h \
+ xbtypes.h xbstring.h xbindex.h xbntx.h xbconfig.h xbfilter.h \
+ xblock.h xbfile.h xbcdx.h xbwincfg.h xbmindex.h xbnode.h
+
+
+
+#install-data-hook:
+# (cd $(includedir); rm -f xbase64.h; ln -s xbase64/xbase64.h xbase64.h)
+libxbase64_la_SOURCES = xbdbf.cpp xbexp.cpp xbexpfnc.cpp xbexpprc.cpp \
+ xbfields.cpp xbmemo.cpp xbndx.cpp xbase64.cpp xbdate.cpp \
+ xbstring.cpp xbindex.cpp xbntx.cpp xbfilter.cpp xblock.cpp \
+ xbfile.cpp xbcdx.cpp xbnode.cpp
+
+
+EXTRA_DIST = makebcc.bat
+
+# makefile.g95 \
+# makebcc.bat \
+# xbase.ide
+libxbase64_la_LDFLAGS = -version-info 1:0:0
+libxbase64_la_LIBADD =
+
+MAINTAINERCLEANFILES = Makefile.in stamp-h.in
+CLEANFILES = *.obj *.BAK *.bak *.tds *.lib compout
+subdir = xbase64
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = xbconfig.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(lib_LTLIBRARIES)
+
+libxbase64_la_DEPENDENCIES =
+am_libxbase64_la_OBJECTS = xbdbf.lo xbexp.lo xbexpfnc.lo xbexpprc.lo \
+ xbfields.lo xbmemo.lo xbndx.lo xbase64.lo xbdate.lo xbstring.lo \
+ xbindex.lo xbntx.lo xbfilter.lo xblock.lo xbfile.lo xbcdx.lo \
+ xbnode.lo
+libxbase64_la_OBJECTS = $(am_libxbase64_la_OBJECTS)
+
+DEFS = @DEFS@
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/xbase64.Plo ./$(DEPDIR)/xbcdx.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbdate.Plo ./$(DEPDIR)/xbdbf.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbexp.Plo ./$(DEPDIR)/xbexpfnc.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbexpprc.Plo ./$(DEPDIR)/xbfields.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbfile.Plo ./$(DEPDIR)/xbfilter.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbindex.Plo ./$(DEPDIR)/xblock.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbmemo.Plo ./$(DEPDIR)/xbndx.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbnode.Plo ./$(DEPDIR)/xbntx.Plo \
+@AMDEP_TRUE@ ./$(DEPDIR)/xbstring.Plo
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+CXXFLAGS = @CXXFLAGS@
+DIST_SOURCES = $(libxbase64_la_SOURCES)
+HEADERS = $(pkginclude_HEADERS)
+
+DIST_COMMON = $(pkginclude_HEADERS) Makefile.am Makefile.in xbconfig.in
+SOURCES = $(libxbase64_la_SOURCES)
+
+all: xbconfig.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu xbase64/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+
+xbconfig.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/xbconfig.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status xbase64/xbconfig.h
+
+$(srcdir)/xbconfig.in: $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOHEADER)
+ touch $(srcdir)/xbconfig.in
+
+distclean-hdr:
+ -rm -f xbconfig.h stamp-h1
+libLTLIBRARIES_INSTALL = $(INSTALL)
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \
+ $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test -z "$dir" && dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libxbase64.la: $(libxbase64_la_OBJECTS) $(libxbase64_la_DEPENDENCIES)
+ $(CXXLINK) -rpath $(libdir) $(libxbase64_la_LDFLAGS) $(libxbase64_la_OBJECTS) $(libxbase64_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT) core *.core
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbase64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbcdx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbdate.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbdbf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbexp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbexpfnc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbexpprc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbfields.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbfile.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbfilter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbindex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xblock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbmemo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbndx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbnode.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbntx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xbstring.Plo@am__quote@
+
+distclean-depend:
+ -rm -rf ./$(DEPDIR)
+
+.cpp.o:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CXXCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+
+.cpp.obj:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(CXXCOMPILE) -c -o $@ `cygpath -w $<`
+
+.cpp.lo:
+@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@
+@AMDEP_TRUE@ $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+ $(LTCXXCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$<
+CXXDEPMODE = @CXXDEPMODE@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+pkgincludeHEADERS_INSTALL = $(INSTALL_HEADER)
+install-pkgincludeHEADERS: $(pkginclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(pkgincludedir)
+ @list='$(pkginclude_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " $(pkgincludeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(pkgincludedir)/$$f"; \
+ $(pkgincludeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(pkgincludedir)/$$f; \
+ done
+
+uninstall-pkgincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkginclude_HEADERS)'; for p in $$list; do \
+ f="`echo $$p | sed -e 's|^.*/||'`"; \
+ echo " rm -f $(DESTDIR)$(pkgincludedir)/$$f"; \
+ rm -f $(DESTDIR)$(pkgincludedir)/$$f; \
+ done
+
+ETAGS = etags
+ETAGSFLAGS =
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+
+TAGS: $(HEADERS) $(SOURCES) xbconfig.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) xbconfig.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$tags$$unique" \
+ || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ @list='$(DISTFILES)'; for file in $$list; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS) xbconfig.h
+
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(pkgincludedir)
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-compile distclean-depend \
+ distclean-generic distclean-hdr distclean-libtool \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkgincludeHEADERS
+
+install-exec-am: install-libLTLIBRARIES
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES \
+ uninstall-pkgincludeHEADERS
+
+.PHONY: GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool distclean distclean-compile \
+ distclean-depend distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am info \
+ info-am install install-am install-data install-data-am \
+ install-exec install-exec-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pkgincludeHEADERS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ tags uninstall uninstall-am uninstall-info-am \
+ uninstall-libLTLIBRARIES uninstall-pkgincludeHEADERS
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/xbase64/makebcc.bat b/xbase64/makebcc.bat
new file mode 100755
index 0000000..efdad66
--- /dev/null
+++ b/xbase64/makebcc.bat
@@ -0,0 +1,32 @@
+
+rem 2/14/04
+rem This batch file builds the xbase64 library using Borland C++ 5.5
+rem use -v for source level debugging
+
+del *.bak
+del *.obj
+
+bcc32 -c -I.. -Id:\borland\bcc55\include xbdate.cpp > compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xblock.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbdbf.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbexp.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbexpfnc.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbexpprc.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbfields.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbindex.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbase64.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbmemo.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbstring.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbfilter.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbndx.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbntx.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbcdx.cpp >> compout
+bcc32 -c -I.. -Id:\borland\bcc55\include xbfile.cpp >> compout
+
+
+del xbase64.lib
+tlib xbase64.lib /C +xbdbf.obj +xbexp.obj +xbexpfnc.obj >> compout
+tlib xbase64.lib /C +xbndx.obj +xbntx.obj +xbexpprc.obj >> compout
+tlib xbase64.lib /C +xbfields.obj +xbfile.obj +xbcdx.obj >> compout
+tlib xbase64.lib /C +xbindex.obj +xbfilter.obj +xbase64.obj >> compout
+tlib xbase64.lib /C +xbmemo.obj +xbdate.obj +xbstring.obj >> compout
diff --git a/xbase64/xbase64.cpp b/xbase64/xbase64.cpp
new file mode 100755
index 0000000..fc5613d
--- /dev/null
+++ b/xbase64/xbase64.cpp
@@ -0,0 +1,766 @@
+/* xbase64.cpp
+
+ Xbase64 project source code
+
+ This file contains logic for the basic Xbase class.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbase64.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+#include <ctype.h>
+#include <string.h>
+
+//#include <xbase64/xbexcept.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_IO_H // windows locking
+#include <io.h>
+#endif
+
+#ifdef HAVE_DOS_H // windows _sleep
+#include <dos.h>
+#endif
+
+
+/*! \file xbase64.cpp
+*/
+
+/*************************************************************************/
+//! Constructor.
+/*!
+*/
+xbXBase::xbXBase()
+{
+ xbShort e = 1;
+ EndianType = *(char *) &e;
+ if( EndianType )
+ EndianType = 'L';
+ else
+ EndianType = 'B';
+ DbfList = NULL;
+ FreeDbfList = NULL;
+
+#ifdef XB_LOCKING_ON
+ LockRetryCount = 5;
+ LockMode = XB_SINGLE_USER_MODE;
+#endif
+
+ DefaultDateFormat = "MM/DD/YY";
+}
+/*************************************************************************/
+//! Get pointer to named dbf.
+/*!
+ Looks up an open DBF file by Name.
+
+ \param Name
+ \returns A pointer to the xbDbf class instance if found or NULL if
+ not found.
+*/
+xbDbf *xbXBase::GetDbfPtr(const char *Name) {
+ xbDbList *t;
+
+ t = DbfList;
+ xbShort len = strlen(Name);
+
+ /* check for -> embedded in the name */
+ for( xbShort i = 0; i < (len-1); i++ )
+ if( Name[i] == '-' && Name[i+1] == '>' )
+ len = i-1;
+
+ while (t) {
+ if (strncmp(Name, t->DbfName, len) == 0 )
+ return t->dbf;
+ t = t->NextDbf;
+ }
+ return NULL;
+}
+
+/*************************************************************************/
+//! Destructor.
+/*!
+*/
+xbXBase::~xbXBase()
+{
+ xbDbList *i = FreeDbfList;
+ while (i) {
+ xbDbList *t = i->NextDbf;
+ if (i->DbfName) {
+ free(i->DbfName);
+ }
+ free(i);
+ i = t;
+ }
+}
+/*************************************************************************/
+//! Add dbf to dbf list.
+/*!
+ Adds an xbDbf class instance to the list of dbf's.
+
+ \param d the xbDbf instance to be added
+ \param DatabaseName name of the database
+
+ \returns One of the following return codes:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_NO_MEMORY</td><td>Out of memory</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No Error \\ \hline
+ XB\_NO\_MEMORY & Out of memory \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbXBase::AddDbfToDbfList(xbDbf *d, const char *DatabaseName) {
+ xbDbList *i, *s, *t;
+
+ if(!FreeDbfList) {
+ if((i = (xbDbList *)malloc(sizeof(xbDbList))) == NULL) {
+ return XB_NO_MEMORY;
+ }
+ } else {
+ i = FreeDbfList;
+ FreeDbfList = i->NextDbf;
+ }
+ memset(i, 0x00, sizeof(xbDbList));
+
+ i->DbfName = strdup(DatabaseName);
+ i->dbf = d;
+
+ /* insert new dbf into the list of open dbf files, sorted by dbf name */
+ s = NULL;
+ t = DbfList;
+ while(t && strcmp(t->DbfName, DatabaseName) < 0) {
+ s = t;
+ t = t->NextDbf;
+ }
+ i->NextDbf = t;
+ if (s == NULL)
+ DbfList = i;
+ else
+ s->NextDbf = i;
+
+ return 0;
+}
+/***********************************************************************/
+//! Remove dbf from dbf list.
+/*!
+ Removes the specified xbDbf class instance from the list of dbf's.
+
+ \param d xbDbf to be removed
+
+ \returns One of the following return codes:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No Error \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbXBase::RemoveDbfFromDbfList(xbDbf *d) {
+ xbDbList *i, *s;
+
+ i = DbfList;
+ s = NULL;
+
+ while (i) {
+ if(i->dbf == d) {
+ /* remove it from current chain */
+ if(s)
+ s->NextDbf = i->NextDbf;
+ else
+ DbfList = i->NextDbf;
+
+ /* add i to the current free chain */
+ i->NextDbf = FreeDbfList;
+ FreeDbfList = i;
+ free(FreeDbfList->DbfName);
+ FreeDbfList->DbfName = NULL;
+ break;
+ } else {
+ s = i;
+ i = i->NextDbf;
+ }
+ }
+ return XB_NO_ERROR;
+}
+
+// FIXME: byte reverse methods are awful, compared to bitwise shifts -- willy
+
+/************************************************************************/
+//! Get a portable short value.
+/*!
+ Converts a short (16 bit integer) value stored at p from a portable
+ format to the machine format.
+
+ \param p pointer to memory containing the portable short value
+
+ \returns the short value.
+*/
+/* This routine returns a short value from a 2 byte character stream */
+xbShort xbXBase::GetShort(const char *p) {
+ xbShort s, i;
+ const char *sp;
+ char *tp;
+
+ s = 0;
+ tp = (char *) &s;
+ sp = p;
+ if( EndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return s;
+}
+/*************************************************************************/
+//! Get a portable long value.
+/*!
+ Converts a long (32 bit integer) value stored at p from a portable
+ format to the machine format.
+
+ \param p pointer to memory containing the portable long value
+
+ \returns the long value.
+*/
+/* This routine returns a long value from a 4 byte character stream */
+xbLong xbXBase::GetLong( const char *p )
+{
+ xbLong l;
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = (char *) &l;
+ sp = p;
+ if( EndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *sp--;
+ }
+ return l;
+}
+/*************************************************************************/
+//! Get a portable unsigned long value.
+/*!
+ Converts an unsigned long (32 bit integer) value stored at p from a portable
+ format to the machine format.
+
+ \param p pointer to memory containing the portable unsigned long value
+
+ \returns the unsigned long value.
+*/
+/* This routine returns a long value from a 4 byte character stream */
+xbULong xbXBase::GetULong( const char *p )
+{
+ xbULong l;
+ char *tp;
+ xbShort i;
+
+ tp = (char *) &l;
+ if( EndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *p++;
+ else{
+ p+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *p--;
+ }
+ return l;
+}
+
+/************************************************************************/
+//! Get a high byte first short value.
+/*!
+ Converts a short (16 bit integer) value stored at p from a high byte first
+ format to the machine format.
+
+ \param p pointer to memory containing the high byte first short value
+
+ \returns the short value.
+*/
+/* This routine returns a short value from a 2 byte character stream */
+xbShort xbXBase::GetHBFShort(const char *p) {
+ xbShort s, i;
+ const char *sp;
+ char *tp;
+
+ s = 0;
+ tp = (char *) &s;
+ sp = p;
+ if( EndianType == 'B' )
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return s;
+}
+
+/*************************************************************************/
+//! Get a high byte first unsigned long value.
+/*!
+ Converts an unsigned long (32 bit integer) value stored at p from a high byte first
+ format to the machine format.
+
+ \param p pointer to memory containing the high byte first unsigned long value
+
+ \returns the unsigned long value.
+*/
+/* This routine returns a long value from a 4 byte character stream */
+xbULong xbXBase::GetHBFULong( const char *p )
+{
+ xbULong l;
+ char *tp;
+ xbShort i;
+
+ tp = (char *) &l;
+ if( EndianType == 'B' )
+ for( i = 0; i < 4; i++ ) *tp++ = *p++;
+ else{
+ p+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *p--;
+ }
+ return l;
+}
+/*************************************************************************/
+//! Get a portable double value.
+/*!
+ Converts a double (64 bit floating point) value stored at p from a portable
+ format to the machine format.
+
+ \param p pointer to memory containing the portable double value
+
+ \returns the double value.
+*/
+/* This routine returns a double value from an 8 byte character stream */
+xbDouble xbXBase::GetDouble( const char *p )
+{
+ xbDouble d;
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = (char *) &d;
+ sp = p;
+ if( EndianType == 'L' )
+ for( i = 0; i < 8; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=7;
+ for( i = 0; i < 8; i++ ) *tp++ = *sp--;
+ }
+
+ return d;
+}
+/*************************************************************************/
+//! Put a portable short value.
+/*!
+ Converts a short (16 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ \param c pointer to memory to hold converted value
+ \param s value to be converted
+*/
+/* This routine puts a short value to a 2 byte character stream */
+void xbXBase::PutShort( char * c, xbShort s )
+{
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = c;
+ sp = (const char *) &s;
+
+ if( EndianType == 'L' )
+ {
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ }
+ else /* big endian */
+ {
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+
+/*************************************************************************/
+//! Put a portable long value.
+/*!
+ Converts a long (32 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ \param c pointer to memory to hold converted value
+ \param l value to be converted
+*/
+/* This routine puts a long value to a 4 byte character stream */
+void xbXBase::PutLong( char * c, xbLong l )
+{
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = c;
+ sp = (const char *) &l;
+ if( EndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+/*************************************************************************/
+//! Put a portable unsigned short value.
+/*!
+ Converts an unsigned long (16 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ \param c pointer to memory to hold converted value
+ \param s value to be converted
+*/
+/* This routine puts a short value to a 2 byte character stream */
+void xbXBase::PutUShort( char * c, xbUShort s )
+{
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = c;
+ sp = (const char *) &s;
+ if( EndianType == 'L' )
+ for( i = 0; i < 2; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp++;
+ for( i = 0; i < 2; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+/*************************************************************************/
+//! Put a portable unsigned long value.
+/*!
+ Converts an unsigned long (32 bit integer) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ \param c pointer to memory to hold converted value
+ \param l value to be converted
+*/
+/* This routine puts a long value to a 4 byte character stream */
+void xbXBase::PutULong( char * c, xbULong l )
+{
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = c;
+ sp = (const char *) &l;
+ if( EndianType == 'L' )
+ for( i = 0; i < 4; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=3;
+ for( i = 0; i < 4; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+/*************************************************************************/
+//! Put a portable double value.
+/*!
+ Converts a double (64 floating point) value from machine format to a
+ portable format and stores the converted value in the memory referenced
+ by c.
+
+ \param c pointer to memory to hold converted value
+ \param d value to be converted
+*/
+/* This routine puts a double value to an 8 byte character stream */
+void xbXBase::PutDouble( char * c, xbDouble d )
+{
+ const char *sp;
+ char *tp;
+ xbShort i;
+
+ tp = c;
+ sp = (const char *) &d;
+ if( EndianType == 'L' )
+ for( i = 0; i < 8; i++ ) *tp++ = *sp++;
+ else
+ {
+ sp+=7;
+ for( i = 0; i < 8; i++ ) *tp++ = *sp--;
+ }
+ return;
+}
+/************************************************************************/
+//! Get offset of last PATH_SEPARATOR in Name.
+/*!
+ Scans the specified Name for the last occurance of PATH_SEPARATOR.
+
+ \param Name string to be scanned.
+
+ \returns offset of last occurance of PATH_SEPARATOR
+*/
+xbShort xbXBase::DirectoryExistsInName( const char * Name )
+{
+ /* returns the offset in the string of the last directory slash */
+
+ xbShort Count, Mark;
+ char Delim;
+ const char *p;
+
+ Delim = PATH_SEPARATOR;
+
+ Count = Mark = 0;
+ p = Name;
+
+ while( *p )
+ {
+ Count++;
+ if( *p++ == Delim ) Mark = Count;
+ }
+ return Mark;
+}
+
+/************************************************************************/
+//! Display description of error code.
+/*!
+ Displays a text description of an XBase error code.
+
+ \param ErrorCode error to be displayed
+*/
+void xbXBase::DisplayError( xbShort ErrorCode ) const
+{
+ std::cout << GetErrorMessage( ErrorCode ) << std::endl;
+}
+/************************************************************************/
+//! Get description of error code.
+/*!
+ Returns a pointer to string containing a text description of an
+ error code.
+
+ \param ErrorCode error number of description to be returned
+*/
+const char* xbXBase::GetErrorMessage( xbShort ErrorCode )
+{
+ switch( ErrorCode ) {
+ case 0: return "No Error";
+ case -100: return "End Of File";
+ case -101: return "Beginning Of File";
+ case -102: return "No Memory";
+ case -103: return "File Already Exists";
+ case -104: return "Database or Index Open Error";
+ case -105: return "Error writing to disk drive";
+ case -106: return "Unknown Field Type";
+ case -107: return "Database already open";
+ case -108: return "Not an Xbase type database";
+ case -109: return "Invalid Record Number";
+ case -110: return "Invalid Option";
+ case -111: return "Database not open";
+ case -112: return "Disk Drive Seek Error";
+ case -113: return "Disk Drive Read Error";
+ case -114: return "Search Key Not Found";
+ case -115: return "Search Key Found";
+ case -116: return "Invalid Key";
+ case -117: return "Invalid Node Link";
+ case -118: return "Key Not Unique";
+ case -119: return "Invalid Key Expression";
+ case -120: return "DBF File Not Open";
+ case -121: return "Invalid Key Type";
+ case -122: return "Invalid Node No";
+ case -123: return "Node Full";
+ case -124: return "Invalid Field Number";
+ case -125: return "Invalid Data";
+ case -126: return "Not a leaf node";
+ case -127: return "Lock Failed";
+ case -128: return "Close Error";
+ case -129: return "Invalid Schema";
+ case -130: return "Invalid Name";
+ case -131: return "Invalid Block Size";
+ case -132: return "Invalid Block Number";
+ case -133: return "Not a Memo field";
+ case -134: return "No Memo Data";
+ case -135: return "Expression syntax error";
+ case -136: return "Parse Error";
+ case -137: return "No Data";
+ case -138: return "Unknown Token Type";
+ case -140: return "Invalid Field";
+ case -141: return "Insufficient Parms";
+ case -142: return "Too Many Parms";
+ case -143: return "Invalid or Undefined Function";
+ case -144: return "Invalid Field Length";
+ case -145: return "Harvest Node";
+ case -146: return "Invalid Date";
+ case -147: return "Invalid Lock Option";
+ default: return "Unknown error code";
+ }
+}
+/************************************************************************/
+#ifdef XB_LOCKING_ON
+
+//! File lock routine
+/*!
+ Lowest level lock routine
+ Locks/unlocks a database,memo or index file.
+ This function assumes the file position has been correctly set
+
+ \param fn file to lock/unlock
+ \param LockType lock type, one of: XB_LOCK or XB_UNLOCK
+ \param lockLen byte count to lock
+*/
+
+#ifdef __WIN32__
+xbShort xbXBase::LockFile( int fn, xbShort LockType, xbOffT lockLen)
+{
+
+ int mode;
+ int rc;
+ int tries = 0;
+
+ /* convert the xbase locking command into a windows locking command */
+ if( LockType == XB_UNLOCK )
+ mode = LK_UNLCK;
+ else if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD )
+ mode = LK_NBLCK;
+ else
+ return XB_INVALID_LOCK_OPTION;
+
+ do{
+ rc = locking( fn, mode, lockLen );
+ if( rc )
+ _sleep( 1 );
+ } while( rc == -1 && tries++ < GetLockRetryCount());
+
+ if( rc )
+ return XB_LOCK_FAILED;
+
+ return 0;
+}
+
+#elif HAVE_FCNTL_H
+
+xbShort xbXBase::LockFile( int fn, xbShort LockType, xbOffT lockLen )
+{
+ xbShort cmd, rc;
+ xbShort tries = 0;
+
+/* convert cross platform xbase lock type to unix lock type */
+ if( LockType == XB_UNLOCK )
+ cmd = F_ULOCK;
+ else if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD )
+ cmd = F_TLOCK;
+ else
+ return XB_INVALID_LOCK_OPTION;
+
+/* do the actual lock */
+ do{
+ #ifdef _LARGEFILE64_SOURCE
+ rc = lockf64( fn, cmd, lockLen );
+ #else
+ rc = lockf( fn, cmd, lockLen );
+ #endif
+ if( rc == -1 && errno != EINTR ){
+ tries++;
+ sleep(1);
+ }
+ } while( rc == -1 && tries < GetLockRetryCount());
+
+ if( rc )
+ return XB_LOCK_FAILED;
+
+ return XB_NO_ERROR;
+}
+#endif // HAVE_FCNTL
+#endif // XB_LOCKING_ON
+
+/************************************************************************/
+#ifdef XB_LOCKING_ON
+
+//! Set high level lock mode
+/*!
+
+ \param nlm New lock mode
+*/
+
+xbShort xbXBase::SetLockMode( xbShort nlm )
+{
+ if( nlm != XB_SINGLE_USER_MODE && nlm != XB_XBASE_LOCK_MODE &&
+ nlm != XB_DBASE5_LOCK_MODE && nlm != XB_CLIPPER5_LOCK_MODE &&
+ nlm != XB_FOXPRO3_LOCK_MODE )
+ return XB_INVALID_LOCK_OPTION;
+
+ LockMode = nlm;
+ return XB_NO_ERROR;
+}
+
+#endif // XB_LOCKING_ON
+
+
+
diff --git a/xbase64/xbase64.h b/xbase64/xbase64.h
new file mode 100755
index 0000000..7267299
--- /dev/null
+++ b/xbase64/xbase64.h
@@ -0,0 +1,239 @@
+/* xbase64.h
+
+ Xbase project source code
+
+ This file contains a header file for the xbXBase class, which is the
+ base class for using the Xbase DBMS library.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_XBASE_H__
+#define __XB_XBASE_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <string.h>
+
+#if defined(__WIN32__)
+#include "windows.h"
+
+// ripped from wxWindows
+
+// _declspec works in BC++ 5 and later, as well as VC++
+#if defined(__VISUALC__) || defined(__BORLANDC__) || defined(__GNU LesserC__)
+# ifdef XBMAKINGDLL
+# define XBDLLEXPORT __declspec( dllexport )
+# define XBDLLEXPORT_DATA(type) __declspec( dllexport ) type
+# define XBDLLEXPORT_CTORFN
+# elif defined(XBUSINGDLL)
+# define XBDLLEXPORT __declspec( dllimport )
+# define XBDLLEXPORT_DATA(type) __declspec( dllimport ) type
+# define XBDLLEXPORT_CTORFN
+# else
+# define XBDLLEXPORT
+# define XBDLLEXPORT_DATA(type) type
+# define XBDLLEXPORT_CTORFN
+# endif
+
+#else
+
+# define XBDLLEXPORT
+# define XBDLLEXPORT_DATA(type) type
+# define XBDLLEXPORT_CTORFN
+#endif
+
+#else // !Windows
+# define XBDLLEXPORT
+# define XBDLLEXPORT_DATA(type) type
+# define XBDLLEXPORT_CTORFN
+#endif // Win/!Win
+
+
+#define XB_SINGLE_USER_MODE 0
+#define XB_UNLOCK 200
+#define XB_LOCK 201
+#define XB_LOCK_HOLD 202
+
+#ifdef XB_LOCKING_ON
+
+ #ifdef HAVE_SYS_LOCKING_H
+ #include <sys/locking.h>
+ #ifdef __MINGW32__
+ #defibe locking _locking
+ #endif
+ #endif
+
+ #ifdef HAVE_FCNTL_H
+ #include <fcntl.h>
+ #endif
+
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+
+ #define XB_XBASE_LOCK_MODE 200
+ #define XB_DBASE5_LOCK_MODE 201
+ #define XB_CLIPPER5_LOCK_MODE 202
+ #define XB_FOXPRO3_LOCK_MODE 203
+
+#endif // XB_LOCKING_ON
+
+#include "xbtypes.h"
+#include "xbretcod.h"
+#include "xbdate.h"
+#include "xbstring.h"
+
+#ifndef XB_MIN
+#define XB_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* XB_MIN */
+
+// 3/18/04 next macro isn't currently used in the library - GK
+//#ifndef XB_MAX
+//#define XB_MAX(a, b) (((a) < (b)) ? (b) : (a))
+//#endif /* XB_MAX */
+
+/*! \file xbase64.h
+*/
+
+class XBDLLEXPORT xbDbf;
+
+//! xbDbList struct
+/*!
+*/
+struct XBDLLEXPORT xbDbList{
+ xbDbList * NextDbf;
+ char * DbfName;
+ xbDbf * dbf;
+};
+
+//! xbXBase class
+/*!
+*/
+class XBDLLEXPORT xbXBase {
+ public:
+ ~xbXBase();
+ xbXBase();
+ xbShort AddDbfToDbfList(xbDbf *d, const char *DatabaseName);
+ xbDbf * GetDbfPtr( const char *Name );
+ xbShort DirectoryExistsInName( const char *Name );
+ xbShort GetEndianType() { return EndianType; }
+ void DisplayError( xbShort ErrorCode ) const;
+ static const char* GetErrorMessage( xbShort ErrorCode );
+ xbString & GetDefaultDateFormat() { return DefaultDateFormat; }
+ void SetDefaultDateFormat( const xbString & f ){ DefaultDateFormat = f; }
+
+ /* next 6 routines handle both big endian and little endian machines */
+ xbDouble GetDouble( const char *p );
+ xbLong GetLong ( const char *p );
+ xbULong GetULong ( const char *p );
+ xbShort GetShort ( const char *p );
+ xbULong GetHBFULong( const char *p );
+ xbShort GetHBFShort ( const char *p );
+
+ void PutLong ( char *p, const xbLong l );
+ void PutShort ( char *p, const xbShort s );
+ void PutULong ( char *p, const xbULong l );
+ void PutUShort( char *p, const xbUShort s );
+ void PutDouble( char *p, const xbDouble d );
+
+ xbShort RemoveDbfFromDbfList( xbDbf * );
+
+#ifdef XB_LOCKING_ON
+ xbShort GetLockRetryCount(){ return LockRetryCount; }
+ void SetLockRetryCount( xbShort lrc ) { LockRetryCount = lrc; }
+ xbShort LockFile( int fn, xbShort type, xbOffT len );
+ xbShort GetLockMode() { return LockMode; }
+ xbShort SetLockMode( xbShort nlm );
+#endif
+
+protected:
+ xbDbList * DbfList;
+ xbDbList * FreeDbfList;
+ xbShort EndianType; /* B = Big Endian, L = Little Endian */
+
+private:
+ xbString DefaultDateFormat;
+
+#ifdef XB_LOCKING_ON
+ xbShort LockRetryCount;
+ xbShort LockMode;
+#endif
+};
+
+#include "xbdbf.h"
+
+#if defined(XB_EXPRESSIONS)
+#include "xbexp.h"
+#endif
+
+#if defined(XB_INDEX_ANY)
+#include "xbindex.h"
+#include "xbmindex.h"
+#endif
+
+#ifdef XB_LOCKING_ON
+#include "xblock.h"
+#endif
+
+#ifdef XB_INDEX_NDX
+#include "xbndx.h"
+#endif
+
+#ifdef XB_INDEX_NTX
+#include "xbntx.h"
+#endif
+
+#ifdef XB_INDEX_CDX
+#include "xbcdx.h"
+#endif
+
+#if defined(XB_FILTERS) && !defined(XB_INDEX_ANY)
+#error XB_FILTERS cant be used without index support
+#elif defined(XB_FILTERS)
+#include "xbfilter.h"
+#endif
+
+#endif // __XB_XBASE_H__
+
+
+
diff --git a/xbase64/xbcdx.cpp b/xbase64/xbcdx.cpp
new file mode 100755
index 0000000..83a69df
--- /dev/null
+++ b/xbase64/xbcdx.cpp
@@ -0,0 +1,113 @@
+#include "xbtypes.h"
+#include "xbcdx.h"
+
+xbShort xbCdx::CreateIndex(const char* filename, const char *expr,
+ xbShort unique, xbShort overwrite)
+{
+ return CreateIndex(filename, "NoName", expr, unique, overwrite);
+}
+
+xbShort xbCdx::CreateIndex(const char* filename, const char *tagName,
+ const char *expr, xbShort unique, xbShort overwrite)
+{
+ if (IsOpen()) CloseIndex();
+ SetFileName(filename);
+
+ indexfp=fopen(GetFileName(), "wb+");
+ WriteTagHeader(tagName);
+ WriteTagRoot(tagName);
+ WriteIndexHeader(expr);
+ WriteIndexRoot();
+ return 0;
+}
+
+const char* xbCdx::GetExtWithDot(bool lower)
+{
+ return lower? ".cdx": ".CDX";
+}
+
+void xbCdx::WriteTagHeader(const char* tagName)
+{
+ memset(&tagHeader_, 0, sizeof(tagHeader_));
+ tagHeader_.rootNode=0x400;
+ tagHeader_.keyLen=strlen(tagName)+1;
+ tagHeader_.features.feature=0xe0;
+ tagHeader_.signature=1;
+ tagHeader_.totalExprLen=1;
+ tagHeader_.forExprLen=1;
+ tagHeader_.keyExprLen=1;
+ fwrite(&tagHeader_, sizeof(tagHeader_), 1, indexfp);
+}
+
+void xbCdx::WriteTagRoot(const char* tagName)
+{
+ memset(&tagRootNode_, 0, sizeof(tagRootNode_));
+ tagRootNode_.attr=3;
+ tagRootNode_.keyCount=1;
+ tagRootNode_.leftSibling=-1;
+ tagRootNode_.rightSibling=-1;
+ tagRootNode_.freeSpace=476;
+ tagRootNode_.recNumberMask=0xffff;
+ tagRootNode_.dupByteCounterMask=0xf;
+ tagRootNode_.tailByteCounterMask=0xf;
+ tagRootNode_.recBitUsing=16;
+ tagRootNode_.dupBitUsing=4;
+ tagRootNode_.tailBitUsing=4;
+ tagRootNode_.byteCount=3;
+ xbShort indexHeadOffset=0x600;
+ int len=sizeof(indexHeadOffset);
+ memcpy(tagRootNode_.keys, &indexHeadOffset, len);
+ tagRootNode_.keys[len]=16;
+ len=strlen(tagName);
+ xbString tag=tagName;
+ tag.toUpperCase();
+ memcpy(tagRootNode_.keys+sizeof(tagRootNode_.keys)-len, tag.c_str(), len);
+ fwrite(&tagRootNode_, sizeof(tagRootNode_), 1, indexfp);
+}
+
+void xbCdx::WriteIndexHeader(const char* expr)
+{
+ memset(&indexHeader_, 0, sizeof(indexHeader_));
+ indexHeader_.rootNode=0xa00;
+ indexHeader_.keyLen=33;
+ indexHeader_.features.feature=0x60;
+ indexHeader_.signature=1;
+ indexHeader_.totalExprLen=strlen(expr)+1;
+ indexHeader_.forExprLen=1;
+ indexHeader_.keyExprLen=strlen(expr)+1;
+ xbString exprn=expr;
+ exprn.toUpperCase();
+ memcpy(indexHeader_.keyforBuffer, exprn.c_str(), indexHeader_.keyExprLen);
+ fwrite(&indexHeader_, sizeof(indexHeader_), 1, indexfp);
+}
+
+void xbCdx::WriteIndexRoot()
+{
+ memset(&indexRootNode_, 0, sizeof(indexRootNode_));
+ indexRootNode_.attr=3;
+ indexRootNode_.keyCount=0;
+ indexRootNode_.leftSibling=-1;
+ indexRootNode_.rightSibling=-1;
+ indexRootNode_.freeSpace=488;
+ indexRootNode_.recNumberMask=0x0fff;
+ indexRootNode_.dupByteCounterMask=0x3f;
+ indexRootNode_.tailByteCounterMask=0x3f;
+ indexRootNode_.recBitUsing=12;
+ indexRootNode_.dupBitUsing=6;
+ indexRootNode_.tailBitUsing=6;
+ indexRootNode_.byteCount=3;
+ fwrite(&indexRootNode_, sizeof(indexRootNode_), 1, indexfp);
+}
+
+xbShort xbCdx::GetHeadNode()
+{
+ ReadTagHeader();
+ ReadIndexHeader(GetIndexTagOffset());
+ return XB_NO_ERROR;
+}
+
+void xbCdx::ReadTagHeader()
+{
+ _fseek(indexfp, 0, SEEK_SET);
+ fread(&tagHeader_, sizeof(tagHeader_), 1, indexfp);
+}
diff --git a/xbase64/xbcdx.h b/xbase64/xbcdx.h
new file mode 100755
index 0000000..f76a45f
--- /dev/null
+++ b/xbase64/xbcdx.h
@@ -0,0 +1,150 @@
+#ifndef cdx_h
+#define cdx_h
+
+#include "xbmindex.h"
+
+struct CdxHeader
+{
+ xbLong rootNode;
+ xbLong freeNode;
+ xbLong reserved;
+ xbShort keyLen;
+ union cdxFeatures
+ {
+ struct Features
+ {
+ bool unique:1;
+ int:2;
+ bool hasFor:1;
+ bool:1;
+ bool cdxHeader:1;
+ bool cdxFmt:1;
+ bool cdxTagHeader:1;
+ } features;
+ char feature;
+ } features;
+ char signature;
+ xbLong reserved1[5];
+ char reserved2[466];
+ xbShort descending;
+ xbShort totalExprLen;
+ xbShort forExprLen;
+ xbShort reserved4;
+ xbShort keyExprLen;
+ char keyforBuffer[512];
+};
+
+struct CdxNode
+{
+ xbShort attr;
+ xbShort keyCount;
+ xbLong leftSibling;
+ xbLong rightSibling;
+}
+#ifdef __GNU LesserC__
+ __attribute__((packed))
+#endif
+;
+
+struct CdxInnerNode: public CdxNode
+{
+ char keys[500];
+}
+#ifdef __GNU LesserC__
+ __attribute__((packed))
+#endif
+;
+
+struct CdxLeafNode: public CdxNode
+{
+ xbShort freeSpace;
+ xbLong recNumberMask;
+ char dupByteCounterMask;
+ char tailByteCounterMask;
+ char recBitUsing;
+ char dupBitUsing;
+ char tailBitUsing;
+ char byteCount;
+ char keys[488];
+}
+#ifdef __GNU LesserC__
+ __attribute__((packed))
+#endif
+;
+
+class XBDLLEXPORT xbCdx: public xbMultiIndex
+{
+ public:
+// xbCdx() {} I don't like to make empty object with no protection
+// to method method call. And I don't see any need of it.
+ xbCdx(xbDbf* dbf): xbMultiIndex(dbf)
+ {
+ memset(&indexHeader_, 0, sizeof(indexHeader_));
+ memset(&tagHeader_, 0, sizeof(tagHeader_));
+ }
+
+ virtual ~xbCdx() {CloseIndex();}
+
+ virtual xbShort CreateIndex(const char *filename, const char *expr,
+ xbShort unique, xbShort overwrite);
+ virtual xbShort CreateIndex(const char *filename, const char* tagname,
+ const char *expr, xbShort unique, xbShort overwrite);
+
+ virtual xbShort AddTag(const char* tagname, const char *expr,
+ xbShort unique, xbShort overwrite) {return 0;}
+
+ virtual xbLong GetTotalNodes() {return 0;}
+ virtual xbULong GetCurDbfRec() {return 0;}
+ virtual xbShort CreateKey( xbShort, xbShort ) {return 0;}
+ virtual xbShort GetCurrentKey(char *key) {return 0;}
+ virtual xbShort AddKey( xbLong ) {return 0;}
+ virtual xbShort UniqueIndex() {return 0;}
+ virtual xbShort DeleteKey( xbLong ) {return 0;}
+ virtual xbShort KeyWasChanged() {return 0;}
+ virtual xbShort FindKey( const char * ) {return 0;}
+ virtual xbShort FindKey() {return 0;}
+ virtual xbShort FindKey( xbDouble ) {return 0;}
+ virtual xbShort GetNextKey() {return 0;}
+ virtual xbShort GetLastKey() {return 0;}
+ virtual xbShort GetFirstKey() {return 0;}
+ virtual xbShort GetPrevKey() {return 0;}
+ virtual xbShort ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0) {return 0;}
+ virtual xbShort KeyExists( xbDouble ) {return 0;}
+ virtual void GetExpression(char *buf, int len) {}
+#ifdef XBASE_DEBUG
+ virtual void DumpHdrNode( xbShort Option ) {};
+ virtual void DumpNodeRec( xbLong ) {};
+ virtual void DumpNodeChain() {};
+ virtual xbShort CheckIndexIntegrity( xbShort ) {return 0;};
+#endif
+
+// static xbString CreateIndexName(const xbString& dbfName);
+ virtual const char* GetExtWithDot(bool lower);
+ const CdxHeader& GetIndexHeader() {return indexHeader_;}
+ const CdxHeader& GetTagHeader() {return tagHeader_;}
+
+ protected:
+ virtual xbShort GetHeadNode();
+ virtual xbUShort GetKeyLen() {return 0;}
+ virtual const char* GetKeyExpression() {return "";}
+ virtual void FreeNodesMemory() {}
+ void ReadTagHeader();
+ xbLong GetIndexTagOffset() {return 0;}
+ void ReadIndexHeader(xbLong) {}
+
+ private:
+ xbCdx(const xbCdx&);
+ xbCdx& operator=(const xbCdx&);
+ void WriteTagHeader(const char* tagName);
+ void WriteTagRoot(const char* tagName);
+ void WriteIndexHeader(const char* expr);
+ void WriteIndexRoot();
+
+ private:
+ CdxHeader tagHeader_;
+ CdxLeafNode tagRootNode_;
+ CdxHeader indexHeader_;
+ CdxLeafNode indexRootNode_;
+};
+
+#endif
diff --git a/xbase64/xbconfig.h b/xbase64/xbconfig.h
new file mode 100644
index 0000000..15a3e69
--- /dev/null
+++ b/xbase64/xbconfig.h
@@ -0,0 +1,131 @@
+/* xbase64/xbconfig.h. Generated by configure. */
+/* xbase64/xbconfig.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fseeko' function. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `ftello' function. */
+#define HAVE_FTELLO 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define to 1 if you have the `vsprintf' function. */
+#define HAVE_VSPRINTF 1
+
+/* Name of package */
+#define PACKAGE "xbase64"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "xdb-devel@lists.sourceforge.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "xbase64"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "xbase64 3.1.2"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "xbase64"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "3.1.2"
+
+#define PATH_SEPARATOR '/'
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "3.1.2"
+
+/* XB_DEBUG */
+#define XBASE_DEBUG 1
+
+#define XB_DBT_BLOCK_SIZE 512
+
+/* XB_EXPRESSIONS */
+#define XB_EXPRESSIONS 1
+
+/* XB_FILTERS */
+#define XB_FILTERS 1
+
+/* XB_INDEX_ANY */
+#define XB_INDEX_ANY 1
+
+/* XB_INDEX_NDX */
+#define XB_INDEX_NDX 1
+
+/* XB_INDEX_NTX */
+#define XB_INDEX_NTX 1
+
+/* XB_LARGEFILE_SUPPORT */
+#define XB_LARGEFILE_SUPPORT 1
+
+/* XB_LOCKING_ON */
+#define XB_LOCKING_ON 1
+
+/* XB_MEMO_FIELDS */
+#define XB_MEMO_FIELDS 1
+
+/* XB_REAL_DELETE */
+#define XB_REAL_DELETE 1
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
diff --git a/xbase64/xbconfig.in b/xbase64/xbconfig.in
new file mode 100755
index 0000000..8c25663
--- /dev/null
+++ b/xbase64/xbconfig.in
@@ -0,0 +1,130 @@
+/* xbase64/xbconfig.in. Generated from configure.in by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fseeko' function. */
+#undef HAVE_FSEEKO
+
+/* Define to 1 if you have the `ftello' function. */
+#undef HAVE_FTELLO
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* 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 `strcasecmp' function. */
+#undef HAVE_STRCASECMP
+
+/* 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 to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_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 to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to 1 if you have the `vsprintf' function. */
+#undef HAVE_VSPRINTF
+
+/* 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
+
+#define PATH_SEPARATOR '/'
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* XB_DEBUG */
+#undef XBASE_DEBUG
+
+#define XB_DBT_BLOCK_SIZE 512
+
+/* XB_EXPRESSIONS */
+#undef XB_EXPRESSIONS
+
+/* XB_FILTERS */
+#undef XB_FILTERS
+
+/* XB_INDEX_ANY */
+#undef XB_INDEX_ANY
+
+/* XB_INDEX_NDX */
+#undef XB_INDEX_NDX
+
+/* XB_INDEX_NTX */
+#undef XB_INDEX_NTX
+
+/* XB_LARGEFILE_SUPPORT */
+#undef XB_LARGEFILE_SUPPORT
+
+/* XB_LOCKING_ON */
+#undef XB_LOCKING_ON
+
+/* XB_MEMO_FIELDS */
+#undef XB_MEMO_FIELDS
+
+/* XB_REAL_DELETE */
+#undef XB_REAL_DELETE
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
diff --git a/xbase64/xbdate.cpp b/xbase64/xbdate.cpp
new file mode 100755
index 0000000..fd26438
--- /dev/null
+++ b/xbase64/xbdate.cpp
@@ -0,0 +1,851 @@
+/* xbdate.cpp
+
+ Xbase64 project source code
+
+ These functions are used for processing dates.
+ All functions assume a standard date format of CCYYMMDD
+ for Century,Year,Month and Day
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbdate.h"
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+#include <xbase64/xbase64.h>
+#include <xbase64/xbdate.h>
+//#include <xbase64/retcodes.h>
+
+/*! \file xbdate.cpp
+*/
+
+int xbDate::DaysInMonths[2][13];
+int xbDate::AggregatedDaysInMonths[2][13];
+
+#define EPOCH_MIN 100
+#define EPOCH_MAX 3000
+#define DAYS_AD(year) ((year) *365L + (year) / 4 - (year) / 100 + (year) / 400)
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbDate::xbDate( const xbString & Date8 ) {
+ if( DateIsValid( Date8 ))
+ cDate8 = Date8;
+ else
+ Sysdate();
+ SetDateTables();
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbDate::xbDate( const char * Date8 ) {
+ if( DateIsValid( Date8 ))
+ cDate8 = Date8;
+ else
+ Sysdate(); /* if invalid date, set class to sysdate */
+ SetDateTables();
+}
+/***************************************************************/
+//! Short description.
+/*!
+*/
+xbDate::xbDate()
+{
+ Sysdate();
+ SetDateTables();
+}
+
+/***************************************************************/
+//! Destructor
+/*!
+*/
+xbDate::~xbDate()
+{
+}
+/***************************************************************/
+//! Short description.
+/*!
+*/
+void xbDate::SetDateTables() {
+ if( AggregatedDaysInMonths[1][12] != 366 ){ /* first time called ? */
+ AggregatedDaysInMonths[0][0] = 0;
+ AggregatedDaysInMonths[0][1] = 31;
+ AggregatedDaysInMonths[0][2] = 59;
+ AggregatedDaysInMonths[0][3] = 90;
+ AggregatedDaysInMonths[0][4] = 120;
+ AggregatedDaysInMonths[0][5] = 151;
+ AggregatedDaysInMonths[0][6] = 181;
+ AggregatedDaysInMonths[0][7] = 212;
+ AggregatedDaysInMonths[0][8] = 243;
+ AggregatedDaysInMonths[0][9] = 273;
+ AggregatedDaysInMonths[0][10] = 304;
+ AggregatedDaysInMonths[0][11] = 334;
+ AggregatedDaysInMonths[0][12] = 365;
+ AggregatedDaysInMonths[1][0] = 0;
+ AggregatedDaysInMonths[1][1] = 31;
+ AggregatedDaysInMonths[1][2] = 60;
+ AggregatedDaysInMonths[1][3] = 91;
+ AggregatedDaysInMonths[1][4] = 121;
+ AggregatedDaysInMonths[1][5] = 152;
+ AggregatedDaysInMonths[1][6] = 182;
+ AggregatedDaysInMonths[1][7] = 213;
+ AggregatedDaysInMonths[1][8] = 244;
+ AggregatedDaysInMonths[1][9] = 274;
+ AggregatedDaysInMonths[1][10] = 305;
+ AggregatedDaysInMonths[1][11] = 335;
+ AggregatedDaysInMonths[1][12] = 366;
+
+ DaysInMonths[0][0] = 0;
+ DaysInMonths[0][1] = 31;
+ DaysInMonths[0][2] = 28;
+ DaysInMonths[0][3] = 31;
+ DaysInMonths[0][4] = 30;
+ DaysInMonths[0][5] = 31;
+ DaysInMonths[0][6] = 30;
+ DaysInMonths[0][7] = 31;
+ DaysInMonths[0][8] = 31;
+ DaysInMonths[0][9] = 30;
+ DaysInMonths[0][10] = 31;
+ DaysInMonths[0][11] = 30;
+ DaysInMonths[0][12] = 31;
+ DaysInMonths[1][0] = 0;
+ DaysInMonths[1][1] = 31;
+ DaysInMonths[1][2] = 29;
+ DaysInMonths[1][3] = 31;
+ DaysInMonths[1][4] = 30;
+ DaysInMonths[1][5] = 31;
+ DaysInMonths[1][6] = 30;
+ DaysInMonths[1][7] = 31;
+ DaysInMonths[1][8] = 31;
+ DaysInMonths[1][9] = 30;
+ DaysInMonths[1][10] = 31;
+ DaysInMonths[1][11] = 30;
+ DaysInMonths[1][12] = 31;
+ }
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this function returns century and year from a CCYYMMDD date */
+int xbDate::CenturyOf( const char * Date8 ) const
+{
+ char Century[3];
+ Century[0] = Date8[0];
+ Century[1] = Date8[1];
+ Century[2] = 0x00;
+ return( atoi( Century ));
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this function returns century and year from a CCYYMMDD date */
+int xbDate::YearOf( const char * Date8 ) const
+{
+ char year[5];
+ year[0] = Date8[0];
+ year[1] = Date8[1];
+ year[2] = Date8[2];
+ year[3] = Date8[3];
+ year[4] = 0x00;
+ return( atoi( year ));
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this function returns the month from a CCYYMMDD date */
+int xbDate::MonthOf( const char * Date8 ) const
+{
+ char month[3];
+ month[0] = Date8[4];
+ month[1] = Date8[5];
+ month[2] = 0x00;
+ return( atoi( month ));
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this function returns TRUE if a CCYYMMDD date is a leap year*/
+
+int xbDate::IsLeapYear( const char * Date8 ) const
+{
+ int year;
+ year = YearOf( Date8 );
+ if(( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
+ return 1;
+ else
+ return 0;
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param CalcYear
+*/
+/* this function returns TRUE if a CCYYMMDD date is a leap year*/
+
+int xbDate::CalcRollingCenturyForYear( int CalcYear ) const
+{
+ /* this routine calculates a century for a year - it uses
+ an 80/20 rolling date window to calculate the century */
+
+ xbDate d;
+ int ThisYear = YearOf( d.Sysdate() );
+ int ThisCentury = CenturyOf( d.Sysdate() );
+
+ ThisYear -= (ThisCentury * 100);
+
+ if( ThisYear < 80 && CalcYear < (ThisYear+20) )
+ return ThisCentury;
+
+ else if( ThisYear >= 80 &&
+ CalcYear < ThisYear &&
+ CalcYear >= (ThisYear-80))
+ return ThisCentury;
+
+ else
+ return ThisCentury - 1;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Format
+ \param Date8
+*/
+/* this function returns the "day of" from a CCYYMMDD date */
+
+/* format = XB_FMT_WEEK Number of day in WEEK 0-6 ( Sun - Sat )
+ format = XB_FMT_MONTH Number of day in MONTH 1-31
+ format = XB_FMT_YEAR Number of day in YEAR 1-366
+*/
+
+int xbDate::DayOf( int Format, const char * Date8 ) const
+{
+ char day[3];
+ int iday, imonth, iyear, iday2;
+
+ /* check for valid format switch */
+
+ if( Format!=XB_FMT_WEEK && Format!=XB_FMT_MONTH && Format!=XB_FMT_YEAR )
+ return XB_INVALID_OPTION;
+
+ if( Format == XB_FMT_WEEK )
+ {
+ iday = DayOf( XB_FMT_MONTH, Date8 );
+ imonth = MonthOf( Date8 );
+ iyear = YearOf ( Date8 );
+
+ /* The following formula uses Zeller's Congruence to determine
+ the day of the week */
+
+ if( imonth > 2 ) /* init to February */
+ imonth -= 2;
+ else
+ {
+ imonth += 10;
+ iyear--;
+ }
+
+ iday2 = ((13 * imonth - 1) / 5) +iday + ( iyear % 100 ) +
+ (( iyear % 100 ) / 4) + ((iyear /100 ) / 4 ) - 2 *
+ ( iyear / 100 ) + 77 ;
+
+ return( iday2 - 7 * ( iday2 / 7 ));
+ }
+
+ else if( Format == XB_FMT_MONTH )
+ {
+ day[0] = Date8[6];
+ day[1] = Date8[7];
+ day[2] = 0x00;
+ return( atoi( day ));
+ }
+ else
+ return(
+ AggregatedDaysInMonths[IsLeapYear(Date8)][MonthOf(Date8)-1]+
+ DayOf(XB_FMT_MONTH, Date8));
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+/* this method sets the class date & returns the system date */
+
+xbString& xbDate::Sysdate()
+{
+ char dt[9];
+ time_t timer;
+ struct tm *tblock;
+ timer = time( NULL );
+ tblock = localtime( &timer );
+ tblock->tm_year += 1900;
+ tblock->tm_mon++;
+ sprintf( dt,"%4d%02d%02d",tblock->tm_year,tblock->tm_mon,tblock->tm_mday );
+ dt[8] = 0x00;
+ cDate8 = dt;
+ return cDate8;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this function checks a date for validity - returns 1 if OK */
+
+int xbDate::DateIsValid( const char * Date8 ) const
+{
+ int year, month, day;
+
+ if(!isdigit( Date8[0] ) || !isdigit( Date8[1] ) || !isdigit( Date8[2] ) ||
+ !isdigit( Date8[3] ) || !isdigit( Date8[4] ) || !isdigit( Date8[5] ) ||
+ !isdigit( Date8[6] ) || !isdigit( Date8[7] ) )
+ return 0;
+
+ year = YearOf ( Date8 );
+ month = MonthOf( Date8 );
+ day = DayOf ( XB_FMT_MONTH, Date8 );
+
+ /* check the basics */
+ if( year == 0 || month < 1 || month > 12 || day < 1 || day > 31 )
+ return 0;
+
+ /* April, June, September and November have 30 days */
+ if(( month==4 || month==6 || month==9 || month==11 )&& day > 30 )
+ return 0;
+
+ /* check for February with leap year */
+ if( month == 2 )
+ if( IsLeapYear( Date8 ))
+ {
+ if( day > 29 )
+ return 0;
+ }
+ else
+ {
+ if( day > 28 )
+ return 0;
+ }
+ return 1;
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+int xbDate::SetDate( const char * Date8 )
+{
+ if( DateIsValid( Date8 ))
+ {
+ cDate8 = Date8;
+ return 1;
+ }
+ return 0;
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this returns the number of days since 1/1/EPOCH_MIN */
+long xbDate::JulianDays( const char * Date8 ) const
+{
+ int year = YearOf( Date8 );
+ if(( year < EPOCH_MIN ) || (year >= EPOCH_MAX))
+ return XB_INVALID_DATE;
+
+ long days = 0;
+ for (long y = EPOCH_MIN; y < year; y++ )
+ days += 365 + ( ( ( y%4==0 && y%100!=0 ) || y%400==0 ) ? 1 : 0 );
+
+ days += (long) DayOf( XB_FMT_YEAR, Date8 ) -1;
+
+ return days;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param days
+*/
+/* this function does the opposite of the JulianDays function */
+/* it converts a julian based date into a Date8 format */
+
+xbString& xbDate::JulToDate8( long days )
+{
+ char Date8[9];
+ int year, leap, month;
+
+ year = EPOCH_MIN;
+ leap = 0; /* EPOCH_MIN of 100 is not a leap year */
+
+/* this while loop calculates the year of the date by incrementing
+ the years counter as it decrements the days counter */
+
+ while( days > ( 364+leap ))
+ {
+ days -= 365+leap;
+ year++;
+ if(( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
+ leap = 1;
+ else
+ leap = 0;
+ }
+
+/* this for loop calculates the month and day of the date by
+ comparing the number of days remaining to one of the tables */
+
+ for( month = 12; month >= 1; month-- )
+ if( days >= (long)AggregatedDaysInMonths[leap][month] ) {
+ days -= AggregatedDaysInMonths[leap][month];
+ break;
+ }
+
+ sprintf( Date8, "%4d%02d%02ld", year, month+1, days+1 );
+
+ Date8[8] = 0x00;
+ cDate8 = Date8;
+ return cDate8;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this routine returns a pointer to the day of the week(Sun-Sat)*/
+xbString& xbDate::CharDayOf( const char * Date8 )
+{
+ struct tm tblock;
+ char buf[25];
+
+ tblock.tm_year = YearOf( Date8 ) - 1900;
+ tblock.tm_mon = MonthOf( Date8 ) - 1;
+ tblock.tm_mday = DayOf( XB_FMT_MONTH, Date8 );
+ tblock.tm_hour = 0;
+ tblock.tm_min = 0;
+ tblock.tm_sec = 1;
+ tblock.tm_isdst = -1;
+ if( mktime( &tblock ) == -1 )
+ fDate = "????";
+ else
+ {
+ strftime( buf, 25, "%A", &tblock );
+ fDate = buf;
+ }
+ return fDate;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this routine returns a pointer to the month */
+
+xbString& xbDate::CharMonthOf( const char * Date8 )
+{
+ struct tm tblock;
+ char buf[25];
+
+ tblock.tm_year = YearOf( Date8 ) - 1900;
+ tblock.tm_mon = MonthOf( Date8 ) - 1;
+ tblock.tm_mday = DayOf( XB_FMT_MONTH, Date8 );
+ tblock.tm_hour = 0;
+ tblock.tm_min = 0;
+ tblock.tm_sec = 1;
+ tblock.tm_isdst = -1;
+ if( mktime( &tblock ) == -1 )
+ fDate = "????";
+ else
+ {
+ strftime( buf, 25, "%B", &tblock );
+ fDate = buf;
+ }
+ return fDate;
+}
+
+/***************************************************************/
+//! Short description.
+/*!
+ \param indate in the format of MM/DD/YY
+*/
+/* This function formats a date and returns a pointer to a */
+/* static buffer containing the date */
+
+xbString& xbDate::FormatCTODdate( const char * indate )
+{
+ xbDate d;
+ char cbuf[3];
+ char odate[9];
+ fDate = "";
+ if( indate[0] == ' ' || indate[1] == ' ' ) // empty date
+ return fDate;
+
+ sprintf( cbuf, "%02d",
+ d.CalcRollingCenturyForYear( atoi( indate+6 )));
+ odate[0] = cbuf[0];
+ odate[1] = cbuf[1];
+ odate[2] = indate[6];
+ odate[3] = indate[7];
+ odate[4] = indate[0];
+ odate[5] = indate[1];
+ odate[6] = indate[3];
+ odate[7] = indate[4];
+ odate[8] = 0x00;
+
+ fDate = odate;
+ return fDate;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Format
+ \param Date8
+*/
+/* This function formats a date and returns a pointer to a */
+/* static buffer containing the date */
+
+xbString& xbDate::FormatDate( const char * Format, const char * Date8 )
+{
+ const char *FmtPtr; /* format pointer */
+ char *BufPtr; /* buffer pointer */
+ char type;
+ char cbuf[10];
+ int type_ctr, i;
+ char buf[50];
+ xbString s;
+
+ memset( buf, 0x00, 50 );
+ if( strstr( Format, "YYDDD" ))
+ {
+ buf[0] = Date8[2];
+ buf[1] = Date8[3];
+ sprintf( buf+2, "%03d", DayOf( XB_FMT_YEAR, Date8 ));
+ }
+ else
+ {
+ BufPtr = buf;
+ FmtPtr = Format;
+ memset( cbuf, 0x00, 10 );
+ while( *FmtPtr )
+ {
+ if( *FmtPtr != 'D' && *FmtPtr != 'M' && *FmtPtr != 'Y' )
+ {
+ *BufPtr = *FmtPtr;
+ BufPtr++;
+ FmtPtr++;
+ }
+ else
+ {
+ type = *FmtPtr;
+ type_ctr = 0;
+ while( *FmtPtr == type )
+ {
+ type_ctr++;
+ FmtPtr++;
+ }
+ switch( type )
+ {
+ case 'D':
+ if( type_ctr == 1 )
+ {
+ sprintf( cbuf, "%d", DayOf( XB_FMT_MONTH, Date8 ));
+ strcat( buf, cbuf );
+ BufPtr += strlen( cbuf );
+ }
+ else if( type_ctr == 2 )
+ {
+ cbuf[0] = Date8[6];
+ cbuf[1] = Date8[7];
+ cbuf[2] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += 2;
+ }
+ else
+ {
+ s = CharDayOf( Date8 );
+ if( type_ctr == 3 )
+ {
+ strncat( buf, s.getData(), 3 );
+ BufPtr += 3;
+ }
+ else
+ {
+ strcpy( cbuf, CharDayOf( Date8 ));
+ for( i = 0; i < 9; i++ )
+ if( cbuf[i] == 0x20 ) cbuf[i] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += strlen( cbuf );
+ }
+ }
+ break;
+
+ case 'M':
+ if( type_ctr == 1 )
+ {
+ sprintf( cbuf, "%d", MonthOf( Date8 ));
+ strcat( buf, cbuf );
+ BufPtr += strlen( cbuf );
+ }
+ else if( type_ctr == 2 )
+ {
+ cbuf[0] = Date8[4];
+ cbuf[1] = Date8[5];
+ cbuf[2] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += 2;
+ }
+ else
+ {
+ s = CharMonthOf( Date8 );
+ if( type_ctr == 3 )
+ {
+ strncat( buf, s.getData(), 3 );
+ BufPtr += 3;
+ }
+ else
+ {
+ strcpy( cbuf, CharMonthOf( Date8 ));
+ for( i = 0; i < 9; i++ )
+ if( cbuf[i] == 0x20 ) cbuf[i] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += strlen( cbuf );
+ }
+ }
+ break;
+
+ case 'Y':
+ if( type_ctr == 2 )
+ {
+ cbuf[0] = Date8[2];
+ cbuf[1] = Date8[3];
+ cbuf[2] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += 2;
+ }
+ else if( type_ctr == 4 )
+ {
+ cbuf[0] = Date8[0];
+ cbuf[1] = Date8[1];
+ cbuf[2] = Date8[2];
+ cbuf[3] = Date8[3];
+ cbuf[4] = 0x00;
+ strcat( buf, cbuf );
+ BufPtr += 4;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ fDate = buf;
+ return fDate;
+}
+/***************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+/* this routine returns the Date8 format of the last day of the
+ month for the given input Date8 */
+
+xbString & xbDate::LastDayOfMonth( const char * Date8 )
+{
+ char tmp[9];
+ sprintf( tmp, "%4.4d%2.2d%2.2d",
+ YearOf( Date8 ), MonthOf( Date8 ),
+ DaysInMonths[IsLeapYear(Date8)][MonthOf(Date8)]);
+ cDate8 = tmp;
+ return cDate8;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator+=( int count )
+{
+ JulToDate8( JulianDays() + count );
+ return cDate8;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator-=( int count )
+{
+ JulToDate8( JulianDays() - count );
+ return cDate8;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator++( int )
+{
+ *this+=1;
+ return cDate8;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator--( int )
+{
+ *this-=1;
+ return cDate8;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator+( int count )
+{
+ xbDate d( GetDate() );
+ d+=count;
+ fDate = d.GetDate();
+ return fDate;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+xbString &xbDate::operator-( int count )
+{
+ xbDate d( GetDate() );
+ d-=count;
+ fDate = d.GetDate();
+ return fDate;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+long xbDate::operator-( const xbDate & d ) const
+{
+ return JulianDays() - d.JulianDays();
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator==( const xbDate & d ) const
+{
+ if( JulianDays() == d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator!=( const xbDate & d ) const
+{
+ if( JulianDays() != d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator<( const xbDate & d ) const
+{
+ if( JulianDays() < d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator>( const xbDate & d ) const
+{
+ if( JulianDays() > d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator<=( const xbDate & d ) const
+{
+ if( JulianDays() <= d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
+//! Short description.
+/*!
+*/
+int xbDate::operator>=( const xbDate & d ) const
+{
+ if( JulianDays() >= d.JulianDays() )
+ return 1;
+ else
+ return 0;
+}
+/**********************************************************************/
diff --git a/xbase64/xbdate.h b/xbase64/xbdate.h
new file mode 100755
index 0000000..617fe50
--- /dev/null
+++ b/xbase64/xbdate.h
@@ -0,0 +1,278 @@
+/* xbdate.h
+
+ Xbase64 project source code
+
+ This file contains a header file for the xbDate object, which is
+ used for handling dates.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+/*! \file xbdate.h
+*/
+
+#ifndef __XB_XBDATE_H__
+#define __XB_XBDATE_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbstring.h>
+
+#define XB_FMT_WEEK 1
+#define XB_FMT_MONTH 2
+#define XB_FMT_YEAR 3
+
+//! xbDate class
+/*!
+*/
+
+class XBDLLEXPORT xbDate {
+ public:
+ xbDate();
+ xbDate( const char * Date8 );
+ xbDate( const xbString &Date8 );
+ virtual ~xbDate();
+
+ //! Short description.
+ /*!
+ */
+ const xbString & GetDate() const
+ { return cDate8; };
+ //! Short description.
+ /*!
+ */
+ xbString & GetDate()
+ { return cDate8; };
+ //! Short description.
+ /*!
+ */
+ const xbString & GetFormattedDate() const
+ { return fDate; };
+ //! Short description.
+ /*!
+ */
+ xbString & GetFormattedDate()
+ { return fDate; };
+
+ int SetDate( const char * Date8 );
+ //! Short description.
+ /*!
+ */
+ int SetDate( const xbString & Date8 )
+ { return SetDate((const char *) Date8 ); };
+
+ long JulianDays ( const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ long JulianDays ( const xbString & Date8 ) const
+ { return JulianDays((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ long JulianDays () const
+ { return JulianDays((const char *) cDate8 ); };
+
+ int YearOf ( const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ int YearOf ( const xbString & Date8 ) const
+ { return YearOf((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ int YearOf () const
+ { return YearOf((const char *) cDate8 ); };
+
+ //! Short description.
+ /*!
+ */
+ int CenturyOf ( const char *Date8 ) const;
+
+ int MonthOf ( const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ int MonthOf ( const xbString &Date8 ) const
+ { return MonthOf((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ int MonthOf () const
+ { return MonthOf(( const char *) cDate8 ); };
+
+ int DayOf ( int Format, const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ int DayOf ( int Format, const xbString &Date8 ) const
+ { return DayOf( Format, (const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ int DayOf ( int Format ) const
+ { return DayOf( Format, (const char *) cDate8 ); };
+
+ int IsLeapYear ( const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ int IsLeapYear ( const xbString &Date8 ) const
+ { return IsLeapYear((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ int IsLeapYear () const
+ { return IsLeapYear((const char *) cDate8 ); };
+
+ //! Short description.
+ /*!
+ */
+ int CalcRollingCenturyForYear( int ) const;
+
+
+ int DateIsValid ( const char *Date8 ) const;
+ //! Short description.
+ /*!
+ */
+ int DateIsValid ( const xbString & Date8 ) const
+ { return DateIsValid( (const char *) Date8 ); };
+
+ xbString& LastDayOfMonth( const char *Date8 );
+ //! Short description.
+ /*!
+ */
+ xbString& LastDayOfMonth( const xbString & Date8 )
+ { return LastDayOfMonth((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& LastDayOfMonth()
+ { return LastDayOfMonth((const char *) cDate8 ); };
+
+ xbString& Sysdate ();
+ xbString& JulToDate8( long );
+
+ //! Short description.
+ /*!
+ */
+ xbString& FormatCTODdate( const char * indate );
+
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const char *Format, const char *Date8 );
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const xbString &Format, const char *Date8 )
+ { return FormatDate((const char *) Format, Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const char *Format, const xbString &Date8 )
+ { return FormatDate( Format, (const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const xbString &Format, const xbString &Date8 )
+ { return FormatDate((const char *) Format,(const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const char *Format )
+ { return FormatDate( (const char *) Format, (const char *) cDate8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& FormatDate( const xbString &Format )
+ { return FormatDate((const char *) Format, (const char *) cDate8 ); };
+
+ xbString& CharDayOf ( const char *Date8 );
+ //! Short description.
+ /*!
+ */
+ xbString& CharDayOf ( const xbString &Date8 )
+ { return CharDayOf((const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& CharDayOf ()
+ { return CharDayOf((const char *) cDate8 ); };
+
+ xbString& CharMonthOf ( const char *Date8 );
+ //! Short description.
+ /*!
+ */
+ xbString& CharMonthOf ( const xbString &Date8 )
+ { return CharMonthOf(( const char *) Date8 ); };
+ //! Short description.
+ /*!
+ */
+ xbString& CharMonthOf ()
+ { return CharMonthOf(( const char *) cDate8 ); };
+
+ xbString &operator+=( int );
+ xbString &operator-=( int );
+ xbString &operator++( int ); /* post increment */
+ xbString &operator--( int ); /* post increment */
+ xbString &operator+ ( int );
+ xbString &operator- ( int );
+ long operator-( const xbDate & ) const;
+ int operator==( const xbDate & ) const;
+ int operator!=( const xbDate & ) const;
+ int operator< ( const xbDate & ) const;
+ int operator> ( const xbDate & ) const;
+ int operator<=( const xbDate & ) const;
+ int operator>=( const xbDate & ) const;
+
+ protected:
+ void SetDateTables();
+ xbString cDate8; /* CCYYMMDD date format */
+ xbString fDate; /* other date format */
+ static int AggregatedDaysInMonths[2][13];
+ static int DaysInMonths[2][13];
+};
+
+#endif // __XB_XBDATE_H__
+
diff --git a/xbase64/xbdbf.cpp b/xbase64/xbdbf.cpp
new file mode 100755
index 0000000..d3790cb
--- /dev/null
+++ b/xbase64/xbdbf.cpp
@@ -0,0 +1,2671 @@
+/* xbdbf.cpp
+
+ Xbase64 project source code
+
+ This file contains the basic Xbase routines for reading and writing
+ Xbase .DBF files.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbdbf.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#include <errno.h>
+
+/*! \file xbdbf.cpp
+*/
+
+/************************************************************************/
+//! Constructor
+/*!
+ \param x pointer to the global xbXbase class
+*/
+xbDbf::xbDbf( xbXBase * x )
+{
+ xbase = x;
+ InitVars();
+}
+/************************************************************************/
+//! Destructor
+/*!
+*/
+xbDbf::~xbDbf()
+{
+ CloseDatabase(true);
+}
+/************************************************************************/
+//! Initialize private data members.
+/*!
+ Internal use only.
+*/
+void xbDbf::InitVars()
+{
+ SetFileName(NULL);
+ NoOfFields = 0;
+ DbfStatus = XB_CLOSED;
+ fp = NULL;
+ CurRec = 0L;
+ SchemaPtr = NULL;
+ RecBuf = NULL;
+ RecBuf2 = NULL;
+ Version = 0x00;
+ UpdateYY = 0x00;
+ UpdateMM = 0x00;
+ UpdateDD = 0x00;
+ NoOfRecs = 0L;
+ HeaderLen = 0x00;
+ RecordLen = 0x00;
+ NdxList = NULL;
+ FreeIxList = NULL;
+ XFV = 3; /* Xbase file version */
+
+#ifdef XB_LOCKING_ON
+ xblfh = NULL; /* lock file for XB_XBASE_LOCK_MODE */
+ LockMode = xbase->GetLockMode();
+ TableLockCnt = 0;
+ IndexLockCnt = 0;
+#ifdef XB_MEMO_FIELDS
+ MemoLockCnt = 0;
+#endif
+
+ AutoLock = 1;
+ CurLockType = -1;
+ CurLockCount = 0;
+ CurLockedRecNo = 0L;
+ CurRecLockType = -1;
+ CurRecLockCount = 0;
+ CurMemoLockType = -1;
+ CurMemoLockCount = 0;
+#else
+ AutoLock = 0;
+#endif
+
+#ifdef XB_MEMO_FIELDS
+ MemofileName = "";
+ MemoHeader.BlockSize = XB_DBT_BLOCK_SIZE;
+ MemoHeader.Version = 0x03;
+ mfp = NULL;
+ mbb = NULL;
+ CurMemoBlockNo = -1;
+ mfield1 = 0;
+ MStartPos = 0;
+ MFieldLen = 0;
+ NextFreeBlock = 0L;
+ FreeBlockCnt = 0L;
+ MNextBlockNo = 0L;
+ MNoOfFreeBlocks = 0L;
+#endif
+
+//#ifdef XB_REAL_DELETE
+ RealDelete = 0;
+ FirstFreeRec = 0L;
+ RealNumRecs = 0L;
+//#endif
+}
+/************************************************************************/
+//! Set dbase version for the dbf file.
+/*!
+ Set dbase version. Should only be used before creating a database with
+ xbDbf::CreateDatabase().
+
+ \param v version, either 3 or 4.
+*/
+xbShort xbDbf::SetVersion(xbShort v) {
+ if (v == 0)
+ return XFV;
+ else
+ if(v == 3) {
+ XFV = 3;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x03;
+#endif
+ return XFV;
+ } else
+ if (v == 4) {
+ XFV = 4;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x00;
+#endif
+ return XFV;
+ }
+
+ return XB_INVALID_OPTION;
+}
+/************************************************************************/
+//! Write the dbf header
+/*!
+ Internal use only.
+
+ \param PositionOption flag that indicates whether file postition should
+ be moved. non-zero if so, zero if not.
+*/
+xbShort xbDbf::WriteHeader( xbShort PositionOption )
+{
+ char buf[32];
+ memset(buf, 0, 32);
+ if(PositionOption)
+ rewind(fp);
+
+ memcpy(&buf[0], &Version, 4);
+ xbase->PutLong(&buf[4], NoOfRecs);
+ xbase->PutShort(&buf[8], HeaderLen );
+ xbase->PutShort(&buf[10], RecordLen );
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete){
+ xbase->PutULong(&buf[12], FirstFreeRec);
+ xbase->PutULong(&buf[16], RealNumRecs);
+ }
+#endif
+ if(fwrite(buf, 32, 1, fp) != 1)
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Read the dbf header.
+/*!
+ Internal use only.
+
+ \param PositionOption
+*/
+xbShort xbDbf::ReadHeader( xbShort PositionOption )
+{
+#if 0
+ char buf[4];
+ if (PositionOption)
+ rewind(fp);
+ if (fread(&Version, 4, 1, fp) != 1)
+ xb_error(XB_READ_ERROR);
+
+ if (fread(buf, 4, 1, fp ) != 1)
+ xb_error(XB_READ_ERROR);
+
+ NoOfRecs = xbase->GetLong( buf );
+ if(fread(buf, 2, 1, fp) != 1)
+ xb_error(XB_READ_ERROR);
+
+ HeaderLen = xbase->GetShort( buf );
+ if(fread(buf, 2, 1, fp) != 1)
+ xb_error(XB_READ_ERROR);
+
+ RecordLen = xbase->GetShort(buf);
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete)
+ {
+ if (fread(buf, 4, 1, fp ) != 1)
+ xb_error(XB_READ_ERROR);
+ FirstFreeRec = xbase->GetULong( buf );
+
+ if (fread(buf, 4, 1, fp ) != 1)
+ xb_error(XB_READ_ERROR);
+ RealNumRecs = xbase->GetULong( buf );
+ }
+#endif
+#else
+ char buf[32];
+
+ if(PositionOption)
+ rewind(fp);
+
+ if(fread(buf, 32, 1, fp) != 1)
+ return XB_READ_ERROR;
+
+ memcpy(&Version, buf, 4);
+ NoOfRecs = xbase->GetLong(&buf[4]);
+ HeaderLen = xbase->GetShort(&buf[8]);
+ RecordLen = xbase->GetShort(&buf[10]);
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete)
+ {
+ FirstFreeRec = xbase->GetULong(&buf[12]);
+ RealNumRecs = xbase->GetULong(&buf[16]);
+ }
+#endif
+#endif
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Determine if file name suffix is missing
+/*!
+ Internal use only.
+*/
+xbShort xbDbf::NameSuffixMissing( xbShort type, const char * name )
+{
+ /* type 1 is DBF check
+ type 2 is NDX check
+ type 3 is MDX check
+ type 4 is NTX check
+
+ Returns 0 if suffix found
+ 1 if suffix not found, lower case
+ 2 is suffix not found, upper, case
+*/
+
+ xbShort len;
+
+ len = strlen( name );
+ if( len <= 4 )
+ if( name[len-1] >= 'A' && name[len-1] <= 'Z' )
+ return 2;
+ else
+ return 1;
+
+ if( type == 1 && name[len-4] == '.' &&
+ ( name[len-3] == 'd' || name[len-3] == 'D' ) &&
+ ( name[len-2] == 'b' || name[len-2] == 'B' ) &&
+ ( name[len-1] == 'f' || name[len-1] == 'F' )
+ )
+ return 0;
+
+ if( type == 2 && name[len-4] == '.' &&
+ ( name[len-3] == 'n' || name[len-3] == 'N' ) &&
+ ( name[len-2] == 'd' || name[len-2] == 'D' ) &&
+ ( name[len-1] == 'x' || name[len-1] == 'X' )
+ )
+ return 0;
+
+ if( type == 4 && name[len-4] == '.' &&
+ ( name[len-3] == 'n' || name[len-3] == 'N' ) &&
+ ( name[len-2] == 't' || name[len-2] == 'T' ) &&
+ ( name[len-1] == 'x' || name[len-1] == 'X' )
+ )
+ return 0;
+
+ if( name[len-5] >= 'A' && name[len-5] <= 'Z' )
+ return 2;
+ else
+ return 1;
+}
+/************************************************************************/
+//! Create the dbf file.
+/*!
+ This method attempts to create the DBF file with the specified
+ name (TableName) and schema (xbSchema s). The OverLay switch is used to determine
+ if an existing file should be overwritten or an error flagged if the
+ file already exists. The record buffer is blanked (set to spaces).
+
+ \param TableName name of the table
+ \param s xbSchema
+ \param Overlay One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>OverLay</th><th>Description</th></tr>
+ <tr><td>XB_OVERLAY</td><td>Overwrite existing file if it exists</td></tr>
+ <tr><td>XB_DONTOVERLAY</td><td>Report an error if file exists</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{OverLay} & \textbf{Description} \\ \hline \hline
+ XB\_OVERLAY & Overwrite existing file if it exists \\ \hline
+ XB\_DONTOVERLAY & Report an error if file exists \\ \hline
+ \end{tabular}
+ \endlatexonly
+ \returns One of the following return codes:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_FILE_EXISTS</td><td>If the file exists and OverLay is XB_DONTOVERLAY</td></tr>
+ <tr><td>XB_OPEN_ERROR</td><td>Couldn't open the file</td></tr> <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Couldn't write to disk</td><tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No Error \\ \hline
+ XB\_FILE\_EXISTS & If the file exists and OverLay is XB\_DONTOVERAY \\ \hline
+ XB\_OPEN\_ERROR & Couldn't open the file \\ \hline
+ XB\_WRITE\_ERROR & Couldn't write to disk \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::CreateDatabase( const char * TableName, xbSchema * s,
+ const xbShort Overlay )
+{
+ xbShort i, j, k, k2, rc; /* , count; */
+
+#ifdef XB_MEMO_FIELDS
+ xbShort MemoSw = 0;
+#endif
+
+ DbfStatus = XB_CLOSED;
+ SetFileName( TableName );
+
+ /* check if the file already exists */
+ if((( fp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ){
+ fclose( fp );
+ return XB_FILE_EXISTS;
+ }
+ else if( fp ) fclose( fp );
+
+ if(( fp = fopen( GetFileName(), "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+#ifdef XB_LOCKING_ON
+ /* no buffering in multi user mode */
+ setbuf( fp, NULL );
+
+ /* open the lock file if XB_XBASE_LOCK_FLAVOR */
+// if( LockMode == XB_XBASE_LOCK_MODE )
+// if(( rc = OpenXbLockFile()) != XB_NO_ERROR )
+// return rc;
+#endif
+
+ /* count the number of fields and check paramaters */
+ i = 0;
+ while( s[i].Type != 0 ){
+ NoOfFields++;
+
+#ifdef XB_MEMO_FIELDS
+ if(s[i].Type == 'M'){
+ s[i].FieldLen = 10;
+ s[i].NoOfDecs = 0;
+ }
+#endif /* XB_MEMO_FIELDS */
+
+ if(s[i].Type == 'D'){
+ s[i].FieldLen = 8;
+ s[i].NoOfDecs = 0;
+ }
+
+ if(s[i].Type == 'C')
+ s[i].NoOfDecs = 0;
+
+ RecordLen += s[i].FieldLen;
+
+ if( s[i].Type != 'C' &&
+ s[i].Type != 'N' &&
+ s[i].Type != 'F' &&
+ s[i].Type != 'D' &&
+#ifdef XB_MEMO_FIELDS
+ s[i].Type != 'M' &&
+#endif /* XB_MEMO_FIELDS */
+ s[i].Type != 'L' )
+ {
+ fclose( fp );
+ InitVars();
+ return XB_UNKNOWN_FIELD_TYPE;
+ }
+
+#ifdef XB_MEMO_FIELDS
+// 8/18/03 types B and O dont exist yet - gkunkel
+// if( !MemoSw && ( s[i].Type=='M' || s[i].Type=='B' || s[i].Type=='O'))
+ if( !MemoSw && ( s[i].Type=='M' ))
+ MemoSw++;
+#endif
+
+// check for numeric fields which are too long
+ if((s[i].Type == 'N' || s[i].Type == 'F') && s[i].FieldLen > 19 ){
+ fclose( fp );
+ InitVars();
+ return XB_INVALID_FIELD_LEN;
+ }
+ i++;
+ }
+ RecordLen++; /* add one byte for 0x0D */
+
+ if(( RecBuf = (char *) malloc( RecordLen )) == NULL ){
+ fclose( fp );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+
+ if(( RecBuf2 = (char *) malloc( RecordLen )) == NULL ){
+ free( RecBuf );
+ fclose( fp );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+
+ /* BlankRecord(); */
+ memset( RecBuf, 0x20, RecordLen );
+ memset( RecBuf2, 0x20, RecordLen );
+
+ /* set class variables */
+ Version = XFV & 0x7; // file version - bit 0-2
+#ifdef XB_MEMO_FIELDS
+ if(MemoSw){
+ if((XFV & 0x7) == 3)
+ Version |= 0x80; // memo presence - bit 7
+ else
+ Version = (char) 0x8b;
+ }
+#endif
+
+ CurRec = 0L;
+ HeaderLen = 33 + NoOfFields * 32;
+ xbDate d;
+ UpdateYY = (d.YearOf() - 1900);
+ if((XFV & 0x7) == 3)
+ UpdateYY %= 100; // dBASE III seems to do this, but IV does not. DTB
+
+ UpdateMM = d.MonthOf();
+ UpdateDD = d.DayOf( XB_FMT_MONTH );
+
+ /* write the header prolog */
+ if(( rc = WriteHeader( 0 )) != XB_NO_ERROR ){
+ free( RecBuf );
+ free( RecBuf2 );
+ fclose( fp );
+ InitVars();
+ return XB_WRITE_ERROR;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc(NoOfFields*sizeof(xbSchemaRec)))==NULL){
+ free( RecBuf );
+ free( RecBuf2 );
+ fclose( fp );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+ memset( SchemaPtr, 0x00, ( NoOfFields * sizeof(xbSchemaRec)));
+
+ /* write the field information into the header */
+ for( i = 0, k = 1; i < NoOfFields; i++ ){
+ memset( SchemaPtr[i].FieldName, 0x00, 11 );
+ strncpy( SchemaPtr[i].FieldName, s[i].FieldName, 10 );
+
+ SchemaPtr[i].Type = s[i].Type;
+ SchemaPtr[i].FieldLen = s[i].FieldLen;
+ SchemaPtr[i].NoOfDecs = s[i].NoOfDecs;
+
+ if( SchemaPtr[i].NoOfDecs > SchemaPtr[i].FieldLen ) {
+ fclose( fp );
+ free( SchemaPtr );
+ free( RecBuf );
+ free( RecBuf2 );
+ InitVars();
+ return XB_INVALID_SCHEMA;
+ }
+
+ k2 = k;
+ k += SchemaPtr[i].FieldLen;
+
+ if(( fwrite( &SchemaPtr[i], 1, 18, fp )) != 18 ) {
+ fclose( fp );
+ free( SchemaPtr );
+ free( RecBuf );
+ free( RecBuf2 );
+ InitVars();
+ return XB_WRITE_ERROR;
+ }
+
+ for( j = 0; j < 14; j++ ) {
+ if(( fwrite( "\x00", 1, 1, fp )) != 1 ) {
+ free( SchemaPtr );
+ free( RecBuf );
+ free( RecBuf2 );
+ fclose( fp );
+ InitVars();
+ return XB_WRITE_ERROR;
+ }
+ }
+ SchemaPtr[i].Address = RecBuf + k2;
+ SchemaPtr[i].Address2 = RecBuf2 + k2;
+ }
+
+ /* write the header terminator */
+ if(( fputc( XB_CHARHDR, fp )) != XB_CHARHDR ){
+ fclose( fp );
+ free( SchemaPtr );
+ free( RecBuf );
+ free( RecBuf2 );
+ InitVars();
+ return XB_WRITE_ERROR;
+ }
+
+#ifdef XB_MEMO_FIELDS
+ if( MemoSw )
+ if((rc = CreateMemoFile()) != XB_NO_ERROR){
+ fclose(fp);
+ free(RecBuf);
+ free(RecBuf2);
+ InitVars();
+ return rc;
+ }
+#endif
+
+ DbfStatus = XB_OPEN;
+ return xbase->AddDbfToDbfList(this, GetFileName());
+}
+/************************************************************************/
+//! Close the dbf file.
+/*!
+ This method attempts to close the DBF file which was previously
+ opened with either CreateDatabase() or OpenDatabase(). Deletes any
+ memory allocated. Automatically closes any open indexes associated
+ with this data file.
+
+ \param deleteIndexes if TRUE, the indexes (xbIndex instances) will also
+ be deleted (index files will not be deleted)
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File was not open</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No Error \\ \hline
+ XB\_NOT\_OPEN\_ERROR & File was not open \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::CloseDatabase( xbBool deleteIndexes )
+{
+#if defined(XB_INDEX_ANY)
+ xbIxList *i, *ti;
+#endif
+
+ if(DbfStatus == XB_CLOSED)
+ return XB_NO_ERROR;
+
+
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while (i){
+ i->index->CloseIndex();
+ if(deleteIndexes)
+ delete i->index;
+ i = NdxList;
+ }
+/* free up unused nodes */
+ i = FreeIxList;
+ while( i ) {
+ ti = i;
+ i = i->NextIx;
+ free(ti);
+ }
+#endif
+
+ if(SchemaPtr){
+ for( int j = 0; j < NoOfFields; j++ )
+ if( SchemaPtr[j].fp ) delete SchemaPtr[j].fp;
+ free( SchemaPtr );
+ }
+ if(RecBuf)
+ free( RecBuf );
+ if(RecBuf2)
+ free( RecBuf2 );
+
+#ifdef XB_MEMO_FIELDS
+ if( mbb )
+ free( mbb ); /* memo block buffer */
+ if( mfp )
+ fclose( mfp ); /* memo file pointer */
+#endif
+
+#ifdef XB_LOCKING_ON
+ if( xblfh ){
+ fclose( xblfh );
+ xblfh = NULL;
+ }
+#endif
+
+ xbase->RemoveDbfFromDbfList( this );
+ if(fp)
+ fclose( fp );
+ InitVars();
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+/* options 1 = Print header only
+ 2 = Field data only
+ 3 = Header and Field data */
+
+//! Dump header information.
+/*!
+ \param Option One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Option</th><th>Description</th></tr>
+ <tr><td>1</td><td>Print header only</td></tr>
+ <tr><td>2</td><td>Field data only</td></tr>
+ <tr><td>3</td><td>Header and field data</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Option} & \textbf{Description} \\ \hline \hline
+ 1 & Header only \\ \hline
+ 2 & Field data only \\ \hline
+ 3 & Header and field data \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+#ifdef XBASE_DEBUG
+xbShort xbDbf::DumpHeader( xbShort Option )
+{
+ int i;
+
+ if( Option < 1 || Option > 3 )
+ return XB_INVALID_OPTION;
+
+ if( DbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+ std::cout << "\nDatabase file " << GetFileName() << std::endl << std::endl;
+
+ if( Option != 2 ){
+ std::cout << "File header data:" << std::endl;
+ if( Version == 3 )
+ std::cout << "Dbase III file" << std::endl;
+ else if ( Version == 83 )
+ std::cout << "Dbase III file with memo fields" << std::endl << std::endl;
+
+ std::cout << "Last update date = "
+ << (int) UpdateMM << "/" << (int) UpdateDD << "/" << (int) UpdateYY % 100 << std::endl;
+
+ std::cout << "Header length = " << HeaderLen << std::endl;
+ std::cout << "Record length = " << RecordLen << std::endl;
+ std::cout << "Records in file = " << NoOfRecs << std::endl << std::endl;
+#ifdef XB_REAL_DELETE
+ std::cout << "First Free Rec = " << FirstFreeRec << std::endl << std::endl;
+#endif
+ }
+ if( Option != 1 ){
+ std::cout << "Field Name Type Length Decimals" << std::endl;
+ std::cout << "---------- ---- ------ --------" << std::endl;
+ for( i = 0; i <NoOfFields; i++ ){
+ if( SchemaPtr[i].Type == 'C' && SchemaPtr[i].NoOfDecs > 0 )
+ printf( "%10s %1c %4d %4d\n", SchemaPtr[i].FieldName,
+ SchemaPtr[i].Type, SchemaPtr[i].FieldLen, 0 );
+ else
+ printf( "%10s %1c %4d %4d\n", SchemaPtr[i].FieldName,
+ SchemaPtr[i].Type, SchemaPtr[i].FieldLen, SchemaPtr[i].NoOfDecs );
+ }
+ }
+ std::cout << std::endl;
+ return XB_NO_ERROR;
+}
+#endif
+/************************************************************************/
+//! Open the DBF file.
+/*!
+ This method attempts to open the DBF file with the specified
+ name (TableName). This method does not position to any particular
+ record in the file. The record buffer is blanked (set to spaces).
+
+ \param TableName Name of table to open
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_OPEN_ERROR</td><td>Couldn't open file</td></tr>
+ <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr>
+ <tr><td>XB_NOT_XBASE</td><td>Not an DBF file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_OPEN\_ERROR & Couldn't open file \\ \hline
+ XB\_NO\_MEMORY & Memory allocation error \\ \hline
+ XB\_NOT\_XBASE & Not an DBF file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::OpenDatabase( const char * TableName )
+{
+ xbShort i, j, rc;
+ char buf[33];
+ char *p;
+
+#ifdef XB_MEMO_FIELDS
+ xbShort MemoSw = 0;
+#endif
+
+ /* verify the file is not already open */
+ if( DbfStatus != XB_CLOSED )
+ return XB_ALREADY_OPEN;
+
+ /* copy the file name to the class variable */
+ SetFileName( TableName );
+
+ /* open the file */
+ if(( fp = fopen(GetFileName(), "r+b")) == NULL ){
+ //
+ // Try to open read only if failed to open read/write
+ //
+ if(( fp = fopen(GetFileName(), "rb")) == NULL )
+ return XB_OPEN_ERROR;
+ }
+
+#ifdef XB_LOCKING_ON
+ /* no buffering in multi user mode - may not see what others have updated */
+ setbuf( fp, NULL );
+
+ /* open the lock file if XB_XBASE_LOCK_MODE */
+// if( LockMode == XB_XBASE_LOCK_MODE )
+// if(( rc = OpenXbLockFile()) != XB_NO_ERROR )
+// return rc;
+#endif
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR)
+// return rc;
+#endif
+
+ /* copy the header into memory */
+ if(( rc = ReadHeader( 1 )) != XB_NO_ERROR ){
+ InitVars();
+ return rc;
+ }
+
+ /* check the version */
+ if( Version == 3 || Version == (char)0x83 ){ /* dBASE III+ */
+ XFV = 3;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x03;
+#endif
+ }
+ else if( Version == 4 || Version == (char)0x8B ){ /* dBASE IV */
+ XFV = 4;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x00;
+#endif
+ }
+ else if( Version == (char)0xf5 ){ /* FoxPro */
+ XFV = 4;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x00;
+#endif
+ }
+ else if( Version == (char)0x30 ){ /* Visual Foxpro */
+ XFV = 4;
+#ifdef XB_MEMO_FIELDS
+ MemoHeader.Version = 0x00;
+#endif
+ } else {
+ InitVars();
+ return XB_NOT_XBASE;
+ }
+
+ // it would seem that dBASE III+ generates an UpdateYY value
+ // of 0 for 2000 and dBASE IV uses 100, so I have removed the
+ // check for UpdateYY being 0 (which might be valid). DTB
+
+ // Not all flavors of database tools use these fields
+ // Found a month set to 0 in valid dbf file
+ // Commented out this check 2/11/06 - GAK
+
+ // if( UpdateMM == 0 || UpdateDD == 0 ){
+ // InitVars();
+ // return XB_NOT_XBASE;
+ // }
+
+ /* calculate the number of fields */
+ if( Version == (char)0x30 ) {
+ NoOfFields = ( HeaderLen - 296 ) / 32 ;
+ } else {
+ NoOfFields = ( HeaderLen - 33 ) / 32;
+ }
+
+ if(( RecBuf = (char *) malloc( RecordLen )) == NULL ) {
+ fclose( fp );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+ if(( RecBuf2 = (char *) malloc( RecordLen )) == NULL ) {
+ fclose( fp );
+ free( RecBuf );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+
+ if((SchemaPtr=(xbSchemaRec *)malloc(NoOfFields*sizeof(xbSchemaRec)))==NULL){
+ free( RecBuf );
+ free( RecBuf2 );
+ fclose( fp );
+ InitVars();
+ return XB_NO_MEMORY;
+ }
+ memset( SchemaPtr, 0x00, ( NoOfFields * sizeof(xbSchemaRec)));
+
+ /* copy field info into memory */
+ for( i = 0, j = 1; i < NoOfFields; i++ ){
+ _fseek( fp,((xbOffT)i*32+32), 0 );
+ fread( &buf, 1, 32, fp );
+ p = buf;
+ strncpy( SchemaPtr[i].FieldName, p, 10 );
+ p += 11;
+ SchemaPtr[i].Type = *p++;
+
+ SchemaPtr[i].Address = RecBuf + j;
+ SchemaPtr[i].Address2 = RecBuf2 + j;
+
+ SchemaPtr[i].FieldLen = *( p + 4 );
+ SchemaPtr[i].NoOfDecs = *( p + 5 );
+
+ if( SchemaPtr[i].Type == 'C' && SchemaPtr[i].NoOfDecs > 0 ){
+ SchemaPtr[i].LongFieldLen = xbase->GetShort( p + 4 );
+ j += SchemaPtr[i].LongFieldLen;
+ }
+ else
+ j += SchemaPtr[i].FieldLen;
+#ifdef XB_MEMO_FIELDS
+ if( !MemoSw && (SchemaPtr[i].Type == 'M' ||
+ SchemaPtr[i].Type == 'B' || SchemaPtr[i].Type == 'O' ))
+ MemoSw++;
+#endif
+ }
+ CurRec = 0L;
+ BlankRecord();
+ DbfStatus = XB_OPEN;
+
+#ifdef XB_MEMO_FIELDS
+ if( MemoSw ) /* does this table have memo fields ? */
+ if(( rc = OpenMemoFile()) != XB_NO_ERROR ){
+ free( RecBuf );
+ free( RecBuf2 );
+ free( SchemaPtr );
+ fclose( fp );
+ InitVars();
+ return rc;
+ }
+#endif
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// LockDatabase( XB_UNLOCK, 0L );
+#endif /* XB_LOCKING_ON */
+
+ return xbase->AddDbfToDbfList( this, GetFileName() );
+}
+/************************************************************************/
+//! Blank the record buffer.
+/*!
+ Sets the record to spaces.
+*/
+xbShort xbDbf::BlankRecord()
+{
+ if( DbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+ if( DbfStatus != XB_UPDATED ){
+ DbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, RecordLen );
+ }
+
+ memset( RecBuf, 0x20, RecordLen );
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Append the current record to the data file
+/*!
+ This method attempts to append the contents of the current record buffer
+ to the end of the DBF file and updates the file date and number of
+ records in the file. Also updates any open indices associated with
+ this data file.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::AppendRecord()
+{
+ xbShort rc;
+ xbULong nextRecNo;
+
+#if defined(XB_INDEX_ANY)
+ xbIxList *i;
+#endif
+
+/* lock the database */
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR)
+// return rc;
+
+ rc = ReadHeader(1);
+
+// if(AutoLock)
+// LockDatabase( XB_UNLOCK, 0L );
+
+ if( rc )
+ return rc;
+
+#endif
+
+/* lock any indexes */
+#if defined(XB_INDEX_ANY)
+#ifdef XB_LOCKING_ON
+ i = NdxList;
+ while( i && AutoLock ){
+// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR )
+// return rc;
+ i = i->NextIx;
+ }
+#endif /* XB_LOCKING_ON */
+#endif
+
+// if there are no duplicates, and no records set the CurRec to the
+// last record + 1. This is for EXP::RECNO()
+
+/* check for any duplicate keys */
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while( i ){
+ if( i->index->UniqueIndex() ){
+ i->index->CreateKey( 0, 0 );
+ if( i->index->FindKey() == XB_FOUND )
+ return XB_KEY_NOT_UNIQUE;
+ }
+ i = i->NextIx;
+ }
+#endif
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete && FirstFreeRec)
+ nextRecNo = FirstFreeRec;
+ else
+ nextRecNo = NoOfRecs + 1;
+#else
+ nextRecNo = NoOfRecs + 1;
+#endif
+
+ CurRec = NoOfRecs + 1;
+
+#if defined(XB_INDEX_ANY)
+/* update the indexes */
+ i = NdxList;
+ while( i ){
+ if( !i->index->UniqueIndex() ) /* if we didn't prepare the key */
+ if(( rc = i->index->CreateKey( 0, 0 )) != XB_NO_ERROR ) /* then do it before the add */
+ return rc;
+ if(( rc = i->index->AddKey(nextRecNo)) != XB_NO_ERROR )
+ return rc;
+ i->index->TouchIndex();
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+
+#ifdef XB_REAL_DELETE
+ char buf[4];
+
+ if(RealDelete && FirstFreeRec){
+ /*
+ ** Grab the next free rec no and put it in FirstFreeRec
+ */
+ if(_fseek(fp, (HeaderLen+(((xbOffT)FirstFreeRec-1)*RecordLen)+1), 0) != 0)
+ return XB_SEEK_ERROR;
+
+ if(fread(buf, 4, 1, fp) != 1)
+ return XB_READ_ERROR;
+
+ FirstFreeRec = xbase->GetULong(buf);
+ }
+
+ /*
+ ** Okay, seek and write the record out
+ */
+ if(_fseek(fp, (HeaderLen+(((xbOffT)nextRecNo-1)*RecordLen)), 0) != 0)
+ return XB_SEEK_ERROR;
+
+ if(fwrite( RecBuf, RecordLen, 1, fp) != 1)
+ return XB_WRITE_ERROR;
+
+ /*
+ ** If we just appended the record to the file, then write the EOF char
+ */
+ if(nextRecNo == NoOfRecs + 1){
+ if( fputc( XB_CHAREOF, fp ) != XB_CHAREOF )
+ return XB_WRITE_ERROR;
+ }
+#else
+ /* write the last record */
+ if( _fseek( fp,(HeaderLen+((xbOffT)NoOfRecs*RecordLen)), 0 ) != 0 )
+ return XB_SEEK_ERROR;
+
+ if( fwrite( RecBuf, RecordLen, 1, fp ) != 1 )
+ return XB_WRITE_ERROR;
+
+ /* write the end of file marker */
+ if( fputc( XB_CHAREOF, fp ) != XB_CHAREOF )
+ return XB_WRITE_ERROR;
+#endif
+
+ /* calculate the latest header information */
+ xbDate d;
+ UpdateYY = d.YearOf() - 1900;
+ if(XFV == 3)
+ UpdateYY %= 100; // dBASE III seems to do this, IV does not. DTB
+ UpdateMM = d.MonthOf();
+ UpdateDD = d.DayOf( XB_FMT_MONTH );
+#ifndef XB_REAL_DELETE
+ NoOfRecs++;
+#else
+ if(RealDelete){
+ if(nextRecNo == NoOfRecs + 1)
+ NoOfRecs++;
+ RealNumRecs++;
+ }
+ else
+ NoOfRecs++;
+#endif
+ CurRec = nextRecNo;
+
+ /* rewrite the header record */
+ if(( rc = WriteHeader( 1 )) != XB_NO_ERROR )
+ return rc;
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// LockDatabase( XB_UNLOCK, 0L );
+
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while( i && AutoLock ){
+// i->index->LockIndex( XB_UNLOCK );
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+#endif /* XB_LOCKING_ON */
+
+ DbfStatus = XB_OPEN;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Get a record from the data file
+/*!
+ This method attempts to retrieve the record specified by RecNo from the
+ data file into the record buffer.
+
+ \param RecNo Record number to retrieve
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::GetRecord( xbULong RecNo )
+{
+ xbShort rc;
+ if( DbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// if(( rc = LockDatabase( XB_LOCK, RecNo )) != 0 ) return rc;
+
+ rc = ReadHeader(1);
+
+// if(AutoLock)
+// LockDatabase( XB_UNLOCK, RecNo );
+
+ if( rc )
+ return rc;
+
+#endif
+
+ if( RecNo > NoOfRecs || RecNo == 0L )
+ return XB_INVALID_RECORD;
+
+ if( _fseek( fp, (HeaderLen+(((xbOffT)RecNo-1L)*RecordLen)), SEEK_SET )){
+#ifdef XB_LOCKING_ON
+// LockDatabase( XB_UNLOCK, RecNo );
+#endif
+ return XB_SEEK_ERROR;
+ }
+
+ if( fread( RecBuf, RecordLen, 1, fp ) != 1 ){
+#ifdef XB_LOCKING_ON
+// LockDatabase( XB_UNLOCK, RecNo );
+#endif
+ return XB_READ_ERROR;
+ }
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// LockDatabase( XB_LOCK, RecNo );
+#endif
+
+ DbfStatus = XB_OPEN;
+ CurRec = RecNo;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Get the first physical record in the data file
+/*!
+ Attempts to retrieve the first physical record from the data file into
+ the record buffer.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::GetFirstRecord()
+{
+ xbShort rc;
+ if( NoOfRecs == 0 )
+ return XB_INVALID_RECORD;
+
+ rc = GetRecord( 1L );
+#ifdef XB_REAL_DELETE
+ if(!rc && RealDelete && RecordDeleted())
+ rc = GetNextRecord();
+#endif
+
+ return rc;
+}
+/************************************************************************/
+//! Get the last phyiscal record in the data file
+/*!
+ Attempts to retrieve the last physical record from the data file into
+ the record buffer.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_EOF</td><td>At end of file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_EOF & At end of file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::GetLastRecord()
+{
+ xbShort rc;
+ if( NoOfRecs == 0 )
+ return XB_INVALID_RECORD;
+
+ rc = GetRecord( NoOfRecs );
+#ifdef XB_REAL_DELETE
+ if(!rc && RealDelete && RecordDeleted())
+ rc = GetPrevRecord();
+#endif
+
+ return rc;
+}
+/************************************************************************/
+//! Get the next physical record in the data file
+/*!
+ Attempts to retrieve the next physical record from the data file into
+ the record buffer.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_EOF</td><td>At end of file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_EOF & At end of file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::GetNextRecord()
+{
+ xbShort rc;
+ if( NoOfRecs == 0 )
+ return XB_INVALID_RECORD;
+ else if( CurRec >= NoOfRecs )
+ return XB_EOF;
+
+ rc = GetRecord( ++CurRec );
+
+#ifdef XB_REAL_DELETE
+ while(!rc && RealDelete && RecordDeleted())
+ rc = GetRecord(++CurRec);
+#endif
+
+ return rc;
+}
+/************************************************************************/
+//! Get the previous physical record in the data file
+/*!
+ Attempts to retrieve the previous physical record from the data file into
+ the record buffer.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_BOF</td><td>At beginning of file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_BOF & At beginning of file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::GetPrevRecord()
+{
+ xbShort rc;
+ if( NoOfRecs == 0 )
+ return XB_INVALID_RECORD;
+ else if( CurRec <= 1L )
+ return XB_EOF;
+
+ rc = GetRecord( --CurRec );
+
+#ifdef XB_REAL_DELETE
+ while(!rc && RealDelete && RecordDeleted())
+ rc = GetRecord(--CurRec);
+#endif
+
+ return rc;
+}
+/************************************************************************/
+//! Dump record
+/*!
+ Dump the contents of the specified record to stdout.
+
+ \param RecNo Record number of record to be dumped.
+ \returns An error code (same as GetRecord()).
+*/
+xbShort xbDbf::DumpRecord( xbULong RecNo )
+{
+ int i, rc;
+ char buf[4096];
+
+ if( RecNo == 0 || RecNo > NoOfRecs )
+ return XB_INVALID_RECORD;
+
+ rc = GetRecord( RecNo );
+ if( rc != XB_NO_ERROR )
+ return rc;
+
+ std::cout << "\nREC NUMBER " << RecNo << "\n";
+
+ if( RecordDeleted() )
+ std::cout << "\nRecord deleted...\n";
+
+ for( i = 0; i < NoOfFields; i++ ){
+#ifdef XB_MEMO_FIELDS
+ if(SchemaPtr[i].Type == 'M'){
+ if( MemoFieldExists( i )){
+ std::cout << SchemaPtr[i].Type << " " << SchemaPtr[i].FieldName
+ << " len = " << GetMemoFieldLen( i ) << std::endl;
+ memset( buf, 0x00, 4095 );
+ rc = GetMemoField(i, 4095, buf, 0);
+ if(rc != XB_NO_ERROR)
+ return rc;
+ } else {
+ buf[0] = 0x00;
+ }
+ }
+ else
+ GetField( i, buf );
+ std::cout << SchemaPtr[i].Type << " " << SchemaPtr[i].FieldName << " = '" << buf << "'\n";
+#else
+ GetField( i, buf );
+ std::cout << SchemaPtr[i].FieldName << " = '" << buf << "'\n";
+#endif
+ }
+ std::cout << std::endl;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Write the current record buffer to the current record in the data file.
+/*!
+ Attempts to write the contents of the record buffer to the current
+ record in the data file. Updates any open indexes.
+
+ \sa PutRecord(xbULong RecNo)
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+
+/************************************************************************/
+xbShort xbDbf::PutRecord() {
+ return PutRecord(CurRec);
+}
+
+//! Write the current record buffer to the specified record in the data file.
+/*!
+ Attempts to write the contents of the record buffer to the record specified
+ by RecNo. Updates any open indexes.
+
+ \param RecNo Record number to which data should be written
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Couldn't lock file</td></tr>
+ <tr><td>XB_NOT_OPEN</td><td>File is not open</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Error writing to file</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_LOCK\_FAILED & Couldn't lock file \\ \hline
+ XB\_NOT\_OPEN & File is not open \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_WRITE\_ERROR & Error writing to file \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::PutRecord(xbULong RecNo)
+{
+ xbShort rc;
+
+#if defined(XB_INDEX_ANY)
+ xbIxList *i;
+#endif
+
+ if( DbfStatus == XB_CLOSED )
+ return XB_NOT_OPEN;
+
+/* lock the database */
+#ifdef XB_LOCKING_ON
+ if( AutoLock ){
+// if(( rc = LockDatabase( XB_LOCK, RecNo )) != XB_NO_ERROR )
+// return rc;
+// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ){
+// LockDatabase( XB_UNLOCK, RecNo );
+// return rc;
+// }
+
+ if((rc = ReadHeader(1)) != XB_NO_ERROR){
+// if(AutoLock){
+// LockDatabase( XB_UNLOCK, RecNo );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+ return rc;
+ }
+ }
+#endif
+
+ if( RecNo > NoOfRecs || RecNo == 0L )
+ return XB_INVALID_RECORD;
+
+/* lock the indexes */
+#if defined(XB_INDEX_ANY)
+#ifdef XB_LOCKING_ON
+ i = NdxList;
+ while( i && AutoLock ){
+// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR )
+// return rc;
+ i = i->NextIx;
+ }
+#endif /* XB_LOCKING_ON */
+#endif
+
+#if defined(XB_INDEX_ANY)
+ /* for any unique indexes that were updated, verify no unique keys exist */
+ i = NdxList;
+ while( i ){
+ if( i->index->UniqueIndex() ){
+ if(( i->KeyUpdated = i->index->KeyWasChanged()) == 1 ){
+ i->index->CreateKey(0, 0);
+ if( i->index->FindKey() == XB_FOUND && i->index->GetCurDbfRec() != RecNo)
+ return XB_KEY_NOT_UNIQUE;
+ }
+ }
+ i = i->NextIx;
+ }
+#endif
+
+#if defined(XB_INDEX_ANY)
+ /* loop through deleting old index keys and adding new index keys */
+ i = NdxList;
+ while( i ){
+ if( !i->index->UniqueIndex() )
+ i->KeyUpdated = i->index->KeyWasChanged();
+ if( i->KeyUpdated ){
+ i->index->CreateKey( 1, 0 ); /* load key buf w/ old values */
+ if((rc = i->index->DeleteKey( CurRec )) != XB_NO_ERROR){
+#ifdef XB_LOCKING_ON
+// if( AutoLock ){
+// LockDatabase( XB_UNLOCK, RecNo );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while( i && AutoLock ){
+// i->index->LockIndex( XB_UNLOCK );
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+#endif /* XB_LOCKING_ON */
+ return rc;
+ }
+
+ i->index->CreateKey( 0, 0 );
+ if(( rc = i->index->AddKey(CurRec)) != XB_NO_ERROR ){
+#ifdef XB_LOCKING_ON
+// if( AutoLock ){
+// LockDatabase( XB_UNLOCK, RecNo );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while( i && AutoLock ){
+// i->index->LockIndex( XB_UNLOCK );
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+#endif /* XB_LOCKING_ON */
+ return rc;
+ }
+ i->index->TouchIndex();
+ }
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+
+ if( _fseek( fp, (HeaderLen+(((xbOffT)RecNo-1L)*RecordLen)),0 ))
+ return XB_SEEK_ERROR;
+
+ if( fwrite( RecBuf, RecordLen, 1, fp ) != 1 )
+ return XB_WRITE_ERROR;
+
+ /* calculate the latest header information */
+ xbDate d;
+ UpdateYY = d.YearOf() - 1900;
+ if(XFV == 3)
+ UpdateYY %= 100; // dBASE III seems to do this, IV does not. DTB
+ UpdateMM = d.MonthOf();
+ UpdateDD = d.DayOf( XB_FMT_MONTH );
+
+ /* rewrite the header record */
+ if(( rc = WriteHeader( 1 )) != XB_NO_ERROR )
+ return rc;
+
+#ifdef XB_LOCKING_ON
+// if( AutoLock ){
+// LockDatabase( XB_UNLOCK, RecNo );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+
+#if defined(XB_INDEX_ANY)
+ i = NdxList;
+ while( i && AutoLock ){
+// i->index->LockIndex( XB_UNLOCK );
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+#endif /* XB_LOCKING_ON */
+
+ CurRec = RecNo;
+ DbfStatus = XB_OPEN;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Delete the current record
+/*!
+ Marks the current record as deleted or if "real" deletes are turned
+ on (xbDbf::RealDeleteOn()) will delete the record and add it to the
+ free record list. Normal dBase behavior is to simply mark the record
+ as deleted; the record will actually be deleted when the the DBF file
+ "packed" (xbDbf::PackDatabase()). If "real" deletes are not on, a
+ record may be undeleted using xbDbf::UndeleteRecord().
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::DeleteRecord()
+{
+ xbULong newCurRec = 0;
+ xbShort rc = XB_NO_ERROR;
+
+#if defined(XB_INDEX_ANY)
+ xbIxList *i;
+#endif
+
+ if(!RecBuf)
+ return XB_INVALID_RECORD;
+
+ if(CurRec < 1 || CurRec > NoOfRecs)
+ return XB_INVALID_RECORD;
+
+/* lock the database */
+#ifdef XB_LOCKING_ON
+
+ if( AutoLock ){
+/*
+ if(( rc = LockDatabase( XB_LOCK, CurRec )) != XB_NO_ERROR )
+ return rc;
+ if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR ){
+ LockDatabase( XB_UNLOCK, CurRec );
+ return rc;
+ }
+ */
+ if((rc = ReadHeader(1)) != XB_NO_ERROR){
+// if(AutoLock){
+// LockDatabase( XB_UNLOCK, CurRec );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+ return rc;
+ }
+ }
+#endif
+
+/* lock the indexes */
+#if defined(XB_INDEX_ANY) && defined(XB_LOCKING_ON) && defined(XB_REAL_DELETE)
+ i = NdxList;
+ while( i && AutoLock ){
+// if(( rc = i->index->LockIndex( XB_LOCK )) != XB_NO_ERROR )
+// return rc;
+ i = i->NextIx;
+ }
+#endif
+
+/* remove keys from indexes */
+#if defined(XB_REAL_DELETE) && defined(XB_INDEX_ANY)
+
+ if(RealDelete){
+ i = NdxList;
+ while(i){
+ i->index->CreateKey(0, 0); /* load key buf */
+ if(i->index->GetCurDbfRec() == (xbULong)CurRec){
+ i->index->DeleteKey(CurRec);
+ newCurRec = i->index->GetCurDbfRec();
+ }
+ else
+ i->index->DeleteKey(CurRec);
+ i->index->TouchIndex();
+ i = i->NextIx;
+ }
+ }
+
+#endif
+
+ RecBuf[0] = 0x2a;
+
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete){
+#ifdef XB_MEMO_FIELDS
+ //
+ // Delete memo data for memo fields.
+ //
+ for(int f = 0; f < NoOfFields; f++ )
+ if(GetFieldType(f) == 'M' && MemoFieldExists(f))
+ UpdateMemoData(f, 0, 0, XB_LOCK);
+#endif
+ xbase->PutULong(&RecBuf[1], FirstFreeRec);
+ FirstFreeRec = CurRec;
+ RealNumRecs--;
+ WriteHeader(1);
+ }
+#endif
+
+ if(!RealDelete){
+ if( DbfStatus != XB_UPDATED ){
+ DbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, RecordLen );
+ }
+ rc = PutRecord( CurRec );
+ }
+ else
+ {
+ if(_fseek( fp, (HeaderLen+(((xbOffT)CurRec-1L)*RecordLen)), 0))
+ return XB_SEEK_ERROR;
+ if(fwrite( RecBuf, RecordLen, 1, fp ) != 1 )
+ return XB_WRITE_ERROR;
+
+ //
+ // Attempt to read in the record for the current location
+ // in the active index.
+ //
+ CurRec = newCurRec;
+ if(CurRec)
+ rc = GetRecord(CurRec);
+ else
+ BlankRecord();
+ }
+
+#ifdef XB_LOCKING_ON
+// if(AutoLock){
+// LockDatabase( XB_UNLOCK, CurRec );
+// LockDatabase( XB_UNLOCK, 0L );
+// }
+
+#if defined(XB_INDEX_ANY) && defined(XB_REAL_DELETE)
+ i = NdxList;
+ while( i && AutoLock ){
+// i->index->LockIndex( XB_UNLOCK );
+ i = i->NextIx;
+ }
+#endif /* XB_INDEX_ANY */
+#endif /* XB_LOCKING_ON */
+
+ return rc;
+}
+/************************************************************************/
+//! Undelete the current record
+/*!
+ Marks the currect record as not deleted (i.e. removes the flag indicating
+ the record is deleted). This method may not be used (and will return
+ an error code) if "real" deletes are on.
+
+ \returns One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_INVALID_RECORD</td><td>Invalid record number</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No error \\ \hline
+ XB\_INVALID\_RECORD & Invalid record number \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::UndeleteRecord()
+{
+ xbShort rc;
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete)
+ return XB_INVALID_RECORD;
+#endif
+ if( RecBuf ){
+ if( DbfStatus != XB_UPDATED ){
+ DbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, RecordLen );
+ }
+
+ RecBuf[0] = 0x20;
+ if(( rc = PutRecord( CurRec )) != 0 )
+ return rc;
+ }
+ else
+ return XB_INVALID_RECORD;
+
+ return 0;
+}
+/************************************************************************/
+//! Determine if current record is deleted
+/*!
+ \returns TRUE (1) if the current record is marked as deleted or FALSE
+ (0) if not.
+*/
+xbShort xbDbf::RecordDeleted()
+{
+ if( RecBuf && RecBuf[0] == 0x2a )
+ return 1;
+ else
+ return 0;
+}
+/************************************************************************/
+//! Create a unique file name
+/*!
+*/
+xbShort xbDbf::CreateUniqueDbfName( xbString & sDbfn, xbString & sDbtn )
+{
+ xbShort dnf; /* directory in name flag */
+ xbShort unique = 0;
+ xbLong l = 1;
+ char dbfn[13];
+ char dbtn[13];
+
+ dnf = xbase->DirectoryExistsInName( GetFileName() );
+ sprintf( dbfn, "xb%06d.dbf", l );
+ sprintf( dbtn, "xb%06d.dbt", l++ );
+
+ if( dnf ){
+ sDbfn.assign( GetFileName(), 0, dnf );
+ sDbfn += dbfn;
+ sDbtn.assign( GetFileName(), 0, dnf );
+ sDbtn += dbtn;
+ } else {
+ sDbfn = dbfn;
+ sDbtn = dbtn;
+ }
+
+ while( !unique ){
+ if( access( sDbfn.getData(), 0 ) == -1 &&
+ access( sDbtn.getData(), 0 ) == -1 )
+ unique++;
+ else{
+ sprintf( dbfn, "xb%06d.dbf", l );
+ sprintf( dbtn, "xb%06d.dbt", l++ );
+
+ if( dnf ){
+ sDbfn.assign( GetFileName(), 0, dnf );
+ sDbfn += dbfn;
+ sDbtn.assign( GetFileName(), 0, dnf );
+ sDbtn += dbtn;
+ } else {
+ sDbfn = dbfn;
+ sDbtn = dbtn;
+ }
+ }
+ }
+ return 0;
+}
+
+/************************************************************************/
+//! Pack data file
+/*!
+*/
+xbShort xbDbf::PackDatafiles(void (*statusFunc)(xbLong itemNum, xbLong numItems))
+{
+ xbShort rc, i;
+ FILE *t;
+ xbLong l;
+ char *target, *source;
+ xbString TempDbfName;
+ xbString TempDbtName;
+ char * Buf = 0;
+
+#ifdef XB_MEMO_FIELDS
+ char tbuf[4];
+#endif
+
+#ifdef XB_MEMO_FIELDS
+ xbLong len, BufSize;
+ xbShort MemoFields;
+#endif /* XB_MEMO_FIELDS */
+
+ xbDbf Temp( xbase );
+ CreateUniqueDbfName( TempDbfName, TempDbtName );
+
+ if(( t = fopen( TempDbfName, "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ /* copy file header */
+ if(( rc = _fseek( fp, 0, SEEK_SET )) != 0 )
+ return XB_SEEK_ERROR;
+
+ for( i = 0; i < HeaderLen; i++ )
+ fputc( fgetc( fp ), t );
+ fputc( 0x1a, t );
+
+ if( fclose( t ) != 0 )
+ return XB_CLOSE_ERROR;
+
+#ifdef XB_MEMO_FIELDS
+ if(( MemoFields = MemoFieldsPresent()) > 0 ){
+
+ if((t = fopen( TempDbtName, "w+b" )) == NULL)
+ return XB_OPEN_ERROR;
+
+ l = 1L;
+ memset( tbuf, 0x00, 4 );
+ xbase->PutLong( tbuf, l );
+
+ if((fwrite(&tbuf, 4, 1, t)) != 1)
+ return XB_WRITE_ERROR;
+
+ if( MemoHeader.Version == 0x03 ){
+ for( i = 0; i < 12; i++ ) fputc( 0x00, t );
+ fputc( 0x03, t );
+ for( i = 0; i < 495; i++ ) fputc( 0x00, t );
+ } else {
+ for( i = 0; i < 4; i++ ) fputc( 0x00, t );
+ if ((fwrite(&MemoHeader.FileName, 8, 1, t)) != 1)
+ return XB_WRITE_ERROR;
+ for( i = 0; i < 4; i++ ) fputc( 0x00, t );
+ memset( tbuf, 0x00, 2 );
+ xbase->PutShort( tbuf, MemoHeader.BlockSize );
+ if ((fwrite(&tbuf, 2, 1, t)) != 1)
+ return XB_WRITE_ERROR;
+
+ for( i = 22; i < MemoHeader.BlockSize; i++ ) fputc( 0x00, t );
+ }
+
+ if( fclose( t ) != 0 )
+ return XB_CLOSE_ERROR;
+ }
+#endif /* XB_MEMO_FIELDS */
+
+ /* reopen as database */
+ if(( rc = Temp.OpenDatabase( TempDbfName )) != XB_NO_ERROR )
+ return rc;
+
+#ifdef XB_REAL_DELETE
+ if(RealDelete)
+ Temp.RealDeleteOn();
+ Temp.FirstFreeRec = 0;
+ Temp.RealNumRecs = 0;
+#endif
+ Temp.ResetNoOfRecs();
+ Temp.WriteHeader(2); // flush NoOfRecs=0 to disk
+ target = Temp.GetRecordBuf();
+ source = GetRecordBuf();
+
+ for( l = 1; l <= PhysicalNoOfRecords(); l++ ){
+ if(statusFunc && (l == 1 || !(l % 100) || l == PhysicalNoOfRecords()))
+ statusFunc(l, PhysicalNoOfRecords());
+
+ if(( rc = GetRecord( l )) != XB_NO_ERROR )
+ return rc;
+
+ if( !RecordDeleted() ){
+ memcpy( target, source, GetRecordLen());
+
+#ifdef XB_MEMO_FIELDS
+ BufSize = 0L;
+// Buf = NULL; Already set to 0, this statement flags as memory leak
+
+ for( i = 0; i < NoOfFields; i++ ){
+ if( GetFieldType( i ) == 'M' && MemoFieldExists( i )){
+ Temp.PutLongField(i, 0L);
+ len = GetMemoFieldLen( i );
+ if( len > BufSize ){
+ if( Buf )
+ free( Buf );
+ if((Buf = (char *)malloc(len)) == NULL)
+ return XB_NO_MEMORY;
+ BufSize = len;
+ }
+ GetMemoField( i, len, Buf, -1 );
+ Temp.UpdateMemoData( i, len, Buf, -1 );
+ }
+ }
+#endif
+ if(( rc = Temp.AppendRecord()) != XB_NO_ERROR ){
+ if(Buf) free(Buf);
+ return rc;
+ }
+ }
+ }
+ if( Buf ) free( Buf );
+ Temp.CloseDatabase();
+
+ if(fclose(fp) != 0)
+ return XB_CLOSE_ERROR;
+
+ if(remove(GetFileName()) != 0)
+ return XB_WRITE_ERROR;
+
+ if(rename(TempDbfName, GetFileName()) != 0)
+ return XB_WRITE_ERROR;
+
+#ifdef XB_MEMO_FIELDS
+ if( MemoFields ){
+ if(fclose(mfp) != 0)
+ return XB_CLOSE_ERROR;
+
+ if(remove(MemofileName) != 0)
+ return XB_WRITE_ERROR;
+ if( rename( TempDbtName, MemofileName ) != 0 )
+ return XB_WRITE_ERROR;
+ if(( mfp = fopen( MemofileName, "r+b" )) == NULL )
+ return XB_OPEN_ERROR;
+ if(( rc = GetDbtHeader(1)) != 0 ){
+ fclose( mfp );
+ return rc;
+ }
+#ifdef XB_LOCKING_ON
+ /* no buffering in multi user mode */
+ setbuf( mfp, NULL );
+#endif
+ }
+
+#endif /* XB_MEMO_FIELDS */
+
+ if(( fp = fopen( GetFileName(), "r+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+#ifdef XB_LOCKING_ON
+ /* no buffering in multi user mode */
+ setbuf( fp, NULL );
+#endif
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Pack the database
+/*!
+ This method removes all records marked for deletion from an Xbase (.DBF)
+ file, reindexes any open index files, and also reorganizes any memo fields
+ stored in a .DBT memo file.
+
+ \param packStatusFunc status function
+ \param indexStatusFunc index status function
+
+ \param LockWaitOption One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>LockWaitOption</th><th>Description</th></tr>
+ <tr><td>F_SETLK</td><td>Return immediately if the DBF file cannot be locked</td></tr>
+ <tr><td>XB_LOCK</td><td>Wait for lock on DBF file to succeed</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{LockWaitOption} & \textbf{Description} \\ \hline \hline
+ F\_SETLK & Return immediately if DBF file cannot be locked \\ \hline
+ F\_SETLKW & Wait for lock on DBF file to succeed \\ \hline
+ \end{tabular}
+ \endlatexonly
+
+ \returns One of the following return codes:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Return Code</th><th>Description</th></tr>
+ <tr><td>XB_NO_ERROR</td><td>No error</td></tr>
+ <tr><td>XB_CLOSE_ERROR</td><td>Unable to close intermediate work file</td></tr>
+ <tr><td>XB_OPEN_ERROR</td><td>Could not open file</td></tr>
+ <tr><td>XB_NO_MEMORY</td><td>Memory allocation error</td></tr>
+ <tr><td>XB_WRITE_ERROR</td><td>Couldn't write to disk</td></tr>
+ <tr><td>XB_SEEK_ERROR</td><td>Error seeking file</td></tr>
+ <tr><td>XB_LOCK_FAILED</td><td>Unable to lock file or index</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Return Code} & \textbf{Description} \\ \hline \hline
+ XB\_NO\_ERROR & No Error \\ \hline
+ XB\_CLOSE\_ERROR & Unable to close intermediate work file \\ \hline
+ XB\_OPEN\_ERROR & Couldn't open the file \\ \hline
+ XB\_NO\_MEMORY & Memory allocation error \\ \hline
+ XB\_WRITE\_ERROR & Couldn't write to disk \\ \hline
+ XB\_SEEK\_ERROR & Error seeking file \\ \hline
+ XB\_LOCK\_FAILED & Unable to lock file or index \\ \hline
+ \end{tabular}
+ \endlatexonly
+*/
+xbShort xbDbf::PackDatabase(xbShort LockWaitOption,
+ void (*packStatusFunc)(xbLong itemNum, xbLong numItems),
+ void (*indexStatusFunc)(xbLong itemNum, xbLong numItems))
+{
+ xbShort rc;
+
+ /* lock all open files and indexes */
+// if(( rc = ExclusiveLock( LockWaitOption )) != XB_NO_ERROR ) return rc;
+
+ if(( rc = PackDatafiles(packStatusFunc)) != XB_NO_ERROR ){
+// ExclusiveUnlock();
+ return rc;
+ }
+
+ /* refresh file header */
+ if(( rc = ReadHeader(1)) != XB_NO_ERROR )
+ return rc;
+
+ if(( rc = RebuildAllIndices(indexStatusFunc)) != XB_NO_ERROR )
+ return rc;
+
+// ExclusiveUnlock();
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Copy DBF structure
+/*!
+*/
+xbShort xbDbf::CopyDbfStructure(const char *NewFileName, xbShort Overlay) {
+
+ xbShort rc, i;
+ xbString ndfn; /* new dbf file name */
+ char ch;
+
+#ifdef XB_MEMO_FIELDS
+ char buf[9];
+ xbShort ct, NameLen;
+ xbString MemoName;
+#endif
+ FILE *t;
+
+ /* build the new file name */
+ rc = NameSuffixMissing( 1, NewFileName );
+ ndfn = NewFileName;
+ if( rc == 1 )
+ ndfn += ".dbf";
+ else if( rc == 2 )
+ ndfn += ".DBF";
+
+ /* check if the file exists and Overlay is on */
+ if(((t = fopen( ndfn, "r" )) != NULL ) && !Overlay) {
+ fclose(t);
+ return XB_FILE_EXISTS;
+ }
+
+ /* open new file */
+ if((t = fopen(ndfn, "w+b")) == NULL)
+ return XB_OPEN_ERROR;
+
+ /* copy the file header */
+ if(( rc = _fseek( fp, 0, SEEK_SET )) != 0 )
+ return XB_SEEK_ERROR;
+
+ fputc( fgetc( fp ), t );
+
+ /* do the date */
+ xbDate d;
+ ch = d.YearOf() - 1900;
+ if(XFV == 3)
+ ch %= 100; // dBASE III+ does this, dBASE IV does not.
+ fputc( ch, t );
+ ch = d.MonthOf();
+ fputc( ch, t );
+ ch = d.DayOf( XB_FMT_MONTH );
+ fputc( ch, t );
+
+ /* record count */
+ for( i = 0; i < 4; i++ ) fputc( 0x00, t );
+
+ if((rc = _fseek(fp, 7, SEEK_CUR)) != 0) {
+ fclose( t );
+ return XB_SEEK_ERROR;
+ }
+ for( i = 0; i < 4; i++ )
+ fputc( fgetc( fp ), t );
+
+ for( i = 0; i < 17; i++ )
+ fputc( 0x00, t );
+
+ if((rc = _fseek( fp, 17, SEEK_CUR )) != 0) {
+ fclose( t );
+ return XB_SEEK_ERROR;
+ }
+
+ for( i = 29; i < HeaderLen; i++ )
+ fputc( fgetc( fp ), t );
+
+ fputc( 0x1a, t );
+ fclose( t );
+
+#ifdef XB_MEMO_FIELDS
+ if( MemoFieldsPresent()){
+ MemoName = ndfn;
+
+ NameLen = MemoName.len();
+ NameLen--;
+ if( MemoName.getCharacter( NameLen ) == 'F' )
+ MemoName.putAt(NameLen, 'T');
+ else
+ MemoName.putAt(NameLen, 't');
+
+ if(( t = fopen( MemoName, "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ memset( buf, 0x00, 4 );
+ xbase->PutLong( buf, 1L );
+ if(( fwrite( &buf, 4, 1, t )) != 1 ){
+ fclose( t );
+ return XB_WRITE_ERROR;
+ }
+ if( MemoHeader.Version == 0x03 ){
+ for( i = 0; i < 12; i++ ) fputc( 0x00, t );
+ fputc( 0x03, t );
+ for( i = 0; i < 495; i++ ) fputc( 0x00, t );
+ }
+ else
+ {
+ for( i = 0; i < 4; i++ ) fputc( 0x00, t ); // put 4 bytes 0x00
+ memset( buf, 0x00, 9 );
+ NameLen = ndfn.len();
+ for( i = 0, ct = 0; i < NameLen; i++ )
+ if( ndfn.getCharacter( i ) == PATH_SEPARATOR ){
+ ct = i;
+ ct++;
+ }
+
+ for( i = 0; i < 8 && ndfn[i+ct] != '.'; i++ )
+ buf[i] = ndfn[i+ct];
+
+ fwrite( &buf, 8, 1, t );
+ for( i = 0; i < 4; i++ ) fputc( 0x00, t );
+ memset( buf, 0x00, 2 );
+ xbase->PutShort( buf, MemoHeader.BlockSize );
+ if(( fwrite( &buf, 2, 1, t )) != 1 ){
+ fclose(t);
+ return XB_WRITE_ERROR;
+ }
+ for( i = 22; i < MemoHeader.BlockSize; i++ ) fputc( 0x00, t );
+ }
+ }
+ fclose( t );
+#endif // XB_MEMO_FIELDS
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Add index to list
+/*!
+ Adds the specified index to the list of indexes maintained by the
+ dbf.
+
+ \param n index to add
+ \param IndexName name of index
+*/
+#if defined(XB_INDEX_ANY)
+xbShort xbDbf::AddIndexToIxList(xbIndex * n, const char *IndexName)
+{
+ xbIxList *i, *s, *t;
+
+ if( !FreeIxList ){
+ if((i = (xbIxList *) malloc(sizeof(xbIxList))) == NULL)
+ return XB_NO_MEMORY;
+ }
+ else
+ {
+ i = FreeIxList;
+ FreeIxList = i->NextIx;
+ }
+ memset(i, 0x00, sizeof(xbIxList));
+
+ i->IxName = IndexName;
+ i->index = n;
+
+ s = NULL;
+ t = NdxList;
+ while( t && strcmp( t->IxName, IndexName ) < 0 ){
+ s = t;
+ t = t->NextIx;
+ }
+ i->NextIx = t;
+ if( s == NULL )
+ NdxList = i;
+ else
+ s->NextIx = i;
+ return 0;
+}
+#endif
+/************************************************************************/
+//! Rebuild all index files
+/*!
+*/
+xbShort xbDbf::RebuildAllIndices(void (*statusFunc)(xbLong itemNum, xbLong numItems))
+{
+#if defined(XB_INDEX_ANY)
+ xbShort rc;
+ xbIxList *n;
+
+ n = NdxList;
+ while( n ){
+ if(( rc = n->index->ReIndex(statusFunc)) != XB_NO_ERROR ){
+// ExclusiveUnlock();
+ return rc;
+ }
+ n = n->NextIx;
+ }
+#endif
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Delete all records
+/*!
+*/
+xbShort xbDbf::DeleteAll( xbShort Option )
+{
+ xbShort rc;
+
+ if(( NoOfRecords()) == 0 )
+ return XB_NO_ERROR;
+ if(( rc = GetFirstRecord()) != XB_NO_ERROR )
+ return rc;
+
+ if( Option == 0 ){ /* delete all option */
+ while( 1 ){
+ if( !RecordDeleted())
+ if(( rc = DeleteRecord()) != XB_NO_ERROR )
+ return rc;
+ if(( rc = GetNextRecord()) != XB_NO_ERROR )
+ break;
+ }
+ }
+ else /* undelete all option */
+ {
+ while( 1 ){
+ if( RecordDeleted())
+ if(( rc = UndeleteRecord()) != XB_NO_ERROR )
+ return rc;
+ if(( rc = GetNextRecord()) != XB_NO_ERROR )
+ break;
+ }
+ }
+ if( rc == XB_EOF )
+ return XB_NO_ERROR;
+ else
+ return rc;
+}
+/************************************************************************/
+//! Delete all records and pack data file
+/*!
+*/
+xbShort xbDbf::Zap( xbShort WaitOption )
+{
+ xbShort rc;
+ xbString TempDbfName, TempDbtName;
+
+ CreateUniqueDbfName( TempDbfName, TempDbtName );
+ if(( rc = CopyDbfStructure( TempDbfName, 1 )) != XB_NO_ERROR) {
+ return rc;
+ }
+
+ if( fp ){
+ fclose( fp );
+ fp = 0;
+ }
+
+ if(( rc = remove( GetFileName() )) != 0 )
+ return XB_WRITE_ERROR;
+
+ if(( rc = rename( TempDbfName, GetFileName() )) != 0 )
+ return XB_WRITE_ERROR;
+
+ if((fp = fopen( GetFileName(), "r+b" )) == NULL)
+ return XB_OPEN_ERROR;
+
+#ifdef XB_LOCKING_ON
+ setbuf( fp, NULL );
+#endif
+ ReadHeader( 1 );
+
+#ifdef XB_MEMO_FIELDS
+ if( MemoFieldsPresent() ){
+ fclose( mfp );
+
+ if(( rc = remove( MemofileName )) != 0 )
+ return XB_WRITE_ERROR;
+
+ if(( rc = rename( TempDbtName, MemofileName )) != 0 )
+ return XB_WRITE_ERROR;
+
+ if(( mfp = fopen( MemofileName, "r+b" )) == NULL)
+ return XB_OPEN_ERROR;
+
+ }
+#endif // XB_MEMO_FIELDS
+
+ if(( rc = RebuildAllIndices()) != XB_NO_ERROR )
+ return rc;
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Remove an index from the list
+/*!
+*/
+#if defined(XB_INDEX_ANY)
+xbShort xbDbf::RemoveIndexFromIxList(xbIndex * n) {
+ xbIxList *i, *s;
+
+ i = NdxList;
+ s = NULL;
+ while( i ){
+ if( i->index == n ){
+ /* remove it from current chain */
+ if( s )
+ s->NextIx = i->NextIx;
+ else
+ NdxList = i->NextIx;
+
+ /* add i to the current free chain */
+ i->NextIx = FreeIxList;
+ FreeIxList = i;
+ FreeIxList->IxName = (const char *)NULL;
+ FreeIxList->index = NULL;
+ break;
+ }
+ else
+ {
+ s = i;
+ i = i->NextIx;
+ }
+ }
+ return XB_NO_ERROR;
+}
+#endif
+
+/************************************************************************/
+//! Gets the number of records in the data file
+/*!
+*/
+xbLong xbDbf::NoOfRecords()
+{
+ xbLong numRecs;
+
+/* lock the database */
+#ifdef XB_LOCKING_ON
+ xbShort rc;
+
+ if( AutoLock ){
+// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR )
+// return rc;
+
+ if((rc = ReadHeader(1)) != XB_NO_ERROR){
+// if(AutoLock)
+// LockDatabase( XB_UNLOCK, 0L );
+ return rc;
+ }
+ }
+#endif
+
+#ifndef XB_REAL_DELETE
+ numRecs = NoOfRecs;
+#else
+ numRecs = RealDelete ? RealNumRecs : NoOfRecs;
+#endif
+
+#ifdef XB_LOCKING_ON
+// if(AutoLock)
+// LockDatabase( XB_UNLOCK, 0L );
+#endif
+
+ return numRecs;
+}
+/************************************************************************/
+//! Get the physical number of records in the data file
+/*!
+*/
+xbLong xbDbf::PhysicalNoOfRecords()
+{
+ xbShort rc;
+
+/* lock the database */
+#ifdef XB_LOCKING_ON
+// if( AutoLock )
+// if(( rc = LockDatabase( XB_LOCK, 0L )) != XB_NO_ERROR )
+// return rc;
+#endif
+
+ rc = ReadHeader(1);
+
+#ifdef XB_LOCKING_ON
+// if(AutoLock)
+// if(( rc = LockDatabase( XB_UNLOCK, 0L )) != XB_NO_ERROR )
+// return rc;
+#endif
+
+ if( rc )
+ return rc;
+
+ return NoOfRecs;
+}
+
+/************************************************************************/
+#if defined(XB_INDEX_ANY)
+//! Get the number of currently open indexes for data file
+/*!
+*/
+xbShort xbDbf::IndexCount()
+{
+ xbShort count;
+ xbIxList *i;
+
+ for(count = 0, i = NdxList; i; i = i->NextIx, count++) ;
+
+ return count;
+}
+/************************************************************************/
+//! Get a specific index
+/*!
+*/
+xbIndex * xbDbf::GetIndex(xbShort indexNum)
+{
+ xbIxList *i;
+
+ i = NdxList;
+ while(indexNum && i){
+ indexNum--;
+ i = i->NextIx;
+ }
+
+ if(i)
+ return i->index;
+
+ return 0;
+}
+
+#endif // XB_INDEX_ANY
+/************************************************************************/
+void xbDbf::Flush()
+{
+ if(fp)
+ fflush(fp);
+
+#ifdef XB_MEMO_FIELDS
+ if(mfp)
+ fflush(mfp);
+#endif
+
+#if defined(XB_INDEX_ANY)
+ xbIxList
+ *i;
+
+ i = NdxList;
+ while(i) {
+ i->index->Flush();
+ i = i->NextIx;
+ }
+#endif
+}
+/************************************************************************/
+#ifdef XB_LOCKING_ON
+xbShort xbDbf::SetLockMode( xbShort nlm )
+{
+/*
+ xbShort rc;
+ if( LockMode != XB_XBASE_LOCK_MODE &&
+ nlm == XB_XBASE_LOCK_MODE &&
+ !xblfh ){
+ rc = OpenXbLockFile();
+ if( rc )
+ return rc;
+ }
+*/
+ LockMode = nlm;
+ return XB_NO_ERROR;
+}
+#endif
+/************************************************************************/
+const char * xbDbf::GetExtWithDot( bool lower )
+{
+ return lower ? ".dbf" : ".DBF";
+}
diff --git a/xbase64/xbdbf.h b/xbase64/xbdbf.h
new file mode 100755
index 0000000..ce28e9a
--- /dev/null
+++ b/xbase64/xbdbf.h
@@ -0,0 +1,533 @@
+/* xbdbf.h
+
+ Xbase64 project source code
+
+ This file contains the Class definition for a xbDBF object.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_DBF_H__
+#define __XB_DBF_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbtypes.h>
+#include <xbase64/xbdate.h>
+#include <xbase64/xbfile.h>
+
+#include <iostream>
+#include <stdio.h>
+
+/*! \file xbdbf.h
+*/
+
+#if defined(XB_INDEX_ANY)
+ class XBDLLEXPORT xbIndex;
+ class XBDLLEXPORT xbNdx;
+ class XBDLLEXPORT xbNtx;
+#endif
+
+/*****************************/
+/* Field Types */
+
+#define XB_CHAR_FLD 'C'
+#define XB_LOGICAL_FLD 'L'
+#define XB_NUMERIC_FLD 'N'
+#define XB_DATE_FLD 'D'
+#define XB_MEMO_FLD 'M'
+#define XB_FLOAT_FLD 'F'
+
+/*****************************/
+/* File Status Codes */
+
+#define XB_CLOSED 0
+#define XB_OPEN 1
+#define XB_UPDATED 2
+
+/*****************************/
+/* Other defines */
+
+#define XB_OVERLAY 1
+#define XB_DONTOVERLAY 0
+
+#define XB_CHAREOF '\x1A' /* end of DBF */
+#define XB_CHARHDR '\x0D' /* header terminator */
+
+//! Used to define the fields in a database (DBF file).
+/*!
+ Generally one would define an xbSchema array to be passed
+ to xbDbf::CreateDatabase() to define the fields in the database.
+
+ For example, one might create a declaration as follows:
+
+ \code
+ xbSchema MyRecord[] =
+ {
+ { "FIRSTNAME", XB_CHAR_FLD, 15, 0 },
+ { "LASTNAME", XB_CHAR_FLD, 20, 0 },
+ { "BIRTHDATE", XB_DATE_FLD, 8, 0 },
+ { "AMOUNT", XB_NUMERIC_FLD, 9, 2 },
+ { "SWITCH", XB_LOGICAL_FLD, 1, 0 },
+ { "FLOAT1", XB_FLOAT_FLD, 9, 2 },
+ { "FLOAT2", XB_FLOAT_FLD, 9, 1 },
+ { "FLOAT3", XB_FLOAT_FLD, 9, 2 },
+ { "FLOAT4", XB_FLOAT_FLD, 9, 3 },
+ { "MEMO1", XB_MEMO_FLD, 10, 0 },
+ { "ZIPCODE", XB_NUMERIC_FLD, 5, 0 },
+ { "",0,0,0 }
+ };
+ \endcode
+
+ Note that the last xbSchema in an array must be a "null" entry like the
+ one above:
+
+ \code
+ { "",0,0,0 }
+ \endcode
+
+ To indicate the end of the array.
+*/
+struct XBDLLEXPORT xbSchema {
+ char FieldName[11];
+ char Type;
+// xbUShort FieldLen; /* does not work */
+// xbUShort NoOfDecs; /* does not work */
+ unsigned char FieldLen; /* fields are stored as one byte on record*/
+ unsigned char NoOfDecs;
+};
+
+//! Defines a field in an XBase file header (DBF file header)
+/*!
+ This structure is only used internally by the xbDbf class.
+*/
+struct XBDLLEXPORT xbSchemaRec {
+ char FieldName[11];
+ char Type; /* field type */
+ char *Address; /* pointer to field in record buffer 1 */
+// xbUShort FieldLen; /* does not work */
+// xbUShort NoOfDecs; /* does not work */
+ unsigned char FieldLen; /* fields are stored as one byte on record */
+ unsigned char NoOfDecs;
+ char *Address2; /* pointer to field in record buffer 2 */
+ char *fp; /* pointer to null terminated buffer for field */
+ /* see method GetString */
+ xbShort LongFieldLen; /* to handle long field lengths */
+};
+
+//! xbIxList struct
+/*!
+ Internal use only.
+*/
+struct XBDLLEXPORT xbIxList {
+ xbIxList * NextIx;
+ xbString IxName;
+#if defined(XB_INDEX_ANY)
+ xbIndex * index;
+ xbShort Unique;
+ xbShort KeyUpdated;
+#endif
+};
+
+//! xbMH struct
+/*!
+ Internal use only.
+*/
+
+#ifdef XB_MEMO_FIELDS
+struct XBDLLEXPORT xbMH{ /* memo header */
+ xbLong NextBlock; /* pointer to next block to write */
+ char FileName[8]; /* name of dbt file */
+ char Version; /* not sure */
+ xbShort BlockSize; /* memo file block size */
+};
+#endif
+
+//! xbDbf class
+/*!
+ The xbDbf class encapsulates an xbase DBF database file. It includes
+ all dbf access, field access, and locking methods.
+*/
+class XBDLLEXPORT xbDbf : protected xbFile{
+
+public:
+ xbDbf( xbXBase * );
+ virtual ~xbDbf();
+
+ xbXBase *xbase; /* linkage to main base class */
+
+/* datafile methods */
+#if defined(XB_INDEX_ANY)
+ xbShort AddIndexToIxList(xbIndex *, const char *IndexName);
+ xbShort RemoveIndexFromIxList( xbIndex * );
+#endif
+ xbShort AppendRecord();
+ xbShort BlankRecord();
+ xbShort CloseDatabase( xbBool deleteIndexes = 0 );
+ xbShort CopyDbfStructure( const char *, xbShort );
+ xbShort CreateDatabase( const char * Name, xbSchema *, xbShort Overlay );
+ //! Delete all records
+ /*!
+ */
+ xbShort DeleteAllRecords() { return DeleteAll(0); }
+ xbShort DeleteRecord();
+#ifdef XBASE_DEBUG
+ xbShort DumpHeader( xbShort );
+#endif
+ xbShort DumpRecord( xbULong );
+ //! Return number of fields
+ /*!
+ */
+ xbLong FieldCount() { return NoOfFields; }
+ //! Return Dbf name
+ /*!
+ */
+ const xbString& GetDbfName() { return GetFileName(); }
+ //! Return status
+ /*!
+ */
+ xbShort GetDbfStatus() { return DbfStatus; }
+ xbShort GetFirstRecord();
+ xbShort GetLastRecord();
+ xbShort GetNextRecord();
+ xbShort GetPrevRecord();
+ //! Return current record number
+ /*!
+ */
+ xbLong GetCurRecNo() { return CurRec; }
+ xbShort GetRecord( xbULong );
+ //! Return a pointer to the record buffer
+ /*!
+ */
+ char * GetRecordBuf() { return RecBuf; }
+ //! Return record length
+ /*!
+ */
+ xbShort GetRecordLen() { return RecordLen; }
+ xbShort NameSuffixMissing( xbShort, const char * );
+ xbLong GetRecCnt() { return NoOfRecords(); }
+ xbLong NoOfRecords();
+ xbLong PhysicalNoOfRecords();
+ xbShort OpenDatabase( const char * );
+ xbShort PackDatabase(xbShort LockWaitOption,
+ void (*packStatusFunc)(xbLong itemNum, xbLong numItems) = 0,
+ void (*indexStatusFunc)(xbLong itemNum, xbLong numItems) = 0);
+ xbShort PutRecord(); // Put record to current position
+ xbShort PutRecord(xbULong);
+ xbShort RebuildAllIndices(
+ void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0);
+ xbShort RecordDeleted();
+ //! Set number of records to zero????
+ /*!
+ */
+ void ResetNoOfRecs() { NoOfRecs = 0L; }
+ xbShort SetVersion( xbShort );
+ //! Undelete all records
+ /*!
+ */
+ xbShort UndeleteAllRecords() { return DeleteAll(1); }
+ xbShort UndeleteRecord();
+ xbShort Zap( xbShort );
+
+/* field methods */
+ const char *GetField(xbShort FieldNo) const; // Using internal static buffer
+ const char *GetField(const char *Name) const;
+ xbShort GetField( xbShort FieldNo, char *Buf) const;
+ xbShort GetRawField( xbShort FieldNo, char *Buf) const;
+ xbShort GetField( xbShort FieldNo, char *Buf, xbShort RecBufSw) const;
+ xbShort GetField( const char *Name, char *Buf) const;
+ xbShort GetRawField(const char *Name, char *Buf) const;
+ xbShort GetField( const char *Name, char *Buf, xbShort RecBufSw) const;
+ xbShort GetField(xbShort FieldNo, xbString&, xbShort RecBufSw ) const;
+ xbShort GetFieldDecimal( xbShort );
+ xbShort GetFieldLen( xbShort );
+ char * GetFieldName( xbShort );
+ xbShort GetFieldNo( const char * FieldName ) const;
+ char GetFieldType( xbShort FieldNo ) const;
+ xbShort GetLogicalField( xbShort FieldNo );
+ xbShort GetLogicalField( const char * FieldName );
+ char * GetStringField( xbShort FieldNo );
+ char * GetStringField( const char * FieldName );
+ xbShort PutField( xbShort, const char * );
+ xbShort PutRawField( xbShort FieldNo, const char *buf );
+ xbShort PutField( const char *Name, const char *buf);
+ xbShort PutRawField( const char *Name, const char *buf );
+ xbShort ValidLogicalData( const char * );
+ xbShort ValidNumericData( const char * );
+
+ xbLong GetLongField( const char *FieldName) const;
+ xbLong GetLongField( const xbShort FieldNo) const;
+ xbShort PutLongField( const xbShort, const xbLong );
+ xbShort PutLongField( const char *, const xbLong);
+
+ xbFloat GetFloatField( const char * FieldName );
+ xbFloat GetFloatField( xbShort FieldNo );
+ xbShort PutFloatField( const char *, const xbFloat);
+ xbShort PutFloatField( const xbShort, const xbFloat);
+
+ xbDouble GetDoubleField( const char *);
+ xbDouble GetDoubleField( xbShort, xbShort RecBufSw = 0);
+ xbShort PutDoubleField( const char *, xbDouble);
+ xbShort PutDoubleField( const xbShort, xbDouble);
+
+#ifdef XB_LOCKING_ON
+ xbShort GetLockMode() { return LockMode; }
+ xbShort SetLockMode( xbShort );
+// xbShort OpenXbLockFile();
+// xbShort GetTableLockCnt() { return TableLockCnt; }
+// xbShort LockIndex( xbShort LockType ); /* for XB_XBASE_LOCK_MODE */
+ int GetDbfFileNo() { return fileno( fp ); }
+ int GetMemoFileNo() { return fileno( mfp ); }
+
+#ifdef XB_MEMO_FIELDS
+// xbShort GetMemoLockCnt() { return MemoLockCnt; }
+#endif
+
+/*
+ xbShort LockTable( xbShort LockType );
+ xbShort LockXbaseTable( xbShort LockType );
+ xbShort LockClipperTable( xbShort LockType );
+ xbShort LockFoxproTable( xbShort LockType );
+ xbShort LockDbaseTable( xbShort LockType );
+
+ xbShort LockRecord( xbShort LockType, xbULong RecNo, xbULong RecCnt );
+ xbShort LockXbaseRecord( xbShort LockType, xbULong RecNo, xbULong RecCnt );
+ xbShort LockClipperRecord(
+ xbShort LockType, xbULong RecNo, xbULong RecCnt );
+ xbShort LockFoxproRecord( xbShort LockType, xbULong RecNo, xbULong RecCnt );
+ xbShort LockDbaseRecord( xbShort LockType, xbULong RecNo, xbULong RecCnt );
+
+ xbShort LockDatabase( xbShort, xbShort, xbULong );
+ xbShort ExclusiveLock( xbShort );
+ xbShort ExclusiveUnlock();
+ xbShort LockDatabase( xbShort cmd, xbULong recNo ) { return 0; }
+*/
+
+#ifndef HAVE_FCNTL
+ xbShort UnixToDosLockCommand( xbShort WaitOption,
+ xbShort LockType ) const;
+#endif
+
+#else
+ xbShort LockDatabase( xbShort, xbShort, xbLong )
+ { return XB_NO_ERROR; }
+ xbShort ExclusiveLock( xbShort ) { return XB_NO_ERROR; };
+ xbShort ExclusiveUnlock() { return XB_NO_ERROR; };
+#endif
+
+ //! Turn autolock on
+ /*!
+ */
+ void AutoLockOn() { AutoLock = 1; }
+ //! Turn autolock off
+ /*!
+ */
+ void AutoLockOff() { AutoLock = 0; }
+ //! Return whether or not autolocking is on or off
+ /*!
+ */
+ xbShort GetAutoLock() { return AutoLock; }
+
+#ifdef XB_MEMO_FIELDS
+ xbShort GetMemoField( xbShort FieldNo, xbLong len,
+ char * Buf, xbShort LockOption );
+ xbLong GetMemoFieldLen( xbShort FieldNo );
+ xbShort GetFPTField( xbShort FieldNo, xbLong len,
+ char * Buf, xbShort LockOption );
+ xbLong GetFPTFieldLen( xbShort FieldNo );
+ xbShort UpdateMemoData( xbShort FieldNo, xbLong len,
+ const char * Buf, xbShort LockOption );
+ xbShort MemoFieldExists( xbShort FieldNo ) const;
+ xbShort LockMemoFile( xbShort WaitOption, xbShort LockType );
+
+ xbShort MemoFieldsPresent() const;
+ xbLong CalcLastDataBlock();
+ xbShort FindBlockSetInChain( xbLong BlocksNeeded, xbLong
+ LastDataBlock, xbLong & Location, xbLong &PreviousNode );
+ xbShort GetBlockSetFromChain( xbLong BlocksNeeded, xbLong
+ Location, xbLong PreviousNode );
+ xbString & GetDbtName() { return MemofileName; }
+
+#ifdef XBASE_DEBUG
+ xbShort DumpMemoFreeChain();
+ void DumpMemoHeader() const;
+ void DumpMemoBlock() const;
+#endif
+#endif
+
+ //! Turn on "real" deletes
+ /*!
+ This should be done before creating a database (with
+ xbDbf::CreateDatatabase()) and thereafter before opening
+ a database with xbDbfCreateDatabase().
+
+ You cannot "turn on" real deletes once a database has been created
+ and records added.
+ */
+ void RealDeleteOn() { RealDelete = 1; if(fp) ReadHeader(1); }
+ /*! Turn off "real" deletes
+ */
+ void RealDeleteOff() { RealDelete = 0; if(fp) ReadHeader(1); }
+ //! Return whether "real" deletes are on or off
+ /*!
+ Use this to determine if "real deletes" are being used with
+the database.
+ */
+ xbShort GetRealDelete() { return RealDelete; }
+
+#if defined(XB_INDEX_ANY)
+ xbShort IndexCount();
+ xbIndex *GetIndex(xbShort indexNum);
+#endif
+
+ void Flush();
+ virtual const char* GetExtWithDot( bool lower );
+
+ private:
+
+ xbShort DeleteAll( xbShort );
+ void InitVars();
+ xbShort PackDatafiles(void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0);
+ xbShort ReadHeader( xbShort );
+ xbShort WriteHeader( xbShort );
+
+#ifdef XB_MEMO_FIELDS
+ xbShort AddMemoData( xbShort FieldNo, xbLong Len, const char * Buf );
+ xbShort CreateMemoFile();
+ xbShort DeleteMemoField( xbShort FieldNo );
+ xbShort GetDbtHeader( xbShort Option );
+ xbShort GetMemoBlockSize() { return MemoHeader.BlockSize; }
+ xbShort OpenMemoFile();
+ xbShort OpenFPTFile();
+ xbShort PutMemoData( xbLong StartBlock, xbLong BlocksNeeded,
+ xbLong Len, const char * Buf );
+ xbShort ReadMemoBlock( xbLong BlockNo, xbShort Option);
+ xbShort SetMemoBlockSize( xbShort );
+ xbShort UpdateHeadNextNode() const;
+ xbShort WriteMemoBlock( xbLong BlockNo, xbShort Option );
+ xbShort IsType3Dbt() const { return( Version==(char)0x83 ? 1:0 ); }
+ xbShort IsType4Dbt() const
+ {return (( Version==(char)0x8B || Version==(char)0x8E ) ? 1:0 );}
+ xbShort CreateUniqueDbfName( xbString &, xbString & );
+#endif
+
+
+// xbString DatabaseName;
+ xbShort XFV; /* xBASE file version */
+ xbShort NoOfFields;
+ char DbfStatus; /* 0 = closed
+ 1 = open
+ 2 = updates pending */
+ FILE *fp; /* file pointer */
+ xbSchemaRec *SchemaPtr; /* Pointer to field data */
+ char *RecBuf; /* Pointer to record buffer */
+ char *RecBuf2; /* Pointer to original rec buf */
+
+#ifdef XB_MEMO_FIELDS
+ xbString MemofileName; /* memo file name */
+ FILE *mfp; /* memo file pointer */
+ void *mbb; /* memo block buffer */
+ xbMH MemoHeader; /* memo header structure */
+ xbShort mfield1; /* memo block field one FF */
+ xbShort MStartPos; /* memo start pos of data */
+ xbLong MFieldLen; /* memo length of data */
+ xbLong NextFreeBlock; /* next free block in free chain */
+ xbLong FreeBlockCnt; /* count of free blocks this set */
+ xbLong MNextBlockNo; /* free block chain */
+ xbLong MNoOfFreeBlocks; /* free block chain */
+ xbLong CurMemoBlockNo; /* Current block no loaded */
+#endif
+
+/* Next seven variables are read directly off the database header */
+/* Don't change the order of the following seven items */
+ char Version;
+ char UpdateYY;
+ char UpdateMM;
+ char UpdateDD;
+// xbLong NoOfRecs;
+// xbShort HeaderLen;
+// xbShort RecordLen;
+
+ xbULong NoOfRecs;
+ xbUShort HeaderLen;
+ xbUShort RecordLen;
+
+//#ifdef XB_REAL_DELETE
+ xbULong FirstFreeRec;
+ xbULong RealNumRecs;
+//#endif
+
+// xbIxList * MdxList;
+ xbIxList * NdxList;
+ xbIxList * FreeIxList;
+ xbULong CurRec; /* Current record or zero */
+ xbShort AutoLock; /* Auto update option 0 = off */
+
+//#ifdef XB_REAL_DELETE
+ xbShort RealDelete; /* real delete option 0 = off */
+//#endif
+
+#ifdef XB_LOCKING_ON
+ FILE *xblfh; /* xbase lock file pointer for xbase locking */
+ xbShort LockMode; /* lock mode for this table */
+ xbString lfn; /* xbase lock file name for xbase locking */
+ xbShort TableLockCnt; /* number of table locks */
+ xbShort IndexLockCnt; /* no of index locks XB_XBASE_LOCK_MODE only */
+
+#ifdef XB_MEMO_FIELDS
+ xbShort MemoLockCnt; /* number of memo file locks */
+#endif
+
+ /* old locking stuff */
+ xbShort CurLockType; /* current type of file lock */
+ xbShort CurLockCount; /* number of current file locks */
+ xbULong CurLockedRecNo; /* currently locked record no */
+ xbShort CurRecLockType; /* current type of rec lock held (F_RDLOCK or F_WRLCK) */
+ xbShort CurRecLockCount; /* number of current record locks */
+ xbShort CurMemoLockType; /* current type of memo lock */
+ xbShort CurMemoLockCount; /* number of current memo locks */
+#endif
+
+};
+#endif // __XB_DBF_H__
+
+
diff --git a/xbase64/xbexp.cpp b/xbase64/xbexp.cpp
new file mode 100755
index 0000000..a3e1fa5
--- /dev/null
+++ b/xbase64/xbexp.cpp
@@ -0,0 +1,1323 @@
+/* xbexp.cpp
+
+ Xbase64 project source code
+
+ This file contains logic for handling Xbase expressions.
+
+ Copyright (C) 1997,2003,2004 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbexp.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+#ifdef XB_EXPRESSIONS
+#include <ctype.h>
+#include <string.h>
+
+//#include <xbase64/xbexcept.h>
+
+
+/*! \file xbexp.cpp
+*/
+
+// set the default date format
+//xbString xbExpn::DefaultDateFormat = "MM/DD/YY";
+
+/************************************************************************/
+/* putting this part in EXP did not work */
+
+/* No of parms
+
+ value meaning
+
+ 0 0
+ 1 1
+ 2 2
+ 100 0 or more
+ 101 1 or more
+ 102 2 or more
+
+
+ Return Type
+ N Numeric
+ C Char or string
+ 1 Varies, if sibling 1 is C, set to C, otherwise N
+
+
+*/
+
+
+
+
+
+
+static xbFuncDtl FuncList[] =
+{
+ /* Func # of Return
+ Name parms Type */
+ { "ABS", 1, 'N' },
+ { "ASC", 1, 'N' },
+ { "AT", 2, 'N' },
+ { "CDOW", 1, 'C' },
+ { "CHR", 1, 'C' },
+ { "CMONTH", 1, 'C' },
+ { "CTOD", 1, 'C' },
+ { "DATE", 0, 'C' },
+ { "DAY", 1, 'N' },
+ { "DESCEND", 1, '1' },
+ { "DOW", 1, 'N' },
+ { "DTOC", 1, 'C' },
+ { "DTOS", 1, 'C' },
+ { "EXP", 1, 'N' },
+ { "IIF", 3, 'C' },
+ { "INT", 1, 'N' },
+ { "ISALPHA", 1, 'L' },
+ { "ISLOWER", 1, 'L' },
+ { "ISUPPER", 1, 'L' },
+ { "LEFT", 2, 'C' },
+ { "LEN", 1, 'N' },
+ { "LOG", 1, 'N' },
+ { "LOWER", 1, 'C' },
+ { "LTRIM", 1, 'C' },
+ { "MAX", 2, 'N' },
+ { "MIN", 2, 'N' },
+ { "MONTH", 1, 'N' },
+ { "RECNO", 0, 'N' },
+ { "REPLICATE", 2, 'C' },
+ { "RIGHT", 2, 'C' },
+ { "RTRIM", 1, 'C' },
+ { "SPACE", 1, 'C' },
+ { "SQRT", 1, 'N' },
+ { "STR", 101, 'C' },
+ { "STRZERO", 1, 'C' },
+ { "SUBSTR", 3, 'C' },
+ { "TRIM", 1, 'C' },
+ { "UPPER", 1, 'C' },
+ { "VAL", 1, 'N' },
+ { "YEAR", 1, 'N' },
+ { 0, 0, 0 },
+};
+
+/*************************************************************************/
+//! xbExpn Constructor
+/*!
+*/
+xbExpn::xbExpn( xbXBase * x )
+{
+ xbase = x;
+ TokenType = 0;
+ Tree = 0;
+ TokenLen = 0;
+ OpLen1 = 0;
+ OpLen2 = 0;
+ OpDataLen1 = 0;
+ OpDataLen2 = 0;
+ Op1 = 0;
+ Op2 = 0;
+ First = 0;
+ Last = 0;
+ StackDepth = 0;
+ XbaseFuncList = FuncList;
+ memset( WorkBuf, 0x00, WorkBufMaxLen+1 );
+}
+/*************************************************************************/
+//! xbExpn Destructor
+/*!
+*/
+xbExpn::~xbExpn()
+{
+ InitStack();
+
+ delete Tree;
+
+ if(Op1)
+ free(Op1);
+
+ if(Op2)
+ free(Op2);
+
+}
+
+/*************************************************************************/
+//! Get information on a function.
+/*!
+ Returns the information specifed (Option) for the specified function.
+
+ \param Function name of function to get information about
+ \param Option One of the following:
+ \htmlonly
+ <p>
+ <table border=2><tr><th>Option</th><th>Description</th></tr>
+ <tr><td>1</td><td>Return minimum number of parms</td></tr>
+ <tr><td>2</td><td>Return function result type</td></tr>
+ <tr><td>?</td><td>Return 0 if valid function</td></tr>
+ </table>
+ \endhtmlonly
+ \latexonly
+ \\
+ \\
+ \begin{tabular}{|l|l|} \hline
+ \textbf{Option} & \textbf{Description} \\ \hline \hline
+ 1 & Return minimum number of parms \\ \hline
+ 2 & Return function result type \\ \hline
+ ? & Return 0 if valid function \\ \hline
+ \end{tabular}
+ \endlatexonly
+
+ \returns requested information or -1 on failure.
+*/
+xbShort xbExpn::GetFuncInfo( const char * Function, xbShort Option )
+{
+/* Option =
+ 1 - return minimum number of needed parms
+ 2 - return function result type
+ ? - return 0 if valid function
+*/
+ xbFuncDtl * f;
+ xbShort i, len;
+ const char *s;
+
+ if(( Option<1 )||( Option>2 ))
+ return XB_INVALID_OPTION;
+
+ s = Function;
+ len = 0;
+ while( *s && *s != '(' ) { s++; len++; }
+
+ f = XbaseFuncList;
+ i = 0;
+ while( f[i].FuncName ){
+ if( strncmp( f[i].FuncName, Function, len ) == 0 )
+ return( (Option==1) ? f[i].ParmCnt : f[i].ReturnType );
+ i++;
+ }
+ return -1;
+}
+
+/*************************************************************************/
+//! IsWhiteSpace
+/*!
+*/
+xbShort xbExpn::IsWhiteSpace( char c )
+{
+ return(( c == 0x20 )? 1 : 0 );
+}
+
+/*************************************************************************/
+//! GetNextToken
+/*!
+*/
+xbShort xbExpn::GetNextToken( const char * s, xbShort MaxLen )
+{
+ /* TreeResultType Settings
+ Token Action/
+ Was Type Result
+ Unv N N
+ Unv C C
+ Unv Function Table Lookup
+ Unv Field Field Type
+ Not L Any Logical L
+ */
+
+ xbShort Wctr, Wtype, Wsw, EmptyCtr, MaxCtr, MaxCtrSave;
+ const char *sp, *np, *pp; /* save, next and previous pointer */
+
+ LogicalType = 0;
+ TokenType = 0;
+ TokenLen = 0;
+ EmptyCtr = 0;
+ MaxCtr = 0;
+
+ if( !s || ! *s )
+ return XB_NO_DATA;
+
+ /* go past any initial white space */
+ while( s && *s && IsWhiteSpace( *s )){
+ s++;
+ MaxCtr++;
+ if (MaxCtr >= MaxLen)
+ return XB_NO_ERROR;
+ }
+
+/* 1 - check for parens */
+/* '(', if found go to corresponding ')', if no ')', return -1 */
+ if( *s == '(' || *s == '{' ){
+ if( *s == '{' ) Wtype = 0; else Wtype = 1;
+ Wctr = 1;
+ s++;
+
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ while( s && *s ){
+ if(( *s == ')' && Wtype == 1 ) || (*s == '}' && Wtype == 0 )){
+ Wctr--;
+ if( Wctr == 0 ){
+ if( EmptyCtr != 0 ) {
+ TokenType = 'E';
+ PreviousType = 'E';
+ } else
+ return XB_PARSE_ERROR;
+
+ TokenLen += 2;
+ return XB_NO_ERROR;
+ }
+ }
+ else if(( *s == '(' && Wtype == 1 ) || (*s == '{' && Wtype == 0 )){
+ Wctr++;
+ EmptyCtr++;
+ } else if( *s != ' ' )
+ EmptyCtr++;
+
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+ }
+ return XB_PARSE_ERROR;
+ }
+
+
+/* 2 - Check for Constants */
+/* check for "'" or """, if no corresponding quote return -1 */
+ if( *s == '"' || *s == '\'' ){
+ if( *s == '"' ) Wtype = 0; else Wtype = 1;
+ TokenType = 'C'; /* set to constant */
+ PreviousType = 'C';
+ s++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_NO_ERROR;
+ while( s && *s ){
+ if(( *s == '"' && Wtype == 0 ) || (*s == '\'' && Wtype == 1 ))
+ return XB_NO_ERROR;
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_NO_ERROR;
+ }
+ return XB_PARSE_ERROR;
+ }
+
+
+/* 3 - check for .T. .F. .TRUE. or .FALSE. */
+ if( s && *s && *s == '.' ){
+ if(( strncmp( s, ".T.", 3 ) == 0 ) || ( strncmp( s, ".F.", 3 ) == 0 )){
+ TokenLen = 3;
+ TokenType = 'C'; /* constant */
+ PreviousType = 'C';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ } else if( strncmp( s, ".TRUE.", 6 ) == 0 ){
+ TokenLen = 6;
+ TokenType = 'C'; /* constant */
+ PreviousType = 'C';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ } else if( strncmp( s, ".FALSE.", 7 ) == 0 ){
+ TokenLen = 7;
+ TokenType = 'C'; /* constant */
+ PreviousType = 'C';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ }
+ }
+
+/* 4 - check for positive, negative or decimal number constants */
+ if(( *s == '-' && ( PreviousType == 'O' || PreviousType == 0 )) ||
+ ( *s == '+' && ( PreviousType == 'O' || PreviousType == 0 )) ||
+ *s == '.' || isdigit( *s )){
+ sp = s;
+ MaxCtrSave = MaxCtr;
+ Wsw = Wctr = 0;
+ if( *s == '.' ){
+ Wctr++;
+ s++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ if( s && *s && isdigit( *s ))
+ TokenLen++;
+ else
+ Wsw++;
+ } else if( *s == '-' ){
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ /* go past any white space between sign and number */
+ while( s && *s && IsWhiteSpace( *s )){
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+ }
+ }
+
+ if( isdigit( *s ) || (*s == '.' && !Wsw )){
+ while(s && *s && ((*s == '.' && Wctr < 2 ) || isdigit(*s)) && !Wsw ){
+ if( *s == '.' ) {
+ Wctr++;
+ if( Wctr > 1 ) break;
+ s++;
+
+ MaxCtr++;
+ if( MaxCtr >= MaxLen ){
+ TokenType = 'N';
+ PreviousType = 'N';
+ return XB_NO_ERROR;
+ }
+
+ if( s && *s && isdigit( *s ))
+ TokenLen++;
+ else
+ Wsw++;
+ } else {
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen ) {
+ TokenType = 'N';
+ PreviousType = 'N';
+ return XB_NO_ERROR;
+ }
+ }
+ }
+ TokenType = 'N'; /* constant */
+ PreviousType = 'N';
+ return XB_NO_ERROR;
+ } else {
+ s = sp;
+ MaxCtr = MaxCtrSave;
+ }
+ }
+
+/* 5 - Check for operators */
+ if( *s == '+' || *s == '-' || *s == '/' || *s == '^'){
+ TokenLen = 1;
+ TokenType = 'O';
+ PreviousType = 'O';
+ return XB_NO_ERROR;
+ }
+ if(*s == '=' || *s == '$' || *s == '#' ){
+ LogicalType = 1;
+ TokenLen = 1;
+ TokenType = 'O';
+ PreviousType = 'O';
+ return XB_NO_ERROR;
+ }
+ if( strncmp( s, "!=", 2 ) == 0 ){
+ LogicalType = 1;
+ TokenLen = 2;
+ TokenType = 'O';
+ PreviousType = 'O';
+ return XB_NO_ERROR;
+ }
+ if( *s == '*' ){
+ s++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ TokenType = 'O';
+ PreviousType = 'O';
+ if( *s == '*' ){
+ TokenLen = 2;
+ return XB_NO_ERROR;
+ } else {
+ TokenLen = 1;
+ return XB_NO_ERROR;
+ }
+ }
+ if( *s == '<' || *s == '>' ) {
+ s++;
+
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ LogicalType = 1; // added 3/25/00 dtb
+ TokenType = 'O';
+ PreviousType = 'O';
+ if( *s == '<' || *s == '>' || *s == '=' ){
+ TokenLen = 2;
+ return XB_NO_ERROR;
+ } else {
+ TokenLen = 1;
+ return XB_NO_ERROR;
+ }
+ }
+
+/* check for .NOT. .OR. .AND. */
+
+ if( s && *s && *s == '.' ){
+ if( strncmp( s, ".NOT.", 5 ) == 0 ){
+ TokenLen = 5;
+ TokenType = 'O'; /* constant */
+ PreviousType = 'O';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ } else if( strncmp( s, ".AND.", 5 ) == 0 ){
+ TokenLen = 5;
+ TokenType = 'O'; /* constant */
+ PreviousType = 'O';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ } else if( strncmp( s, ".OR.", 4 ) == 0 ){
+ TokenLen = 4;
+ TokenType = 'O'; /* constant */
+ PreviousType = 'O';
+ LogicalType = 1;
+ return XB_NO_ERROR;
+ }
+ }
+
+ /* If get this far, must be function or database field */
+ while( s && *s ){
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen ) {
+ TokenType = 'D';
+ PreviousType = 'D';
+ return XB_NO_ERROR;
+ }
+
+ if( s && *s && *s == '(' ) {
+ /* look for corresponding ) */
+ Wctr = 1;
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+
+ while( s && *s ) {
+ if( *s == ')' ) {
+ Wctr--;
+ if( !Wctr ) {
+ TokenType = 'F'; /* function */
+ PreviousType = 'F';
+ TokenLen++;
+ return XB_NO_ERROR;
+ }
+ }
+ if( *s == '(' ) Wctr++;
+ s++;
+ TokenLen++;
+ MaxCtr++;
+ if( MaxCtr >= MaxLen )
+ return XB_PARSE_ERROR;
+ }
+ return XB_PARSE_ERROR;
+ } else {
+ np = s + 1;
+ pp = s - 1;
+ if( !s || !*s || (IsSeparator( *s ) &&
+ !(*s == '-' && *np == '>' ) && !(*s == '>' && *pp == '-' ))) {
+ if( TokenLen > 0 ){
+ TokenType = 'D'; /* database field */
+ PreviousType = 'D';
+ return XB_NO_ERROR;
+ }
+ }
+ }
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! IsSeparator
+/*!
+*/
+char xbExpn::IsSeparator( char c )
+{
+ if( c == '-' || c == '+' || c == '*' || c == '/' || c == '$' ||
+ c == ' ' || c == '#' || c == '<' || c == '>' || c == '^' ||
+ c == '=' || c == '.' || c == '!' /* || c == ')' */ )
+ return c;
+ else
+ return 0;
+}
+
+/*************************************************************************/
+//! GetExpNode
+/*!
+*/
+/*
+xbExpNode * xbExpn::GetExpNode(xbShort Len) {
+ xbExpNode * Temp;
+
+ Temp = new xbExpNode;
+ if( Temp && Len > 0 )
+ Temp->ResultLen = Len;
+ return Temp;
+}
+*/
+/*************************************************************************/
+//! LoadExpNode
+/*!
+*/
+xbExpNode * xbExpn::LoadExpNode(
+ const char *ENodeText, /* pointer to text data */
+ const char EType, /* Operand type */
+ const xbShort ELen, /* length of node text data */
+ const xbShort BufLen ) /* length needed in the buffer*/
+{
+// xbExpNode * CurNode;
+// if(( CurNode = GetExpNode(BufLen)) == NULL ) return NULL;
+
+ xbExpNode * CurNode = new xbExpNode;
+ if( !CurNode )
+ return NULL;
+ CurNode->ResultLen = BufLen;
+
+ CurNode->NodeText = strdup( ENodeText );
+ CurNode->Type = EType;
+ CurNode->Len = ELen;
+ CurNode->InTree = 1;
+ CurNode->ResultLen = BufLen;
+ return CurNode;
+}
+
+/*************************************************************************/
+//! BuildExpressionTree
+/*!
+*/
+xbShort xbExpn::BuildExpressionTree( const char * Expression,
+ xbShort MaxTokenLen, xbDbf * d )
+{
+ /* previous node is the node to insert under */
+ xbExpNode * CurNode = 0;
+ xbExpNode * PreviousNode;
+ xbShort rc, FieldNo=0, BufLen;
+ xbShort TokenLenCtr;
+ char c;
+ const char *p;
+ char TempField[11];
+ char TableName[31];
+ xbDbf * TempDbf=0;
+ int LocTokenLen;
+
+ if( Tree ) {
+ delete Tree;
+ Tree = NULL;
+ }
+
+ p = Expression;
+ PreviousNode = NULL;
+ PreviousType = TokenLenCtr = 0;
+
+ while( IsWhiteSpace( *p )) {
+ p++;
+ TokenLenCtr++;
+ if(TokenLenCtr >= MaxTokenLen)
+ return XB_NO_ERROR;
+ }
+
+ rc = GetNextToken( p, MaxTokenLen-TokenLenCtr );
+ LocTokenLen = TokenLen;
+ if( rc != XB_NO_DATA && rc != XB_NO_ERROR )
+ return rc;
+
+ while( rc == 0 ){
+ if( TokenType == 'D' && d ){
+ if( TokenLen > 30 )
+ strncpy( TableName, p, 30 );
+ else
+ strncpy( TableName, p, TokenLen );
+
+ memset( TempField, 0x00, 11 );
+
+ if( strstr( p, "->" ) != NULL ) {
+ if(( TempDbf = d->xbase->GetDbfPtr( TableName )) == NULL )
+ return XB_INVALID_FIELD;
+ xbShort tlen = 0;
+ while( TableName[tlen] != '-' && TableName[tlen+1] != '>' )
+ tlen++;
+ tlen = TokenLen - tlen - 2; // length of field name
+ const char * fp = strstr( p, "->" );
+ fp += 2; // ptr to beginning of field name
+ strncpy( TempField, fp, tlen );
+ } else {
+ TempDbf = d;
+ if( TokenLen > 10 )
+ return XB_INVALID_FIELD;
+ strncpy( TempField, p, TokenLen );
+ }
+ if(( FieldNo = TempDbf->GetFieldNo( TempField )) == -1 )
+ return XB_INVALID_FIELD;
+ BufLen = TempDbf->GetFieldLen( FieldNo ) + 1;
+ }
+ else if( TokenType == 'C' || TokenType == 'N' )
+ BufLen = TokenLen + 1;
+ else
+ BufLen = 0;
+
+ if( TokenType == 'C' ) p++; /* go past first ' */
+
+ if( TokenType != 'O' ){
+ if( !Tree ) { /* create root node with this token */
+ CurNode = LoadExpNode( p, TokenType, TokenLen, BufLen );
+ Tree = CurNode;
+ } else { /* put as child 2 of previous node */
+ CurNode = LoadExpNode( p, TokenType, TokenLen, BufLen );
+ PreviousNode->Sibling2 = CurNode;
+ CurNode->Node = PreviousNode;
+ }
+
+ if( TokenType == 'E' ){
+ if((rc=ReduceComplexExpression(p,TokenLen,CurNode,d))!=0)
+ return rc;
+ if(PreviousNode)
+ CurNode = PreviousNode->Sibling2;
+ else
+ CurNode = Tree;
+ } else if( TokenType == 'F' ){
+ if(( rc = ReduceFunction( p, CurNode, d)) != 0 )
+ return rc;
+
+ xbShort parmCnt = GetFuncInfo( p, 1 );
+ if( (parmCnt == 1 || parmCnt == 101 ) && !CurNode->Sibling1 ||
+ (parmCnt == 2 || parmCnt == 201 ) && !CurNode->Sibling2 ||
+ (parmCnt == 3 ) && !CurNode->Sibling3 )
+ return XB_INSUFFICIENT_PARMS;
+ else if( parmCnt == 0 && CurNode->Sibling1 )
+ return XB_TOO_MANY_PARMS;
+ else if( parmCnt == 1 && CurNode->Sibling2 )
+ return XB_TOO_MANY_PARMS;
+ else if( parmCnt == 2 && CurNode->Sibling3 )
+ return XB_TOO_MANY_PARMS;
+
+ CurNode->ExpressionType = GetFuncInfo( p, 2 );
+ if( CurNode->ExpressionType == '1' ){
+ if( CurNode->Sibling1 )
+ if( CurNode->Sibling1->ExpressionType == 'C' )
+ CurNode->ExpressionType = 'C';
+ else
+ CurNode->ExpressionType = 'N';
+ else
+ return XB_INSUFFICIENT_PARMS;
+ }
+
+ CurNode->dbf = d;
+ }
+ else if( TokenType == 'D' && d ) {
+ CurNode->DataLen = BufLen - 1;
+ CurNode->FieldNo = FieldNo;
+ CurNode->dbf = TempDbf;
+ c = TempDbf->GetFieldType( FieldNo );
+ if( c == 'C' || c == 'M' ) CurNode->ExpressionType = 'C';
+ else if( c == 'L' ) CurNode->ExpressionType = 'L';
+ else if( c == 'N' || c == 'F' ) CurNode->ExpressionType = 'N';
+ else if( c == 'D' ) CurNode->ExpressionType = 'D';
+ } else if( TokenType == 'C' || TokenType == 'N' ) {
+ CurNode->DataLen = CurNode->Len;
+ CurNode->StringResult = CurNode->NodeText;
+ CurNode->StringResult.resize( CurNode->DataLen+1 );
+ if( TokenType == 'N' ) {
+ CurNode->DoubResult = strtod( CurNode->StringResult, 0 );
+ CurNode->ExpressionType = 'N';
+ } else
+ CurNode->ExpressionType = 'C';
+ }
+ }
+ else /* it is an operator */
+ {
+ if(!Tree){
+ if(*p == '-'){
+ CurNode = LoadExpNode( p, TokenType, TokenLen, 0 );
+ CurNode->ExpressionType = 'C';
+ } else
+ return XB_EXP_SYNTAX_ERROR;
+ } else {
+ if( Tree->Type != 'O' ){
+ CurNode = LoadExpNode( p, TokenType, TokenLen, 0 );
+ Tree->Node = CurNode; /* link the new parent to old tree */
+ CurNode->Sibling1 = Tree; /* connect the sibling */
+ Tree = CurNode; /* root in tree */
+ } else {
+ PreviousNode = CurNode->Node;
+ CurNode = LoadExpNode( p, TokenType, TokenLen, 0 );
+ while( PreviousNode &&
+ (( OperatorWeight( PreviousNode->NodeText, TokenLen ) == 0 ) ||
+ ( OperatorWeight( CurNode->NodeText, TokenLen ) <=
+ OperatorWeight( PreviousNode->NodeText, TokenLen ))))
+ PreviousNode = PreviousNode->Node;
+
+ if( PreviousNode ) {
+ CurNode->Node = PreviousNode;
+ CurNode->Sibling1 = PreviousNode->Sibling2;
+ PreviousNode->Sibling2 = CurNode;
+ CurNode->Sibling1->Node = CurNode;
+ } else { /* insert at root */
+ CurNode->Sibling1 = Tree;
+ Tree = CurNode;
+ CurNode->Sibling1->Node = CurNode;
+ }
+ }
+ if( LogicalType )
+ CurNode->ExpressionType = 'L';
+ }
+ }
+ PreviousNode = CurNode;
+// p += CurNode->Len; // 2/20/04 - not sure when this was updated - gk
+ p += LocTokenLen;
+
+// if( TokenType == 'C' ) { gk - 2/20/04 func("fff") + 4 didn't work
+ if( TokenType == 'C' && CurNode->Type != 'F' ){
+ p++; /* go past last ' */
+ TokenLenCtr+=2; /* add the quotes */
+ }
+
+// TokenLenCtr += CurNode->Len; // 2/20/04 - not sure when this was updated - gk
+ TokenLenCtr += LocTokenLen;
+ if( TokenLenCtr >= MaxTokenLen )
+ return XB_NO_ERROR;
+ if( p && *p && TokenType == 'E' ) {
+ p++;
+ TokenLenCtr++;
+ }
+
+ while( IsWhiteSpace( *p )) {
+ p++;
+ TokenLenCtr++;
+ if( TokenLenCtr >= MaxTokenLen )
+ return XB_NO_ERROR;
+ }
+ rc = GetNextToken( p, MaxTokenLen-TokenLenCtr );
+ LocTokenLen = TokenLen;
+ if( rc != XB_NO_DATA && rc != XB_NO_ERROR )
+ return rc;
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! GetExpressionResultType
+/*!
+*/
+char xbExpn::GetExpressionResultType( xbExpNode * e ) {
+ xbExpNode * Temp = 0;
+ if( e )
+ Temp = e;
+ else if( !Temp )
+ Temp = Tree;
+ else
+ return 0;
+
+ if( e->Type == 'O' &&
+ ( *e->NodeText == '<' || *e->NodeText == '>' || *e->NodeText == '=' ||
+ *e->NodeText == '#' || *e->NodeText == '$' ||
+ strncmp( e->NodeText, "!=", 2 ) == 0 ))
+ return 'L';
+
+ /* go down to second lowest level */
+ while( Temp && Temp->Sibling1 && Temp->Sibling1->Sibling1 )
+ Temp = Temp->Sibling1;
+
+ /* if subtracting dates, return numeric type */
+ if( Temp->Type == 'O' && *Temp->NodeText == '-' &&
+ Temp->Sibling1 && Temp->Sibling2 &&
+ Temp->Sibling1->ExpressionType == 'D' &&
+ Temp->Sibling2->ExpressionType == 'D' )
+ return 'N';
+
+ /* else return the type of the lowest left node */
+ while( Temp && !Temp->ExpressionType && Temp->Sibling1 )
+ Temp = Temp->Sibling1;
+ return Temp->ExpressionType;
+}
+/*************************************************************************/
+//! GetExpressionHandle
+/*!
+*/
+xbExpNode * xbExpn::GetExpressionHandle() {
+ xbExpNode * e;
+ e = Tree;
+ Tree = NULL;
+ return e;
+}
+/*************************************************************************/
+//! OperatorWeight
+/*! This function determines the priority of an operator
+*/
+xbShort xbExpn::OperatorWeight( const char * Oper, xbShort len )
+{
+ /* operator precendence
+
+ not all are implemented yet, but the structure is here
+
+ 10 .AND. .OR. .NOT. (not really an operator)
+ 9 > or < (includes <= or >=)
+ 6 unary plus or minus (+,-)
+ 5 prefix increment and/or decrement (++,--)
+ 4 exponentiation ** or ^
+ 3 multiplication,division or modulus (*,/,%)
+ 2 Addition, subtraction (+,-)
+ 1 Postfix increment and/or decrement (++,--)
+ */
+
+ if( len < 1 || len > 5 ) return 0;
+
+
+ if( Oper[0] == '>' || Oper[0] == '<' )
+ return 13;
+
+ if( strncmp( Oper, ".AND.", 5 ) == 0 ||
+ strncmp( Oper, ".OR.", 4 ) == 0 ||
+ strncmp( Oper, ".NOT.", 5 ))
+ return 10;
+
+ if( strncmp( Oper, "**", 2 ) == 0 || Oper[0] == '^' )
+ return 4;
+
+ if( Oper[0] == '*' || Oper[0] == '/' || Oper[0] == '%' )
+ return 3;
+
+ if( Oper[0] == '+' || Oper[0] == '-' )
+ return 1;
+
+ return 0;
+}
+/*************************************************************************/
+//! ReduceComplexExpression
+/*!
+*/
+xbShort xbExpn::ReduceComplexExpression(const char *NextToken, xbShort Len,
+ xbExpNode *cn, xbDbf *d) {
+ const char *p;
+ xbShort rc;
+ xbExpNode * SaveTree;
+
+ SaveTree = Tree;
+ Tree = NULL;
+
+ p = NextToken;
+ p++;
+
+ if(( rc = BuildExpressionTree( p, Len-2, d )) != XB_NO_ERROR )
+ return rc;
+
+ if(cn->Node) { /* then this is the base tree */
+ cn->Node->Sibling2 = Tree;
+ Tree->Node = cn->Node;
+ delete cn;
+ Tree = SaveTree;
+ } else
+ delete cn;
+
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! GetFunctionTokenLen
+/*!
+*/
+xbShort xbExpn::GetFunctionTokenLen( const char * s )
+{
+ xbShort cnt, LeftParenCtr;
+ const char *p;
+
+ cnt = LeftParenCtr = 0;
+ p = s;
+
+ while( p && ( *p != ',' || ( *p == ',' && LeftParenCtr > 0 )) &&
+ !( LeftParenCtr == 0 && *p == ')')) {
+ if( *p == '(' )
+ LeftParenCtr++;
+ else if( *p == ')' )
+ LeftParenCtr--;
+ p++;
+ cnt++;
+ }
+ return cnt;
+}
+/*************************************************************************/
+//! ReduceFunction
+/*!
+*/
+xbShort xbExpn::ReduceFunction(const char *NextToken, xbExpNode *cn, xbDbf *d)
+{
+ const char *p;
+ xbShort rc;
+ xbShort FuncTokenLen;
+ xbExpNode * SaveTree;
+
+ p = strchr( NextToken, '(' );
+ if (!p)
+ return XB_PARSE_ERROR;
+
+ p++;
+ while( IsWhiteSpace( *p )) p++;
+ if (*p == ')')
+ return XB_NO_ERROR;
+
+ /* do function paramater 1 */
+ FuncTokenLen = GetFunctionTokenLen( p );
+ SaveTree = Tree;
+ Tree = NULL;
+ if(( rc = BuildExpressionTree( p, FuncTokenLen, d )) != XB_NO_ERROR )
+ return rc;
+ cn->Sibling1 = Tree;
+ Tree->Node = cn;
+ Tree = SaveTree;
+
+ /* do function paramater 2 */
+
+ p += FuncTokenLen;
+ while( IsWhiteSpace( *p )) p++;
+ if(*p == ')')
+ return XB_NO_ERROR;
+ if( *p != ',' )
+ return XB_PARSE_ERROR;
+
+ p++;
+ while( IsWhiteSpace( *p )) p++;
+ FuncTokenLen = GetFunctionTokenLen( p );
+ SaveTree = Tree;
+ Tree = NULL;
+ if(( rc = BuildExpressionTree( p, FuncTokenLen, d )) != XB_NO_ERROR )
+ return rc;
+
+ cn->Sibling2 = Tree;
+ Tree->Node = cn;
+ Tree = SaveTree;
+
+ /* do function paramater 3 */
+ p += FuncTokenLen;
+ while( IsWhiteSpace( *p )) p++;
+ if (*p == ')')
+ return XB_NO_ERROR;
+ if( *p != ',' )
+ return XB_PARSE_ERROR;
+
+ p++;
+ while( IsWhiteSpace( *p )) p++;
+ FuncTokenLen = GetFunctionTokenLen( p );
+ SaveTree = Tree;
+ Tree = NULL;
+ if(( rc = BuildExpressionTree( p, FuncTokenLen, d )) != XB_NO_ERROR )
+ return rc;
+
+ cn->Sibling3 = Tree;
+ Tree->Node = cn;
+ Tree = SaveTree;
+
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! ParseExpression
+/*!
+*/
+xbShort xbExpn::ParseExpression(const char *exp, xbDbf *d) {
+ return BuildExpressionTree(exp, strlen(exp), d);
+}
+/*************************************************************************/
+//! ProcessExpression
+/*!
+*/
+xbShort xbExpn::ProcessExpression(const char *e, xbDbf *d) {
+ xbShort rc;
+ if(( rc = BuildExpressionTree( e, strlen( e ), d )) != XB_NO_ERROR )
+ return rc;
+ if(( rc = ProcessExpression( Tree )) != XB_NO_ERROR )
+ return rc;
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+#ifdef XBASE_DEBUG
+//! DumpExpressionTree
+/*!
+*/
+void xbExpn::DumpExpressionTree( xbExpNode * E, xbShort printOption )
+{
+ if( !E ) E = Tree;
+ if( !E ) return;
+ DumpExpNode( E, printOption );
+
+ if( E->Sibling1 ) DumpExpressionTree( E->Sibling1, printOption );
+ if( E->Sibling2 ) DumpExpressionTree( E->Sibling2, printOption );
+ if( E->Sibling3 ) DumpExpressionTree( E->Sibling3, printOption );
+ return;
+}
+/*************************************************************************/
+//! DumpExpNode
+/*!
+*/
+void xbExpn::DumpExpNode(xbExpNode *e, xbShort printOption)
+{
+ xbString ntext;
+
+ ntext = e->NodeText;
+ ntext.resize( e->Len + 1 );
+
+ if( printOption ){
+ FILE * dmp;
+ if(( dmp = fopen( "xbase64.log", "a" )) == NULL )
+ return;
+
+ fprintf( dmp, "******* Exp Node *******\n" );
+ fprintf( dmp, "Exp Node Address = %x\n", e );
+ fprintf( dmp, "Node Text = %s\n", ntext.getData());
+ fprintf( dmp, "Type = %c\n", e->Type );
+ fprintf( dmp, "Len = %d\n", e->Len );
+ fprintf( dmp, "InTree = %d\n", e->InTree );
+ fprintf( dmp, "Field No = %d\n", e->FieldNo );
+ fprintf( dmp, "ExpressionType = %c\n", e->ExpressionType );
+ fprintf( dmp, "StringResult = %s\n", e->StringResult.getData());
+ fprintf( dmp, "DoubResult = %d\n", e->DoubResult );
+ fprintf( dmp, "IntResult = %d\n", e->IntResult );
+ fprintf( dmp, "ResultLen = %d\n", e->ResultLen );
+ fprintf( dmp, "DataLen = %x\n", e->DataLen );
+
+ if( e->Node )
+ fprintf( dmp, "Parent = %x\n", e->Node );
+ if( e->Sibling1 )
+ fprintf( dmp, "Sibling 1 = %x\n", e->Sibling1 );
+ if( e->Sibling2 )
+ fprintf( dmp, "Sibling 2 = %x\n", e->Sibling2 );
+ if( e->Sibling3 )
+ fprintf( dmp, "Sibling 3 = %x\n", e->Sibling3 );
+ fprintf( dmp, "\n" );
+ fclose( dmp );
+ }
+ else
+ {
+ std::cout << "****** Exp Node ******";
+ std::cout << "Exp Node Address = " << e << std::endl;
+ std::cout << "Node Text = " << ntext << std::endl;
+ std::cout << "Type = " << e->Type << std::endl;
+ std::cout << "Len = " << e->Len << std::endl;
+ std::cout << "InTree = " << e->InTree << std::endl;
+ std::cout << "Field No = " << e->FieldNo << std::endl;
+ std::cout << "ExpressionType = " << e->ExpressionType << std::endl;
+ std::cout << "StringResult = " << e->StringResult << std::endl;
+ std::cout << "DoubResult = " << e->DoubResult << std::endl;
+ std::cout << "IntResult = " << e->IntResult << std::endl;
+ std::cout << "ResultLen = " << e->ResultLen << std::endl;
+ std::cout << "DataLen = " << e->DataLen << std::endl;
+ if( e->Node )
+ std::cout << "Parent = " << e->Node << std::endl;
+ if( e->Sibling1 )
+ std::cout << "Sibling 1 = " << e->Sibling1 << std::endl;
+ if( e->Sibling2 )
+ std::cout << "Sibling 2 = " << e->Sibling2 << std::endl;
+ if( e->Sibling3 )
+ std::cout << "Sibling3 = " << e->Sibling3 << std::endl;
+ }
+ return;
+}
+#endif
+
+/*************************************************************************/
+//! xbExpNode()
+/*!
+*/
+xbExpNode::xbExpNode() :
+ NodeText(0),
+ Type(0),
+ Len(0),
+ InTree(0),
+ Node(0),
+ Sibling1(0),
+ Sibling2(0),
+ Sibling3(0),
+ DataLen(0),
+ ResultLen(0),
+ DoubResult(0),
+ IntResult(0),
+ dbf(0),
+ FieldNo(-1),
+ ExpressionType(0)
+{
+}
+/*************************************************************************/
+//! ~xbExpNode()
+/*!
+*/
+xbExpNode::~xbExpNode()
+{
+ if(NodeText)
+ free(NodeText);
+
+ if(Sibling1)
+ delete Sibling1;
+
+ if(Sibling2)
+ delete Sibling2;
+
+ if(Sibling3)
+ delete Sibling3;
+}
+/*************************************************************************/
+//! Constructor.
+/*!
+*/
+xbStackElement::xbStackElement()
+{
+ Next = 0;
+ Previous = 0;
+ NodePtr = 0;
+}
+/*************************************************************************/
+//! Destructor.
+/*!
+*/
+xbStackElement::~xbStackElement()
+{
+}
+/*************************************************************************/
+
+//! Destructor.
+/*!
+*/
+
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+void xbExpn::InitStack()
+{
+ xbStackElement *next;
+
+ while(First){
+ next = First->Next;
+
+ if( First->NodePtr->InTree == 0 )
+ delete First->NodePtr;
+
+ delete First;
+ First = next;
+ }
+
+ Last = 0;
+ StackDepth = 0;
+ return;
+}
+/*************************************************************************/
+//! Push a value onto the stack.
+/*!
+ \param p
+*/
+xbShort xbExpn::Push( xbExpNode *p )
+{
+ xbStackElement *Temp = new xbStackElement;
+
+ if(!Temp)
+ return XB_NO_MEMORY;
+
+ Temp->NodePtr = p;
+
+ if( !First ){
+ First = Temp;
+ Last = Temp;
+ StackDepth = 1;
+ } else {
+ Last->Next = Temp;
+ Temp->Previous = Last;
+ Last = Temp;
+ StackDepth++;
+ }
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! Pop the top value from the stack.
+/*!
+*/
+xbExpNode * xbExpn::Pop()
+{
+ xbExpNode *p;
+ xbStackElement *Save;
+
+ if( StackDepth == 0 )
+ return 0;
+ else {
+ p = Last->NodePtr;
+ if( StackDepth == 1 ){
+ delete First;
+ First = 0;
+ Last = 0;
+ } else { /* number of items in Stack must be > 1 */
+ Last->Previous->Next = 0;
+ Save = Last;
+ Last = Last->Previous;
+ delete Save;
+ }
+ StackDepth--;
+ return p;
+ }
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+#ifdef XBASE_DEBUG
+void xbExpn::DumpStack()
+{
+ xbStackElement * e;
+ if( StackDepth == 0 ){
+ std::cout << "\nStack is empty...";
+ return;
+ }
+
+ std::cout << "\nThere are " << StackDepth << " entries.";
+ std::cout << "\nFirst = " << First << " Last = " << Last;
+
+ e = First;
+ while( e ){
+ std::cout << "\n*****************************";
+ std::cout << "\nThis = " << e;
+ std::cout << "\nNext = " << e->Next;
+ std::cout << "\nPrevious = " << e->Previous;
+ std::cout << "\nNode Ptr = " << e->NodePtr;
+ e = e->Next;
+ }
+ return;
+}
+#endif // XB_EXPRESSIONS
+#endif
+/*************************************************************************/
diff --git a/xbase64/xbexp.h b/xbase64/xbexp.h
new file mode 100755
index 0000000..ec769a9
--- /dev/null
+++ b/xbase64/xbexp.h
@@ -0,0 +1,290 @@
+/* xbexp.h
+
+ Xbase64 project source code
+
+ This file contains a header file for the EXP object, which is
+ used for expression processing.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_EXP_H__
+#define __XB_EXP_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#include <xbase64/xbase64.h>
+
+#ifdef XB_EXPRESSIONS /* compile if expression logic on */
+#include <xbase64/xbtypes.h>
+
+/*! \file xbexp.h
+*/
+
+#undef ABS
+#undef MIN
+#undef MAX
+
+class XBDLLEXPORT xbDbf;
+
+/************************************************************************/
+//! xbFuncDtl struct
+/*! This structure defines function information
+*/
+
+struct XBDLLEXPORT xbFuncDtl {
+ const char * FuncName; /* function name */
+ xbShort ParmCnt; /* no of parms it needs */
+ char ReturnType; /* return type of function */
+ void (*ExpFuncPtr)(); /* pointer to function routine */
+};
+
+/************************************************************************/
+//! xbExpNode struct
+/*! This class defines a node within a tree of nodes, each token
+ in an expression gets placed onto its own node
+*/
+
+class XBDLLEXPORT xbExpNode {
+ public:
+ xbExpNode();
+ virtual ~xbExpNode();
+
+ public:
+ char * NodeText; /* expression text */
+ char Type; /* same as TokenType below */
+ xbShort Len; /* length of expression text */
+ xbShort InTree; /* this node in the tree? 1=yes */
+ xbExpNode * Node; /* pointer to parent */
+ xbExpNode * Sibling1; /* pointer to sibling 1 */
+ xbExpNode * Sibling2; /* pointer to sibling 2 */
+ xbExpNode * Sibling3; /* pointe/r to sibling 3 */
+ xbShort DataLen; /* length of data in result buffer */
+ xbShort ResultLen; /* length of result buffer */
+ xbString StringResult; /* string result */
+ xbDouble DoubResult; /* Numeric Result */
+ xbShort IntResult; /* logical result */
+ xbDbf * dbf; /* pointer to datafile */
+ xbShort FieldNo; /* field no if DBF field */
+ char ExpressionType; /* used in head node C,N,L or D */
+};
+/************************************************************************/
+//! xbStackElement class
+/*!
+*/
+
+class XBDLLEXPORT xbStackElement
+{
+ public:
+ xbStackElement();
+ ~xbStackElement();
+
+ friend class xbExpn;
+
+ private:
+ xbStackElement *Previous;
+ xbStackElement *Next;
+ xbExpNode *NodePtr;
+};
+/************************************************************************/
+//! xbExpn class
+/*! This class is used for processing expressions
+*/
+
+/* Expression handler */
+
+class XBDLLEXPORT xbExpn{
+ public:
+ xbExpn( xbXBase * );
+ virtual ~xbExpn();
+
+ xbShort GetNextToken( const char *s, xbShort MaxLen );
+ xbShort ProcessExpression( xbExpNode *n, xbShort );
+ xbShort ProcessExpression( xbShort opt )
+ { return ProcessExpression( Tree, opt ); }
+
+ xbExpNode * GetTree() { return Tree; }
+ void SetTreeToNull() { Tree = NULL; }
+ xbExpNode * GetFirstTreeNode( xbExpNode * );
+ xbExpNode * GetFirstTreeNode()
+ { return GetFirstTreeNode( Tree ); }
+ xbShort ProcessExpression( const char *exp, xbDbf * d );
+ xbShort ParseExpression( const char *exp, xbDbf * d );
+ xbExpNode * GetExpressionHandle();
+ char GetExpressionResultType( xbExpNode * );
+ char GetExpressionResultType()
+ { return GetExpressionResultType( Tree ); }
+ char * GetCharResult();
+ xbString & GetStringResult();
+ xbDouble GetDoubleResult();
+ xbLong GetIntResult();
+ xbShort ProcessExpression( xbExpNode * );
+ xbShort ProcessExpression() { return ProcessExpression( Tree ); }
+ xbShort BuildExpressionTree( const char * Expression, xbShort MaxTokenLen,
+ xbDbf *d );
+
+ /* stack functions */
+ void InitStack();
+ xbExpNode * Pop();
+ xbShort Push(xbExpNode *);
+ xbShort GetStackDepth() { return StackDepth; }
+ void DumpStack();
+ const char * GetValidFuncName( xbShort funcNo )
+ { return XbaseFuncList[funcNo].FuncName; }
+
+#ifdef XBASE_DEBUG
+ void DumpExpressionTree( xbShort printOption )
+ { DumpExpressionTree( Tree, printOption ); }
+ void DumpExpressionTree( xbExpNode *, xbShort printOption );
+ void DumpExpNode( xbExpNode *, xbShort printOption );
+#endif
+
+ /* expression methods */
+ xbDouble ABS( xbDouble );
+ xbLong ASC( const char * );
+ xbLong AT( const char *, const char * );
+ char * CDOW( const char * );
+ char * CHR( xbLong );
+ char * CMONTH( const char * );
+ char * CTOD( const char * );
+ char * DATE();
+ xbLong DAY( const char * );
+ char * DESCEND( const char * );
+ xbLong DESCEND( const xbDate & );
+ xbDouble DESCEND( xbDouble );
+ xbLong DOW( const char * );
+ char * DTOC( const char * );
+ char * DTOS( const char * );
+ xbDouble EXP( xbDouble );
+ char * IIF( xbShort, const char *, const char * );
+ xbLong INT( xbDouble );
+ xbLong ISALPHA( const char * );
+ xbLong ISLOWER( const char * );
+ xbLong ISUPPER( const char * );
+ char * LEFT( const char *, xbShort );
+ xbLong LEN( const char * );
+ xbDouble LOG( xbDouble );
+ char * LOWER( const char * );
+ char * LTRIM( const char * );
+ xbDouble MAX( xbDouble, xbDouble );
+ xbLong MONTH( const char * ); /* MONTH() */
+ xbDouble MIN( xbDouble, xbDouble );
+ xbLong RECNO( xbDbf * );
+ char * REPLICATE( const char *, xbShort );
+ char * RIGHT( const char *, xbShort );
+ char * RTRIM( const char * );
+ char * SPACE( xbShort );
+ xbDouble SQRT( xbDouble );
+ char * STR( const char * );
+ char * STR( const char *, xbShort );
+ char * STR( const char *, xbShort, xbShort );
+ char * STR( xbDouble );
+ char * STR( xbDouble, xbShort );
+ char * STR(xbDouble, xbUShort length, xbShort numDecimals );
+ char * STRZERO( const char * );
+ char * STRZERO( const char *, xbShort );
+ char * STRZERO( const char *, xbShort, xbShort );
+ char * STRZERO( xbDouble );
+ char * STRZERO( xbDouble, xbShort );
+ char * STRZERO( xbDouble, xbShort, xbShort );
+ char * SUBSTR( const char *, xbShort, xbShort );
+ char * TRIM( const char * );
+ char * UPPER( const char * );
+ xbLong VAL( const char * );
+ xbLong YEAR( const char * );
+
+ protected:
+ xbShort IsWhiteSpace( char );
+ char IsSeparator( char );
+ xbExpNode * LoadExpNode( const char * ENodeText, const char EType,
+ const xbShort ELen, const xbShort BufLen );
+ xbShort OperatorWeight( const char *Oper, xbShort len );
+ xbShort ReduceComplexExpression( const char * NextToken, xbShort Len,
+ xbExpNode * cn, xbDbf *d );
+ xbShort GetFunctionTokenLen( const char *s );
+ xbShort ReduceFunction( const char *NextToken, xbExpNode *cn, xbDbf *d );
+ xbExpNode * GetNextTreeNode( xbExpNode * );
+ xbShort ProcessOperator( xbShort );
+ xbShort ProcessFunction( char * );
+ xbShort ValidOperation( char *, char, char );
+ char GetOperandType( xbExpNode * );
+ xbShort AlphaOperation( char * );
+ xbShort NumericOperation( char * );
+ xbShort GetFuncInfo( const char *Function, xbShort Option );
+ xbDouble GetDoub( xbExpNode * );
+ xbLong GetInt( xbExpNode * );
+
+ private:
+ xbXBase *xbase;
+ xbFuncDtl *XbaseFuncList; /* pointer to list of Xbase functions */
+ xbExpNode *Tree; /* pointer to tree of parsed nodes */
+ xbShort LogicalType; /* set to 1 for logical type nodes */
+ char TokenType; /* E - Expression, not in simplest form */
+ /* C - Constant */
+ /* N - Numeric Constant */
+ /* O - Operator */
+ /* F - Function */
+ /* D - Database Field */
+ /* s - character string result */
+ /* l - logical or short int result */
+ /* d - double result */
+ char PreviousType; /* used to see if "-" follows operator */
+ char * Op1; /* pointer to operand 1 */
+ char * Op2; /* pointer to operand 2 */
+ xbDouble Opd1; /* double result 1 */
+ xbDouble Opd2; /* double result 2 */
+ xbShort OpLen1; /* length of memory allocated to operand 1 */
+ xbShort OpLen2; /* length of memory allocated to operand 2 */
+ xbShort OpDataLen1; /* length of data in op1 */
+ xbShort OpDataLen2; /* length of data in op2 */
+ char OpType1; /* type of operand 1 */
+ char OpType2; /* type of operand 2 */
+ xbShort TokenLen; /* length of token */
+
+// static xbString DefaultDateFormat; /*default date format for DTOC func*/
+ enum { WorkBufMaxLen = 200 };
+ char WorkBuf[WorkBufMaxLen+1];
+
+ /* stack variables */
+ xbShort StackDepth;
+ xbStackElement *First;
+ xbStackElement *Last;
+};
+
+#endif // XB_EXPRESSIONS
+#endif // __XB_EXP_H__
+
+
diff --git a/xbase64/xbexpfnc.cpp b/xbase64/xbexpfnc.cpp
new file mode 100755
index 0000000..91b6074
--- /dev/null
+++ b/xbase64/xbexpfnc.cpp
@@ -0,0 +1,1092 @@
+/* xbexpfnc.cpp
+
+ Xbase64 project source code
+
+ This file contains logic for handling Xbase expressions.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+#ifdef XB_EXPRESSIONS
+
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xbase64/xbexp.h>
+//#include <xbase64/xbexcept.h>
+
+
+/*! \file xbexpfnc.cpp
+*/
+
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Func
+*/
+xbShort xbExpn::ProcessFunction( char * Func )
+{
+/* 1 - pop function from stack
+ 2 - verify function name and get no of parms needed
+ 3 - verify no of parms >= remainder of stack
+ 4 - pop parms off stack
+ 5 - execute function
+ 6 - push result back on stack
+*/
+
+
+ char *buf = 0;
+ xbExpNode *p1, *p2, *p3, *WorkNode, *FuncNode;
+ xbShort ParmsNeeded,len;
+ char ptype = 0; /* process type s=string, l=logical, d=double */
+ xbDouble DoubResult = 0;
+ xbLong IntResult = 0;
+ FuncNode = (xbExpNode *) Pop();
+
+ ParmsNeeded = GetFuncInfo( Func, 1 );
+
+ if( ParmsNeeded == -1 ) {
+ return XB_INVALID_FUNCTION;
+ }
+ else {
+ ParmsNeeded = 0;
+ if( FuncNode->Sibling1 ) ParmsNeeded++;
+ if( FuncNode->Sibling2 ) ParmsNeeded++;
+ if( FuncNode->Sibling3 ) ParmsNeeded++;
+ }
+
+ if( ParmsNeeded > GetStackDepth())
+ return XB_INSUFFICIENT_PARMS;
+
+ p1 = p2 = p3 = NULL;
+ if( ParmsNeeded > 2 ) p3 = (xbExpNode *) Pop();
+ if( ParmsNeeded > 1 ) p2 = (xbExpNode *) Pop();
+ if( ParmsNeeded > 0 ) p1 = (xbExpNode *) Pop();
+ memset( WorkBuf, 0x00, WorkBufMaxLen+1);
+
+ if( strncmp( Func, "ABS", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = ABS( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "ASC", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = ASC( p1->StringResult );
+ }
+ else if( strncmp( Func, "AT", 2 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = AT( p1->StringResult, p2->StringResult );
+ }
+ else if( strncmp( Func, "CDOW", 4 ) == 0 ) {
+ ptype = 's';
+ buf = CDOW( p1->StringResult );
+ }
+ else if( strncmp( Func, "CHR", 3 ) == 0 ) {
+ ptype = 's';
+ buf = CHR( GetInt( p1 ));
+ }
+ else if( strncmp( Func, "CMONTH", 6 ) == 0 ) {
+ ptype = 's';
+ buf = CMONTH( p1->StringResult );
+ }
+ else if( strncmp( Func, "CTOD", 4 ) == 0 ) {
+ ptype = 's';
+ buf = CTOD( p1->StringResult );
+ }
+ else if( strncmp( Func, "DATE", 4 ) == 0 ) {
+ ptype = 's';
+ buf = DATE();
+ }
+ else if( strncmp( Func, "DAY", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = DAY( p1->StringResult );
+ }
+ else if( strncmp( Func, "DESCEND", 7 ) == 0 && p1->ExpressionType == 'C' ) {
+ ptype = 's';
+ buf = DESCEND( p1->StringResult.c_str() );
+ }
+ else if( strncmp( Func, "DESCEND", 7 ) == 0 && p1->ExpressionType == 'N' ) {
+ ptype = 'd';
+ DoubResult = DESCEND( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "DESCEND", 7 ) == 0 && p1->ExpressionType == 'D' ) {
+ xbDate d( p1->StringResult );
+ ptype = 'd';
+ DoubResult = DESCEND( d );
+ }
+ else if( strncmp( Func, "DOW", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = DOW( p1->StringResult );
+ }
+ else if( strncmp( Func, "DTOC", 4 ) == 0 ) {
+ ptype = 's';
+ buf = DTOC( p1->StringResult );
+ }
+ else if( strncmp( Func, "DTOS", 4 ) == 0 ) {
+ ptype = 's';
+ buf = DTOS( p1->StringResult );
+ }
+ else if( strncmp( Func, "EXP", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = EXP( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "IIF", 3 ) == 0 ){
+ ptype = 's';
+ buf = IIF( p1->IntResult, p2->StringResult, p3->StringResult );
+ }
+ else if( strncmp( Func, "INT", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = INT( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "ISALPHA", 7 ) == 0 ) {
+ ptype = 'l';
+ IntResult = ISALPHA( p1->StringResult );
+ }
+ else if( strncmp( Func, "ISLOWER", 7 ) == 0 ) {
+ ptype = 'l';
+ IntResult = ISLOWER( p1->StringResult );
+ }
+ else if( strncmp( Func, "ISUPPER", 7 ) == 0 ) {
+ ptype = 'l';
+ IntResult = ISUPPER( p1->StringResult );
+ }
+ else if( strncmp( Func, "LEN", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = LEN( p1->StringResult );
+ }
+ else if( strncmp( Func, "LEFT", 4 ) == 0 ) {
+ ptype = 's';
+ buf = LEFT( p1->StringResult, INT( p2->DoubResult ));
+ }
+ else if( strncmp( Func, "LTRIM", 5 ) == 0 ) {
+ ptype = 's';
+ buf = LTRIM( p1->StringResult );
+ }
+ else if( strncmp( Func, "LOG", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = LOG( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "LOWER", 5 ) == 0 ) {
+ ptype = 's';
+ buf = LOWER( p1->StringResult );
+ }
+ else if( strncmp( Func, "MAX", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = MAX( GetDoub( p1 ), GetDoub( p2 ));
+ }
+ else if( strncmp( Func, "MIN", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = MIN( GetDoub( p1 ), GetDoub( p2 ));
+ }
+ else if( strncmp( Func, "MONTH", 5 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = MONTH( p1->StringResult );
+ }
+
+ else if( strncmp( Func, "RECNO", 5 ) == 0 )
+ {
+ ptype = 'd';
+ DoubResult = RECNO( FuncNode->dbf );
+ }
+
+ else if( strncmp( Func, "REPLICATE", 9 ) == 0 ) {
+ ptype = 's';
+ buf = REPLICATE( p1->StringResult, GetInt( p2 ));
+ }
+ else if( strncmp( Func, "RIGHT", 5 ) == 0 ) {
+ ptype = 's';
+ buf = RIGHT( p1->StringResult, GetInt( p2 ));
+ }
+ else if( strncmp( Func, "RTRIM", 5 ) == 0 ) {
+ ptype = 's';
+ buf = RTRIM( p1->StringResult );
+ }
+ else if( strncmp( Func, "SPACE", 5 ) == 0 ) {
+ ptype = 's';
+ buf = SPACE( INT( GetDoub( p1 )));
+ }
+ else if( strncmp( Func, "SQRT", 4 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = SQRT( GetDoub( p1 ));
+ }
+ else if( strncmp( Func, "STRZERO", 7 ) == 0 && ParmsNeeded == 1 ) {
+ ptype = 's';
+ buf = STRZERO( p1->StringResult );
+ }
+ else if( strncmp( Func, "STRZERO", 7 ) == 0 && ParmsNeeded == 2 ) {
+ ptype = 's';
+ buf = STRZERO( p1->StringResult, GetInt( p2 ));
+ }
+ else if( strncmp( Func, "STRZERO", 7 ) == 0 && ParmsNeeded == 3 ) {
+ ptype = 's';
+ buf = STRZERO( p1->StringResult, GetInt( p2 ), GetInt( p3 ));
+ }
+
+ else if( strncmp( Func, "STR", 3 ) == 0 && p3 ) {
+ ptype = 's';
+ if(p1->ExpressionType == 'N')
+ buf = STR( p1->DoubResult, GetInt( p2 ), GetInt( p3 ));
+ else
+ buf = STR( p1->StringResult, GetInt( p2 ), GetInt( p3 ));
+ }
+
+ else if( strncmp( Func, "STR", 3 ) == 0 && p2 ) {
+ ptype = 's';
+ buf = STR( p1->StringResult, GetInt( p2 ));
+ }
+
+ else if( strncmp( Func, "STR", 3 ) == 0 && p1 ) {
+ ptype = 's';
+ buf = STR( p1->StringResult );
+ }
+
+ else if( strncmp( Func, "SUBSTR", 6 ) == 0 ) {
+ ptype = 's';
+ buf = SUBSTR( p1->StringResult, GetInt( p2 ), GetInt( p3 ));
+ }
+ else if( strncmp( Func, "TRIM", 4 ) == 0 ) {
+ ptype = 's';
+ buf = TRIM( p1->StringResult );
+ }
+ else if( strncmp( Func, "UPPER", 5 ) == 0 ) {
+ ptype = 's';
+ buf = UPPER( p1->StringResult );
+ }
+ else if( strncmp( Func, "VAL", 3 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = VAL( p1->StringResult );
+ }
+ else if( strncmp( Func, "YEAR", 4 ) == 0 ) {
+ ptype = 'd';
+ DoubResult = YEAR( p1->StringResult );
+ }
+ if( p1 && !p1->InTree ) delete p1;
+ if( p2 && !p2->InTree ) delete p2;
+ if( p3 && !p3->InTree ) delete p3;
+ if( !FuncNode->InTree ) delete FuncNode;
+ if( buf ){
+ len = strlen( buf );
+ WorkNode = new xbExpNode;
+ if( !WorkNode )
+ return XB_NO_MEMORY;
+ WorkNode->ResultLen = len + 1;
+
+ } else {
+ len = 0;
+ WorkNode = new xbExpNode;
+ if( !WorkNode )
+ return XB_NO_MEMORY;
+ WorkNode->ResultLen = 0;
+ }
+
+ switch( ptype ){
+ case 's': /* string or char result */
+ WorkNode->DataLen = len;
+ WorkNode->ExpressionType = 'C';
+ WorkNode->Type = 's';
+ WorkNode->StringResult = buf;
+ break;
+ case 'd': /* numeric result */
+ WorkNode->DataLen = 0;
+ WorkNode->ExpressionType = 'N';
+ WorkNode->Type = 'd';
+ WorkNode->DoubResult = DoubResult;
+ break;
+ case 'l': /* logical result */
+ WorkNode->DataLen = 0;
+ WorkNode->ExpressionType = 'L';
+ WorkNode->Type = 'l';
+ WorkNode->IntResult = IntResult;
+ break;
+ default:
+ std::cout << "\nInternal error. " << ptype;
+ break;
+ }
+ Push(WorkNode);
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+xbString & xbExpn::GetStringResult()
+{
+ xbString *s = 0;
+ xbExpNode *e;
+ if( GetStackDepth() < 1 ) return *s;
+ e = (xbExpNode *) Pop();
+ s = &e->StringResult;
+ Push(e);
+ return *s;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+xbLong xbExpn::GetIntResult()
+{
+ xbLong l;
+ xbExpNode * e;
+ if( GetStackDepth() < 1 ) return 0L;
+ e = (xbExpNode *) Pop();
+ l = e->IntResult;
+ Push(e);
+ return l;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+xbDouble xbExpn::GetDoubleResult()
+{
+ xbDouble d;
+ xbExpNode * e;
+ if( GetStackDepth() < 1 ) return (xbDouble) 0;
+ e = (xbExpNode *) Pop();
+ d = e->DoubResult;
+ Push(e);
+ return d;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param p
+*/
+xbDouble xbExpn::GetDoub( xbExpNode * p )
+{
+ if( p->Type == 'd' )
+ return p->DoubResult;
+ else if( p->Type == 'N' || p->Type == 's' )
+ return( strtod( p->StringResult, NULL ));
+ else if( p->Type == 'D' )
+ return( p->dbf->GetDoubleField( p->FieldNo ));
+ else
+ return 0;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param p
+*/
+xbLong xbExpn::GetInt( xbExpNode *p )
+{
+ if( p->Type == 'l' || p->Type == 'i' )
+ return p->IntResult;
+ else if( p->Type == 'N' || p->Type == 's' )
+ return atoi( p->StringResult );
+ else if( p->Type == 'D' )
+ return p->dbf->GetLongField( p->FieldNo );
+ else
+ return 0L;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbDouble xbExpn::ABS( xbDouble d )
+{
+ if( d < (xbDouble) 0 )
+ return d * -1;
+ else
+ return d;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::ASC( const char * String )
+{
+ return *String;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param s1
+ \param s2
+*/
+xbLong xbExpn::AT( const char * s1, const char *s2 )
+{
+ /* looks for s1 in s2 */
+ xbLong cnt;
+ const char *p;
+ if( strlen( s1 ) > strlen( s2 )) return 0;
+ if(( p = strstr( s2, s1 )) == NULL )
+ return 0;
+ cnt = 1;
+ while( s2++ != p ) cnt++;
+ return cnt;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+char * xbExpn::CDOW( const char * Date8 )
+{
+ static char buf[10];
+ xbDate d;
+ xbShort len,i;
+ strcpy( buf, d.FormatDate( "DDDD", Date8 ));
+ len = strlen( buf );
+ for( i = len; i < 9; i++ ) buf[i] = 0x20;
+ buf[9] = 0x00;
+ return buf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param l
+*/
+char * xbExpn::CHR( xbLong l )
+{
+ static char buf[2];
+ xbShort i;
+ i = (xbShort) l;
+ buf[0] = i;
+ buf[1] = 0x00;
+ return buf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+char * xbExpn::CMONTH( const char * Date8 )
+{
+ static char buf[10];
+ xbShort len,i;
+ xbDate d;
+ strcpy( buf, d.FormatDate( "MMMM", Date8 ));
+ len = strlen( buf );
+ for( i = len; i < 9; i++ ) buf[i] = 0x20;
+ buf[9] = 0x00;
+ return buf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param indate
+*/
+char * xbExpn::CTOD( const char * indate )
+{
+ xbDate d;
+ strcpy( WorkBuf, d.FormatCTODdate( indate ));
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbLong xbExpn::DAY( const char * Date8 )
+{
+ xbDate d;
+ return d.DayOf( XB_FMT_MONTH, Date8 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param date
+*/
+xbLong xbExpn::DESCEND( const xbDate & date )
+{
+ return 2415021 + date.JulianDays( "29991231" ) - date.JulianDays();
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param num
+*/
+xbDouble xbExpn::DESCEND( xbDouble d )
+{
+ return d * -1;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param str
+*/
+char * xbExpn::DESCEND( const char * str )
+{
+ xbShort i;
+ xbShort len = strlen( str );
+
+ for( i = 0; i < len; i++ )
+ WorkBuf[i] = 255 - str[i];
+ WorkBuf[i] = 0x00;
+
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbLong xbExpn::DOW( const char * Date8 )
+{
+ xbDate d;
+ return d.DayOf( XB_FMT_WEEK, Date8 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+char * xbExpn::DTOC( const char * Date8 )
+{
+ xbDate d;
+ strcpy( WorkBuf, d.FormatDate( xbase->GetDefaultDateFormat(), Date8 ));
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+char * xbExpn::DTOS( const char * Date8 )
+{
+ xbDate d;
+ strcpy( WorkBuf, d.FormatDate( "YYYYMMDD", Date8 ));
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbDouble xbExpn::EXP( xbDouble d )
+{
+ return exp( d );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param ifCondition
+ \param trueRslt
+ \param falseRslt
+*/
+char * xbExpn::IIF( xbShort ifCondition,
+ const char * trueRslt, const char * falseRslt )
+{
+ if( ifCondition )
+ strcpy( WorkBuf, trueRslt );
+ else
+ strcpy( WorkBuf, falseRslt );
+
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbLong xbExpn::INT( xbDouble d )
+{
+ return (xbLong) d;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::ISALPHA( const char * String )
+{
+ if( isalpha(*String) ) return 1;
+ else return 0;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::ISLOWER( const char * String )
+{
+ if( islower(*String) ) return 1;
+ else return 0;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::ISUPPER( const char * String )
+{
+ if( isupper(*String) ) return 1;
+ else return 0;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::LEN( const char * String )
+{
+ xbLong len;
+ len = strlen( String );
+ len--;
+ while( len >= 0 && String[len] == 0x20 ) len--;
+ return ++len;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param Len
+*/
+char * xbExpn::LEFT( const char * String, xbShort Len )
+{
+ xbShort i;
+ for( i = 0; i < Len && i < 100; i++ )
+ WorkBuf[i] = String[i];
+ WorkBuf[i] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+/* This method removes any leading spaces from String */
+char * xbExpn::LTRIM( const char *String) {
+ WorkBuf[0] = 0x00;
+ if (!String)
+ return WorkBuf;
+
+ xbShort i;
+ i = 0;
+ while( *String && *String == 0x20 ) String++;
+ while( *String && i < WorkBufMaxLen ){
+ WorkBuf[i++] = *String;
+ String++;
+ }
+ WorkBuf[i] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbDouble xbExpn::LOG( xbDouble d )
+{
+ return log( d );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char *xbExpn::LOWER( const char *String )
+{
+ WorkBuf[0] = 0x00;
+ if (!String)
+ return WorkBuf;
+ xbShort i = 0;
+ while( *String && i < WorkBufMaxLen) {
+ WorkBuf[i++] = tolower( *String );
+ String++;
+ }
+ WorkBuf[i] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d1
+ \param d2
+*/
+xbDouble xbExpn::MAX( xbDouble d1, xbDouble d2 )
+{
+ if( d1 > d2 )
+ return d1;
+ else
+ return d2;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d1
+ \param d2
+*/
+xbDouble xbExpn::MIN( xbDouble d1, xbDouble d2 )
+{
+ if( d1 < d2 )
+ return d1;
+ else
+ return d2;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbLong xbExpn::MONTH( const char * Date8 )
+{
+ xbDate d;
+ return d.MonthOf( Date8 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbLong xbExpn::RECNO( xbDbf * d ) {
+ return d->GetCurRecNo();
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param Cnt
+*/
+char * xbExpn::REPLICATE( const char * String, xbShort Cnt )
+{
+ xbShort len, i;
+ len = strlen( String );
+ if(( len * Cnt ) > 100 ) return NULL;
+ memset( WorkBuf, 0x00, len+1 );
+ for( i = 0; i < Cnt; i++ )
+ strcat( WorkBuf, String );
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \paran cnt
+*/
+char * xbExpn::RIGHT( const char * String, xbShort cnt )
+{
+ xbShort len;
+ strcpy( WorkBuf, String );
+ len = strlen( String );
+ if( len < cnt ) return WorkBuf;
+ len = LEN( String );
+ if( len < cnt ) return WorkBuf;
+ strcpy( WorkBuf, String + len - cnt );
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char * xbExpn::RTRIM( const char * String )
+{
+ return TRIM( String );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Cnt
+*/
+char * xbExpn::SPACE( xbShort Cnt )
+{
+ if( Cnt > 100 ) return NULL;
+ memset( WorkBuf, 0x20, Cnt );
+ WorkBuf[Cnt] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbDouble xbExpn::SQRT( xbDouble d )
+{
+ return sqrt( d );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+ \param length
+ \param numDecimals
+*/
+char * xbExpn::STR(xbDouble d, xbUShort length, xbShort numDecimals) {
+ // sanity check for length arg
+ if (length > WorkBufMaxLen)
+ {
+ // maybe should generate an error here instead ?
+ length = WorkBufMaxLen;
+ }
+
+ // check the length required
+ sprintf(WorkBuf, "%.*f", numDecimals, d);
+
+ if ((xbUShort) strlen(WorkBuf) > length) {
+ memset(WorkBuf, '*', length);
+ WorkBuf[length] = 0x00;
+ } else
+ sprintf( WorkBuf, "%*.*f", length, numDecimals, d );
+
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+ \param length
+*/
+char * xbExpn::STR( xbDouble d, xbShort length )
+{
+ return STR( d, length, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+char * xbExpn::STR( xbDouble d )
+{
+ return STR( d, 10, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param length
+ \param
+*/
+char * xbExpn::STR( const char * String, xbShort length, xbShort dec )
+{
+ xbShort len, i;
+ double d;
+ d = strtod( String, NULL );
+ return STR( d, length, dec );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param length
+*/
+char * xbExpn::STR( const char *String, xbShort length )
+{
+ return STR( String, length, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char * xbExpn::STR( const char * String )
+{
+ return STR( String, 10, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+ \param length
+ \param
+*/
+char * xbExpn::STRZERO( xbDouble d, xbShort length, xbShort )
+{
+ xbShort len,i;
+ sprintf(WorkBuf, "%*.*g", length, length, d);
+// gcvt( d, length, WorkBuf );
+ len = strlen( WorkBuf );
+ if( len > length )
+ strcpy( WorkBuf, "**********" );
+ else if( len < length )
+ {
+ for( i = len; i < length; i++ )
+ WorkBuf[i] = 0x30;
+ WorkBuf[i] = 0x00;
+ }
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+ \param length
+*/
+char * xbExpn::STRZERO( xbDouble d, xbShort length )
+{
+ return STRZERO( d, length, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+char * xbExpn::STRZERO( xbDouble d )
+{
+ return STRZERO( d, 10, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param length
+ \param
+*/
+char * xbExpn::STRZERO( const char * String, xbShort length, xbShort )
+{
+ xbShort i, len ;
+ while( *String == ' ' ) String++;
+ len = strlen(String);
+ for( i = 0; i < abs( length-len); i++ )
+ WorkBuf[i] = 0x30;
+ WorkBuf[i] = 0x00;
+ strcat( WorkBuf, String );
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param length
+*/
+char * xbExpn::STRZERO( const char * String, xbShort length )
+{
+ return STRZERO( String, length, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char * xbExpn::STRZERO( const char * String )
+{
+ return STRZERO( String, 10, 0 );
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+ \param StartPos
+ \param Len
+*/
+char * xbExpn::SUBSTR( const char * String, xbShort StartPos, xbShort Len )
+{
+ xbShort i;
+ if( StartPos < 1 ) return NULL;
+ String += (StartPos - 1);
+ for( i = 0; i < Len; i++ )
+ WorkBuf[i] = *String++;
+ WorkBuf[i] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+char * xbExpn::DATE()
+{
+ xbDate d;
+ strcpy( WorkBuf, d.Sysdate());
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char * xbExpn::TRIM( const char * String )
+{
+ WorkBuf[0] = 0x00;
+ if( !String )
+ return WorkBuf;
+ char *sp;
+ xbShort len;
+ len = strlen( String );
+ if( len < WorkBufMaxLen ) {
+ strcpy( WorkBuf, String );
+ }
+ else {
+ strncpy( WorkBuf, String, WorkBufMaxLen );
+ WorkBuf[ WorkBufMaxLen ] = 0x00;
+ len = WorkBufMaxLen;
+ }
+ sp = WorkBuf + len - 1;
+ while( *sp == 0x20 && sp >= WorkBuf ) {
+ *sp = 0x00;
+ sp--;
+ }
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+char *xbExpn::UPPER( const char *String )
+{
+ WorkBuf[0] = 0x00;
+ if (!String)
+ return WorkBuf;
+ xbShort i;
+ i = 0;
+ while(*String && i < WorkBufMaxLen) {
+ WorkBuf[i++] = toupper(*String);
+ String++;
+ }
+ WorkBuf[i] = 0x00;
+ return WorkBuf;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param String
+*/
+xbLong xbExpn::VAL( const char * String )
+{
+ if( String )
+ return (xbLong) *String;
+ else
+ return 0;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+ \param Date8
+*/
+xbLong xbExpn::YEAR( const char * Date8 ){
+ xbDate d;
+ return d.YearOf( Date8 );
+}
+/*************************************************************************/
+#endif // XB_EXPRESSIONS
diff --git a/xbase64/xbexpprc.cpp b/xbase64/xbexpprc.cpp
new file mode 100755
index 0000000..8334ea4
--- /dev/null
+++ b/xbase64/xbexpprc.cpp
@@ -0,0 +1,549 @@
+/* xbexpprc.cpp
+
+ Xbase64 project source code
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#ifdef XB_EXPRESSIONS
+
+#include <ctype.h>
+#include <math.h>
+
+/*! \file xbexpprc.cpp
+*/
+
+/*************************************************************************/
+//! Short description
+/*!
+ \param e
+*/
+xbExpNode * xbExpn::GetFirstTreeNode( xbExpNode * e )
+{
+ xbExpNode * WorkNode;
+ if( !e ) return e;
+ WorkNode = e;
+ while( WorkNode->Sibling1 )
+ WorkNode = WorkNode->Sibling1;
+ return WorkNode;
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param Operand
+ \param Op1
+ \pamam Op2
+*/
+xbShort xbExpn::ValidOperation( char * Operand, char Op1, char Op2 )
+{
+ /* Valid operation table
+
+ operator Op1 Op2 operator Op1 Op2
+
+ ** N N = N N
+ * N N = C C
+ / N N = D D
+ + N N <>,# N N
+ + C C <>,# C C
+ + D N <>,# D D
+ - N N <= N N
+ - C C <= D D
+ - D D <= C C
+ - D N >= N N
+ < N N >= D D
+ < C C >= C C
+ < D D $ C C
+ > N N
+ > C C
+ > D D
+
+ C = Character
+ D = Date
+ N = Numeric
+
+ Maybe reversed to what you are thinking ==> think OP2 - OP1
+
+ */
+
+ // check for **
+ if( Operand[0] == '*' && Operand[1] == '*' && Op1 == 'N' && Op2 == 'N' )
+ return 1;
+
+ // check for !=
+ if(( Operand[0] == '!' && Operand[1] == '=' ) &&
+ (( Op1 == 'N' && Op2 == 'N' ) ||
+ ( Op1 == 'C' && Op2 == 'C' ) ||
+ ( Op1 == 'D' && Op2 == 'D' )))
+ return 1;
+
+ switch( Operand[0] ) {
+ case '*':
+ case '/':
+ if( Op1 == 'N' && Op2 == 'N' )
+ return 1;
+ else
+ return 0;
+
+ case '+':
+ if(( Op1 == 'N' && Op2 == 'N' ) ||
+ ( Op1 == 'C' && Op2 == 'C' ) ||
+ ( Op1 == 'N' && Op2 == 'D' ))
+ return 1;
+ else
+ return 0;
+
+ case '-':
+ if(( Op1 == 'N' && Op2 == 'N' ) ||
+ ( Op1 == 'C' && Op2 == 'C' ) ||
+ ( Op1 == 'D' && Op2 == 'D' ) ||
+ ( Op1 == 'N' && Op2 == 'D' ))
+ return 1;
+ else
+ return 0;
+
+ case '<':
+ case '>':
+ case '=':
+ case '#':
+ if(( Op1 == 'N' && Op2 == 'N' ) ||
+ ( Op1 == 'C' && Op2 == 'C' ) ||
+ ( Op1 == 'D' && Op2 == 'D' ))
+ return 1;
+ else
+ return 0;
+
+ case '$':
+ if( Op1 == 'C' && Op2 == 'C' )
+ return 1;
+ else
+ return 0;
+
+ case '.' :
+ if( (strncmp( Operand, ".AND.", 5 ) == 0 ) ||
+ (strncmp( Operand, ".OR.", 4 ) == 0 ) ||
+ (strncmp( Operand, ".NOT.", 5 ) == 0 ))
+ return 1;
+ else
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param e
+*/
+xbExpNode * xbExpn::GetNextTreeNode( xbExpNode * e )
+{
+ if( !e->Node ) return NULL;
+
+ /* sibling 1 && sibling 2 exists */
+ if( e == e->Node->Sibling1 && e->Node->Sibling2 )
+ return GetFirstTreeNode( e->Node->Sibling2 );
+
+ /* sibling2 && sibling3 exists */
+ else if( e == e->Node->Sibling2 && e->Node->Sibling3 )
+ return GetFirstTreeNode( e->Node->Sibling3 );
+
+ else
+ return e->Node;
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param e
+*/
+xbShort xbExpn::ProcessExpression( xbExpNode * e )
+{
+ return ProcessExpression( e, 0 );
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param Wtree
+ \param RecBufSw
+*/
+xbShort xbExpn::ProcessExpression( xbExpNode * Wtree, xbShort RecBufSw )
+{
+ xbExpNode * WorkNode;
+ xbShort rc;
+ if( Wtree == 0 )
+ Wtree = Tree;
+ memset(WorkBuf, 0x00, WorkBufMaxLen+1 );
+ /* initialize the stack - free any expnodes */
+ while( GetStackDepth() > 0 ) {
+ WorkNode = (xbExpNode *) Pop();
+ if( !WorkNode->InTree )
+ delete WorkNode;
+ }
+ if(( WorkNode = GetFirstTreeNode( Wtree )) == NULL )
+ return XB_NO_DATA;
+
+ while( WorkNode ) {
+ Push(WorkNode);
+ if( WorkNode->Type == 'D' && WorkNode->dbf ) {
+ WorkNode->dbf->GetField( WorkNode->FieldNo, WorkNode->StringResult, RecBufSw );
+ if( WorkNode->dbf->GetFieldType( WorkNode->FieldNo ) == 'N' ||
+ WorkNode->dbf->GetFieldType( WorkNode->FieldNo ) == 'F' )
+ WorkNode->DoubResult = WorkNode->dbf->GetDoubleField( WorkNode->FieldNo, RecBufSw );
+ } else if( WorkNode->Type == 'O' ) {
+ if(( rc = ProcessOperator( RecBufSw )) != XB_NO_ERROR )
+ return rc;
+ } else if( WorkNode->Type == 'F' )
+ if(( rc = ProcessFunction( WorkNode->NodeText )) != XB_NO_ERROR )
+ return rc;
+ WorkNode = GetNextTreeNode( WorkNode );
+ }
+ if( GetStackDepth() != 1 ) /* should only have result left in stack */
+ return XB_PARSE_ERROR;
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param e
+*/
+char xbExpn::GetOperandType( xbExpNode * e )
+{
+ /* this routine returns
+ L - logical
+ N - Numeric
+ C - Character
+ 0 - error
+ */
+ char WorkType;
+ if( e->Type == 'd' || e->Type == 'N' || e->Type == 'i' ) return 'N';
+ if( e->Type == 'l' ) return 'L';
+ if( e->Type == 's' ) return 'C';
+ if( e->Type == 'C' ) {
+ if(e->NodeText[0]=='-' || e->NodeText[0]=='+' ||
+ (isdigit(e->NodeText[0]) &&
+ !(e->NodeText[e->DataLen] == '\'' || e->NodeText[e->DataLen] == '"')))
+ return 'N';
+ else
+ return 'C';
+ } else if( e->Type == 'D' && e->dbf ){
+ WorkType = e->dbf->GetFieldType( e->FieldNo );
+ if( WorkType == 'C' ) return 'C';
+ else if( WorkType == 'F' || WorkType == 'N' ) return 'N';
+ else if( WorkType == 'L' ) return 'L';
+ else if( WorkType == 'D' ) return 'D';
+ else return 0;
+ } else
+ return 0;
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param RecBufSw
+*/
+xbShort xbExpn::ProcessOperator( xbShort RecBufSw )
+{
+ xbExpNode * WorkNode;
+ char Operator[6];
+ char t;
+ if( GetStackDepth() < 3 )
+ return XB_PARSE_ERROR;
+ WorkNode = (xbExpNode *) Pop();
+ if( WorkNode->Len > 5 )
+ return XB_PARSE_ERROR;
+
+ memset( Operator, 0x00, 6 );
+ strncpy( Operator, WorkNode->NodeText, WorkNode->Len );
+ if( !WorkNode->InTree )
+ delete WorkNode;
+
+ /* load up operand 1 */
+ WorkNode = (xbExpNode *) Pop();
+ if(( OpType1 = GetOperandType( WorkNode )) == 0 )
+ return XB_PARSE_ERROR;
+
+ if( OpLen1 < WorkNode->DataLen+1 && WorkNode->Type != 'd' ) {
+ if( OpLen1 > 0 ) free( Op1 );
+ if(( Op1 = (char *) malloc( WorkNode->DataLen+1 )) == NULL ) {
+ return XB_NO_MEMORY;
+ }
+ OpLen1 = WorkNode->DataLen+1;
+ }
+ OpDataLen1 = WorkNode->DataLen;
+ memset( Op1, 0x00, WorkNode->DataLen+1 );
+ if( WorkNode->Type == 'D' && WorkNode->dbf ) { /* database field */
+ WorkNode->dbf->GetField( WorkNode->FieldNo, Op1, RecBufSw );
+ t = WorkNode->dbf->GetFieldType( WorkNode->FieldNo );
+ if( t == 'N' || t == 'F' )
+ Opd1 = strtod( WorkNode->StringResult, 0 );
+ else if( t == 'D' ){ // date field
+ xbDate d;
+ Opd1 = d.JulianDays( WorkNode->StringResult );
+ }
+ }
+ else if( WorkNode->Type == 'C' ) /* constant */
+ memcpy( Op1, WorkNode->NodeText, WorkNode->DataLen );
+ else if( WorkNode->Type == 's' ) /* previous result */
+ memcpy( Op1, WorkNode->StringResult, WorkNode->DataLen+1 );
+ else if( WorkNode->Type == 'd' ) /* previous numeric result */
+ Opd1 = WorkNode->DoubResult;
+ else if( WorkNode->Type == 'N' ) /* previous numeric result */
+ Opd1 = strtod( WorkNode->StringResult, 0 );
+ else if(WorkNode->Type == 'l') /* previous logical result 3/26/00 dtb */
+ Opd1 = WorkNode->IntResult;
+ if( !WorkNode->InTree )
+ delete WorkNode;
+
+ /* load up operand 2 */
+ WorkNode = (xbExpNode *) Pop();
+ if(( OpType2 = GetOperandType( WorkNode )) == 0 )
+ return XB_PARSE_ERROR;
+
+ if( OpLen2 < WorkNode->DataLen+1 && WorkNode->Type != 'd' ) {
+ if( OpLen2 > 0 ) free( Op2 );
+ if(( Op2 = (char *) malloc( WorkNode->DataLen+1 )) == NULL ) {
+ return XB_NO_MEMORY;
+ }
+ OpLen2 = WorkNode->DataLen+1;
+ }
+ OpDataLen2 = WorkNode->DataLen;
+ memset( Op2, 0x00, WorkNode->DataLen+1 );
+ if( WorkNode->Type == 'D' && WorkNode->dbf ) { /* database field */
+ WorkNode->dbf->GetField( WorkNode->FieldNo, Op2, RecBufSw );
+ t = WorkNode->dbf->GetFieldType( WorkNode->FieldNo );
+ if( t == 'N' || t == 'F' )
+ Opd2 = strtod( WorkNode->StringResult, 0 );
+ else if( t == 'D' ){ // date field
+ xbDate d;
+ Opd2 = d.JulianDays( WorkNode->StringResult );
+ }
+ }
+ else if( WorkNode->Type == 'C' ) /* constant */
+ memcpy( Op2, WorkNode->NodeText, WorkNode->DataLen );
+ else if( WorkNode->Type == 's' ) /* previous result */
+ memcpy( Op2, WorkNode->StringResult, WorkNode->DataLen+1 );
+ else if( WorkNode->Type == 'd' ) /* previous numeric result */
+ Opd2 = WorkNode->DoubResult;
+ else if( WorkNode->Type == 'N' ) /* previous numeric result */
+ Opd2 = strtod( WorkNode->StringResult, 0 );
+ else if(WorkNode->Type == 'l') /* previous logical result 3/26/00 dtb*/
+ Opd2 = WorkNode->IntResult;
+ if( !WorkNode->InTree )
+ delete WorkNode;
+ if( !ValidOperation( Operator, OpType1, OpType2 ))
+ return XB_PARSE_ERROR;
+
+ if( OpType1 == 'N' || OpType1 == 'L' || OpType1 == 'D' ) /* numeric procesing */
+ return NumericOperation( Operator );
+ else /* must be character */
+ return AlphaOperation( Operator );
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param Operator
+*/
+xbShort xbExpn::NumericOperation( char * Operator )
+{
+ xbDouble Operand1, Operand2;
+ xbExpNode * WorkNode;
+ xbShort ResultLen;
+ char SaveType;
+ ResultLen = 0;
+
+/* This function assumes a valid operation coming in */
+
+ if( Operator[0] == '=' || Operator[0] == '<' ||
+ Operator[0] == '>' || Operator[0] == '#' ||
+ Operator[0] == '.' || (strncmp( Operator, "!=", 2 ) == 0 ))
+ SaveType = 'l';
+ else
+ SaveType = 'd';
+
+ WorkNode = new xbExpNode;
+
+ if( !WorkNode )
+ return XB_PARSE_ERROR;
+ WorkNode->ResultLen = ResultLen;
+ WorkNode->Type = SaveType;
+ WorkNode->DataLen = ResultLen;
+
+ if( OpType1 == 'd' || OpType1 == 'N' || OpType2 == 'D' )
+ Operand1 = Opd1;
+ else
+ Operand1 = strtod( Op1, NULL );
+
+ if( OpType2 == 'd' || OpType2 == 'N' || OpType2 == 'D' )
+ Operand2 = Opd2;
+ else
+ Operand2 = strtod( Op2, NULL );
+
+ if( Operator[0] == '*' && Operator[1] == '*' )
+ WorkNode->DoubResult = pow( Operand2, Operand1 );
+ else if( Operator[0] == '*' )
+ WorkNode->DoubResult = Operand2 * Operand1;
+ else if( Operator[0] == '/')
+ WorkNode->DoubResult = Operand2 / Operand1;
+ else if( Operator[0] == '+' ){
+ WorkNode->DoubResult = Operand2 + Operand1;
+ xbDate d;
+ WorkNode->StringResult = d.JulToDate8((xbLong) WorkNode->DoubResult );
+ } else if( Operator[0] == '-' ){
+ WorkNode->DoubResult = Operand2 - Operand1;
+ xbDate d;
+ WorkNode->StringResult = d.JulToDate8((xbLong) WorkNode->DoubResult );
+ }
+
+ /* = */
+ else if( Operator[0]== '=' && Operand1 == Operand2 )
+ WorkNode->IntResult = 1;
+ else if( Operator[0] == '=' )
+ WorkNode->IntResult = 0;
+ /* not = */
+ else if(( Operator[0] == '<' && Operator[1] == '>' )||
+ ( Operator[0] == '!' && Operator[1] == '=' )||
+ Operator[0] == '#' || (strncmp( Operator, "!=", 2 ) == 0 ))
+ WorkNode->IntResult = ( Operand1 != Operand2 ) ? 1 : 0;
+ /* less than */
+ else if( Operator[0] == '<' )
+ WorkNode->IntResult = ( Operand2 < Operand1 ) ? 1 : 0;
+ /* greater than */
+ else if( Operator[0] == '>' )
+ WorkNode->IntResult = ( Operand2 > Operand1 ) ? 1 : 0;
+ else if(Operator[0] == '.'){ // logical operators, added 3/26/00 dtb
+ switch(Operator[1]){
+ case 'A' : // and
+ WorkNode->IntResult = (Opd1 && Opd2) ? 1 : 0;
+ break;
+
+ case 'N' : // not
+ WorkNode->IntResult = (!(Opd1 && Opd2)) ? 1 : 0;
+ break;
+
+ case 'O' : // or
+ WorkNode->IntResult = (Opd1 || Opd2) ? 1 : 0;
+ break;
+
+ default :
+ return XB_PARSE_ERROR;
+ }
+ } else
+ return XB_PARSE_ERROR;
+
+ Push(WorkNode);
+ return 0;
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param Operator
+*/
+xbShort xbExpn::AlphaOperation( char * Operator )
+{
+ xbShort ResultLen, i;
+ char SaveType;
+ xbExpNode * WorkNode;
+
+ if( Operator[0] == '=' || Operator[0] == '<' ||
+ Operator[0] == '>' || Operator[0] == '#' ||
+ (strncmp( Operator, "!=", 2 ) == 0 ) ||
+ Operator[0] == '$'){
+ ResultLen = 0;
+ SaveType = 'l';
+ } else {
+ ResultLen = OpDataLen1 + OpDataLen2 + 1;
+ SaveType = 's';
+ }
+
+ WorkNode = new xbExpNode;
+ if( !WorkNode )
+ return XB_PARSE_ERROR;
+ WorkNode->ResultLen = ResultLen;
+ WorkNode->Type = SaveType;
+ if( WorkNode->Type == 'l' )
+ WorkNode->DataLen = 0;
+ else
+ WorkNode->DataLen = ResultLen - 1;
+
+ if( Operator[0] == '+' ){
+ WorkNode->StringResult = Op2;
+ WorkNode->StringResult += Op1;
+ } else if( Operator[0] == '-' ) {
+ WorkNode->StringResult = RTRIM( Op2 );
+ WorkNode->StringResult += Op1;
+ i = WorkNode->StringResult.len();
+ for( ; i < ResultLen-1; i++)
+ WorkNode->StringResult += " ";
+ }
+ /* == */
+ else if(( strncmp( Operator, "==", 2 ) == 0 ) && strcmp(Op1,Op2) == 0)
+ WorkNode->IntResult = 1;
+
+ else if(( strncmp( Operator, "==", 2 ) == 0 ))
+ WorkNode->IntResult = 0;
+
+ /* = */
+ else if( Operator[0] == '=' && strcmp(Op1,Op2) == 0 )
+ WorkNode->IntResult = 1;
+
+ else if( Operator[0] == '=' )
+ WorkNode->IntResult = 0;
+
+ /* not = */
+ else if(( strncmp( Operator, "<>", 2 ) == 0 ) ||
+ Operator[0] == '#' ||
+ strncmp( Operator, "!=", 2 ) == 0 )
+ WorkNode->IntResult = ( strcmp( Op1, Op2 ) != 0 ) ? 1 : 0;
+ /* less than */
+ else if( Operator[0] == '<' )
+ WorkNode->IntResult = ( strcmp( Op2, Op1 ) < 0 ) ? 1 : 0;
+ /* greater than */
+ else if( Operator[0] == '>' )
+ WorkNode->IntResult = ( strcmp( Op2, Op1 ) > 0 ) ? 1 : 0;
+ else if(Operator[0] == '$')
+ WorkNode->IntResult = (strstr(Op1,Op2)) ? 1 : 0;
+ else
+ return XB_PARSE_ERROR;
+
+ Push(WorkNode);
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+#endif // XB_EXPRESSIONS
diff --git a/xbase64/xbfields.cpp b/xbase64/xbfields.cpp
new file mode 100755
index 0000000..d3e2388
--- /dev/null
+++ b/xbase64/xbfields.cpp
@@ -0,0 +1,672 @@
+/* xbfields.cpp
+
+ Xbase64 project source code
+
+ This file contains the basic X-Base routines for reading and writing
+ Xbase fields.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+/*! \file xbfields.cpp
+*/
+/************************************************************************/
+/* This function returns true if the data is valid logical data */
+//! Determines if data is valid logical data.
+/*! Determines if the data in buf is valid for a logical field value.
+
+ \param buf data to be tested
+ \returns TRUE (non-zero) if valid, FALSE (zero) if not.
+*/
+xbShort xbDbf::ValidLogicalData(const char * buf) {
+ if( buf[0] )
+ if( buf[0] == 'T' || buf[0] == 't' || buf[0] == 'F' || buf[0] == 'f' ||
+ buf[0] == 'Y' || buf[0] == 'y' || buf[0] == 'N' || buf[0] == 'n' ||
+ buf[0] == '?' )
+ return 1;
+ return 0;
+}
+/************************************************************************/
+/* This function returns true if the data is valid numeric data */
+//! Determines if data is valid numeric data.
+/*! Determines if the data in buf is valid for a numeric field value.
+
+ \param buf
+ \returns TRUE (non-zero) if valid, FALSE (zero) if not.
+*/
+xbShort xbDbf::ValidNumericData(const char * buf) {
+ const char *p;
+
+ p = buf;
+ while( *p ){
+ if( *p != '+' && *p != '-' && *p != '.' && *p != '0' && *p != '1' &&
+ *p != '2' && *p != '3' && *p != '4' && *p != '5' && *p != '6' &&
+ *p != '7' && *p != '8' && *p != '9' )
+ return 0;
+ else
+ p++;
+ }
+ return 1;
+}
+/************************************************************************/
+/* This function returns a fields length */
+//! Returns the length of the specified field.
+/*! Returns the length of the field specified by FieldNo.
+
+ \param FieldNo Number of field.
+ \returns Length of the specified field in bytes.
+*/
+xbShort xbDbf::GetFieldLen( xbShort FieldNo )
+{
+ if( FieldNo >= 0 && FieldNo < NoOfFields ){
+ if( SchemaPtr[FieldNo].Type == 'C' && SchemaPtr[FieldNo].NoOfDecs > 0 )
+ return SchemaPtr[FieldNo].LongFieldLen;
+ else
+ return SchemaPtr[FieldNo].FieldLen;
+ }
+ else
+ return 0;
+}
+/************************************************************************/
+/* This function returns a fields decimal length */
+//! Returns the number of decimals in the specified field.
+/*! Returns the number of decimals in the field specified by FieldNo.
+
+ \param FieldNo Number of field.
+ \returns Length of the specified field in bytes.
+*/
+
+xbShort xbDbf::GetFieldDecimal( xbShort FieldNo )
+{
+ if( FieldNo >= 0 && FieldNo < NoOfFields )
+ return SchemaPtr[FieldNo].NoOfDecs;
+ else
+ return 0;
+}
+/************************************************************************/
+/* This function returns a fields type */
+//! Returns the type of the specified field.
+/*! Returns the type of the field specified by FieldNo.
+
+ \param FieldNo Number of field.
+ \returns Type of specified field.
+*/
+char xbDbf::GetFieldType( xbShort FieldNo ) const
+{
+ if( FieldNo >= 0 && FieldNo < NoOfFields )
+ return SchemaPtr[FieldNo].Type;
+ else
+ return 0;
+}
+/************************************************************************/
+/* This function returns a fields name */
+//! Returns the name of the specified field.
+/*! Returns a pointer to the name for the field specified by FieldNo.
+
+ \param FieldNo Number of field.
+ \returns A pointer to the name of the field.
+*/
+char * xbDbf::GetFieldName( xbShort FieldNo )
+{
+ if( FieldNo >= 0 && FieldNo < NoOfFields )
+ return SchemaPtr[FieldNo].FieldName;
+ else
+ return 0;
+}
+/************************************************************************/
+/* This function returns the field ID number for a given field
+ or -1 if the field is not one of the fields of the database */
+//! Returns the field number of the specified field.
+/*! Returns the field number for the named field.
+
+ \param name Name of field.
+ \returns Number of field named name.
+*/
+xbShort xbDbf::GetFieldNo( const char * name ) const
+{
+ int i, len1, len2;
+
+ if(( len1 = strlen( name )) > 10 )
+ return -1;
+
+ for( i = 0; i < NoOfFields; i++ ){
+ len2 = strlen( SchemaPtr[i].FieldName );
+ if( len1 == len2 )
+
+//#ifndef __WIN32__
+#ifdef HAVE_STRCASECMP
+ if(!strcasecmp( SchemaPtr[i].FieldName, name ))
+#else
+ if(!stricmp( SchemaPtr[i].FieldName, name ))
+#endif
+
+ return i;
+ }
+ return -1;
+}
+/************************************************************************/
+/*
+ Helpers
+*/
+
+//! Get the value of the specified field.
+/*! Get the value of the field referenced by Name and place its value
+ in buf.
+
+ \param Name Name of field.
+ \param buf Buffer to hold field value. Must be large enough to hold
+ the entire field value. Use GetFieldLen() to determine
+ the length of the field, if necessary.
+ \param RecBufSw
+ \returns One of the following:
+*/
+xbShort xbDbf::GetField(const char *Name, char *buf,
+ const xbShort RecBufSw ) const
+{
+ return GetField(GetFieldNo(Name), buf, RecBufSw);
+}
+
+/************************************************************************/
+//! Get the value of the specified field.
+/*! Get the value of the field specified by Name and place its value
+ in buf.
+
+ \param Name Name of field.
+ \param buf Buffer to hold field value. Must be large enough to hold
+ the entire field value. Use GetFieldLen() to determine
+ the length of the field, if necessary.
+ \returns One of the following:
+*/
+xbShort xbDbf::GetField(const char *Name, char *buf) const
+{
+ return GetField(GetFieldNo(Name), buf);
+}
+/************************************************************************/
+//! Get the raw value of the specified field.
+/*! Get the value of the field specified by Name and place its value
+ in buf.
+
+ \param Name Name of field.
+ \param buf Buffer to hold field value. Must be large enough to hold
+ the entire field value. Use GetFieldLen() to determine
+ the length of the field, if necessary.
+ \returns One of the following:
+*/
+xbShort xbDbf::GetRawField(const char *Name, char *buf) const
+{
+ return GetRawField(GetFieldNo(Name), buf);
+}
+
+/************************************************************************/
+
+// FIXME this function doesn't follow look and feel of the rest of the lib
+// GAK
+
+static char __buf[1024];
+
+static void trim(char *s) {
+ int len = strlen(s)-1;
+ if (len > 0) {
+ while ((len != 0) && (s[len] == ' '))
+ len--;
+ s[len+1] = 0;
+ }
+}
+
+//! Get the value of the specified field.
+/*! Returns the value of the field specified by Name.
+
+ \param Name Name of field.
+ \returns Value of the specified field.
+*/
+const char *xbDbf::GetField(const char *Name) const {
+ GetField(GetFieldNo(Name), __buf);
+ trim(__buf);
+ return __buf;
+}
+
+//! Get the value of the specified field.
+/*! Returns the value of the field specified by FieldNo.
+
+ \param FieldNo Number of field.
+ \returns Value of the specified field.
+*/
+const char *xbDbf::GetField(xbShort FieldNo) const {
+ GetField(FieldNo, __buf);
+ trim(__buf);
+ return __buf;
+}
+/************************************************************************/
+/* This function fills a buffer with data from the record buffer
+ for a particular field number.
+
+ Use GetFieldNo to get a number based on a field's name
+
+ If successful, this function returns the field size.
+*/
+
+//! Get the value of the specified field.
+/*! Get the value of the field specified by FieldNo and place its value
+ in buf.
+
+ \param FieldNo Number of field.
+ \param buf Buffer to hold field value. Must be large enough to hold
+ the entire field value. Use GetFieldLen() to determine
+ the length of the field, if necessary.
+ \param RecBufSw
+ \returns The length of the field.
+*/
+xbShort xbDbf::GetField( xbShort FieldNo, char * buf, xbShort RecBufSw) const
+{
+ xbShort length;
+ if( FieldNo < 0 || FieldNo >= NoOfFields ) {
+ buf[0] = 0x00;
+ return 0x00;
+ }
+
+// Check for existence of a long field length
+ if( SchemaPtr[FieldNo].Type == 'C' && SchemaPtr[FieldNo].NoOfDecs > 0 )
+ length = SchemaPtr[FieldNo].LongFieldLen;
+ else
+ length = SchemaPtr[FieldNo].FieldLen;
+
+ if( RecBufSw )
+ memcpy( buf, SchemaPtr[FieldNo].Address2, length );
+ else
+ memcpy( buf, SchemaPtr[FieldNo].Address, length );
+ buf[length] = 0x00;
+ return( length );
+}
+/************************************************************************/
+xbShort xbDbf::GetField( xbShort FieldNo, xbString & sf, xbShort RecBufSw) const
+{
+ xbShort length;
+ if( FieldNo < 0 || FieldNo >= NoOfFields ) {
+ sf = "";
+ return 0;
+ }
+
+ // Check for existence of a long field length
+ if( SchemaPtr[FieldNo].Type == 'C' && SchemaPtr[FieldNo].NoOfDecs > 0 )
+ length = SchemaPtr[FieldNo].LongFieldLen;
+ else
+ length = SchemaPtr[FieldNo].FieldLen;
+
+ if( RecBufSw )
+ sf.assign( xbString(SchemaPtr[FieldNo].Address2, length), 0, length );
+ else
+ sf.assign( xbString(SchemaPtr[FieldNo].Address, length), 0, length );
+
+ return( length );
+}
+/************************************************************************/
+/* This function fills a field in the record buffer with data from
+ a buffer for a particular field.
+
+ Use GetFieldNo to get a number based on a field's name
+
+ Field type N or F is loaded as right justified, left blank filled.
+ Other fields are loaded as left justified, right blank filled.
+
+ This method does check the data's validity.
+
+ If successful, this function returns 0, if invalid data, it returns -1
+ or XB_INVALID_FIELDNO
+*/
+
+//! Put a value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutField(const char *Name, const char *buf) {
+ return PutField(GetFieldNo(Name), buf);
+}
+/************************************************************************/
+//! Put a raw value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutRawField(const char *Name, const char *buf) {
+ return PutRawField(GetFieldNo(Name), buf);
+}
+/************************************************************************/
+//! Put a value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutField(const xbShort FieldNo, const char *buf) {
+ xbShort len, i;
+ char * startpos;
+ char * tp; /* target pointer */
+ const char * sp; /* source pointer */
+
+ if( FieldNo < 0 || FieldNo >= NoOfFields )
+ return XB_INVALID_FIELDNO;
+
+ if( DbfStatus != XB_UPDATED ){
+ DbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, RecordLen );
+ }
+
+ if( SchemaPtr[FieldNo].Type == 'L' && !ValidLogicalData( buf ))
+ return XB_INVALID_DATA;
+
+ else if(( SchemaPtr[FieldNo].Type == 'F' || SchemaPtr[FieldNo].Type == 'N' )
+ && !ValidNumericData( buf ))
+ return XB_INVALID_DATA;
+
+ else if( SchemaPtr[FieldNo].Type == 'D' ){
+ xbDate d;
+ if( !d.DateIsValid( buf ))
+ return XB_INVALID_DATA;
+ }
+
+ if( SchemaPtr[FieldNo].Type == 'C' && SchemaPtr[FieldNo].NoOfDecs > 0 )
+ memset( SchemaPtr[FieldNo].Address, 0x20, SchemaPtr[FieldNo].LongFieldLen );
+ else
+ memset( SchemaPtr[FieldNo].Address, 0x20, SchemaPtr[FieldNo].FieldLen );
+
+ len = strlen( buf );
+
+ if(( SchemaPtr[FieldNo].Type == 'N' || SchemaPtr[FieldNo].Type == 'F')
+ && len > SchemaPtr[FieldNo].FieldLen )
+ return XB_INVALID_DATA;
+ else if( len > SchemaPtr[FieldNo].FieldLen )
+ len = SchemaPtr[FieldNo].FieldLen;
+
+ if( SchemaPtr[FieldNo].Type == 'F' || SchemaPtr[FieldNo].Type == 'N'
+ || SchemaPtr[FieldNo].Type == 'M') {
+
+ const char *sdp = strchr( buf, '.' ); /* source decimal point */
+ len = 0;
+ sp =buf;
+ while( *sp && *sp != '.' ) { len++; sp++; }
+ if( SchemaPtr[FieldNo].NoOfDecs > 0 ){
+ /* do the right of decimal area */
+ tp = SchemaPtr[FieldNo].Address;
+ tp += SchemaPtr[FieldNo].FieldLen - SchemaPtr[FieldNo].NoOfDecs - 1;
+ *tp++ = '.';
+ sp = sdp;
+ if( sp ) sp++;
+ for( i = 0; i < SchemaPtr[FieldNo].NoOfDecs; i++ )
+ if( sp && *sp ) *tp++ = *sp++; else *tp++ = '0';
+
+ startpos= SchemaPtr[FieldNo].Address +
+ SchemaPtr[FieldNo].FieldLen -
+ SchemaPtr[FieldNo].NoOfDecs - len - 1;
+ }
+ else
+ {
+ startpos=SchemaPtr[FieldNo].Address+SchemaPtr[FieldNo].FieldLen-len;
+ }
+ }
+ else
+ startpos = SchemaPtr[FieldNo].Address;
+
+ memcpy( startpos, buf, len );
+ return 0;
+}
+
+/************************************************************************/
+//! Put a raw value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutRawField(const xbShort FieldNo, const char *buf) {
+ xbShort len;
+ char * startpos;
+
+ if( FieldNo < 0 || FieldNo >= NoOfFields )
+ return XB_INVALID_FIELDNO;
+
+ if( DbfStatus != XB_UPDATED ){
+ DbfStatus = XB_UPDATED;
+ memcpy( RecBuf2, RecBuf, RecordLen );
+ }
+
+ startpos = SchemaPtr[FieldNo].Address;
+ len = SchemaPtr[FieldNo].FieldLen;
+ memcpy( startpos, buf, len );
+
+ return 0;
+}
+
+/************************************************************************/
+//! Get the value of the specified field.
+/*!
+*/
+xbShort xbDbf::GetField( xbShort FieldNo, char *buf) const {
+ return GetField(FieldNo, buf, 0);
+}
+/************************************************************************/
+//! Get the raw value of the specified field.
+/*!
+*/
+xbShort xbDbf::GetRawField( xbShort FieldNo, char *buf ) const {
+ return GetField(FieldNo, buf, 0);
+}
+/************************************************************************/
+//! Get the long value of the specified field.
+/*!
+*/
+xbLong xbDbf::GetLongField( xbShort FieldNo ) const
+{
+ char buf[18];
+ memset( buf, 0x00, 18 );
+ GetField( FieldNo, buf );
+ return atol( buf );
+}
+/************************************************************************/
+//! Get the long value of the specified field.
+/*!
+*/
+xbLong xbDbf::GetLongField( const char * FieldName ) const
+{
+ return( GetLongField( GetFieldNo( FieldName )));
+}
+/************************************************************************/
+//! Put a long value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutLongField( xbShort FieldNo, xbLong Val )
+{
+ char buf[18];
+ memset( buf, 0x00, 18 );
+ sprintf( buf, "%ld", Val );
+ return( PutField( FieldNo, buf ));
+}
+/************************************************************************/
+//! Put a long value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutLongField(const char *FieldName, xbLong Val) {
+ return ( PutLongField( GetFieldNo( FieldName ), Val ));
+}
+/************************************************************************/
+//! Get the float value of the specified field.
+/*!
+*/
+xbFloat xbDbf::GetFloatField( xbShort FieldNo )
+{
+ char buf[21];
+ memset( buf, 0x00, 21 );
+ if( GetField( FieldNo, buf ) != 0 )
+ return atof( buf );
+ else
+ return 0;
+}
+/************************************************************************/
+//! Get the float value of the specified field.
+/*!
+*/
+xbFloat xbDbf::GetFloatField(const char * FieldName) {
+ xbShort fnum;
+ if((fnum = GetFieldNo(FieldName)) != -1)
+ return GetFloatField(fnum);
+ else
+ return 0;
+}
+/************************************************************************/
+//! Put a float value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutFloatField( xbShort FldNo, xbFloat f )
+{
+ char buf[25];
+ char buf2[12];
+ memset( buf, 0x00, 25 );
+ memset( buf2, 0x00, 12 );
+ sprintf( buf, "%d.%df", GetFieldLen( FldNo ), GetFieldDecimal( FldNo ));
+ strcpy( buf2, "%-" );
+ strcat( buf2, buf );
+ sprintf( buf, buf2, f );
+
+ /* remove trailing space */
+ xbShort i = 0;
+ while( i < 25 )
+ if( buf[i] == 0x20 ){
+ buf[i] = 0x00;
+ break;
+ } else
+ i++;
+ return PutField( FldNo, buf );
+}
+/************************************************************************/
+//! Put a float value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutFloatField(const char *FieldName, xbFloat f) {
+ xbShort fnum;
+ if ((fnum = GetFieldNo(FieldName)) != -1)
+ return PutFloatField(fnum, f);
+ else
+ return 0;
+}
+/************************************************************************/
+//! Get the double value of the specified field.
+/*!
+*/
+xbDouble xbDbf::GetDoubleField( xbShort FieldNo, xbShort RecBufSw )
+{
+ char buf[21];
+ memset( buf, 0x00, 21 );
+ if( GetField( FieldNo, buf, RecBufSw ) != 0 )
+ return strtod( buf, NULL );
+ else
+ return 0;
+}
+/************************************************************************/
+//! Get the double value of the specified field.
+/*!
+*/
+xbDouble xbDbf::GetDoubleField(const char *FieldName) {
+ xbShort fnum;
+ if ((fnum = GetFieldNo(FieldName)) != -1)
+ return GetDoubleField(fnum);
+ else
+ return 0;
+}
+/************************************************************************/
+//! Put a double value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutDoubleField( xbShort FieldNo, xbDouble d) {
+ return PutFloatField(FieldNo, (xbFloat)d);
+}
+/************************************************************************/
+//! Put a double value into the specified field.
+/*!
+*/
+xbShort xbDbf::PutDoubleField(const char *FieldName, xbDouble d) {
+ xbShort fnum;
+ if ((fnum = GetFieldNo(FieldName)) != -1)
+ return PutFloatField(fnum, (xbFloat)d);
+ else
+ return 0;
+}
+/************************************************************************/
+//! Get the logical value of the specified field.
+/*!
+*/
+xbShort xbDbf::GetLogicalField( xbShort FieldNo )
+{
+ char buf[3];
+ if( GetFieldType( FieldNo ) != 'L' ) return -1;
+ memset( buf, 0x00, 3 );
+ GetField( FieldNo, buf );
+ if( buf[0] == 'Y' || buf[0] == 'y' || buf[0] == 'T' || buf[0] == 't' )
+ return 1;
+ else
+ return 0;
+}
+/************************************************************************/
+//! Get the logical value of the specified field.
+/*!
+*/
+xbShort xbDbf::GetLogicalField( const char * FieldName )
+{
+ xbShort fnum;
+ if(( fnum = GetFieldNo( FieldName )) != -1 )
+ return GetLogicalField( fnum );
+ else
+ return -1;
+}
+/************************************************************************/
+//! Get the string value of the specified field.
+/*!
+*/
+char * xbDbf::GetStringField( const char * FieldName )
+{
+ return GetStringField(GetFieldNo(FieldName));
+}
+/************************************************************************/
+//! Get the string value of the specified field.
+/*!
+*/
+char * xbDbf::GetStringField( xbShort FieldNo )
+{
+ /* allocate memory if needed */
+ if( !SchemaPtr[FieldNo].fp )
+ SchemaPtr[FieldNo].fp = new char[GetFieldLen(FieldNo)+1];
+
+ if( !SchemaPtr[FieldNo].fp )
+ return 0;
+
+ GetField( FieldNo, SchemaPtr[FieldNo].fp );
+ return SchemaPtr[FieldNo].fp;
+}
+/************************************************************************/
diff --git a/xbase64/xbfile.cpp b/xbase64/xbfile.cpp
new file mode 100755
index 0000000..0064a88
--- /dev/null
+++ b/xbase64/xbfile.cpp
@@ -0,0 +1,69 @@
+/* xbfile.cpp
+
+ Xbase64 project source code
+
+ This file contains logic for the basic Xbase class.
+
+ Copyright (C) 1997,2003,2004 Gary A Kunkel
+ Sergiy Yakovin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbfile.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+xbString xbFile::MakeFileName(const char *name)
+{
+ xbString file=name;
+ if (file.isEmpty()) return file;
+ int len=strlen(name);
+ const char *extLower=GetExtWithDot(true);
+ const char *extUpper=GetExtWithDot(false);
+ int lenLower=strlen(extLower);
+ int lenUpper=strlen(extUpper);
+ if (len>lenLower && strcmp(&name[len-lenLower], extLower)==0 ||
+ len>lenUpper && strcmp(&name[len-lenUpper], extUpper)==0) return file;
+ char lastSymbol=name[len-1];
+ file+=GetExtWithDot(lastSymbol<'A' || lastSymbol>'Z');
+ return file;
+}
+
diff --git a/xbase64/xbfile.h b/xbase64/xbfile.h
new file mode 100755
index 0000000..50c69e9
--- /dev/null
+++ b/xbase64/xbfile.h
@@ -0,0 +1,70 @@
+/* xbfile.h
+
+ Xbase project source code
+
+ This file conatains a header file for the xbLock virtual objects which
+ is used for controlling file and record locking. Record and file
+ locking has been rewritten in version 3.
+
+ Copyright (C) 1997,2003,2004 Gary A Kunkel
+ Sergio Yakovin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+/*! \file xblock.h
+*/
+
+#ifndef __XB_FILE_H__
+#define __XB_FILE_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+class XBDLLEXPORT xbFile
+{
+ public:
+ xbFile(){}
+ virtual const char* GetExtWithDot(bool lower)=0;
+ const xbString& GetFileName() {return fileName_;}
+ xbString MakeFileName(const char* filename);
+
+ protected:
+ void SetFileName(const char *filename)
+ {
+ fileName_=MakeFileName(filename);
+ }
+
+ private:
+ xbString fileName_;
+};
+
+#endif // XBFILE_H
diff --git a/xbase64/xbfilter.cpp b/xbase64/xbfilter.cpp
new file mode 100755
index 0000000..104e113
--- /dev/null
+++ b/xbase64/xbfilter.cpp
@@ -0,0 +1,231 @@
+/* xbfilter.cpp
+
+ Xbase project source code
+
+ This file conatains logic for the xbfilter class.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbfilter.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+//#include <xbase64/xbexcept.h>
+
+/*! \file xbfilter.cpp
+*/
+
+#ifdef XB_FILTERS
+/************************************************************************/
+//! Constructor.
+/*!
+ \param dbf
+ \param index
+ \param exp
+*/
+xbFilter::xbFilter( xbDbf * dbf, xbIndex * index, char * exp )
+{
+ xbShort rc;
+ Status = 0;
+ CurFilterRecNo = 0L;
+ d = dbf;
+ i = index;
+// e = 0;
+
+
+ flExpn = new xbExpn( d->xbase );
+ if(( rc = flExpn->ParseExpression( exp, d )) != XB_NO_ERROR )
+ Status = rc;
+ else{
+ if( flExpn->GetExpressionResultType() != 'L' )
+ Status = XB_PARSE_ERROR;
+ }
+}
+
+/***********************************************************************/
+//! Destructor.
+/*!
+*/
+xbFilter::~xbFilter()
+{
+ if( flExpn )
+ delete flExpn;
+}
+
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbFilter::GetFirstFilterRec()
+{
+ xbShort rc;
+
+ if( Status )
+ return Status;
+
+ if( i )
+ rc = i->GetFirstKey();
+ else
+ rc = d->GetFirstRecord();
+
+ while( rc == XB_NO_ERROR ){
+ if(( rc = flExpn->ProcessExpression()) != XB_NO_ERROR )
+ return rc;
+
+ if( flExpn->GetIntResult() )
+ {
+ CurFilterRecNo = d->GetCurRecNo();
+ return XB_NO_ERROR;
+ }
+ if( i )
+ rc = i->GetNextKey();
+ else
+ rc = d->GetNextRecord();
+ }
+ return rc;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbFilter::GetLastFilterRec()
+{
+ xbShort rc;
+
+ if( Status )
+ return Status;
+
+ if( i )
+ rc = i->GetLastKey();
+ else
+ rc = d->GetLastRecord();
+
+ while( rc == XB_NO_ERROR ){
+ if(( rc = flExpn->ProcessExpression()) != XB_NO_ERROR )
+ return rc;
+
+ if( flExpn->GetIntResult() )
+ {
+ CurFilterRecNo = d->GetCurRecNo();
+ return XB_NO_ERROR;
+ }
+ if( i )
+ rc = i->GetPrevKey();
+ else
+ rc = d->GetPrevRecord();
+ }
+ return rc;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbFilter::GetNextFilterRec()
+{
+ xbShort rc;
+
+ if( Status )
+ return Status;
+
+ if( !CurFilterRecNo )
+ return GetFirstFilterRec();
+
+ if( i ){
+ rc = i->GetNextKey();
+ }
+ else
+ rc = d->GetNextRecord();
+
+ while( rc == XB_NO_ERROR ){
+ if(( rc = flExpn->ProcessExpression()) != XB_NO_ERROR )
+ return rc;
+
+ if( flExpn->GetIntResult())
+ {
+ CurFilterRecNo = d->GetCurRecNo();
+ return XB_NO_ERROR;
+ }
+ if( i )
+ rc = i->GetNextKey();
+ else
+ rc = d->GetNextRecord();
+ }
+ return rc;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbFilter::GetPrevFilterRec()
+{
+ xbShort rc;
+
+ if( Status )
+ return Status;
+
+ if( !CurFilterRecNo )
+ return GetLastFilterRec();
+
+ if( i ){
+ rc = i->GetPrevKey();
+ }
+ else
+ rc = d->GetPrevRecord();
+
+ while( rc == XB_NO_ERROR ){
+ if(( rc = flExpn->ProcessExpression()) != XB_NO_ERROR )
+ return rc;
+
+ if( flExpn->GetIntResult())
+ {
+ CurFilterRecNo = d->GetCurRecNo();
+ return XB_NO_ERROR;
+ }
+ if( i )
+ rc = i->GetPrevKey();
+ else
+ rc = d->GetPrevRecord();
+ }
+ return rc;
+}
+/***********************************************************************/
+#endif // XB_FILTERS_ON
diff --git a/xbase64/xbfilter.h b/xbase64/xbfilter.h
new file mode 100755
index 0000000..578a1da
--- /dev/null
+++ b/xbase64/xbfilter.h
@@ -0,0 +1,75 @@
+/* xbfilter.h
+
+ Xbase project source code
+
+ This file conatains a header file for the xbFilter object which
+ is used for filtering data.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+/*! \file xbfilter.h
+*/
+
+#ifndef __XB_FILTER_H__
+#define __XB_FILTER_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+//! xbFilter class
+/*!
+*/
+
+class XBDLLEXPORT xbFilter
+{
+public:
+ xbFilter( xbDbf * dbf, xbIndex * index, char * expression );
+ virtual ~xbFilter();
+
+ xbShort GetFirstFilterRec();
+ xbShort GetLastFilterRec();
+ xbShort GetNextFilterRec();
+ xbShort GetPrevFilterRec();
+ xbShort GetStatus() { return Status; }
+
+protected:
+ xbULong CurFilterRecNo;
+ xbShort Status;
+ xbExpn * flExpn;
+ xbDbf *d;
+ xbIndex *i;
+};
+
+#endif
diff --git a/xbase64/xbindex.cpp b/xbase64/xbindex.cpp
new file mode 100755
index 0000000..f3a9c17
--- /dev/null
+++ b/xbase64/xbindex.cpp
@@ -0,0 +1,220 @@
+/* xbindex.cpp
+
+ Xbase64 project source code
+
+ This file contains the implementation of the xbIndex class.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbindex.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*! \file xbindex.cpp
+*/
+
+#ifdef XB_INDEX_ANY
+//! Constructor
+/*!
+ \param pdbf
+*/
+xbIndex::xbIndex(xbDbf * pdbf)
+{
+ index = this;
+ dbf = pdbf;
+// ExpressionTree = NULL;
+ IxExp = NULL;
+ indexfp = NULL;
+// IndexStatus = 0;
+ CurDbfRec = 0L;
+ KeyBuf = NULL;
+ KeyBuf2 = NULL;
+#ifdef XB_LOCKING_ON
+ LockCnt = 0;
+
+ CurLockCount = 0;
+ CurLockType = -1;
+#endif // XB_LOCKING_ON
+}
+
+/*************************************************************************/
+
+//! Destructor
+/*!
+ \param pdbf
+*/
+xbIndex::~xbIndex()
+{
+ if( IxExp ){
+ delete IxExp;
+ IxExp = NULL;
+ }
+}
+/*************************************************************************/
+
+void xbIndex::Flush()
+{
+ if(indexfp) fflush(indexfp);
+}
+
+/*************************************************************************/
+xbShort xbIndex::OpenIndex(const char* FileName)
+{
+ if (IsOpen()) return XB_ALREADY_OPEN;
+
+ int rc;
+
+ SetFileName(FileName);
+
+ /* open the file */
+ if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL ){
+ //
+ // Try to open read only if can't open read/write
+ //
+ if(( indexfp = fopen( GetFileName(), "rb" )) == NULL )
+ return XB_OPEN_ERROR;
+ }
+
+#ifdef XB_LOCKING_ON
+ /*
+ ** Must turn off buffering when multiple programs may be accessing
+ ** index files.
+ */
+ setbuf( indexfp, NULL );
+#endif
+
+// IndexStatus = 1;
+ if(( rc = GetHeadNode()) != 0){
+ fclose( indexfp );
+ return rc;
+ }
+
+ /* parse the expression */
+/* pre 3.0
+ if(( rc = dbf->xbase->BuildExpressionTree( HeadNode.KeyExpression,
+ strlen( HeadNode.KeyExpression ), dbf )) != XB_NO_ERROR ){
+ return rc;
+ }
+ ExpressionTree = dbf->xbase->GetTree();
+ dbf->xbase->SetTreeToNull();
+*/
+ IxExp = new xbExpn( dbf->xbase );
+ if(( rc = IxExp->BuildExpressionTree( GetKeyExpression(),
+ strlen( GetKeyExpression() ), dbf )) != XB_NO_ERROR ){
+ fclose( indexfp );
+ return rc;
+ }
+
+ rc=AllocKeyBufs();
+ if(rc){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ fclose(indexfp);
+ return rc;
+ }
+
+#ifdef XBASE_DEBUG
+// CheckIndexIntegrity( 0 );
+#endif
+
+#ifdef XB_LOCKING_ON
+ //if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+
+ rc = dbf->AddIndexToIxList( index, GetFileName() );
+ return rc;
+}
+/*************************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbIndex::AllocKeyBufs()
+{
+ KeyBuf = (char *) malloc( GetKeyLen() + 1 );
+ if(KeyBuf==NULL) {
+ return XB_NO_MEMORY;
+ };
+ KeyBuf2 = (char *) malloc( GetKeyLen() + 1);
+ if(KeyBuf2==NULL) {
+ free(KeyBuf);
+ return XB_NO_MEMORY;
+ };
+ memset( KeyBuf, 0x00, GetKeyLen() + 1 );
+ memset( KeyBuf2, 0x00, GetKeyLen() + 1 );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+
+xbShort xbIndex::CloseIndex( void )
+{
+ if(KeyBuf){
+ free(KeyBuf);
+ KeyBuf = NULL;
+ }
+ if(KeyBuf2){
+ free(KeyBuf2);
+ KeyBuf2 = NULL;
+ }
+
+ dbf->RemoveIndexFromIxList( index ); // why not 'this'?
+ FreeNodesMemory();
+ if( IxExp ){
+ delete IxExp;
+ IxExp = 0;
+ }
+
+ if(indexfp){
+ fclose( indexfp );
+ indexfp = NULL;
+ }
+// IndexStatus = 0;
+ return 0;
+}
+/***********************************************************************/
+
+#endif // XB_INDEX_ANY
diff --git a/xbase64/xbindex.h b/xbase64/xbindex.h
new file mode 100755
index 0000000..69f55dc
--- /dev/null
+++ b/xbase64/xbindex.h
@@ -0,0 +1,137 @@
+/* xbindex.h
+
+ Xbase64 project source code
+
+ This file contains a header file for the NTX object, which is used
+ for handling NTX type indices. NTX are the Clipper equivalant of xbNdx
+ files.
+
+ Copyright (C) 1998 SynXis Corp., Bob Cotton
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_INDEX_H__
+#define __XB_INDEX_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#include <xbase64/xbase64.h>
+#include <string.h>
+/*! \file xbindex.h
+*/
+
+#define XB_UNIQUE 1
+#define XB_NOT_UNIQUE 0
+
+//! xbIndex class
+/*!
+*/
+
+class XBDLLEXPORT xbIndex: protected xbFile
+{
+ public:
+ xbIndex() {}
+ xbIndex(xbDbf *);
+
+ virtual ~xbIndex();
+
+ xbShort OpenIndex ( const char * );
+ xbShort CloseIndex();
+ virtual xbShort CreateIndex( const char *, const char *, xbShort, xbShort ) = 0;
+ virtual xbLong GetTotalNodes() = 0;
+ virtual xbULong GetCurDbfRec() = 0;
+ virtual xbShort CreateKey( xbShort, xbShort ) = 0;
+ virtual xbShort GetCurrentKey(char *key) = 0;
+ virtual xbShort AddKey( xbLong ) = 0;
+ virtual xbShort UniqueIndex() = 0;
+ virtual xbShort DeleteKey( xbLong ) = 0;
+ virtual xbShort KeyWasChanged() = 0;
+ virtual xbShort FindKey( const char * ) = 0;
+ virtual xbShort FindKey() = 0;
+ virtual xbShort FindKey( xbDouble ) = 0;
+ virtual xbShort GetNextKey() = 0;
+ virtual xbShort GetLastKey() = 0;
+ virtual xbShort GetFirstKey() = 0;
+ virtual xbShort GetPrevKey() = 0;
+ virtual xbShort ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0) = 0;
+// virtual xbShort KeyExists( char * Key ) { return FindKey( Key, strlen( Key ), 0 ); }
+ virtual xbShort KeyExists( xbDouble ) = 0;
+ virtual xbShort TouchIndex() { return XB_NO_ERROR; }
+ virtual void SetNodeSize(xbShort size) {}
+ virtual xbShort GetNodeSize() { return NodeSize; }
+ virtual void GetExpression(char *buf, int len) = 0;
+ virtual void Flush();
+ virtual const char * GetIxName() {return GetFileName().getData();}
+ xbShort AllocKeyBufs();
+ xbBool IsOpen() {return indexfp!=NULL;}
+
+#ifdef XBASE_DEBUG
+ virtual void DumpHdrNode( xbShort Option ) = 0;
+ virtual void DumpNodeRec( xbLong ) = 0;
+ virtual void DumpNodeChain() = 0;
+ virtual xbShort CheckIndexIntegrity( xbShort ) = 0;
+#endif
+
+#ifdef XB_LOCKING_ON
+// xbShort LockIndex( xbShort LockType );
+// virtual xbShort LockIndex( const xbShort, const xbShort );
+#else
+// virtual xbShort LockIndex( const xbShort, const xbShort ) const { return XB_NO_ERROR; }
+#endif
+
+ protected:
+ virtual xbShort GetHeadNode()=0;
+ virtual xbUShort GetKeyLen()=0;
+ virtual const char* GetKeyExpression()=0;
+ virtual void FreeNodesMemory()=0;
+
+ xbIndex *index;
+ xbDbf *dbf;
+ xbExpn *IxExp; /* index expression defines keys */
+ FILE *indexfp; /* NULL = closed, other = open */
+// int IndexStatus; /* old - 0 = closed, 1 = open */
+ xbULong CurDbfRec; /* current Dbf record number */
+ char *KeyBuf; /* work area key buffer */
+ char *KeyBuf2; /* work area key buffer */
+ xbShort NodeSize;
+
+#ifdef XB_LOCKING_ON
+ int LockCnt; /* current index lock count */
+ int CurLockCount; /* old locking field */
+ int CurLockType; /* old locking field */
+#endif
+};
+
+#endif /* __XB_INDEX_H__ */
diff --git a/xbase64/xblock.cpp b/xbase64/xblock.cpp
new file mode 100755
index 0000000..c44cbb9
--- /dev/null
+++ b/xbase64/xblock.cpp
@@ -0,0 +1,580 @@
+/* xblock.cpp
+
+ Xbase64 project source code
+ written at 35000 feet on SWA
+
+ This file contains the implementation of the xbLock.
+
+ This file conatains a header file for the xbLock virtual objects which
+ is used for controlling file and record locking. Record and file
+ locking has been rewritten in version 3.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xblock.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#ifdef HAVE_IO_H // windows locking
+#include <io.h>
+#endif
+
+#ifdef HAVE_DOS_H // _sleep
+#include <dos.h>
+#endif
+
+//#include <stdio.h>
+//#include <stdlib.h>
+
+/*! \file xblock.cpp
+*/
+#ifdef XB_LOCKING_ON
+
+//! Constructor
+/*!
+ \param pdbf
+*/
+
+/*************************************************************************/
+xbLock::xbLock(xbDbf * pdbf)
+{
+ dbf = pdbf;
+ HdrLockCnt = 0;
+ TableLockCnt = 0;
+ MemoLockCnt = 0;
+ IndexLockCnt = 0;
+ std::cout << "xbLock constructor" << std::cout;
+}
+/*************************************************************************/
+xbLock::~xbLock()
+{
+ std::cout << "xbLock destructor" << std::endl;
+}
+/*************************************************************************/
+
+//! File lock routine
+/*!
+ Lowest level lock routine
+ Locks/unlocks a database,memo or index file.
+ This function assumes the file position has been correctly set
+
+ \param fn file to lock/unlock
+ \param LockType lock type, one of: XB_LOCK or XB_UNLOCK
+ \param lockLen byte count to lock
+*/
+
+#ifdef __WIN32__
+xbShort xbLock::LockFile( int fn, xbShort LockType, xbOffT lockLen)
+{
+ int mode;
+ int rc;
+ int tries = 0;
+
+ /* convert the xbase locking command into a windows locking command */
+ if( LockType == XB_UNLOCK )
+ mode = LK_UNLCK;
+ else if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD )
+ mode = LK_NBLCK;
+ else
+ return XB_INVALID_LOCK_OPTION;
+
+ do{
+ rc = locking( fn, mode, lockLen );
+ if( rc )
+ _sleep( 1 );
+ } while( rc == -1 && tries < dbf->xbase->GetLockRetryCount());
+
+ if( rc )
+ return XB_LOCK_FAILED;
+
+ return 0;
+}
+
+#elif HAVE_FCNTL_H
+
+xbShort xbLock::LockFile( int fn, xbShort LockType, xbOffT lockLen )
+{
+ xbShort cmd, rc;
+ xbShort tries = 0;
+
+/* convert cross platform xbase lock type to unix lock type */
+ if( LockType == XB_UNLOCK )
+ cmd = F_ULOCK;
+else if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD )
+ cmd = F_TLOCK;
+ else
+ return XB_INVALID_LOCK_OPTION;
+
+/* do the actual lock */
+ do{
+ #ifdef _LARGEFILE64_SOURCE
+ rc = lockf64( fn, cmd, lockLen );
+ #else
+ rc = lockf( fn, cmd, lockLen );
+ #endif
+ if( rc == -1 && errno != EINTR ){
+ tries++;
+ sleep(1);
+ }
+ } while( rc == -1 && tries < dbf->xbase->GetLockRetryCount());
+
+ if( rc )
+ return XB_LOCK_FAILED;
+
+ return XB_NO_ERROR;
+}
+#endif // HAVE_FCNTL
+/*************************************************************************/
+/*************************************************************************/
+xbaseLock::xbaseLock( xbDbf * pdbf ) : xbLock( pdbf )
+{
+ std::cout << "xbaseLock constructor" << std::cout;
+}
+/*************************************************************************/
+xbShort xbaseLock::LockTableHeader( xbShort LockType )
+{
+ if( LockType == XB_UNLOCK )
+ return XB_NO_ERROR;
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort xbaseLock::LockTable( xbShort LockType )
+{
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && TableLockCnt ){
+ TableLockCnt++;
+ return XB_NO_ERROR;
+ }
+ if( LockType == XB_UNLOCK && TableLockCnt > 1 ){
+ TableLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( fileno( lfh ), 1, SEEK_SET ) != 1 )
+ return XB_LOCK_FAILED;
+
+ if( LockFile( fileno( lfh ), LockType, 4294967295LL ) != XB_NO_ERROR )
+ return XB_LOCK_FAILED;
+#else
+ if( lseek( fileno( lfh ), 1, SEEK_SET ) != 1 )
+ return XB_LOCK_FAILED;
+
+ if( LockFile( fileno( lfh ), LockType, 4294967295L ) != XB_NO_ERROR )
+ return XB_LOCK_FAILED;
+#endif
+
+ if( LockType == XB_UNLOCK )
+ TableLockCnt--;
+ else
+ TableLockCnt++;
+
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+xbShort xbaseLock::LockRecord(xbShort LockType,xbULong RecNo,xbOffT RecCnt)
+{
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( fileno( lfh ), 100L + RecNo, SEEK_SET ) == -1 ){
+ return XB_LOCK_FAILED;
+ }
+#else
+ if( lseek( fileno( lfh ), 100L + RecNo, SEEK_SET ) == -1 ){
+ return XB_LOCK_FAILED;
+ }
+#endif
+
+ return LockFile( fileno( lfh ), LockType, (xbOffT) RecCnt );
+}
+/*************************************************************************/
+xbShort xbaseLock::LockMemo( xbShort LockType )
+{
+ xbShort rc;
+
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && MemoLockCnt ){
+ MemoLockCnt++;
+ return XB_NO_ERROR;
+ }
+ else if ( LockType == XB_UNLOCK && MemoLockCnt > 1 ){
+ MemoLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( fileno( lfh ), 2, SEEK_SET ) != 2 )
+ return XB_LOCK_FAILED;
+#else
+ if( lseek( fileno( lfh ), 2, SEEK_SET ) != 2 )
+ return XB_LOCK_FAILED;
+#endif
+ rc = LockFile( fileno( lfh ), LockType, 1 );
+
+ if( rc == XB_NO_ERROR ){
+ if( LockType == XB_UNLOCK )
+ MemoLockCnt--;
+ else
+ MemoLockCnt++;
+ }
+ return rc;
+}
+/*************************************************************************/
+//! Lock Index
+/*!
+ Locks all indices for a table when using lock mode XB_XBASE_LOCK_MODE
+
+ \param LockType is one of XB_LOCK, XB_LOCK_HOLD or XB_UNLOCK
+*/
+
+xbShort xbaseLock::LockIndex( xbShort LockType )
+{
+ xbShort rc;
+
+// if( !NdxList )
+// printf( "no index\n" );
+
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && IndexLockCnt ){
+ IndexLockCnt++;
+ return XB_NO_ERROR;
+ }
+ if( LockType == XB_UNLOCK && IndexLockCnt > 1 ){
+ IndexLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( fileno( lfh ), 3, SEEK_SET ) == -1 ){
+ printf( "here cp1\n");
+ return XB_LOCK_FAILED;
+ }
+#else
+ if( lseek( fileno( lfh ), 3, SEEK_SET ) == -1 ){
+ printf( "here cp2\n" );
+ return XB_LOCK_FAILED;
+ }
+#endif
+
+ rc = LockFile( fileno( lfh ), LockType, 1 );
+
+ if( rc == XB_NO_ERROR )
+ if( LockType == XB_UNLOCK )
+ IndexLockCnt--;
+ else
+ IndexLockCnt++;
+
+ return rc;
+}
+/*************************************************************************/
+xbShort xbaseLock::UnlockAll()
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/************************************************************************/
+xbShort xbaseLock::LockInit()
+{
+ xbShort len;
+ xbString lfn;
+
+ lfn = dbf->GetDbfName();
+ lfn.resize( lfn.len() - 3 );
+
+ lfn += ".lck";
+
+#ifdef _LARGEFILE_SOURCE
+ if(( lfh = fopen64( lfn.getData(), "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+#else
+ if(( lfh = fopen( lfn.getData(), "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+#endif
+
+ else
+ return XB_NO_ERROR;
+}
+
+/*************************************************************************/
+/*************************************************************************/
+dbaseLock::dbaseLock( xbDbf * pdbf ) : xbLock( pdbf )
+{
+ std::cout << "dbaseLock constructor" << std::cout;
+}
+/*************************************************************************/
+xbShort dbaseLock::LockTableHeader( xbShort LockType )
+{
+ if( LockType == XB_UNLOCK )
+ return XB_NO_ERROR;
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort dbaseLock::LockTable( xbShort LockType )
+{
+
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && TableLockCnt ){
+ TableLockCnt++;
+ return XB_NO_ERROR;
+ }
+ if( LockType == XB_UNLOCK && TableLockCnt > 1 ){
+ TableLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( dbf->GetDbfFileNo(), 4026531838LL, SEEK_SET ))
+ return XB_LOCK_FAILED;
+
+ if( LockFile( dbf->GetDbfFileNo(), LockType, 1 ) != XB_NO_ERROR )
+ return XB_LOCK_FAILED;
+
+ if( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ){
+ if( LockRecord( XB_LOCK, 1, 4294967295LL ) != XB_NO_ERROR )
+ return LockTable( XB_UNLOCK );
+ LockRecord( XB_UNLOCK, 1, 4294967295LL );
+ }
+
+ if( LockType == XB_UNLOCK )
+ TableLockCnt--;
+ else
+ TableLockCnt++;
+
+ return XB_NO_ERROR;
+#else
+ /* I couldn't figure out how Dbase locks a file at offset 4026531838
+ for a 32 bit platform - if you know how, please let me know
+
+ Gary - gkunkel@zhsac.com
+
+ */
+
+ return XB_INVALID_LOCK_OPTION;
+#endif
+
+}
+
+/*************************************************************************/
+xbShort dbaseLock::LockRecord( xbShort LockType, xbULong RecNo, xbOffT RecCnt )
+{
+#ifdef _LARGEFILE64_SOURCE
+
+ if( lseek64( dbf->GetDbfFileNo(), 4026531838LL - (RecNo+RecCnt-1), SEEK_SET ) == -1 )
+ return XB_LOCK_FAILED;
+
+ return LockFile( dbf->GetDbfFileNo(), LockType, RecCnt );
+
+#else
+
+ /* I couldn't figure out how dbase locks a file at offset 4026531838
+ for a 32 bit platform - if you know how, please let me know
+
+ Gary - gkunkel@zhsac.com
+
+ */
+
+ return XB_INVALID_LOCK_OPTION;
+#endif
+}
+
+/*************************************************************************/
+xbShort dbaseLock::LockMemo( xbShort LockType )
+{
+
+ xbShort rc;
+
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && MemoLockCnt ){
+ MemoLockCnt++;
+ return XB_NO_ERROR;
+ }
+ else if ( LockType == XB_UNLOCK && MemoLockCnt > 1 ){
+ MemoLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+#ifdef _LARGEFILE_SOURCE
+ if( lseek64( dbf->GetMemoFileNo(), 4026531838LL, SEEK_SET ) == -1 )
+ return XB_LOCK_FAILED;
+ rc = LockFile( dbf->GetMemoFileNo(), LockType, 1 );
+#else
+ rc = XB_INVALID_OPTION;
+#endif
+
+ if( rc == XB_NO_ERROR ){
+ if( LockType == XB_UNLOCK )
+ MemoLockCnt--;
+ else
+ MemoLockCnt++;
+ }
+ return rc;
+}
+/*************************************************************************/
+xbShort dbaseLock::LockIndex( xbShort LockType )
+{
+ if( LockType == XB_NO_ERROR )
+ return XB_NO_ERROR;
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort dbaseLock::UnlockAll()
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+/*************************************************************************/
+clipperLock::clipperLock( xbDbf * pdbf ) : xbLock( pdbf )
+{
+ std::cout << "clipperLock constructor" << std::cout;
+}
+/*************************************************************************/
+xbShort clipperLock::LockTableHeader( xbShort LockType )
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort clipperLock::LockTable( xbShort LockType )
+{
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && TableLockCnt ){
+ TableLockCnt++;
+ return XB_NO_ERROR;
+ }
+ if( LockType == XB_UNLOCK && TableLockCnt > 1 ){
+ TableLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+ if( LockRecord( LockType, 1L, 1000000000L ) != XB_NO_ERROR )
+ return XB_LOCK_FAILED;
+
+ if( LockType == XB_UNLOCK )
+ TableLockCnt--;
+ else
+ TableLockCnt++;
+
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+xbShort clipperLock::LockRecord(
+ xbShort LockType, xbULong RecNo, xbOffT RecCnt )
+{
+
+#ifdef _LARGEFILE64_SOURCE
+ if( lseek64( dbf->GetDbfFileNo(), 1000000000L + RecNo, SEEK_SET ))
+ return XB_LOCK_FAILED;
+#else
+ if( lseek( dbf->GetDbfFileNo(), 1000000000L + RecNo, SEEK_SET ))
+ return XB_LOCK_FAILED;
+#endif
+
+ return LockFile( dbf->GetDbfFileNo(), LockType, RecCnt );
+}
+/*************************************************************************/
+xbShort clipperLock::LockMemo( xbShort LockType )
+{
+ return XB_NO_ERROR;
+}
+/*************************************************************************/
+xbShort clipperLock::LockIndex( xbShort LockType )
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort clipperLock::UnlockAll()
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+/*************************************************************************/
+foxproLock::foxproLock( xbDbf * pdbf ) : xbLock( pdbf )
+{
+ std::cout << "foxproLock constructor" << std::cout;
+}
+/*************************************************************************/
+xbShort foxproLock::LockTableHeader( xbShort LockType )
+{
+
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort foxproLock::LockTable( xbShort LockType )
+{
+
+ if(( LockType == XB_LOCK || LockType == XB_LOCK_HOLD ) && TableLockCnt ){
+ TableLockCnt++;
+ return XB_NO_ERROR;
+ }
+ if( LockType == XB_UNLOCK && TableLockCnt > 1 ){
+ TableLockCnt--;
+ return XB_NO_ERROR;
+ }
+
+
+// something goes in here
+
+
+ if( LockType == XB_UNLOCK )
+ TableLockCnt--;
+ else
+ TableLockCnt++;
+
+ return XB_NO_ERROR;
+
+}
+/*************************************************************************/
+xbShort foxproLock::LockRecord( xbShort LockType, xbULong RecNo, xbOffT len )
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort foxproLock::LockMemo( xbShort LockType )
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort foxproLock::LockIndex( xbShort LockType )
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+xbShort foxproLock::UnlockAll()
+{
+ return XB_INVALID_LOCK_OPTION;
+}
+/*************************************************************************/
+
+#endif // XB_LOCKING_ON
+
diff --git a/xbase64/xblock.h b/xbase64/xblock.h
new file mode 100755
index 0000000..50dfcf2
--- /dev/null
+++ b/xbase64/xblock.h
@@ -0,0 +1,159 @@
+/* xblock.h
+
+ Xbase project source code
+
+ This file conatains a header file for the xbLock virtual objects which
+ is used for controlling file and record locking. Record and file
+ locking has been rewritten in version 3.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+/*! \file xblock.h
+*/
+
+#ifndef __XB_XBLOCK_H__
+#define __XB_XBLOCK_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#ifdef XB_LOCKING_ON
+
+//! xbLock class
+/*!
+*/
+
+class XBDLLEXPORT xbLock
+{
+public:
+ xbLock( xbDbf * dbf );
+ virtual ~xbLock();
+ virtual xbShort LockTableHeader( xbShort LockType ) = 0;
+ virtual xbShort LockTable( xbShort LockType ) = 0;
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo, xbOffT len ) = 0;
+ virtual xbShort LockMemo( xbShort LockType ) = 0;
+ virtual xbShort LockIndex( xbShort LockType ) = 0;
+ virtual xbShort UnlockAll() = 0;
+ virtual xbShort LockInit() { return XB_NO_ERROR; }
+
+protected:
+ xbDbf *dbf;
+ xbShort HdrLockCnt;
+ xbShort TableLockCnt;
+ xbShort MemoLockCnt;
+ xbShort IndexLockCnt;
+ xbShort LockFile( int fn, xbShort LockType, xbOffT lockLen );
+};
+
+class XBDLLEXPORT xbaseLock : xbLock
+{
+public:
+ xbaseLock( xbDbf * pdbf );
+ virtual ~xbaseLock() {}
+ virtual xbShort LockTableHeader( xbShort LockType );
+ virtual xbShort LockTable( xbShort LockType );
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo, xbOffT len );
+ virtual xbShort LockMemo( xbShort LockType );
+ virtual xbShort LockIndex( xbShort LockType );
+ virtual xbShort UnlockAll();
+ virtual xbShort LockInit();
+private:
+ FILE *lfh; /* lock file handle */
+
+};
+
+class XBDLLEXPORT dbaseLock : xbLock
+{
+public:
+ dbaseLock( xbDbf * pdbf );
+ virtual ~dbaseLock() {}
+ virtual xbShort LockTableHeader( xbShort LockType );
+ virtual xbShort LockTable( xbShort LockType );
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo, xbOffT len );
+ virtual xbShort LockMemo( xbShort LockType );
+ virtual xbShort LockIndex( xbShort LockType );
+ virtual xbShort UnlockAll();
+};
+
+
+class XBDLLEXPORT clipperLock : xbLock
+{
+public:
+ clipperLock( xbDbf * pdbf );
+ virtual ~clipperLock() {}
+ virtual xbShort LockTableHeader( xbShort LockType );
+ virtual xbShort LockTable( xbShort LockType );
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo, xbOffT len );
+ virtual xbShort LockMemo( xbShort LockType );
+ virtual xbShort LockIndex( xbShort LockType );
+ virtual xbShort UnlockAll();
+};
+
+class XBDLLEXPORT foxproLock : xbLock
+{
+public:
+ foxproLock( xbDbf * pdbf );
+ virtual ~foxproLock() {}
+ virtual xbShort LockTableHeader( xbShort LockType );
+ virtual xbShort LockTable( xbShort LockType );
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo, xbOffT len );
+ virtual xbShort LockMemo( xbShort LockType );
+ virtual xbShort LockIndex( xbShort LockType );
+ virtual xbShort UnlockAll();
+};
+
+class XBDLLEXPORT noLock : xbLock
+{
+public:
+ noLock( xbDbf * pdbf ) : xbLock( pdbf ) {};
+ virtual ~noLock() {}
+ virtual xbShort LockTableHeader( xbShort LockType )
+ { return XB_NO_ERROR; }
+ virtual xbShort LockTable( xbShort LockType )
+ { return XB_NO_ERROR; }
+ virtual xbShort LockRecord( xbShort LockType, xbULong RecNo )
+ { return XB_NO_ERROR; }
+ virtual xbShort LockMemo( xbShort LockType )
+ { return XB_NO_ERROR; }
+ virtual xbShort LockIndex( xbShort LockType )
+ { return XB_NO_ERROR; }
+ virtual xbShort UnlockAll()
+ { return XB_NO_ERROR; }
+};
+
+
+
+
+#endif // XB_LOCKING_ON
+#endif // __XB_XBLOCK_H__
diff --git a/xbase64/xbmemo.cpp b/xbase64/xbmemo.cpp
new file mode 100755
index 0000000..75956dd
--- /dev/null
+++ b/xbase64/xbmemo.cpp
@@ -0,0 +1,1173 @@
+/* xbmemo.cpp
+
+ Xbase64 project source code
+
+ This file contains the basic Xbase64 routines for handling
+ dBASE III+ and dBASE IV style memo .dbt files
+
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+#ifdef XB_MEMO_FIELDS
+
+#include <stdio.h>
+//#include <xbase64/xbexcept.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+
+/*! \file xbmemo.cpp
+*/
+
+/************************************************************************/
+//! Short description
+/*!
+*/
+xbLong xbDbf::CalcLastDataBlock()
+{
+ if( _fseek( mfp, 0, SEEK_END ) != 0 )
+ return XB_SEEK_ERROR;
+ return ( _ftell( mfp ) / MemoHeader.BlockSize );
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param BlocksNeeded
+ \param Location
+ \param PrevNode
+*/
+xbShort xbDbf::GetBlockSetFromChain( xbLong BlocksNeeded,
+ xbLong Location, xbLong PrevNode )
+
+/* this routine grabs a set of blocks out of the free block chain */
+{
+ xbShort rc;
+ xbLong NextFreeBlock2, NewFreeBlocks, SaveNextFreeBlock;
+
+ if(( rc = ReadMemoBlock( Location, 2 )) != XB_NO_ERROR )
+ return rc;
+
+ if( BlocksNeeded == FreeBlockCnt ){ /* grab this whole set of blocks */
+ if( PrevNode == 0 ){ /* first in the chain */
+ MemoHeader.NextBlock = NextFreeBlock;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR )
+ return rc;
+ }
+ else /* remove out of the middle or end */
+ {
+ NextFreeBlock2 = NextFreeBlock;
+ if(( rc = ReadMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ NextFreeBlock = NextFreeBlock2;
+ if(( rc = WriteMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ }
+ }
+
+ else /* only take a portion of this set */
+ {
+ if( PrevNode == 0 ){ /* first in the set */
+ MemoHeader.NextBlock = Location + BlocksNeeded;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR )
+ return rc;
+ FreeBlockCnt -= BlocksNeeded;
+ if(( rc = WriteMemoBlock( MemoHeader.NextBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ }
+ else /* remove out of the middle or end */
+ {
+ NewFreeBlocks = FreeBlockCnt - BlocksNeeded;
+ SaveNextFreeBlock = NextFreeBlock;
+ NextFreeBlock2= Location + BlocksNeeded;
+ if(( rc = ReadMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ NextFreeBlock = NextFreeBlock2;
+ if(( rc = WriteMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ FreeBlockCnt = NewFreeBlocks;
+ NextFreeBlock = SaveNextFreeBlock;
+ if(( rc = WriteMemoBlock( NextFreeBlock2, 2 )) != XB_NO_ERROR )
+ return rc;
+ }
+ }
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param BlocksNeeded
+ \param LastDataBlock
+ \param Location
+ \param PreviousNode
+*/
+xbShort xbDbf::FindBlockSetInChain( xbLong BlocksNeeded,
+ xbLong LastDataBlock, xbLong &Location, xbLong &PreviousNode )
+
+/* this routine searches thru the free node chain in a dbase IV type
+ memo file searching for a place to grab some free blocks for reuse
+
+ LastDataBlock- is the last data block in the file, enter 0
+ for the routine to calculate it.
+ BlocksNeeded - is the size to look in the chain for
+ Location - is the location it finds
+ PreviousNode - is the block number of the node imediately previous
+ to this node in the chain - 0 if header node
+ returns - 0 if no spot in chain found
+ 1 if spot in chain is found
+*/
+{
+ xbShort rc;
+ xbLong LDB, PrevNode, CurNode;
+
+ if( LastDataBlock == 0 )
+ LDB = CalcLastDataBlock();
+ else
+ LDB = LastDataBlock;
+
+ if( MemoHeader.NextBlock < LDB ){
+ PrevNode = 0L;
+ CurNode = MemoHeader.NextBlock;
+ if(( rc = ReadMemoBlock( MemoHeader.NextBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ while( BlocksNeeded > FreeBlockCnt && NextFreeBlock < LDB ){
+ PrevNode = CurNode;
+ CurNode = NextFreeBlock;
+ if(( rc = ReadMemoBlock( NextFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ }
+ if( BlocksNeeded <= FreeBlockCnt ){
+ Location = CurNode;
+ PreviousNode = PrevNode;
+ return 1;
+ }
+ else{ /* no data found and at end of chain */
+ PreviousNode = CurNode;
+ return 0;
+ }
+ }
+ else{
+ PreviousNode = 0;
+ return 0;
+ }
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param BlockSize
+*/
+xbShort xbDbf::SetMemoBlockSize( xbShort BlockSize )
+{
+ if(IsType3Dbt())
+ return XB_NO_ERROR; // not applicable for type 3
+ if( BlockSize % 512 != 0 )
+ return XB_INVALID_BLOCK_SIZE;
+
+ MemoHeader.BlockSize = BlockSize;
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param Option
+*/
+xbShort xbDbf::GetDbtHeader( xbShort Option )
+{
+ char *p;
+ xbShort i;
+ char MemoBlock[24];
+
+ /* Option = 0 --> read only first four bytes
+ 1 --> read the entire thing */
+
+ if( !mfp )
+ return XB_NOT_OPEN;
+
+ if( _fseek( mfp, 0, SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+ if(( fread( MemoBlock, 24, 1, mfp )) != 1 )
+ return XB_READ_ERROR;
+
+ p = MemoBlock;
+ MemoHeader.NextBlock = xbase->GetLong( p );
+ if(IsType3Dbt() || Option == 0)
+ return XB_NO_ERROR;
+
+ /* version IV stuff follows */
+ p+=8;
+ for( i = 0; i < 8; i++, p++ )
+ MemoHeader.FileName[i] = *p;
+ MemoHeader.Version = *p;
+ p+=4;
+ MemoHeader.BlockSize = xbase->GetShort( p );
+ return XB_NO_ERROR;
+}
+
+/***********************************************************************/
+xbShort xbDbf::OpenFPTFile()
+{
+ if (GetFileName().len() < 3)
+ return XB_INVALID_NAME;
+
+ xbShort len = GetFileName().len() - 1;
+ xbString ext = GetFileName().mid(len-2, 3);
+ MemofileName = GetFileName().mid(0, len-2);
+ if (ext == "DBF")
+ MemofileName += "FPT";
+ else
+ if (ext = "dbf")
+ MemofileName += "fpt";
+ else
+ return XB_INVALID_NAME;
+ if ((mfp = fopen(MemofileName, "r+b" )) == NULL){
+ //
+ // Try to open read only if can't open read/write
+ //
+ if ((mfp = fopen(MemofileName, "rb" )) == NULL)
+ return XB_OPEN_ERROR;
+ }
+ char header[8];
+ if ((fread(header, 8, 1, mfp)) != 1)
+ return XB_READ_ERROR;
+
+ char *p = header;
+ MemoHeader.NextBlock = xbase->GetHBFULong(p);
+ p += 6;
+ MemoHeader.BlockSize = xbase->GetHBFShort(p);
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbDbf::OpenMemoFile()
+{
+ if (Version == (char)0xf5 || Version == (char)0x30)
+ return OpenFPTFile();
+
+ xbShort len, rc;
+ xbOffT Size, NewSize, l;
+ MemofileName = GetFileName();
+ len = GetFileName().len() - 1;
+ if( MemofileName[len] == 'F' )
+ MemofileName.putAt(len, 'T');
+ else if( MemofileName[len] == 'f' )
+ MemofileName.putAt(len, 't');
+ else
+ return XB_INVALID_NAME;
+
+ if(( mfp = fopen( MemofileName, "r+b" )) == NULL ){
+ //
+ // Try to open read only if can't open read/write
+ //
+ if(( mfp = fopen( MemofileName, "rb" )) == NULL )
+ return XB_OPEN_ERROR;
+ }
+#ifdef XB_LOCKING_ON
+ setbuf( mfp, NULL );
+#endif
+ if(( rc = GetDbtHeader(1)) != 0 ){
+ fclose( mfp );
+ return rc;
+ }
+
+ len = GetMemoBlockSize();
+ if( len == 0 || ((len % 512) != 0 )){
+ fclose( mfp );
+ return XB_INVALID_BLOCK_SIZE;
+ }
+
+ /* logic to verify file size is a multiple of block size */
+ if(( rc = _fseek( mfp, 0, SEEK_END )) != 0 ){
+ fclose( mfp );
+ return XB_SEEK_ERROR;
+ }
+
+ /* if the file is not a multiple of block size, fix it, append nulls */
+ Size = _ftell( mfp );
+ if(( Size % MemoHeader.BlockSize ) != 0 ){
+ NewSize = ( Size / MemoHeader.BlockSize + 1) * MemoHeader.BlockSize;
+ for( l = Size; l < NewSize; l++ )
+ fputc( 0x00, mfp );
+ }
+
+ if(( mbb = (void *) malloc(len)) == NULL ){
+ fclose( mfp );
+ return XB_NO_MEMORY;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbDbf::CreateMemoFile( void )
+{
+ xbShort len,i;
+ char *sp;
+ char buf[4];
+
+ len = GetMemoBlockSize();
+ if( len == 0 || len % 512 != 0 )
+ return XB_INVALID_BLOCK_SIZE;
+
+ if(( sp = (char*)strrchr(GetFileName(), PATH_SEPARATOR)) != NULL )
+ sp++;
+ else
+ sp = MemoHeader.FileName;
+
+ memset( MemoHeader.FileName, 0x00, 8 );
+
+ for( i = 0; i < 8 && *sp != '.'; i++ )
+ MemoHeader.FileName[i] = *sp++;
+
+ MemofileName = GetFileName();
+
+ len = GetFileName().len() - 1;
+ if( MemofileName[len] == 'F' )
+ MemofileName.putAt(len, 'T');
+ else if( MemofileName[len] == 'f' )
+ MemofileName.putAt(len, 't');
+ else
+ return XB_INVALID_NAME;
+
+ /* Initialize the variables */
+ MemoHeader.NextBlock = 1L;
+
+ if(( mfp = fopen( MemofileName, "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+#ifdef XB_LOCKING_ON
+ setbuf( mfp, NULL );
+#endif
+
+ if(( _fseek( mfp, 0, SEEK_SET )) != 0 ){
+ fclose( mfp );
+ return XB_SEEK_ERROR;
+ }
+
+ memset( buf, 0x00, 4 );
+ xbase->PutLong( buf, MemoHeader.NextBlock );
+ if(( fwrite( &buf, 4, 1, mfp )) != 1 ){
+ fclose( mfp );
+ return XB_WRITE_ERROR;
+ }
+
+ if( IsType3Dbt() ){ /* dBASE III+ */
+ for( i = 0; i < 12; i++ ) fputc( 0x00, mfp );
+ fputc( 0x03, mfp );
+ for( i = 0; i < 495; i++ ) fputc( 0x00, mfp );
+ }
+ else
+ {
+ for( i = 0; i < 4; i++ ) fputc( 0x00, mfp );
+ fwrite( &MemoHeader.FileName, 8, 1, mfp );
+ for( i = 0; i < 4; i++ ) fputc( 0x00, mfp );
+ memset( buf, 0x00, 2 );
+ xbase->PutShort( buf, MemoHeader.BlockSize );
+ if(( fwrite( &buf, 2, 1, mfp )) != 1 ){
+ fclose( mfp );
+ return XB_WRITE_ERROR;
+ }
+ for( i = 22; i < MemoHeader.BlockSize; i++ ) fputc( 0x00, mfp );
+ }
+
+ if(( mbb = (void *) malloc( MemoHeader.BlockSize )) == NULL ){
+ fclose( mfp );
+ return XB_NO_MEMORY;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param BlockNo
+ \param Option
+*/
+
+/* Option = 0 - 1st Block of a set of valid data blocks, load buckets */
+/* Option = 1 - subsequant block of data in a multi block set or db III*/
+/* Option = 2 - 1st block of a set of free blocks, load buckets */
+/* Option = 3 - read 8 bytes of a block, don't load any buckets */
+/* Option = 4 - read 8 bytes of a block, load data buckets */
+
+xbShort xbDbf::ReadMemoBlock( xbLong BlockNo, xbShort Option )
+{
+ size_t ReadSize;
+ CurMemoBlockNo = -1;
+
+ if( BlockNo < 1L )
+ return XB_INVALID_BLOCK_NO;
+
+ if( _fseek( mfp,((xbOffT)BlockNo*MemoHeader.BlockSize), SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+
+ if( Option == 0 || Option == 1 )
+ ReadSize = MemoHeader.BlockSize;
+ else
+ ReadSize = 8L;
+
+ if(fread( mbb, ReadSize, 1, mfp ) != 1 )
+ return XB_READ_ERROR;
+
+ if( Option == 0 || Option == 4){ // 1st block of a set of valid data blocks
+ mfield1 = xbase->GetShort( (char *) mbb );
+ MStartPos = xbase->GetShort( (char *) mbb+2 );
+ MFieldLen = xbase->GetLong ( (char *) mbb+4 );
+ }
+ else if( Option == 2 ){ // 1st block of a set of free blocks
+ NextFreeBlock = xbase->GetLong( (char *) mbb );
+ FreeBlockCnt = xbase->GetLong( (char *) mbb+4 );
+ }
+
+ if( Option == 0 || Option == 1 )
+ CurMemoBlockNo = BlockNo;
+
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param BlockNo
+ \param Option
+*/
+xbShort xbDbf::WriteMemoBlock( xbLong BlockNo, xbShort Option )
+{
+/* Option = 0 - 1st Block of a set of valid data blocks, set buckets */
+/* Option = 1 - subsequant block of data in a multi block set or db III */
+/* Option = 2 - 1st block of a set offree blocks, set buckets */
+
+ xbLong WriteSize;
+
+ if( BlockNo < 1L )
+ return XB_INVALID_BLOCK_NO;
+
+ CurMemoBlockNo = -1;
+
+ if( Option == 0 ){
+ xbase->PutShort( (char *) mbb, mfield1 );
+ xbase->PutShort( (char *) mbb+2, MStartPos );
+ xbase->PutLong ( (char *) mbb+4, MFieldLen );
+ WriteSize = MemoHeader.BlockSize;
+ }
+ else if( Option == 2 ){
+ xbase->PutLong((char *) mbb, NextFreeBlock );
+ xbase->PutLong((char *) mbb+4, FreeBlockCnt );
+ WriteSize = 8L;
+ }
+ else
+ WriteSize = MemoHeader.BlockSize;
+
+ if( _fseek( mfp,((xbOffT)BlockNo*MemoHeader.BlockSize), SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+ if(( fwrite( mbb, WriteSize, 1, mfp )) != 1 )
+ return XB_WRITE_ERROR;
+
+ if( Option == 0 || Option == 1 )
+ CurMemoBlockNo = BlockNo;
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+ \param len
+ \param Buf
+ \param LockOpt
+*/
+xbShort xbDbf::GetFPTField(xbShort FieldNo, xbLong len,
+ char * Buf, xbShort LockOpt) {
+
+ if (FieldNo < 0 || FieldNo > (NoOfFields - 1))
+ return XB_INVALID_FIELDNO;
+
+ if (GetFieldType(FieldNo) != 'M')
+ return XB_NOT_MEMO_FIELD;
+
+#ifdef XB_LOCKING_ON
+// if( LockOpt != -1 )
+// if( LockMemoFile( XB_LOCK ) != XB_NO_ERROR )
+// return XB_LOCK_FAILED;
+#endif
+
+ xbLong BlockNo;
+ char buf[18];
+
+ if( Version == (char)0x30 ) {
+ memset( buf, 0x00, 18 ) ;
+ GetField( FieldNo, buf );
+ BlockNo = xbase->GetLong((char*) buf);
+ } else {
+ BlockNo = GetLongField(FieldNo);
+ }
+
+ if ( BlockNo == 0L )
+ return 0L;
+
+ // Seek to start_of_block + 4
+
+
+// FIXME LOCK
+
+#ifdef XB_LOCKING_ON
+// try {
+#endif
+ if (_fseek(mfp, ((xbOffT)BlockNo * MemoHeader.BlockSize + 4), SEEK_SET) != 0)
+ return XB_SEEK_ERROR;
+ char h[4];
+ if ((fread(h, 4, 1, mfp)) != 1)
+ return XB_READ_ERROR;
+
+ xbULong fLen = xbase->GetHBFULong(h);
+
+ xbULong l = (fLen < (xbULong)len) ? fLen : len;
+ if ((fread(Buf, l, 1, mfp)) != 1)
+ return XB_READ_ERROR;
+ Buf[l]=0;
+#ifdef XB_LOCKING_ON
+// }
+// catch (...) {
+// if (LockOpt != -1)
+// LockMemoFile( XB_UNLOCK );
+// throw;
+// }
+#endif
+
+#ifdef XB_LOCKING_ON
+// if (LockOpt != -1)
+// LockMemoFile( XB_UNLOCK );
+#endif
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+ \param len
+ \param Buf
+ \param LockOpt
+*/
+xbShort xbDbf::GetMemoField( xbShort FieldNo, xbLong len,
+ char * Buf, xbShort LockOpt )
+{
+ if( Version == (char)0xf5 || Version == (char)0x30 )
+ return GetFPTField(FieldNo, len, Buf, LockOpt);
+
+ xbLong BlockNo, Tcnt, Scnt;
+ char *tp, *sp; /* target and source pointers */
+ xbShort rc;
+ xbShort Vswitch;
+ xbLong MemoLen;
+
+ if( FieldNo < 0 || FieldNo > ( NoOfFields - 1 ))
+ return XB_INVALID_FIELDNO;
+
+ if( GetFieldType( FieldNo ) != 'M' )
+ return XB_NOT_MEMO_FIELD;
+
+#ifdef XB_LOCKING_ON
+// if( LockOpt != -1 )
+// if(( rc = LockMemoFile( LockOpt, XB_LOCK )) != XB_NO_ERROR )
+// return XB_LOCK_FAILED;
+#endif
+
+ if(( BlockNo = GetLongField( FieldNo )) == 0 ){
+#ifdef XB_LOCKING_ON
+// if( LockOpt != -1 )
+// LockMemoFile( XB_UNLOCK );
+#endif
+ return XB_NO_MEMO_DATA;
+ }
+
+ if( IsType3Dbt() )
+ Vswitch = 1;
+ else
+ Vswitch = 0;
+
+ if(( rc = ReadMemoBlock( BlockNo, Vswitch )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( LockOpt != -1 )
+// LockMemoFile( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ tp = Buf;
+ sp = (char *) mbb;
+
+ if( IsType4Dbt() ){
+ sp+=8;
+ Scnt = 8L;
+ }
+ else
+ Scnt = 0L;
+
+ Tcnt = 0L;
+ MemoLen = GetMemoFieldLen( FieldNo );
+ while( Tcnt < len && Tcnt < MemoLen ){
+ *tp++ = *sp++;
+ Scnt++;
+ Tcnt++;
+ if( Scnt >= MemoHeader.BlockSize ){
+ BlockNo++;
+ if(( rc = ReadMemoBlock( BlockNo, 1 )) != 0 )
+ return rc;
+ Scnt = 0;
+ sp = (char *) mbb;
+ }
+ }
+#ifdef XB_LOCKING_ON
+ //if( LockOpt != -1 )
+// LockMemoFile( XB_LOCK );
+#endif
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+*/
+xbLong xbDbf::GetFPTFieldLen( xbShort FieldNo )
+{
+ xbLong BlockNo;
+ if(( BlockNo = GetLongField(FieldNo)) == 0L )
+ return 0L;
+ // Seek to start_of_block + 4
+ if(_fseek(mfp, ((xbOffT)BlockNo * MemoHeader.BlockSize + 4), SEEK_SET) != 0)
+ return XB_SEEK_ERROR;
+ char h[4];
+ if((fread(h, 4, 1, mfp)) != 1)
+ return XB_READ_ERROR;
+
+ return xbase->GetHBFULong(h);
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+*/
+xbLong xbDbf::GetMemoFieldLen( xbShort FieldNo ) {
+ if (Version == (char)0xf5 || Version == (char)0x30 )
+ return GetFPTFieldLen(FieldNo);
+
+ xbLong BlockNo, ByteCnt;
+ xbShort scnt, NotDone;
+ char *sp, *spp;
+
+ if(( BlockNo = GetLongField( FieldNo )) == 0L )
+ return 0L;
+
+ if( IsType4Dbt()){ /* dBASE IV */
+ if( BlockNo == CurMemoBlockNo && CurMemoBlockNo != -1 )
+ return MFieldLen - MStartPos;
+ if( ReadMemoBlock( BlockNo, 0 ) != XB_NO_ERROR )
+ return 0L;
+ return MFieldLen - MStartPos;
+ } else { /* version 0x03 dBASE III+ */
+ ByteCnt = 0L;
+ spp = NULL;
+ NotDone = 1;
+ while( NotDone ){
+ if( ReadMemoBlock( BlockNo++, 1 ) != XB_NO_ERROR )
+ return 0L;
+ scnt = 0;
+ sp = (char *) mbb;
+ while( scnt < 512 && NotDone ){
+ if( *sp == 0x1a && *spp == 0x1a )
+ NotDone = 0;
+ else{
+ ByteCnt++; scnt++; spp = sp; sp++;
+ }
+ }
+ }
+ if( ByteCnt > 0 ) ByteCnt--;
+ return ByteCnt;
+ }
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbDbf::MemoFieldsPresent() const
+{
+ xbShort i;
+ for( i = 0; i < NoOfFields; i++ )
+ if( GetFieldType( i ) == 'M' )
+ return 1;
+
+ return 0;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+*/
+xbShort xbDbf::DeleteMemoField( xbShort FieldNo )
+{
+ xbLong SBlockNo, SNoOfBlocks, SNextBlock;
+ xbLong LastFreeBlock, LastFreeBlockCnt, LastDataBlock;
+ xbShort rc;
+
+ NextFreeBlock = 0L;
+ LastFreeBlockCnt = 0L;
+ LastFreeBlock = 0L;
+
+ if( IsType3Dbt() ){ /* type III */
+ PutField( FieldNo, " " );
+ return XB_NO_ERROR;
+ }
+
+ /* Get Block Number */
+ if(( SBlockNo = GetLongField( FieldNo )) == 0 )
+ return XB_INVALID_BLOCK_NO;
+
+ /* Load the first block */
+
+ if(( rc = ReadMemoBlock( SBlockNo, 4 )) != XB_NO_ERROR )
+ return rc;
+
+ if( (MFieldLen+2) % MemoHeader.BlockSize )
+ SNoOfBlocks = (MFieldLen+2)/MemoHeader.BlockSize+1L;
+ else
+ SNoOfBlocks = (MFieldLen+2)/MemoHeader.BlockSize;
+
+ /* Determine last good data block */
+ LastDataBlock = CalcLastDataBlock();
+
+ /* position to correct location in chain */
+ NextFreeBlock = MemoHeader.NextBlock;
+ while( SBlockNo > NextFreeBlock && SBlockNo < LastDataBlock ){
+ LastFreeBlock = NextFreeBlock;
+ if(( rc = ReadMemoBlock( NextFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ LastFreeBlockCnt = FreeBlockCnt;
+ }
+
+
+ /* if next block should be concatonated onto the end of this set */
+
+ if((SBlockNo+SNoOfBlocks) == NextFreeBlock && NextFreeBlock < LastDataBlock )
+ {
+ if(( rc = ReadMemoBlock( NextFreeBlock, 2 )) != XB_NO_ERROR )
+ return XB_NO_ERROR;
+ SNoOfBlocks += FreeBlockCnt;
+ SNextBlock = NextFreeBlock;
+ } else if( LastFreeBlock == 0L )
+ SNextBlock = MemoHeader.NextBlock;
+ else
+ SNextBlock = NextFreeBlock;
+
+ /* if this is the first set of free blocks */
+ if( LastFreeBlock == 0L ){
+ /* 1 - write out the current block */
+ /* 2 - update header block */
+ /* 3 - write header block */
+ /* 4 - update data field */
+
+ NextFreeBlock = SNextBlock;
+ FreeBlockCnt = SNoOfBlocks;
+ if(( rc = WriteMemoBlock( SBlockNo, 2 )) != XB_NO_ERROR )
+ return rc;
+
+ MemoHeader.NextBlock = SBlockNo;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR )
+ return rc;
+ PutField( FieldNo, " " );
+ return XB_NO_ERROR;
+ }
+
+/* determine if this block set should be added to the previous set */
+
+ if(( LastFreeBlockCnt + LastFreeBlock ) == SBlockNo ){
+ if(( rc = ReadMemoBlock( LastFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ NextFreeBlock = SNextBlock;
+ FreeBlockCnt += SNoOfBlocks;
+ if(( rc = WriteMemoBlock( LastFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ PutField( FieldNo, " " );
+ return XB_NO_ERROR;
+ }
+
+ /* insert into the chain */
+ /* 1 - set the next bucket on the current node */
+ /* 2 - write this node */
+ /* 3 - go to the previous node */
+ /* 4 - insert this nodes id into the previous node set */
+ /* 5 - write previous node */
+
+ FreeBlockCnt = SNoOfBlocks;
+ if(( rc = WriteMemoBlock( SBlockNo, 2 )) != XB_NO_ERROR )
+ return rc;
+ if(( rc = ReadMemoBlock( LastFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ NextFreeBlock = SBlockNo;
+ if(( rc = WriteMemoBlock( LastFreeBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ PutField( FieldNo, " " );
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+ \param DataLen
+ \param Buf
+*/
+
+xbShort xbDbf::AddMemoData( xbShort FieldNo, xbLong DataLen,
+ const char * Buf )
+{
+ xbShort rc;
+ xbLong BlocksNeeded, LastDataBlock;
+ xbLong PrevNode, HeadBlock;
+ xbLong TotalLen; /* total length of needed area for memo field */
+
+ TotalLen = DataLen+2;
+ LastDataBlock = CalcLastDataBlock();
+
+ if( IsType3Dbt() || /* always append to end */
+ ( LastDataBlock == MemoHeader.NextBlock )){ /* no free space */
+ if( TotalLen % MemoHeader.BlockSize )
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize + 1;
+ else
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize;
+
+ MemoHeader.NextBlock = LastDataBlock + BlocksNeeded; /* reset to eof */
+ if(( rc = PutMemoData( LastDataBlock, BlocksNeeded, DataLen, Buf ))
+ != XB_NO_ERROR )
+ return rc;
+ HeadBlock = LastDataBlock;
+ if(( rc = UpdateHeadNextNode()) != XB_NO_ERROR )
+ return rc;
+ }else{
+ TotalLen += 8;
+ if( TotalLen % MemoHeader.BlockSize )
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize + 1;
+ else
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize;
+
+ if(( rc = FindBlockSetInChain( BlocksNeeded, LastDataBlock,
+ HeadBlock, PrevNode )) == 1 ){
+ if(( rc = GetBlockSetFromChain( BlocksNeeded, HeadBlock, PrevNode ))
+ != XB_NO_ERROR )
+ return rc;
+ if(( rc = PutMemoData( HeadBlock, BlocksNeeded, DataLen, Buf ))
+ != XB_NO_ERROR )
+ return rc;
+ } else { /* append to the end */
+ /* if header block needed updated, already done by here */
+ if(( rc = PutMemoData( LastDataBlock, BlocksNeeded, DataLen, Buf ))
+ != XB_NO_ERROR )
+ return rc;
+ HeadBlock = LastDataBlock;
+ if(( rc = ReadMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ NextFreeBlock += BlocksNeeded;
+ if(( rc = WriteMemoBlock( PrevNode, 2 )) != XB_NO_ERROR )
+ return rc;
+ }
+ }
+ PutLongField( FieldNo, HeadBlock );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbDbf::UpdateHeadNextNode() const
+{
+ char buf[4];
+ memset( buf, 0x00, 4 );
+ xbase->PutLong( buf, MemoHeader.NextBlock );
+ if(( _fseek( mfp, 0, SEEK_SET )) != 0 )
+ return XB_SEEK_ERROR;
+
+ if(( fwrite( &buf, 4, 1, mfp )) != 1 )
+ return XB_WRITE_ERROR;
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param StartBlock First block to write
+ \param BlocksNeeded Total number of blocks needed
+ \param DataLen Length of data to write
+ \param Buf Actual data
+*/
+xbShort xbDbf::PutMemoData( xbLong StartBlock, xbLong BlocksNeeded,
+ xbLong DataLen, const char *Buf )
+{
+ xbShort i, rc, Tctr;
+ xbShort BytesProcessed; // bytes processed so far
+ xbShort TotalLen; // total length of data
+ xbLong CurBlock;
+ char *tp;
+ const char *sp;
+
+ TotalLen = DataLen + 2;
+ CurBlock = StartBlock;
+ memset( (char *) mbb, 0x00, MemoHeader.BlockSize );
+ tp = (char *) mbb;
+ sp = Buf;
+ BytesProcessed = 0; /* total length processed */
+
+
+ if( IsType3Dbt() )
+ Tctr = 0;
+ else{ /* dBASE IV */
+ tp += 8;
+ Tctr = 8;
+ }
+
+ for( i = 0; i < BlocksNeeded; i++ ){
+ while( Tctr < MemoHeader.BlockSize && BytesProcessed < TotalLen ){
+ if( BytesProcessed >= DataLen )
+ *tp++ = 0x1a; /* end of data marker */
+ else
+ *tp++ = *sp++; /* copy data to memo block buffer */
+ Tctr++;
+ BytesProcessed++;
+ }
+
+ /* if incomplete block, finish it out with 0x00 */
+ while( Tctr++ < MemoHeader.BlockSize )
+ *tp++ = 0x00;
+
+ if( i == 0 && IsType4Dbt() ){
+ mfield1 = -1;
+ MStartPos = 8;
+ MFieldLen = DataLen + MStartPos;
+ if(( rc = WriteMemoBlock( CurBlock++, 0 )) != XB_NO_ERROR )
+ return rc;
+ } else {
+ if(( rc = WriteMemoBlock( CurBlock++, 1 )) != XB_NO_ERROR )
+ return rc;
+ }
+ Tctr = 0;
+ tp = (char *) mbb;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+ \param DataLen
+ \param Buf
+ \param LockOpt
+*/
+xbShort xbDbf::UpdateMemoData( xbShort FieldNo, xbLong DataLen,
+ const char * Buf, xbShort LockOpt )
+{
+ xbShort rc;
+ xbLong TotalLen;
+ xbLong BlocksNeeded, BlocksAvailable;
+
+ #ifdef XB_LOCKING_ON
+ if( LockOpt != -1 )
+// if(( rc = LockMemoFile( XB_LOCK )) != XB_NO_ERROR )
+// return XB_LOCK_FAILED;
+ #endif
+
+ if( DataLen ){
+ TotalLen = DataLen + 2; // add 2 eod 0x1a chars
+ if( IsType4Dbt()) TotalLen += 8; // leading fields for dbase iv
+ }
+ else
+ TotalLen = 0;
+
+ if( DataLen == 0L ){ /* handle delete */
+ if( MemoFieldExists( FieldNo )){
+ if(( rc = DeleteMemoField( FieldNo )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+ }
+ } else if((IsType3Dbt() || GetMemoFieldLen(FieldNo)==0L)){
+ if(( rc = AddMemoData( FieldNo, DataLen, Buf )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+ } else { /* version IV type files, reuse unused space */
+ if( TotalLen % MemoHeader.BlockSize )
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize + 1;
+ else
+ BlocksNeeded = TotalLen / MemoHeader.BlockSize;
+
+ if(( rc = ReadMemoBlock( GetLongField( FieldNo ), 4 )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+
+ if( (MFieldLen+2) % MemoHeader.BlockSize )
+ BlocksAvailable = (MFieldLen+2) / MemoHeader.BlockSize + 1;
+ else
+ BlocksAvailable = (MFieldLen+2) / MemoHeader.BlockSize;
+
+ if( BlocksNeeded == BlocksAvailable ){
+ if(( rc = PutMemoData( GetLongField( FieldNo ), BlocksNeeded,
+ DataLen, Buf )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+ } else {
+ if(( rc = DeleteMemoField( FieldNo )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+ if(( rc = AddMemoData( FieldNo, DataLen, Buf )) != XB_NO_ERROR ){
+ #ifdef XB_LOCKING_ON
+// LockMemoFile( XB_UNLOCK );
+ #endif
+ return rc;
+ }
+ }
+ }
+
+
+ #ifdef XB_LOCKING_ON
+// if( LockOpt != -1 )
+// if(( rc = LockMemoFile( XB_UNLOCK )) != XB_NO_ERROR )
+// return XB_LOCK_FAILED;
+ #endif
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param FieldNo
+*/
+xbShort xbDbf::MemoFieldExists( xbShort FieldNo ) const
+{
+ if( GetLongField( FieldNo ) == 0L )
+ return 0;
+ else
+ return 1;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+#ifdef XBASE_DEBUG
+void xbDbf::DumpMemoHeader() const
+{
+ xbShort i;
+ std::cout << "\n*********************************";
+ std::cout << "\nMemo header data...";
+ std::cout << "\nNext Block " << MemoHeader.NextBlock;
+ if( IsType4Dbt() ){
+ std::cout << "\nFilename ";
+ for( i = 0; i < 8; i++ )
+ std::cout << MemoHeader.FileName[i];
+ }
+ std::cout << "\nBlocksize " << MemoHeader.BlockSize;
+ return;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbDbf::DumpMemoFreeChain()
+{
+ xbShort rc;
+ xbLong CurBlock, LastDataBlock;
+
+ if(( rc = GetDbtHeader(1)) != XB_NO_ERROR )
+ return rc;
+ LastDataBlock = CalcLastDataBlock();
+ CurBlock = MemoHeader.NextBlock;
+ std::cout << "Total blocks in file = " << LastDataBlock << std::endl;
+ std::cout << "Head Next Block = " << CurBlock << std::endl;;
+ while( CurBlock < LastDataBlock ){
+ if(( rc = ReadMemoBlock( CurBlock, 2 )) != XB_NO_ERROR )
+ return rc;
+ std::cout << "**********************************" << std::endl;
+ std::cout << "This Block = " << CurBlock << std::endl;
+ std::cout << "Next Block = " << NextFreeBlock << std::endl;
+ std::cout << "No Of Blocks = " << FreeBlockCnt << std::endl;
+ CurBlock = NextFreeBlock;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+void xbDbf::DumpMemoBlock() const
+{
+ xbShort i;
+ char *p;
+ p = (char *) mbb;
+ if( IsType3Dbt() ){
+ for( i = 0; i < 512; i++ )
+ std::cout << *p++;
+ } else {
+ std::cout << "\nField1 => " << mfield1;
+ std::cout << "\nStart Pos => " << MStartPos;
+ std::cout << "\nField Len => " << MFieldLen;
+ std::cout << "\nBlock data => ";
+ p += 8;
+ for( i = 8; i < MemoHeader.BlockSize; i++ )
+ std::cout << *p++;
+ }
+ return;
+}
+#endif /* XBASE_DEBUG */
+#endif /* MEMO_FIELD */
diff --git a/xbase64/xbmindex.h b/xbase64/xbmindex.h
new file mode 100755
index 0000000..2cda630
--- /dev/null
+++ b/xbase64/xbmindex.h
@@ -0,0 +1,14 @@
+#ifndef xbMultiIndex_h
+#define xbMultiIndex_h
+
+#include <xbase64/xbase64.h>
+
+class xbMultiIndex: public xbIndex
+{
+ public:
+ xbMultiIndex(xbDbf* dbf): xbIndex(dbf) {}
+ virtual ~xbMultiIndex() {}
+ virtual xbShort CreateIndex(const char * filename, const char* tag,
+ const char* expr, xbShort unique, xbShort overwrite)=0;
+};
+#endif
diff --git a/xbase64/xbndx.cpp b/xbase64/xbndx.cpp
new file mode 100755
index 0000000..e89bc7a
--- /dev/null
+++ b/xbase64/xbndx.cpp
@@ -0,0 +1,2402 @@
+/* xbndx.cpp
+
+ Xbase64 project source code
+
+ NDX indexing routines for X-Base
+
+ Copyright (C) 1997,2003,2004 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbndx.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+#include <iostream>
+
+#ifdef XB_INDEX_NDX
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+//#include <xbase64/xbexcept.h>
+
+/*! \file xbndx.cpp
+*/
+
+/***********************************************************************/
+//! Short description
+/*!
+*/
+/* This routine dumps the node chain to stdout */
+#ifdef XBASE_DEBUG
+void xbNdx::DumpNodeChain()
+{
+ xbNdxNodeLink *n;
+ std::cout << std::endl << "*************************" << std::endl;
+ std::cout << "xbNodeLinkCtr = " << xbNodeLinkCtr << std::endl;
+ std::cout << "Reused = " << ReusedxbNodeLinks << std::endl;
+
+ n = NodeChain;
+ while(n){
+ std::cout << "xbNodeLink Chain ->" << n->NodeNo << std::endl;
+ std::cout << " CurKeyNo ->" << n->CurKeyNo << std::endl;
+ n = n->NextNode;
+ }
+ n = FreeNodeChain;
+ while(n){
+ std::cout << "FreexbNodeLink Chain " << n->NodeNo << std::endl;
+ n = n->NextNode;
+ }
+ n = DeleteChain;
+ while(n){
+ std::cout << "DeleteLink Chain " << n->NodeNo << std::endl;
+ n = n->NextNode;
+ }
+}
+#endif
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+/* This routine returns a chain of one or more index nodes back to the */
+/* free node chain */
+
+void xbNdx::ReleaseNodeMemory(xbNdxNodeLink *n, xbBool doFree)
+{
+ xbNdxNodeLink *temp;
+
+ if(doFree){
+ while(n){
+ temp = n->NextNode;
+ free(n);
+ n = temp;
+ }
+ } else {
+ if( !FreeNodeChain )
+ FreeNodeChain = n;
+ else { /* put this list at the end */
+ temp = FreeNodeChain;
+ while( temp->NextNode )
+ temp = temp->NextNode;
+ temp->NextNode = n;
+ }
+ }
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+/* This routine returns a node from the free chain if available, */
+/* otherwise it allocates new memory for the requested node */
+
+xbNdxNodeLink * xbNdx::GetNodeMemory()
+{
+ xbNdxNodeLink * temp;
+ if( FreeNodeChain ){
+ temp = FreeNodeChain;
+ FreeNodeChain = temp->NextNode;
+ ReusedxbNodeLinks++;
+ } else {
+ temp = (xbNdxNodeLink *) malloc( sizeof( xbNdxNodeLink ));
+ xbNodeLinkCtr++;
+ }
+ memset( temp, 0x00, sizeof( xbNdxNodeLink ));
+ return temp;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+#ifdef XBASE_DEBUG
+void xbNdx::DumpHdrNode( xbShort opt )
+{
+ if( opt ){
+ FILE * log;
+ if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return;
+ fprintf( log, "Index Header Node for %s\n", GetFileName().getData());
+ fprintf( log, "--------------------------------\n" );
+ fprintf( log, "Start node = %ld\n", HeadNode.StartNode );
+ fprintf( log, "Total nodes = %ld\n", HeadNode.TotalNodes );
+ fprintf( log, "No of keys = %ld\n", HeadNode.NoOfKeys );
+ fprintf( log, "Key Length = %d\n", HeadNode.KeyLen );
+ fprintf( log, "Keys Per Node = %d\n", HeadNode.KeysPerNode );
+ fprintf( log, "Key type = %d\n", HeadNode.KeyType );
+ fprintf( log, "Key size = %ld\n", HeadNode.KeySize );
+ fprintf( log, "Unknown 2 = %d\n", HeadNode.Unknown2 );
+ fprintf( log, "Unique = %d\n", HeadNode.Unique );
+ fprintf( log, "KeyExpression = %s\n", HeadNode.KeyExpression );
+ fclose( log );
+ }
+ else
+ {
+ std::cout << "Start node = " << HeadNode.StartNode << std::endl;
+ std::cout << "Total nodes = " << HeadNode.TotalNodes << std::endl;
+ std::cout << "No of keys = " << HeadNode.NoOfKeys << std::endl;
+ std::cout << "Key Length = " << HeadNode.KeyLen << std::endl;
+ std::cout << "Keys Per Node = " << HeadNode.KeysPerNode << std::endl;
+ std::cout << "Key type = " << HeadNode.KeyType << std::endl;
+ std::cout << "Key size = " << HeadNode.KeySize << std::endl;
+ std::cout << "Unknown 2 = " << HeadNode.Unknown2 << std::endl;
+ std::cout << "Unique = " << HeadNode.Unique << std::endl;
+ std::cout << "KeyExpression = " << HeadNode.KeyExpression << std::endl;
+#ifdef XB_VAR_NODESIZE
+ std::cout << "NodeSize = " << NodeSize << std::endl;
+#endif // XB_VAR_NODESIZE
+ std::cout << std::endl;
+ }
+}
+#endif
+
+/***********************************************************************/
+//! Constructor
+/*!
+ \param pdbf
+*/
+xbNdx::xbNdx() : xbIndex()
+{
+}
+
+/***********************************************************************/
+//! Constructor
+/*!
+ \param pdbf
+*/
+xbNdx::xbNdx(xbDbf *pdbf) : xbIndex(pdbf) {
+#ifndef XB_VAR_NODESIZE
+ memset( Node, 0x00, XB_NDX_NODE_SIZE );
+#else
+ memset( Node, 0x00, XB_MAX_NDX_NODE_SIZE );
+#endif
+ memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode ));
+ NodeChain = NULL;
+ FreeNodeChain = NULL;
+ DeleteChain = NULL;
+ CurNode = NULL;
+ xbNodeLinkCtr = 0L;
+ ReusedxbNodeLinks = 0L;
+#ifndef XB_VAR_NODESIZE
+ NodeSize = XB_NDX_NODE_SIZE;
+#else
+ NodeSize = XB_DEFAULT_NDX_NODE_SIZE;
+#endif // XB_VAR_NODESIZE
+}
+
+/***********************************************************************/
+//! Destructor
+/*!
+*/
+xbNdx::~xbNdx()
+{
+ CloseIndex();
+}
+
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbNdx::GetHeadNode( void )
+{
+ char *p, *q;
+ xbShort i;
+
+ if( !IsOpen() )
+ return XB_NOT_OPEN;
+
+ if( _fseek( indexfp, 0, SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+ if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 )
+ return XB_READ_ERROR;
+
+ /* load the head node structure */
+ p = Node;
+ HeadNode.StartNode = dbf->xbase->GetLong ( p ); p+=4;
+ HeadNode.TotalNodes = dbf->xbase->GetLong ( p ); p+=4;
+ HeadNode.NoOfKeys = dbf->xbase->GetLong ( p ); p+=4;
+ HeadNode.KeyLen = dbf->xbase->GetShort( p ); p+=2;
+ HeadNode.KeysPerNode = dbf->xbase->GetShort( p ); p+=2;
+ HeadNode.KeyType = dbf->xbase->GetShort( p ); p+=2;
+ HeadNode.KeySize = dbf->xbase->GetLong ( p ); p+=4;
+ HeadNode.Unknown2 = *p++;
+ HeadNode.Unique = *p++;
+
+#ifdef XB_VAR_NODESIZE
+ //
+ // Automagically determine the node size. Note the (2 * sizeof(xbLong))
+ // is taken directly from CreateIndex(). I don't understand it exactly,
+ // but this is the value used to calculate the number of keys per node.
+ // DTB.
+ //
+ NodeSize = (2 * sizeof(xbLong)) + HeadNode.KeySize * HeadNode.KeysPerNode;
+ if(NodeSize % XB_NDX_NODE_MULTIPLE)
+ NodeSize = ((NodeSize + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) *
+ XB_NDX_NODE_MULTIPLE;
+#endif
+
+ q = HeadNode.KeyExpression;
+ for( i = XB_NDX_NODE_BASESIZE; i < XB_NDX_NODE_SIZE && *p; i++ )
+ *q++ = *p++;
+
+ return 0;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param NodeNo
+ \param SetNodeChain
+*/
+/* This routine reads a leaf node from disk */
+/* */
+/* If SetNodeChain 2, then the node is not appended to the node chain */
+/* but the CurNode pointer points to the node read */
+/* If SetNodeChain 1, then the node is appended to the node chain */
+/* If SetNodeChain 0, then record is only read to Node memory */
+
+xbShort xbNdx::GetLeafNode( xbLong NodeNo, xbShort SetNodeChain )
+{
+ xbNdxNodeLink *n;
+
+ if( !IsOpen() )
+ return XB_NOT_OPEN;
+
+ if( _fseek( indexfp, (xbOffT)NodeNo * XB_NDX_NODE_SIZE, SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+ if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 )
+ return XB_READ_ERROR;
+
+ if( !SetNodeChain ) return 0;
+
+ if(( n = GetNodeMemory()) == NULL )
+ return XB_NO_MEMORY;
+
+ n->NodeNo = NodeNo;
+ n->CurKeyNo = 0L;
+ n->NextNode = NULL;
+ n->Leaf.NoOfKeysThisNode = dbf->xbase->GetLong( Node );
+ memcpy( n->Leaf.KeyRecs, Node+4, XB_NDX_NODE_SIZE - 4 );
+
+ /* put the node in the chain */
+ if( SetNodeChain == 1 ){
+ if( NodeChain == NULL ){ /* first one ? */
+ NodeChain = n;
+ CurNode = n;
+ CurNode->PrevNode = NULL;
+ } else {
+ n->PrevNode = CurNode;
+ CurNode->NextNode = n;
+ CurNode = n;
+ }
+ }
+ else
+ CurNode = n;
+ return 0;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+#ifdef XBASE_DEBUG
+void xbNdx::DumpNodeRec( xbLong n )
+{
+ char *p;
+ xbLong NoOfKeys, LeftBranch, RecNo, NodeType;
+ xbShort i,j;
+ FILE * log;
+
+ if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return;
+ GetLeafNode( n, 0 );
+ NoOfKeys = dbf->xbase->GetLong( Node );
+ p = Node + 4; /* go past no of keys */
+
+ fprintf( log, "----------------------------------------------------\n" );
+ fprintf( log, "Node # %ld\n", n );
+ fprintf( log, "Number of keys = %ld\n", NoOfKeys );
+ fprintf( log, " Key Left Dbf Rec Key\n" );
+ fprintf( log, "Number Branch Number Data\n" );
+
+ NodeType = 0;
+ for( i = 0; i < (NoOfKeys+NodeType); i++ ){
+ LeftBranch = dbf->xbase->GetLong( p );
+ if( i == 0 && LeftBranch ){
+ NodeType = 1; /* print one extra entry for interior nodes */
+ fprintf( log, "Interior node\n" );
+ }
+
+ p+=4;
+ RecNo = dbf->xbase->GetLong( p );
+ p+=4;
+
+ fprintf( log, " %3d %9ld %9ld ", i, LeftBranch, RecNo );
+
+ if( NodeType == 1 && i == NoOfKeys )
+ fprintf( log, "...\n" );
+
+ else if( !HeadNode.KeyType ){
+ for( j = 0; j < HeadNode.KeyLen; j++ )
+ fputc( *p++, log );
+ fputc( '\n', log );
+ }
+
+ else {
+ fprintf( log, "??????\n" /*, dbf->xbase->GetDouble( p )*/ );
+ p += 8;
+ }
+ }
+ fclose( log );
+}
+#endif
+/***********************************************************************/
+#ifndef XB_INLINE_GETDBFNO
+xbLong xbNdx::GetDbfNo( xbShort RecNo, xbNdxNodeLink * n )
+{
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L;
+ p = temp->KeyRecs + 4;
+ p += RecNo * ( 8 + HeadNode.KeyLen );
+ return( dbf->xbase->GetLong( p ));
+}
+#endif
+/***********************************************************************/
+//! Short description
+/*!
+ \param RecNo
+ \param n
+*/
+xbLong xbNdx::GetLeftNodeNo( xbShort RecNo, xbNdxNodeLink * n )
+{
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > temp->NoOfKeysThisNode ) return 0L;
+ p = temp->KeyRecs;
+ p += RecNo * ( 8 + HeadNode.KeyLen );
+ return( dbf->xbase->GetLong( p ));
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RecNo
+ \param n
+*/
+char * xbNdx::GetKeyData( xbShort RecNo, xbNdxNodeLink * n )
+{
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L;
+ p = temp->KeyRecs + 8;
+ p += RecNo * ( 8 + HeadNode.KeyLen );
+ return( p );
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbLong xbNdx::GetTotalNodes( void )
+{
+ if( &HeadNode )
+ return HeadNode.TotalNodes;
+ else
+ return 0L;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbUShort xbNdx::GetKeysPerNode( void )
+{
+ if( &HeadNode )
+ return HeadNode.KeysPerNode;
+ else
+ return 0L;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNdx::GetFirstKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the first index pointer */
+
+ xbLong TempNodeNo;
+ xbShort rc;
+
+ /* initialize the node chain */
+ if( NodeChain ){
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+
+ if(( rc = GetHeadNode()) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ /* get a node and add it to the link */
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+ return rc;
+ }
+
+/* traverse down the left side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){
+ TempNodeNo = GetLeftNodeNo( 0, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = 0;
+ }
+ CurDbfRec = GetDbfNo( 0, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNdx::GetNextKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the next index pointer */
+
+ xbNdxNodeLink * TempxbNodeLink;
+ xbLong TempNodeNo;
+ xbShort rc;
+
+ if( !IsOpen() ){
+ CurDbfRec = 0L;
+ return XB_NOT_OPEN;
+ }
+
+ if( !CurNode ){
+ rc = GetFirstKey( RetrieveSw );
+ return rc;
+ }
+
+ /* more keys on this node ? */
+ if(( CurNode->Leaf.NoOfKeysThisNode-1) > CurNode->CurKeyNo ){
+ CurNode->CurKeyNo++;
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+ }
+
+ /* if head node we are at eof */
+ if( CurNode->NodeNo == HeadNode.StartNode ) {
+ return XB_EOF;
+ }
+
+ /* this logic assumes that interior nodes have n+1 left node no's where */
+ /* n is the number of keys in the node */
+
+ /* pop up one node to the interior node level & free the leaf node */
+
+ TempxbNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempxbNodeLink );
+
+ /* while no more right keys && not head node, pop up one node */
+ while(( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode ) &&
+ ( CurNode->NodeNo != HeadNode.StartNode )){
+ TempxbNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempxbNodeLink );
+ }
+
+ /* if head node && right most key, return end-of-file */
+ if(( HeadNode.StartNode == CurNode->NodeNo ) &&
+ ( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode )) {
+ return XB_EOF;
+ }
+
+ /* move one to the right */
+ CurNode->CurKeyNo++;
+ TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );
+
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ return rc;
+ }
+
+/* traverse down the left side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){
+ TempNodeNo = GetLeftNodeNo( 0, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = 0;
+ }
+ CurDbfRec = GetDbfNo( 0, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param NodeNo
+ \param RetrieveSw
+*/
+xbShort xbNdx::GetLastKey( xbLong NodeNo, xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the last index pointer */
+
+/* If NodeNo = 0, start at head node, otherwise start at NodeNo */
+
+ xbLong TempNodeNo;
+ xbShort rc;
+
+ if( NodeNo < 0 || NodeNo > HeadNode.TotalNodes )
+ return XB_INVALID_NODE_NO;
+
+ /* initialize the node chain */
+ if( NodeChain ){
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+ if( NodeNo == 0L )
+ if(( rc = GetHeadNode()) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ /* get a node and add it to the link */
+ if( NodeNo == 0L ){
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ } else {
+ if(( rc = GetLeafNode( NodeNo, 1 )) != 0 ) {
+ CurDbfRec = 0L;
+ return rc;
+ }
+ }
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+
+/* traverse down the right side of the tree */
+ while( GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode )){
+ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ }
+ CurNode->CurKeyNo--; /* leaf node has one fewer ix recs */
+ CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode-1, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNdx::GetPrevKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the previous index pointer */
+
+ xbNdxNodeLink * TempxbNodeLink;
+ xbLong TempNodeNo;
+ xbShort rc;
+
+ if( !IsOpen() ){
+ CurDbfRec = 0L;
+ return XB_NOT_OPEN;
+ }
+
+ if( !CurNode ){
+ CurDbfRec = 0L;
+ return GetFirstKey( RetrieveSw );
+ }
+
+ /* more keys on this node ? */
+ if( CurNode->CurKeyNo > 0 ){
+ CurNode->CurKeyNo--;
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+ }
+
+ /* this logic assumes that interior nodes have n+1 left node no's where */
+ /* n is the number of keys in the node */
+ /* pop up one node to the interior node level & free the leaf node */
+
+ if( !CurNode->PrevNode ) { /* michael - make sure prev node exists */
+ return XB_EOF;
+ }
+
+ TempxbNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempxbNodeLink );
+
+ /* while no more left keys && not head node, pop up one node */
+ while(( CurNode->CurKeyNo == 0 ) &&
+ ( CurNode->NodeNo != HeadNode.StartNode )) {
+ TempxbNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempxbNodeLink );
+ }
+
+ /* if head node && left most key, return beginning-of-file */
+ if(( HeadNode.StartNode == CurNode->NodeNo ) &&
+ ( CurNode->CurKeyNo == 0 )) {
+ return XB_BOF;
+ }
+
+ /* move one to the left */
+ CurNode->CurKeyNo--;
+ TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );
+
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ) {
+ return rc;
+ }
+
+ if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ else /* leaf node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1;
+
+/* traverse down the right side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */
+ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ else /* leaf node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1;
+ }
+ CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode - 1, CurNode );
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/**************************************************************************/
+//! Short description
+/*!
+ \param key
+ \param klen
+ \param node
+ \param comp
+*/
+/*
+** This is a pretty basic binary search with two exceptions: 1) it will
+** find the first of duplicate key values and 2) will return the index
+** and the value of the last comparision even if it doesn't find a
+** match.
+*/
+xbShort
+xbNdx::BSearchNode(const char *key, xbShort klen, const xbNdxNodeLink *node,
+ xbShort *comp)
+{
+ xbShort c, p, start = 0, end = node->Leaf.NoOfKeysThisNode - 1;
+
+ if(start > end){
+ *comp = 2;
+ return 0;
+ }
+
+ do {
+ p = (start + end) / 2;
+ c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen);
+ switch(c){
+
+ case 1 : /* greater than */
+ start = p + 1;
+ break;
+
+ case 2 : /* less than */
+ end = p - 1;
+ break;
+ }
+ } while(start <= end && c);
+
+
+ if(c == 1)
+ while(p < node->Leaf.NoOfKeysThisNode &&
+ (c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen)) == 1)
+ p++;
+
+ *comp = c;
+
+ if(!c)
+ while(p > 0 && !CompareKey(key, GetKeyData(p - 1, (xbNdxNodeLink *)node), klen))
+ p--;
+
+ return p;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param Tkey
+ \param Klen
+*/
+xbLong xbNdx::GetLeafFromInteriorNode( const char * Tkey, xbShort Klen )
+{
+ /* This function scans an interior node for a key and returns the */
+ /* correct interior leaf node no */
+
+ xbShort p, c;
+
+ /* if Tkey > any keys in node, return right most key */
+ p = CurNode->Leaf.NoOfKeysThisNode - 1;
+ if( CompareKey( Tkey, GetKeyData( p, CurNode ), Klen ) == 1 ) {
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ return GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ }
+
+ p = BSearchNode(Tkey, Klen, CurNode, &c);
+ CurNode->CurKeyNo = p;
+ return GetLeftNodeNo( p, CurNode );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param d
+*/
+xbShort xbNdx::KeyExists( xbDouble d )
+{
+ char buf[9];
+ memset( buf, 0x00, 9 );
+ dbf->xbase->PutDouble( buf, d );
+ return FindKey( buf, 8, 0 );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param d
+*/
+xbShort xbNdx::FindKey( xbDouble d )
+{
+ char buf[9];
+ memset( buf, 0x00, 9 );
+ dbf->xbase->PutDouble( buf, d );
+ return FindKey( buf, 8, 1 );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param Key
+*/
+xbShort xbNdx::FindKey( const char * Key )
+{
+ return FindKey( Key, strlen( Key ), 1 );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param Tkey
+ \param DbfRec
+*/
+xbShort xbNdx::FindKey( const char * Tkey, xbLong DbfRec )
+{
+ /* find a key with a specifc DBF record number */
+ xbShort rc;
+
+ xbLong CurDbfRecNo;
+ xbLong CurNdxDbfNo;
+
+ /* if we are already on the correct key, return XB_FOUND */
+ if( CurNode ) {
+ CurDbfRecNo = dbf->GetCurRecNo();
+ CurNdxDbfNo = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ if( CurDbfRecNo == CurNdxDbfNo &&
+ (strncmp(Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ),
+ HeadNode.KeyLen ) == 0 )) {
+ return XB_FOUND;
+ }
+ }
+ rc = FindKey( Tkey, HeadNode.KeyLen, 0 );
+
+ while( rc == 0 || rc == XB_FOUND ) {
+ if( strncmp( Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ),
+ HeadNode.KeyLen ) == 0 ){
+ if( DbfRec == GetDbfNo( CurNode->CurKeyNo, CurNode )) {
+ return XB_FOUND;
+ }
+ else
+ rc = GetNextKey( 0 );
+ } else {
+ return XB_NOT_FOUND;
+ }
+ }
+ return XB_NOT_FOUND;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbNdx::FindKey( void )
+{
+ /* if no paramaters given, use KeyBuf */
+ return( FindKey( KeyBuf, HeadNode.KeyLen, 0 ));
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param Tkey
+ \param Klen
+ \param RetrieveSw
+*/
+xbShort xbNdx::FindKey( const char * Tkey, xbShort Klen, xbShort RetrieveSw )
+{
+ /* This routine sets the current key to the found key */
+ /* if RetrieveSw is true, the method positions the dbf record */
+ xbShort rc,i;
+ xbLong TempNodeNo;
+
+ if( NodeChain ) {
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+
+ if(( rc = GetHeadNode()) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ /* load first node */
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ /* traverse down the tree until it hits a leaf */
+ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */
+ TempNodeNo = GetLeafFromInteriorNode( Tkey, Klen );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+ CurDbfRec = 0L;
+ return rc;
+ }
+ }
+
+ i = BSearchNode(Tkey, Klen, CurNode, &rc);
+ switch(rc) {
+ case 0 : /* found! */
+ CurNode->CurKeyNo = i;
+ CurDbfRec = GetDbfNo( i, CurNode );
+ if( RetrieveSw )
+ dbf->GetRecord(CurDbfRec);
+ return XB_FOUND;
+
+ case 1 : /* less than */
+// if(i < CurNode->Leaf.NoOfKeysThisNode)
+ break;
+// i++;
+
+ case 2 : /* greater than */
+ CurNode->CurKeyNo = i;
+ CurDbfRec = GetDbfNo( i, CurNode );
+ if( RetrieveSw )
+ dbf->GetRecord(CurDbfRec);
+ return XB_NOT_FOUND;
+ }
+
+ CurNode->CurKeyNo = i;
+ if(i >= CurNode->Leaf.NoOfKeysThisNode){
+ CurDbfRec = 0;
+ return XB_EOF;
+ }
+
+ CurDbfRec = GetDbfNo( i, CurNode );
+ if((RetrieveSw) && (CurDbfRec > 0))
+ dbf->GetRecord( CurDbfRec );
+
+ return XB_NOT_FOUND;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbNdx::CalcKeyLen()
+{
+ xbShort rc;
+ xbExpNode * TempNode;
+ char FieldName[11];
+ char Type;
+
+ TempNode = IxExp->GetFirstTreeNode();
+
+ if( !TempNode )
+ return 0;
+
+ if( TempNode->Type == 'd' ) return -8;
+ if( TempNode->Type == 'D' ){
+ memset( FieldName, 0x00, 11 );
+ memcpy( FieldName, TempNode->NodeText, TempNode->Len );
+ Type = dbf->GetFieldType( dbf->GetFieldNo( FieldName ));
+ if( Type == 'N' || Type == 'F' )
+ return -8;
+ }
+
+ if(( rc = IxExp->ProcessExpression()) != XB_NO_ERROR )
+ return 0;
+
+ TempNode = (xbExpNode *) IxExp->Pop();
+ if( !TempNode )
+ return 0;
+ rc = TempNode->DataLen;
+
+ if( !TempNode->InTree )
+ delete TempNode;
+
+ return rc;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param IxName
+ \param Exp
+ \param Unique
+ \param Overlay
+*/
+xbShort xbNdx::CreateIndex(const char * IxName, const char * Exp,
+ xbShort Unique, xbShort Overlay )
+{
+ xbShort i, KeyLen, rc;
+
+ if( IsOpen()) CloseIndex();
+ if( strlen( Exp ) > 488 )
+ return XB_INVALID_KEY_EXPRESSION;
+
+ if( dbf->GetDbfStatus() == 0 )
+ return XB_NOT_OPEN;
+
+ /* Get the index file name and store it in the class */
+ SetFileName(IxName);
+
+ /* check if the file already exists */
+ if (((indexfp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ) {
+ fclose( indexfp );
+ return XB_FILE_EXISTS;
+ }
+
+ if (indexfp)
+ fclose(indexfp);
+
+ if(( indexfp = fopen( GetFileName(), "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+#ifdef XB_LOCKING_ON
+ /*
+ ** Must turn off buffering when multiple programs may be accessing
+ ** index files.
+ */
+ setbuf( indexfp, NULL );
+#endif
+
+ /* parse the expression */
+ IxExp = new xbExpn( dbf->xbase );
+ if(( rc = IxExp->BuildExpressionTree( Exp, strlen( Exp ), dbf )) != XB_NO_ERROR )
+ return rc;
+
+ /* build the header record */
+ memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode ));
+ HeadNode.StartNode = 1L;
+ HeadNode.TotalNodes = 2L;
+ HeadNode.NoOfKeys = 1L;
+ KeyLen = CalcKeyLen();
+
+ if( KeyLen == 0 || KeyLen > 100 ) /* 100 byte key length limit */
+ return XB_INVALID_KEY;
+ else if( KeyLen == -8 ){
+ HeadNode.KeyType = 1; /* numeric key */
+ HeadNode.KeyLen = 8;
+ } else {
+ HeadNode.KeyType = 0; /* character key */
+ HeadNode.KeyLen = KeyLen;
+ }
+
+// HeadNode.KeysPerNode = (xbUShort) ( XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) /
+// (HeadNode.KeyLen + 8 );
+// HeadNode.KeySize = HeadNode.KeyLen + 8;
+// while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/
+
+/* above code replaced with following by Paul Koufalis pkoufalis@cogicom.com */
+// while(( HeadNode.KeyLen % 4 ) != 0 ) HeadNode.KeyLen++; /* multiple of 4*/
+// HeadNode.KeySize = HeadNode.KeyLen + 8;
+
+
+/* above two lines commented out by gary 4/14/99 and replaced w/ following
+ For compatibility with other Xbase tools
+ KeyLen is the length of the key data
+ KeySize = KeyLen+8, rounded up until divisible by 4
+*/
+
+ HeadNode.KeySize = HeadNode.KeyLen + 8;
+ while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/
+
+ HeadNode.KeysPerNode = (xbUShort)
+ (XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) / HeadNode.KeySize;
+
+ HeadNode.Unique = Unique;
+ strncpy( HeadNode.KeyExpression, Exp, 488 );
+ KeyBuf = (char *) malloc( HeadNode.KeyLen + 1 );
+ KeyBuf2 = (char *) malloc( HeadNode.KeyLen + 1 );
+ memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 );
+ memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 );
+
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 0 )) != 0 ){
+ return rc;
+ }
+ /* write node #1 all 0x00 */
+ for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){
+ if ((fwrite("\x00", 1, 1, indexfp)) != 1){
+ fclose( indexfp );
+ return XB_WRITE_ERROR;
+ }
+ }
+// IndexStatus = XB_OPEN;
+ return dbf->AddIndexToIxList( index, GetFileName() );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RecNo
+ \param n
+ \param NodeNo
+*/
+xbShort xbNdx::PutLeftNodeNo( xbShort RecNo, xbNdxNodeLink *n, xbLong NodeNo )
+{
+ /* This routine sets n node's leftnode number */
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n )
+ return XB_INVALID_NODELINK;
+
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > HeadNode.KeysPerNode)
+ return XB_INVALID_KEY;
+
+ p = temp->KeyRecs;
+ p+= RecNo * ( 8 + HeadNode.KeyLen );
+ dbf->xbase->PutLong( p, NodeNo );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param RecNo
+ \param n
+ \param DbfNo
+*/
+xbShort xbNdx::PutDbfNo( xbShort RecNo, xbNdxNodeLink *n, xbLong DbfNo )
+{
+ /* This routine sets n node's dbf number */
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n )
+ return XB_INVALID_NODELINK;
+
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1))
+ return XB_INVALID_KEY;
+
+ p = temp->KeyRecs + 4;
+ p+= RecNo * ( 8 + HeadNode.KeyLen );
+ dbf->xbase->PutLong( p, DbfNo );
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param l
+ \param n
+*/
+xbShort xbNdx::PutLeafNode( xbLong l, xbNdxNodeLink *n )
+{
+ if ((_fseek(indexfp, (xbOffT)l * XB_NDX_NODE_SIZE , SEEK_SET)) != 0) {
+ fclose( indexfp );
+ return XB_SEEK_ERROR;
+ }
+ dbf->xbase->PutLong( Node, n->Leaf.NoOfKeysThisNode );
+
+ if(( fwrite( Node, 4, 1, indexfp )) != 1 ){
+ fclose( indexfp );
+ return XB_WRITE_ERROR;
+ }
+ if(( fwrite( &n->Leaf.KeyRecs, XB_NDX_NODE_SIZE-4, 1, indexfp )) != 1 ){
+ fclose( indexfp );
+ return XB_WRITE_ERROR;
+ }
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param Head
+ \param f
+ \param UpdateOnly
+*/
+xbShort xbNdx::PutHeadNode( xbNdxHeadNode * Head, FILE * f, xbShort UpdateOnly )
+{
+ char buf[4];
+
+ if(( _fseek( f, 0L, SEEK_SET )) != 0 ){
+ fclose( f );
+ return XB_SEEK_ERROR;
+ }
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutLong( buf, Head->StartNode );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutLong( buf, Head->TotalNodes );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutLong( buf, Head->NoOfKeys );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ if( UpdateOnly )
+ return XB_NO_ERROR;
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutLong( buf, Head->KeyLen );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutLong( buf, Head->KeysPerNode );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutLong( buf, Head->KeyType );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutLong( buf, Head->KeySize );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ if(( fwrite( &Head->Unknown2, XB_NDX_NODE_SIZE - 22, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param RecNo
+ \param n
+*/
+xbShort xbNdx::PutKeyData( xbShort RecNo, xbNdxNodeLink *n )
+{
+ /* This routine copies the KeyBuf data into xbNdxNodeLink n */
+ xbNdxLeafNode *temp;
+ char *p;
+ xbShort i;
+ if( !n )
+ return XB_INVALID_NODELINK;
+
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1))
+ return XB_INVALID_KEY;
+
+ p = temp->KeyRecs + 8;
+ p+= RecNo * ( 8 + HeadNode.KeyLen );
+ for( i = 0; i < HeadNode.KeyLen; i++ ) {
+ *p = KeyBuf[i];
+ p++;
+ }
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param n
+ \param pos
+ \param d
+ \param l
+ \param w
+*/
+xbShort xbNdx::PutKeyInNode( xbNdxNodeLink * n, xbShort pos, xbLong d,
+ xbLong l, xbShort w )
+{
+ xbShort i;
+
+ /* check the node */
+ if (!n)
+ return XB_INVALID_NODELINK;
+
+ if(pos < 0 || pos > HeadNode.KeysPerNode)
+ return XB_INVALID_RECORD;
+
+ if(n->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode)
+ return XB_NODE_FULL;
+
+ /* if key movement, save the original key */
+ if( pos < n->Leaf.NoOfKeysThisNode )
+ memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1);
+
+ /* if interior node, handle the right most left node no */
+ if( GetLeftNodeNo( 0, n ))
+ PutLeftNodeNo( n->Leaf.NoOfKeysThisNode+1, n,
+ GetLeftNodeNo( n->Leaf.NoOfKeysThisNode, n ));
+
+ for( i = n->Leaf.NoOfKeysThisNode; i > pos; i-- ){
+ memcpy( KeyBuf, GetKeyData(i-1,n), HeadNode.KeyLen );
+ PutKeyData( i, n );
+ PutDbfNo( i, n, GetDbfNo(i-1,n));
+ PutLeftNodeNo(i, n, GetLeftNodeNo(i-1,n));
+ }
+
+ /* put new key in node */
+ if( pos < n->Leaf.NoOfKeysThisNode )
+ memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1);
+
+ PutKeyData( pos, n );
+ PutDbfNo( pos, n, d );
+ PutLeftNodeNo( pos, n, l );
+ n->Leaf.NoOfKeysThisNode++;
+ if( w )
+ return PutLeafNode( n->NodeNo, n );
+ else
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param curNode Current Node
+ \param newNode New Empty Node
+ \param pos Position of new key in current node
+ \param d dbf record number
+*/
+/* This function splits a full index leaf node into two parts
+ as of 2/13/04, this split logic was modified to cause an
+ even split in order to keep the index tree balanced
+
+*/
+
+xbShort xbNdx::SplitLeafNode( xbNdxNodeLink *curNode,
+ xbNdxNodeLink *newNode, xbShort pos, xbLong d )
+{
+ xbShort curNodeNewSize;
+ xbShort newNodeSize;
+ xbShort i,j,rc,startPos;
+
+ curNodeNewSize = (curNode->Leaf.NoOfKeysThisNode + 1) / 2;
+ newNodeSize = curNode->Leaf.NoOfKeysThisNode + 1 - curNodeNewSize;
+
+ /* save off the current key buffer */
+ memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1 );
+ if( pos < curNodeNewSize ){ /* new key goes in current node */
+
+ /* copy second half of current node to beginning of new node */
+ /* memcpy( dst, src, len ); */
+ startPos = curNode->Leaf.NoOfKeysThisNode - newNodeSize;
+
+ for( i = startPos, j = 0; i < CurNode->Leaf.NoOfKeysThisNode; i++, j++){
+ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ PutDbfNo( j, newNode, GetDbfNo( i, curNode ));
+ }
+
+ /* make a hole for the new key */
+ for( i = curNodeNewSize - 1; i > pos; i-- ){
+ memcpy( KeyBuf, GetKeyData( i-1, curNode ), HeadNode.KeyLen );
+ PutKeyData( i, curNode );
+ PutDbfNo( i, curNode, GetDbfNo( i-1, curNode ));
+ }
+
+ /* insert key appropriately */
+ memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 );
+ PutKeyData( pos, curNode );
+ PutDbfNo( pos, curNode, d );
+ }
+ else
+ {
+ pos -= curNodeNewSize;
+
+ /* do part one of the key migration */
+ if( pos ){
+
+/* was originally
+
+ startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1;
+
+
+ then was changed to
+*/
+
+/*
+ if( ((pos + curNodeNewSize) == HeadNode.KeysPerNode) &&
+ (pos == newNodeSize) ){ // off the right end
+ startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize;
+ }
+ else
+ {
+ startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1;
+ }
+
+*/
+
+/*
+ and this didn't work
+
+
+ startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize;
+
+*/
+
+ startPos = curNodeNewSize;
+ for( i = startPos, j = 0;
+ j < pos && i < curNode->Leaf.NoOfKeysThisNode; i++, j++){
+ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ PutDbfNo( j, newNode, GetDbfNo( i, curNode ));
+ }
+ }
+
+ /* insert new key appropriately */
+ memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 );
+ PutKeyData( pos, newNode );
+ PutDbfNo( pos, newNode, d );
+
+ /* Load the remainder of the keys on the new node past the new key */
+ if( pos < (newNodeSize-1) ){
+
+// startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + pos + 1;
+
+ startPos = curNodeNewSize + pos;
+ for( i = startPos, j = pos+1; j < newNodeSize; i++, j++){
+ memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ PutDbfNo( j, newNode, GetDbfNo( i, curNode ));
+ }
+ }
+ }
+
+ curNode->Leaf.NoOfKeysThisNode = curNodeNewSize;
+ newNode->Leaf.NoOfKeysThisNode = newNodeSize;
+
+ /* write the new nodes to disk */
+ if(( rc = PutLeafNode( curNode->NodeNo, curNode )) != 0 )
+ return rc;
+ if(( rc = PutLeafNode( newNode->NodeNo, newNode )) != 0 )
+ return rc;
+
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param nodeToSplit Interior node to split
+ \param newNode New empty node to use
+ \param dscNodeNo Descendant node number
+*/
+/* This routine splits an interior node */
+
+xbShort xbNdx::SplitINode( xbNdxNodeLink *nodeToSplit,
+ xbNdxNodeLink *newNode, xbLong dscNodeNo )
+{
+ xbShort i,j,rc;
+ xbNdxNodeLink * SaveNodeChain;
+ xbNdxNodeLink * SaveCurNode;
+ xbLong newNodeToSplitSize;
+ xbLong newNodeSize;
+ xbShort pos, startPos, offset;
+
+ newNodeToSplitSize = (nodeToSplit->Leaf.NoOfKeysThisNode + 2 ) / 2;
+ newNodeSize = nodeToSplit->Leaf.NoOfKeysThisNode + 2 - newNodeToSplitSize;
+ pos = nodeToSplit->CurKeyNo;
+
+ if( pos < (newNodeToSplitSize-1) ){
+ /* copy second half of nodeToSplit to newNode */
+ startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeSize +1;
+ for(i=startPos, j=0; i <= nodeToSplit->Leaf.NoOfKeysThisNode; i++, j++ ){
+ if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){
+ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ }
+ PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit ));
+ }
+
+ /* make a hole for the new key */
+ for( i = newNodeToSplitSize; i > pos; i-- ){
+ memcpy( KeyBuf, GetKeyData( i-1, nodeToSplit ), HeadNode.KeyLen );
+ PutKeyData( i, nodeToSplit );
+ PutLeftNodeNo( i, nodeToSplit, GetLeftNodeNo( i-1, nodeToSplit ));
+ }
+
+ /* load new high key value into current position on nodeToSplit */
+ if( pos < (newNodeToSplitSize - 1 )){
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ SaveCurNode = CurNode;
+ GetLastKey( GetLeftNodeNo( pos, nodeToSplit ), 0 );
+ memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen );
+ PutKeyData( pos, nodeToSplit );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ }
+ PutLeftNodeNo( pos+1, nodeToSplit, dscNodeNo );
+ }
+
+/*************/
+/* part b */
+/*************/
+
+ else
+ {
+ pos -= newNodeToSplitSize-1;
+ /* do part one of the key migration */
+ if( pos ){
+// startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 2;
+
+// 5/29/04 gak changed the following line for index probs
+// startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 1;
+
+ if( HeadNode.KeysPerNode % 2 )
+ offset = 2;
+ else
+ offset = 1;
+
+ startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + offset;
+
+ for( i = startPos, j = 0; j < pos; i++, j++ ){
+ if( i < nodeToSplit->Leaf.NoOfKeysThisNode && j < (pos-1)){
+ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ }
+ else
+ {
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ SaveCurNode = CurNode;
+ GetLastKey( GetLeftNodeNo( i, nodeToSplit ), 0 );
+ memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen);
+ PutKeyData( j, newNode );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ }
+ PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit ));
+ }
+ }
+
+ /* insert new key appropriately */
+ if( pos < (newNodeSize - 1)){
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ SaveCurNode = CurNode;
+ GetLastKey( dscNodeNo, 0 );
+ memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen);
+ PutKeyData( pos, newNode );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ }
+ PutLeftNodeNo( pos, newNode, dscNodeNo );
+
+ /* load remainder of the keys */
+ if( pos < (newNodeSize - 1)){
+
+
+// startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+2;
+// 5/29/04 gak changed the following line for index probs
+
+ startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+offset;
+
+ for( i = startPos, j = pos+1; j < newNodeSize; i++, j++ ){
+ if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){
+ memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen );
+ PutKeyData( j, newNode );
+ }
+ PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit ));
+ }
+ }
+ }
+
+ nodeToSplit->Leaf.NoOfKeysThisNode = newNodeToSplitSize - 1;
+ newNode->Leaf.NoOfKeysThisNode = newNodeSize - 1;
+
+ if((rc = PutLeafNode( nodeToSplit->NodeNo, nodeToSplit )) != 0) return rc;
+ if((rc = PutLeafNode( newNode->NodeNo, newNode )) != 0) return rc;
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param RecBufSw
+ \param KeyBufSw
+*/
+xbShort xbNdx::CreateKey( xbShort RecBufSw, xbShort KeyBufSw )
+{
+ /* RecBufSw 0 Use RecBuf */
+ /* 1 Use RecBuf2 */
+ /* KeyBufSw 0 Use KeyBuf */
+ /* 1 Use KeyBuf2 */
+
+ xbShort rc;
+ xbExpNode * TempNode;
+
+ if(( rc = IxExp->ProcessExpression( RecBufSw )) != XB_NO_ERROR )
+ return rc;
+ TempNode = (xbExpNode *) IxExp->Pop();
+ if( !TempNode )
+ return XB_INVALID_KEY;
+
+ if( KeyBufSw ){
+ if( HeadNode.KeyType == 1 ) /* numeric key */
+ dbf->xbase->PutDouble( KeyBuf2, TempNode->DoubResult );
+ else{ /* character key */
+ memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 );
+ memcpy( KeyBuf2, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
+ }
+ } else {
+ if( HeadNode.KeyType == 1 ) /* numeric key */
+ dbf->xbase->PutDouble( KeyBuf, TempNode->DoubResult );
+ else { /* character key */
+ memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 );
+ memcpy( KeyBuf, TempNode->StringResult.c_str(), XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
+ }
+ }
+// if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode );
+ if( !TempNode->InTree ) delete TempNode;
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param key
+*/
+xbShort
+xbNdx::GetCurrentKey(char *key)
+{
+ CreateKey(0, 0);
+ if(HeadNode.KeyType == 1)
+ memcpy(key, KeyBuf, 8);
+ else
+ memcpy(key, KeyBuf, HeadNode.KeyLen + 1);
+ return 0;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param DbfRec
+*/
+xbShort xbNdx::AddKey( xbLong DbfRec )
+{
+ /* This routine assumes KeyBuf contains the contents of the index to key */
+
+ char *p;
+ xbShort i,rc;
+ xbNdxNodeLink * TempNode;
+ xbNdxNodeLink * Tparent;
+ xbLong TempNodeNo; /* new, unattached leaf node no */
+ xbNdxNodeLink * SaveNodeChain;
+ xbNdxNodeLink * SaveCurNode;
+
+ /* find node key belongs in */
+ rc = FindKey( KeyBuf, HeadNode.KeyLen, 0 );
+ if( rc == XB_FOUND && HeadNode.Unique )
+ return XB_KEY_NOT_UNIQUE;
+
+ if( CurNode->Leaf.NoOfKeysThisNode > 0 && rc == XB_FOUND ){
+ rc = 0;
+ while( rc == 0 ){
+ if(( p = GetKeyData( CurNode->CurKeyNo, CurNode )) == NULL )
+ rc = -1;
+ else {
+ rc = CompareKey( KeyBuf, p, HeadNode.KeyLen );
+ if( rc == 0 && DbfRec >= GetDbfNo( CurNode->CurKeyNo, CurNode )){
+ if((rc = GetNextKey(0)) == XB_EOF) {
+ if((rc = GetLastKey(0, 0)) != XB_NO_ERROR)
+ return rc;
+ CurNode->CurKeyNo++;
+ }
+ }
+ else
+ rc = -1;
+ }
+ }
+ }
+
+ /* update header node */
+ HeadNode.NoOfKeys++;
+ /************************************************/
+ /* section A - if room in node, add key to node */
+ /************************************************/
+
+ if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){
+ if(( rc = PutKeyInNode( CurNode,CurNode->CurKeyNo,DbfRec,0L,1)) != 0)
+ return rc;
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0)
+ return rc;
+ return XB_NO_ERROR;
+ }
+
+ /***********************************************************************/
+ /* section B - split leaf node if full and put key in correct position */
+ /***********************************************************************/
+
+ TempNode = GetNodeMemory();
+ TempNode->NodeNo = HeadNode.TotalNodes++;
+ rc = SplitLeafNode( CurNode, TempNode, CurNode->CurKeyNo, DbfRec );
+ if( rc )
+ return rc;
+
+ TempNodeNo = TempNode->NodeNo;
+ ReleaseNodeMemory( TempNode );
+
+ /*****************************************************/
+ /* section C go up tree splitting nodes as necessary */
+ /*****************************************************/
+ Tparent = CurNode->PrevNode;
+
+ while( Tparent &&
+ Tparent->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode-1) {
+
+ TempNode = GetNodeMemory();
+
+ if( !TempNode )
+ return XB_NO_MEMORY;
+ TempNode->NodeNo = HeadNode.TotalNodes++;
+
+ rc = SplitINode( Tparent, TempNode, TempNodeNo );
+ if( rc ) return rc;
+
+ TempNodeNo = TempNode->NodeNo;
+ ReleaseNodeMemory( TempNode );
+ ReleaseNodeMemory( CurNode );
+ CurNode = Tparent;
+ CurNode->NextNode = NULL;
+ Tparent = CurNode->PrevNode;
+ }
+
+ /************************************************************/
+ /* Section D if CurNode is split root, create new root */
+ /************************************************************/
+
+ /* at this point
+ CurNode = The node that was just split
+ TempNodeNo = The new node split off from CurNode */
+
+ if(CurNode->NodeNo == HeadNode.StartNode ){
+ TempNode = GetNodeMemory();
+ if( !TempNode )
+ return XB_NO_MEMORY;
+
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ SaveCurNode = CurNode;
+ GetLastKey( CurNode->NodeNo, 0 );
+ memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode ),HeadNode.KeyLen );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ PutKeyData( 0, TempNode );
+ PutLeftNodeNo( 0, TempNode, CurNode->NodeNo );
+ PutLeftNodeNo( 1, TempNode, TempNodeNo );
+ TempNode->NodeNo = HeadNode.TotalNodes++;
+ TempNode->Leaf.NoOfKeysThisNode++;
+ HeadNode.StartNode = TempNode->NodeNo;
+ rc = PutLeafNode( TempNode->NodeNo, TempNode );
+ if( rc ) return rc;
+ rc = PutHeadNode( &HeadNode, indexfp, 1 );
+ if( rc ) return rc;
+ ReleaseNodeMemory( TempNode );
+ return XB_NO_ERROR;
+ }
+ /**********************************/
+ /* Section E make room in parent */
+ /**********************************/
+ for( i = Tparent->Leaf.NoOfKeysThisNode; i > Tparent->CurKeyNo; i-- ){
+ memcpy( KeyBuf, GetKeyData( i-1, Tparent ), HeadNode.KeyLen );
+ PutKeyData( i, Tparent );
+ PutLeftNodeNo( i+1, Tparent, GetLeftNodeNo( i, Tparent ));
+ }
+
+ /* put key in parent */
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ SaveCurNode = CurNode;
+ GetLastKey( CurNode->NodeNo, 0 );
+ memcpy( KeyBuf,GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ PutKeyData( i, Tparent );
+ PutLeftNodeNo( i+1, Tparent, TempNodeNo );
+ Tparent->Leaf.NoOfKeysThisNode++;
+ rc = PutLeafNode( Tparent->NodeNo, Tparent );
+ if( rc ) return rc;
+ rc = PutHeadNode( &HeadNode, indexfp, 1 );
+ if( rc ) return rc;
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param pos
+ \param n
+*/
+xbShort xbNdx::RemoveKeyFromNode( xbShort pos, xbNdxNodeLink *n )
+{
+ xbShort i;
+ /* check the node */
+ if( !n )
+ return XB_INVALID_NODELINK;
+
+ if( pos < 0 || pos > HeadNode.KeysPerNode )
+ return XB_INVALID_KEY;
+
+ for( i = pos; i < n->Leaf.NoOfKeysThisNode-1; i++ ){
+ memcpy( KeyBuf, GetKeyData( i+1, n), HeadNode.KeyLen );
+ PutKeyData( i, n );
+ PutDbfNo( i, n, GetDbfNo( i+1, n ));
+ PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n ));
+ }
+ PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n ));
+ n->Leaf.NoOfKeysThisNode--;
+ /* if last key was deleted, decrement CurKeyNo */
+ if( n->CurKeyNo > n->Leaf.NoOfKeysThisNode )
+ n->CurKeyNo--;
+ return PutLeafNode( n->NodeNo, n );
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+xbShort xbNdx::UpdateParentKey( xbNdxNodeLink * n )
+{
+/* this routine goes backwards thru the node chain looking for a parent
+ node to update */
+
+ xbNdxNodeLink * TempNode;
+ if( !n )
+ return XB_INVALID_NODELINK;
+
+ if( !GetDbfNo( 0, n ))
+ return XB_NOT_LEAFNODE;
+
+ TempNode = n->PrevNode;
+ while( TempNode ){
+ if( TempNode->CurKeyNo < TempNode->Leaf.NoOfKeysThisNode ){
+ memcpy(KeyBuf,GetKeyData(n->Leaf.NoOfKeysThisNode-1,n),HeadNode.KeyLen);
+ PutKeyData( TempNode->CurKeyNo, TempNode );
+ return PutLeafNode( TempNode->NodeNo, TempNode );
+ }
+ TempNode = TempNode->PrevNode;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+/* This routine queues up a list of nodes which have been emptied */
+void xbNdx::UpdateDeleteList( xbNdxNodeLink *n )
+{
+ n->NextNode = DeleteChain;
+ DeleteChain = n;
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+/* Delete nodes from the node list - for now we leave the empty nodes */
+/* dangling in the file. Eventually we will remove nodes from the file */
+
+void xbNdx::ProcessDeleteList( void )
+{
+ if( DeleteChain ){
+ ReleaseNodeMemory( DeleteChain );
+ DeleteChain = NULL;
+ }
+}
+/***********************************************************************/
+//! Short description
+/*!
+*/
+xbShort xbNdx::KeyWasChanged( void )
+{
+ CreateKey( 0, 0 ); /* use KeyBuf, RecBuf */
+ CreateKey( 1, 1 ); /* use KeyBuf2, RecBuf2 */
+ if( CompareKey( KeyBuf, KeyBuf2, HeadNode.KeyLen ) != 0 )
+ return 1;
+ else
+ return 0;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+xbNdxNodeLink * xbNdx::LeftSiblingHasSpace( xbNdxNodeLink * n )
+{
+ xbNdxNodeLink * TempNode;
+ xbNdxNodeLink * SaveCurNode;
+
+ /* returns a Nodelink to xbNdxNodeLink n's left sibling if it has space */
+ /* if left most node in parent return NULL */
+ if( n->PrevNode->CurKeyNo == 0 )
+ return NULL;
+
+ SaveCurNode = CurNode;
+ GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 );
+ if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){
+ TempNode = CurNode;
+ CurNode = SaveCurNode;
+ TempNode->PrevNode = n->PrevNode;
+ return TempNode;
+ } else { /* node is already full */
+ ReleaseNodeMemory( CurNode );
+ CurNode = SaveCurNode;
+ return NULL;
+ }
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+xbNdxNodeLink * xbNdx::RightSiblingHasSpace( xbNdxNodeLink * n )
+{
+ /* returns a Nodelink to xbNdxNodeLink n's right sibling if it has space */
+
+ xbNdxNodeLink * TempNode;
+ xbNdxNodeLink * SaveCurNode;
+
+ /* if left most node in parent return NULL */
+ if( n->PrevNode->CurKeyNo >= n->PrevNode->Leaf.NoOfKeysThisNode )
+ return NULL;
+ SaveCurNode = CurNode;
+ /* point curnode to right sib*/
+ GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 );
+ if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){
+ TempNode = CurNode;
+ CurNode = SaveCurNode;
+ TempNode->PrevNode = n->PrevNode;
+ return TempNode;
+ } else { /* node is already full */
+ ReleaseNodeMemory( CurNode );
+ CurNode = SaveCurNode;
+ return NULL;
+ }
+}
+/*************************************************************************/
+//! Short description
+/*!
+ \param n
+ \param Right
+*/
+xbShort xbNdx::MoveToRightNode( xbNdxNodeLink * n, xbNdxNodeLink * Right )
+{
+ xbShort j;
+ xbNdxNodeLink * TempNode;
+ xbNdxNodeLink * SaveCurNode;
+ xbNdxNodeLink * SaveNodeChain;
+
+ if( n->CurKeyNo == 0 ){
+ j = 1;
+ SaveNodeChain = NodeChain;
+ SaveCurNode = CurNode;
+ NodeChain = NULL;
+ GetLastKey( n->NodeNo, 0 );
+ memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode),HeadNode.KeyLen);
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ } else {
+ j = 0;
+ memcpy( KeyBuf, GetKeyData( j, n ), HeadNode.KeyLen);
+ }
+ PutKeyInNode( Right, 0, 0L, GetLeftNodeNo( j, n ), 1 );
+ ReleaseNodeMemory( Right );
+ TempNode = n;
+ CurNode = n->PrevNode;
+ n = n->PrevNode;
+ n->NextNode = NULL;
+ UpdateDeleteList( TempNode );
+ DeleteSibling( n );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+ \param Left
+*/
+xbShort xbNdx::MoveToLeftNode( xbNdxNodeLink * n, xbNdxNodeLink * Left )
+{
+ xbShort j, rc;
+ xbNdxNodeLink * SaveNodeChain;
+ xbNdxNodeLink * TempNode;
+
+ if( n->CurKeyNo == 0 )
+ j = 1;
+ else
+ j = 0;
+
+ /* save the original node chain */
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+
+ /* determine new right most key for left node */
+ GetLastKey( Left->NodeNo, 0 );
+ memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen);
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL; /* for next GetLastKey */
+ PutKeyData( Left->Leaf.NoOfKeysThisNode, Left);
+ PutLeftNodeNo( Left->Leaf.NoOfKeysThisNode+1, Left, GetLeftNodeNo( j,n ));
+ Left->Leaf.NoOfKeysThisNode++;
+ Left->CurKeyNo = Left->Leaf.NoOfKeysThisNode;
+ if(( rc = PutLeafNode( Left->NodeNo, Left )) != 0 )
+ return rc;
+ n->PrevNode->NextNode = NULL;
+ UpdateDeleteList( n );
+
+ /* get the new right most key for left to update parents */
+ GetLastKey( Left->NodeNo, 0 );
+
+ /* assemble the chain */
+ TempNode = Left->PrevNode;
+ TempNode->CurKeyNo--;
+ NodeChain->PrevNode = Left->PrevNode;
+ UpdateParentKey( CurNode );
+ ReleaseNodeMemory( NodeChain );
+ ReleaseNodeMemory( Left );
+ CurNode = TempNode;
+ NodeChain = SaveNodeChain;
+ TempNode->CurKeyNo++;
+ DeleteSibling( TempNode );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param n
+*/
+xbShort xbNdx::DeleteSibling( xbNdxNodeLink * n )
+{
+ xbNdxNodeLink * Left;
+ xbNdxNodeLink * Right;
+ xbNdxNodeLink * SaveCurNode;
+ xbNdxNodeLink * SaveNodeChain;
+ xbNdxNodeLink * TempNode;
+ xbShort rc;
+
+ /* this routine deletes sibling CurRecNo out of xbNodeLink n */
+ if( n->Leaf.NoOfKeysThisNode > 1 ){
+ RemoveKeyFromNode( n->CurKeyNo, n );
+ if( n->CurKeyNo == n->Leaf.NoOfKeysThisNode ){
+ SaveNodeChain = NodeChain;
+ SaveCurNode = CurNode;
+ NodeChain = NULL;
+ GetLastKey( n->NodeNo, 0 );
+ /* assemble the node chain */
+ TempNode = NodeChain->NextNode;
+ NodeChain->NextNode = NULL;
+ ReleaseNodeMemory( NodeChain );
+ TempNode->PrevNode = n;
+ UpdateParentKey( CurNode );
+ /* take it back apart */
+ ReleaseNodeMemory( TempNode );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ }
+ } else if( n->NodeNo == HeadNode.StartNode ) {
+ /* get here if root node and only one child remains */
+ /* make remaining node the new root */
+ if( n->CurKeyNo == 0 )
+ HeadNode.StartNode = GetLeftNodeNo( 1, n );
+ else
+ HeadNode.StartNode = GetLeftNodeNo( 0, n );
+ UpdateDeleteList( n );
+ NodeChain = NULL;
+ CurNode = NULL;
+ }
+ else if (( Left = LeftSiblingHasSpace( n )) != NULL )
+ return MoveToLeftNode( n, Left );
+ else if (( Right = RightSiblingHasSpace( n )) != NULL )
+ return MoveToRightNode( n, Right );
+ /* else if left sibling exists */
+ else if( n->PrevNode->CurKeyNo > 0 ) {
+ /* move right branch from left sibling to this node */
+ SaveCurNode = CurNode;
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+ GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 );
+ Left = CurNode;
+ Left->PrevNode = SaveCurNode->PrevNode;
+ GetLastKey( Left->NodeNo, 0 );
+ strncpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode),HeadNode.KeyLen );
+ if( n->CurKeyNo == 1 )
+ PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, n ));
+ PutKeyData( 0, n );
+ PutLeftNodeNo( 0, n, GetLeftNodeNo( Left->Leaf.NoOfKeysThisNode, Left ));
+ if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc;
+ SaveCurNode = n->PrevNode;
+ SaveCurNode->NextNode = NULL;
+ ReleaseNodeMemory( n );
+ Left->Leaf.NoOfKeysThisNode--;
+ if(( rc = PutLeafNode( Left->NodeNo, Left )) != XB_NO_ERROR ) return rc;
+ /* rebuild left side of tree */
+ GetLastKey( Left->NodeNo, 0 );
+ NodeChain->PrevNode = SaveCurNode;
+ SaveCurNode->CurKeyNo--;
+ UpdateParentKey( CurNode );
+ ReleaseNodeMemory( NodeChain );
+ ReleaseNodeMemory( Left );
+ CurNode = SaveCurNode;
+ NodeChain = SaveNodeChain;
+ }
+ /* right sibling must exist */
+ else if( n->PrevNode->CurKeyNo <= n->PrevNode->Leaf.NoOfKeysThisNode ){
+ /* move left branch from left sibling to this node */
+ SaveCurNode = CurNode;
+ SaveNodeChain = NodeChain;
+ NodeChain = NULL;
+
+ /* move the left node number one to the left if necessary */
+ if( n->CurKeyNo == 0 ){
+ PutLeftNodeNo( 0, n, GetLeftNodeNo( 1, n ));
+ GetLastKey( GetLeftNodeNo( 0, n ), 0 );
+ memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen);
+ PutKeyData( 0, n );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+ GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 );
+ /* put leftmost node number from right node in this node */
+ PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, CurNode ));
+ if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc;
+
+ /* remove the key from the right node */
+ RemoveKeyFromNode( 0, CurNode );
+ if(( rc = PutLeafNode( CurNode->NodeNo, CurNode )) != XB_NO_ERROR )
+ return rc;
+ ReleaseNodeMemory( CurNode );
+
+ /* update new parent key value */
+ GetLastKey( n->NodeNo, 0 );
+ NodeChain->PrevNode = n->PrevNode;
+ UpdateParentKey( CurNode );
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = SaveNodeChain;
+ CurNode = SaveCurNode;
+ } else {
+ /* this should never be true-but could be if 100 byte limit is ignored*/
+ std::cout << "Fatal index error" << std::endl;
+ exit(0);
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description
+/*!
+ \param DbfRec
+*/
+xbShort xbNdx::DeleteKey( xbLong DbfRec )
+{
+/* this routine assumes the key to be deleted is in KeyBuf */
+
+ xbNdxNodeLink * TempNode;
+ xbShort rc;
+
+#if 0
+ // Not sure why this check is here, but it prevents numeric keys
+ // from being deleted (and thus index updates will also fail).
+ // I have removed it for now. Derry Bryson
+ if( HeadNode.KeyType != 0x00 )
+ xb_error(XB_INVALID_KEY_TYPE);
+#endif
+
+ if(( rc = FindKey( KeyBuf, DbfRec )) != XB_FOUND )
+ return rc;
+
+ /* found the record to delete at this point */
+ HeadNode.NoOfKeys--;
+
+ /* delete the key from the node */
+ if(( rc = RemoveKeyFromNode( CurNode->CurKeyNo, CurNode )) != 0 )
+ return rc;
+
+ /* if root node, we are done */
+ if( !( CurNode->NodeNo == HeadNode.StartNode )){
+ /* if leaf node now empty */
+ if( CurNode->Leaf.NoOfKeysThisNode == 0 ){
+ TempNode = CurNode->PrevNode;
+ TempNode->NextNode = NULL;
+ UpdateDeleteList( CurNode );
+ CurNode = TempNode;
+ DeleteSibling( CurNode );
+ ProcessDeleteList();
+ }
+
+ /* if last key of leaf updated, update key in parent node */
+ /* this logic updates the correct parent key */
+
+ else if( CurNode->CurKeyNo == CurNode->Leaf.NoOfKeysThisNode )
+ UpdateParentKey( CurNode );
+ }
+
+ if(CurNode)
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ else
+ CurDbfRec = 0;
+
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0 )
+ return rc;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description
+/*!
+ \param option
+*/
+#ifdef XBASE_DEBUG
+xbShort xbNdx::CheckIndexIntegrity( const xbShort option )
+{
+ /* if option = 1, print out some stats */
+ xbShort rc;
+ xbLong ctr = 1L;
+ while( ctr <= dbf->NoOfRecords()){
+ if( option ) std::cout << "Checking Record " << ctr << std::endl;
+ if(( rc = dbf->GetRecord(ctr++)) != XB_NO_ERROR )
+ return rc;
+ if(!dbf->RecordDeleted()){
+ CreateKey( 0, 0 );
+ rc = FindKey( KeyBuf, dbf->GetCurRecNo());
+ if( rc != XB_FOUND ){
+ if( option ){
+ std::cout << "Record number " << dbf->GetCurRecNo()
+ << " Not Found" << std::endl;
+ std::cout << "Key = " << KeyBuf << std::endl;
+ }
+ return rc;
+ }
+ }
+ }
+ if( option )
+ std::cout << std::endl << "Total records checked = "
+ << ctr - 1 << std::endl;
+ return XB_NO_ERROR;
+}
+#endif
+/***********************************************************************/
+//! Short description
+/*!
+ \param statusFunc
+*/
+xbShort xbNdx::ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems))
+{
+ /* this method assumes the index has been locked in exclusive mode */
+ xbLong l;
+ xbShort rc, i, saveAutoLock;
+ xbNdxHeadNode TempHead;
+ FILE *t;
+ xbString TempName;
+ memcpy( &TempHead, &HeadNode, sizeof( struct xbNdxHeadNode ));
+ TempHead.NoOfKeys = 1L;
+ TempHead.TotalNodes = 2L;
+ TempHead.StartNode = 1L;
+ rc = dbf->xbase->DirectoryExistsInName( GetFileName() );
+ if( rc ){
+ TempName.assign(GetFileName(), 0, rc);
+ TempName += "TEMPFILE.NDX";
+ } else
+ TempName = "TEMPFILE.NDX";
+
+ if(( t = fopen( TempName, "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ if(( rc = PutHeadNode( &TempHead, t, 0 )) != 0 ){
+ fclose( t );
+ remove(TempName);
+ return rc;
+ }
+
+ for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){
+ if(( fwrite( "\x00", 1, 1, t )) != 1 ){
+ fclose( t );
+ remove(TempName);
+ return XB_WRITE_ERROR;
+ }
+ }
+ if( fclose( indexfp ) != 0 )
+ return XB_CLOSE_ERROR;
+ if( fclose( t ) != 0 )
+ return XB_CLOSE_ERROR;
+ if( remove( GetFileName() ) != 0 )
+ return XB_CLOSE_ERROR;
+ if( rename(TempName, GetFileName() ) != 0 )
+ return XB_WRITE_ERROR;
+ if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ saveAutoLock = dbf->GetAutoLock();
+ dbf->AutoLockOff();
+ for( l = 1; l <= dbf->PhysicalNoOfRecords(); l++ ){
+ if(statusFunc && (l == 1 || !(l % 100) || l == dbf->PhysicalNoOfRecords()))
+ statusFunc(l, dbf->PhysicalNoOfRecords());
+ if(( rc = dbf->GetRecord(l)) != XB_NO_ERROR ){
+ if(saveAutoLock)
+ dbf->AutoLockOn();
+ return rc;
+ }
+
+ if(!dbf->GetRealDelete() || !dbf->RecordDeleted()){
+ /* Create the key */
+ CreateKey( 0, 0 );
+ /* add key to index */
+ if(( rc = AddKey( l )) != XB_NO_ERROR ){
+ if(saveAutoLock)
+ dbf->AutoLockOn();
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+/***********************************************************************/
+//! Short description
+/*!
+ \param size
+*/
+void xbNdx::SetNodeSize(xbShort size)
+{
+#ifdef XB_VAR_NODESIZE
+ if(size >= XB_DEFAULT_NDX_NODE_SIZE)
+ {
+ if(size % XB_NDX_NODE_MULTIPLE)
+ NodeSize = ((size + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) *
+ XB_NDX_NODE_MULTIPLE;
+ else
+ NodeSize = size;
+ }
+ else
+ NodeSize = XB_DEFAULT_NDX_NODE_SIZE;
+#endif
+}
+
+/***********************************************************************/
+//! Short description
+/*!
+ \param buf
+ \param len
+*/
+void xbNdx::GetExpression(char *buf, int len)
+{
+ memcpy(buf, HeadNode.KeyExpression,
+ len < XB_NDX_NODE_SIZE ? len : XB_NDX_NODE_SIZE - XB_NDX_NODE_BASESIZE);
+}
+
+const char* xbNdx::GetExtWithDot(bool lower)
+{
+ return lower? ".ndx": ".NDX";
+}
+
+xbUShort xbNdx::GetKeyLen()
+{
+ return HeadNode.KeyLen;
+}
+
+const char* xbNdx::GetKeyExpression()
+{
+ return HeadNode.KeyExpression;
+}
+
+void xbNdx::FreeNodesMemory()
+{
+ ReleaseNodeMemory(NodeChain, true);
+ NodeChain = 0;
+// ReleaseNodeMemory(CloneChain, true);
+// CloneChain = 0;
+ ReleaseNodeMemory(FreeNodeChain, true);
+ FreeNodeChain = 0;
+ ReleaseNodeMemory(DeleteChain, true);
+ DeleteChain = 0;
+}
+
+#endif /* XB_INDEX_NDX */
diff --git a/xbase64/xbndx.h b/xbase64/xbndx.h
new file mode 100755
index 0000000..9f9d2d7
--- /dev/null
+++ b/xbase64/xbndx.h
@@ -0,0 +1,292 @@
+/* xbndx.h
+
+ Xbase64 project source code
+
+ This file contains a header file for the xbNdx object, which is used
+ for handling NDX type indices.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_NDX_H__
+#define __XB_NDX_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#include <xbase64/xbase64.h>
+#include <string.h>
+
+/*! \file xbndx.h
+*/
+
+//
+// Define the following to use inline versions of the respective methods.
+//
+#define XB_INLINE_GETDBFNO
+
+#define XB_NDX_NODE_BASESIZE 24 // size of base header data
+
+#define XB_VAR_NODESIZE // define to enable variable node sizes
+
+#ifndef XB_VAR_NODESIZE
+#define XB_NDX_NODE_SIZE 2048
+//#define XB_NDX_NODE_SIZE 512 // standard dbase node size
+#else
+#define XB_DEFAULT_NDX_NODE_SIZE 512
+#define XB_MAX_NDX_NODE_SIZE 4096
+#define XB_NDX_NODE_SIZE NodeSize
+#define XB_NDX_NODE_MULTIPLE 512
+#endif // XB_VAR_NODESIZE
+
+//! xbNdxHeadnode struct
+/*!
+*/
+
+struct XBDLLEXPORT xbNdxHeadNode { /* ndx header on disk */
+ xbLong StartNode; /* header node is node 0 */
+ xbLong TotalNodes; /* includes header node */
+ xbLong NoOfKeys; /* actual count + 1 */
+ /* not updated by borland dbe? */
+ xbUShort KeyLen; /* length of key data */
+ xbUShort KeysPerNode;
+ xbUShort KeyType; /* 00 = Char, 01 = Numeric */
+ xbLong KeySize; /* key len + 8 bytes */
+ char Unknown2;
+ char Unique;
+// char KeyExpression[488];
+#ifndef XB_VAR_NODESIZE
+ char KeyExpression[XB_NDX_NODE_SIZE - 24];
+#else
+ char KeyExpression[XB_MAX_NDX_NODE_SIZE - 24];
+#endif // XB_VAR_NODESIZE
+};
+
+//! xbNdxLeafNode struct
+/*!
+*/
+
+struct XBDLLEXPORT xbNdxLeafNode { /* ndx node on disk */
+ xbLong NoOfKeysThisNode;
+#ifndef XB_VAR_NODESIZE
+ char KeyRecs[XB_NDX_NODE_SIZE-4];
+#else
+ char KeyRecs[XB_MAX_NDX_NODE_SIZE - 4];
+#endif // XB_VAR_NODESIZE
+};
+
+//! xbNdxNodeLink struct
+/*!
+*/
+
+struct XBDLLEXPORT xbNdxNodeLink { /* ndx node memory */
+ xbNdxNodeLink * PrevNode;
+ xbNdxNodeLink * NextNode;
+ xbLong CurKeyNo; /* 0 - KeysPerNode-1 */
+ xbLong NodeNo;
+ struct xbNdxLeafNode Leaf;
+};
+
+//! xbNdx class
+/*!
+*/
+
+class XBDLLEXPORT xbNdx : public xbIndex
+{
+ public:
+ xbNdx();
+ xbNdx(xbDbf *);
+ virtual ~xbNdx();
+
+/* don't uncomment next line - it causes seg faults for some undiagnosed reason*/
+// ~NDX() { if( NdxStatus ) CloseIndex(); }
+
+ xbShort CreateIndex( const char *IxName, const char *Exp,
+ xbShort Unique, xbShort OverLay );
+ xbLong GetTotalNodes();
+ xbULong GetCurDbfRec() { return CurDbfRec; }
+ xbShort CreateKey( xbShort, xbShort );
+ xbShort GetCurrentKey(char *key);
+ xbShort AddKey( xbLong );
+ xbShort UniqueIndex() { return HeadNode.Unique; }
+ xbShort DeleteKey( xbLong );
+ xbShort KeyWasChanged();
+ xbShort FindKey( const char *Key );
+ xbShort FindKey();
+ xbShort FindKey( xbDouble );
+#ifdef XBASE_DEBUG
+ void DumpHdrNode( xbShort Option );
+ void DumpNodeRec( xbLong NodeNo );
+ void DumpNodeChain();
+ xbShort CheckIndexIntegrity( xbShort Option );
+#endif
+ //! Short description.
+ /*!
+ */
+ xbShort GetNextKey() { return GetNextKey( 1 ); }
+ //! Short description.
+ /*!
+ */
+ xbShort GetLastKey() { return GetLastKey( 0, 1 ); }
+ //! Short description.
+ /*!
+ */
+ xbShort GetFirstKey() { return GetFirstKey( 1 ); }
+ //! Short description.
+ /*!
+ */
+ xbShort GetPrevKey() { return GetPrevKey( 1 ); }
+ xbShort ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0);
+ xbShort KeyExists( const char * Key ) { return FindKey( Key, strlen( Key ), 0 ); }
+ xbShort KeyExists( xbDouble );
+
+ virtual void SetNodeSize(xbShort size);
+
+ virtual void GetExpression(char *buf, int len);
+ virtual const char* GetExtWithDot(bool lower);
+
+ protected:
+ virtual xbUShort GetKeyLen();
+ virtual const char* GetKeyExpression();
+ virtual void FreeNodesMemory();
+
+ protected:
+ xbNdxHeadNode HeadNode;
+ xbNdxLeafNode LeafNode;
+ xbLong xbNodeLinkCtr;
+ xbLong ReusedxbNodeLinks;
+
+#ifndef XB_VAR_NODESIZE
+ char Node[XB_NDX_NODE_SIZE];
+#else
+ char Node[XB_MAX_NDX_NODE_SIZE];
+#endif // XB_VAR_NODESIZE
+
+ xbNdxNodeLink * NodeChain; /* pointer to node chain of index nodes */
+ xbNdxNodeLink * FreeNodeChain; /* pointer to chain of free index nodes */
+ xbNdxNodeLink * CurNode; /* pointer to current node */
+ xbNdxNodeLink * DeleteChain; /* pointer to chain to delete */
+// xbNdxNodeLink * CloneChain; /* pointer to node chain copy (add dup) */
+
+/* private functions */
+ xbLong GetLeftNodeNo( xbShort, xbNdxNodeLink * );
+
+
+ // in line functions for performance reasons
+ //! Short description.
+ /*!
+ */
+ inline xbShort CompareKey( const char *Key1, const char *Key2, xbShort Klen )
+ {
+ xbDouble d1, d2;
+ int c;
+
+ if(!( Key1 && Key2 )) return -1;
+
+ if( Klen > HeadNode.KeyLen ) Klen = HeadNode.KeyLen;
+
+ if( HeadNode.KeyType == 0 )
+ {
+ c = memcmp(Key1, Key2, Klen);
+ if(c < 0)
+ return 2;
+ else if(c > 0)
+ return 1;
+ return 0;
+ }
+ else /* key is numeric */
+ {
+ d1 = dbf->xbase->GetDouble( Key1 );
+ d2 = dbf->xbase->GetDouble( Key2 );
+ if( d1 == d2 ) return 0;
+ else if( d1 > d2 ) return 1;
+ else return 2;
+ }
+ }
+
+#ifndef XB_INLINE_GETDBFNO
+ xbLong GetDbfNo( xbShort, xbNdxNodeLink * );
+#else
+ //! Short description.
+ /*!
+ */
+ inline xbLong GetDbfNo( xbShort RecNo, xbNdxNodeLink *n )
+ {
+ xbNdxLeafNode *temp;
+ char *p;
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L;
+ p = temp->KeyRecs + 4;
+ p += RecNo * ( 8 + HeadNode.KeyLen );
+ return( dbf->xbase->GetLong( p ));
+ }
+#endif
+ char * GetKeyData( xbShort, xbNdxNodeLink * );
+ xbUShort GetKeysPerNode();
+ virtual xbShort GetHeadNode();
+ xbShort GetLeafNode( xbLong, xbShort );
+ xbNdxNodeLink * GetNodeMemory();
+ void ReleaseNodeMemory(xbNdxNodeLink *n, xbBool doFree = false);
+ xbShort BSearchNode(const char *key, xbShort klen,
+ const xbNdxNodeLink *node, xbShort *comp);
+ xbLong GetLeafFromInteriorNode( const char *Tkey, xbShort Klen );
+ xbShort CalcKeyLen();
+ xbShort PutKeyData( xbShort, xbNdxNodeLink * );
+ xbShort PutLeftNodeNo( xbShort, xbNdxNodeLink *, xbLong );
+ xbShort PutLeafNode( xbLong, xbNdxNodeLink * );
+ xbShort PutHeadNode( xbNdxHeadNode *, FILE *, xbShort );
+ xbShort PutDbfNo( xbShort, xbNdxNodeLink *, xbLong );
+ xbShort PutKeyInNode( xbNdxNodeLink *, xbShort, xbLong, xbLong, xbShort );
+ xbShort SplitLeafNode( xbNdxNodeLink *, xbNdxNodeLink *, xbShort, xbLong );
+ xbShort SplitINode( xbNdxNodeLink *, xbNdxNodeLink *, xbLong );
+ xbShort AddToIxList();
+ xbShort RemoveFromIxList();
+ xbShort RemoveKeyFromNode( xbShort, xbNdxNodeLink * );
+ xbShort FindKey( const char *Tkey, xbShort Klen, xbShort RetrieveSw );
+ xbShort UpdateParentKey( xbNdxNodeLink * );
+ xbShort GetFirstKey( xbShort );
+ xbShort GetNextKey( xbShort );
+ xbShort GetLastKey( xbLong, xbShort );
+ xbShort GetPrevKey( xbShort );
+ void UpdateDeleteList( xbNdxNodeLink * );
+ void ProcessDeleteList();
+ xbNdxNodeLink * LeftSiblingHasSpace( xbNdxNodeLink * );
+ xbNdxNodeLink * RightSiblingHasSpace( xbNdxNodeLink * );
+ xbShort DeleteSibling( xbNdxNodeLink * );
+ xbShort MoveToLeftNode( xbNdxNodeLink *, xbNdxNodeLink * );
+ xbShort MoveToRightNode( xbNdxNodeLink *, xbNdxNodeLink * );
+ xbShort FindKey( const char *Tkey, xbLong DbfRec ); /* for a specific dbf no */
+};
+#endif /* __XB_NDX_H__ */
diff --git a/xbase64/xbnode.cpp b/xbase64/xbnode.cpp
new file mode 100755
index 0000000..5e688c1
--- /dev/null
+++ b/xbase64/xbnode.cpp
@@ -0,0 +1,6 @@
+#include "xbNode.h"
+
+void xbNodeLink::AddNode(xbNodeLink* node)
+{
+ nextNode_=node;
+}
diff --git a/xbase64/xbnode.h b/xbase64/xbnode.h
new file mode 100755
index 0000000..b1c3fdf
--- /dev/null
+++ b/xbase64/xbnode.h
@@ -0,0 +1,19 @@
+#ifndef xbNode_h
+#define xbNode_h
+
+class xbNodeLink
+{
+ public:
+ xbNodeLink(): nextNode_(0) {}
+ void AddNode(xbNodeLink* node);
+ xbNodeLink* GetNext() {return nextNode_;}
+
+ private:
+ xbNodeLink(const xbNodeLink&);
+ xbNodeLink& operator=(const xbNodeLink&);
+
+ private:
+ xbNodeLink* nextNode_;
+};
+
+#endif
diff --git a/xbase64/xbntx.cpp b/xbase64/xbntx.cpp
new file mode 100755
index 0000000..673aa68
--- /dev/null
+++ b/xbase64/xbntx.cpp
@@ -0,0 +1,2604 @@
+/* xbntx.xpp
+
+ Xbase64 project source code
+
+ NTX (Clipper) indexing routines for X-Base
+
+ Copyright (C) 1999 SynXis Corp., Bob Cotton
+ email - bob@synxis.com
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbntx.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#ifdef XB_INDEX_NTX
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ctype.h>
+#include <sys/stat.h>
+
+//#include <xbase64/xbexcept.h>
+
+/*! \file xbntx.cpp
+*/
+
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+/* This routine dumps the node chain to stdout */
+#ifdef XBASE_DEBUG
+void xbNtx::DumpNodeChain( void )
+{
+ xbNodeLink *n;
+ std::cout << "*************************" << std::endl;
+ std::cout << "NodeLinkCtr = " << NodeLinkCtr << std::endl;
+ std::cout << "Reused = " << ReusedNodeLinks << std::endl;
+
+ n = NodeChain;
+ while(n){
+ std::cout << "xbNodeLink Chain" << n->NodeNo << std::endl;
+ n = n->NextNode;
+ }
+ n = FreeNodeChain;
+ while(n){
+ std::cout << "FreeNodeLink Chain" << n->NodeNo << std::endl;
+ n = n->NextNode;
+ }
+ n = DeleteChain;
+ while(n){
+ std::cout << "DeleteLink Chain" << n->NodeNo << std::endl;
+ n = n->NextNode;
+ }
+}
+#endif
+/***********************************************************************/
+//! Short description.
+/*!
+ \param n
+*/
+/* This routine returns a chain of one or more index nodes back to the */
+/* free node chain */
+
+void xbNtx::ReleaseNodeMemory( xbNodeLink * n, xbBool doFree )
+{
+ xbNodeLink *temp;
+
+ if(doFree){
+ while(n){
+ temp = n->NextNode;
+ if(n->offsets)
+ free(n->offsets);
+ free(n);
+ n = temp;
+ }
+ } else {
+ if(!FreeNodeChain )
+ FreeNodeChain = n;
+ else { /* put this list at the end */
+ temp = FreeNodeChain;
+ while( temp->NextNode )
+ temp = temp->NextNode;
+ temp->NextNode = n;
+ }
+ }
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+/* This routine returns a node from the free chain if available, */
+/* otherwise it allocates new memory for the requested node */
+
+xbNodeLink * xbNtx::GetNodeMemory( void )
+{
+ xbNodeLink * temp;
+ if( FreeNodeChain ){
+ temp = FreeNodeChain;
+ temp->offsets = FreeNodeChain->offsets;
+ FreeNodeChain = temp->NextNode;
+ ReusedNodeLinks++;
+ memset( temp->Leaf.KeyRecs, 0x00, XB_NTX_NODE_SIZE );
+ temp->Leaf.NoOfKeysThisNode = 0;
+ temp->PrevNode = 0x00;
+ temp->NextNode = 0x00;
+ temp->CurKeyNo = 0L;
+ temp->NodeNo = 0L;
+
+ for (int i = 0; i < HeadNode.KeysPerNode + 1; i++){
+ temp->offsets[i] = 2 + ((HeadNode.KeysPerNode + 1) * 2) + (HeadNode.KeySize * i);
+ }
+ } else {
+ temp = (xbNodeLink *) malloc( sizeof( xbNodeLink ));
+ if(temp==NULL) return NULL;
+ memset( temp, 0x00, sizeof( xbNodeLink ));
+ temp->offsets = (xbUShort *)malloc( (HeadNode.KeysPerNode + 1) * sizeof(xbUShort));
+ if (temp->offsets==NULL) {
+ free(temp);
+ return NULL;
+ };
+ NodeLinkCtr++;
+ }
+ return temp;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+#ifdef XBASE_DEBUG
+void xbNtx::DumpHdrNode( xbShort Option )
+{
+ if( Option == 0 ){
+ std::cout << "Signature = " << HeadNode.Signature << std::endl;
+ std::cout << "Version = " << HeadNode.Version << std::endl;
+ std::cout << "StartPahe = " << HeadNode.StartNode << std::endl;
+ std::cout << "UnusedOffset = " << HeadNode.UnusedOffset << std::endl;
+ std::cout << "KeySize = " << HeadNode.KeySize << std::endl;
+ std::cout << "KeyLen = " << HeadNode.KeyLen << std::endl;
+ std::cout << "DecimalCount = " << HeadNode.DecimalCount << std::endl;
+ std::cout << "KeysPerNode = " << HeadNode.KeysPerNode << std::endl;
+ std::cout << "HalfKeysPerPage = " << HeadNode.HalfKeysPerNode << std::endl;
+ std::cout << "KeyExpression = " << HeadNode.KeyExpression << std::endl;
+ std::cout << "Unique = " << HeadNode.Unique << std::endl;
+ } else
+ std::cout << "Print Hdr Node option not implemented yet" << std::endl;
+}
+#endif
+/***********************************************************************/
+//! Constructor
+/*!
+*/
+xbNtx::xbNtx() : xbIndex()
+{
+}
+/***********************************************************************/
+//! Constructor
+/*!
+ \param pdbf
+*/
+xbNtx::xbNtx( xbDbf * pdbf ) : xbIndex (pdbf)
+{
+ memset( Node, 0x00, XB_NTX_NODE_SIZE );
+ memset( &HeadNode, 0x00, sizeof( NtxHeadNode ));
+ NodeChain = NULL;
+// CloneChain = NULL;
+ FreeNodeChain = NULL;
+ DeleteChain = NULL;
+ CurNode = NULL;
+ NodeLinkCtr = 0L;
+ ReusedNodeLinks = 0L;
+}
+/***********************************************************************/
+//! Destructor
+/*!
+*/
+xbNtx::~xbNtx()
+{
+ CloseIndex();
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbNtx::GetHeadNode( void )
+{
+ char *p;
+ if( !IsOpen() )
+ return XB_NOT_OPEN;
+ if( _fseek( indexfp, 0, SEEK_SET ))
+ return XB_SEEK_ERROR;
+ if(( fread( Node, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 )
+ return XB_READ_ERROR;
+
+ /* load the head node structure */
+ p = Node;
+ HeadNode.Signature = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.Version = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.StartNode = dbf->xbase->GetULong( p ); p += sizeof(xbULong);
+ HeadNode.UnusedOffset = dbf->xbase->GetULong( p ); p += sizeof(xbULong);
+ HeadNode.KeySize = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.KeyLen = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.DecimalCount = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.KeysPerNode = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ HeadNode.HalfKeysPerNode = dbf->xbase->GetShort( p ); p += sizeof(xbUShort);
+ strncpy(HeadNode.KeyExpression, p, 256); p+= 256;
+// HeadNode.Unique = *p++; ++ is unused code 8/19/03 - gkunkel
+ HeadNode.Unique = *p;
+
+ p = HeadNode.KeyExpression;
+ while (*p){
+ *p = toupper(*p);
+ p++;
+ }
+ return 0;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param NodeNo
+ \param SetNodeChain
+*/
+/* This routine reads a leaf node from disk */
+/* */
+/* If SetNodeChain 2, then the node is not appended to the node chain */
+/* but the CurNode pointer points to the node read */
+/* If SetNodeChain 1, then the node is appended to the node chain */
+/* If SetNodeChain 0, then record is only read to Node memory */
+
+xbShort xbNtx::GetLeafNode( xbLong NodeNo, xbShort SetNodeChain )
+{
+ xbNodeLink *n;
+ char *p;
+
+ if( !IsOpen() )
+ return XB_NOT_OPEN;
+
+ if( _fseek( indexfp, (xbOffT)NodeNo, SEEK_SET ))
+ return XB_SEEK_ERROR;
+
+ if(( fread( Node, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 )
+ return XB_READ_ERROR;
+
+ if( !SetNodeChain ) return 0;
+
+ if(( n = GetNodeMemory()) == NULL )
+ return XB_NO_MEMORY;
+
+ n->NodeNo = NodeNo;
+ n->CurKeyNo = 0L;
+ n->NextNode = NULL;
+
+ // The offsets at the head of each leaf are not necessarly in order.
+ p = Node + 2;
+ for( int i = 0; i < HeadNode.KeysPerNode + 1; i++){
+ n->offsets[i] = dbf->xbase->GetShort( p );
+ p += 2;
+ }
+
+ // Do the edian translation correctly
+ n->Leaf.NoOfKeysThisNode = dbf->xbase->GetShort( Node );
+ memcpy( n->Leaf.KeyRecs, Node, XB_NTX_NODE_SIZE );
+
+ /* put the node in the chain */
+ if( SetNodeChain == 1 ){
+ if( NodeChain == NULL ){ /* first one ? */
+ NodeChain = n;
+ CurNode = n;
+ CurNode->PrevNode = NULL;
+ } else {
+ n->PrevNode = CurNode;
+ CurNode->NextNode = n;
+ CurNode = n;
+ }
+ }
+ else
+ CurNode = n;
+
+ return 0;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param n
+*/
+#ifdef XBASE_DEBUG
+void xbNtx::DumpNodeRec( xbLong n )
+{
+ char *p;
+ xbShort NoOfKeys;
+ xbLong LeftBranch, RecNo;
+ xbShort i,j;
+
+ GetLeafNode( n, 0 );
+ NoOfKeys = dbf->xbase->GetShort( Node );
+ p = Node + 4; /* go past no of keys */
+std::cout << "-----------------------------------------------" << std::endl;
+ std::cout << "Node # " << n;
+ std::cout << "Number of keys = " << NoOfKeys << std::endl;
+ std::cout << " Key Left Rec Key" << std::endl;
+ std::cout << "Number Branch Number Data" << std::endl;
+
+ for( i = 0; i < GetKeysPerNode()+1 /*NoOfKeys*/; i++ ){
+ LeftBranch = dbf->xbase->GetLong( p );
+ p+=4;
+ RecNo = dbf->xbase->GetLong( p );
+ p+=4;
+ std::cout << i << " "
+ << LeftBranch << " "
+ << RecNo << " " << std::endl;
+ for( j = 0; j < HeadNode.KeyLen; j++ ) std::cout << *p++;
+ }
+}
+#endif
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+*/
+xbLong xbNtx::GetDbfNo( xbShort RecNo, xbNodeLink * n )
+{
+ NtxLeafNode *temp;
+ char *p;
+ xbUShort itemOffset;
+
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ p = temp->KeyRecs;
+ if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode )) return 0L;
+ itemOffset = GetItemOffset(RecNo, n, 0);
+ // ItemOffset is from the beginning of the record.
+ p += itemOffset;
+ p += 4;
+ return( dbf->xbase->GetLong( p ));
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+*/
+xbLong xbNtx::GetLeftNodeNo( xbShort RecNo, xbNodeLink * n )
+{
+ NtxLeafNode *temp;
+ char *p;
+ xbUShort itemOffset;
+
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ p = temp->KeyRecs;
+ if( RecNo < 0 || RecNo > temp->NoOfKeysThisNode ) return 0L;
+ itemOffset = GetItemOffset(RecNo, n, 0);
+ // ItemOffset is from the beginning of the record.
+ p += itemOffset;
+ return( dbf->xbase->GetULong( p ));
+}
+
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+*/
+char * xbNtx::GetKeyData( xbShort RecNo, xbNodeLink * n )
+{
+ NtxLeafNode *temp;
+ char *p;
+ xbUShort itemOffset;
+ if( !n ) return 0L;
+ temp = &n->Leaf;
+ p = temp->KeyRecs;
+ if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode )) return 0L;
+ itemOffset = GetItemOffset(RecNo, n, 0);
+ // ItemOffset is from the beginning of the record.
+ p += itemOffset + 8;
+ return( p );
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+ \param
+*/
+xbUShort
+xbNtx::GetItemOffset(xbShort RecNo, xbNodeLink *n, xbShort) {
+ if( RecNo > (this->HeadNode.KeysPerNode + 1) ){
+ std::cout << "RecNo = " << RecNo << std::endl;
+ std::cout << "this->HeadNode.KeysPerNode = "
+ << this->HeadNode.KeysPerNode << std::endl;
+ std::cout << "********************* BUG ***********************"
+ << std::endl;
+ // ;-)
+ exit(1);
+ }
+
+ return n->offsets[RecNo];
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbUShort
+xbNtx::InsertKeyOffset(xbShort pos, xbNodeLink *n)
+{
+ xbUShort temp;
+
+ // save the new offset
+ temp = n->offsets[n->Leaf.NoOfKeysThisNode + 1];
+
+ for( int i = n->Leaf.NoOfKeysThisNode + 1; i > pos; i-- ){
+ n->offsets[i] = n->offsets[i-1];
+ }
+ n->offsets[pos] = temp;
+
+ return n->offsets[pos];
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbUShort
+xbNtx::DeleteKeyOffset(xbShort pos, xbNodeLink *n)
+{
+ xbUShort temp;
+ xbShort i;
+ // save the old offset
+ temp = n->offsets[pos];
+
+ for( i = pos; i < n->Leaf.NoOfKeysThisNode; i++ ){
+ n->offsets[i] = n->offsets[i+1];
+ }
+ n->offsets[i] = temp;
+ return n->offsets[i];
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbLong xbNtx::GetTotalNodes( void )
+{
+// if( &HeadNode )
+// return HeadNode.TotalNodes;
+// else
+ return 0L;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbUShort xbNtx::GetKeysPerNode( void )
+{
+ if( &HeadNode )
+ return HeadNode.KeysPerNode;
+ else
+ return 0L;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNtx::GetFirstKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the first index pointer */
+
+ xbLong TempNodeNo;
+ xbShort rc;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+
+ /* initialize the node chain */
+ if( NodeChain ){
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+
+ if(( rc = GetHeadNode()) != 0 ){
+ CurDbfRec = 0L;
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ /* get a node and add it to the link */
+
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+/* traverse down the left side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){
+ TempNodeNo = GetLeftNodeNo( 0, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = 0;
+ }
+ CurDbfRec = GetDbfNo( 0, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNtx::GetNextKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the next index pointer */
+
+ xbNodeLink * TempNodeLink;
+ xbLong TempNodeNo;
+ xbShort rc;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+ // if((rc = LockIndex( XB_LOCK )) != 0)
+ // return rc;
+#endif
+
+ if( !IsOpen() ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return XB_NOT_OPEN;
+ }
+
+ if( !CurNode ){
+ rc = GetFirstKey( RetrieveSw );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ /* more keys on this node ? */
+ if(( CurNode->Leaf.NoOfKeysThisNode -1 ) > CurNode->CurKeyNo ){
+ CurNode->CurKeyNo++;
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+ }
+
+ /* if head node we are at eof */
+ if( CurNode->NodeNo == HeadNode.StartNode ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_EOF;
+ }
+
+ /* this logic assumes that interior nodes have n+1 left node no's where */
+ /* n is the number of keys in the node */
+ /* pop up one node to the interior node level & free the leaf node */
+ TempNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempNodeLink );
+
+ /* while no more right keys && not head node, pop up one node */
+ while(( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode ) &&
+ ( CurNode->NodeNo != HeadNode.StartNode )){
+ TempNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempNodeLink );
+ }
+
+ /* if head node && right most key, return end-of-file */
+ if(( HeadNode.StartNode == CurNode->NodeNo ) &&
+ ( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode )){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_EOF;
+ }
+
+ /* move one to the right */
+ CurNode->CurKeyNo++;
+ TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );
+
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+/* traverse down the left side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){
+ TempNodeNo = GetLeftNodeNo( 0, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = 0;
+ }
+ CurDbfRec = GetDbfNo( 0, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param NodeNo
+ \param RetrieveSw
+*/
+xbShort xbNtx::GetLastKey( xbLong NodeNo, xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the last index pointer */
+
+/* If NodeNo = 0, start at head node, otherwise start at NodeNo */
+
+ xbLong TempNodeNo;
+ xbShort rc;
+
+// TODO
+// NTX files keep no TotalNode count.
+// if( NodeNo < 0 || NodeNo > HeadNode.TotalNodes )
+// return XB_INVALID_NODE_NO;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+
+ /* initialize the node chain */
+ if( NodeChain ){
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+ if( NodeNo == 0L )
+ if(( rc = GetHeadNode()) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ /* get a node and add it to the link */
+ if( NodeNo == 0L ){
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK);
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ } else {
+ if(( rc = GetLeafNode( NodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ }
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+
+/* traverse down the right side of the tree */
+ while( GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode )){
+ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ }
+ CurNode->CurKeyNo--; /* leaf node has one fewer ix recs */
+ CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode-1, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RetrieveSw
+*/
+xbShort xbNtx::GetPrevKey( xbShort RetrieveSw )
+{
+/* This routine returns 0 on success and sets CurDbfRec to the record */
+/* corresponding to the previous index pointer */
+
+ xbNodeLink * TempNodeLink;
+ xbLong TempNodeNo;
+ xbShort rc;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+
+ if( !IsOpen() ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return XB_NOT_OPEN;
+ }
+
+ if( !CurNode ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return GetFirstKey( RetrieveSw );
+ }
+
+ /* more keys on this node ? */
+ if( CurNode->CurKeyNo > 0 ){
+ CurNode->CurKeyNo--;
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+ }
+
+ /* this logic assumes that interior nodes have n+1 left node no's where */
+ /* n is the number of keys in the node */
+ /* pop up one node to the interior node level & free the leaf node */
+
+ if( !CurNode->PrevNode ){ /* michael - make sure prev node exists */
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_EOF;
+ }
+
+ TempNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempNodeLink );
+
+ /* while no more left keys && not head node, pop up one node */
+ while(( CurNode->CurKeyNo == 0 ) &&
+ ( CurNode->NodeNo != HeadNode.StartNode )){
+ TempNodeLink = CurNode;
+ CurNode = CurNode->PrevNode;
+ CurNode->NextNode = NULL;
+ ReleaseNodeMemory( TempNodeLink );
+ }
+
+ /* if head node && left most key, return end-of-file */
+ if(( HeadNode.StartNode == CurNode->NodeNo ) &&
+ ( CurNode->CurKeyNo == 0 )){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_EOF;
+ }
+
+ /* move one to the left */
+ CurNode->CurKeyNo--;
+ TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ else /* leaf node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode -1;
+
+/* traverse down the right side of the tree */
+ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */
+ TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ if( GetLeftNodeNo( 0, CurNode )) /* if interior node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ else /* leaf node */
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode -1;
+ }
+ CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode -1, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw )
+ return dbf->GetRecord( CurDbfRec );
+ else
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Key1
+ \param Key2
+ \param Klen
+*/
+xbShort xbNtx::CompareKey( const char * Key1, const char * Key2, xbShort Klen )
+{
+/* if key1 = key2 --> return 0 */
+/* if key1 > key2 --> return 1 */
+/* if key1 < key2 --> return 2 */
+
+ const char *k1, *k2;
+ xbShort i;
+
+ if( Klen > HeadNode.KeyLen ) Klen = HeadNode.KeyLen;
+ k1 = Key1;
+ k2 = Key2;
+ for( i = 0; i < Klen; i++ ){
+ if( *k1 > *k2 ) return 1;
+ if( *k1 < *k2 ) return 2;
+ k1++;
+ k2++;
+ }
+ return 0;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Key1
+ \param Key2
+*/
+xbShort xbNtx::CompareKey( const char * Key1, const char * Key2)
+{
+/* if key1 = key2 --> return 0 */
+/* if key1 > key2 --> return 1 */
+/* if key1 < key2 --> return 2 */
+
+ int rc;
+ rc = strcmp(Key1, Key2);
+ if( rc < 0 )
+ return 2;
+ else if( rc > 0 )
+ return 1;
+ else
+ return 0;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Tkey
+ \param
+*/
+xbULong xbNtx::GetLeafFromInteriorNode( const char * Tkey, xbShort )
+{
+ /* This function scans an interior node for a key and returns the */
+ /* correct interior leaf node no */
+
+ xbShort rc, p;
+
+ /* if Tkey > any keys in node, return right most key */
+ p = CurNode->Leaf.NoOfKeysThisNode -1 ;
+ if( CompareKey( Tkey, GetKeyData( p, CurNode )) == 1 ){
+ CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode;
+ return GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode );
+ }
+
+ /* otherwise, start at the beginning and scan up */
+ p = 0;
+ while( p < CurNode->Leaf.NoOfKeysThisNode){
+ rc = CompareKey( Tkey, GetKeyData( p, CurNode ) );
+ if (rc == 2) break;
+ else if (rc == 0){
+ CurNode->CurKeyNo = p;
+ CurDbfRec = GetDbfNo( p, CurNode );
+ return 0;
+ }
+ p++;
+ }
+
+ CurNode->CurKeyNo = p;
+ return GetLeftNodeNo( p, CurNode );
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbShort xbNtx::KeyExists( xbDouble d )
+{
+ char buf[9];
+ memset( buf, 0x00, 9 );
+ dbf->xbase->PutDouble( buf, d );
+ return FindKey( buf, 8, 0 );
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param d
+*/
+xbShort xbNtx::FindKey( xbDouble d )
+{
+ char buf[9];
+ memset( buf, 0x00, 9 );
+ dbf->xbase->PutDouble( buf, d );
+ return FindKey( buf, 8, 1 );
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Key
+*/
+xbShort xbNtx::FindKey( const char * Key )
+{
+ return FindKey( Key, strlen( Key ), 1 );
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Tkey
+ \param DbfRec
+*/
+xbShort xbNtx::FindKey( const char * Tkey, xbLong DbfRec )
+{
+ /* find a key with a specifc xbDbf record number */
+ xbShort rc;
+ xbLong CurDbfRecNo;
+ xbLong CurNtxDbfNo;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+
+ /* if we are already on the correct key, return XB_FOUND */
+ if( CurNode ){
+ CurDbfRecNo = dbf->GetCurRecNo();
+ CurNtxDbfNo = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ if( CurDbfRecNo == CurNtxDbfNo ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_FOUND;
+ }
+ }
+
+ rc = FindKey( Tkey, HeadNode.KeyLen, 0 );
+
+ while( rc == 0 || rc == XB_FOUND ){
+ if( strncmp( Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ),
+ HeadNode.KeyLen ) == 0 ){
+ if( DbfRec == GetDbfNo( CurNode->CurKeyNo, CurNode )){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+ // LockIndex( XB_UNLOCK );
+#endif
+ return XB_FOUND;
+ } else
+ rc = GetNextKey( 0 );
+ } else {
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_NOT_FOUND;
+ }
+ }
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_NOT_FOUND;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbNtx::FindKey( void )
+{
+ /* if no paramaters given, use KeyBuf */
+ return( FindKey( KeyBuf, HeadNode.KeyLen, 0 ));
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param Tkey
+ \param Klen
+ \param RetrieveSw
+*/
+xbShort xbNtx::FindKey( const char * Tkey, xbShort Klen, xbShort RetrieveSw )
+{
+ /* This routine sets the current key to the found key */
+ /* if RetrieveSw is true, the method positions the dbf record */
+
+ xbShort rc,i;
+ xbLong TempNodeNo;
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+ if( NodeChain ){
+ ReleaseNodeMemory( NodeChain );
+ NodeChain = NULL;
+ }
+
+ if(( rc = GetHeadNode()) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+
+ // If the index is empty
+ if( HeadNode.StartNode == 0){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_NOT_FOUND;
+ }
+
+ /* load first node */
+ if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){
+ CurDbfRec = 0L;
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ /* traverse down the tree until it hits a leaf */
+ while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */
+ TempNodeNo = GetLeafFromInteriorNode( Tkey, Klen );
+
+#if 1
+ // GetLeafFromInteriorNode will return 0 if the key is found on
+ // an inode. But the leftNodeNo will not be 0.
+ if (TempNodeNo == 0 && GetLeftNodeNo( 0, CurNode ) != 0){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
+ return XB_FOUND;
+ }
+#endif
+
+ if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ CurDbfRec = 0L;
+ return rc;
+ }
+ }
+
+ /* leaf level */
+ for( i = 0; i < CurNode->Leaf.NoOfKeysThisNode; i++ ){
+ rc = CompareKey( Tkey, GetKeyData( i, CurNode ) );
+ if( rc == 0 ){
+ CurNode->CurKeyNo = i;
+ CurDbfRec = GetDbfNo( i, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
+ return XB_FOUND;
+ } else if( rc == 2 ) {
+ CurNode->CurKeyNo = i;
+ CurDbfRec = GetDbfNo( i, CurNode );
+ if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
+ // If key is lessthan, without length involved,
+ // Check to see if the substring match
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if(CompareKey( Tkey, GetKeyData( i, CurNode ), Klen ) == 0)
+ return XB_FOUND;
+ else
+ return XB_NOT_FOUND;
+ }
+ }
+ CurNode->CurKeyNo = i;
+ CurDbfRec = GetDbfNo( i, CurNode );
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ if( RetrieveSw ) dbf->GetRecord( CurDbfRec );
+ return XB_NOT_FOUND;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbNtx::CalcKeyLen( void )
+{
+ xbShort rc;
+ xbExpNode * TempNode;
+ char FieldName[11];
+ char Type;
+ TempNode = IxExp->GetFirstTreeNode();
+
+ if( !TempNode ) return 0;
+ if( TempNode->Type == 'd' ) return TempNode->ResultLen;
+ if( TempNode->Type == 'D' ) {
+ memset( FieldName, 0x00, 11 );
+ memcpy( FieldName, TempNode->NodeText, TempNode->Len );
+ Type = dbf->GetFieldType( dbf->GetFieldNo( FieldName ));
+ if( Type == 'N' || Type == 'F' )
+ return TempNode->ResultLen;
+ }
+
+ if(( rc = IxExp->ProcessExpression()) != XB_NO_ERROR )
+ return 0;
+
+ TempNode = (xbExpNode *) IxExp->Pop();
+ if( !TempNode ) return 0;
+ rc = TempNode->DataLen;
+
+// if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode );
+ if( !TempNode->InTree ) delete TempNode;
+ return rc;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param IxName
+ \param Exp
+ \param Unique
+ \param Overlay
+*/
+xbShort xbNtx::CreateIndex(const char * IxName, const char * Exp, xbShort Unique, xbShort Overlay )
+{
+ xbShort i, KeyLen, rc;
+
+ if( IsOpen()) CloseIndex();
+ if( strlen( Exp ) > 255 ) return XB_INVALID_KEY_EXPRESSION;
+ if( dbf->GetDbfStatus() == 0 ) return XB_NOT_OPEN;
+
+ /* Get the index file name and store it in the class */
+ SetFileName(IxName);
+
+ /* check if the file already exists */
+ if(((indexfp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ){
+ fclose( indexfp );
+ return XB_FILE_EXISTS;
+ }
+ else if( indexfp ) fclose( indexfp );
+
+ if(( indexfp = fopen( GetFileName(), "w+b" )) == NULL ){
+ return XB_OPEN_ERROR;
+ }
+
+#ifdef XB_LOCKING_ON
+ /*
+ ** Must turn off buffering when multiple programs may be accessing
+ ** index files.
+ */
+ setbuf( indexfp, NULL );
+#endif
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// if((rc = LockIndex( XB_LOCK )) != 0)
+// return rc;
+#endif
+
+ /* parse the expression */
+ IxExp = new xbExpn( dbf->xbase );
+ if(( rc = IxExp->BuildExpressionTree( Exp, strlen( Exp ), dbf )) != XB_NO_ERROR )
+ return rc;
+
+// ExpressionTree = dbf->xbase->GetTree();
+// dbf->xbase->SetTreeToNull();
+
+ /* build the header record */
+ memset( &HeadNode, 0x00, sizeof( NtxHeadNode ));
+ HeadNode.Signature = 0x6; // Clipper 5.x
+ HeadNode.Version = 1;
+ HeadNode.StartNode = 1024L;
+ KeyLen = CalcKeyLen();
+
+ // TODO
+ // What is the Clipper key length limit?
+ if( KeyLen == 0 || KeyLen > 100 ){ /* 100 byte key length limit */
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return XB_INVALID_KEY;
+ } else {
+ HeadNode.KeyLen = KeyLen;
+ }
+
+ // This is not the algorithm that Clipper uses. I cant figure out
+ // what they use from looking at the examples.
+ // This is correct tho.
+ HeadNode.KeysPerNode = (xbUShort)
+ (( XB_NTX_NODE_SIZE - (2 * sizeof( xbUShort ))) / (HeadNode.KeyLen + 10 )) - 1;
+ if( HeadNode.KeysPerNode % 2 )
+ HeadNode.KeysPerNode--;
+
+ HeadNode.HalfKeysPerNode = (xbUShort) HeadNode.KeysPerNode / 2;
+ HeadNode.KeySize = HeadNode.KeyLen + 8;
+// while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/
+ HeadNode.Unique = Unique;
+ strncpy( HeadNode.KeyExpression, Exp, 255 );
+
+ rc=AllocKeyBufs();
+ if(rc) {
+ fclose(indexfp);
+ return rc;
+ };
+
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 0 )) != 0 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+ /* write node #1 all 0x00 */
+ for( i = 0; i < XB_NTX_NODE_SIZE; i++ ){
+ if(( fwrite( "\x00", 1, 1, indexfp )) != 1 ){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ fclose( indexfp );
+ return XB_WRITE_ERROR;
+ }
+ }
+
+ if((rc = GetLeafNode(HeadNode.StartNode, 1)) != 0){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+ for( i = 0; i < HeadNode.KeysPerNode + 1; i++ )
+ CurNode->offsets[i] = (i * HeadNode.KeySize) +
+ 2 + (2 * (HeadNode.KeysPerNode + 1));
+
+ if((rc = PutLeafNode(HeadNode.StartNode, CurNode )) != 0){
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return rc;
+ }
+
+#ifdef XB_LOCKING_ON
+// if( dbf->GetAutoLock() )
+// LockIndex( XB_UNLOCK );
+#endif
+ return dbf->AddIndexToIxList( index, GetFileName());
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+ \param NodeNo
+*/
+xbShort xbNtx::PutLeftNodeNo( xbShort RecNo, xbNodeLink *n, xbLong NodeNo )
+{
+ /* This routine sets n node's leftnode number */
+ NtxLeafNode *temp;
+ char *p;
+ xbUShort itemOffset;
+ if( !n ) return XB_INVALID_NODELINK;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > HeadNode.KeysPerNode)
+ return XB_INVALID_KEY;
+ p = temp->KeyRecs;
+ itemOffset = GetItemOffset(RecNo, n, 1);
+ p += itemOffset;
+ dbf->xbase->PutLong( p, NodeNo );
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+ \param DbfNo
+*/
+xbShort xbNtx::PutDbfNo( xbShort RecNo, xbNodeLink *n, xbLong DbfNo )
+{
+ /* This routine sets n node's dbf number */
+
+ NtxLeafNode *temp;
+ char *p;
+ xbUShort itemOffset;
+ if( !n ) return XB_INVALID_NODELINK;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode))
+ return XB_INVALID_KEY;
+ itemOffset = GetItemOffset(RecNo, n, 1);
+ p = temp->KeyRecs;
+ p += itemOffset;
+ p += 4;
+ dbf->xbase->PutLong( p, DbfNo );
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param l
+ \param n
+*/
+xbShort xbNtx::PutLeafNode( xbLong l, xbNodeLink *n )
+{
+ NtxLeafNode *temp;
+ char *p;
+
+ if(( _fseek( indexfp, (xbOffT)l , SEEK_SET )) != 0 ){
+ fclose( indexfp );
+ return XB_SEEK_ERROR;
+ }
+
+ temp = &n->Leaf;
+ p = temp->KeyRecs;
+ dbf->xbase->PutShort( p, temp->NoOfKeysThisNode );
+
+ // The offsets at the head of each leaf are not necessarly in order.
+ p += 2;
+ for( int i = 0; i < HeadNode.KeysPerNode + 1; i++){
+ dbf->xbase->PutShort( p, n->offsets[i] );
+ p += 2;
+ }
+
+ if(( fwrite( &n->Leaf.KeyRecs, XB_NTX_NODE_SIZE, 1, indexfp )) != 1 ){
+ fclose( indexfp );
+ return XB_WRITE_ERROR;
+ }
+
+ PutHeadNode(&HeadNode, indexfp, 1);
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param Head
+ \param f
+ \param UpdateOnly
+*/
+xbShort xbNtx::PutHeadNode( NtxHeadNode * Head, FILE * f, xbShort UpdateOnly )
+{
+ char buf[4];
+ char *p;
+
+ if(( _fseek( f, 0L, SEEK_SET )) != 0 ){
+ fclose( f );
+ return XB_SEEK_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->Signature );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->Version );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutULong( buf, Head->StartNode );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 4 );
+ dbf->xbase->PutULong( buf, Head->UnusedOffset );
+ if(( fwrite( &buf, 4, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ if( UpdateOnly ){
+ fflush(indexfp);
+ return XB_NO_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->KeySize );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->KeyLen );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->DecimalCount );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->KeysPerNode );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 2 );
+ dbf->xbase->PutUShort( buf, Head->HalfKeysPerNode );
+ if(( fwrite( &buf, 2, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ p = HeadNode.KeyExpression;
+ while(*p){
+ *p = tolower(*p);
+ p++;
+ }
+
+ if(( fwrite( &Head->KeyExpression, 256, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ memset( buf, 0x00, 1 );
+ buf[0] = Head->Unique;
+ if(( fwrite( &buf, 1, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+
+ if(( fwrite( &Head->NotUsed, 745, 1, f )) != 1 ){
+ fclose( f );
+ return XB_WRITE_ERROR;
+ }
+ return 0;
+}
+
+xbShort xbNtx::TouchIndex( void )
+{
+ xbShort rc;
+ if (( rc = GetHeadNode()) != XB_NO_ERROR) return rc;
+ HeadNode.Version++;
+ if (( rc = PutHeadNode(&HeadNode, indexfp, 1)) != XB_NO_ERROR) return rc;
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param RecNo
+ \param n
+*/
+xbShort xbNtx::PutKeyData( xbShort RecNo, xbNodeLink *n )
+{
+ /* This routine copies the KeyBuf data into xbNodeLink n */
+ NtxLeafNode *temp;
+ char *p;
+ xbShort i;
+ xbUShort itemOffset;
+ if( !n ) return XB_INVALID_NODELINK;
+ temp = &n->Leaf;
+ if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode))
+ return XB_INVALID_KEY;
+ itemOffset = GetItemOffset(RecNo, n, 1);
+ p = temp->KeyRecs;
+ p += itemOffset;
+ p += 8;
+ for( i = 0; i < HeadNode.KeyLen; i++ ){
+ *p = KeyBuf[i];
+ p++;
+ }
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param n
+ \param pos
+ \param d
+ \param l
+ \param w
+*/
+xbShort xbNtx::PutKeyInNode( xbNodeLink * n, xbShort pos, xbLong d, xbLong l, xbShort w )
+{
+ /* check the node */
+ if( !n ) return XB_INVALID_NODELINK;
+ if( pos < 0 || pos > HeadNode.KeysPerNode ) return XB_INVALID_RECORD;
+ if( n->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode ) return XB_NODE_FULL;
+
+ InsertKeyOffset(pos, n);
+ PutKeyData( pos, n );
+ PutDbfNo( pos, n, d );
+ PutLeftNodeNo( pos, n, l );
+ n->Leaf.NoOfKeysThisNode++;
+ if( w )
+ return PutLeafNode( n->NodeNo, n );
+ else
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param n1
+ \param n2
+ \param pos
+ \param d
+*/
+xbShort xbNtx::SplitLeafNode( xbNodeLink *n1, xbNodeLink *n2, xbShort pos, xbLong d )
+{
+ xbShort i,j,rc;
+ xbShort temp;
+ xbShort start;
+ xbShort end;
+// xbShort length;
+
+ if( !n1 || !n2 ) return XB_INVALID_NODELINK;
+ if( pos < 0 || pos > HeadNode.KeysPerNode ) return XB_INVALID_RECORD;
+
+// length = strlen(KeyBuf);
+
+ // If the new key goes in the first node.
+ if( pos < HeadNode.HalfKeysPerNode ){
+ // Setup key to insert into parent
+ memcpy(PushItem.Key,
+ GetKeyData(HeadNode.HalfKeysPerNode -1, n1), HeadNode.KeyLen);
+ PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode -1, n1);
+ PushItem.Node = 0L;
+ start = pos;
+ end = HeadNode.HalfKeysPerNode - 1;
+ temp = n1->offsets[end];
+ for( i = end; i > start; i--)
+ n1->offsets[i] = n1->offsets[i-1];
+
+ n1->offsets[start] = temp;
+ // Insert new key
+ PutKeyData( start , n1 );
+ PutDbfNo ( start , n1, d );
+ } else {
+ // If the passed-in key IS median key, just copy it.
+ if( pos == HeadNode.HalfKeysPerNode ){
+ memcpy(PushItem.Key, KeyBuf, HeadNode.KeyLen);
+ PushItem.RecordNumber = d;
+ start = pos;
+ end = pos;
+ } else {
+ // Otherwise, the median key will be middle key because the
+ // new key will be inserted somewhere above the middle.
+ memcpy( PushItem.Key,
+ GetKeyData(HeadNode.HalfKeysPerNode, n1),
+ HeadNode.KeyLen);
+ PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode, n1);
+ start = HeadNode.HalfKeysPerNode ;
+ end = pos -1;
+ }
+ temp = n1->offsets[start];
+ for( i = start; i < end; i++)
+ n1->offsets[i] = n1->offsets[i+1];
+
+ n1->offsets[end] = temp;
+
+ // Insert new key
+ PutKeyData( pos -1 , n1 );
+ PutDbfNo ( pos -1 , n1, d );
+ }
+
+ // Dup the node data
+ memcpy(n2->Leaf.KeyRecs, n1->Leaf.KeyRecs, XB_NTX_NODE_SIZE);
+
+ // Dup the offsets
+ for( i = 0; i < HeadNode.KeysPerNode +1; i++)
+ n2->offsets[i] = n1->offsets[i];
+
+ // Setup the second node
+ for(j=0, i=HeadNode.HalfKeysPerNode; i < HeadNode.KeysPerNode; i++, j++ ){
+ temp = n2->offsets[j];
+ n2->offsets[j] = n2->offsets[i];
+ n2->offsets[i] = temp;
+ }
+
+ // Get the last offset for both nodes
+ temp = n2->offsets[j];
+ n2->offsets[j] = n2->offsets[HeadNode.KeysPerNode];
+ n2->offsets[HeadNode.KeysPerNode] = temp;
+
+ // Set the new count of both nodes
+ n2->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
+ n1->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
+
+ if(( rc = PutLeafNode( n1->NodeNo, n1 )) != 0 )
+ return rc;
+ if(( rc = PutLeafNode( n2->NodeNo, n2 )) != 0 )
+ return rc;
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param n1
+ \param n2
+ \param
+*/
+xbShort xbNtx::SplitINode( xbNodeLink *n1, xbNodeLink *n2, xbLong )
+ /* parent, tempnode, tempnodeno */
+{
+ xbShort i,j,rc;
+ xbShort temp;
+ xbShort pos = n1->CurKeyNo;
+ xbShort start;
+ xbShort end;
+ xbLong n1LastNodeNo = 0;
+
+ NtxItem oldPushItem;
+ oldPushItem.Node = PushItem.Node;
+ oldPushItem.RecordNumber = PushItem.RecordNumber;
+ memcpy(oldPushItem.Key, PushItem.Key, sizeof(PushItem.Key));
+
+ // n2->NodeNo = HeadNode.TotalNodes++;
+ n2->NodeNo = GetNextNodeNo();
+
+ // If the new key goes in the first node.
+ if( pos < HeadNode.HalfKeysPerNode ){
+ // Setup key to insert into parent
+ memcpy(PushItem.Key,
+ GetKeyData(HeadNode.HalfKeysPerNode -1, n1), HeadNode.KeyLen);
+ PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode -1, n1);
+ PushItem.Node = n2->NodeNo;
+ n1LastNodeNo = GetLeftNodeNo(HeadNode.HalfKeysPerNode -1, n1);
+ start = pos;
+ end = HeadNode.HalfKeysPerNode - 1;
+
+ // Insert the new key.
+ temp = n1->offsets[end];
+ for( i = end; i > start; i--)
+ n1->offsets[i] = n1->offsets[i-1];
+ n1->offsets[start] = temp;
+ } else {
+ // If the passed-in key IS median key, just copy it.
+ if( pos == HeadNode.HalfKeysPerNode ){
+ PutLeftNodeNo(0, n2, oldPushItem.Node);
+ // PushItem should remain the same, except for its left pointer
+ PushItem.Node = n2->NodeNo;
+// start = pos; unused code
+// end = pos; unused code
+ } else {
+ // Otherwise, the median key will be middle key becasue the
+ // new key will be inserted somewhere above the middle.
+ memcpy( PushItem.Key,
+ GetKeyData(HeadNode.HalfKeysPerNode, n1),
+ HeadNode.KeyLen);
+ PushItem.RecordNumber = GetDbfNo(HeadNode.HalfKeysPerNode, n1);
+ PushItem.Node = n2->NodeNo;
+ n1LastNodeNo = GetLeftNodeNo(HeadNode.HalfKeysPerNode, n1);
+
+// start = HeadNode.HalfKeysPerNode + 1;
+ start = HeadNode.HalfKeysPerNode;
+ end = pos -1;
+
+ // Insert the new key.
+ temp = n1->offsets[start];
+ for( i = start; i < end; i++)
+ n1->offsets[i] = n1->offsets[i+1];
+ n1->offsets[end] = temp;
+ pos--;
+ }
+ }
+
+ /* restore original key */
+ memcpy( KeyBuf, oldPushItem.Key, HeadNode.KeyLen + 1);
+
+ // Insert new key
+ PutKeyData( pos, n1 );
+ PutDbfNo ( pos, n1, oldPushItem.RecordNumber);
+ PutLeftNodeNo( pos, n1, GetLeftNodeNo (pos + 1, n1));
+ PutLeftNodeNo( pos + 1 /* +1 ?*/, n1, oldPushItem.Node /* t */ );
+
+ // Dup the node data into the new page
+ memcpy(n2->Leaf.KeyRecs, n1->Leaf.KeyRecs, XB_NTX_NODE_SIZE);
+
+ // Dup the offsets
+ for( i = 0; i < HeadNode.KeysPerNode +1; i++){
+ n2->offsets[i] = n1->offsets[i];
+ }
+
+ // Setup the second node
+ for( j = 0, i = HeadNode.HalfKeysPerNode; i<HeadNode.KeysPerNode; i++,j++ ){
+ temp = n2->offsets[j];
+ n2->offsets[j] = n2->offsets[i];
+ n2->offsets[i] = temp;
+ }
+
+ // Get the last offset for both nodes
+ temp = n2->offsets[j];
+ n2->offsets[j] = n2->offsets[HeadNode.KeysPerNode];
+ n2->offsets[HeadNode.KeysPerNode] = temp;
+ PutLeftNodeNo(HeadNode.HalfKeysPerNode, n1, n1LastNodeNo);
+
+ // Set the new count of both nodes
+ n2->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
+ n1->Leaf.NoOfKeysThisNode = HeadNode.HalfKeysPerNode;
+
+ if((rc = PutLeafNode( n1->NodeNo,n1 )) != 0) return rc;
+ if((rc = PutLeafNode( n2->NodeNo,n2 )) != 0) return rc;
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param RecBufSw
+ \param KeyBufSw
+*/
+xbShort xbNtx::CreateKey( xbShort RecBufSw, xbShort KeyBufSw )
+{
+ /* RecBufSw 0 Use RecBuf */
+ /* 1 Use RecBuf2 */
+ /* KeyBufSw 0 Use KeyBuf */
+ /* 1 Use KeyBuf2 */
+
+ xbShort rc;
+ xbExpNode * TempNode;
+
+ if(( rc = IxExp->ProcessExpression( RecBufSw )) != XB_NO_ERROR )
+ return rc;
+ TempNode = (xbExpNode *) IxExp->Pop();
+ if( !TempNode ) return XB_INVALID_KEY;
+
+ if( KeyBufSw ){
+ memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 );
+ memcpy( KeyBuf2, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
+ } else {
+ memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 );
+ memcpy( KeyBuf, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) );
+ }
+// if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode );
+ if( !TempNode->InTree ) delete TempNode;
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param key
+*/
+xbShort
+xbNtx::GetCurrentKey(char *key)
+{
+ CreateKey(0, 0);
+ memcpy(key, KeyBuf, HeadNode.KeyLen + 1);
+ return 0;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param DbfRec
+*/
+xbShort xbNtx::AddKey( xbLong DbfRec )
+{
+ /* This routine assumes KeyBuf contains the contents of the index to key */
+
+ xbShort i,rc;
+ xbNodeLink * TempNode;
+ xbNodeLink * Tparent;
+ xbLong TempNodeNo; /* new, unattached leaf node no */
+
+ /* find node key belongs in */
+ rc = FindKey( KeyBuf, HeadNode.KeyLen, 0 );
+ if( rc == XB_FOUND && HeadNode.Unique )
+ return XB_KEY_NOT_UNIQUE;
+
+ /************************************************/
+ /* section A - if room in node, add key to node */
+ /************************************************/
+
+ if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){
+ if(( rc = PutKeyInNode( CurNode,CurNode->CurKeyNo,DbfRec,0L,1)) != 0)
+ return rc;
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0)
+ return rc;
+ return XB_NO_ERROR;
+ }
+
+ /***********************************************************************/
+ /* section B - split leaf node if full and put key in correct position */
+ /***********************************************************************/
+
+ TempNode = GetNodeMemory();
+ // Create a new page
+ TempNode->NodeNo = GetNextNodeNo();
+
+ rc = SplitLeafNode( CurNode, TempNode, CurNode->CurKeyNo, DbfRec );
+ if( rc ) return rc;
+
+ /* TempNode is on disk, now we have to point someone above to
+ that node. Keep the NodeNo of the on disk new node.
+ */
+ TempNodeNo = TempNode->NodeNo;
+ ReleaseNodeMemory( TempNode );
+
+ /*
+ PushItem also contains the key to put into the parent
+ PushItem should point at TempNode
+ */
+ PushItem.Node = TempNodeNo;
+
+ /*****************************************************/
+ /* section C go up tree splitting nodes as necessary */
+ /*****************************************************/
+
+ Tparent = CurNode->PrevNode;
+
+ while( Tparent && Tparent->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode ){
+ TempNode = GetNodeMemory();
+ if( !TempNode )
+ return XB_NO_MEMORY;
+
+ rc = SplitINode( Tparent, TempNode, TempNodeNo );
+ if( rc ) return rc;
+ TempNodeNo = TempNode->NodeNo;
+ ReleaseNodeMemory( TempNode );
+ ReleaseNodeMemory( CurNode );
+ CurNode = Tparent;
+ CurNode->NextNode = NULL;
+ Tparent = CurNode->PrevNode;
+ }
+
+ /************************************************************/
+ /* Section D if CurNode is split root, create new root */
+ /************************************************************/
+
+ /* at this point
+ CurNode = The node that was just split
+ TempNodeNo = The new node split off from CurNode */
+
+ if(CurNode->NodeNo == HeadNode.StartNode ){
+ TempNode = GetNodeMemory();
+ if( !TempNode )
+ return XB_NO_MEMORY;
+
+ memcpy( KeyBuf, PushItem.Key, HeadNode.KeyLen );
+ PutKeyData( 0, TempNode );
+ PutDbfNo ( 0, TempNode, PushItem.RecordNumber );
+ PutLeftNodeNo( 0, TempNode, CurNode->NodeNo );
+ PutLeftNodeNo( 1, TempNode, PushItem.Node );
+ TempNode->NodeNo = GetNextNodeNo();
+ TempNode->Leaf.NoOfKeysThisNode++;
+ HeadNode.StartNode = TempNode->NodeNo;
+ rc = PutLeafNode( TempNode->NodeNo, TempNode );
+ if( rc ) return rc;
+ rc = PutHeadNode( &HeadNode, indexfp, 1 );
+ if( rc ) return rc;
+ ReleaseNodeMemory( TempNode );
+ return XB_NO_ERROR;
+ }
+ /**********************************/
+ /* Section E make room in parent */
+ /**********************************/
+ InsertKeyOffset(Tparent->CurKeyNo, Tparent);
+
+ /* put key in parent */
+
+ i = Tparent->CurKeyNo;
+ memcpy( KeyBuf, PushItem.Key, HeadNode.KeyLen);
+ PutKeyData( i, Tparent );
+
+ PutDbfNo( i, Tparent, PushItem.RecordNumber);
+ PutLeftNodeNo( i , Tparent, CurNode->NodeNo );
+ PutLeftNodeNo( i + 1 , Tparent, TempNodeNo );
+ Tparent->Leaf.NoOfKeysThisNode++;
+
+ rc = PutLeafNode( Tparent->NodeNo, Tparent );
+ if( rc ) return rc;
+ rc = PutHeadNode( &HeadNode, indexfp, 1 );
+ if( rc ) return rc;
+
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param n
+*/
+xbShort xbNtx::UpdateParentKey( xbNodeLink * n )
+{
+/* this routine goes backwards thru the node chain looking for a parent
+ node to update */
+
+ xbNodeLink * TempNode;
+
+ if( !n ) return XB_INVALID_NODELINK;
+ if( !GetDbfNo( 0, n )){
+ std::cout << "Fatal index error - Not a leaf node" << n->NodeNo << std::endl;
+// exit(0);
+ return XB_NOT_LEAFNODE;
+ }
+ TempNode = n->PrevNode;
+
+ while( TempNode ){
+ if( TempNode->CurKeyNo < TempNode->Leaf.NoOfKeysThisNode ){
+ memcpy(KeyBuf,GetKeyData(n->Leaf.NoOfKeysThisNode-1,n),HeadNode.KeyLen);
+ PutKeyData( TempNode->CurKeyNo, TempNode );
+ return PutLeafNode( TempNode->NodeNo, TempNode );
+ }
+ TempNode = TempNode->PrevNode;
+ }
+ return XB_NO_ERROR;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param n
+*/
+/* This routine queues up a list of nodes which have been emptied */
+void xbNtx::UpdateDeleteList( xbNodeLink *n )
+{
+ n->NextNode = DeleteChain;
+ DeleteChain = n;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+/* Delete nodes from the node list - for now we leave the empty nodes */
+/* dangling in the file. Eventually we will remove nodes from the file */
+
+void xbNtx::ProcessDeleteList( void )
+{
+ if( DeleteChain ){
+ ReleaseNodeMemory( DeleteChain );
+ DeleteChain = NULL;
+ }
+}
+/***********************************************************************/
+//! Short description.
+/*!
+*/
+xbShort xbNtx::KeyWasChanged( void )
+{
+ CreateKey( 0, 0 ); /* use KeyBuf, RecBuf */
+ CreateKey( 1, 1 ); /* use KeyBuf2, RecBuf2 */
+ if( CompareKey( KeyBuf, KeyBuf2, HeadNode.KeyLen ) != 0 )
+ return 1;
+ else
+ return 0;
+}
+/***********************************************************************/
+//! Short description.
+/*!
+ \param DbfRec
+*/
+xbShort xbNtx::DeleteKey( xbLong DbfRec )
+{
+/* this routine assumes the key to be deleted is in KeyBuf */
+
+ xbShort rc;
+
+ // FindKey will set CurNodeNo on evey page down to the
+ // key being deleted. This is important. Plus we
+ // need to be able to find the key to delete it.
+ CurNode = NULL;
+ if(( rc = FindKey( KeyBuf, DbfRec )) != XB_FOUND )
+ return rc;
+
+ // Then delete it
+ // next sentence modified 8/20/03 - gkunkel
+ if(( rc = DeleteKeyFromNode( CurNode->CurKeyNo, CurNode )) != XB_NO_ERROR )
+ return rc;
+
+ CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode );
+ if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0 )
+ return rc;
+ return XB_NO_ERROR;
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbShort
+xbNtx::DeleteKeyFromNode(xbShort pos, xbNodeLink *n )
+{
+ xbNodeLink *TempNode;
+ xbShort rc;
+
+ // Check to see if this is an inode
+ if( GetLeftNodeNo( 0 , n ) != 0 ){
+ // Copy the rightmost key from the left node.
+ TempNode = n;
+ GetLeafNode ( GetLeftNodeNo (n->CurKeyNo, n), 1);
+ while(( rc = GetLeftNodeNo( 0, CurNode )) != 0 )
+ GetLeafNode ( GetLeftNodeNo (CurNode->Leaf.NoOfKeysThisNode, CurNode), 1);
+
+ // Get the key Data
+ strcpy (KeyBuf , GetKeyData( CurNode->Leaf.NoOfKeysThisNode -1, CurNode));
+ PutKeyData( pos, TempNode );
+
+ // Get the xbDbf no
+ PutDbfNo (pos, TempNode, GetDbfNo( CurNode->Leaf.NoOfKeysThisNode -1, CurNode) );
+
+ // We don't change the LeftNodeNo. determined later
+ // Write the changed node
+ PutLeafNode( TempNode->NodeNo, TempNode );
+
+ // Now delete the key from the child
+ TempNode = CurNode;
+
+ if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
+
+ return DeleteKeyFromNode( TempNode->Leaf.NoOfKeysThisNode -1, TempNode);
+ } else {
+ return RemoveKeyFromNode(pos, n);
+ }
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbShort xbNtx::RemoveKeyFromNode( xbShort pos, xbNodeLink *n )
+{
+ xbNodeLink *TempNode;
+ xbNodeLink *sibling;
+ xbNodeLink *parent;
+ xbShort rc;
+ xbLong newHeadNode = 0;
+ xbBool harvest = false;
+
+ // Here we are a leaf node..
+
+ if( n->NodeNo == HeadNode.StartNode && n->Leaf.NoOfKeysThisNode == 1)
+ // we are about to delete the last node from the head node.
+ newHeadNode = GetLeftNodeNo( 0 , n );
+
+ // Remove the key from the current node.
+ DeleteKeyOffset(pos, n);
+ n->Leaf.NoOfKeysThisNode--;
+
+ // Check to see if the number of keys left is less then
+ // 1/2 KeysPerNode
+ if( ! ( n->NodeNo == HeadNode.StartNode )
+ && n->Leaf.NoOfKeysThisNode < HeadNode.HalfKeysPerNode){
+ // This observed clipper behavior.
+ // If less then 1/2 keys per node, then merge with right sibling.
+ // If no right sibling, merge with left sibling.
+ parent = n->PrevNode;
+
+ // If the parents cur key is the last key, then take the left node
+ if( parent->CurKeyNo == parent->Leaf.NoOfKeysThisNode ){
+ TempNode = CurNode;
+ GetLeafNode( GetLeftNodeNo(parent->CurKeyNo -1, parent), 2 );
+ sibling = CurNode;
+ CurNode = TempNode;
+
+ rc = JoinSiblings(parent, parent->CurKeyNo -1, sibling, n);
+
+ // Harvest the empty node, if necessary Clipper keeps the old key
+ // count on the node, to we can't set it to 0
+ if( rc == XB_HARVEST_NODE )
+ harvest = true;
+
+ if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
+ if((rc = PutLeafNode( sibling->NodeNo,sibling )) != 0) return rc;
+ if((rc = PutLeafNode( parent->NodeNo,parent )) != 0) return rc;
+
+ if(harvest){
+ HeadNode.UnusedOffset = n->NodeNo;
+ // Save the empty xbNodeLink
+ // ReleaseNodeMemory(n);
+ // We may have to delete a node from the parent
+ return RemoveKeyFromNode( parent->CurKeyNo, parent);
+ }
+ } else {
+ // Take the right node
+ TempNode = CurNode;
+ GetLeafNode( GetLeftNodeNo(parent->CurKeyNo + 1, parent), 2 );
+ sibling = CurNode;
+ CurNode = TempNode;
+
+ rc = JoinSiblings(parent, parent->CurKeyNo, n, sibling);
+ // Harvest the empty node, if necessary Clipper keeps the old key
+ // count on the node, to we can't set it to 0
+ if( rc == XB_HARVEST_NODE )
+ harvest = true;
+
+ if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
+ if((rc = PutLeafNode( sibling->NodeNo,sibling )) != 0) return rc;
+ if((rc = PutLeafNode( parent->NodeNo,parent )) != 0) return rc;
+
+ if( harvest ){
+ HeadNode.UnusedOffset = sibling->NodeNo;
+ // Save the empty xbNodeLink
+ ReleaseNodeMemory( sibling );
+
+ // Now the parents->CurKeyNo+1 left pointer is empty, and
+ // we are about to delete the parent. So move the left node no
+ // from the parents->CurKeyNo+1 to the parent->CurNodeNo
+ PutLeftNodeNo( parent->CurKeyNo +1 , parent,
+ GetLeftNodeNo( parent->CurKeyNo, parent ));
+ // We may have to delete a node from the parent
+ return RemoveKeyFromNode( parent->CurKeyNo, parent);
+ }
+ }
+ } else {
+ if( n->NodeNo == HeadNode.StartNode && n->Leaf.NoOfKeysThisNode == 0 ){
+ // we are about to delete the last node from the head node.
+ HeadNode.UnusedOffset = HeadNode.StartNode;
+ HeadNode.StartNode = newHeadNode;
+ }
+
+ if((rc = PutLeafNode( n->NodeNo,n )) != 0) return rc;
+ // If more then 1/2 keys per node -> done.
+ return XB_NO_ERROR;
+ }
+ return XB_NO_ERROR;
+}
+
+//! Short description.
+/*!
+ \param parent
+ \param parentPos
+ \param n1
+ \param n2
+*/
+xbShort
+xbNtx::JoinSiblings(xbNodeLink *parent, xbShort parentPos, xbNodeLink *n1, xbNodeLink* n2)
+{
+ // ASSUMES: keys in n1 are less then keys in n2
+ //
+ // Here, the contents of n1 need to be merged with n2. If n1 + parent_key
+ // + n2 can all fit in n1, then leave n2 empty, and remove the key from the
+ // parent.
+ // Otherwise evenly distribute the keys from n1 and n2 over both, resetting
+ // the parent.
+
+ xbShort i, j;
+ int totalKeys;
+ int median;
+
+ // if n1 has exactly (it will never have less) 1/2 keys per node
+ // then put everything into n1.
+ if((n1->Leaf.NoOfKeysThisNode + n2->Leaf.NoOfKeysThisNode + 1) <= HeadNode.KeysPerNode){
+ int n1LastNodeNo = GetLeftNodeNo(n2->Leaf.NoOfKeysThisNode, n2);
+ // Bring down the parent
+ strcpy(KeyBuf, GetKeyData( parentPos, parent ));
+ PutKeyData( n1->Leaf.NoOfKeysThisNode , n1);
+ PutDbfNo ( n1->Leaf.NoOfKeysThisNode, n1, GetDbfNo( parentPos, parent ) );
+ n1->Leaf.NoOfKeysThisNode++;
+
+ // Copy over the rest of the keys
+ for(i = n1->Leaf.NoOfKeysThisNode, j = 0; j < n2->Leaf.NoOfKeysThisNode; i++, j++){
+ strcpy(KeyBuf, GetKeyData( j, n2 ));
+ PutKeyData( i, n1);
+ PutLeftNodeNo( i, n1, GetLeftNodeNo( j, n2) );
+ PutDbfNo ( i , n1, GetDbfNo( j, n2 ) );
+ }
+ n1->Leaf.NoOfKeysThisNode += j;
+ PutLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1, n1LastNodeNo);
+
+ // We need a way to signal that this node will be harvested.
+ // Clipper keeps the KeyCount on harvested nodes, it does NOT
+ // set them to 0.
+ return XB_HARVEST_NODE;
+ } else {
+ // Distribute the keys evenly. Of off by one, the extra
+ // goes to n1.
+
+ // If n1 contains the greater than keys, then at this point we
+ // know that n1 has more than 1/2MKPN. therefore we copy
+ // over untill we get to median. All the while removing
+ // keys from n2. Then
+
+ totalKeys = n1->Leaf.NoOfKeysThisNode + n2->Leaf.NoOfKeysThisNode + 1;
+ median = (int) totalKeys/2;
+
+ // If n1 has more keys then n2, then we need to copy the last keys
+ // of n1 to the beginning of n2.
+ // Leave HalfKeysPerNode+1 keys in n1, then the last key will
+ // be copied up to the parent.
+ if( n1->Leaf.NoOfKeysThisNode > HeadNode.HalfKeysPerNode ){
+ // Bring down the parent
+ InsertKeyOffset(0, n2);
+ strcpy(KeyBuf, GetKeyData( parentPos, parent ));
+ PutKeyData( 0 , n2);
+ PutDbfNo ( 0, n2, GetDbfNo( parentPos, parent ) );
+ n2->Leaf.NoOfKeysThisNode++;
+ PutLeftNodeNo(0, n2, GetLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1));
+ for( i = n1->Leaf.NoOfKeysThisNode -1; i > median; i-- ){
+ // Put the key in n2
+ InsertKeyOffset(0, n2);
+ strcpy(KeyBuf, GetKeyData( i, n1 ));
+ PutKeyData( 0, n2);
+ PutLeftNodeNo( 0, n2, GetLeftNodeNo( i, n1) );
+ PutDbfNo ( 0 , n2, GetDbfNo( i, n1 ) );
+
+ // Remove the key from the current node.
+ n1->Leaf.NoOfKeysThisNode--;
+ n2->Leaf.NoOfKeysThisNode++;
+ }
+ // Copy up the last key from n1, that will become the new parent key.
+ strcpy(KeyBuf, GetKeyData( n1->Leaf.NoOfKeysThisNode -1 , n1 ));
+ PutKeyData( parentPos, parent);
+ PutDbfNo ( parentPos , parent, GetDbfNo( n1->Leaf.NoOfKeysThisNode -1, n1) );
+ n1->Leaf.NoOfKeysThisNode--;
+ } else {
+ xbLong n1LastLeftNodeNo;
+ xbShort medianOffset = n2->Leaf.NoOfKeysThisNode - median -1;
+ // Bring down the parent
+ strcpy(KeyBuf, GetKeyData( parentPos, parent ));
+ PutKeyData( n1->Leaf.NoOfKeysThisNode , n1);
+ PutDbfNo ( n1->Leaf.NoOfKeysThisNode, n1, GetDbfNo( parentPos, parent ) );
+ n1->Leaf.NoOfKeysThisNode++;
+
+// 8/20/03 gkunkel n1LastLeftNodeNo = GetLeftNodeNo(medianOffset, n2);
+ PutLeftNodeNo( n1->Leaf.NoOfKeysThisNode, n1, GetLeftNodeNo(medianOffset, n2));
+
+ // Moving the median to the parent may have to occur
+ // before moving the other keys to n1. This we would have
+ // to calcualte the correct offset from the median
+ // Copy up the first key from n2 (the median),
+ // that will become the new parent key.
+ strcpy(KeyBuf, GetKeyData( medianOffset, n2 ));
+ PutKeyData( parentPos, parent);
+ PutDbfNo ( parentPos , parent, GetDbfNo(medianOffset, n2 ) );
+ n1LastLeftNodeNo = GetLeftNodeNo(medianOffset, n2);
+
+// Still investigating the -1 thing with clipper, If anyone has clues,
+// please let me know - bob@synxis.com
+// if ( n1->Leaf.NoOfKeysThisNode >= (median - 1))
+// {
+// // Clipper, don't know why
+// PutLeftNodeNo(0, n2 , -1 );
+// std::cout << "Clipper hack" << std::endl;
+// }
+
+ DeleteKeyOffset(medianOffset, n2);
+ n2->Leaf.NoOfKeysThisNode--;
+
+// xbShort clipperMessedUpIndex = n1->Leaf.NoOfKeysThisNode;
+ for( i = n1->Leaf.NoOfKeysThisNode, j = 0; j < medianOffset; i++, j++ ){
+ strcpy(KeyBuf, GetKeyData( 0, n2 ));
+ PutKeyData( i, n1);
+ PutLeftNodeNo( i, n1, GetLeftNodeNo( 0, n2) );
+ PutDbfNo ( i , n1, GetDbfNo( 0, n2 ) );
+
+// if( i == clipperMessedUpIndex){
+// // Clipper, don't know why
+// PutLeftNodeNo(0, n2 , -1 );
+// std::cout << "Clipper hack in loop i = " << i << std::endl;
+// }
+
+ // Remove the key from the current node.
+ DeleteKeyOffset(0, n2);
+ n2->Leaf.NoOfKeysThisNode--;
+ n1->Leaf.NoOfKeysThisNode++;
+ }
+ PutLeftNodeNo(n1->Leaf.NoOfKeysThisNode, n1, n1LastLeftNodeNo);
+
+ }
+ }
+ return XB_NO_ERROR;
+}
+/************************************************************************/
+//! Short description.
+/*!
+ \param option
+*/
+#ifdef XBASE_DEBUG
+xbShort xbNtx::CheckIndexIntegrity( const xbShort option )
+{
+ /* if option = 1, print out some stats */
+
+ xbShort rc;
+ xbLong ctr = 1L;
+
+ if ( option ) std::cout << "Checking NTX " << GetFileName() << std::endl;
+ rc = dbf->GetRecord( ctr );
+ while( ctr < dbf->NoOfRecords() ){
+ ctr++;
+ if( option ) std::cout << "Checking Record " << ctr << std::endl;
+ if( !dbf->RecordDeleted() ){
+ CreateKey( 0, 0 );
+ rc = FindKey( KeyBuf, dbf->GetCurRecNo());
+ if( rc != XB_FOUND ){
+ if( option ){
+ std::cout << "Record number " << dbf->GetCurRecNo()
+ << " Not Found" << std::endl;
+ std::cout << "Key = " << KeyBuf << std::endl;
+ }
+ return rc;
+ }
+ }
+ if(( rc = dbf->GetRecord( ctr )) != XB_NO_ERROR )
+ return rc;
+ }
+
+ if( option )
+ std::cout << "Exiting with rc = " << rc << std::endl;
+ return XB_NO_ERROR;
+}
+#endif
+/***********************************************************************/
+//! Short description.
+/*!
+ \param statusFunc
+*/
+xbShort xbNtx::ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems))
+{
+ /* this method assumes the index has been locked in exclusive mode */
+ xbLong l;
+ xbShort rc, i, saveAutoLock;
+ NtxHeadNode TempHead;
+ FILE *t, *temp;
+ xbString TempName;
+
+ memcpy( &TempHead, &HeadNode, sizeof( struct NtxHeadNode ));
+ TempHead.StartNode = 1024L;
+ rc = dbf->xbase->DirectoryExistsInName( GetFileName() );
+ if( rc ) {
+ TempName.assign(GetFileName(), 0, rc);
+ TempName += "TEMPFILE.NTX";
+ } else
+ TempName = "TEMPFILE.NTX";
+
+ if(( t = fopen( TempName, "w+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ if(( rc = PutHeadNode( &TempHead, t, 0 )) != 0 ){
+ fclose( t );
+ remove( TempName );
+ return rc;
+ }
+
+ for( i = 0; i < XB_NTX_NODE_SIZE; i++ ){
+ if(( fwrite( "\x00", 1, 1, t )) != 1 ){
+ fclose( t );
+ remove( TempName );
+ return XB_WRITE_ERROR;
+ }
+ }
+
+ temp = indexfp;
+ indexfp = t;
+
+ if(( rc = GetLeafNode(TempHead.StartNode, 1)) != 0 )
+ return rc;
+
+ for(i = 0; i < TempHead.KeysPerNode + 1; i++)
+ CurNode->offsets[i] = (i * HeadNode.KeySize) +
+ 2 + (2 * (HeadNode.KeysPerNode + 1));
+
+ HeadNode.StartNode = TempHead.StartNode;
+ if((rc = PutLeafNode(TempHead.StartNode, CurNode )) != 0)
+ return rc;
+
+ indexfp = temp;
+
+ if( fclose( indexfp ) != 0 )
+ return XB_CLOSE_ERROR;
+
+ if( fclose( t ) != 0 )
+ return XB_CLOSE_ERROR;
+
+ if( remove( GetFileName() ) != 0 )
+ return XB_CLOSE_ERROR;
+
+ if( rename( TempName, GetFileName()) != 0 )
+ return XB_WRITE_ERROR;
+
+ if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL )
+ return XB_OPEN_ERROR;
+
+ saveAutoLock = dbf->GetAutoLock();
+ dbf->AutoLockOff();
+
+ for( l = 1; l <= dbf->NoOfRecords(); l++ ){
+ if(statusFunc)
+ statusFunc(l, dbf->NoOfRecords());
+
+ if(( rc = dbf->GetRecord(l)) != XB_NO_ERROR )
+ return rc;
+
+ if(!dbf->GetRealDelete() || !dbf->RecordDeleted()){
+ /* Create the key */
+ CreateKey( 0, 0 );
+
+ /* add key to index */
+ if(( rc = AddKey( l )) != XB_NO_ERROR )
+ return rc;
+ }
+ }
+ if(saveAutoLock)
+ dbf->AutoLockOn();
+
+ return XB_NO_ERROR;
+}
+
+//! Short description.
+/*!
+*/
+xbLong
+xbNtx::GetNextNodeNo()
+{
+ struct stat FileStat;
+ int rc;
+ xbULong FileSize;
+
+ if( HeadNode.UnusedOffset != 0){
+ FileSize = HeadNode.UnusedOffset;
+ HeadNode.UnusedOffset = 0;
+ PutHeadNode(&HeadNode, indexfp, 1);
+ return FileSize;
+ }
+
+ rc = fstat(fileno(indexfp), &FileStat);
+ if( rc != 0 )
+ return 0;
+
+
+ FileSize = (xbULong)FileStat.st_size;
+ // File offset is zero based, so the file size will be the
+ // offset of the next page.
+ return FileSize;
+}
+
+//! Short description.
+/*!
+ \param buf
+ \param len
+*/
+void xbNtx::GetExpression(char *buf, int len)
+{
+ memcpy(buf, HeadNode.KeyExpression, len < 256 ? len : 256);
+}
+
+const char* xbNtx::GetExtWithDot(bool lower)
+{
+ return lower? ".ntx": ".NTX";
+}
+
+xbUShort xbNtx::GetKeyLen()
+{
+ return HeadNode.KeyLen;
+}
+
+const char* xbNtx::GetKeyExpression()
+{
+ return HeadNode.KeyExpression;
+}
+
+void xbNtx::FreeNodesMemory()
+{
+ ReleaseNodeMemory(NodeChain, true);
+ NodeChain = 0;
+// ReleaseNodeMemory(CloneChain, true);
+// CloneChain = 0;
+ ReleaseNodeMemory(FreeNodeChain, true);
+ FreeNodeChain = 0;
+ ReleaseNodeMemory(DeleteChain, true);
+ DeleteChain = 0;
+}
+
+#endif /* XB_INDEX_NTX */
diff --git a/xbase64/xbntx.h b/xbase64/xbntx.h
new file mode 100755
index 0000000..daa1aa7
--- /dev/null
+++ b/xbase64/xbntx.h
@@ -0,0 +1,213 @@
+/* xbntx.h
+
+ Xbase64 project source code
+
+ This file contains a header file for the xbNdx object, which is used
+ for handling xbNdx type indices.
+
+ Copyright (C) 1997,2003 Bob Cotton
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_NTX_H__
+#define __XB_NTX_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#include <xbase64/xbase64.h>
+#include <string.h>
+
+/*! \file xbntx.h
+*/
+
+#define XB_NTX_NODE_SIZE 1024
+
+//! xbNtxHeadNode struct
+/*!
+*/
+
+struct NtxHeadNode { /* ntx header on disk */
+ xbUShort Signature; /* Clipper 5.x or Clipper 87 */
+ xbUShort Version; /* Compiler Version */
+ /* Also turns out to be a last modified counter */
+ xbLong StartNode; /* Offset in file for first index */
+ xbULong UnusedOffset; /* First free page offset */
+ xbUShort KeySize; /* Size of items (KeyLen + 8) */
+ xbUShort KeyLen; /* Size of the Key */
+ xbUShort DecimalCount; /* Number of decimal positions */
+ xbUShort KeysPerNode; /* Max number of keys per page */
+ xbUShort HalfKeysPerNode; /* Min number of keys per page */
+ char KeyExpression[256]; /* Null terminated key expression */
+ unsigned Unique; /* Unique Flag */
+ char NotUsed[745];
+};
+
+//! xbNtxLeafNode struct
+/*!
+*/
+
+struct NtxLeafNode { /* ndx node on disk */
+ xbUShort NoOfKeysThisNode;
+ char KeyRecs[XB_NTX_NODE_SIZE];
+};
+
+
+//! xbNtxItem struct
+/*!
+*/
+
+struct NtxItem
+{
+ xbULong Node;
+ xbULong RecordNumber;
+ char Key[256];
+};
+
+//! xbNtxNodeLink struct
+/*!
+*/
+
+struct xbNodeLink { /* ndx node memory */
+ xbNodeLink * PrevNode;
+ xbNodeLink * NextNode;
+ xbUShort CurKeyNo; /* 0 - KeysPerNode-1 */
+ xbLong NodeNo;
+ struct NtxLeafNode Leaf;
+ xbUShort * offsets;
+};
+
+//! xbNtx class
+/*!
+*/
+
+class XBDLLEXPORT xbNtx : public xbIndex
+{
+protected:
+ NtxHeadNode HeadNode;
+ NtxLeafNode LeafNode;
+ xbLong NodeLinkCtr;
+ xbLong ReusedNodeLinks;
+ char Node[XB_NTX_NODE_SIZE];
+ xbNodeLink * NodeChain; /* pointer to node chain of index nodes */
+ xbNodeLink * FreeNodeChain; /* pointer to chain of free index nodes */
+ xbNodeLink * CurNode; /* pointer to current node */
+ xbNodeLink * DeleteChain; /* pointer to chain to delete */
+// xbNodeLink * CloneChain; /* pointer to node chain copy (add dup) */
+ NtxItem PushItem;
+
+/* private functions */
+ xbLong GetLeftNodeNo( xbShort, xbNodeLink * );
+ xbShort CompareKey( const char *, const char *, xbShort );
+ xbShort CompareKey( const char *, const char * );
+ xbLong GetDbfNo( xbShort, xbNodeLink * );
+ char * GetKeyData( xbShort, xbNodeLink * );
+ xbUShort GetItemOffset ( xbShort, xbNodeLink *, xbShort );
+ xbUShort InsertKeyOffset ( xbShort, xbNodeLink * );
+ xbUShort GetKeysPerNode();
+ virtual xbShort GetHeadNode();
+ xbShort GetLeafNode( xbLong, xbShort );
+ xbNodeLink * GetNodeMemory();
+ xbLong GetNextNodeNo();
+ void ReleaseNodeMemory(xbNodeLink *n, xbBool doFree = false);
+ xbULong GetLeafFromInteriorNode( const char *, xbShort );
+ xbShort CalcKeyLen();
+ xbShort PutKeyData( xbShort, xbNodeLink * );
+ xbShort PutLeftNodeNo( xbShort, xbNodeLink *, xbLong );
+ xbShort PutLeafNode( xbLong, xbNodeLink * );
+ xbShort PutHeadNode( NtxHeadNode *, FILE *, xbShort );
+ xbShort TouchIndex();
+ xbShort PutDbfNo( xbShort, xbNodeLink *, xbLong );
+ xbShort PutKeyInNode( xbNodeLink *, xbShort, xbLong, xbLong, xbShort );
+ xbShort SplitLeafNode( xbNodeLink *, xbNodeLink *, xbShort, xbLong );
+ xbShort SplitINode( xbNodeLink *, xbNodeLink *, xbLong );
+ xbShort AddToIxList();
+ xbShort RemoveFromIxList();
+ xbShort RemoveKeyFromNode( xbShort, xbNodeLink * );
+ xbShort DeleteKeyFromNode( xbShort, xbNodeLink * );
+ xbShort JoinSiblings(xbNodeLink *, xbShort, xbNodeLink *, xbNodeLink *);
+ xbUShort DeleteKeyOffset( xbShort, xbNodeLink *);
+ xbShort FindKey( const char *, xbShort, xbShort );
+ xbShort UpdateParentKey( xbNodeLink * );
+ xbShort GetFirstKey( xbShort );
+ xbShort GetNextKey( xbShort );
+ xbShort GetLastKey( xbLong, xbShort );
+ xbShort GetPrevKey( xbShort );
+ void UpdateDeleteList( xbNodeLink * );
+ void ProcessDeleteList();
+ xbShort FindKey( const char *, xbLong ); /* for a specific dbf no */
+
+public:
+ xbNtx();
+ xbNtx(xbDbf *);
+ virtual ~xbNtx();
+
+/* note to gak - don't uncomment next line - it causes seg faults */
+// ~NTX() { if( NtxStatus ) CloseIndex(); }
+
+ void DumpHdrNode ( xbShort Option );
+ void DumpNodeRec ( xbLong );
+ xbShort CreateIndex( const char *, const char *, xbShort, xbShort );
+ xbLong GetTotalNodes();
+ xbULong GetCurDbfRec() { return CurDbfRec; }
+ void DumpNodeChain();
+ xbShort CreateKey( xbShort, xbShort );
+ xbShort GetCurrentKey(char *key);
+ xbShort AddKey( xbLong );
+ xbShort UniqueIndex() { return HeadNode.Unique; }
+ xbShort DeleteKey( xbLong DbfRec );
+ xbShort KeyWasChanged();
+ xbShort FindKey( const char * );
+ xbShort FindKey();
+ xbShort FindKey( xbDouble );
+ xbShort GetNextKey() { return GetNextKey( 1 ); }
+ xbShort GetLastKey() { return GetLastKey( 0, 1 ); }
+ xbShort GetFirstKey() { return GetFirstKey( 1 ); }
+ xbShort GetPrevKey() { return GetPrevKey( 1 ); }
+ xbShort ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems) = 0) ;
+ xbShort KeyExists( char * Key ) { return FindKey( Key, strlen( Key ), 0 ); }
+ xbShort KeyExists( xbDouble );
+ virtual void GetExpression(char *buf, int len);
+#ifdef XBASE_DEBUG
+ xbShort CheckIndexIntegrity( xbShort Option );
+#endif
+
+ virtual const char* GetExtWithDot(bool lower);
+
+ protected:
+ virtual xbUShort GetKeyLen();
+ virtual const char* GetKeyExpression();
+ virtual void FreeNodesMemory();
+};
+#endif /* __XB_NTX_H__ */
diff --git a/xbase64/xbretcod.h b/xbase64/xbretcod.h
new file mode 100755
index 0000000..dd0748a
--- /dev/null
+++ b/xbase64/xbretcod.h
@@ -0,0 +1,99 @@
+/* xbretcod.h
+
+ Xbase64 project source code
+
+ This file contains a listing of all the Xbase return codes.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+/*! \file xbretcod.h
+*/
+
+#ifndef __XB_RETCODES_H__
+#define __XB_RETCODES_H__
+
+/***********************************************/
+/* Return Codes and Error Messages */
+
+#define XB_NO_ERROR 0
+#define XB_EOF -100
+#define XB_BOF -101
+#define XB_NO_MEMORY -102
+#define XB_FILE_EXISTS -103
+#define XB_OPEN_ERROR -104
+#define XB_WRITE_ERROR -105
+#define XB_UNKNOWN_FIELD_TYPE -106
+#define XB_ALREADY_OPEN -107
+#define XB_NOT_XBASE -108
+#define XB_INVALID_RECORD -109
+#define XB_INVALID_OPTION -110
+#define XB_NOT_OPEN -111
+#define XB_SEEK_ERROR -112
+#define XB_READ_ERROR -113
+#define XB_NOT_FOUND -114
+#define XB_FOUND -115
+#define XB_INVALID_KEY -116
+#define XB_INVALID_NODELINK -117
+#define XB_KEY_NOT_UNIQUE -118
+#define XB_INVALID_KEY_EXPRESSION -119
+#define XB_DBF_FILE_NOT_OPEN -120
+#define XB_INVALID_KEY_TYPE -121
+#define XB_INVALID_NODE_NO -122
+#define XB_NODE_FULL -123
+#define XB_INVALID_FIELDNO -124
+#define XB_INVALID_DATA -125
+#define XB_NOT_LEAFNODE -126
+#define XB_LOCK_FAILED -127
+#define XB_CLOSE_ERROR -128
+#define XB_INVALID_SCHEMA -129
+#define XB_INVALID_NAME -130
+#define XB_INVALID_BLOCK_SIZE -131
+#define XB_INVALID_BLOCK_NO -132
+#define XB_NOT_MEMO_FIELD -133
+#define XB_NO_MEMO_DATA -134
+#define XB_EXP_SYNTAX_ERROR -135
+#define XB_PARSE_ERROR -136
+#define XB_NO_DATA -137
+#define XB_UNKNOWN_TOKEN_TYPE -138
+#define XB_INVALID_FIELD -140
+#define XB_INSUFFICIENT_PARMS -141
+#define XB_TOO_MANY_PARMS -142
+#define XB_INVALID_FUNCTION -143
+#define XB_INVALID_FIELD_LEN -144
+#define XB_HARVEST_NODE -145
+#define XB_INVALID_DATE -146
+#define XB_INVALID_LOCK_OPTION -147
+#endif /* __XB_RETCODES_H__ */
+
diff --git a/xbase64/xbstring.cpp b/xbase64/xbstring.cpp
new file mode 100755
index 0000000..419014f
--- /dev/null
+++ b/xbase64/xbstring.cpp
@@ -0,0 +1,1041 @@
+/* xbstring.cpp
+
+ Xbase64 project source code
+
+ This file contains the xbString object methods
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifdef __GNU LesserG__
+ #pragma implementation "xbstring.h"
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <xbase64/xbase64.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include <xbase64/xbstring.h>
+//#include <xbase64/xbexcept.h>
+
+//#define free(x)
+
+/*! \file xbstring.cpp
+*/
+
+const char * xbString::NullString = "";
+
+//! Short description.
+/*!
+*/
+xbString::xbString() {
+ ctor(NULL);
+}
+
+//! Short description.
+/*!
+ \param size
+*/
+xbString::xbString(size_t size) {
+ data = (char *)calloc(1, size);
+ this->size = size;
+}
+
+//! Short description.
+/*!
+ \param c
+*/
+xbString::xbString(char c) {
+ ctor(NULL);
+ *this = c;
+}
+
+//! Short description.
+/*!
+ \param s
+*/
+xbString::xbString(const char *s) {
+ ctor(s);
+}
+
+//! Short description.
+/*!
+ \param s
+*/
+xbString::xbString(const xbString &s) {
+ ctor((const char *)s);
+}
+
+//! Short description.
+/*!
+ \param s
+ \param maxlen
+*/
+xbString::xbString(const char *s, size_t maxlen) {
+#if 0
+ size_t len = strlen(s);
+
+ if(len < maxlen)
+ maxlen = len;
+#endif
+
+ size = maxlen + 1;
+ data = (char *)calloc(1, size);
+ strncpy(data, s, maxlen);
+ data[maxlen] = 0;
+}
+
+//! Short description.
+/*!
+*/
+xbString::~xbString() {
+ if (data != NULL)
+ free(data);
+}
+
+//! Short description.
+/*!
+ \param s
+*/
+void xbString::ctor(const char *s) {
+ if (s == NULL) {
+ data = NULL;
+ size = 0;
+ return;
+ }
+
+ size = strlen(s) + 1;
+
+ data = (char *)calloc(1, size);
+ strcpy(data, s);
+}
+
+//! Short description.
+/*!
+ \param s
+ \param maxlen
+*/
+void xbString::ctor(const char *s, size_t maxlen) {
+
+ if (s == NULL) {
+ data = NULL;
+ size =0;
+ return;
+ }
+ size = maxlen + 1;
+ data = (char *)calloc(1, size);
+ strncpy(data, s, maxlen);
+ data[maxlen] = 0;
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator=(char c) {
+ if (data != NULL)
+ free(data);
+
+ data = (char *)calloc(1, 2);
+ data[0] = c;
+ data[1] = 0;
+
+ size = 2;
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator=(const xbString &s) {
+ if (data != NULL)
+ free(data);
+
+ const char *sd = s;
+ if (sd == NULL) {
+ data = NULL;
+ size = 0;
+ return (*this);
+ }
+
+ data = (char *)calloc(1, strlen(s) + 1);
+ strcpy(data, s);
+
+ size = strlen(data)+1;
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator=(const char *s) {
+ if(data != NULL)
+ free(data);
+
+ if(s == NULL) {
+ data = NULL;
+ size = 0;
+ return (*this);
+ }
+ data = (char *)calloc(1, strlen(s) + 1);
+ strcpy(data, s);
+ size = strlen(data) + 1;
+ return (*this);
+}
+
+//! Short description.
+/*!
+ \param size
+*/
+void xbString::resize(size_t size) {
+ data = (char *)realloc(data, size);
+ if( size > 0 )
+ data[size-1] = 0;
+ this->size = size;
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::isNull() const {
+ return( data == NULL );
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::isEmpty() const {
+ if( data == NULL )
+ return true;
+ if( data[0] == 0 )
+ return true;
+ return false;
+}
+
+//! Short description.
+/*!
+*/
+size_t xbString::len() const {
+ return( data ? strlen(data) : 0 );
+}
+
+//! Short description.
+/*!
+*/
+size_t xbString::length() const {
+ return len();
+}
+
+//! Short description.
+/*!
+*/
+xbString xbString::copy() const {
+ return( *this );
+}
+
+//! Short description.
+/*!
+ \param format
+*/
+xbString &xbString::sprintf(const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ if (size < 256)
+ resize(256); // make string big enough
+
+#ifdef HAVE_VSNPRINTF
+ if (vsnprintf(data, size, format, ap) == -1)
+ data[size-1] = 0;
+#else
+# if HAVE_VSPRINTF
+ vsprintf(data, format, ap);
+# else
+# error "You have neither vsprintf nor vsnprintf!!!"
+# endif
+#endif
+
+ resize(strlen(data)+1); // truncate
+ va_end(ap);
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+xbString::operator const char *() const {
+ return (data != NULL) ? data : NullString;
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator-=(const char *s) {
+ if( s == NULL ) return (*this);
+ int len = strlen(s);
+ int oldlen = this->len();
+
+ data = (char *)realloc(data, oldlen+len+1);
+ if( oldlen == 0 ) data[0] = 0;
+
+ // looking for an occurence of space in the first string
+ char *lftspc = strchr(data,' ');
+ if( lftspc==NULL ) { // left string has no spaces
+ strcat(data,s);
+ } else { // left string has one or more spaces
+ int numspc = strlen(lftspc);
+ strcpy(lftspc,s);
+ while( numspc-- > 0 ) strcat(lftspc," ");
+ }
+
+ size += len;
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator+=(const char *s) {
+ if (s == NULL)
+ return (*this);
+ int len = strlen(s);
+ int oldlen = this->len();
+
+ data = (char *)realloc(data, oldlen+len+1);
+ if (oldlen == 0)
+ data[0] = 0;
+ strcat(data, s);
+
+ size += len;
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+xbString &xbString::operator+=(char c) {
+ int len = 1;
+ int oldlen = this->len();
+
+ data = (char *)realloc(data, oldlen+len+1);
+ data[oldlen] = c;
+ data[oldlen+1] = 0;
+
+ size++;
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+const char *xbString::getData() const {
+ return data ? data : NullString;
+}
+
+//! Short description.
+/*!
+*/
+const char *xbString::c_str() const {
+ return data ? data : NullString;
+}
+
+//! Short description.
+/*!
+*/
+void xbString::toLowerCase() {
+ int len = this->len();
+ for (int i=0;i<len;i++)
+ data[i] = (char)tolower(data[i]);
+}
+
+
+//! Short description.
+/*!
+*/
+void xbString::toUpperCase() {
+ int len = this->len();
+ for (int i=0;i<len;i++)
+ data[i] = (char)toupper(data[i]);
+}
+
+
+//! Short description.
+/*!
+ \param c
+*/
+int xbString::pos(char c) {
+ if (data == NULL)
+ return (-1);
+
+ const char *p = strchr(data, c);
+
+ if (p == NULL)
+ return (-1);
+
+ return p-data;
+}
+
+//! Short description.
+/*!
+ \param s
+*/
+int xbString::pos(const char* s) {
+ if (data == NULL)
+ return (-1);
+
+ const char *p = strstr(data, s);
+
+ if (p == NULL)
+ return (-1);
+
+ return p-data;
+}
+
+//! Short description.
+/*!
+ \param num
+*/
+void xbString::setNum(long num) {
+ sprintf("%ld", num);
+}
+
+//! Short description.
+/*!
+ \param fmt
+ \param num
+*/
+
+void xbString::setNum( char * fmt, double num) {
+ xbString f;
+ f = "%";
+ f += fmt;
+ f += "f";
+ sprintf( f.getData(), num);
+}
+
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbBool operator==(const xbString &s1, const char *s2) {
+ if (s2 == NULL) {
+ if (s1.getData() == NULL)
+ return true;
+ return false;
+ }
+
+ if ((s2[0] == 0) && s1.getData() == NULL)
+ return true;
+
+ if (s1.getData() == NULL)
+ return false;
+
+ return (strcmp(s1, s2) == 0);
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbBool operator!=(const xbString &s1, const char *s2) {
+ if (s2 == NULL) {
+ if (s1.getData() == NULL)
+ return false;
+ return true;
+ }
+
+ if ((s2[0] == 0) && s1.getData() == NULL)
+ return false;
+
+ if (s1.getData() == NULL)
+ return true;
+
+ return (strcmp(s1, s2) != 0);
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator==( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // NULL == NULL
+ return false; // NULL == !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // !NULL == NULL
+ return strcmp(data,s2.data) == 0; //!NULL == !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator!=( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // NULL != NULL
+ return true; // NULL != !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // !NULL != NULL
+ return strcmp(data,s2.data) != 0; //!NULL != !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator< ( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // NULL < NULL
+ return true; // NULL < !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // !NULL < NULL
+ return strcmp(data,s2.data) < 0; //!NULL < !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator> ( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // NULL > NULL
+ return false; // NULL > !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // !NULL > NULL
+ return strcmp(data,s2.data) > 0; //!NULL > !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator<=( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // NULL <= NULL
+ return true; // NULL <= !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return false; // !NULL <= NULL
+ return strcmp(data,s2.data) <= 0; //!NULL <= !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+xbBool xbString::operator>=( const xbString &s2 ) const {
+ if( data == NULL || data[0] == 0 ) {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // NULL >= NULL
+ return false; // NULL >= !NULL
+ } else {
+ if( s2.data == NULL || s2.data[0] == 0 ) return true; // !NULL >= NULL
+ return strcmp(data,s2.data) >= 0; //!NULL >= !NULL
+ }
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT std::ostream& operator<< ( std::ostream& os,
+ const xbString& xbs ) {
+ return os << xbs.data;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator-(const xbString &s1, const xbString &s2) {
+ xbString tmp(s1.getData());
+ tmp -= s2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator+(const xbString &s1, const xbString &s2) {
+ xbString tmp(s1.getData());
+ tmp += s2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator+(const xbString &s1, const char *s2) {
+ xbString tmp(s1.getData());
+ tmp += s2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator+(const char *s1, const xbString &s2) {
+ xbString tmp(s1);
+ tmp += s2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator+(const xbString &s1, char c2) {
+ xbString tmp(s1.getData());
+ tmp += c2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+*/
+XBDLLEXPORT xbString operator+(char c1, const xbString &s2) {
+ xbString tmp(c1);
+ tmp += s2;
+ return tmp;
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param c
+*/
+void xbString::putAt(size_t pos, char c) {
+ if (pos>len())
+ return;
+
+ data[pos] = c;
+}
+
+//! Short description.
+/*!
+ \param str
+ \param pos
+ \param n
+*/
+xbString& xbString::assign(const xbString& str, size_t pos, int n)
+{
+ if(data){
+ free(data);
+ data = 0;
+ }
+
+ if(str.len() <= pos){
+ size = 0;
+ return (*this);
+ }
+
+ if(str.len() < pos + n){
+ n = str.len() - pos;
+ }
+
+ const char *d = str;
+
+ if (n == -1){
+// data = (char *)malloc(str.len()-pos+1); ms win/nt bug fix
+ data = (char *)calloc(str.len()-pos+1, sizeof( char ));
+ strcpy(data, d+pos);
+ size = str.len()-pos+1;
+ }
+ else
+ {
+// data = (char *)malloc(n); ms win/nt bug fix
+// boundschecker flags the next line as a memory leak
+// but this is a valid memory allocation
+ data = (char *)calloc(n + 1, sizeof(char));
+ strncpy(data, d + pos, n);
+ data[n] = '\0';
+ size = n + 1;
+ }
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+ \param str
+ \param n
+*/
+xbString& xbString::assign(char* str, int n)
+{
+ if(data)
+ {
+ free(data);
+ data = 0;
+ }
+
+ data = (char *)calloc(n + 1, sizeof(char));
+ strncpy(data, str, n);
+ data[n] = 0;
+ size = n + 1;
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+*/
+void xbString::trim() {
+ int l = len()-1;
+
+ for (;;) {
+ if (data[l] != ' ')
+ break;
+ data[l] = 0;
+ if (l == 0)
+ break;
+ l--;
+ }
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbString &xbString::remove(size_t pos, int n) {
+ if (data == NULL)
+ return (*this);
+ if (data[0] == 0)
+ return (*this);
+
+ size_t l = len();
+
+ if (pos>l)
+ return (*this);
+ if (n == 0)
+ return (*this);
+ if (n > int(l-pos))
+ n = l-pos;
+ if (n<0)
+ n = l-pos;
+ memcpy(data+pos, data+pos+n, l-pos-n+1);
+
+ return (*this);
+}
+
+//! Short description.
+/*!
+ \param pos
+ \param n
+*/
+xbString xbString::mid(size_t pos, int n) const {
+ if (data == NULL)
+ return (*this);
+ if (data[0] == 0)
+ return (*this);
+
+ size_t l = len();
+
+ if (pos>l)
+ return (*this);
+ if (n == 0)
+ return (*this);
+ if (n > int(l-pos))
+ n = l-pos;
+ if (n<0)
+ n = l-pos;
+
+ xbString s;
+ s.data = (char *)malloc(n+1);
+ strncpy(s.data, data+pos, n);
+ s.data[n] = 0;
+
+ return s;
+}
+
+//! Short description.
+/*!
+ \param from
+ \param to
+*/
+void xbString::swapChars( char from, char to )
+{
+ size_t i;
+ for( i = 0; i < size; i++ )
+ if( data[i] == from )
+ data[i] = to;
+}
+
+//! Short description.
+/*!
+ \param c
+*/
+void xbString::zapChar( char c )
+{
+ /* routine zaps every occurrence of a given character from the string */
+ int p;
+ size_t s;
+ p = pos( c );
+ while( p != -1 ){
+ for( s = (size_t) p; s < size; s++ )
+ putAt( s, data[s+1]);
+ resize( size-1 );
+ p = pos( c );
+ }
+}
+
+//! Short description.
+/*!
+ \param c
+*/
+int xbString::countChar( char c ) const
+{
+ int i,j;
+
+ for( i = 0,j = 0; i < (int) size; i++ )
+ if( data[i] == c )
+ j++;
+
+ return j;
+}
+
+
+//! Short description.
+/*!
+ \param c
+*/
+
+void xbString::addBackSlash( char c )
+{
+ /* prefixes all char "c" with a backslash */
+ int i, t, cnt;
+ xbString ws;
+
+ cnt = countChar( c );
+ if( !cnt )
+ return;
+
+ ws.resize( size+cnt );
+ for( i = 0, t = 0; i < (int)size; i++ ){
+ if( data[i] == c )
+ ws.putAt( t++, '\\' );
+ ws.putAt( t++, data[i] );
+ }
+ ws.putAt( t, 0 );
+ *this = ws.getData();
+}
+
+//! Short description.
+/*!
+ \param cnt
+*/
+void xbString::lTrunc( size_t cnt )
+{
+ /* left truncate cnt butes */
+
+ char * ndata;
+ char * p;
+ if( cnt >= size ){
+ ctor(0);
+ return;
+ }
+ ndata = (char *) malloc( size - cnt );
+ p = data;
+ p += cnt;
+ strcpy( ndata, p );
+ free( data );
+ data = ndata;
+ size = size - cnt;
+}
+
+
+//! Short description.
+/*!
+ \param c
+*/
+void xbString::zapLeadingChar( char c )
+{
+ /* left truncate all of character c */
+
+ int len = 0;
+ char *p;
+ p = data;
+ while( *p && *p == c ){
+ len++;
+ p++;
+ }
+ if( len )
+ lTrunc( len );
+}
+
+
+//! Short description.
+/*!
+*/
+xbBool xbString::hasAlphaChars() const
+{
+ for( int i = 0; i < (int) size; i++ )
+ if( isalpha( data[i] ))
+ return 1;
+ return 0;
+}
+
+
+//! Short description.
+/*!
+ \param out
+*/
+
+int xbString::cvtHexChar( char & out )
+{
+ /* this routine converts a four byte string in the format of 0x00
+ to a one byte char value
+
+ the first four bytes of the string must be in the format 0x00
+ anything past the first four bytes is disregarded
+
+ returns -1 on error
+ 0 on success
+ */
+
+ int j, k;
+ char c;
+
+ if( len() < 4 || data[0] != '0' || (data[1]!='X' && data[1]!='x' ))
+ return -1;
+
+ c = toupper( data[2] );
+ j = ( c > '9' ? c - 'A' + 10 : c - '0' );
+ c = toupper( data[3] );
+ k = ( c > '9' ? c - 'A' + 10 : c - '0' );
+ j = ( j << 4 ) + k;
+
+ out = ( char ) j;
+ return 0;
+}
+
+//! Short description.
+/*!
+ \param out
+*/
+int xbString::cvtHexString( xbString & out )
+{
+ /* this routine converts a string of four byte format of 0x00
+ to a string of one byte chars
+
+ returns -1 on error
+ 0 on success
+ */
+
+ char c;
+ xbString ws;
+ ws = data;
+ out = "";
+ while( ws.len()){
+ if( ws.cvtHexChar( c ))
+ return -1;
+ out += c;
+ ws.lTrunc( 4 );
+ }
+ return 0;
+}
+//! Short description.
+/*!
+ \param src
+ \param delim
+ \param skipcnt
+ \param opt
+*/
+int xbString::setFromDelimitedInput( const char * src,
+ char delim, int skipcnt, int opt )
+{
+ /* opt values
+
+ 1 - ignore delimiters between quotes
+ 2 - treat crlf characters as delimters
+ 3 - both options 1 and 2
+ */
+
+ int len;
+ int curpos = 0;
+ int quotesw = 0;
+ const char * s;
+ const char * anchor;
+
+ /* skip past skipcnt delimiters */
+ s = src;
+ while( *s && curpos < skipcnt ){
+ if( *s == delim && !quotesw )
+ curpos++;
+ else if (( opt == 1 || opt == 3 ) && *s == '"' )
+ quotesw = (quotesw) ? 0 : 1;
+ s++;
+ }
+ /* at the beginning of the field */
+ anchor = s;
+ while( *s && ( *s != delim || ( *s == delim && quotesw ))){
+ if( *s == '"' )
+ quotesw = (quotesw) ? 0 : 1;
+ s++;
+ }
+ len = s - anchor;
+
+ /* copy data */
+ data = (char *) realloc( data, len+1 );
+ memcpy( data, anchor, len );
+ data[len] = 0;
+ this->size = len+1;
+
+ if( opt == 2 || opt == 3 ){
+ zapChar( 0x0a );
+ zapChar( 0x0c );
+ zapChar( 0x0d );
+ }
+
+ return len;
+}
+
diff --git a/xbase64/xbstring.h b/xbase64/xbstring.h
new file mode 100755
index 0000000..9896cdc
--- /dev/null
+++ b/xbase64/xbstring.h
@@ -0,0 +1,145 @@
+/* xbstring.h
+
+ Xbase64 project source code
+
+ This file contains the Class definition for a xbString object.
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XBSTRING_H__
+#define __XBSTRING_H__
+
+#ifdef __GNU LesserG__
+#pragma interface
+#endif
+
+#ifdef __WIN32__
+#include <xbase64/xbwincfg.h>
+#else
+#include <xbase64/xbconfig.h>
+#endif
+
+#include <stdlib.h>
+#include <iostream>
+
+/*! \file xbstring.h
+*/
+
+//! xbString class
+/*!
+*/
+class XBDLLEXPORT xbString {
+public:
+ enum {npos = -1};
+
+ xbString();
+ xbString(size_t size);
+ xbString(char c);
+ xbString(const char *s);
+ xbString(const char *s, size_t maxlen);
+ xbString(const xbString &s);
+ virtual ~xbString();
+
+ operator const char *() const;
+ char operator[](int n) { return data[n]; }
+
+ xbString &operator=(const xbString &s);
+ xbString &operator=(const char *s);
+ xbString &operator=(char c);
+ xbString &operator+=(const char *s);
+ xbString &operator+=(char c);
+ xbString &operator-=(const char *s);
+
+ xbBool operator == ( const xbString& ) const;
+ xbBool operator != ( const xbString& ) const;
+ xbBool operator < ( const xbString& ) const;
+ xbBool operator > ( const xbString& ) const;
+ xbBool operator <= ( const xbString& ) const;
+ xbBool operator >= ( const xbString& ) const;
+
+ friend XBDLLEXPORT std::ostream& operator << ( std::ostream&,
+ const xbString& );
+ void addBackSlash( char c );
+ xbString &assign(const xbString& str, size_t pos = 0, int n = npos);
+ xbString &assign(char* str, int n);
+ xbString copy() const;
+ const char *c_str() const;
+ int countChar( char c ) const;
+ int cvtHexChar( char & out );
+ int cvtHexString( xbString & out );
+ char getCharacter( int n ) const { return data[n]; }
+ const char *getData() const;
+ xbBool hasAlphaChars() const;
+ xbBool isEmpty() const;
+ xbBool isNull() const;
+ size_t len() const;
+ size_t length() const;
+ xbString mid(size_t pos = 0, int n = npos) const;
+ void lTrunc( size_t cnt );
+ int pos(char c);
+ int pos(const char* s);
+ void putAt(size_t pos, char c);
+ xbString &remove(size_t pos = 0, int n = npos);
+ void resize(size_t size);
+ void setNum(long num);
+ void setNum(char * fmt, double num);
+ xbString &sprintf(const char *format, ...);
+ void swapChars( char from, char to );
+ void toLowerCase();
+ void toUpperCase();
+ void trim();
+ void zapChar( char c );
+ void zapLeadingChar( char c );
+ int setFromDelimitedInput(const char *,char, int, int );
+
+protected:
+ void ctor(const char *s);
+ void ctor(const char *s, size_t maxlen);
+ char *data;
+ size_t size;
+ static const char * NullString;
+};
+
+XBDLLEXPORT xbString operator-(const xbString &s1, const xbString &s2);
+XBDLLEXPORT xbString operator+(const xbString &s1, const xbString &s2);
+XBDLLEXPORT xbString operator+(const xbString &s1, const char *s2);
+XBDLLEXPORT xbString operator+(const char *s1, const xbString &s2);
+XBDLLEXPORT xbString operator+(const xbString &s1, char c2);
+XBDLLEXPORT xbString operator+(char c1, const xbString &s2);
+XBDLLEXPORT xbBool operator==(const xbString &s1, const char *s2);
+XBDLLEXPORT xbBool operator!=(const xbString &s1, const char *s2);
+
+#endif
+
diff --git a/xbase64/xbtypes.h b/xbase64/xbtypes.h
new file mode 100755
index 0000000..de5f08c
--- /dev/null
+++ b/xbase64/xbtypes.h
@@ -0,0 +1,99 @@
+/* xbtypes.h
+
+ Xbase64 project source code
+
+ Copyright (C) 1997,2003 Gary A Kunkel
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ Contact:
+
+ Email:
+
+ xdb-devel@lists.sourceforge.net
+ xdb-users@lists.sourceforge.net
+
+
+ Regular Mail:
+
+ XBase Support
+ 149C South Main St
+ Keller Texas, 76248
+ USA
+
+*/
+
+#ifndef __XB_XTYPES_H__
+#define __XB_XTYPES_H__
+
+#include <stdio.h>
+
+/*! \file xbtypes.h
+*/
+
+//! xbULong type
+/*!
+*/
+typedef unsigned long int xbULong;
+
+//! xbUShort type
+/*!
+*/
+typedef unsigned short int xbUShort;
+
+//! xbShort type
+/*!
+*/
+typedef short int xbShort;
+typedef long xbLong;
+
+
+//! xbFloat type
+/*!
+*/
+typedef float xbFloat;
+
+
+//! xbDouble type
+/*!
+*/
+typedef double xbDouble;
+
+//! xbBool type
+/*!
+*/
+typedef short int xbBool;
+
+//! xbOffT type
+/*!
+*/
+#ifdef XB_LOCKING_ON
+#ifdef __WIN32__
+#else
+#endif
+#endif // XB_LOCKING_ON
+#endif // __XB_XTYPES_H__
+
+// 64 bit file processing
+#if defined(HAVE_FSEEKO) && defined(HAVE_FTELLO) && defined(XB_LARGEFILE_SUPPORT)
+ #define _ftell ftello
+ #define _fseek fseeko
+ typedef off_t xbOffT;
+#else
+ #define _ftell ftell
+ #define _fseek fseek
+ typedef long xbOffT;
+#endif
diff --git a/xbase64/xbwincfg.h b/xbase64/xbwincfg.h
new file mode 100755
index 0000000..df2d187
--- /dev/null
+++ b/xbase64/xbwincfg.h
@@ -0,0 +1,82 @@
+/* config file for windows environments */
+
+/* Name of package */
+#define PACKAGE "xbase64"
+
+/* Version number of package */
+#define VERSION "3.1.2"
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you have io.h */
+#define HAVE_IO_H 1
+
+/* Define if you need to have .ndx indexes */
+#define XB_INDEX_NDX 1
+
+/* Define if you need to have .ntx indexes */
+#define XB_INDEX_NTX 1
+
+/* Define if you need to have .cdx indexes */
+#define XB_INDEX_CDX 1
+
+/* Define if you need to support memo fields */
+#define XB_MEMO_FIELDS 1
+
+/* Define if you need expressions */
+#define XB_EXPRESSIONS 1
+
+/* Define if you need locking support */
+#undef XB_LOCKING_ON
+
+/* Define if you need to turn on XBase specific debug */
+#define XBASE_DEBUG 1
+
+/* Define if using real deletes */
+#define XB_REAL_DELETE 1
+
+/* Define if need filters */
+#define XB_FILTERS 1
+
+/* Define if you have the fcntl function. */
+#define HAVE_FCNTL 1
+
+/* Define if you have the vsnprintf function. */
+//#define HAVE_VSNPRINTF 1
+
+/* Define if you have the vsprintf function. */
+#define HAVE_VSPRINTF 1
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Should we include generic index support? */
+#if defined(XB_INDEX_NDX) || defined(XB_INDEX_NTX)
+#define XB_INDEX_ANY 1
+#endif
+
+/* expressions required for indexes */
+#if defined(XB_INDEX_ANY) && !defined(XB_EXPRESSIONS)
+#define XB_EXPRESSIONS 1
+#endif
+
+/* default memo block size */
+#define XB_DBT_BLOCK_SIZE 512
+
+/* filename path separator */
+#define PATH_SEPARATOR '/'
+
+/* MS uses WIN32, Borland uses __WIN32__ */
+#ifdef WIN32
+ #ifndef __WIN32__
+ #define __WIN32__
+ #endif
+#endif
+