diff options
Diffstat (limited to 'xbase64')
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 + |