libflaim-4.9.966/0000755000175000017500000000000010510774540015065 5ustar ahodgkinsonahodgkinsonlibflaim-4.9.966/VERSION0000644000175000017500000000000710510774540016132 0ustar ahodgkinsonahodgkinson4.9.966libflaim-4.9.966/SVNRevision.9660000644000175000017500000000000110510774540017507 0ustar ahodgkinsonahodgkinson libflaim-4.9.966/Makefile0000644000175000017500000023452510510774540016540 0ustar ahodgkinsonahodgkinson#------------------------------------------------------------------------- # Desc: GNU makefile for FLAIM library and utilities # Tabs: 3 # # Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. # # 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # # To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com # # $Id: Makefile 3105 2006-01-11 11:14:10 -0700 (Wed, 11 Jan 2006) ahodgkinson $ #------------------------------------------------------------------------- ############################################################################# # # Sample Usage: # # make clean debug all # ############################################################################# # -- Include -- -include config.in # -- Project -- project_name = flaim project_display_name = FLAIM project_brief_desc = An extensible, flexible, adaptable, embeddable database engine # -- Maintainers -- ahodgkinson_info = Andrew Hodgkinson (Sr. Software Engineer) dsanders_info = Daniel Sanders (Sr. Software Engineer) # -- Versions -- major_version = 4 minor_version = 9 version = $(major_version).$(minor_version).$(svn_revision) # libtool versions are updated according to the following rules: # # 1. Start with a version of 0.0.0 for a library # 2. Update the version number immediately before a public release of the software # 3. If the library source code has changed at all since the last update, increment revision # 4. If any interfaces have been added, removed, or changed since the last update, increment current # 5. If any interfaces have been added since the last public release, increment age # 6. If any interfaces have been removed since the last public release, set age to 0 so_current = 4 so_revision = 1 so_age = 0 shared_lib_version = package_release_num = 1 # -- Paths initializations -- install_prefix = /usr # -- RPM, SPEC file names package_proj_name = lib$(project_name) package_proj_name_and_ver = $(package_proj_name)-$(version) # -- Determine if we are only cleaning -- util_targets = checkdb rebuild view sample dbshell gigatest test_targets = basictest all_targets = java rpms install libs all allutils test $(util_targets) $(test_targets) found_targets = $(foreach target,$(MAKECMDGOALS),$(if $(findstring $(target),$(all_targets)),$(target),)) ifneq (,$(findstring clean,$(MAKECMDGOALS))) do_clean = 1 ifeq ($(if $(findstring 0,$(words $(found_targets))),1,0),0) $(error Cannot specify other targets with clean target) endif else do_clean = 0 endif # -- Target variables -- target_build_type = usenativecc = yes target_os_family = target_processor_family = target_word_size = requested_word_size = win_target = unix_target = netware_target = submake_targets = netware_ring_0_target = sparc_generic = debian_arch = unknown # -- Enable command echoing -- ifneq (,$(findstring verbose,$(MAKECMDGOALS))) submake_targets += verbose ec = else ec = @ endif # -- Determine the host operating system -- ifndef host_os_family ifneq (,$(findstring WIN,$(OS))) host_os_family = win endif endif ifndef host_os_family ifneq (,$(findstring Win,$(OS))) host_os_family = win endif endif ifndef host_os_family ifeq (,$(OSTYPE)) ifneq (,$(RPM_OS)) OSTYPE = $(RPM_OS) endif endif ifeq (,$(OSTYPE)) OSTYPE := $(shell uname -s) endif endif ifndef host_os_family ifneq (,$(findstring Linux,$(OSTYPE))) host_os_family = linux endif endif ifndef host_os_family ifneq (,$(findstring linux,$(OSTYPE))) host_os_family = linux endif endif ifndef host_os_family ifneq (,$(findstring solaris,$(OSTYPE))) host_os_family = solaris endif endif ifndef host_os_family ifneq (,$(findstring SunOS,$(OSTYPE))) host_os_family = solaris endif endif ifndef host_os_family ifneq (,$(findstring darwin,$(OSTYPE))) host_os_family = osx endif endif ifndef host_os_family ifneq (,$(findstring Darwin,$(OSTYPE))) host_os_family = osx endif endif ifndef host_os_family ifneq (,$(findstring aix,$(OSTYPE))) host_os_family = aix endif endif ifndef host_os_family ifneq (,$(findstring hpux,$(OSTYPE))) host_os_family = hpux endif endif ifndef host_os_family ifneq (,$(findstring HP-UX,$(OSTYPE))) host_os_family = hpux endif endif ifndef host_os_family $(error Host operating system could not be determined. You may need to export OSTYPE from the environment.) endif # -- Target build type -- ifndef target_build_type ifneq (,$(findstring debug,$(MAKECMDGOALS))) submake_targets += debug target_build_type = debug endif endif ifndef target_build_type ifneq (,$(findstring release,$(MAKECMDGOALS))) submake_targets += release target_build_type = release endif endif ifndef target_build_type target_build_type = release endif # -- Use non-native (i.e., gcc) compiler on Solaris, etc. ifneq (,$(findstring usegcc,$(MAKECMDGOALS))) submake_targets += usegcc usenativecc = no endif # -- Override platform default word size? -- ifneq (,$(findstring 64bit,$(MAKECMDGOALS))) submake_targets += 64bit requested_word_size = 64 endif ifneq (,$(findstring 32bit,$(MAKECMDGOALS))) submake_targets += 32bit requested_word_size = 32 endif # -- Target operating system -- ifndef target_os_family ifeq ($(host_os_family),linux) unix_target = yes target_os_family = linux endif endif ifndef target_os_family ifeq ($(host_os_family),solaris) unix_target = yes target_os_family = solaris endif endif ifndef target_os_family ifeq ($(host_os_family),osx) unix_target = yes target_os_family = osx endif endif ifndef target_os_family ifeq ($(host_os_family),aix) unix_target = yes target_os_family = aix endif endif ifndef target_os_family ifeq ($(host_os_family),hpux) unix_target = yes target_os_family = hpux endif endif ifneq (,$(findstring nlm,$(MAKECMDGOALS))) submake_targets += nlm netware_target = yes target_os_family = netware host_os_family = win ifneq (,$(findstring ring0,$(MAKECMDGOALS))) submake_targets += ring0 netware_ring_0_target = yes endif endif ifndef target_os_family ifeq ($(host_os_family),win) win_target = yes target_os_family = win endif endif ifndef target_os_family $(error Target operating system could not be determined) endif # -- Host word size and processor -- host_native_word_size = host_processor_family = host_supported_word_sizes = ifneq (,$(PROCESSOR_ARCHITECTURE)) HOSTTYPE = $(PROCESSOR_ARCHITECTURE) endif ifeq (,$(HOSTTYPE)) ifneq (,$(RPM_ARCH)) HOSTTYPE = $(RPM_ARCH) endif endif ifeq (,$(HOSTTYPE)) ifneq ($(host_os_family),hpux) ifneq ($(host_os_family),linux) HOSTTYPE := $(shell uname -p) endif endif ifeq (,$(HOSTTYPE)) HOSTTYPE := $(shell uname -m) else ifneq (,$(findstring nvalid,$(HOSTTYPE))) HOSTTYPE := $(shell uname -m) else ifneq (,$(findstring unknown,$(HOSTTYPE))) HOSTTYPE := $(shell uname -m) endif endif endif endif ifeq (,$(HOSTTYPE)) $(error HOSTTYPE environment variable has not been set) endif ifndef host_native_word_size ifneq (,$(findstring x86_64,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring x86,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring 86,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring ia64,$(HOSTTYPE))) host_processor_family = ia64 host_native_word_size = 64 host_supported_word_sizes = 64 endif endif ifndef host_native_word_size ifneq (,$(findstring s390x,$(HOSTTYPE))) host_processor_family = s390 host_native_word_size = 64 host_supported_word_sizes = 31 64 endif endif ifndef host_native_word_size ifneq (,$(findstring s390,$(HOSTTYPE))) host_processor_family = s390 host_native_word_size = 31 host_supported_word_sizes = 31 endif endif ifndef host_native_word_size ifneq (,$(findstring ppc64,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring ppc,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring sparc,$(HOSTTYPE))) host_processor_family = sparc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring powerpc,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring Power,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring rs6000,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring hppa,$(HOSTTYPE))) host_processor_family = hppa host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring 9000,$(HOSTTYPE))) host_processor_family = hppa host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size $(error Unable to determine host word size. $(HOSTTYPE)) endif # -- Target word size and processor -- ifneq (,$(findstring nlm,$(MAKECMDGOALS))) target_processor_family = x86 target_word_size = 32 target_supported_word_sizes = 32 else target_processor_family = $(host_processor_family) target_word_size = $(host_native_word_size) target_supported_word_sizes = $(host_supported_word_sizes) endif ifdef requested_word_size ifneq (,$(findstring $(requested_word_size),$(target_supported_word_sizes))) target_word_size = $(requested_word_size) else $(error Unsupported target word size) endif endif # -- Debian architecture -- ifeq ($(target_os_family),linux) ifeq ($(target_processor_family),x86) ifeq ($(target_word_size),64) debian_arch = amd64 else debian_arch = i386 endif endif ifeq ($(target_processor_family),sparc) debian_arch = sparc endif ifeq ($(target_processor_family),powerpc) debian_arch = powerpc endif endif # -- Other targets and options -- ifneq (,$(findstring sparcgeneric,$(MAKECMDGOALS))) sparc_generic = yes endif # -- Helper functions -- define normpath $(strip $(subst \,/,$(1))) endef ifeq (win,$(host_os_family)) define hostpath $(strip $(subst /,\,$(1))) endef else define hostpath $(strip $(1)) endef endif ifeq (win,$(host_os_family)) define ppath $(strip $(subst \,\\,$(subst /,\,$(1)))) endef else define ppath $(strip $(1)) endef endif ifeq (win,$(host_os_family)) define create_archive -$(ec)$(call rmcmd,$(2)) $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -ttar -r $(call hostpath,$(2)).tar $(call hostpath,$(3))" $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -tgzip -r $(call hostpath,$(2)).tar.gz $(call hostpath,$(2)).tar" $(ec)cmd /C "cd $(call hostpath,$(1)) && del $(call hostpath,$(2)).tar" endef define extract_archive $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar.gz $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar endef else define create_archive -$(ec)$(call rmcmd,$(2)) $(ec)tar cf $(2).tar -C $(1) $(3) $(ec)gzip -f $(2).tar $(ec)chmod 775 $(2).tar.gz endef define extract_archive $(ec)gunzip -f $(strip $(1))/$(2).tar.gz $(ec)tar xvf $(strip $(1))/$(2).tar -C $(1) endef endif # Platform-specific commands, directories, etc. ifeq ($(host_os_family),win) allprereqs = $(call hostpath,$+) copycmd = copy /Y $(call hostpath,$(1)) $(call hostpath,$(2)) 1>NUL dircopycmd = xcopy /Y /E /V /I $(call hostpath,$(1)) $(call hostpath,$(2)) rmcmd = if exist $(call hostpath,$(1)) del /Q $(call hostpath,$(1)) 1>NUL rmdircmd = if exist $(call hostpath,$(1)) rmdir /q /s $(call hostpath,$(1)) 1>NUL mkdircmd = -if not exist $(call hostpath,$(1)) mkdir $(call hostpath,$(1)) runtest = cmd /C "cd $(call hostpath,$(test_dir)) && $(1) -d" topdir := $(call normpath,$(shell chdir)) else allprereqs = $+ copycmd = cp -f $(1) $(2) dircopycmd = cp -rf $(1) $(2) rmcmd = rm -f $(1) rmdircmd = rm -rf $(1) mkdircmd = mkdir -p $(1) runtest = sh -c "cd $(test_dir); ./$(1) -d; exit" topdir := $(shell pwd) endif # If this is an un-tar'd or un-zipped source package, the tools directory # will be subordinate to the top directory. Otherwise, it will be # a sibling to the top directory - which is how it is set up in the # subversion repository. ifeq "$(wildcard $(topdir)/tools*)" "" tooldir := $(dir $(topdir))tools/$(host_os_family) else tooldir := $(topdir)/tools/$(host_os_family) endif # -- Utility variables -- em := sp := $(em) $(em) percent := \045 dollar := \044 question := \077 asterisk := \052 dash := \055 backslash := \134 double_quote := \042 # -- printf -- ifdef unix_target gprintf = printf else gprintf = $(call hostpath,$(tooldir)/printf.exe) endif # Determine the toolkit directory ifeq "$(wildcard $(topdir)/ftk)" "" ftk_dir := $(dir $(topdir))ftk else ftk_dir := $(topdir)/ftk endif ftk_src_dir = $(ftk_dir)/src # -- Subversion Revision -- calc_svn_revision = ignore_local_mods = ifneq (,$(findstring ignore-local-mods,$(MAKECMDGOALS))) submake_targets += ignore-local-mods ignore_local_mods = 1 endif ifneq (,$(findstring ilm,$(MAKECMDGOALS))) submake_targets += ilm ignore_local_mods = 1 endif ifdef ignore_local_mods local_mods_ok = 1 else local_mods_ok = endif ifneq (,$(findstring dist,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring rpm,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring ubuntusrc,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring docs,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring changelog,$(MAKECMDGOALS))) calc_svn_revision = 1 # Get the info for this directory ifndef svn_user $(error Must define svn_user= in environment or as a parameter) endif ifndef svn_rev $(error Must define svn_rev= in environment or as a parameter) endif svnrevs = $(subst :, ,$(svn_rev)) svn_low_rev = $(word 1,$(svnrevs)) svn_high_rev = $(word 2,$(svnrevs)) svnurl0 := $(shell svn info) svnurl1 = $(subst URL: ,URL:,$(svnurl0)) svnurl2 = $(filter URL:%,$(svnurl1)) svnurl3 = $(subst URL:,,$(svnurl2)) svnurl = $(subst ://,://$(svn_user)@,$(svnurl3)) endif ifdef calc_svn_revision # Get the info for all files. ifndef local_mods_ok srevision := $(shell svnversion . -n) ifneq (,$(findstring M,$(srevision))) $(error Local modifications found - please check in before making distro) endif ifneq (,$(findstring :,$(srevision))) $(error Mixed revisions in repository - please update before making distro) endif srevision := $(shell svnversion $(ftk_dir) -n) ifneq (,$(findstring M,$(srevision))) $(error Local modifications found - please check in before making distro) endif ifneq (,$(findstring :,$(srevision))) $(error Mixed revisions in repository - please update before making distro) endif endif numdigits = $(words $(subst 9,9 ,$(subst 8,8 ,$(subst 7,7 ,\ $(subst 6,6 ,$(subst 5,5 ,$(subst 4,4 ,$(subst 3,3 ,\ $(subst 2,2 ,$(subst 1,1 ,$(subst 0,0 ,$(1)))))))))))) revision0 := $(shell svn info -R . $(ftk_dir)) revision1 = $(subst Last Changed Rev: ,LastChangedRev:,$(revision0)) revision2 = $(filter LastChangedRev:%,$(revision1)) revision3 = $(subst LastChangedRev:,,$(revision2)) revision4 = $(sort $(revision3)) revision5 = $(foreach num,$(revision4),$(call numdigits,$(num)):$(num)) revision6 = $(sort $(revision5)) revision7 = $(word $(words $(revision6)),$(revision6)) svn_revision = $(word 2,$(subst :, ,$(revision7))) else ifeq "$(wildcard SVNRevision.*)" "" svn_revision = 0 else svn_revision = $(word 2,$(subst ., ,$(wildcard SVNRevision.*))) endif endif ifeq "$(svn_high_rev)" "" svn_high_rev = $(svn_revision) endif # Files and Directories ifeq ($(target_word_size),64) ifeq ($(target_os_family),linux) lib_dir_name = lib64 endif endif ifndef lib_dir_name lib_dir_name = lib endif ifndef rpm_build_root ifneq (,$(DESTDIR)) rpm_build_root = $(DESTDIR) else rpm_build_root = endif endif lib_install_dir = $(rpm_build_root)$(install_prefix)/$(lib_dir_name) include_install_dir = $(rpm_build_root)$(install_prefix)/include pkgconfig_install_dir = $(lib_install_dir)/pkgconfig build_output_dir = $(topdir)/build doxygen_output_dir = $(build_output_dir)/docs target_path = $(build_output_dir)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) package_dir = $(target_path)/package spec_dir = $(package_dir)/SPECS spec_file = $(spec_dir)/$(package_proj_name).spec package_sources_dir = $(package_dir)/SOURCES package_bin_dir = $(package_dir)/BIN package_build_dir = $(package_dir)/BUILD package_rpms_dir = $(package_dir)/RPMS package_srpms_dir = $(package_dir)/SRPMS pkgconfig_file_name = $(package_proj_name).pc pkgconfig_file = $(package_dir)/$(pkgconfig_file_name) package_debian_dir = $(package_dir)/DEBIAN debian_stage_dir = $(package_dir)/debian_stage debian_pkginfo_dir = $(debian_stage_dir)/DEBIAN package_ubuntu_dir = $(package_dir)/UBUNTU package_version_ubuntu = $(version)-0ubuntu1 package_distro_ubuntu = edgy ubuntu_stage_dir = $(package_dir)/ubuntu_stage ubuntu_pkginfo_dir = $(ubuntu_stage_dir)/DEBIAN package_stage_parent_dir = $(package_dir)/stage package_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver) package_bin_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) package_lib_stage_dir = $(package_bin_stage_dir)/lib package_shared_lib_stage_dir = $(package_lib_stage_dir)/shared package_static_lib_stage_dir = $(package_lib_stage_dir)/static package_util_stage_dir = $(package_bin_stage_dir)/util package_inc_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/include src_package_dir = $(package_sources_dir) bin_package_dir = $(package_bin_dir) src_package_base_name = $(package_proj_name_and_ver) bin_package_base_name = $(package_proj_name_and_ver)-$(target_os_family)-$(target_processor_family)-$(target_word_size)-bin src_package_name=$(src_package_base_name).tar.gz bin_package_name=$(bin_package_base_name).tar.gz rpm_name = $(package_proj_name_and_ver)-$(package_release_num).$(HOSTTYPE).rpm srpm_name = $(package_proj_name_and_ver)-$(package_release_num).src.rpm develrpm_name = $(package_proj_name)-devel-$(version)-$(package_release_num).$(HOSTTYPE).rpm inc_dirs = src util $(ftk_src_dir) util_dir = $(target_path)/util test_dir = $(target_path)/test sample_dir = $(target_path)/sample lib_dir = $(target_path)/$(lib_dir_name) shared_lib_dir = $(lib_dir)/shared static_lib_dir = $(lib_dir)/static util_obj_dir = $(util_dir)/obj test_obj_dir = $(test_dir)/obj sample_obj_dir = $(sample_dir)/obj lib_obj_dir = $(static_lib_dir)/obj ifdef win_target lib_sobj_dir = $(shared_lib_dir)/obj else lib_sobj_dir = $(lib_obj_dir) endif doxyfile = $(doxygen_output_dir)/Doxyfile # -- Tools -- libr = exe_linker = shared_linker = compiler = # Compiler definitions and flags ccflags = ccdefs = ifeq ($(target_word_size),64) ccdefs += FLM_64BIT endif ############################################################################## # Win settings ############################################################################## ifdef win_target exe_suffix = .exe obj_suffix = .obj lib_prefix = static_lib_suffix = .lib shared_lib_suffix = .dll libr = lib.exe exe_linker = link.exe shared_linker = link.exe compiler = cl.exe # Compiler defines and flags ccflags += /nologo /c /GF /GR /J /MD /W4 /WX /Zi /Zp1 ccdefs += _CRT_SECURE_NO_DEPRECATE ccdefs += WIN32_LEAN_AND_MEAN ccdefs += WIN32_EXTRA_LEAN ifeq ($(target_build_type),debug) ccflags += /Ob1 /Od /RTC1 /Wp64 ccdefs += FLM_DEBUG else ccflags += /O2 endif # Linker switches shared_link_flags = \ /DLL \ /DEBUG /PDB:$(call hostpath,$(@:.dll=.pdb)) \ /map:$(call hostpath,$(@:.dll=.map)) \ /INCREMENTAL:NO \ /NOLOGO \ /OUT:$(call hostpath,$@) exe_link_flags = \ /DEBUG /PDB:$(call hostpath,$(@:.exe=.pdb)) \ /map:$(call hostpath,$(@:.exe=.map)) \ /INCREMENTAL:NO \ /FIXED:NO \ /NOLOGO \ /OUT:$(call hostpath,$@) # Libraries that our various components need to link against lib_link_libs = imagehlp.lib user32.lib rpcrt4.lib wsock32.lib advapi32.lib exe_link_libs = $(lib_link_libs) # Convert the list of defines into a proper set of command-line params ifdef ccdefs ccdefine = $(foreach def,$(strip $(ccdefs)),/D$(def)) endif # Same thing for the include dirs ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),/I$(call hostpath,$(inc_dir))) # Concatenate everything into the ccflags variable ccflags += $(ccdefine) $(ccinclude) endif ############################################################################## # Linux/Unix settings ############################################################################## ifdef unix_target ifneq ($(so_age),0) shared_lib_version = $(so_current).$(so_revision).$(so_age) else ifneq ($(so_revision),0) shared_lib_version = $(so_current).$(so_revision) else shared_lib_version = $(so_current) endif endif exe_suffix = obj_suffix = .o lib_prefix = lib static_lib_suffix = .a shared_lib_suffix = .so.$(shared_lib_version) compiler = g++ exe_linker = g++ shared_linker = g++ ifeq ($(target_os_family),osx) libr = libtool else libr = ar endif gcc_optimization_flags = \ -O \ -foptimize-sibling-calls \ -fstrength-reduce -fcse-follow-jumps \ -fcse-skip-blocks \ -frerun-cse-after-loop \ -frerun-loop-opt \ -fgcse \ -fgcse-lm \ -fgcse-sm \ -fdelete-null-pointer-checks \ -fexpensive-optimizations \ -fregmove \ -fsched-interblock \ -fsched-spec \ -fcaller-saves \ -fpeephole2 \ -freorder-blocks \ -freorder-functions \ -falign-functions \ -falign-jumps \ -falign-loops \ -falign-labels \ -fcrossjumping ifeq ($(usenativecc),yes) ifeq ($(target_os_family),solaris) compiler = CC exe_linker = CC shared_linker = CC compiler_version := $(shell $(compiler) -V 2>&1) ifneq (,$(findstring Sun C++,$(compiler_version))) sun_studio_compiler = yes endif endif endif ifeq ($(usenativecc),yes) ifeq ($(target_os_family),aix) compiler = xlC_r exe_linker = xlC_r shared_linker = xlC_r endif endif ifeq ($(usenativecc),yes) ifeq ($(target_os_family),hpux) compiler = aCC exe_linker = aCC shared_linker = aCC endif endif # Compiler defines and flags ifeq ($(compiler),g++) ccflags += -Wall -Werror -fPIC ifneq ($(target_processor_family),ia64) ccflags += -m$(target_word_size) endif endif ifeq ($(target_os_family),linux) # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += N_PLAT_UNIX _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 ifeq ($(target_build_type),release) ccflags += $(gcc_optimization_flags) endif endif ifeq ($(target_os_family),solaris) ifeq ($(usenativecc),yes) ccflags += -KPIC ifeq ($(target_build_type),release) ccflags += -xO3 endif ifeq ($(sun_studio_compiler),yes) ccflags += -errwarn=%all -errtags -erroff=hidef,inllargeuse,doubunder endif ifeq ($(target_word_size),64) ccflags += -xarch=generic64 else # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 ifdef sparc_generic ccflags += -xarch=generic ccdefs += FLM_SPARC_GENERIC else ccflags += -xarch=v8plus endif endif endif endif ifeq ($(target_os_family),aix) ifeq ($(usenativecc),yes) ccflags += -qthreaded -qstrict ifeq ($(target_word_size),64) ccflags += -q64 else # Must support 64 bit file sizes - even for 32 bit builds. ccflags += -q32 ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 endif endif endif ifeq ($(target_os_family),hpux) ifeq ($(usenativecc),yes) # Disable "Placement operator delete invocation is not yet # implemented" warning ccflags += +W930 ifeq ($(target_word_size),64) ccflags += +DD64 else # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 endif endif endif ifeq ($(target_os_family),osx) ccdefs += OSX ifeq ($(target_build_type),release) ccflags += $(gcc_optimization_flags) endif endif ccdefs += _REENTRANT ifeq ($(target_build_type),debug) ccdefs += FLM_DEBUG ccflags += -g endif # Convert the list of defines into a proper set of command-line params ifdef ccdefs ccdefine = $(foreach def,$(strip $(ccdefs)),-D$(def)) endif # Same thing for the include dirs ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),-I$(inc_dir)) # Concatenate everything into the ccflags variable ccflags += $(ccdefine) $(ccinclude) # Linker switches shared_link_flags = link_flags = -o $@ libr_flags = ifeq ($(compiler),g++) ifneq ($(target_processor_family),ia64) shared_link_flags += -m$(target_word_size) link_flags += -m$(target_word_size) endif endif lib_link_libs = -lpthread exe_link_libs = -lpthread ifeq ($(target_os_family),linux) lib_link_libs += -lrt -lstdc++ -ldl -lncurses exe_link_libs += -lrt -lstdc++ -ldl -lncurses shared_link_flags += -shared -Wl,-Bsymbolic -fpic \ -Wl,-soname,$(@F) -o $@ endif ifeq ($(target_os_family),solaris) link_flags += -R /usr/lib/lwp shared_link_flags += -G -pic -o $@ ifeq ($(usenativecc),yes) ifeq ($(target_word_size),64) link_flags += -xarch=generic64 shared_link_flags += -xarch=generic64 else link_flags += -xarch=v8plus endif endif lib_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt -lcurses exe_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt -lcurses endif ifeq ($(target_os_family),aix) ifeq ($(target_word_size),64) link_flags += -q64 libr_flags = -X64 else link_flags += -q32 libr_flags = -X32 endif lib_link_libs += -lm -lc -lcurses exe_link_libs += -lm -lc -lcurses endif ifeq ($(target_os_family),hpux) ifeq ($(target_word_size),64) link_flags += +DD64 endif lib_link_libs += -lm -lc -lrt -lcurses exe_link_libs += -lm -lc -lrt -lcurses endif ifeq ($(target_os_family),osx) shared_lib_suffix = -$(major_version).$(so_current).dylib lib_link_libs += -lstdc++ -ldl -lncurses exe_link_libs += -lstdc++ -ldl -lncurses shared_link_flags += -dynamiclib shared_link_flags += -current_version $(major_version).$(so_current).$(so_revision) shared_link_flags += -compatibility_version $(major_version).$(so_current).0 shared_link_flags += -o $@ endif exe_link_flags = $(link_flags) endif ############################################################################## # NetWare settings ############################################################################## ifdef netware_target exe_suffix = .nlm obj_suffix = .obj lib_prefix = static_lib_suffix = .lib shared_lib_suffix = .nlm ifdef WATCOM wc_dir = $(WATCOM) endif ifdef watcom wc_dir = $(watcom) endif ifndef wc_dir wc_dir = $(WC_DIR) endif ifndef wc_dir $(error Watcom compiler could not be found. Please define wc_dir) endif wc_dir := $(call normpath,$(wc_dir)) ifndef netware_ring_0_target ifndef ndk_dir $(error Netware SDK could not be found. Please define ndk_dir) endif ndk_dir := $(call normpath,$(ndk_dir)) endif libr = "$(call normpath,$(strip $(wc_dir)))/binnt/wlib.exe" exe_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" shared_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" compiler = "$(call normpath,$(wc_dir))/binnt/wpp386.exe" ifneq ($(target_build_type),release) ccdefs += FLM_DEBUG endif ifdef netware_ring_0_target ccdefs += FLM_RING_ZERO_NLM else ccdefs += FLM_LIBC_NLM endif ccflags += /ez /6s /w4 /za /zp1 /zq /zm /s /ei /of+ /we /bt=NETWARE ifeq ($(target_build_type),release) ccflags += /oair else ccflags += /hc endif libflags += /b /q /p=256 link_flags = /m /l /v /s inc_dirs += $(ndk_dir)/libc/include \ $(ndk_dir)/libc/include/winsock export include = $(foreach inc_dir,$(strip $(inc_dirs)),$(call hostpath,$(inc_dir));) export INCLUDE = $(include) export wpp386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) export wcc386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) define make_ring_0_lis_file_cmd $(ec)$(gprintf) "option verbose\n" > $(4) $(ec)$(gprintf) "option stack=32k\n" >> $(4) $(ec)$(gprintf) "option nod\n" >> $(4) $(ec)$(gprintf) "option map\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option screenname 'NONE'\n" >> $(4) $(ec)$(gprintf) "option threadname '$(2)'\n" >> $(4) $(ec)$(gprintf) "option start = f_nlmEntryPoint\n" >> $(4) $(ec)$(gprintf) "option exit = f_nlmExitPoint\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option xdcdata=nlm.xdc\n" >> $(4) $(ec)$(gprintf) "option pseudopreemption\n" >> $(4) $(ec)$(gprintf) "debug all debug novell\n" >> $(4) $(ec)$(gprintf) "form novell nlm '$(2)'\n" >> $(4) $(ec)$(gprintf) "name $(call ppath,$(1)/$(2)$(exe_suffix))\n" >> $(4) $(ec)$(gprintf) "file $(subst $(sp),\nfile ,$(call ppath,$(3)))\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(flaim_static_lib))\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(ctorarst)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(dtorarst)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(undefed)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(undefmbd)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(pure_err)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stablcl)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabact)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabactv)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabmod)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(prwdata)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(moddtorr)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabadt)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8d)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8m)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8s)\n" >> $(4) $(ec)$(gprintf) "alias __wcpp_4_fatal_runtime_error_=f_fatalRuntimeError\n" >> $(4) endef define make_libc_lis_file_cmd $(ec)$(gprintf) "option verbose\n" > $(4) $(ec)$(gprintf) "option stack=32k\n" >> $(4) $(ec)$(gprintf) "option nod\n" >> $(4) $(ec)$(gprintf) "option map\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option screenname 'NONE'\n" >> $(4) $(ec)$(gprintf) "option threadname '$(2)'\n" >> $(4) $(ec)$(gprintf) "option start = _LibCPrelude\n" >> $(4) $(ec)$(gprintf) "option exit = _LibCPostlude\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option xdcdata=nlm.xdc\n" >> $(4) $(ec)$(gprintf) "option pseudopreemption\n" >> $(4) $(ec)$(gprintf) "debug all debug novell\n" >> $(4) $(ec)$(gprintf) "form novell nlm '$(2)'\n" >> $(4) $(ec)$(gprintf) "name $(call ppath,$(1)/$(2)$(exe_suffix))\n" >> $(4) $(ec)$(gprintf) "file $(subst $(sp),\nfile ,$(call ppath,$(3)))\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(ndk_dir)/libc/imports/libcpre.obj)\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(flaim_static_lib))\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(wc_dir)/lib386/plib3s.lib)\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(wc_dir)/lib386/netware/libc3s.lib)\n" >> $(4) endef define make_ring_0_imp_file_cmd $(ec)$(gprintf) "import __WSAFDIsSet\n" > $(1) $(ec)$(gprintf) "import ActivateScreen\n" >> $(1) $(ec)$(gprintf) "import Alloc\n" >> $(1) $(ec)$(gprintf) "import AllocateResourceTag\n" >> $(1) $(ec)$(gprintf) "import atomic_dec\n" >> $(1) $(ec)$(gprintf) "import atomic_inc\n" >> $(1) $(ec)$(gprintf) "import atomic_xchg\n" >> $(1) $(ec)$(gprintf) "import BitTest\n" >> $(1) $(ec)$(gprintf) "import CEvaluateExpression\n" >> $(1) $(ec)$(gprintf) "import CFindLoadModuleHandle\n" >> $(1) $(ec)$(gprintf) "import CheckKeyStatus\n" >> $(1) $(ec)$(gprintf) "import ClearScreen\n" >> $(1) $(ec)$(gprintf) "import CloseFile\n" >> $(1) $(ec)$(gprintf) "import CloseScreen\n" >> $(1) $(ec)$(gprintf) "import CMovB\n" >> $(1) $(ec)$(gprintf) "import CMoveFast\n" >> $(1) $(ec)$(gprintf) "import ConvertPathString\n" >> $(1) $(ec)$(gprintf) "import ConvertSecondsToTicks\n" >> $(1) $(ec)$(gprintf) "import ConvertTicksToSeconds\n" >> $(1) $(ec)$(gprintf) "import CpuCurrentProcessor\n" >> $(1) $(ec)$(gprintf) "import CreateDirectory\n" >> $(1) $(ec)$(gprintf) "import CreateFile\n" >> $(1) $(ec)$(gprintf) "import CSetD\n" >> $(1) $(ec)$(gprintf) "import DebuggerSymbolList\n" >> $(1) $(ec)$(gprintf) "import DeleteDirectory\n" >> $(1) $(ec)$(gprintf) "import DirectorySearch\n" >> $(1) $(ec)$(gprintf) "import DirectReadFile\n" >> $(1) $(ec)$(gprintf) "import DirectReadFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFileNoWait\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFileNoWait\n" >> $(1) $(ec)$(gprintf) "import DisableInputCursor\n" >> $(1) $(ec)$(gprintf) "import DisplayScreenTextWithAttribute\n" >> $(1) $(ec)$(gprintf) "import DOSFirstByteBitMap\n" >> $(1) $(ec)$(gprintf) "import EnableInputCursor\n" >> $(1) $(ec)$(gprintf) "import EnterDebugger\n" >> $(1) $(ec)$(gprintf) "import EraseFile\n" >> $(1) $(ec)$(gprintf) "import ExpandFileInContiguousBlocks\n" >> $(1) $(ec)$(gprintf) "import ExpandFileInContiguousBlocks\n" >> $(1) $(ec)$(gprintf) "import ExportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import FindAndLoadNLM\n" >> $(1) $(ec)$(gprintf) "import Free\n" >> $(1) $(ec)$(gprintf) "import FreeLimboVolumeSpace\n" >> $(1) $(ec)$(gprintf) "import FreeLimboVolumeSpace\n" >> $(1) $(ec)$(gprintf) "import GetCacheBufferSize\n" >> $(1) $(ec)$(gprintf) "import GetClosestSymbol\n" >> $(1) $(ec)$(gprintf) "import GetCurrentClock\n" >> $(1) $(ec)$(gprintf) "import GetCurrentNumberOfCacheBuffers\n" >> $(1) $(ec)$(gprintf) "import GetCurrentTime\n" >> $(1) $(ec)$(gprintf) "import GetEntryFromPathStringBase\n" >> $(1) $(ec)$(gprintf) "import GetEntryFromPathStringBase\n" >> $(1) $(ec)$(gprintf) "import GetFileSize\n" >> $(1) $(ec)$(gprintf) "import GetKey\n" >> $(1) $(ec)$(gprintf) "import GetNLMAllocMemoryCounts\n" >> $(1) $(ec)$(gprintf) "import GetOriginalNumberOfCacheBuffers\n" >> $(1) $(ec)$(gprintf) "import GetProductMajorVersionNumber\n" >> $(1) $(ec)$(gprintf) "import GetRunningProcess\n" >> $(1) $(ec)$(gprintf) "import GetScreenSize\n" >> $(1) $(ec)$(gprintf) "import GetSyncClockFields\n" >> $(1) $(ec)$(gprintf) "import GetSystemConsoleScreen\n" >> $(1) $(ec)$(gprintf) "import ImportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import kCreateThread\n" >> $(1) $(ec)$(gprintf) "import kCurrentThread\n" >> $(1) $(ec)$(gprintf) "import kDelayThread\n" >> $(1) $(ec)$(gprintf) "import kDestroyThread\n" >> $(1) $(ec)$(gprintf) "import kExitThread\n" >> $(1) $(ec)$(gprintf) "import kGetThreadName\n" >> $(1) $(ec)$(gprintf) "import kGetThreadName\n" >> $(1) $(ec)$(gprintf) "import KillMe\n" >> $(1) $(ec)$(gprintf) "import kMutexAlloc\n" >> $(1) $(ec)$(gprintf) "import kMutexFree\n" >> $(1) $(ec)$(gprintf) "import kMutexLock\n" >> $(1) $(ec)$(gprintf) "import kMutexUnlock\n" >> $(1) $(ec)$(gprintf) "import kReturnCurrentProcessorID\n" >> $(1) $(ec)$(gprintf) "import kScheduleThread\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreAlloc\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreExamineCount\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreFree\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreSignal\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreTimedWait\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreWait\n" >> $(1) $(ec)$(gprintf) "import kSetThreadLoadHandle\n" >> $(1) $(ec)$(gprintf) "import kSetThreadName\n" >> $(1) $(ec)$(gprintf) "import kYieldIfTimeSliceUp\n" >> $(1) $(ec)$(gprintf) "import kYieldThread\n" >> $(1) $(ec)$(gprintf) "import LoadModule\n" >> $(1) $(ec)$(gprintf) "import LoadRules\n" >> $(1) $(ec)$(gprintf) "import MapFileHandleToFCB\n" >> $(1) $(ec)$(gprintf) "import MapPathToDirectoryNumber\n" >> $(1) $(ec)$(gprintf) "import MapPathToDirectoryNumber\n" >> $(1) $(ec)$(gprintf) "import MapVolumeNameToNumber\n" >> $(1) $(ec)$(gprintf) "import ModifyDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import ModifyDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import MountVolume\n" >> $(1) $(ec)$(gprintf) "import NDSCreateStreamFile\n" >> $(1) $(ec)$(gprintf) "import NDSDeleteStreamFile\n" >> $(1) $(ec)$(gprintf) "import NDSOpenStreamFile\n" >> $(1) $(ec)$(gprintf) "import NWLocalToUnicode\n" >> $(1) $(ec)$(gprintf) "import NWUnicodeToLocal\n" >> $(1) $(ec)$(gprintf) "import OpenFile\n" >> $(1) $(ec)$(gprintf) "import OpenScreen\n" >> $(1) $(ec)$(gprintf) "import OutputToScreen\n" >> $(1) $(ec)$(gprintf) "import PositionInputCursor\n" >> $(1) $(ec)$(gprintf) "import PositionOutputCursor\n" >> $(1) $(ec)$(gprintf) "import ReadFile\n" >> $(1) $(ec)$(gprintf) "import RenameEntry\n" >> $(1) $(ec)$(gprintf) "import RestartServer\n" >> $(1) $(ec)$(gprintf) "import ReturnResourceTag\n" >> $(1) $(ec)$(gprintf) "import ReturnVolumeMappingInformation\n" >> $(1) $(ec)$(gprintf) "import ReturnVolumeMappingInformation\n" >> $(1) $(ec)$(gprintf) "import RevokeFileHandleRights\n" >> $(1) $(ec)$(gprintf) "import SetCursorStyle\n" >> $(1) $(ec)$(gprintf) "import SetFileSize\n" >> $(1) $(ec)$(gprintf) "import SetFileSize\n" >> $(1) $(ec)$(gprintf) "import SGUIDCreate\n" >> $(1) $(ec)$(gprintf) "import SizeOfAllocBlock\n" >> $(1) $(ec)$(gprintf) "import SwitchToDirectFileMode\n" >> $(1) $(ec)$(gprintf) "import SwitchToDirectFileMode\n" >> $(1) $(ec)$(gprintf) "import UngetKey\n" >> $(1) $(ec)$(gprintf) "import UnImportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import UnloadRules\n" >> $(1) $(ec)$(gprintf) "import VMGetDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import WriteFile\n" >> $(1) $(ec)$(gprintf) "import WS2_32_bind\n" >> $(1) $(ec)$(gprintf) "import WS2_32_closesocket\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostbyaddr\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostbyname\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostname\n" >> $(1) $(ec)$(gprintf) "import WS2_32_htonl\n" >> $(1) $(ec)$(gprintf) "import WS2_32_htons\n" >> $(1) $(ec)$(gprintf) "import WS2_32_inet_addr\n" >> $(1) $(ec)$(gprintf) "import WS2_32_inet_ntoa\n" >> $(1) $(ec)$(gprintf) "import WS2_32_listen\n" >> $(1) $(ec)$(gprintf) "import WS2_32_recv\n" >> $(1) $(ec)$(gprintf) "import WS2_32_select\n" >> $(1) $(ec)$(gprintf) "import WS2_32_send\n" >> $(1) $(ec)$(gprintf) "import WS2_32_setsockopt\n" >> $(1) $(ec)$(gprintf) "import WS2_32_shutdown\n" >> $(1) $(ec)$(gprintf) "import WS2_32_socket\n" >> $(1) $(ec)$(gprintf) "import WSAAccept\n" >> $(1) $(ec)$(gprintf) "import WSACleanup\n" >> $(1) $(ec)$(gprintf) "import WSAConnect\n" >> $(1) $(ec)$(gprintf) "import WSAGetLastError\n" >> $(1) $(ec)$(gprintf) "import WSAStartup\n" >> $(1) endef define make_libc_imp_file_cmd $(ec)$(gprintf) "import CurrentProcess\n" > $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/libc.imp)\n" >> $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/netware.imp)\n" >> $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/ws2nlm.imp)\n" >> $(1) endef define flm_exe_link_cmd $(call $(if $(netware_ring_0_target),make_ring_0_imp_file_cmd,make_libc_imp_file_cmd),$(call hostpath,$(1)/$(2).imp)) $(call $(if $(netware_ring_0_target),make_ring_0_lis_file_cmd,make_libc_lis_file_cmd),$(1),$(2),$(3),$(call hostpath,$(1)/$(2).lis)) $(ec)$(call hostpath,$(exe_linker)) @$(call hostpath,$(1)/$(2).lis) @$(call hostpath,$(1)/$(2).imp) $(ec)$(call rmcmd,$(target_path)/$(1).lis) $(ec)$(call rmcmd,$(target_path)/$(1).imp) endef endif # -- File lists -- flaim_src = \ $(patsubst src/%.cpp,%.cpp,$(wildcard src/*.cpp)) ftk_src = \ $(patsubst $(ftk_src_dir)/%.cpp,%.cpp,$(wildcard $(ftk_src_dir)/*.cpp)) util_common_src = \ flm_dlst.cpp \ flm_lutl.cpp \ sharutil.cpp checkdb_src = \ checkdb.cpp \ $(util_common_src) gigatest_src = \ gigatest.cpp \ $(util_common_src) rebuild_src = \ rebuild.cpp \ $(util_common_src) view_src = \ view.cpp \ viewblk.cpp \ viewdisp.cpp \ viewedit.cpp \ viewfhdr.cpp \ viewlhdr.cpp \ viewlfil.cpp \ viewmenu.cpp \ viewsrch.cpp \ $(util_common_src) sample_src = \ sample.cpp dbshell_src = \ dbshell.cpp \ flm_edit.cpp \ $(util_common_src) ut_basictest_src = \ flmunittest.cpp \ basic_test.cpp \ $(util_common_src) # -- FLAIM library -- ftk_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(ftk_src)) flaim_static_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(flaim_src)) flaim_shared_obj = $(patsubst %.cpp,$(lib_sobj_dir)/%$(obj_suffix),$(flaim_src)) flaim_static_lib = $(static_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) ifndef netware_target flaim_shared_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(shared_lib_suffix) flaim_shared_imp_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) endif # -- Unit tests -- ut_basictest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_basictest_src)) # -- Utilities -- checkdb_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(checkdb_src)) checkdb_exe = $(util_dir)/checkdb$(exe_suffix) gigatest_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(gigatest_src)) gigatest_exe = $(util_dir)/gigatest$(exe_suffix) rebuild_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(rebuild_src)) rebuild_exe = $(util_dir)/rebuild$(exe_suffix) view_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(view_src)) view_exe = $(util_dir)/view$(exe_suffix) dbshell_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(dbshell_src)) dbshell_exe = $(util_dir)/dbshell$(exe_suffix) sample_obj = $(patsubst %.cpp,$(sample_obj_dir)/%$(obj_suffix),$(sample_src)) sample_exe = $(sample_dir)/sample$(exe_suffix) # -- Make system pattern search paths -- vpath %.cpp src util sample java/jni $(ftk_src_dir) # -- Default target -- .PHONY : libs libs: status clean dircheck $(flaim_static_lib) $(flaim_shared_lib) # -- *.cpp -> *$(obj_suffix) -- ifdef win_target $(lib_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(compiler) $(ccflags) /Fo$(call hostpath,$@) $(call hostpath,$<) endif ifdef win_target $(util_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(compiler) $(ccflags) /Fo$(call hostpath,$@) $(call hostpath,$<) endif ifdef win_target $(sample_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(compiler) $(ccflags) /Fo$(call hostpath,$@) $(call hostpath,$<) endif ifdef win_target $(test_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(compiler) $(ccflags) /Fo$(call hostpath,$@) $(call hostpath,$<) endif ifdef unix_target $(lib_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$<\n" $(ec)$(compiler) $(ccflags) -c $< -o $@ endif ifdef unix_target $(util_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$<\n" $(ec)$(compiler) $(ccflags) -c $< -o $@ endif ifdef unix_target $(sample_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$<\n" $(ec)$(compiler) $(ccflags) -c $< -o $@ endif ifdef unix_target $(test_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$<\n" $(ec)$(compiler) $(ccflags) -c $< -o $@ endif ifdef netware_target $(lib_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$(notdir $(strip $@))\n" $(ec)$(call hostpath,$(compiler)) $(call hostpath,$<) /fo=$(call hostpath,$@) endif ifdef netware_target $(util_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$(notdir $(strip $@))\n" $(ec)$(call hostpath,$(compiler)) $(call hostpath,$<) /fo=$(call hostpath,$@) endif ifdef netware_target $(sample_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$(notdir $(strip $@))\n" $(ec)$(call hostpath,$(compiler)) $(call hostpath,$<) /fo=$(call hostpath,$@) endif ifdef netware_target $(test_obj_dir)/%$(obj_suffix) : %.cpp $(ec)$(gprintf) "$(notdir $(strip $@))\n" $(ec)$(call hostpath,$(compiler)) $(call hostpath,$<) /fo=$(call hostpath,$@) endif ifdef win_target $(lib_sobj_dir)/%$(obj_suffix) : %.cpp $(ec)$(compiler) $(ccflags) /DFLM_SRC /DFLM_DLL \ /Fd$(call hostpath,$(lib_sobj_dir)/tmp.pdb) \ /Fo$(call hostpath,$@) $(call hostpath,$<) endif # -- flaim.lib and libflaim.a -- $(flaim_static_lib) : $(flaim_static_obj) $(ftk_obj) $(ec)$(gprintf) "Building $@ ...\n" ifdef win_target $(ec)$(libr) /NOLOGO $(call hostpath,$+) /OUT:$(call hostpath,$@) endif ifdef unix_target $(ec)rm -f $@ ifeq ($(target_os_family),osx) $(ec)$(libr) -static -o $@ $+ else $(ec)$(libr) $(libr_flags) -rcs $@ $+ endif endif ifdef netware_target $(ec)dir /s/b $(call hostpath,$(lib_obj_dir)/*$(obj_suffix)) > $(call hostpath,$(static_lib_dir)/flmlib.lis) $(ec)$(call hostpath,$(libr)) $(libflags) $(call hostpath,$(flaim_static_lib)) @$(call hostpath,$(static_lib_dir)/flmlib.lis) endif # -- flaim.dll and libflaim.so -- $(flaim_shared_lib) : $(flaim_shared_obj) $(ftk_obj) $(ec)$(gprintf) "Building $@ ...\n" ifdef win_target $(ec)$(shared_linker) $(call hostpath,$+) $(shared_link_flags) $(lib_link_libs) endif ifdef unix_target $(ec)rm -f $@ $(ec)$(shared_linker) $+ $(shared_link_flags) $(lib_link_libs) endif # -- Executable link command -- ifndef flm_exe_link_cmd define flm_exe_link_cmd $(ec)$(exe_linker) $(exe_link_flags) $(allprereqs) $(exe_link_libs) endef endif # -- checkdb -- .PHONY : checkdb checkdb: status clean dircheck $(flaim_static_lib) $(checkdb_exe) $(checkdb_exe): $(checkdb_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),checkdb,$(checkdb_obj)) # -- gigatest -- .PHONY : gigatest gigatest: status clean dircheck $(flaim_static_lib) $(gigatest_exe) $(gigatest_exe): $(gigatest_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),gigatest,$(gigatest_obj)) # -- rebuild -- .PHONY : rebuild rebuild: status clean dircheck $(flaim_static_lib) $(rebuild_exe) $(rebuild_exe): $(rebuild_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),rebuild,$(rebuild_obj)) # -- view -- .PHONY : view view: status clean dircheck $(flaim_static_lib) $(view_exe) $(view_exe): $(view_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),view,$(view_obj)) # -- dbshell -- .PHONY : dbshell dbshell: status clean dircheck $(flaim_static_lib) $(dbshell_exe) $(dbshell_exe): $(dbshell_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),dbshell,$(dbshell_obj)) # -- sample -- .PHONY : sample ifndef netware_target sample: status clean dircheck $(flaim_static_lib) $(sample_exe) $(sample_exe): $(sample_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(sample_dir),sample,$(sample_obj)) endif # -- basictest -- .PHONY : basictest basictest: status clean dircheck $(flaim_static_lib) $(test_dir)/basictest$(exe_suffix) $(test_dir)/basictest$(exe_suffix): $(ut_basictest_obj) $(flaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),basictest,$(ut_basictest_obj)) # -- version -- define make_version_files $(ec)$(gprintf) "$(version)" > $(1)/VERSION $(ec)$(gprintf) " " > $(1)/SVNRevision.$(svn_revision) $(ec)$(gprintf) "Version files created.\n" endef # -- srcdist -- .PHONY : srcdist srcdist: status clean dircheck docs spec ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating source package (SVN revision $(svn_revision)) ...\n" endif -$(ec)$(call rmdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_stage_dir)) $(ec)$(call make_version_files,$(package_stage_dir)) $(ec)$(call copycmd,Makefile,$(package_stage_dir)) $(ec)$(call copycmd,COPYING,$(package_stage_dir)) $(ec)$(call copycmd,COPYRIGHT,$(package_stage_dir)) $(ec)$(call dircopycmd,docs,$(package_stage_dir)/docs) $(ec)$(call dircopycmd,src,$(package_stage_dir)/src) $(ec)$(call copycmd,$(ftk_src_dir)/ftk.h,$(package_stage_dir)/src/flaimtk.h) $(ec)$(call dircopycmd,util,$(package_stage_dir)/util) $(ec)$(call dircopycmd,debian,$(package_stage_dir)/debian) $(ec)$(call dircopycmd,sample,$(package_stage_dir)/sample) $(ec)$(call dircopycmd,$(doxygen_output_dir),$(package_stage_dir)/docs) $(ec)$(call dircopycmd,$(dir $(topdir))tools,$(package_stage_dir)/tools) $(ec)$(call mkdircmd,$(package_stage_dir)/ftk) $(ec)$(call dircopycmd,$(dir $(topdir))ftk/src,$(package_stage_dir)/ftk/src) ifneq ($(host_os_family),win) -$(ec)rm -rf `find $(package_stage_dir) -name .svn` endif ifeq ($(host_os_family),win) $(ec)$(call copycmd,make.exe,$(package_stage_dir)) endif $(ec)$(call create_archive,$(package_stage_parent_dir), \ $(src_package_dir)/$(src_package_base_name), \ $(package_proj_name_and_ver)) $(ec)$(call rmdircmd,$(package_stage_parent_dir)) $(ec)$(gprintf) "Source package created.\n" # -- bindist -- .PHONY : bindist bindist: status clean dircheck all binpackage $(ec)$(gprintf) "" # -- binpackage -- .PHONY : binpackage binpackage: status ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating binary package (SVN revision $(svn_revision)) ...\n" endif -$(ec)$(call rmdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_inc_stage_dir)) $(ec)$(call mkdircmd,$(package_shared_lib_stage_dir)) $(ec)$(call mkdircmd,$(package_static_lib_stage_dir)) $(ec)$(call mkdircmd,$(package_util_stage_dir)) $(ec)$(call make_version_files,$(package_stage_dir)) $(ec)$(call copycmd,COPYING,$(package_stage_dir)) $(ec)$(call copycmd,COPYRIGHT,$(package_stage_dir)) $(ec)$(call copycmd,src/flaim.h,$(package_inc_stage_dir)) $(ec)$(call copycmd,$(ftk_src_dir)/ftk.h,$(package_inc_stage_dir)/flaimtk.h) $(ec)$(call copycmd,$(flaim_static_lib),$(package_static_lib_stage_dir)) ifdef flaim_shared_lib $(ec)$(call copycmd,$(flaim_shared_lib),$(package_shared_lib_stage_dir)) endif ifdef win_target $(ec)$(call copycmd,$(flaim_shared_imp_lib),$(package_shared_lib_stage_dir)) endif $(ec)$(call copycmd,$(checkdb_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(gigatest_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(rebuild_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(view_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(dbshell_exe),$(package_util_stage_dir)) $(ec)$(call create_archive,$(package_stage_parent_dir), \ $(bin_package_dir)/$(bin_package_base_name), \ $(package_proj_name_and_ver)) $(ec)$(call rmdircmd,$(package_stage_parent_dir)) $(ec)$(gprintf) "Binary package created.\n" # -- dist -- .PHONY : dist dist: status clean dircheck srcdist ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating distribution (SVN revision $(svn_revision)) ...\n" endif $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(package_dir)) $(ec)$(call extract_archive,$(package_dir),$(src_package_base_name)) $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) clean $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) all $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) binpackage package_dir="$(package_dir)" $(ec)$(call rmdircmd,$(package_dir)/$(package_proj_name_and_ver)) $(ec)$(call rmcmd,$(package_dir)/$(src_package_name)) $(ec)$(gprintf) "Distribution created.\n" # -- Change log -- .PHONY : changelog changelog: $(ec)$(gprintf) "Creating change log for SVN revisions $(svn_low_rev)-$(svn_high_rev) ...\n" $(ec)$(gprintf) "Using SVN URL $(svnurl) ...\n" $(ec)svn log $(svnurl) -v -r $(svn_low_rev):$(svn_high_rev) > $(package_sources_dir)/$(package_proj_name_and_ver).tar.log $(ec)$(gprintf) "Change log created.\n" # -- install -- .PHONY : install install: libs pkgconfig ifneq ($(host_os_family),win) $(ec)$(gprintf) "Installing ...\n" mkdir -p $(lib_install_dir)/pkgconfig mkdir -p $(include_install_dir) install --mode=644 $(flaim_shared_lib) $(lib_install_dir) install --mode=644 $(flaim_static_lib) $(lib_install_dir) install --mode=644 $(pkgconfig_file) $(pkgconfig_install_dir) install --mode=644 src/flaim.h $(include_install_dir) install --mode=644 $(ftk_src_dir)/ftk.h $(include_install_dir)/flaimtk.h ifneq ($(so_age),0) ifneq ($(so_revision),0) cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current).$(so_revision).$(so_age) $(lib_prefix)$(project_name).so.$(so_current).$(so_revision) endif endif ifneq ($(so_revision),0) cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current).$(so_revision) $(lib_prefix)$(project_name).so.$(so_current) endif cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current) $(lib_prefix)$(project_name).so -ldconfig $(lib_install_dir) $(ec)$(gprintf) "Installation complete.\n" endif # -- uninstall -- .PHONY : uninstall uninstall: ifneq ($(host_os_family),win) $(ec)$(gprintf) "Uninstalling ...\n" -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)*$(shared_lib_suffix)* -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) -rm -rf $(pkgconfig_install_dir)/$(pkgconfig_file_name) -rm -rf $(include_install_dir)/flaim.h -rm -rf $(include_install_dir)/flaimtk.h $(ec)$(gprintf) "Uninstalled.\n" endif # -- spec file -- .PHONY : spec spec: dircheck $(ec)$(gprintf) "Creating spec file ...\n" $(ec)$(gprintf) "Name: $(package_proj_name)\n" > $(spec_file) $(ec)$(gprintf) "$(percent)define prefix $(install_prefix)\n" >> $(spec_file) $(ec)$(gprintf) "BuildRequires: gcc-c++ libstdc++ libstdc++-devel\n" >> $(spec_file) $(ec)$(gprintf) "Summary: $(project_brief_desc)\n" >> $(spec_file) $(ec)$(gprintf) "URL: http://forge.novell.com/modules/xfmod/project/$(question)flaim\n" >> $(spec_file) $(ec)$(gprintf) "Version: $(version)\n" >> $(spec_file) $(ec)$(gprintf) "Release: $(package_release_num)\n" >> $(spec_file) $(ec)$(gprintf) "License: GPL\n" >> $(spec_file) $(ec)$(gprintf) "Vendor: Novell, Inc.\n" >> $(spec_file) $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) $(ec)$(gprintf) "Source: $(package_proj_name_and_ver).tar.gz\n" >> $(spec_file) $(ec)$(gprintf) "BuildRoot: $(percent){_tmppath}/$(percent){name}-$(percent){version}-build\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)description\n" >> $(spec_file) $(ec)$(gprintf) "FLAIM is an embeddable cross-platform database engine that provides a\n" >> $(spec_file) $(ec)$(gprintf) "rich, powerful, easy-to-use feature set. It is the database engine used\n" >> $(spec_file) $(ec)$(gprintf) "by Novell eDirectory. It has proven to be highly scalable, reliable,\n" >> $(spec_file) $(ec)$(gprintf) "and robust. It is available on a wide variety of 32 bit and 64 bit\n" >> $(spec_file) $(ec)$(gprintf) "platforms.\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "Authors:\n" >> $(spec_file) $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) $(ec)$(gprintf) " $(dsanders_info)\n" >> $(spec_file) $(ec)$(gprintf) " $(ahodgkinson_info)\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)package devel\n" >> $(spec_file) $(ec)$(gprintf) "Summary: FLAIM static library and header file\n" >> $(spec_file) $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) $(ec)$(gprintf) "Provides: $(package_proj_name)-devel\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)description devel\n" >> $(spec_file) $(ec)$(gprintf) "FLAIM is an embeddable cross-platform database engine that provides a\n" >> $(spec_file) $(ec)$(gprintf) "rich, powerful, easy-to-use feature set. It is the database engine used\n" >> $(spec_file) $(ec)$(gprintf) "by Novell eDirectory. It has proven to be highly scalable, reliable,\n" >> $(spec_file) $(ec)$(gprintf) "and robust. It is available on a wide variety of 32 bit and 64 bit\n" >> $(spec_file) $(ec)$(gprintf) "platforms.\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "Authors:\n" >> $(spec_file) $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) $(ec)$(gprintf) " Daniel Sanders\n" >> $(spec_file) $(ec)$(gprintf) " Andrew Hodgkinson\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)prep\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)setup -q\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)build\n" >> $(spec_file) $(ec)$(gprintf) "$(MAKE) lib_dir_name=$(percent){_lib} $(submake_targets) libs\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)install\n" >> $(spec_file) $(ec)$(gprintf) "$(MAKE) rpm_build_root=$(dollar)RPM_BUILD_ROOT install_prefix=$(percent){prefix} lib_dir_name=$(percent){_lib} $(submake_targets) install\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)clean\n" >> $(spec_file) $(ec)$(gprintf) "rm -rf $(dollar)RPM_BUILD_ROOT\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)files\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)defattr(-,root,root)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)doc COPYING COPYRIGHT VERSION\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(asterisk)$(shared_lib_suffix)$(asterisk)\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)files devel\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(static_lib_suffix)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/pkgconfig/$(pkgconfig_file_name)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/include/flaim.h\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/include/flaimtk.h\n" >> $(spec_file) $(ec)$(gprintf) "Created spec file.\n" # -- PKG-CONFIG -- .PHONY : pkgconfig pkgconfig: dircheck $(ec)$(gprintf) "prefix=$(install_prefix)\n" > $(pkgconfig_file) $(ec)$(gprintf) "exec_prefix=$(dollar){prefix}\n" >> $(pkgconfig_file) $(ec)$(gprintf) "libdir=$(dollar){exec_prefix}/$(lib_dir_name)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "includedir=$(dollar){prefix}/include\n\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Name: $(package_proj_name)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Description: $(project_brief_desc)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Version: $(version)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Libs: $(lib_link_libs) -lflaim -L$(dollar){libdir}\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Cflags: -I$(dollar){includedir}\n" >> $(pkgconfig_file) # -- SRCRPM -- .PHONY : srcrpm srcrpm: srcdist spec $(ec)$(gprintf) "Creating source RPM ...\n" $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet --nodeps -bs $(spec_file) $(ec)$(gprintf) "Source RPM created.\n" # -- RPMS -- .PHONY : rpms rpms: dist spec $(ec)$(gprintf) "Creating source and binary RPMs ...\n" $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet --nodeps -ba $(spec_file) $(ec)find $(package_dir) -name *.rpm | xargs chmod 775 $(ec)$(gprintf) "Source and binary RPMs created.\n" # -- Ubuntu Source Package -- .PHONY : ubuntusrc ubuntusrc: srcdist $(ec)$(gprintf) "Creating Ubuntu source package ...\n" -$(ec)$(call rmdircmd,$(ubuntu_stage_dir)) $(ec)$(call mkdircmd,$(ubuntu_stage_dir)) $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(ubuntu_stage_dir)/$(src_package_base_name).orig.tar.gz) $(ec)$(call extract_archive,$(ubuntu_stage_dir),$(src_package_base_name).orig) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/$(src_package_base_name).orig.tar) $(ec)$(call copycmd,$(ubuntu_stage_dir)/$(package_proj_name_and_ver)/COPYRIGHT,$(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/copyright) $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(ubuntu_stage_dir)/$(package_proj_name)_$(version).orig.tar.gz) $(ec)$(gprintf) "Creating Ubuntu changelog file ...\n" $(ec)$(gprintf) "$(package_proj_name) ($(package_version_ubuntu)) $(package_distro_ubuntu); urgency=low\n\n" > $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) " * Package update for Ubuntu.\n\n" >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) " -- $(ahodgkinson_info) " >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)822-date >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) "\n" >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)cat ChangeLog.ubuntu >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)cd $(ubuntu_stage_dir)/$(package_proj_name_and_ver); dpkg-buildpackage -S -sa -rfakeroot $(ec)$(gprintf) "Checking Ubuntu package ...\n" $(ec)lintian -i $(ubuntu_stage_dir)/$(package_proj_name)_$(package_version_ubuntu).dsc $(ec)linda -i $(ubuntu_stage_dir)/$(package_proj_name)_$(package_version_ubuntu).dsc $(ec)$(gprintf) "Moving packages to UBUNTU directory ...\n" $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.dsc,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.dsc) $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.gz,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.gz) $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.changes,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.changes) $(ec)$(gprintf) "Removing temporary files ...\n" $(ec)$(call rmdircmd,$(ubuntu_stage_dir)) $(ec)$(gprintf) "Done.\n" # -- Documentation -- .PHONY : docs docs: status clean dircheck doxyfile $(ec)$(gprintf) "Creating documentation ...\n" $(ec)doxygen $(doxyfile) $(ec)$(gprintf) "Documentation created.\n" # -- misc. targets -- .PHONY : dircheck dircheck: $(ec)$(call mkdircmd,$(util_obj_dir)) $(ec)$(call mkdircmd,$(test_obj_dir)) $(ec)$(call mkdircmd,$(sample_obj_dir)) $(ec)$(call mkdircmd,$(lib_obj_dir)) ifneq ($(lib_sobj_dir),$(lib_obj_dir)) $(ec)$(call mkdircmd,$(lib_sobj_dir)) endif $(ec)$(call mkdircmd,$(doxygen_output_dir)) $(ec)$(call mkdircmd,$(util_dir)) $(ec)$(call mkdircmd,$(test_dir)) $(ec)$(call mkdircmd,$(sample_dir)) $(ec)$(call mkdircmd,$(static_lib_dir)) $(ec)$(call mkdircmd,$(shared_lib_dir)) $(ec)$(call mkdircmd,$(package_dir)) $(ec)$(call mkdircmd,$(spec_dir)) $(ec)$(call mkdircmd,$(package_sources_dir)) $(ec)$(call mkdircmd,$(package_bin_dir)) $(ec)$(call mkdircmd,$(package_build_dir)) $(ec)$(call mkdircmd,$(package_rpms_dir)) $(ec)$(call mkdircmd,$(package_srpms_dir)) $(ec)$(call mkdircmd,$(package_debian_dir)) $(ec)$(call mkdircmd,$(package_ubuntu_dir)) # -- phony targets -- .PHONY : all all: libs allutils $(ec)$(gprintf) "" .PHONY : allutils allutils: status dircheck libs $(util_targets) $(ec)$(gprintf) "" .PHONY : test test: status dircheck $(flaim_static_lib) $(test_targets) ifndef netware_target $(ec)$(call runtest,basictest) endif .PHONY : debug debug: $(ec)$(gprintf) "" .PHONY : release release: $(ec)$(gprintf) "" .PHONY : flm_dbg_log flm_dbg_log: $(ec)$(gprintf) "" .PHONY : usegcc usegcc: $(ec)$(gprintf) "" .PHONY : 32bit 32bit: $(ec)$(gprintf) "" .PHONY : 64bit 64bit: $(ec)$(gprintf) "" .PHONY : win win: $(ec)$(gprintf) "" .PHONY : linux linux: $(ec)$(gprintf) "" .PHONY : solaris solaris: $(ec)$(gprintf) "" .PHONY : sparcgeneric sparcgeneric: $(ec)$(gprintf) "" .PHONY : osx osx: $(ec)$(gprintf) "" .PHONY : nlm nlm: $(ec)$(gprintf) "" .PHONY : ring0 ring0: $(ec)$(gprintf) "" .PHONY : verbose verbose: $(ec)$(gprintf) "" .PHONY : check check: $(ec)$(gprintf) "" .PHONY : TAGS TAGS: $(ec)$(gprintf) "" .PHONY : info info: $(ec)$(gprintf) "" .PHONY : ignore-local-mods ignore-local-mods: $(ec)$(gprintf) "" .PHONY : ilm ilm: $(ec)$(gprintf) "" .PHONY : installcheck installcheck: $(ec)$(gprintf) "" .PHONY : clean clean: ifeq ($(do_clean),1) $(ec)$(gprintf) "\n" $(ec)$(gprintf) "Cleaning $(target_path) ...\n" -$(ec)$(call rmdircmd,$(target_path)) -$(ec)$(call rmcmd,*.pch) $(ec)$(gprintf) "\n" endif .PHONY : distclean -$(ec)$(call rmcmd,*.pch) .PHONY : mostlyclean mostlyclean : clean $(ec)$(gprintf) "" .PHONY : maintainer-clean maintainer-clean: -$(ec)$(call rmdircmd,$(build_output_dir)) -$(ec)$(call rmcmd,*.pch) .PHONY : status status: $(ec)$(gprintf) "===============================================================================\n" $(ec)$(gprintf) "SVN Revision.................... $(svn_revision)\n" $(ec)$(gprintf) "Host Operating System Family.... $(host_os_family)\n" $(ec)$(gprintf) "Top Directory................... $(call ppath,$(topdir))\n" $(ec)$(gprintf) "Target Operating System Family.. $(target_os_family)\n" $(ec)$(gprintf) "Target Processor Family......... $(target_processor_family)\n" $(ec)$(gprintf) "Target Word Size................ $(target_word_size)\n" $(ec)$(gprintf) "Target Build Type............... $(target_build_type)\n" $(ec)$(gprintf) "Target Path..................... $(call ppath,$(target_path))\n" $(ec)$(gprintf) "Toolkit Path.................... $(call ppath,$(ftk_dir))\n" $(ec)$(gprintf) "Install Prefix.................. $(call ppath,$(install_prefix))\n" $(ec)$(gprintf) "Compiler........................ $(call ppath,$(compiler))\n" $(ec)$(gprintf) "Librarian....................... $(call ppath,$(libr))\n" $(ec)$(gprintf) "Defines......................... $(strip $(ccdefs))\n" $(ec)$(gprintf) "===============================================================================\n" .PHONY : doxyfile doxyfile: dircheck $(ec)$(gprintf) "PROJECT_NAME = \"$(project_display_name)\"\n" > $(doxyfile) $(ec)$(gprintf) "PROJECT_NUMBER = \"$(version)\"\n" >> $(doxyfile) $(ec)$(gprintf) "OUTPUT_DIRECTORY = $(doxygen_output_dir)\n" >> $(doxyfile) $(ec)$(gprintf) "CREATE_SUBDIRS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "OUTPUT_LANGUAGE = English\n" >> $(doxyfile) $(ec)$(gprintf) "USE_WINDOWS_ENCODING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "BRIEF_MEMBER_DESC = YES\n" >> $(doxyfile) $(ec)$(gprintf) "REPEAT_BRIEF = YES\n" >> $(doxyfile) $(ec)$(gprintf) "ABBREVIATE_BRIEF = \"The $(dollar)name class\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " \"The $(dollar)name widget\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " \"The $(dollar)name file\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " is $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " provides $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " specifies $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " contains $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " represents $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " a $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " an $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " the\n" >> $(doxyfile) $(ec)$(gprintf) "ALWAYS_DETAILED_SEC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_INHERITED_MEMB = NO\n" >> $(doxyfile) $(ec)$(gprintf) "FULL_PATH_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_FROM_PATH = \"\"\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_FROM_INC_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "SHORT_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "JAVADOC_AUTOBRIEF = YES\n" >> $(doxyfile) $(ec)$(gprintf) "MULTILINE_CPP_IS_BRIEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DETAILS_AT_TOP = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INHERIT_DOCS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SEPARATE_MEMBER_PAGES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TAB_SIZE = 3\n" >> $(doxyfile) $(ec)$(gprintf) "ALIASES = \n" >> $(doxyfile) $(ec)$(gprintf) "OPTIMIZE_OUTPUT_FOR_C = NO\n" >> $(doxyfile) $(ec)$(gprintf) "OPTIMIZE_OUTPUT_JAVA = NO\n" >> $(doxyfile) $(ec)$(gprintf) "BUILTIN_STL_SUPPORT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DISTRIBUTE_GROUP_DOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SUBGROUPING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_ALL = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_PRIVATE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_STATIC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_LOCAL_CLASSES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_LOCAL_METHODS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_MEMBERS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_CLASSES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_FRIEND_COMPOUNDS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_IN_BODY_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INTERNAL_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CASE_SENSE_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_SCOPE_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_INCLUDE_FILES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_INFO = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_MEMBER_DOCS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_BRIEF_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_BY_SCOPE_NAME = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TODOLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TESTLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_BUGLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_DEPRECATEDLIST= YES\n" >> $(doxyfile) $(ec)$(gprintf) "ENABLED_SECTIONS = \n" >> $(doxyfile) $(ec)$(gprintf) "MAX_INITIALIZER_LINES = 30\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_USED_FILES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_DIRECTORIES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "FILE_VERSION_FILTER = \n" >> $(doxyfile) $(ec)$(gprintf) "QUIET = NO\n" >> $(doxyfile) $(ec)$(gprintf) "WARNINGS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_IF_UNDOCUMENTED = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_IF_DOC_ERROR = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_NO_PARAMDOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_FORMAT = \"$(dollar)file:$(dollar)line: $(dollar)text\"\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_LOGFILE = \n" >> $(doxyfile) $(ec)$(gprintf) "INPUT = src/flaim.h $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " $(ftk_src_dir)/ftk.h\n" >> $(doxyfile) $(ec)$(gprintf) "FILE_PATTERNS = *.h\n" >> $(doxyfile) $(ec)$(gprintf) "RECURSIVE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE = \n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE_SYMLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_PATTERNS = *\n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_RECURSIVE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "IMAGE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "INPUT_FILTER = \n" >> $(doxyfile) $(ec)$(gprintf) "FILTER_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "FILTER_SOURCE_FILES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SOURCE_BROWSER = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_SOURCES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_CODE_COMMENTS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "REFERENCED_BY_RELATION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "REFERENCES_RELATION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "USE_HTAGS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "VERBATIM_HEADERS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "ALPHABETICAL_INDEX = YES\n" >> $(doxyfile) $(ec)$(gprintf) "COLS_IN_ALPHA_INDEX = 5\n" >> $(doxyfile) $(ec)$(gprintf) "IGNORE_PREFIX = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_HTML = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_OUTPUT = html\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_FILE_EXTENSION = .html\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_HEADER = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_FOOTER = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_STYLESHEET = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_ALIGN_MEMBERS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_HTMLHELP = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CHM_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "HHC_LOCATION = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_CHI = NO\n" >> $(doxyfile) $(ec)$(gprintf) "BINARY_TOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TOC_EXPAND = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DISABLE_INDEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "ENUM_VALUES_PER_LINE = 4\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TREEVIEW = YES\n" >> $(doxyfile) $(ec)$(gprintf) "TREEVIEW_WIDTH = 250\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_OUTPUT = latex\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_CMD_NAME = latex\n" >> $(doxyfile) $(ec)$(gprintf) "MAKEINDEX_CMD_NAME = makeindex\n" >> $(doxyfile) $(ec)$(gprintf) "COMPACT_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PAPER_TYPE = a4wide\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRA_PACKAGES = \n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_HEADER = \n" >> $(doxyfile) $(ec)$(gprintf) "PDF_HYPERLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "USE_PDFLATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_BATCHMODE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_HIDE_INDICES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_RTF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_OUTPUT = rtf\n" >> $(doxyfile) $(ec)$(gprintf) "COMPACT_RTF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_HYPERLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_STYLESHEET_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "RTF_EXTENSIONS_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_MAN = NO\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_OUTPUT = man\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_EXTENSION = .3\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_LINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_XML = NO\n" >> $(doxyfile) $(ec)$(gprintf) "XML_OUTPUT = xml\n" >> $(doxyfile) $(ec)$(gprintf) "XML_SCHEMA = \n" >> $(doxyfile) $(ec)$(gprintf) "XML_DTD = \n" >> $(doxyfile) $(ec)$(gprintf) "XML_PROGRAMLISTING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_AUTOGEN_DEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_PERLMOD = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_PRETTY = YES\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_MAKEVAR_PREFIX = \n" >> $(doxyfile) $(ec)$(gprintf) "ENABLE_PREPROCESSING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "MACRO_EXPANSION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXPAND_ONLY_PREDEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SEARCH_INCLUDES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_FILE_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "PREDEFINED = \n" >> $(doxyfile) $(ec)$(gprintf) "EXPAND_AS_DEFINED = \n" >> $(doxyfile) $(ec)$(gprintf) "SKIP_FUNCTION_MACROS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "TAGFILES = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TAGFILE = \n" >> $(doxyfile) $(ec)$(gprintf) "ALLEXTERNALS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTERNAL_GROUPS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "PERL_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "CLASS_DIAGRAMS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_RELATIONS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HAVE_DOT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CLASS_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "COLLABORATION_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GROUP_GRAPHS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "UML_LOOK = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TEMPLATE_RELATIONS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDED_BY_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "CALL_GRAPH = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GRAPHICAL_HIERARCHY = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DIRECTORY_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_IMAGE_FORMAT = png\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "DOTFILE_DIRS = \n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_WIDTH = 1024\n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_HEIGHT = 1024\n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_DEPTH = 1000\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_TRANSPARENT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_MULTI_TARGETS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_LEGEND = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_CLEANUP = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SEARCHENGINE = NO\n" >> $(doxyfile) libflaim-4.9.966/COPYING0000644000175000017500000004313310510774540016124 0ustar ahodgkinsonahodgkinson GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libflaim-4.9.966/COPYRIGHT0000644000175000017500000000440610510774540016364 0ustar ahodgkinsonahodgkinsonFLAIM Database Engine Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, contact Novell, Inc. To contact Novell about this file by physical or electronic mail, you may find current contact information at www.novell.com On a Debian system, a copy of the GNU GPL is installed in /usr/share/common-licenses/GPL-2 ------------------------------------------------------------------------------ svn2cl.xsl Copyright (C) 2004, 2005, 2006 Arthur de Jong. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libflaim-4.9.966/docs/0000755000175000017500000000000010510774540016015 5ustar ahodgkinsonahodgkinsonlibflaim-4.9.966/docs/introduction_to_flaim_4.doc0000644000175000017500000076700010510774540023334 0ustar ahodgkinsonahodgkinsonÐÏࡱá;þÿ ôþÿÿÿ€‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿþÿÿÿ þÿÿÿþÿÿÿ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ƒRoot Entryÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ ÿÿÿÿ ÀFMicrosoft Word-Dokument MSWordDocWord.Document.8ô9²qþÿà…ŸòùOh«‘+'³Ù0$ p x „ œ ¨´Ìäð éý131@ö€|u@0&× Â@>…z'Â@žÎ­jÂAndy HodgkinsonAndy HodgkinsonNormal[ [bñÿbDefault$a$1$*$A$/B*OJQJCJmH sH PJnHÿ^JaJ_HÿtHÿnn Heading 15@& & Fdh„°^„°„]„„Pþ`„Pþ¤X¤<$OJQJCJ5KHhh Heading 24@& & F Æ€„¨^„¨„]„„Àý`„Àý¤h¤hOJQJCJ5dd Heading 3/@& & F„ ^„ „]„„0ý`„0ý¤h¤h$OJQJCJ5T1T Heading 4'@& & F1$„˜^„˜„]„„ ü`„ üCJnH `` Heading 5,@& & F„ ^„ „]„„ü`„ü¤h¤hOJQJCJ5`` Heading 6,@& & F„ˆ ^„ˆ „]„„€û`„€û¤h¤hOJQJCJ5XX Heading 7,@& & F„^„„]„„ðú`„ðú¤ð¤<OJQJ\\ Heading 8,@& & F„ ^„ „]„„`ú`„`ú¤ð¤< OJQJ6b b Heading 9, @& & F„0^„0„]„„Ðù`„Ðù¤ð¤<OJQJCJ65BA@òÿ¡BAbsatz-Standardschriftart.þòÿñ. WW8Num2z0 OJQJCJ.þòÿ. WW8Num3z0 OJQJCJ.þòÿ. WW8Num4z0 OJQJCJ*þòÿ!* WW8Num5z0OJQJ*þòÿ1* WW8Num6z0OJQJ*þòÿA* WW8Num7z0OJQJ*þòÿQ* WW8Num8z0OJQJ*þòÿa* WW8Num9z0OJQJ,þòÿq, WW8Num10z0OJQJ,þòÿ, WW8Num12z0OJQJ,þòÿ‘, WW8Num13z0OJQJ,þòÿ¡, WW8Num14z0OJQJ,þòÿ±, WW8Num15z0OJQJ,þòÿÁ, WW8Num16z0OJQJ,þòÿÑ, WW8Num17z0OJQJ,þòÿá, WW8Num18z0OJQJ,þòÿñ, WW8Num19z0OJQJ,þòÿ, WW8Num20z0OJQJ,þòÿ, WW8Num21z0OJQJ,þòÿ!, WW8Num23z0OJQJ,þòÿ1, WW8Num24z0OJQJ,þòÿA, WW8Num25z0OJQJ,þòÿQ, WW8Num26z0OJQJBþòÿaBAbsatz-Standardschriftart*þòÿq* WW8Num1z0OJQJ,þòÿ, WW8Num22z0OJQJ0þòÿ‘0 WW8Num27z0 OJQJCJ,þòÿ¡, WW8Num28z0OJQJ,þòÿ±, WW8Num29z0OJQJ,þòÿÁ, WW8Num31z0OJQJ,þòÿÑ, WW8Num32z0OJQJ,þòÿá, WW8Num34z0OJQJ,þòÿñ, WW8Num36z0OJQJ,þòÿ, WW8Num38z0OJQJ,þòÿ, WW8Num40z0OJQJ,þòÿ!, WW8Num41z0OJQJ,þòÿ1, WW8Num42z0OJQJ,þòÿA, WW8Num44z0OJQJ,þòÿQ, WW8Num47z0OJQJ,þòÿa, WW8Num48z0OJQJ,þòÿq, WW8Num49z0OJQJ,þòÿ, WW8Num50z0OJQJ0þòÿ‘0 WW8NumSt42z0OJQJ<þòÿ¡<Default Paragraph Font&)¢±& Page Number.BÂ. Text body <¤¤x /ÁÒ List=^J@þâ@Caption >¤x¤x $CJ6^JaJ]&þò&Index? $^JFþÂFHeading @¤ð¤x$OJQJCJPJ^JaJFþF Normal IndentA„^„„]„„`„66 Contents 1 B¤x¤x;5BþB TOA Heading C¤x¤OJQJCJ5XþÁBXBody Text First IndentD„^„„]„„Ò`„Ò>þ1R>Head4!E & F„^„„]„„`„NþbN List Bullet 2F„^„„]„„`„OJQJVþrV List Continue"G„h^„h„]„„`„¤¤xOJQJ,‚,Header H ÆàÀ!, ’,Footer I ÆàÀ!DD Contents 2J„È^„È„]„„`„:DD Contents 3K„^„„]„„`„6DD Contents 4L„X^„X„]„„`„CJDD Contents 5M„ ^„ „]„„`„CJDD Contents 6N„è^„è„]„„`„CJDD Contents 7O„°^„°„]„„`„CJDD Contents 8P„x^„x„]„„`„CJDD Contents 9Q„@^„@„]„„`„CJ4>24TitleR$a$OJQJCJ58JÂ8SubtitleS$a$CJ6aJ](þÁB(BylineTOJQJ6þR6 TextExampleU OJQJCJ@þQ@Head 6!V & F„^„„]„„`„JþrJLegal 1W1$„^„„]„„`„ OJQJCJJþñ‚J Contents 10"X Æô& „ó ^„ó „]„„`„4þ’4Table ContentsY $Dþ‘¢D Table Heading Z$a$ $ 65]\ö  ˆ¨ÿÿÿÿ7¨ÿÿÿÿr¨ÿÿÿÿ©¨ÿÿÿÿà¨ÿÿÿÿ©ÿÿÿÿttttttttttttttttttttttúúúü®:.AÖh©¢Öª&Ø(½”ž¸BÞ è†ù`‹ŒŽ‘’“”•–—˜™š›âNò`|ª"†&È):.~4 ;DKVÖg\sBŒ®™Æ¤J³,ÔPï†â7pE fØ›ظìÐ@ÙVà’ûzh=à[8y ¸· Ð†Ý,àãŠí÷Øü’þöÿºF¼PœžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ ‡ ÿ€elpëòöû!ÿ€!ÿ€ÿÿ _Ref512928956]Ú‡iÚ‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„°„Pþư„@„ÀýÆ@.„Є0ýÆÐ..„`„ üÆ`... „ð„üÆð .... „€„€ûÆ€ ..... „„ðúÆ ...... „ „`úÆ ....... „0„ÐùÆ0........ „h„˜þÆhOJQJCJ·ð „h„˜þÆhOJQJCJ·ð „h„˜þÆhOJQJCJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„p„˜þÆp.„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„h„˜þÆhOJQJ·ð„°„Pþư„@„ÀýÆ@.„Є0ýÆÐ..„`„ üÆ`... „ð„üÆð .... „€„€ûÆ€ ..... „„ðúÆ ...... „ „`úÆ ....... „0„ÐùÆ0........ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿOutlineWW8Num2WW8Num3WW8Num4WW8Num5WW8Num6WW8Num7WW8Num8WW8Num9WW8Num10WW8Num11WW8Num12WW8Num13WW8Num14WW8Num15WW8Num16WW8Num17WW8Num18WW8Num19WW8Num20WW8Num21WW8Num23WW8Num24WW8Num25WW8Num26Outlineÿ@#†0†?†H†^†_†c†k†{†›†œ† †¨†¸†ç†è†ì†ô†‡ ‡‡ ˆ""""¶""""¶""""¶""""¶ ˆP GTimes New Roman5Symbol3&Arial5DialogYWP IconicSymbolsASymbol5TahomaOLucida Sans Unicode5Tahoma?4Courier NewBÐhÉZfF lf†0kf†ƒ'0€1ì¥ÂM ¿0Caolan80 R©‡ûÿÿ‹ÿÿœ8ÿÿl000d”°DŒÐÄE)B”ª(Ò"ô z(‡+X0)Ú¨$ ´&Æ|(´ÙIntroduction to FLAIM 4 An overview of the FLAIM technology and related concepts "Novell, Inc. makes no representations or warranties with respect to the contents or use of this documentation, and specifically disclaims any express or implied warranties of merchantability or fitness for any particular purpose. Further, Novell, Inc. reserves the right to revise this publication and to make changes to its content, at any time, without obligation to notify any person or entity of such revisions or changes. Copyright © 1997-2002,2006 Novell, Inc. All rights reserved. THIS DOCUMENTATION MAY NOT BE REVISED OR MODIFIED WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC.  THE DOCUMENTATION IS PROVIDED "AS IS."  IN NO EVENT SHALL NOVELL OR THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE DOCUMENTATION OR THE USE OR OTHER DEALINGS IN THE DOCUMENTATION." Table of Contents  TOC \o "1-9" \t "Heading 9;9;Heading 8;8;Heading 7;7;Heading 6;6;Heading 5;5;Heading 4;4;Heading 3;3;Heading 2;2;Heading 1;1;Heading 1;1;Legal 1;1;Heading 2;2;Heading 3;3;Head4;3;Heading 4;4;Heading 5;5;Heading 6;6;Head 6;6;Heading 7;7;Heading 8;8;Heading 9;9" 1 Introduction 1 2 Summary of FLAIM Features 1 2.1 Fields and Records 1 2.2 Containers 1 2.3 Clustering 1 2.4 Indexing 1 2.5 Dynamic Dictionary 2 2.6 Query Capabilities 2 2.7 Read and Update Operations 3 2.8 Transactions 3 2.9 Roll-forward Logging 3 2.10 Database Reliability and Recovery 3 2.11 Concurrency 4 2.12 Caching 4 2.13 Optimized Disk Reading/Writing 4 2.14 Database Validation and Repair 4 2.15 Backup/Restore 5 2.16 Database Monitoring, Statistics Collection 5 2.17 Utilities 6 2.18 Checksumming 6 2.19 Database Size 6 2.20 Testing 6 2.21 Cross Platform 7 2.22 Application Programming Interfaces 7 3 Concepts 8 3.1 Fields 8 3.1.1 Field Identifier 8 3.1.2 Field Data Type 8 3.1.3 Field Value 8 3.2 Records 8 3.2.1 Hierarchical Structure 9 3.2.2 Other Features of Records 9 3.2.2.1 Repeating Fields 9 3.2.2.2 Non-Occurring Fields 9 3.2.2.3 Flexible Field Ordering 10 3.3 Containers 10 3.3.1 DRN (Data Record Number) 10 3.3.2 Predefined Containers 10 3.3.2.1 Dictionary Container 10 3.3.2.2 Tracker Container 10 3.3.2.3 Default Data Container 11 3.4 Indexes 11 3.4.1 Background Indexing 11 3.4.2 Suspending Indexes 11 3.4.3 Resuming Indexes 11 3.5 Dictionary 12 3.5.1 Dictionary Definition Identifier 12 3.5.2 Registered Fields 12 3.5.3 Unregistered Fields 13 3.5.4 Reserved Fields 13 3.5.5 Definition Record Types 13 3.5.6 Field Definitions 14 3.5.6.1.1 Data Types 14 3.5.6.1.1.1 Number 14 3.5.6.1.1.2 Text 14 3.5.6.1.1.3 Binary 14 3.5.6.1.1.4 Context 14 3.5.6.1.1.5 BLOB 15 3.5.7 Container Definitions 15 3.5.8 Index Definitions 15 3.5.8.1 Index Types 16 3.5.8.1.1 Single Field Index 16 3.5.8.1.2 Compound (Multi-Field) Index 16 3.5.8.2 Index Options 16 3.5.8.2.1 Field Paths 16 3.5.8.2.2 Unique 17 3.5.8.2.3 International Languages 17 3.5.8.2.4 Each Word 17 3.5.8.2.5 Substring 17 3.5.8.2.6 Case-Insensitive Collation 17 3.5.8.2.7 Post Case-Sensitive Collation 17 3.5.8.2.8 Field Identifier Indexing 18 3.5.8.3 Using an Index to Provide Sorted Result Sets 18 3.6 Querying the Database 18 3.6.1 Cursor 18 3.6.1.1 Selection Criteria 18 3.6.1.1.1 Operators 18 3.6.1.1.2 Operands 19 3.6.2 Evaluating Arbitrarily Structured Records 19 3.6.2.1 Missing Fields 19 3.6.2.1.1 Field Existence Predicate 19 3.6.2.1.2 Unknown Values 19 3.6.2.2 Repeating Fields 20 3.6.2.2.1 Adapting Operators for Multi-Valued Operands 20 3.6.2.2.2 Field Paths 20 3.6.2.3 Partial Evaluation 20 3.6.3 Result Sets 21 3.7 Database Files 21 3.7.1 Control File (xxx.db) 21 3.7.2 Lock File 21 3.7.3 Data Files 22 3.7.3.1 Naming Convention 22 3.7.4 Rollback Log Files 22 3.7.4.1 Naming Convention 23 3.7.5 Maximum Data File and Rollback File Sizes 23 3.7.6 Roll Forward Log Files 24 3.7.6.1 Naming Convention 24 3.8 Data Integrity and Transactions 24 3.8.1 Checkpoint 24 3.8.2 Transactions 25 3.8.2.1 Update Transaction 25 3.8.2.2 Read Transaction 25 3.8.2.3 Transaction Failures 25 3.8.2.3.1 Rollback Logging 25 3.8.2.3.2 Roll-Forward Logging 26 3.8.2.3.3 Recovery 26 3.8.3 Concurrency Control 27 3.8.3.1 Locking 27 3.8.3.1.1 Lock Wait Period 27 3.8.3.1.2 Deadlock Prevention 27 3.8.3.2 Many Readers / One Writer 27 3.8.4 Backup and Restore 28 3.8.4.1 Backup 28 3.8.4.1.1 Full Backup 28 3.8.4.1.2 Incremental Backup 28 3.8.4.1.3 Continuous Backup 29 3.8.4.2 Restore 29 3.9 Caching 29 3.9.1 Block Cache 29 3.9.2 Record Cache 29 3.9.3 Cache Poisoning 30 3.9.4 Cache Performance Measurements 30 3.9.5 Configuration 31 3.9.5.1 Hard Limit 31 3.9.5.2 Dynamic Limit 31 3.9.5.3 Distribution 31 3.9.5.4 Extended Server Memory 32 3.9.5.5 Issues 32 3.9.6 Cache Cleanup 32 3.10 Database Maintenance 32 3.10.1 Run-Time Data Verification 33 3.10.1.1 Block Checksumming 33 3.10.1.2 Block Sanity Checking 33 3.10.1.2.1 No Sanity Checking 33 3.10.1.2.2 Basic Sanity Checking 33 3.10.1.2.3 Intermediate Sanity Checking 33 3.10.1.2.4 Extensive Sanity Checking 33 3.10.2 Database Check 33 3.10.2.1 Physical Check 34 3.10.2.2 Index Check 34 3.10.3 Database Rebuild (Salvage) 34 3.10.4 Space Reclamation 34 4 Appendix A  Dictionary Definition Records 35 4.1 Field Definition Record 35 4.2 Container Definition Record 35 4.3 Index Definition Record 35 5 Appendix B  International Languages 37 6 Appendix C  FLAIM Utilities 38 6.1 DBShell 38 6.2 CheckDB 39 6.3 Rebuild 39 6.4 View 39 6.5 RFLView 39 7 Appendix D  Building the FLAIM Libraries 39 8 Appendix E  Sample Code 39 9 Document Change Log 39 Introduction FLAIM is a fast, flexible, reliable, cross-platform database engine. Even though FLAIM provides many traditional database features, it was conceived with a broader view toward the greater flexibility and adaptability that is offered by an XML-like data model. FLAIM is not new; various products have used it for nearly 15 years. For instance, Novell s scalable, reliable directory and collaboration products, eDirectory and GroupWise, both use FLAIM as the data store, with user licenses totaling well into the hundreds of millions. Summary of FLAIM Features The following is a brief summary of the features available in FLAIM. Fields and Records Variable length fields, text and binary fields up to 64K per field. All fields are tagged - record is self-describing - no schema for record structure - structure is embedded in each record - XML-like. Nested sub-records, N-levels deep. Repeating fields and repeating sub-records. No storage used for omitted fields or to pad text fields to a fixed length. Unregistered fields (can store fields that are not defined in the dictionary). Data types: text, numeric, binary, context, blob. Text types: UNICODE. Containers Allow user to partition data records physically and/or logically. Multiple containers per database. Clustering Multiple record types per container. Multiple record types may be inserted into a single block. Indexing Compound indexes, component fields may be any FLAIM data type except BLOB. Optional and/or required fields in compound indexes (key not generated if required field missing). Existence indexes (indexes the presence of a field versus the field s content). Case insensitive and case sensitive collation. Case insensitive collation with case preserved (post indexes). White space compression, other special indexing rules. Cross-record type indexes. Counter indexes. Sub-string indexing. Each-word indexing. Unique indexes. Support for many international languages and collating sequences, including Arabic, Hebrew, Asian (Japanese, Korean, Chinese), etc. Each index in a database can have its own international language. Fast updating of large reference sets. Keys up to 640 bytes long, key truncation supported. Multiple indexes per container and/or per record type. Left-end compression of index keys. Compression of index reference sets. APIs for reading of indexes directly (keys and references). Dynamically updated when records are added, modified, or deleted. Background indexing threads. Suspend, resume indexing. Can take indexes  offline. Dynamic Dictionary Add, modify, drop indexes, containers, field definitions. Comment fields allowed in ALL dictionary records. Query Capabilities Rich set of query expression operators: Comparison operators (equal, not equal, less than, less than or equal, greater than, greater than or equal, match, match begin, contains, match end). Text comparison operators include wild card matching. Arithmetic operators (unary minus, multiply, divide, mod, plus, minus). Logical operators (not, and, or). Parentheses (used to alter normal operator precedence). Simple, powerful mechanism for building up query expression programmatically: Expression does not have to be passed in as a string. Allows program to add operators, operands, and parentheses to the expression in infix order. Allows program to easily use program variables which contain comparison values or field names. Allows use of values that are not easily formatted into a string (such as binary). Advanced query optimization (FLAIM will automatically select a indexes, etc. based on cost estimation). Index specification - application may specify an index instead of letting FLAIM choose one. Embedded Predicates. Powerful navigational calls for retrieving and browsing through query results (retrieve first, last, next, previous, and current records). Only records which satisfy query expression are retrieved. Read and Update Operations Reading data records directly from containers (including dictionary container). Reading of indexes directly (keys and references). Advanced querying capabilities. Navigating forward and backward through containers and indexes. Update operations are: add, modify, and delete (including dictionary records). Transactions Transaction begin, commit, abort. Use of rollback log for transaction abort and for recovery after a crash. Transaction types: Update. Update, read, and query operations allowed. Read. Only read and query operations allowed. Read transactions provide a read consistent snapshot of the database as of the point in time the transaction is started. Automatic. Single update operations may be told to automatically begin and end (commit or abort) a transaction if no transaction has been explicitly started. Automatic rollback of failed transactions (due to program aborts or CPU failures). Periodic checkpoints to minimize recovery time after a system crash. No limit on size of update transactions. ACID principles supported: Atomicity, Consistency, Isolation, Durability. Roll-forward Logging Use of roll-forward log to minimize data that has to be written to commit a transaction. Roll-forward log is used in automatic recovery after a crash. Transactions that were committed since the last checkpoint will be  redone. Multiple roll-forward log files may be used to support  continuous backup feature. Files are numbered sequentially and are also identified with serial numbers to guarantee proper sequencing - no spoofing. Up to 4 billion log files supported - capacity is practically unlimited. Option to use only a single roll-forward log file - for applications that do not care about  continuous backup. Roll-forward log files may be stored on a separate disk from rest of database. Minimal transaction logging. Only deltas logged for record modifies. Only DRNs logged for record deletes. Aborted transactions can be logged for debug purposes, but default is to not log them. Support for logging of  application data. (This feature is used in SMI to log stream files). Database Reliability and Recovery Automatic database recovery after a system crash. Rollback log is used to roll database back to last consistent checkpointed state. Then roll-forward log is used to  redo transactions that were committed after the last checkpoint. Recovery is idempotent. That is, if we crash during recovery, it will be resumed when the database is subsequently opened. Reliability has been tested using an automated  pull-the-plug test, which randomly cycles the power on the server during high volume updates to test database recovery. Thousands of  pull-the-plug iterations have been performed on both Windows and NetWare. Handling of disk-full conditions and other disk errors. Database attempts to  stall new update transactions until disk-full condition is resolved - without requiring a shut down. Protection against media failure. Customers can take hot backups and put roll-forward logs on a different volume than the database. If they do these things, two simultaneous disk failures would be required to lose any data. Concurrency One writer, multiple readers. Readers don't block writers (they NEVER lock items in the database). Writers don't block readers. Read consistency for readers (readers get a stable consistent snapshot of the database). Rollback log is used to provide block multi-versioning. Uncommitted data is not visible to other transactions. Caching Block cache, shared by all threads in a process - up to 4 GB on 32 bit machines, much more on 64 bit machines. Record cache. Cache poisoning prevention Cache statistics available - hits, faults, hit looks, fault looks. Optimized Disk Reading/Writing Direct IO - bypass file system cache. Asynchronous writes. Sorting of blocks to optimize disk head movements. Also attempt to coalesce adjacent dirty blocks into larger write buffers for improved performance. Will fill write buffer with non-dirty blocks that are already in cache if it results in a more optimal write. Database Validation and Repair Routine for checking physical structure of database. Links between Blocks verified, B-Tree structure verified, block checksums verified, field and record structures verified, index keys and reference sets verified, data in fields verified. Routine for checking indexes. Ensures that all keys that ought to be in an index are, in fact, in the indexes, and that no extra keys are in the indexes. In-line repair of index problems is allowed during index checking. Extra keys will be automatically deleted. Missing keys will be added. Routine for repairing database. Can rebuild from a totally trashed file - or even a zero length file! Callback facility in all functions to report progress. Allows application to display progress and cancel out if desired. Corruptions are also reported via the callback so that an application can create a detailed log of corruptions found if desired. Backup/Restore Hot backup. Backups can be performed without taking the database off-line and without stopping updates. Continuous backup. Roll-forward logs can be managed in a way that allows them to serve as a  continuous backup of the database. No committed transaction will be lost. Incremental backups. This minimizes what must be backed up - only blocks changed since last backup. Capture of output during backup using callbacks. This allows an application to capture backup output and stream it directly to tape or other backup medium without having to stage it to an intermediate disk file first. An application could even choose to send backup data across a network connection to be stored on a remote device. FLAIM uses double buffering so that an output device can be kept busy while FLAIM is fetching the next set of blocks to backup. This would help prevent a streaming tape device from stalling, resulting in dramatically improved backup throughput. All blocks in backup include a checksum to ensure that data is reliable when restored. Simple block compression used to minimize size of backup. Use of serial numbers in roll-forward log files and backups to ensure  identifiability when restoring. Database also has a serial number. Restore from full backup, multiple incremental backups, and/or roll-forward logs - all in one call. Streaming input during restore using callbacks. Allows an application to restore backed up data directly from tape or other backup medium without having to stage backed up data to an intermediate file first. An application could also use this to restore directly from a remote location by bringing the data over a network connection. FLAIM uses double buffering so that an input device can be kept busy while FLAIM is writing out blocks from a backup to the database. This would help prevent a streaming tape device from stalling, resulting in dramatically improved restore throughput. Status callbacks during backup/restore so that application can report progress and/or abort the backup or restore operation. Partial restore supported. An application has the option of stopping a restore operation after either: 1) a full backup or incremental has been restored, or 2) after any transaction in the roll-forward log has been redone. Database Monitoring, Statistics Collection Ability to collect detailed statistics on disk I/O activity and transaction activity. Ability to monitor cache utilization, including bytes used, number of blocks and records cached, cache hits, faults, etc. Ability to collect detailed information about queries - to see what indexes were used, how many keys were fetched, how many records were fetched, how many failed the criteria, etc. This allows analyzing of query efficiency and troubleshooting of query performance problems. Utilities Database checking utility (checkdb). Database rebuild utility (rebuild). Database browser/editor utility (dbshell). Can retrieve, add, modify, and delete records, perform transactions, perform queries, etc. Low-level viewers: Physical structure viewer/editor (view) and roll-forward log viewer/searcher (rflview). Text interface (TUI) for all platforms - supports colors, rudimentary windowing, keyboard access, and multiple screens. Have a common cross-platform abstraction for these services to hide platform specific details. All utilities build and work on all platforms and have the same look and feel. Checksumming Block checksum set on all blocks in the database when writing to disk. Block checksum verified when reading blocks from disk. Checksum used to automatically detect corruption. Database Size Database may grow up to 8 terabytes or 4 terabytes (depends on platform). Up to 4096 files may be created. Each file is limited to either ~2GB or ~4GB, depending on operating system limitations. Number of records up to 4 billion per container. Database grows as needed. No need to preallocate disk space. However, when extending files, it is more optimal to extend by a large amount than a small amount, so we typically extend a file by 8 MB at a time (on Windows and NetWare). Routine for re-claiming unused database blocks and log areas and returning to OS. Space may be reclaimed without taking database off-line. Benchmarks and comparisons show FLAIM database size to be smaller than other databases (25-40%). Database block size can be set on database creation to 4K or 8K. Sophisticated block splitting and block combining to maximize block utilization. Roughly 70% utilization in index blocks. Roughly 70-75% utilization in data blocks. Left end compression of index keys. Compression of index reference sets. Testing Automated testing randomly varies parameters and calls to aggressively test millions and millions of possible combinations of usage. Simulations involving a large variety of random combinations of operations and data. Multiple continuous runs of days and weeks on multiple machines for high volume concurrency testing. Automated power failure testing to test database reliability and recovery. Cross Platform Database file is binary portable to ALL supported platforms, no need for conversions when moving database file from platform to platform. Little endian format used for most internal integer values. Platforms: Linux (Novell SuSE, RedHat, Ubuntu, etc.), NetWare, Windows (NT, 2000, XP-64 bit), various flavors of UNIX (Solaris, AIX, Mac OS X). Source code is developed in C++ programming language (one source for all platforms), allowing FLAIM to easily build libraries for other platforms  64-bit Windows XP was built and ready to go in a few days. Operating System services are abstracted into common interfaces or C++ classes for upper layers of code so they don t have to worry about operating system differences. Code is maintained in a handful of files. Abstractions exist for disk I/O, memory management, semaphores and mutexes, and so forth. Application Programming Interfaces C++ Startup/Shutdown. FlmStartup, FlmShutdown. System management, configuration, monitoring. FlmConfig, FlmGetConfig. Cache management, configuration, information. FlmSetDynamicMemoryLimit, FlmSetHardMemoryLimit, FlmGetMemoryInfo. Statistics, monitoring. FlmGetStats, FlmDbGetLockInfo. Create, open, close. FlmDbCreate, FlmDbOpen, FlmDbClose. Copy, delete, rename. FlmDbCopy, FlmDbDelete, FlmDbRename. Backup, restore. FlmDbBackupBegin, FlmDbBackup, FlmDbBackupEnd, FlmBackupGetConfig, FlmDbRestore. Check, rebuild. FlmDbCheck, FlmDbRebuild. Locking, transaction, checkpointing. FlmDbLock, FlmDbUnlock, FlmDbTransBegin, FlmDbTransCommit, FlmDbTransAbort, FlmDbGetTransType, FlmDbGetTransId, FlmDbGetCommitCnt, FlmDbCheckpoint. Add, modify, delete records. FlmRecordAdd, FlmRecordModify, FlmRecordDelete, FlmReserveNextDrn. Index control. FlmIndexStatus, FlmIndexSuspend, FlmIndexResume. Queries. FlmCursorInit, FlmCursorFree, FlmCursorClone, FlmCursorConfig, FlmCursorAddField, FlmCursorAddOp, FlmCursorAddValue, FlmCursorFirst, FlmCursorLast, FlmCursorNext, FlmCursorPrev, FlmCursorCurrent, and others. Other read operations. FlmRecordRetrieve, FlmKeyRetrieve. Database configuration, information. FlmDbConfig, FlmDbGetConfig. Space reclamation. FlmDbReduceSize. Concepts This section presents various FLAIM features and concepts in detail. Fields The most basic unit of information in a FLAIM database is a field. A field is comprised of a field identifier (sometimes referred to as a label), a data type, and a value (or content). A field s identifier typically conveys the creator s intended meaning (or semantic) for the field. It provides the context for interpreting and clarifying the content. For example, city is the context for Denver (the content). One might consider context to be the question, and content to be its answer. Field Identifier  INHALT "Field Identifier" \l 3 In FLAIM, a field s identifier is a number with a corresponding name, both of which are assigned by the creator of the field. For performance reasons, FLAIM s APIs use the numeric identifier to reference a field. However, it is recognized that some applications may desire to use names instead of numbers. To support this method of access, FLAIM provides interfaces that allow an application to list names of fields that are defined in the database, as well as interfaces that map name identifiers to the corresponding numeric identifiers and vice versa. Field Data Type  INHALT "Field Data Type" \l 3 A field s data type (e.g. text, number, etc.) defines intrinsic characteristics that are applicable to the field s value. As such, a field s data type tells FLAIM how to store, index, validate, and otherwise manipulate the field s value. Field Value  INHALT "Field Value" \l 3 A field s value (content) is always variable length, regardless of the data type. When storing a field s value, whether in memory or on disk, only the actual space required is allocated. Although FLAIM stores information per field not normally found in traditional databases (including value length, field ID, etc.), great care has been taken to minimize the per-field overhead. Interestingly, and perhaps contrary to what might be supposed given this fact, FLAIM s overall data storage efficiency is superior to similar commercial products. This is primarily due to the fact that FLAIM does not allocate fixed size units for storing field values, thus wasting unused space. The savings in disk space translates directly into performance benefits, because it takes fewer disk hits to retrieve the same amount of data. Records Application data stored in a FLAIM database is organized into records. A record (or combination of records) in a database generally represents an object or a concept in the real world (a product, a customer, an employee, a business division, etc.). As such, a record consists of a collection of fields that represent attributes of the object. In FLAIM, there is no requirement that records conform to a pre-defined template or type. Traditional databases use schemas to define record  types or object classes. In such databases, a record type (or object class) is an abstraction or generalization about the collection of records in the database that represent the same  kind of real world object. A record type (or object class) typically defines the relevant attributes and/or behavior that are to be found in instances of that record type (or object class). For example, ýÿÿÿýÿÿÿýÿÿÿ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóþÿÿÿõþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿthe record type  person may specify that a  person record contains attributes of height, weight, hair color, phone number, etc. In contrast to databases that require all records to conform strictly to a record type definition, FLAIM supports the creation of arbitrarily structured records. In traditional databases, the creator of a record cannot add new attributes to a record or otherwise alter its makeup, because the makeup of the record is predetermined by a record type definition. The creator of a record is limited to assigning content (or values) to the defined attributes. No such constraints exist for arbitrarily structured records. The creator of an arbitrarily structured record is not only allowed to determine the contents of attributes within the record but is allowed to determine the structure of the record as well. Hierarchical Structure In a FLAIM record, a field can be placed subordinate to another field. The fields are then said to have a parent/child relationship. A field may have at most one parent field. Fields that have the same parent are said to be siblings. Each field in a FLAIM record is said to exist at some level in the record s hierarchy. The outermost level of a record is, by definition, level zero. Every record in a FLAIM database has one and only one field at level zero. Other Features of Records Besides the ability to create a hierarchical record, FLAIM also provides a number of other liberties in creating records that are not generally allowed in other databases. These are: Repeating Fields FLAIM places no implicit restriction on the number of times a field may be repeated at any given level within a record. Non-Occurring Fields FLAIM does not require that any particular field exist in a record. No space is consumed in the database for place holders for non-occurring fields. Flexible Field Ordering FLAIM does not require any particular ordering of fields. However, the order of the fields specified by the application is preserved. When a record is retrieved, the fields will ALWAYS be returned in the same order and organization in which they were stored. Containers Most databases provide some means for defining collections of records. For example, in relational databases, a collection of records is represented as a table. In FLAIM, records are organized into containers. Each container may hold a heterogeneous collection of records, meaning that a container may store many different types of records. Containers may be added to a database at any time. A container may also be dropped (deleted) from a database. DRN (Data Record Number)  INHALT "DRNs (Data Record Number)" \l 3 Within a container, records are uniquely identified by 32-bit DRNs (Data Record Numbers). All numbers from 1 through 4294967294 (0xFFFFFFFE) are valid candidate DRNs. Note that the numbers 0 (zero) and 4294967295 (0xFFFFFFFF) are not valid DRNs; they are reserved for special uses within FLAIM. Once assigned, any given record will always be associated with the same DRN. When creating a record, an application may assign the DRN, or it may choose to have FLAIM assign the DRN. In the latter case, FLAIM keeps track of the highest DRN ever assigned within a container (either by FLAIM or by an application) and assigns the next DRN following the highest DRN. Even if ALL of the records in a container are deleted, previously used DRNs are NOT reassigned by FLAIM. An application, however, may assign a DRN that has been previously used if the DRN is currently unused. Whether the DRN is assigned by FLAIM or by an application, FLAIM ensures that all records in a container have a unique DRN. Predefined Containers When a new database is created, three default containers are added automatically. These have special purposes and cannot be removed by the application. Dictionary Container Within every database, FLAIM maintains a local dictionary container that keeps track of all field, index, and user container definitions. The container number for the local dictionary is 32000 (defined in FLAIM.H as FLM_DICT_CONTAINER). Tracker Container The tracker container keeps track of miscellaneous information for index management. The container number of the tracker container is 32002 (defined in FLAIM.H as FLM_TRACKER_CONTAINER). Records in this container cannot be directly updated by an application, but are available for reading. Default Data Container The default data container is provided as a default place for storing user data. FLAIM makes no internal use of this container. The container number of the default data container is 32001 (defined in FLAIM.H as FLM_DATA_CONTAINER). Indexes All database applications retrieve records from the database. The task of finding the desired records can be accomplished by sequentially scanning a container until the desired records are located. However, in containers with large numbers of records, this may be extremely slow. Indexes are provided as a means for finding records more efficiently. In FLAIM, an index is associated with one or more containers and each container may have many indexes. Indexes may be created or dropped at any time. An index is essentially a set of keys that are arranged in a way that significantly speeds up the task of finding any particular key within the index. Index keys are constructed by extracting the contents of one or more fields from records. Each key in an index references the record or records from which it was constructed. Note that a key may reference more than one record; it is possible for multiple records to have field contents that result in identical keys in the index. It is also possible to define unique indexes where each key in the index is not permitted to reference more than one record. The list of references associated with a key is called the key s reference set. The reference set is a list of DRNs. Background Indexing FLAIM allows indexes to be added in the background. When this option is selected, a thread is created that scans records within the scope of the index being created. A small number of records is selected for each iteration of the background thread and each record s keys is added to the new index. Once all of the candidate records have been indexed, the new index will come on-line automatically and will be available for use in queries. Suspending Indexes FLAIM also allows indexes to be taken off line (suspended). This makes an index unavailable for use and the index to stop updating its keys when new records are added to the database. Suspending indexes can be useful during a batch load of records because the overhead of adding records is reduced. Resuming Indexes Of course, a suspended index can be resumed. When a resume of a suspended index is requested, FLAIM starts a background indexing thread to bring the index up-to-date. Only records that were added to the database after the index was suspended are scanned. All other records will be correctly represented in the index already. When the background thread completes its work, the index is brought back on-line automatically. Dictionary The overall design and logical structure of a database is often called the database schema. In FLAIM, database schemas are specified by a set of definition records, which are stored in a special type of container called a dictionary container. Thus, a dictionary is a repository of definitive and descriptive metadata (i.e.,  data about data ) that provides information about the overall design and logical structure of the database. It provides the information needed by FLAIM to properly store, retrieve, and index application data. Dictionary definition records may be constructed using the same methods that are used to construct user data records, but their specific structures, syntaxes, and semantics are predefined. Dictionary definition records can be dynamically added, modified, and deleted at run time (with some restrictions) using the same APIs that are used to add, modify, and delete user data records. Indexes, for example, may be dynamically added or deleted at run time simply by adding or deleting the appropriate index definition records. Dictionary definition records may be read using the same APIs used to read user data records. Dictionary Definition Identifier Every definition record within the dictionary is assigned an identifier, which consists of a number and a corresponding name. Because a dictionary is a container, definition records are assigned a DRN when they are added to the container (either by FLAIM or by the application). A definition record s assigned DRN becomes the definition record s numeric identifier. Numeric identifiers for dictionary definition records range between 1 and 31999, inclusive. A definition record s name is always assigned by the application. It is part of the syntax of the specific type of record being created (see Appendix A for a complete specification of dictionary definition syntaxes). A definition record s assigned numeric identifier is used in FLAIM s APIs to reference the defined item. For example, a field whose numeric identifier is 12 is referenced using the numeric value 12; an index whose numeric identifier is 15 is referenced using the numeric value 15; and so forth. Some applications may desire to use names instead of numbers. To support this method of access, FLAIM provides interfaces that allow an application to list names of items in the database (field names, index names, etc.), as well as interfaces that map a name identifier to the corresponding numeric identifier and vice versa. Registered Fields  INHALT "Registered Fields" \l 3 Registered fields are fields that are defined in the dictionary. Field definitions describe each registered field and include the field s identifier (name and number), as well as its data type. A registered field s numeric identifier must be a number between 1 and 31999. There are three primary reasons for using registered fields. The first is that registered fields are the ONLY fields that can be indexed in a FLAIM database. In order for FLAIM to be able to index a field, it must be assured that the field s data type will be consistent for all instances of the field that occur within the scope of the index. Thus, when adding or modifying records in the database, FLAIM checks each field in the record and, for fields that are registered, verifies that the field's data type is, in fact, the data type declared in the field s definition record in the dictionary. An error occurs if the field s data type does not match the declared data type. The second reason for using registered fields is that the storage overhead per field is smaller for registered fields than it is for unregistered fields. This is because the data type for registered fields is stored in the data dictionary; hence, the data type does not need to be stored with each instance of the field. The third reason for using registered fields is that only registered fields can be given a name identifier as well as a numeric identifier. Unregistered Fields  INHALT "Unregistered Fields" \l 3 Unregistered fields are fields that are NOT defined in the dictionary. A field identifier for an unregistered field consists only of a numeric identifier, there is no name. The only valid numeric identifiers for unregistered fields are 32769 to 65535. Unlike registered fields, unregistered fields do not have a predefined data type. In fact, every instance of an unregistered field with the same numeric identifier can actually be a different data type. Because of this freedom, FLAIM makes no attempt to validate data types of unregistered fields. This also means that unregistered fields cannot be indexed, because there is no guarantee that all instances of unregistered fields with the same numeric identifier will have the same data type throughout the database. Unregistered fields have additional overhead for storing the data type in each instance where the field is used. Reserved Fields  INHALT "Reserved Fields" \l 3 Reserved fields are fields whose numeric identifiers have been reserved for use in dictionary definition records. As such, these fields have pre-defined semantics that are intrinsically understood by FLAIM. All of the numbers in the range of 32000 through 32768 are reserved as identifiers for these fields. NOTE: Not all of these numbers are reserved as field identifiers. Some of the numbers are reserved for future use; some have uses other than as field identifiers. The main point to be understood is that this is a reserved range of identifiers. Definition Record Types The types of definition records that are available in FLAIM are: 1) field definitions, 2) container definitions, and 3) index definitions. Field Definitions  INHALT "Field Definitions" \l 3 Field definitions define fields that may be indexed. These fields are also called the database s registered fields. The field s name and data type are specified in the field definition. The field s data type tells FLAIM how a field s data is to be stored, used, converted, and collated (if used in an index). FLAIM also verifies that the data type of a field in a record matches the type specified in the field definition when an application submits a record to be added or modified. A field definition record may be added or modified at any time. When modifying a field definition record, only the field s name may be changed. It is illegal to change the field s data type. A field definition may only be deleted if FLAIM has verified that there are no instances of the field in the database. Data Types A field s data type defines intrinsic characteristics that are applicable to the field s value. As such, a field s data type tells FLAIM how to store, index, validate, and otherwise manipulate the field s value. One fundamental reason for FLAIM to intrinsically define a data type is so that it can perform some intrinsic operation on that type of data. At present, the only operations that FLAIM performs on data are indexing and querying. These operations require FLAIM to know how to do comparisons on values (equal, less-than, greater-than, etc.). Currently, there are five fundamental data types in FLAIM. Number This data type encompasses 32-bit signed and unsigned integers. Text The text data type provides support for 16-bit Unicode characters. Up to 64K of text data may be stored in a text field. Binary A binary field is used when storing raw binary data. FLAIM makes no attempt to interpret binary data. An application can index fields of this type, but sorting is done by performing a byte-for-byte comparison of the data. Up to 64K of data may be stored in a binary field. Context A context field is used as a contextual placeholder. Fields that have subordinates typically (but not necessarily) have a data type of context. BLOB The BLOB data type is used to store potentially large data objects. FLAIM does not own or manage BLOBs. Rather, a BLOB field simply references a file in the file system. Container Definitions Container definitions are used to create additional containers for application data. At present, the only information specified in a container definition is the container s name. A container definition record may be added, modified, or deleted at any time. The only thing that can be changed in a container definition is the container s name. A container definition record may not be deleted if there are container-specific indexes still defined on the container. When a container is deleted, all records in the container are automatically deleted. Index Definitions Index definitions describe indexes on a database. An index definition specifies the scope of the index (either a specific container or all containers), the fields to be indexed, and various indexing options. Note that only fields defined in the dictionary can be indexed. Indexing of unregistered fields is not supported. An index definition may be added, modified, or deleted at any time. Adding or modifying an index definition causes the index to be generated or re-generated, which can be done in the foreground or background. If the index is generated in the foreground, the add or modify operation will not return until the index has been built. If the data set being indexed is large, the operation could take a significant amount of time. The disadvantage to allowing a large index to be built in the foreground is that all other update operations are held off until the index has been generated. If the index is generated in the background, FLAIM will create a background thread that will build the index by starting and committing a series of small update transactions. Each of these transactions will scan a portion of the records being indexed and will generate the corresponding index keys. Once the thread has visited all records within the scope of the index, the index is automatically brought on-line. Until the index comes on-line it is unavailable for use by the application. Deleting an index definition for the dictionary causes the index to be removed and all blocks previously allocated to the index are put into a free list for re-use. It is important to note that once an index is on-line, FLAIM automatically keeps it up-to-date as records are added, modified, or deleted. When a record is added to a container, FLAIM scans the fields in the record and adds the necessary key values and references to all appropriate indexes. When a record is modified, FLAIM scans the fields in the old version of the record and the new version. After scanning the old and new records, FLAIM adds or deletes key values and references in the appropriate indexes. When a record is deleted from a container, FLAIM scans the fields in the record and deletes the necessary key values and/or references from the appropriate indexes. Index Types FLAIM supports indexing of all field types, except BLOBs. Several different types of indexes may be created, each of which is suited to a particular type of query. Single Field Index A single field index is one in which only one field is indexed. This type of index is useful when records need to be retrieved based on the value of a single field. The index can help when using operators such as <, <=, >, or >=. Compound (Multi-Field) Index A compound index is one in which multiple fields are concatenated to create a single key in the index. The fields are concatenated in the order they are specified in the index definition. In a compound index, each component field is said to be either required or optional. A required field is one that must be present in each key in the index. An optional field is one that is not required to be present. If an optional field is not present, a null value is used to hold the field s place in the key. Records that contain all of the required fields will be indexed. If all of the fields in the index are optional, all records that contain at least one of the optional fields will be indexed. Thus, a record that contains none of the fields will not be indexed. A compound index is useful when records need to be retrieved based on values contained in multiple fields. If all of the relevant fields are indexed in a compound index, the query processor may be able to perform most of its work without having to retrieve records for evaluation. Index Options There are several options that may be specified when defining an index. Field Paths When specifying the fields that are to be indexed, the user may opt to specify a field path rather than a simple field identifier. A field path is a list of fields that defines a more specific context for a field being indexed. When a field path is specified, the field is indexed only when it is found in the specific context defined by the path. When determining if a record should be referenced from a particular index, FLAIM checks the entire path. This allows an index definition to be specific about exactly when a particular field should be indexed, thus allowing it to be indexed only when it appears in certain contexts. Unique A unique index is one where every key has only one reference. If an application attempts to add or modify a record that violates this requirement, the operation is refused and an error is returned. It is important to understand that it is NOT a violation of uniqueness for a single record that has two or more fields with the same value to be indexed in a unique index. The reference set of the key that is generated in the index will still have only one reference to the record, even though there are multiple fields in the record with the same key value. International Languages FLAIM provides support for 38 international text collations (see Appendix B for a complete list). This feature allows applications to support multiple languages within a single database simply by specifying the desired collation language on the index definition. Each Word This option indicates that key values of text fields should be generated from each of the words contained within the text, as opposed to using the full value of the text field. Substring This option indicates that key values should be generated by using the text string to produce a set of sub-string values. The set of sub-strings is generated by removing the left-most character of the text value in an iterative process until the string is empty. Case-Insensitive Collation For indexes that include text fields, FLAIM allows the collation to be performed with or without sensitivity to case. Post Case-Sensitive Collation When comparing two compound keys to determine the proper collation order, each component field is compared separately. The collation order is determined as soon as a difference is detected in one of the component fields. If all component fields in the keys are equal, this collation method can sometimes yield unwanted results when the component fields are text fields. A post-case sensitive index preserves the case attributes of the text fields, but postpones the comparison of the case attributes until after each component field has been compared. Only if each of the fields are exactly identical will the upper/lower case attributes be used to differentiate between keys. This causes a collation that is very similar to a case-insensitive index. However, because a post case-sensitive index preserves the case attributes of text fields, it produces separate keys whenever there are differences in the case attributes, whereas the case-insensitive index does not. Field Identifier Indexing Keys in an index are normally constructed using the indexed field s value. FLAIM also allows a field s numeric identifier to be indexed instead of its value. This allows the creation of a  presence index that is useful for optimizing queries that have criteria on the existence of a particular field. Using an Index to Provide Sorted Result Sets A very useful side-effect of using an index in a query is that the result set produced by the query will be streamed back to the application according to the order of the index keys. Although FLAIM has the ability to perform a cost-based analysis to determine the best index(es) for use in a query, it is sometimes advantageous to use a less optimal index if it provides the desired sort order. For this reason, FLAIM allows the application to override the index selection during query optimization. Querying the Database Any application that relies on a database system to store its data obviously also needs mechanisms for finding and retrieving that data. In brief, a few of FLAIM s query capabilities include: Specification of complex selection criteria with existential, arithmetic, comparison, logical, and text operators. A streaming result set interface, which includes methods to move to the first, last, next, and previous records in the result set. In most cases, the result set does not have to be fully generated before FLAIM can start returning results to the application. Cost-based, multiple-index optimization of the query is performed. Cursor In FLAIM, an application poses a query by creating and configuring an object called a cursor. A cursor collects the selection criteria for a query, including the container that is being queried, and interprets and optimizes this information so that the requested records can be retrieved efficiently. Selection Criteria FLAIM has well-defined rules and grammar for expressing selection criteria. The syntax of FLAIM s selection criteria follows standard practices for constructing boolean expressions and a set of functions that allow boolean expressions to be built without using text strings are provided. FLAIM provides functions for adding operands, operators, parentheses, fields, and values to the selection criteria. Operators The supported operators include: exists, logical not, unary minus, multiply, divide, mod, plus, minus, not-equal, equal, less-than, less-than-or-equal, greater-than, greater-than-or-equal, logical and, logical or, bitwise and, bitwise or, bitwise xor, match, match begin, and contains. These operators have the same precedence as defined by the C programming language specification. Parentheses may be used to change operator precedence. The operators are added to the selection criteria in infix order. This allows a query to be expressed in a format that is familiar to C and C++ programmers. Operands Operands may be atomic or complex. An atomic operand is either a field identifier or a constant. A complex operand is merely another expression. Whenever an expression is an operand, the expression is evaluated and the result is used as the value of the operand. Evaluating Arbitrarily Structured Records Arbitrarily structured records pose an interesting problem for querying, because they are, by definition, unpredictable in their makeup. The query engine must be capable of dealing with missing fields, repeating fields, and a hierarchical record structure. Missing Fields A missing field is one that is referenced in the selection criteria but is not present in the record currently being evaluated. FLAIM cannot predetermine whether or not a given field will be present in every record that will be evaluated; thus it must include mechanisms to deal with the possibility of missing fields. These include a capability to explicitly test for field existence and also the adaptation of operator semantics to deal with  unknown values in their operands. Field Existence Predicate In FLAIM, the syntax for formulating selection criteria includes a mechanism that allows the framer of the query to test for the presence of a field in a record. Supplying a field identifier as an operand to a logical operator does this. When FLAIM sees a field identifier used in this way, it automatically treats it as a field-existence predicate. Unknown Values Since a field used in the selection criteria may not be present in the record being evaluated, FLAIM can use either a special value of UNKNOWN for the field s value or some other default value appropriate to the type of field. Repeating Fields  INHALT "Repeating Fields" \l 4 A repeating field is one that occurs more than once in a record. If such a field is used in a field-existence predicate, the multiple values are irrelevant because FLAIM simply tests for field existence. In this case, the field-existence predicate returns a TRUE. However, in all other cases, a repeating field can potentially yield multiple distinct values. Each distinct value represents a distinct potential evaluation path for the record. Adapting Operators for Multi-Valued Operands  INHALT "Adapting Operators For Multi-Value Operands" \l 5 Because arbitrarily structured records can have multiple occurrences of a field, all FLAIM query operators have been adapted to deal with multi-value operands arising either directly or indirectly from repeating fields. When evaluating operators within a selection criteria, if one or both operands is multi-valued, the combinations of values contained in the operator s operands are evaluated one at a time to produce a set of result values. Each distinct result value is kept, and the result of the operation is, in effect, multi-valued. More specifically, a binary operator that has n values in one operand and m values in the other operand may yield anywhere from 1 to n*m distinct values. A unary operator that has n distinct values in its operand may yield anywhere from 1 to n distinct values. Field Paths  INHALT "Specifying Field Context (Field Path)" \l 4 Since an arbitrarily structured record is hierarchical in nature, a field may appear at any level in the record s hierarchy, even having multiple occurrences at different levels (different contexts). This gives rise to an interesting problem for FLAIM. Because it is possible for a field to be used in different contexts in arbitrarily structured records, the meaning of the field in one context may be different from the meaning of the field when used in a different context. Hence, while it may be appropriate for a field s contents to be used when extracted from one context, it may not be appropriate when extracted from a different context. To differentiate, FLAIM allows the framer of a query to specify the desired context of the field. A full field path or partial field path may be specified. The field path essentially specifies the desired context the field is to be found in. When evaluating the selection criteria against a specific record, if a referenced field is present in the record being evaluated, but not in the specified context, it is treated as if the field were missing from the record. Partial Evaluation The query evaluation code has been carefully optimized to short-circuit (or bypass) sections of the query tree that do not need to be evaluated. For example, if two predicates are joined by an AND operator and the first predicate evaluates to FALSE, there is no reason to evaluate the second predicate, allowing it to be skipped. Result Sets The answer to a query is the set of records that satisfy the selection criteria. This is often called a result set. From a conceptual point of view, a query s result set exists the instant its selection criteria has been defined. All that remains from the application s point of view is to start retrieving the individual records of the result set. FLAIM provides a set of functions for navigating through a result set and retrieving records from it. This includes the ability to position to and optionally retrieve the first, last, next, previous, and current record in the result set. Database Files A FLAIM database consists of five types of files: 1) A control file, 2) lock file, 3) data files, 4) rollback log files, and 5) roll-forward log files. The name of the database is the name of the control file. The names of all other files are based on the name of the control file. The naming convention and usage of each type of file is explained below. Control File (xxx.db) The control file name is expected to conform to the following convention: xxx.db where xxx is a one to three character string. All other file names are derived from the xxx name. The first block of the control file is reserved for some application information, a log header, and a database header. These are described in the next section. The rest of the file is actually part of the rollback log space in the database. Because the rollback log can grow and shrink, it is common to see the control file change its size. Lock File The lock file name is xxx.lck. It resides in the same directory as the control file and is used to prevent multiple processes from opening a database at the same time. On NetWare and Windows platforms, the lock file is created and opened in exclusive mode. When FLAIM first opens a database, it will attempt to create and open this file. When FLAIM finally closes a database, the lock file will be deleted. The mere existence of the lock file does not mean that the database is currently open by some process. It may be that the process has aborted without shutting down FLAIM, or the system crashed before FLAIM could close the database. Thus, when opening a database for the first time, if the file already exists, FLAIM will attempt to delete the file first. If it cannot delete the file, it knows that another process is currently accessing the database, and it will return an access denied error. On Unix platforms, FLAIM uses the lock file in a slightly different way. Instead of deleting and re-creating the file every time it opens a database, the file is created when the database is first created, and remains as long as the database remains. To prevent multiple processes from accessing the database, FLAIM will put a byte lock on byte zero of the file. If it cannot obtain the byte lock, it knows that another process has already obtained the byte lock and is accessing the database. Data Files The data files are used to store all of the blocks of the database, including data blocks, index blocks, available blocks, etc. Data files reside in the same directory as the control file, and have the same xxx name as the control file, but different extensions. Each data file has a number that is encoded into the extension. Naming Convention The maximum number of data files is 2047 (file numbers 1 through 2047). For file numbers 1 through 511, the extension for a data file is its file number encoded as a two digit base 24 number. For file numbers 512 through 2047, the file number mod 512 is used to encode the first two digits as a two digit base 24 number, and then an additional third digit is added to the extension, as follows: Data File Numbers Additional Third Digit 512 through 1023  r 1024 through 1535  s 1536 through 2047  t The following examples illustrate: Data File Number Data File Name 1 xxx.01 2 xxx.02 512 xxx.00r (512 mod 512 is 0) 513 xxx.01r (513 mod 512 is 1) 1024 xxx.00s (1024 mod 512 is 0) 1025 xxx.01s (1025 mod 512 is 1) 1536 xxx.00t (1536 mod 512 is 0) 1537 xxx.01t (1537 mod 512 is 1) Rollback Log Files The rollback log files are used to log blocks of the database. The control file is actually also a rollback log file, except for its very first block (see explanation above). It is considered to be rollback log file number zero. If this file fills up because of a very large transaction (a circumstance that will be very rare), additional rollback log files will be created. These additional rollback log files reside in the same directory as the control file, and will have the same xxx name as the control file, but different extensions. Each additional rollback log file has a number that is encoded into its extension. Naming Convention The maximum number of rollback log files is 2049 -- file number 0 (the control file), and file numbers 2048 through 4095. The file name for file number zero is, of course, xxx.db. Additional rollback log files (2048 through 4095) use the file number mod 512 to encode a two digit extension (base 24 format described above), and then add on a third digit as follows: Rollback Log File Number Additional Third Digit 2048 through 2559  v 2560 through 3071  w 3072 through 3583  x 3584 through 4095  z Below are some examples: Rollback Log File Number Rollback Log File Name 2048 xxx.00v (2048 mod 512 is 0) 2049 xxx.01v (2049 mod 512 is 1) 2560 xxx.00w (2560 mod 512 is 0) 2561 xxx.01w (2561 mod 512 is 1) 3072 xxx.00x (3072 mod 512 is 0) 3073 xxx.01x (3073 mod 512 is 1) 3584 xxx.00z (3584 mod 512 is 0) 3585 xxx.01z (3585 mod 512 is 1) Maximum Data File and Rollback File Sizes The maximum file size for data and rollback files is 0xFFFC0000 bytes (almost 4 gigabytes). Because databases allow up to 2047 data files, database capacity is almost 8 terabytes. This will always be the case for newly created databases. However, databases that are converted from a prior version may not be able to have a maximum file size of 0xFFFC0000. This would happen if the database already has more than one data file (xxx.02, xxx.03, etc.) at the time it is converted. In this case, FLAIM has to set the maximum file size to the old limit, which is 0x7FFFF0000 (about 2 gigabytes). With 2047 data files, this still increases the database capacity to almost 4 terabytes. Note on the reason for choosing 0xFFFC0000 as the new maximum file size: This odd number was chosen because of a bug that was discovered in the NetWare legacy file system that only allows a file to grow to one block less that 0xFFFFFFFF when operating in direct I/O mode. Assuming a block size of 64K (normal for most NetWare legacy file systems), this means we could have had a maximum of 0xFFFF0000. However, since we do not know what the maximum possible block size is, we had to assume that there was a chance it could be more than 64K on some systems, but would probably never be more than 256K - thus the limit of 0xFFFC0000 - which is 256K less than 0xFFFFFFFF. Roll Forward Log Files FLAIM logs the operations of transactions to a roll-forward log. Roll-forward log files are used to recover transactions after a system failure and when restoring a database from backup. Naming Convention Roll-forward log files are stored in a subdirectory called xxx.rfl. Unless otherwise specified by an administrator, this subdirectory is located in the same directory as the other database files (xxx.db, xxx.01, etc.). If an administrator specifies a different directory for the roll-forward log files, an xxx.rfl subdirectory will still be created within the specified directory. For example, if an administrator specified sys:\rflfiles as the directory for roll-forward log files, FLAIM would create an xxx.rfl subdirectory: sys:\rflfiles\xxx.rfl. Roll forward log files in the xxx.rfl subdirectory will be named as nnnnnnnn.log, where nnnnnnnn is a hex number that is the log file's sequence number. Thus, log file number 1 is named 00000001.log, log file number 2 is named 00000002.log, and so forth. Data Integrity and Transactions It is desirable that database operations be performed in such a way as to preserve logical database integrity. However, it is not always possible to leave the database in a logically consistent state after a single update. Multiple update operations may be required before consistency is restored. Thus, in order to preserve consistency, a multi-operation transaction must be atomic; that is, all of the operations in the transaction must either complete or none of them must complete. This allows the database system to support a more complex notion of database integrity than it otherwise could. Checkpoint A checkpoint brings the on-disk version of the database up to the same coherent state as the in-memory (cached) database. FLAIM attempts to do a checkpoint whenever there are periods of minimal update activity on the database. In this case, FLAIM acquires a lock on the database and does as much work as possible until either the checkpoint completes or another thread wants to update the database. To prevent the on-disk database from becoming too out of sync, there are conditions under which a checkpoint will be forced even if threads are waiting to update the database. First, if the checkpoint thread has not been able to complete a checkpoint within a specified time interval (default is three minutes), a checkpoint will be forced. Second, a checkpoint will always be forced when FLAIM is told to shut down. Third, I/O errors or out-of-disk conditions on the RFL volume will cause a checkpoint to be forced. Forcing a checkpoint helps to shorten the amount of time it takes to recover the database after a system failure. Transactions FLAIM provides two types of transactions, update and read. Update Transaction An update transaction allows an application to read and update data. Until a transaction has been committed, none of the operations performed during the transaction are made permanent in the database. Furthermore, changes to the database are not visible to other concurrent transactions. If an update transaction is aborted, the changes made to the database during the transaction are undone (rolled back). Read Transaction A read transaction is a transaction where only read operations are allowed. This type of transaction provides a read-consistent view of the database, which can be logically viewed as a snapshot of the database taken at the start of the transaction. In effect, updates made by other concurrent processes that have not committed before the start of the read transaction are not visible from within the transaction. In a concurrent environment, a read transaction is executed so that it never blocks other read or update transactions. Maintaining a read-consistent view of the database requires FLAIM to keep multiple versions of database blocks and records in the database caches. Each prior version of a block and/or record is kept until it is no longer needed by any active read transaction. Transaction Failures There are two types of transaction failures. The first type of failure occurs when the application executing the transaction discovers an error that makes it impossible to continue the transaction. Upon detecting the error, the application can request that FLAIM abort the transaction. FLAIM will then undo (or rollback) all operations that were performed within the transaction. The other type of transaction failure occurs when the application that is performing the transaction terminates before committing or aborting the transaction, thus leaving the effects of a partially completed transaction in the database. Such transactions are sometimes called  dead transactions because the application that created the transaction has terminated without specifying a final disposition for the transaction. Dead transactions may be the result of external events over which the application has no control (CPU failures, etc.), or they may be the result of faulty application code. Whatever the reason, FLAIM provides for the automatic detection and rollback of dead transactions. Rollback Logging When updated blocks are written to disk, FLAIM must first write the prior versions of the blocks to a rollback log. Rollback logging has three primary purposes: 1) to undo a transaction when it aborts, 2) to recover a database to its last checkpointed state when doing database recovery after a system crash, and 3) to maintain read-consistent views of the database for read transactions. To ensure that the rollback log can be used for recovery after a system failure, the state of the database and the rollback log after any single write must be such that a consistent (checkpointed) state can be restored if a failure were to occur during or after that write. Roll-Forward Logging FLAIM logs the operations of each update transaction to a roll-forward log. Roll-forward log files are used to recover transactions after a system failure and when restoring a database from backup. FLAIM is able to operate in two modes with respect to the roll-forward log. In the default mode, the log is truncated every time a checkpoint is completed, since the log is no longer needed for recovery. This mode allows applications that do not need continuous backup capabilities to conserve disk space. The other mode allows transactions logged to the roll-forward log be kept indefinitely. When this mode is employed, multiple log files are utilized instead of just one. Roll-forward log files are not reset and reused when checkpoints are performed. Instead, the roll-forward log continually grows. For all practical purposes, a single file with a 64-bit address space would be more than adequate for thousands of years worth of transactions, given the transaction rate we can realistically sustain. However, there are a couple of reasons it is not practical or useful to simply keep growing a single file, even one with 64-bit capacity. First, not all operating systems support 64 bit files (NetWare s legacy file system only allows 4 gigabytes per file). Second, in the design of hot continuous backup, it was desirable that an administrator be allowed to move older portions of the roll-forward log to tape or some other backup media, thus conserving disk space on the volume where the roll-forward log files are kept. To achieve this, the roll-forward log is broken into multiple files. Each log file has a sequence number. The sequence number is written into a header within the file and is also encoded into the log file's name. For recovery after a non-catastrophic event, only the RFL entries since the last checkpoint are needed. For recovery after a media failure, requiring a backup and the RFL to be used, only the RFL entries logged since the backup are needed. In short, only a subset of the RFL is needed to allow recovery in either case, thus allowing obsolete portions of the RFL to be removed as needed to reduce its footprint. FLAIM provides mechanisms for an application to identify and remove sections of the log that are no longer relevant. Recovery In order to recover from a system failure, a mechanism for undoing the effects of partially completed transactions is required. When FLAIM performs recovery, it uses the rollback log first to recover the database to its last checkpoint. Subsequently, the transactions in the roll-forward log are replayed to recover the database up to the last committed transaction. Database recovery is idempotent. This means that if a crash occurs during the recovery, the process can be repeated until the database is successfully recovered. During recovery, occasional checkpoints will be performed so that if a failure happens during the recovery process, the recovery can be resumed without having to re-start from the beginning. Concurrency Control The goal of concurrency control is to ensure that operations being executed at the same time by different applications do not interleave in such a way as to compromise database integrity. Because transactions are defined as the unit of work that transforms a database from one consistent state to another, it is necessary to address concurrency issues in the context of transaction processing. Individual transactions that run in isolation should always leave the database in a consistent state. In practice, it is usually desirable to allow many transactions to run concurrently. However, if the various operations of the different transactions were allowed to interleave indiscriminately, serious errors may result that could leave the database in an inconsistent state. The fundamental concern of database concurrency control is to ensure that concurrent execution of transactions does not result in a loss of database consistency. This means that the effect of interleaving the operations of multiple concurrent transactions should be the same as running the transactions serially. Locking In FLAIM, locking is the technique used to coordinate multiple update transactions. Locking is not used for read transactions. Update transactions do not block read transactions, read transactions do not block update transactions, and read transactions do not block each other. The only transactions that block each other are update transactions. At present, the locking granularity is at the database level. Thus, when an update transaction is started, other updates will be held off until the transaction commits or aborts. Lock Wait Period When two update transactions contend for the database lock, one is granted the lock and the other is put into a queue to wait for the lock. An application may specify a lock wait period at the beginning of a transaction. The lock wait period indicates the number of seconds that FLAIM should allow the transaction to wait for the lock. If the transaction does not obtain the lock within the specified amount of time, the transaction is removed from the lock wait queue and automatically aborted. Deadlock Prevention A deadlock can occur when two or more threads try to obtain locks that are already held by each other. FLAIM prevents deadlock by aborting an update transaction whenever it is denied a lock request. Many Readers / One Writer FLAIM places no restrictions on the number of concurrent readers that can access a database. It is impossible for readers to interfere with each other because they do not modify the database. Whenever an application knows that a transaction will only perform read operations, a read transaction should be used instead of an update transaction. This improves concurrent access to the database. Backup and Restore A basic, no-frills backup solution requires that all updates to the database be held off while the backup runs. This could be as simple as shutting down the database server and copying the files to a backup location, or to be slightly more sophisticated, the database server could continue to run in a read-only mode (after all dirty cache is flushed to disk) while the files are copied to a backup location. For most database deployments, this type of backup is generally not acceptable. The next level of sophistication, hot backup, refers to a backup that is performed while other concurrent operations are allowed to execute against the database. This type of backup results in a snapshot in time of the database, capturing all committed transactions at the time of the backup. All modifications made to the database during the backup are excluded. A hot backup allows for reasonable protection of the data in the database, while also allowing the database to remain fully on-line for the duration of the backup. The drawback is that changes made to the database between backups are not protected against catastrophic failure. This could mean the loss of several hours, or even days, of database updates depending on when the last backup was made. For some deployments, this risk of partial data loss is unacceptable. Hot, continuous backup extends the concept of a hot backup by providing a mechanism for protecting changes to the database made between backups. Typically, this is accomplished by preserving roll-forward log (RFL) files, thus maintaining a complete record of changes made to the database since the last hot backup. These log files are typically stored on a device (disk, tape, etc.) separate from the device that hosts the database. Backup FLAIM supports three different types of backups: Full, Incremental, and Continuous. All backup operations take place while the database is on-line, without blocking concurrent transactions. Full Backup A full backup makes a complete copy of all data in the database that is committed as of the start of the backup. It does this by starting a single read transaction (thus guaranteeing a read-consistent view of the database) and then streaming each of the blocks in the database out to the backup utility. Since this type of database scan is a classic example of a cache-poisoning operation, the read transaction is started with a special flag that prevents it from using cache in a way that would cause it to be poisoned. It is interesting to note that since block reads are done from cache when possible, it is likely that some of the blocks in the backup set will be newer than the corresponding database blocks on disk. Incremental Backup An incremental backup is similar to a full backup in that it is done within a single read transaction that scans every block in the database. The difference is that an incremental backup only copies those blocks that have changed since the last backup (either full or incremental). Continuous Backup As discussed above, full and incremental backups are essentially snapshots of the database at the time of the backup. Thus, transactions posted to the database after the start of the backup will not be recorded in the backup set. Continuous backup overcomes this shortcoming by preserving the transactions written to the roll-forward log. During a database restore, the transactions recorded in the roll-forward log can be applied to the newly restored database to bring it up to date with the last committed transaction. Restore A FLAIM database restore is done via a callback mechanism which allows the application to stream bytes from the backup media into FLAIM. During a restore, FLAIM will first request data from a full backup. Subsequently, FLAIM will request data from any incremental backups that are available. And finally, if roll-forward logs are available, FLAIM will replay transactions until the database is up-to-date or until the restore is terminated. Caching FLAIM uses a two-level caching system: a block cache and a record cache. Block Cache The block cache stores in-memory images of the database blocks. Each block in cache maintains a linked list of older and/or newer versions of the blocks that are cached. This is essential for providing read consistency. Record Cache The record cache operates at a logically higher level than the block cache. The items in the record cache are FLAIM records that have been extracted from database blocks. Without the record cache, every record access would require FLAIM to re-construct the record from its corresponding elements in the data blocks of the database. Because of the obvious inefficiency of reconstituting records every time they are needed, the records are placed in the record cache after their first non-cached access. Once in cache, records can be returned by FLAIM without having to access database blocks. Like block cache, items in the record cache may also have a linked list of older and/or newer versions of the records that are cached. Cache Poisoning Cache poisoning occurs when an item is inserted into cache and is subsequently removed from cache before any cache hits occur on that item. Cache poisoning degrades performance, to the point that a severely poisoned cache usually performs slower than running without any cache at all. The types of access patterns that poison a cache will depend on the algorithm used to determine which items to remove from a full cache. Typical access patterns that cause cache poisoning in FLAIM are scans and cycles. For example, database scans can iterate over more records and blocks than could fit in cache, while only visiting each item once. FLAIM offers a non-poisoning mode for transactions. In the case of a read transaction, newly added cache blocks are added to the least-recently used end of the cache. Since the item at the LRU end may simply be replaced over and over, the rest of the cache remains undisturbed. In the case of an update transaction, new blocks that are read from disk are also added to at the LRU end. However, if a block becomes dirty during an update, it is relocated to the MRU end, since replacing a dirty block is more expensive than replacing a non-dirty block. Also, whenever a cache hit occurs in non-poisoning mode, the item is transposed with its neighbor toward the MRU end. This way, cache hits are promoted incrementally toward the MRU without poisoning cache. Cache Performance Measurements There are four types of cache measurements we can make: cache hits (how many times we have reused items from cache), cache looks (how many links we follow on the bucket collision chain to end up with a hit), faults (how many times we could not find an item in cache and had to read from disk), and fault looks (how many links we follow on the bucket collision chain only to end up with a fault). The formula (cache looks/cache hits) * 2 gives us an average of how long our collision chains are (the factor of 2 comes from the fact that we would seek an average of halfway down the collision chain to reach a hit). The formula (fault looks/faults) also gives us an average of how long our collision chains are, since every fault results in as many fault looks as there are collision links. The primary metric by which cache performance should be measured is in the number of faults per unit of throughput. Each cache fault results in an expensive I/O operation. The cost of the I/O operation may vary from platform to platform and may become more expensive as CPU speeds increase and disk speeds stagnate, but will probably be equivalent to at least thousands if not hundreds of thousands of CPU instructions. Efforts spent in reconfiguring a system's cache should be spent in trying to reduce the number of faults. Increasing the cache hits on a system will net little gain unless there is a corresponding decrease in the number of faults over the same operation. An administrator has many variables to work with when trying to optimize FLAIM database performance, including total amount of system memory, cache configuration, and access patterns. Adding more memory may not always help performance if the access pattern results in a cache poisoning. Allowing FLAIM to occupy more cache may not help either, depending on the access pattern. The best recommendation for administrators is to experiment with various tuning variables in a production environment. Configuration The FLAIM cache size can be configured to limit the amount of memory used. The size can be specified as either a hard limit or a dynamically adjusting limit. Hard Limit A hard limit, put simply, is a fixed maximum number of bytes that FLAIM may use for cache. The number, once set, will not change unless a new cache size is explicitly set. A disadvantage to using a hard cache limit is that if the system RAM availability changes for some reason (e.g., a memory upgrade on the server), the cache size will not adapt automatically. A new limit would have to be specified to take advantage of the additional memory. Dynamic Limit In an attempt to avoid problems associated with a static cache size, dynamically adjusting limits were developed. Dynamically adjusting limits allow the user to specify a certain percentage of available memory to be used for cache. Available memory is defined as RAM that is not currently allocated to any process plus the RAM which FLAIM is using for cache at that point in time. In addition to specifying the percentage of available memory to use, the user indicates a lower and upper bound for how many bytes cache should consume. The lower bound is expressed as a number of bytes. The upper bound is expressed either in terms of a maximum number of bytes to use or in terms of a minimum number of bytes to leave available on the system. In order to calculate the actual cache size with the dynamically adjusting limit, the amount of available memory is computed, and the user-specified percentage of that number is computed. Next, that result is compared with the upper bound, and the smaller of those two numbers is used. The final step is to compare that result with the lower bound, and the larger of those two numbers is used as the cache size. At a certain time interval, known as a cache adjust interval, FLAIM will perform the above calculations again, and compute a new cache limit. The default cache adjust interval is 15 seconds but the user may configure it differently if desired. The primary disadvantage of a dynamically adjusting limit is its complexity, which results in a larger user support cost. Users want a simple explanation for how the dynamically adjusting limit works, and a simple formula to compute the optimum configuration values. Unfortunately, the system is inherently complex, and the optimum values for any given system can only be learned by trial and error. Therefore, users wanting to use this feature must be willing to spend adequate time and resources learning about and tinkering with dynamically adjusting cache limits. Distribution Cache is divided between record cache and block cache. The default split is 50% record cache and 50% block cache but the user may modify this, if desired. We wish to note that our performance tests have yet to reveal any record cache/block cache divisions that are clearly superior to the default 50/50 split. Extended Server Memory TBD Extended server memory is any memory installed in a machine running a 32-bit operating system that is above the On 32-bit platforms with more than 4 gigabytes of RAM, FLAIM Issues The maximum amount of memory that can be used for cache on a system is determined by several factors. Obviously, a certain amount of physical RAM will be consumed by the OS, other processes running on the system, and parts of the FLAIM system unrelated to cache. The maximum size of the platform pointer type and FLMUINT type may confine the addressable memory space. For example, if a system's void * or FLMUINT is 32 bits wide, the maximum addressable memory will be 232 bytes or approximately 4 GB. In addition, the OS may impose limitations on how much physical RAM it will allow a process to allocate. For example, most versions of Windows limit processes to 2 GB; NetWare has a limit of approximately 3 GB. The memory manager is another factor that affects cache. For example, NetWare's memory management system allocates pools of memory for each process. When a NetWare process frees memory, the memory manager may delay (perhaps by several minutes) returning that memory back to the OS in order to optimize near-future allocations for that process. Until that memory leaves the process' pool, NetWare will continue to report that memory as unavailable; this can limit the usefulness of the dynamically adjusting limit Finally, paging FLAIM's cache to disk in a virtual memory environment will degrade performance. Setting the cache size to some amount less than the amount of physical RAM may be the only effective means of eliminating this problem if the platform does not allow the pages to be pinned in memory. Cache Cleanup A background thread, known as the monitor thread, periodically scans the FLAIM caches looking for items that are no longer needed. These items are release and the memory allocated to them is returned to the system. Database Maintenance For a variety of reasons, computer systems are subject to failures. These include disk crashes, power failures, software errors, and even sabotage. Despite FLAIM s proven stability, extensive experience has shown that there are many factors beyond FLAIM s control that can cause database corruptions. These include faulty disk array controller firmware, file system bugs, etc. No database will ever be able to fully isolate itself from external problems that can cause corruptions. Because of this, FLAIM provides mechanisms that allow corruptions to be detected and repaired. Run-Time Data Verification In the normal course of processing database operations, FLAIM provides capabilities for verifying data. Block Checksumming Whenever FLAIM writes a database block to disk, it calculates a checksum for the data in the block and stores the checksum in the block header. As blocks are read from disk, this checksum is verified. If the checksum is bad, an error is repored. Block Sanity Checking FLAIM provides four levels of block sanity checking. No Sanity Checking All sanity checks are disabled. The checksum is still verified, however. Basic Sanity Checking FLAIM checks information in each block header for any anomalies. It also traverses the physical elements of the block to ensure that they are correct. Intermediate Sanity Checking All of the basic checks are performed. In addition, FLAIM verifies that the keys in index blocks are in ascending order and that DRNs in container blocks are in ascending order. Extensive Sanity Checking All of the intermediate checks are performed. In addition, reference sets in index blocks are verified for consistency and the fields in each element of container blocks are checked to ensure that records end on correct boundaries, that fields have valid data types, and that the contents of fields appear consistent with their data types. Database Check In addition to the continuous run-time data verification mechanisms that are built into FLAIM, an API for performing a comprehensive on-line database check is provided. There are two levels of checking available: physical checking and index checking. Both can be performed without requiring exclusive access to the database; both update and read transactions may operate concurrently with a database check. Physical Check The physical check performs operations similar to the block sanity checks discussed earlier, but provides a more detailed check than is possible with the simple sanity checks. A comprehensive physical check is able to verify relationships between blocks as well as information within blocks. Index Check A structurally sound database may still have logical errors, generally due to code errors in the indexing code (which are rare). The index check is used to verify that all records that are referenced from indexes are present and generate the correct keys, and that there are no extraneous keys in the indexes. Database Rebuild (Salvage) A database rebuild operation attempts to salvage data from a damaged database. The first thing that a rebuild must do is determine the database block size. Once determined, the rebuild will create an empty destination database for storing the recovered records. The dictionary container in the source database is dredged to extract all usable dictionary definitions, which are added to the destination database. Finally, the rebuild tries to extract records from the source database and adds them to the destination. Note that the rebuild does not try to recover index keys; these are re-created automatically in the destination database by virtue of the fact that the index definitions from the source database were added to the destination s dictionary. Space Reclamation Whenever a block becomes empty, FLAIM links the block into an available block list (or  avail list). Subsequently, if FLAIM needs to create a new block, it will first look in the avail list for a block before extending the database. In certain instances, it may be desirable to have blocks in the avail list returned to the file system to reduce the footprint of a database. FLAIM provides a function for reorganizing blocks so that free space can be returned to the file system. The space reclamation function can be performed on-line, without requiring exclusive access to the database. Update operations, but not reads, are prevented while a reclamation operation is in progress. However, the reclamation function allows the specification of the maximum amount of unused space to be reclaimed. Typically, it is best to reclaim small chunks at a time by making successive calls to the reclamation function instead of trying to reclaim all unused space in one call. This helps to minimize interference with normal update operations. Appendix A  Dictionary Definition Records Field Definition Record The following record is used to define fields in the data dictionary. 0 field <name> 1 type {context | number | text | binary | blob} [ 1 state { checking | unused | purge}] Notes: checking: User request to determine if field is used. This is done by calling FlmDbSweep. unused: Field is not used and may be deleted. FlmDbSweep will set this state if it discovers that the field is not used. If a field subsequently becomes used, this state will be changed back to active. purge: Field is to be deleted. If an attempt is made to use the field in a record update (add, modify, or delete), the update will fail. FlmDbSweep will remove all usages of the field and then delete the field definition record. NOTE: A field in this state may still have other dictionary records that reference it (such as an index definition). It is up to the application to fix these situations. The typical way an application deletes a field is by marking the field as 'purge' and calling FlmDbSweep. Container Definition Record The following record is used to define containers in the data dictionary. 0 container <name> Index Definition Record The following record is used to define indexes in the data dictionary. 0 index <name> [ 1 container <ID> ] [ 1 language <language> ] 1 key [EACHWORD] [ 2 base <field path> ] [ 2 post ] [ 2 positioning ] [ 2 unique ] { 2 field <field path>}... [ 3 case upper ] [ 3 required ] [ 3 use {eachword|substring|value|field} ] [ 3 filter {minspace|nodash|nounderscore|nospace} ] [ 3 limit <limit> ] Following are some in-depth notes about particular parts of the index definition: [ 1 container <ID> ] This line, if not specified will default to DEFAULT_DATA_CONTAINER. [ 1 language <language> ] This line, if not given, will cause the default DB language to be used (the default language is one of the DB create options). The choice of language will affect the collation order of the keys. It may also affect the storage format used for the keys (for example, 1 byte per character is used for keys in US English, while 2 bytes per character is used for keys in Japanese, Chinese, Korean etc.) 1 key [EACHWORD] The EACHWORD keyword, when applied to a key, will cause keys to be generated for each word in the field value. This only makes sense for text fields. Whitespace characters in the field value delimit each word. [ 2 base <field path>] The base keyword optionally specifies the beginning of the field path for this index. In other words, any field paths given via { 2 field <field path>} will have the base <field path> automatically prepended to the given field path. [ 2 post] The post keyword defines a sorting order where keys will be sorted first in a case-insensitive manner, and if there are any ties to break, the tie-breaker will be to sort in a case-sensitive manner. This is only relevant with compound indexes. Note: Lower-case sorts before upper-case. [ 2 positioning ] The presence of the positioning keyword will cause FLAIM to store subordinate counts in each non-leaf node of the B-Tree. [ 2 unique] The unique keyword enforces that no duplicate keys will be allowed for the index. { 2 field <field path>}... The <field path> outlines a path from lower-level field(s) to higher-level field(s). The syntax for indexing fields 2.3.4 (where 3 is subordinate to 2 and 4 is subordinate to 3) is simply "2 3 4". This will only generate keys for records containing fields which have a 2.3.4 field path. Before the index is built, if the base keyword is present, its ID will be prepended to this field path. Including more than one {2 field <field path>} statement in an index definition will result in a compound index. [ 3 case upper ] The default case-sensitivity of an index is case-sensitive. Setting the upper option will cause a case-insensitive key to be generated. This reduces the space each key occupies, since no case bits are needed. It also optimizes queries for this field using case-insensitive operators, while incurring a penalty on queries using case-sensitive operators. [ 3 required ] The required keyword will ensure that a key is only generated for the index if this field, plus all other required fields for the index are present. [ 3 use {eachword|substring|value|field}] The eachword keyword is discussed above, but in this case applies to only one field. The substring keyword is discussed in an earlier section. The value keyword is the default, and indicates that the field value should be indexed. [ 3 filter {minspace|nodash|nounderscore|nospace}] TBD [ 3 limit <limit>] TBD Appendix B  International Languages Within an index definition, the user may specify the  language of the index. The language defines what the collation sequence for TEXT fields within the index will be. The default collation sequence for TEXT data is according to the US language. Alternate collation sequences may be specified by selecting one of the following language codes: AF - Afrikaans AR - Arabic CA - Catalán HR - Croatian CZ - Czech DK - Danish NL - Dutch OZ - English  Australia CE - English  Canada UK - English  United Kingdom US - English  United States (this is the default) FA - Farsi SU - Finnish CF - French  Canada FR - French  France GA - Galician DE - German  Germany SD - German  Switzerland GR - Greek HE - Hebrew HU - Hungarian IS - Icelandic IT - Italian JP - Japanese NO - Norwegian PL - Polish BR - Portuguese  Brazil PO - Portuguese  Portugal RU - Russian SL - Slovak ES - Spanish SV - Swedish YK - Ukranian UR - Urdu TK - Turkey NOTE: The collation sequence for each language is not necessarily unique. The following rules describe the sort order (collating sequence) for characters in TEXT fields: White space is sorted BEFORE all other characters Characters such as "!@#$%^" etc. are sorted between white space and numeric characters. These characters may vary from language to language. Numeric characters are sorted next (sorted in numeric sequence). Alphabetic characters (sorted from lowercase to uppercase with Greek characters 1st, Latin characters 2nd, and Cyrillic characters 3rd.) Appendix C  FLAIM Utilities Several rudimentary utilities have been developed for testing and diagnostic purposes. Even though they are a little rough around the edges, these utilities are still very useful. These utilities all use a windowed user interface known as FTX, or the FLAIM Text interface. FTX provides for many of the common graphical user interface widgets such as forms, menus, and pull-down lists and receives keyboard input, while still allowing the utilities to run on all supported platforms. DBShell DBShell, simply put, is a wrapper around another utility known as the Record Editor. The Record Editor allows a user to view and change the entries of the records in a database. CheckDB CheckDB calls FlmDBCheck and prints a detailed status information to the screen. CheckDB can also log the check results to a file if desired. Rebuild Rebuild calls FlmDbRebuild and reports the number of corruptions and records recovered. Rebuild can also log results to a file if desired. View View allows a user to examine the database blocks at the lowest levels and edit blocks and checksums. RFLView RFLView allows a user to examine the Roll Forward Log files at the lowest levels and examine their headers, serial numbers, etc. but does not allow for editing. Appendix D  Building the FLAIM Libraries TBD Appendix E  Sample Code TBD Document Change Log Doc RevisionDate of ChangeModifierDescription of change0.36/07/06Andy HodgkinsonChanges for open-source release0.26/13/02Andy HodgkinsonIncorporated changes after document was review0.16/12/02Andy HodgkinsonCreation Introduction to FLAIM 4 FLAIM Documentation Copyright © 1997-2002, 2006, Novell, Inc. Page  PAGE iii Introduction to FLAIM 4 Introduction to FLAIM 4 FLAIM Documentation Copyright © 1997-2002, 2006 Novell, Inc. Page  PAGE iii 024¦¨ª¬®°²´¶¸º¼¾êì "–˜è ê    "$F‚´ÖøHz¼âj®èÔÔ¼¼¼¼¼¼¼¼¼¼¼¼¼¼Ô¼Ô¼Ô¼¼¼¼Ô¦¤¤wwwwwwwwwwww*:B*CJmH sH PJnHÿ^JaJ_HÿtHÿ-;B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿU*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ/B*CJmH sH PJnHÿ^JaJ_HÿtHÿOJQJ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ-B*CJ$mH sH 5PJnHÿ^JaJ$_HÿtHÿ\.®úFrÖø Jh”èN~¦ÂDz¸þ"f¤äb€ºò&JžÔ@‚¸è<h–¾ü2` ô&X€Êø&xÎŒÆæ"P|âêêêêêêêêêêÓê½½½ê½½©©©ê½½©©©ê½½½ê½½½½½½©©©©©©½½©©©©©©©©©©©©©ê½©©©½©'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 6PJnHÿ^JaJ_HÿtHÿ-;B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ*:B*CJmH sH PJnHÿ^JaJ_HÿtHÿBdœÔH z ¶ à !J!p!˜!Ò! "D"ª"ê"$#r#š#Æ#$:$z$¶$ú$&%`%†%Â%&N&†&ª&Ü&'Z'€'ž'È'ô'&(v(¤(Ð()2)v)š)È)*L*Š*Î*+X+®+þ+0,f,–,à,-x-¶-ü-:.ëëëëëëÕ¿ÕÕÕëÕëÕÕë¿ÕÕëëëëëëÕëëëëÕëëëëë¿ÕÕÕÕÕëëëëëÕ¿ÕëëëëëëÕëëÕÕ¨¿¿¿-;B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ*:B*CJmH sH PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 6PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿB:.Ž.Ò.ð./,/D/b/À/ü/,0.00020N0~4²4<5b5ê5ö6<7”7,8Ê8.9X9n9ò96:L:–: ;;´;z<=x=ö=d>š>¼>æ>?.?6@º@AèèÒÒÒÒÒèèÐ踸¸¢ŽŽŽŽŽŽŽŽ¢Žz¢ŽŽ¢ŽŽŽŽŽŽŽŽŽŽŽŽŽŽ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ.B*CJmH sH 5KHPJnHÿ^JaJ_HÿtHÿU*:B*CJmH sH PJnHÿ^JaJ_HÿtHÿ-;B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ/ArAàA(BrBêBnC¨CDD>D²DE*5PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnH ^JaJ_HÿtHÿ655U*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿEª&¬&+6+L,p,r,´,¶,z-œ-†0ˆ0ú23ä7ò7t8~8t9‚9¬;¼;Þ<è<@>l>ÀBäB‚TšTäU VÞWXZ"Z*Z:ZR`n`þ`a ffxj¨j¸lÌl.nBnRpTpŠpvq²qNy‚yâ{<|(€T€ØüææüäüàÉææææææææ³ææ±±³æææææææææ³æ5*B*CJmH sH 5PJnH ^JaJ_HÿtHÿ-B*CJmH sH >*5PJnHÿ^JaJ_HÿtHÿ65*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿU>ؾ‚ă„H…V…††²‡؇‹‹ÈÚî‘B’F”d”(˜\˜›:›"$dfä >¡@¡¶¡¸¡p£r£R¦T¦ЦŒ¦§§`§b§ܧÞ§¨¨¨†¨ˆ¨F±l±´´º¸ظ¤»лf¼t¼‚¼ˆ¼(½ëëëëÕÓ½ÕÕÕ½ÕÕ½¹·¹Õ¹·¹¡Õ¹·¹½ÕÕÕ¡Ó65*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿU*B*CJmH sH 5PJnH ^JaJ_HÿtHÿ5*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ=(½.½ð¿À0À>ÀËˬÍÐÍìÐ@ÑÒXÒZÒ2ÔXÔ@ÙdÙHܪܢÝÞVàªàæ–æDërëêìí„í’í˜î¤î¨î´îvï„ïbð|ðññ2ñ`ñžñ¬ñêñòò"òØòðò*óBóbó¢óXønøˆ¢>r”ýçýçÑ»»§çÑ»»ç£çÑýýýýýý»ýýýýýçççÑÑ65'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnH ^JaJ_HÿtHÿ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ5@”Î ø p’ÂìÆ*Ø*‚0ª029B9h=Š=pA˜A(C\CtFšFjTxTøUVº[à[^:^TbdbÜeìe~f–fRhlh$nDn8yvyæyúy`zvz{"{Ê{à{òƒôƒüƒþƒÀ„„܈øˆ6ŠLŠÎê`z蟠z¡ˆ¡:¥>¥„­ ­P¯z¯´<´ µ2µ"·N·¸·Þ·r¸ž¸êÔÔÔÔêÔÔêÔêÔÔÔêÔÔÔÔÔÒÒÒÒÒÒÒÔêêêêêÐÔÔÔêêÔÔH*5*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnH ^JaJ_HÿtHÿRž¸ιºn»¢»L¾j¾œÁºÁÄÄŠÆÀƲÌÖÌüÔRÕ‚ÕÖÖ.Ö2Ö:Ö@ÖNÖTÖ`ÖfÖnÖtÖ€Ö†ÖŽÖ’ÖšÖ¤Ö¬Ö¼ÖÂÖÎÖÔÖÞÖäÖöÖøÖװײ׾×LÙNÙXÙNÝ†Ý Þ2ÞBÞêêêÔÔêê¼êº¦¦¦¦¦¦¦¦¦¦¦¦ºººêº*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ5.B*CJmH sH 5KHPJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnH ^JaJ_HÿtHÿ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ8BÞrÞßß ß(ß:ßLßTßdß€ß„ßŠß¢ß°ß¸ßØßàßêßîßöß ààà&à,à4à>àbàjàràtà~à„àŒàœà¢àªà°à´àÄàÆàØàÚàäàæàðàøàá áá á"á.á0áHáJáXá`áháráˆá8âJâ\âäâîâþâã<æBæHæbæjæzæ èêèÔ¾ÔÔ¾ÔÔ¾ÔèÔ¾ÔÔ¾ÔÔ¾ÔÔ¾ÔÔ¾Ô¾ÔÔ¾ÔÔ¾Ô¾Ô¾Ô¾Ô¾ÔÔ¾Ô¾Ô¾Ô¾Ô¾ÔÔ¾ÔèÔèÔèÔ¾Ô*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ5*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿK èèè<èDèLèêê"ê(ê0ê8êjìtìŠì”ì¼ìÒìˆí’íží¤í¬í¸íHîRî\î‚î ññzò„ò˜ò ò2ó<óhõrõ‚õŒõ”õ¤õ¶öÀöÆöÊöÚöÜöîöðöúöüö÷÷÷&÷Â÷Ô÷8øBøàøêøöøúø ù ùùù2ù4ùBùJùRù\ùfù~ù†ùýéÓéýéÓéýéÓéýéÓéýéÓéýéÓéýéÓéýýýýýéÓéÓéÓéýýýýýéýé*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ5L†ùÐùˆü¦ü¾üØüôü ý"ý8ýjý–ýÒý8þNþhþ’þ¼þØþÿ8ÿNÿfÿ„ÿ¢ÿ¼ÿØÿöÿ@v¨ÂÜø $&0º¼~âþ€”Ζ ¦   : J ` j 6FˆÜäFN`èÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÔÒ¼ÔÔÔÔ覦¦¦¦èèèÔÔ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ*B*CJmH sH 5PJnHÿ^JaJ_HÿtHÿ5'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ.B*CJmH sH 5KHPJnHÿ^JaJ_HÿtHÿA`p~¼ÆÖö6@PpÎØè.0bèêöøþ<>pôö  ëëëëëëëëëëëëëëëëë××ÔÐÍÐÍÇ××ÔÐÍÐÍÇ 0J;U0J;0J;0J;UCJ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ'B*CJmH sH PJnHÿ^JaJ_HÿtHÿ$024¦¨ª¬®°²´¶¸º¼¾îéäßÚÕÐËÆÁ¼·²­¨£žœ<<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$<$a$$a$„h^„h„]„„`„–˜è ê ì    F‚´ÖøHz¼âúøöåãá×ÑËÅ¿¹³­§¡J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& B ÆÐ& B ÆÐ& ¤x¤xRR$a$„°^„°„]„„`„<<<$a$âj®úFrÖø Jh”èNùóíçáÛÕÏÉý·±«¥Ÿ™K ÆÐ& J ÆÐ& B ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& N~¦ÂDz¸þ"f¤äb€ºòùóíçáÛÕÏÉý·±«¥Ÿ™K ÆÐ& K ÆÐ& J ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& K ÆÐ& K ÆÐ& ò&JžÔ@‚¸è<h–¾ü2`ùóíçáÛÕÏÉý·±«¥Ÿ™L ÆÐ& K ÆÐ& K ÆÐ& N ÆÐ& N ÆÐ& N ÆÐ& N ÆÐ& N ÆÐ& M ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& K ÆÐ& ` ô&X€Êø&xÎŒÆæ"P|ùóíçáÛÕÏÉý·±«¥Ÿ™M ÆÐ& M ÆÐ& L ÆÐ& K ÆÐ& J ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& |âdœÔH z ¶ à !J!p!˜!Ò! "D"ª"ùóíçáÛÕÏÉý·±«¥Ÿ™K ÆÐ& L ÆÐ& K ÆÐ& L ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& K ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& K ÆÐ& ª"ê"$#r#š#Æ#$:$z$¶$ú$&%`%†%Â%&N&†&ùóíçáÛÕÏÉý·±«¥Ÿ™K ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& K ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& L ÆÐ& K ÆÐ& †&ª&Ü&'Z'€'ž'È'ô'&(v(¤(Ð()2)v)š)È)ùóíçáÛÕÏÉý·±«¥Ÿ™K ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& K ÆÐ& J ÆÐ& L ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& È)*L*Š*Î*+X+®+þ+0,f,–,à,-x-¶-ü-:.ùóíçáÛÕÏÉý·±«¥Ÿ™J ÆÐ& J ÆÐ& J ÆÐ& B ÆÐ& K ÆÐ& K ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& M ÆÐ& L ÆÐ& L ÆÐ& K ÆÐ& J ÆÐ& :.Ž.Ò.ð./,/D/b/À/ü/002040N0~4ùóíçáÛÕÏÉ¿²—•A & F ưÆ& „°^„°„]„„Pþ`„Pþ¤X¤< & F ÆÐ& ¤x¤x B ÆÐ& ¤x¤xB ÆÐ& B ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& J ÆÐ& B ÆÐ& B ÆÐ& ~4²4<5b5ê5ö6<7”7,8Ê8.9X9n9ò96:L:–: ;êèÓ¾¾¾¾¾¾¾¾Ó©©Ó””A & F Æ„^„„]„„˜þ`„˜þA & F Æ„^„„]„„˜þ`„˜þA & F Æ„^„„]„„˜þ`„˜þ & F ƨ„¨^„¨„]„„Àý`„ÀýA & F ư„°^„°„]„„Pþ`„Pþ ;;´;z<=x=ö=d>š>¼>æ>?.?6@º@ArAàA(BrBêBnC¨CDêÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕÕA & F Æ„^„„]„„˜þ`„˜þ & F ƨ„¨^„¨„]„„Àý`„ÀýDD>D²DEl>Ô?Ö?ÀBäBnEpEýèæäèâàèÞÜèÚèØÃÁ¿½Ã»¹AAAAA & F Æ „ ^„ „]„„0ý`„0ýAAAAAAAA & F ƈ „ˆ ^„ˆ „]„„€û`„€ûApEGGJJâMäM.O0O‚TšTäU VÞWX^^R`n`þ`a fýûù÷õóñïíØÖÁ¿Á½»¹Ø·ÁµAAAAAA & F Æ „ ^„ „]„„ü`„üA & F Ƙ„˜^„˜„]„„ ü`„ üAAAAAAAAA ffxj¨j¸lÌl.nBnRpŠpvq²qNy‚yâ{<|(€T€ÖØêèêæêäêâêàêÞêÜÇŰ®¬AA & F ƨ„¨^„¨„]„„Àý`„ÀýA & F Ƙ„˜^„˜„]„„ ü`„ üAAAAAAA & F Æ „ ^„ „]„„ü`„üؾ‚ă„H…V…²‡؇‹‹ÈÚî‘B’F”d”(˜\˜›êêêêÕÓ¾¼§¥§£Õ¡¾Ÿ§AAAAA & F Æ „ ^„ „]„„ü`„üA & F Ƙ„˜^„˜„]„„ ü`„ üA & F Æ „ ^„ „]„„0ý`„0ýA & F Æp„p^„p„]„„˜þ`„˜þ›:›"ä >¡p£r£¨¨F±l±´´Ú¶ܶº¸ظêèÓÑêÏÍËêÉÓDz°®¬— & F ƨ„¨^„¨„]„„Àý`„ÀýAAA & F Æ „ ^„ „]„„0ý`„0ýAAAAAA & F Ƙ„˜^„˜„]„„ ü`„ üA & F Æ „ ^„ „]„„ü`„üظ¤»лd¼f¼t¼v¼<½>½ð¿ÀVÁXÁ Ç"ÇËˬÍÐÍêÐìÐýèæäâàÞÜÚèØÖÔÒÐèι·µAA & F Ƙ„˜^„˜„]„„ ü`„ üAAAAAAAAAAAAA & F Æ „ ^„ „]„„0ý`„0ýAìÐ@ÑBÑpÑžÑÌÑÎÑÒÒXÒZÒrÒŠÒÎÒÓZÓ¢ÓêÓ2ÔXÔ@Ùýûù÷õóñïíßÝÛÙ×ÕÓÑϺ¸A & F Æ „ ^„ „]„„0ý`„0ýAAAAAAAA A„^„„]„„`„AAAAAAAAA@ÙdÙFÜHܪܬÜÜÜ Ý<ÝlÝnݠݢÝÞÞPÞšÞäÞ.ßxßÂß àVàêèæäâàÞÜÚØÖÔÒÐÎÌÊÈÆÄÂÀAAAAAAAAAAAAAAAAAAAAA & F Ƙ„˜^„˜„]„„ ü`„ üVàªàææDërëêìí0ñ2ñ`ñbñbó¢óXønøû’ûêèæäêâÍËÉ»¹·¢ êžœAAA & F ƨ„¨^„¨„]„„Àý`„ÀýAA A„^„„]„„Ð`„ÐAA & F Ƙ„˜^„˜„]„„ ü`„ üAAAA & F Æ „ ^„ „]„„0ý`„0ý’ûˆ¢>r”ÂÄÎ ø ö ø p’œžÂìzýèæÑÏÑÍËÉÑÇÅ¨®¦AAAA & F Æ „ ^„ „]„„ü`„üAAAAAAA & F Ƙ„˜^„˜„]„„ ü`„ üA & F Æ „ ^„ „]„„0ý`„0ýAz|æèBDž& &Æ*Ø*º-¼-‚0ª0À3Â329B9h=ýûù÷õóñïÚØÖÔ¿½»¹¤¢A & F Ƙ„˜^„˜„]„„ ü`„ üAAA & F Æ „ ^„ „]„„0ý`„0ýAAA & F Æ „ ^„ „]„„ü`„üAAAAAAAAh=Š=pA˜A(C\CtFšFpJtJPMRMQQjTxTøUVº[à[êèêæÑϺ¸¶´²°®¬Ñªê¨êAAAAAAAAA & F Æ „ ^„ „]„„0ý`„0ýA & F Ƙ„˜^„˜„]„„ ü`„ üAA & F Æ „ ^„ „]„„ü`„üà[^:^TbdbÜeìe~f–fRhlh$nDn€p‚p@sBs8yýèæÑϺ¸£¡£Ÿ£›™—•AAAAAAA & F Æ „ ^„ „]„„0ý`„0ýA & F ƨ„¨^„¨„]„„Àý`„ÀýA & F Ƙ„˜^„˜„]„„ ü`„ üA & F Æ „ ^„ „]„„ü`„üA8yvy¤¦ô„ö„܈øˆ6ŠLŠÎ꾓À“ü–þ–è˜ê˜`z蟠êèæäâàêÞÉÇÉÅÃÁ¿½»¹É·ÉAAAAAAAAA & F Ƙ„˜^„˜„]„„ ü`„ üAAAAAA & F Æ „ ^„ „]„„0ý`„0ý  z¡ˆ¡$§&§.«0«„­ ­P¯z¯´<´ µ2µ"·N·¸·ýûæäâàÞÜÇŰ®Ç¬æªæ¨AAAA & F ƨ„¨^„¨„]„„Àý`„ÀýA & F Æ „ ^„ „]„„0ý`„0ýAAAAA & F Ƙ„˜^„˜„]„„ ü`„ üAA¸·Þ·r¸ž¸ιºn»¢»L¾j¾œÁºÁÄÄŠÆÀƲÌÖ̞РÐêèêæêäêâÍ˶´¶²Í°Í®¬AAAAA & F Ƙ„˜^„˜„]„„ ü`„ üA & F Æ „ ^„ „]„„0ý`„0ýAAAA & F Æ „ ^„ „]„„ü`„ü ÐüÔRÕ‚ÕÖÖ.Ö’ÖäÖæÖôÖöְָײ×LÙNÙN݆ÝýèÓÑÏÍ¿±¯­«©§¥£¡ŸÓAAAAAAAAA A„^„„]„„Ð`„Ð A„^„„]„„Ð`„ÐAAA & F ƨ„¨^„¨„]„„Àý`„Àý & F ư„°^„°„]„„Pþ`„PþA†ÝÞÞBÞrÞßß ßL߀ߢߨßîßà,àýûùäâàÞд²¤–ˆ A„p^„p„]„„Ð`„Ð A„p^„p„]„„Ð`„Ð A„p^„p„]„„Ð`„ÐA A„^„„]„„Ð`„Ð A„^„„]„„Ð`„Ð A„^„„]„„Ð`„ÐAAA & F ƨ„¨^„¨„]„„Àý`„ÀýAAA,àbà„à¢àøà`áˆáŠá.â0âZâ\âäâæâããñãÕǹ«©§¥£¡“‘AAA A„p^„p„]„„`„AAAAA A„@ ^„@ „]„„Ð`„Ð A„@ ^„@ „]„„Ð`„Ð A„@ ^„@ „]„„Ð`„Ð A„@ ^„@ „]„„Ð`„Ð A„@ ^„@ „]„„Ð`„Ð A„p^„p„]„„Ð`„Ðã<æ>æ`æbæ è è:è<èêê&ê(êjìlì’ì”ìˆíŠíñïíëÝÛÙ×ÉÇÅõ³±¯¡ŸA A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„Ší¢í¤íHîJî€î‚îzò|òžò òhõjõŠõŒõ¶ö¸ö ÷÷ýûíëéçÙ×ÕÓÅÃÁ¿±¯­«AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„AA÷àøâøHùJùRùTù|ù~ù†ùÐù†üˆü¦ü¾üØüñïíëÝÛÙ×É´²°¢”† A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„AA & F ư„°^„°„]„„Pþ`„Pþ A„p^„p„]„„`„AAA A„p^„p„]„„`„AAA A„p^„p„]„„`„Øüôü ý"ý8ýjý–ýÒý8þNþhþ’þñãÕǹ«se A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ ’þ¼þØþÿ8ÿNÿfÿ„ÿ¢ÿ¼ÿØÿöÿñãÕǹ«se A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ öÿ@v¨ÂÜø $&ºñãÕǹ«sqoAA A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ A„p^„p„]„„`„ º¼|~âþ€”Ζ ¦    : J ` j 6FýûùääääÏ͸¶´¸²¸°¸®¸AAAA & F ƨ„¨^„¨„]„„Àý`„ÀýA & F ư„°^„°„]„„Pþ`„PþA & F Æp„p^„p„]„„˜þ`„˜þAAAFˆÜäFN`p~¼ýèæèäèÛÓÛËÛÛH$ ÆàÀ!H$ ÆàÀ! H$ ÆàÀ!G$AA & F ư„°^„°„]„„Pþ`„PþA ¼¾ÆÖö68@Pœ““““K““G$$4Ö\Ûß´#4Ö4Öl4Ö4Öl H$ ÆàÀ!G$b$$4Ö\Ûß´#4Ö4Öl4Ö4Öl ÖÖ(ÿÿÿ²ÿÿÿ²ÿÿÿ²ÿÿÿ²PpÎÐØèöö®ööööfX A„^„„]„„`„G$$4Ö\Ûß´#4Ö4Öl4Ö4ÖlG$$4Ö\Ûß´#4Ö4Öl4Ö4Öl H$ ÆàÀ!G$  "$&(*,.0b68:<>pýûù÷õóñïíëéçåãáßÝÛÙ×ÕÓÑIIIIHHHIIIIHHHHHHñ A„^„„]„„`„5°Ð/ °à=!°€"°€°€#¸$€2P1h3P(200P9°Ð/ °à=!°€"°€°€#¸°€$¸2P1h3P(200P5°Ð/ °à=!°€"°€°€#¸°€$¸2P1h3P(2(2 05°Ð/ °à=!°€"°€°€#¸°€$¸2P1h3P(2(2 05°Ð/ °à=!°€"°€°€#¸°€$¸2P1h3P(20 09°Ð/ °à=!°€"°€°€#¸°€$¸2P1h3P(200PRoot Entryÿÿÿÿÿÿÿÿ ÀF@CompObjÿÿÿÿjOle ÿÿÿÿÿÿÿÿ1Tableÿÿÿÿÿÿÿÿÿÿÿÿß-SummaryInformation(ÿÿÿÿÿÿÿÿTWordDocumentÿÿÿÿÿÿÿÿÿÿÿÿR©ÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿlibflaim-4.9.966/docs/flaim_4_database_format.doc0000644000175000017500000123600010510774540023215 0ustar ahodgkinsonahodgkinsonÐÏࡱá;þÿ ›þÿÿÿ€‚ƒ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿþÿÿÿ*  !"#$%&'()þÿÿÿþÿÿÿ,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~…Root Entryÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿ þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿ ÿÿÿÿ ÀFMicrosoft Word-Dokument MSWordDocWord.Document.8ô9²qvDd¼-¯î »±ð*² ð ð€"ñ?þÿà…ŸòùOh«‘+'³Ù0( p x „ œ ¨´Ìäðéý774@B*yV0 [bñÿbDefault$a$1$A$*$/B*OJQJCJmH sH PJnH ^JaJ_HÿtHÿ`` Heading 13@& & Fdà1$„^„„]„„`„$$CJ 5bb Heading 25@& & Fdh„^„„]„„`„¤à¤$CJ5XX Heading 3/@& & F„^„„]„„`„¤h¤<$5XX Heading 4/@& & F„^„„]„„`„¤ð¤<$5:: Heading 5$a$1$$CJ566 Heading 6 ¤ð¤<CJ6:: Heading 7 ¤ð¤< OJQJCJ>> Heading 8 ¤ð¤<OJQJCJ6@ @ Heading 9 ¤ð¤<OJQJCJ65BA@òÿ¡BAbsatz-Standardschriftart6þòÿñ6 WW8Num10z0OJQJCJ 656þòÿ6 WW8Num10z1OJQJCJ656þòÿ6 WW8Num10z2OJQJCJ656þòÿ!6 WW8Num10z3OJQJCJ65,þòÿ1, WW8Num13z0OJQJ6þòÿA6 WW8Num16z0OJQJCJ 656þòÿQ6 WW8Num16z1OJQJCJ656þòÿa6 WW8Num16z2OJQJCJ65<þòÿq<Default Paragraph Font6þòÿ6Footnote Characters&)r‘& Page Number.B¢. Text body ¤¤x /¡² List^J@þÂ@Caption ¤x¤x $CJ6^JaJ]&þÒ&Index $^JFþ¢FHeading ¤ð¤x$OJQJCJPJ^JaJ:: Contents 1 ¤x¤x ;CJ5HH Contents 2 „ð^„ð„]„„`„:CJHH Contents 3!„à^„à„]„„`„CJ6DD Contents 4"„Ð^„Є]„„`„CJDD Contents 5#„À^„À„]„„`„CJDD Contents 6$„°^„°„]„„`„CJDD Contents 7%„ ^„ „]„„`„CJDD Contents 8&„^„„]„„`„CJDD Contents 9'„€^„€„]„„`„CJJþÑ‚J Contents 10"( Æô& „ó ^„ó „]„„`„4þ’4Table Contents) $Dþ‘¢D Table Heading *$a$ $ 65]\0þ¡²0Frame contents+. Â.Footer, Æzô& $^þáÒ^Contents Heading-„^„„]„„`„ $CJ 5aJ \4>ò4Title.$a$OJQJCJ58Já¢8Subtitle/$a$CJ6aJ] (8Nbèéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿ ÿÿÿÿ ÿÿÿÿ (8Nbèéî  À${o˜Ôÿÿÿÿ$ÔÿÿÿÿSÔÿÿÿÿ‚Ôÿÿÿÿ±Ôÿÿÿÿ Õÿÿÿÿ9ÕÿÿÿÿeÊÊÊÊÊÊÊÊÊÊÊÊÊÊÊÌ®Œ3ª{"‡B (°öÈBå|î¨÷Lÿ ¢i¢oàrÐwT‘$œÊ·jí~ð€ à–9¤MrÜ’Œ§:¼Z×–ÝdÚ2‚5›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½,46>ŠQˆW€l„ô„6…¸…:†È†ò‰˜œ¼Ÿ( † î£F¤¬¤î¤H¥¥¦ì¨B©à«T®Š°Ê°j¸œ¸Þ¸ÆxÈ¶ÈøÈZÍšÍpÑÒLÒXÕTÞüÞ´àáÄäåbæ.çŠçÎçèlìÌî2ï„ïÈï ð`ð¼ðñžø*ùùúûJûHü¢üjýÚýTþÿªÿ ª ô8H˜Î~Ò €†>Bn¾opjpœp¼p0qRuÈu0vèv’w2x²xR,°‘æ‘V’ “r”0–8¯nÕÀíîlî´îÞî þ¦ÿâ¨N€ ® ì"(%î(b)‚/ö>ŠKnUb¶o¨{€ƒjŠÈ¼—œ¯ZÄÕÖjÖÌÖLגר’ØâÛ"ÜV܆ܶܿÜÝFÝvÝšÝäñb2¨34„5¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghiD F&F*F.GHGLGŽ_–_™_^´x´z´ÿ´µµT·n·p··ÆÑÆÕƧÇÁÇÅÇþÇÈÈÇÙáÙåÙ?öYö[ö5OSì v”›"µ"»"K"K(K MºMÀM¬QÆQÌQER_ReRì[\ \|m–mœm“}­}³}‘‡«‡±‡ — ÿ€ÿ€ÿ€“_ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€ÿ€V]`»ÂÅË!ÿ€!ÿ€ð8ð@ñ÷ðN ððòð( ð€ÿÿ€ÿÿ ððnB ð@ “ ð6D¿ÀÂÿÿÿË$Öÿ?C"ñ‘’ðnB ð@ “ ð6D¿ÀÂÿÿÿË$Öÿ?C"ñ‘’ð¼¢ ð  ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð  ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð  ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð  ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð  ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ððtB ð @ £ ð<D¿ÀÂÿÿÿË$ÎÖÿ?C"ñ‘’ðtB ð  £ ð<D¿ÀÂÿÿÿË$ÎÖÿ?C"ñ‘’ð¼¢ ð   ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð   ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¼¢ ð   ð`€—q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷ÀC"ñ‘’ðð ðð¶¢ ð  ð`€ —q‚ÿ¾ƒ—q„ÿ¾ˆÿÿÿƒ¿ÀÂÿÿÿËÎÍÿ„÷À†÷À3"ñ’?ðð ð ð< ðC ðÀËÿð¤M¥M¦M§M¨M©MªM­M®M¯M´MµM—_˜X Tx!TtæRÙ •tÖ R9 •t6 R9•t6R™•t–RY •t fRé•tX lx!lt xlØ ìt 8 lìt vši mt f š mtí t€ÿÿ _Ref512917491 _Ref513009791 _Ref513009866 _Ref512995061 _Ref512928900 _Ref512928970 _Ref512930807 _Ref512998915 _Ref513001668 _Ref513868105 _Ref513005921 _Ref513947492 _Ref513282791 _Ref513356611 _Ref513356417tÕyÕyÃÄ6á6á6áùù|åDÏUUr\yÍ}ö— +téyéyÙÄOáOáOá@ù@ù†EßUerdyø}ö—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„„ÆOJQJCJ 65.„„ÆOJQJCJ65.„„ÆOJQJCJ65..„„ÆOJQJCJ65... „¸„èüƸ ..... „° „Xüư  ...... „¨ „Èûƨ ....... „ „8ûÆ ........ „à„`úÆà.........„h„˜þÆhOJQJ·ð„„ÆOJQJCJ 65.„„ÆOJQJCJ65.„„ÆOJQJCJ65..„„ÆOJQJCJ65... „¸„èüƸ ..... „° „Xüư  ...... „¨ „Èûƨ ....... „ „8ûÆ ........ „à„`úÆà.........ÿÿÿÿÿÿÿÿÿÿÿÿÿÿOutlineWW8Num13Outlineÿ@è?@@"@6@7@W@Y@[@`@a@z@|@~@ƒ@„@‘@“@–@›@œ@Ñ@Ô@×@Ü@Ý@AAAAAXA[A^AcAdA‘A•A™AŸA A»M¼MÊMÞMßMüMþMNN NNNN!N#N(N)NNCNDNáOíO÷OøOúO P PP"P#P%P7P8P:PEPFPHPVPWPYPgPhPjPvPwPyP‡PˆPŠP–P—PšP¤P¥P¨P¶P·PºPÇPÈPËPÛPÜPßPúPûPþPQQLRMR[RuRvR…R‡RŒRR˜RšRŸR R°S±S¿SÙSÚSéSëSðSñS T TTTVVV.V/V>V@VEVFVRVTVYVZV^V`VeVfVZ#Z5Z6Z>Z@ZAZKZMZNZTZVZWZ^Z`ZaZfZoZpZ bb(b)b.b0b1b:bu[urusu‡uŠu‘u™ušu¯u²uºuÂuÃuÕuØuÜuäuåuñuôuýuvvvv(v0v1vJvMvVv^v_vtvyvƒv‹vŒvOzazoz~z•z–z»z½zÃzÈzÉzúzüz{{{5{7{>{C{D{t{v{~{ƒ{„{•{—{Ÿ{¤{¥{è{ê{ò{÷{ø{|||$|%|A|C|K|P|Q|w|y||†|‡|«|­|µ|º|»|Þ|à|è|í|î|}} }}}*},}4}9}:}u}w}}„}…}Æ}È}Ð}Õ}Ö}õ}÷}ÿ}~~~~'~,~-~F~H~P~U~V~v~x~€~…~†~~~—~œ~~ð~ò~õ~ú~û~=?BGHšœŸ¤¥¼¾ÆËÌ€€€€€V€Y€b€g€h€¬€¯€¹€¾€¿€WZdijsu„…Õ×ßä圵°µ¿µÏµÐµÞµâµêµëµ¶¶¶¶'¶+¶4¶5¶>¶@¶B¶C¶L¶N¶P¶Q¶Z¶\¶^¶_¶v¶z¶˜¶™¶‘¸±¸Â¸ã¸ä¸ø¸ú¸¹¹¹¹¹¹M¹O¹Q¹R¹l¹n¹t¹u¹ª¹¬¹³¹´¹Ç¹É¹Ñ¹Ò¹ú¹ü¹ÿ¹º+º-º0º1ºMºPºXºYº’ũźÅÔÅÕÅÆÆÆÆ8Æ:Æ@ÆAÆXÆZÆ`ÆaÆtÆvÆ}Æ~Æ‰Æ‹ÆŽÆÆíÆïÆòÆóÆÇÇ Ç Ç Ç"Ç*Ç+ÇHÇJÇRÇSÇ{Ç}DždžÇÝÇßÇçÇèÇ4È6È9È:ȄȆȉȊȶôÎôßôñôòôõõ õ õõ"õ4õ5õ;õAõGõHõNõTõZõ[õaõgõmõnõyõƒõ•õ–õLýTý[ýgýhýjýsýÓýÔýÕýÞýåýæýçýòýqþrþsþ~þDÿEÿGÿHÿTUYZ‰ŠŒ§¨·¸mn†Ž•¡¢¤­vwx  ”•–¡ghjkwxz{“”–—±²ÁÂåæÂhÕhðhþhÿhiiiiiii i&i)i5i6i— 6 Å#V{)@> h»)8õ),ÙFLAIM 4 Database Format Provides details of the FLAIM 4 database file format "Novell, Inc. makes no representations or warranties with respect to the contents or use of this documentation, and specifically disclaims any express or implied warranties of merchantability or fitness for any particular purpose. Further, Novell, Inc. reserves the right to revise this publication and to make changes to its content, at any time, without obligation to notify any person or entity of such revisions or changes. Copyright © 1991,2006 Novell, Inc. All rights reserved. THIS DOCUMENTATION MAY NOT BE REVISED OR MODIFIED WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC.  THE DOCUMENTATION IS PROVIDED "AS IS."  IN NO EVENT SHALL NOVELL OR THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE DOCUMENTATION OR THE USE OR OTHER DEALINGS IN THE DOCUMENTATION." Table of Contents  TOC \o "1-9" \t "Heading 4;4;Heading 3;3;Heading 2;2;Heading 1;1" 1.1 Database Files 1 1.2 Control File (xxx.db) 1 1.3 Lock File 1 1.4 Data Files 2 1.4.1 Naming Convention for Pre 4.3 Databases 2 1.4.2 Naming Convention for 4.3 Databases (and beyond) 2 1.5 Rollback Log Files 3 1.5.1 Naming Convention for Pre 4.3 Databases 3 1.5.2 Naming Convention for 4.3 Databases (and beyond) 3 1.6 Maximum Data File and Rollback File Sizes 4 1.7 Block Addresses 4 1.8 Roll Forward Log Files 5 1.8.1 Naming Convention For Pre-4.3 Databases 5 1.8.2 Naming Convention For 4.3 Databases (and beyond) 5 1.8.3 Roll Forward Log File Header 6 1.8.4 Roll Forward Log File Layout 7 2. First Block of the Control File (xxx.db) 13 2.1 Database Header Structure 13 2.2 Log Header Structure 14 3. Logical Files (Containers and Indexes) 20 3.1 LFH Block Structure 20 3.2 Logical File Header Structure 21 4. Block Header Structure 23 4.1 Avail List Block Linkages 25 5. B-Tree Organization 27 5.1 Non-Leaf Variable Key Size Blocks 27 5.2 Non-Leaf Variable Key Size With Counts Blocks 28 5.3 Non-Leaf Fixed Key Size Blocks 29 5.4 Leaf Blocks 29 5.4.1 Next DRN Marker Element 30 5.5 Rightmost Elements 30 6. Data Portion of Leaf Elements 31 6.1 Data Record Structure 31 6.1.1 Standard Field 31 6.1.2 Open Field 31 6.1.3 Free Field 32 6.1.4 No Value Field 33 6.1.5 Set Field Level 33 6.1.6 Field Value Formats 33 6.2 Reference Set Structure 36 6.2.1 SEN (Simple Encoded Number) Format 36 6.2.2 Reference Set Format 36 7. Index Key Structure 38 7.1 Compound Markers 38 7.2 Truncated Pieces 38 7.3 Valueless Pieces 38 7.4 First Substring Pieces 38 7.5 Binary Index Piece 39 7.6 Context Index Piece 39 7.7 Field ID Index Piece 39 7.8 Numeric Index Piece 39 7.9 Text Index Piece 40 7.9.1 Tables for Mapping Characters to Collation Values 40 7.9.2 Sub-Collation Format 41 7.9.3 Case Information 41 7.9.4 Post Compound Indexes 42 7.9.5 Language 42 7.9.6 Two-Byte Collation Values (Asian Languages) 42 7.9.7 Hebrew and Arabic Sorting 44 Database Files A FLAIM database consists of five types of files: 1) A control file, 2) lock file, 3) data files, 4) rollback log files, and 5) roll-forward log files. The name of the database is the name of the control file. The names of all other files are based on the name of the control file. The naming convention and usage of each type of file is explained below. Control File (xxx.db) The control file name is expected to conform to the following convention: xxx.db where xxx is a one to three character string. All other file names are derived from the xxx name. The first block of the control file is reserved for some application information, a log header, and a database header. These are described in the next section. The rest of the file is actually part of the rollback log space in the database. Because the rollback log can grow and shrink, it is common to see the control file change its size. Lock File The lock file name is xxx.lck. It resides in the same directory as the control file and is used to prevent multiple processes from opening a database at the same time. On Netware and Windows platforms, the lock file is created and opened in exclusive mode. When FLAIM first opens a database, it will attempt to create and open this file. When FLAIM finally closes a database, the lock file will be deleted. The mere existence of the lock file does not mean that the database is currently open by some process. It may be that the process has aborted without shutting down FLAIM, or the system crashed before FLAIM could close the database. Thus, when opening a database for the first time, if the file already exists, FLAIM will attempt to delete the file first. If it cannot delete the file, it knows that the database is currently being accessed by another process or NLM, and it will return an access denied error. On Unix platforms there is no way to open a file in exclusive mode. In addition, it is possible to delete a file that another process has opened. Therefore, FLAIM uses the lock file in a slightly different way. Instead of deleting and re-creating the file every time it opens a database, the file is created when the database is first created, and remains as long as the database remains. To prevent multiple processes from accessing the database, FLAIM will put a byte lock on byte zero of the file. If it cannot obtain the byte lock, it knows that another process has already obtained the byte lock and is accessing the database, and an access denied error will be returned. Data Files The data files are used to store all of the blocks of the database, including data blocks, index blocks, available blocks, etc. Data files reside in the same directory as the control file, and have the same xxx name as the control file, but different extensions. Each data file has a number that is encoded into the extension. The total number of data files and the naming conventions for data files depends on the version of the database. Naming Convention for Pre 4.3 Databases For versions of the database prior to 4.3, the maximum number of data files is 511 (file numbers 1 through 511). The extension for a data file is its file number encoded as a two digit base 24 number. The ASCII characters used to represent the 24 values (0-23) of a base 24 digit are as follows: Numeric Value ASCII Digit 0-9  0   9 10-23  g ,  h ,  j ,  k ,  m ,  n ,  p ,  q ,  s ,  t ,  w ,  x ,  y ,  z The following examples illustrate: Data File Number Data File Name 1 xxx.01 2 xxx.02 3 xxx.03 Naming Convention for 4.3 Databases (and beyond) For versions of the database of 4.3 and beyond, the maximum number of data files is 2047 (file numbers 1 through 2047). For file numbers 1 through 511, the same naming convention that exists for pre-4.3 databases is used. That is, the extension for a data file is its file number encoded as a two digit base 24 number (see above). For file numbers 512 through 2047, the file number mod 512 is used to encode the first two digits as a two digit base 24 number, and then an additional third digit is added to the extension, as follows: Data File Numbers Additional Third Digit 512 through 1023  r 1024 through 1535  s 1536 through 2047  t The following examples illustrate: Data File Number Data File Name 1 xxx.01 2 xxx.02 512 xxx.00r (512 mod 512 is 0) 513 xxx.01r (513 mod 512 is 1) 1024 xxx.00s (1024 mod 512 is 0) 1025 xxx.01s (1025 mod 512 is 1) 1536 xxx.00t (1536 mod 512 is 0) 1537 xxx.01t (1537 mod 512 is 1) Rollback Log Files The rollback log files are used to log blocks of the database. Rollback logging has three primary purposes: 1) to undo a transaction when it aborts, 2) to recover a database to its last checkpointed state when doing database recovery after a system crash, and 3) to maintain read-consistent views of the database for read transactions. The control file is actually also a rollback log file, except for its very first block (see explanation above). It is considered to be rollback log file number zero. If this file fills up because of a very large transaction (a circumstance that will be very rare), additional rollback log files will be created. These additional rollback log files reside in the same directory as the control file, and will have the same xxx name as the control file, but different extensions. Each additional rollback log file has a number that is encoded into its extension. The total number of rollback log files and the naming conventions for data files depends on the version of the database. Naming Convention for Pre 4.3 Databases For versions of the database prior to 4.3, the maximum number of rollback files is 513 - file number 0 (the control file), and file numbers 512 through 1023. The file name for file number zero is, of course, xxx.db. Additional rollback log files (512 through 1023) use the file number mod 512 to encode a two digit extension (base 24 format described above), and then add on a third digit of  x so that the extension does not conflict the extensions used for data file names. The following are some examples: Rollback Log File Number Rollback Log File Name 512 xxx.00x (512 mod 512 is 0) 513 xxx.01x (513 mod 512 is 1) 514 xxx.02x (514 mod 512 is 2) Naming Convention for 4.3 Databases (and beyond) For versions of the database of 4.3 and beyond, the maximum number of rollback log files is 2049 - file number 0 (the control file), and file numbers 2048 through 4095. The file name for file number zero is, of course, xxx.db. Additional rollback log files (2048 through 4095) use the file number mod 512 to encode a two digit extension (base 24 format described above), and then add on a third digit as follows: Rollback Log File Number Additional Third Digit 2048 through 2559  v 2560 through 3071  w 3072 through 3583  x 3584 through 4095  z Below are some examples: Rollback Log File Number Rollback Log File Name 2048 xxx.00v (2048 mod 512 is 0) 2049 xxx.01v (2049 mod 512 is 1) 2560 xxx.00w (2560 mod 512 is 0) 2561 xxx.01w (2561 mod 512 is 1) 3072 xxx.00x (3072 mod 512 is 0) 3073 xxx.01x (3073 mod 512 is 1) 3584 xxx.00z (3584 mod 512 is 0) 3585 xxx.01z (3585 mod 512 is 1) Maximum Data File and Rollback File Sizes Prior to database version 4.3, the maximum file size for both data and rollback files was fixed at 0x7FFF0000 (roughly 2 gigabytes). With the maximum number of data files being 511, this meant that a database could only grow to be just under 1 terabyte. In database versions 4.3 and greater, the maximum file size for data and rollback files has been increased to 0xFFFC0000 (almost 4 gigabytes). Because 4.3 databases allow up to 2047 data files, database capacity has been increased to almost 8 terabytes. This will always be the case for newly created databases. However, databases that are converted from a version prior to 4.3 up to version 4.3 may not be able to have a maximum file size of 0xFFFC0000. This would happen if the database already has more than one data file created (xxx.02, xxx.03, etc.) at the time it is converted. In this case, FLAIM has to set the maximum file size to the old limit, which is 0x7FFFF0000 (about 2 gigabytes). With 2047 data files, this still increases the database capacity to almost 4 terabytes. Note on the reason for choosing 0xFFFC0000 as the new maximum file size: This odd number was chosen because of a bug that was discovered in the Netware Legacy file system that only allows a file to grow to one block less that 0xFFFFFFFF when operating in direct I/O mode. Assuming a block size of 64K (normal for most Netware Legacy file systems), this means we could have had a maximum of 0xFFFF0000. However, since we do not know what the maximum possible block size is, we had to assume that there was a chance it could be more than 64K on some systems, but would probably never be more than 256K - thus the limit of 0xFFFC0000 - which is 256K less than 0xFFFFFFFF. Block Addresses A block address has two components. The lower 12 bits represents a file number. The upper 20 bits represents a 4K block offset within the file. To obtain the offset for a block, it is only necessary to mask off the lower 12 bits of the block address. To obtain the file number, it is only necessary to mask off the upper 20 bits of the block address. Using the lower 12 bits for a file number allows for file numbers 0 to 4095, which covers all possible data file numbers and rollback log file numbers. However, for versions of the database prior to 4.3, only 10 of those bits were used, limiting the range of file numbers to 0 to 1023. File number zero is reserved for the control file (xxx.db) in all database versions. The first block in the control file (offset 0) is reserved for the database header and log header. Blocks coming after the first block are rollback log blocks. For database versions 4.3 and greater, file numbers 1 through 2047 are data files, and file numbers 2048 through 4095 are additional rollback log files. For database versions prior to 4.3, file numbers 1 through 511 are data files, and file numbers 512 through 1023 are additional rollback log files. Roll Forward Log Files FLAIM logs the operations of transactions to a roll-forward log. Roll-forward log files are used to recover transactions after a system failure and when restoring a database from backup. Naming Convention For Pre-4.3 Databases In database versions prior to 4.3, a single roll-forward log file is used. It is named xxx00001.log and resides in the same directory as the other database files (xxx.db, xxx.01, etc.). Transactions are only kept until a checkpoint is performed on the database, at which time the roll-forward file is reset and reused. Naming Convention For 4.3 Databases (and beyond) In version 4.3 and beyond an administrator can request that transactions logged to the roll-forward log be kept for recovery purposes if needed. When this mode is employed, multiple log files are utilized instead of just one. Roll-forward log files are not reset and reused when checkpoints are performed. Instead, the roll-forward log continually grows. For all practical purposes, a single file with a 64 bit address space would be more than adequate for thousands of years worth of transactions, given the transaction rate we can realistically sustain. However, there are a couple of reasons it is not practical or useful to simply keep growing a single file, even one with 64 bit capacity. First, not all operating systems support 64 bit files (Netware's legacy file system only allows 4 gigabytes - 32 bit address space - per file). Second, in the design of hot continous backup, it was desirable that an administrator be allowed to move older portions of the roll-forward log to tape or some other backup media, thus conserving disk space on the volume where the roll-forward log files are kept. To achieve this, the roll-forward log is broken into multiple files. Each log file has a sequence number. The sequence number is written into a header within the file and is also encoded into the log file's name. In database versions 4.3 and beyond, roll-forward log files are stored in a subdirectory called xxx.rfl. Unless otherwise specified by an administrator, this subdirectory is located in the same directory as the other database files (xxx.db, xxx.01, etc.). If an administrator specifies a different directory for the roll-forward log files, an xxx.rfl subdirectory will still be created within the specified directory. For example, if an administrator specified sys:\rflfiles as the directory for roll-forward log files, FLAIM would create an xxx.rfl subdirectory: sys:\rflfiles\xxx.rfl. Roll forward log files in the xxx.rfl subdirectory will be named as nnnnnnnn.log, where nnnnnnnn is a hex number that is the log file's sequence number. Thus, log file number 1 is named 00000001.log, log file number 2 is named 00000002.log, and so forth. Roll Forward Log File Header The first 512 bytes of every roll-forward log file is reserved for a log file header. Not all 512 bytes are currently in use. The header is formatted as follows: ROLL-FORWARD LOG FILE HEADERSIZE IN BYTESOFFSET IN FILESTARTING HEX OFFSETRoll-forward Log File Signature800x00Log File Sequence Number480x08Log File EOF4120x0CDatabase Serial Number (Only used in 4.3 and beyond)16160x10Log File Serial Number (Only used in 4.3 and beyond)16320x20Next Log File Serial Number (Only used in 4.3 and beyond)16480x30Keep Signature (Only used in 4.3 and beyond) 16 64 0x40 NOTE: Unless otherwise specified, 32 bit (4 byte) numeric values and 16 bit (2 byte) numeric values are stored in little-endian byte order. Roll-forward Log File Signature. This is an eight byte string that contains the string  RFL31.00 . When a log file is opened, this signature is used to verify that the file is a roll-forward log file. Log File Sequence Number. This is the sequence number for the log file. When a roll-forward log file is opened, there is an expectation of what sequence number the file represents. The expected sequence number will be verified against the value stored here. Log File EOF. This is the offset in the file where the roll-forward data ends. Anything after this point in the file is unused. NOTE: This field may contain a value of zero, which signifies that the EOF has not yet been determined. FLAIM only updates this field when it switches to a new roll-forward log file. It would be too costly to update it at the end of every transaction. Until FLAIM switches to a new file, the EOF for the current roll-forward log file is kept in the log header for the database (see section  REF _Ref513009791 \n \h 2.2). Database Serial Number. This is only present in database versions 4.3 and greater. It is the serial number of the database this roll-forward log file belongs to. It is the database serial number that is stored in the database s log header (see section  REF _Ref513009866 \n \h 2.2). When a roll-forward log file is opened, this serial number is compared to the serial number in the database s log header to make sure they are the same. Log File Serial Number. This is the serial number for this log file. The serial number is assigned when the log file is first created. During restore operations, the serial number is verified as the restore process opens roll-forward log files. Next Log File Serial Number. This is the serial number that should appear on the next roll-forward log file in the sequence. During restore operations, when moving from one roll-forward log file to the next, the chain of serial numbers is verified. Keep Signature. This is a string that indicates whether this roll-forward log file is being operated in  keep mode or  no-keep mode. The string is 16 characters long, including the null terminating character. When operating in keep mode, the string is:  ----KeepLog---- When operating in no-keep mode, the string is:  --DontKeepLog-- When FLAIM switches from keep mode to no-keep mode, it will go to the next roll-forward log file in the sequence. Thus, the keep signature on a log file is permanent. A string was chosen so that it could be easily seen by someone who wanted to just try to list the file. Roll Forward Log File Layout After the header, the data in the roll-forward log file consists of a sequence of packets, written one after the other. Packets are variable size and each consists of a header and a body. The information in the header identifies the packet type and allows FLAIM to determine the format of the data in the body.    Size In BytesStarting Hex OffsetAddress of Packet in the RFL40x00Checksum10x04Packet Type10x05Packet Body Length20x06 Address of Packet in the RFL: specifies the offset from the start of the RFL. This information is actually redundant; FLAIM uses it as a double check. Checksum: This is a checksum of the packet type and length fields in the header, and the data in the packet body. Packet Body Length: Specifies the length of the packet body (in bytes). Packet Type: FLAIM currently supports 15 types of packets in the RFL: Type NumberType Name1Begin Transaction2Commit Transaction3Abort Transaction4Record Add5Record Modify6Record Delete7Reserve DRN8Change Fields9Data Record10Index Set11Start Unknown12Unknown Data13Reduce Database14Extended Begin Transaction15Upgrade Database Begin Transaction Packet A Begin Transaction packet marks the start of data for a specific transaction. There should be a corresponding Commit Transaction or Abort Transaction packet to mark the end of data for that transaction. The body of a Begin Transaction packet is 8 bytes long and contains two elements: Size In BytesOffset from Start of BodyTransaction ID40x00Time (GMT)40x04 Commit Transaction and Abort Transaction Packets The Commit Transaction and Abort Transaction ýÿÿÿýÿÿÿýÿÿÿýÿÿÿýÿÿÿ†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™šþÿÿÿœþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpackets record how a transaction finished ¾ð either committing or aborting. The bodies of these packets are exactly the same. They are 8 bytes long and contain two elements: Size In BytesOffset from Start of BodyTransaction ID40x00Transaction Begin Address40x04 The second element, Transaction Begin Address, is the offset in the RFL of the Transaction Begin packet that corresponds to this packet. This field is no longer used in FLAIM and is kept only for compatibility. Record Add, Record Modify, Record Delete and Reserve DRN Packets The Record Add, Record Modify and Record Delete packets signify that a change is going to be made as part of the current transaction. They all have the same bodies. They are 10 bytes long and contain three elements: Size In BytesOffset from Start of BodyTransaction ID40x00Container #20x04DRN40x06 Record Add packets are followed by one or more Data Record packets that contain the actual data to add. Record Modify packets are followed by either one or more Change Field packets or one or more Record Add packets, depending on which is the most efficient means of representing the modifications. (Both of these packet types are described in further detail below.) A Reserve DRN packet is used to specify that a particular DRN has been reserved by the application. This prevents FLAIM from issuing that DRN when it creates a new record. Its body is exactly the same as the Record Add, Record Modify and Record Delete packets. Data Record Packet Data Record packets consist of a sequence of fields. A field in a Data Record packet contains all the data to completely specify one field in a FLAIM record. There can be multiple fields in a single packet, and one field can span multiple packets. There are five parts to each field: Description:Length (in bytes)Field #2Data Type1Level1Length2Datavariable A Field # of 0 (which is an illegal number for a FLAIM field) indicates that there is no more data for this particular record. In this case, the packet will end immediately after the 0. (This is analogous to a Null-terminated string in a C program.) Note that only the Data element will be split across a packet. If any of the other elements would cause the packet to exceed its maximum size, then the entire field will be put in another Data Record packet. Change Field Packet A Change Field packet consists of a sequence of "deltas", or individual changes to a single record. There can be multiple deltas in a single Change Field packet, and a single delta can span multiple packets. The first three bytes of each delta consist of one byte of type information and two bytes to specify the position of the field that is being changed. There are four types of changes that can be made: insert field (type 1), delete field (type 2), modify field (type 3) and end-of-changes (type 4). Position is a field's ordinal position within a record. This should not be confused with a field's level, which is a measure of how deep the field is in the record's hierarchy. For example, in the record shown below Name is at level 1, but position 5. (Level numbers start at 0, but position numbers start at 1.)  SHAPE  The end-of-changes type signifies there are no more changes to be made to the current record and the Change Fields packet will end immediately after the 4. (This is similar to what happens when a field in a Data Record packet has a field number of 0.) For the other three types of changes, the structure of the rest of the delta depends on the type of change. Delete field removes a field from the record. This is the simplest type of delta. It consists only of the type and position bytes ¾ð no further information is needed. Insert field is used to insert a new field into the record. Its format is shown below: Description:Length (in bytes)Type1Position2Field #2Data Type1Level6Data Length2Datavariable Position is the position within the record where the new field will be inserted. (For example, if Position is 4, then all the fields whose position is currently 4 or higher will be moved down, and the new field will be inserted at position 4.) Modify field is used to change an existing field in the record. There are three modification operations currently defined: replace all bytes, insert bytes, and delete bytes. However, only the replace all bytes operation is currently used. Its structure is defined below: DescriptionLength (in bytes)Type1Position2ModType1Length2Datavariable (The structures for the insert bytes and delete bytes have not yet been defined.) ModType is the operation that is being performed (1 for replace, 2 for insert and 3 for delete). Length is the number of bytes in the data field and data is the new data. Index Set Packet An Index Set packet is used to generate the keys for an index. (An index is created by adding its definition record to the Dictionary container, but this does not generate the keys for that index.) TypeSize in BytesOffset From Start of Packet BodyTransaction ID40x00Index #20x04Start DRN40x06End DRN40x0A The Transaction ID and Index # elements are self explanatory. The Start DRN and End DRN fields indicate which records are to be indexed during this transaction. This is necessary because indexes created in the background will use multiple transactions to generate their keys a few at a time, instead of generating all of them at once. Start Unknown and Unknown Packets Unknown packets are used to store application-specified data. (Unknown, in this case refers to the fact the FLAIM doesn't know what the data is. It is assumed that the application knows how to interpret the data in these packets.) The body of a Start Unknown packet consists of only 4 bytes which hold the transaction ID. Any number of Unknown packets follow the Start Unknown packet and have unspecified content. (These packets are analogous to binary fields in the database. That is, FLAIM will simply read the data and pass it right to the application; it does not try to interpret the data at all.) These packets may be used by the application during a restore operation; they are ignored during a recovery. Reduce Database Packet This packet records the fact that a Database Reduce operation has been performed. It consists of a Transaction ID and a Count. Count is the number of blocks that the application has indicated that FLAIM should try to remove from the database. (Note that this is not necessarily the number of blocks that were actually removed.) TypeSize in BytesOffset From Start of Packet BodyTransaction ID40x00Count40x04 This packet type is new to version 4.3. Extended Begin Transaction This is a replacement for the Begin Transaction packet. It was added to version 4.3 and has one additional element: Last Logged Committed Transaction ID. Size In BytesOffset from Start of BodyTransaction ID40x00Time (GMT)40x04Last Logged Committed ID40x08 This new element holds the ID number of the last transaction to be both committed and logged to the RFL. It is used during RFL playback to ensure no transactions are missed (which could happen if an RFL file was corrupted). Database Upgrade This packet is used to record the fact that a Database Upgrade operation has occurred. It consists of the transaction ID of the upgrade as well as the old and new version numbers. Size In BytesOffset from Start of BodyTransaction ID40x00Old Version40x04New Version40x08 First Block of the Control File (xxx.db) The first block of the control file (xxx.db) reserves room for the items shown in the table below: FLAIM 4.x FORMAT OF FIRST BLOCK SIZE IN BYTES OFFSET IN Control File STARTING HEX OFFSET Application Information 16 0  15 0x0000 Log Header 156 16-171 0x0010 Database Header 172 1876  2047 0x0754 Application Information. This 16 bytes is the area which is reserved for application information. FLAIM will initialize this area when the database is created. The application data is supplied by the application as part of the CREATE_OPTS structure on database creation. Database Header. This 172 bytes contains information about the high level layout of the database. Log Header. This 156 bytes contains information which is used during database transactions to preserve the logical integrity of the database. Database Header Structure The database header begins at byte 1876 (0x0754) in the control file (xxx.db) and is 172 bytes long. The database header contains information about the high level layout of the database. It is structured as follows: ITEMS IN DATABASE HEADER SIZE IN BYTES OFFSET IN Database Header STARTING HEX OFFSET FLAIM Version Info 9 0 - 8 0x0754 UNUSED - zero value 4 9 - 12 0x075d Default Language 1 13 0x0761 Block Size 2 14 - 15 0x0762 UNUSED - zero value 16 16 - 31 0x0764 First LFH Block Address 4 32 - 35 0x0774 UNUSED - zero value 136 36 - 171 0x0778 NOTE: Unless otherwise specified, 32 bit (4 byte) numeric values and 16 bit (2 byte) numeric values are stored in little-endian byte order. FLAIM Version Info. This contains an ASCII string which tells the version of the database format. FLAIM checks this when it opens the database to make sure we are really accessing a FLAIM database and that it is accessible under the current version of the FLAIM software. Default Language. This contains a number which specifies the default language for indexes in the database. If an index definition does not explicitly specify a language, the language specified here will be used. Block Size. This contains the size (in bytes) of blocks in the database. All blocks in the database are the same size. First LFH Block Address. This is the address of the first LFH block in the database. Log Header Structure The log header begins at byte 16 (0x10) in the control file (xxx.db), and is structured as follows: LOG HEADER ITEMS SIZE (BYTES) HEADER OFFSETOFFSET IN CONTROL FILECurrent Roll-forward Log File Number40 - 30x10Last Transaction Offset in Roll-forward log file44 - 70x14Last Checkpoint Roll-forward Log File Number48 - 110x18Last Checkpoint Offset in Roll-forward log file412 - 150x1CRollback Log EOF416 - 190x20Incremental Backup Sequence Number (Not used prior to version 4.3)420 - 230x24Last Database Transaction ID424 - 270x28Committed Transaction Count428 - 310x2CFirst Checkpoint Rollback Log Address432 - 350x30 Last Roll-forward Log File Deleted436 - 390x34Minimum Roll-forward Log File Size440 - 430x38Log Header Checksum244 - 450x3ADatabase Version Number246 - 470x3CLast Backup Transaction ID (Not used prior to version 4.3)448 - 510x40Blocks Changed Since Last Backup (Not used prior to version 4.3)452 - 550x44Last Checkpoint Transaction ID456 - 590x48First Backchain Address460 - 630x4CAvail List Block Address464 - 670x50Logical End Of Database Address468 - 720x54Unused472 - 750x58Keep Aborted Transactions in Roll-Forward Log Flag (Not used prior to version 4.3)1760x5CFirst Backchain Count1770x5DKeep Roll-forward Log Files Flag1780x5EAuto-Turn-Off Keep of Roll-Forward Log Files Flag (Not used prior to version 4.3)1790x5FNumber of Avail Blocks480 - 830x60Maximum Roll-forward Log File Size (Not used prior to version 4.3)484 - 870x64Database Serial Number (Not used prior to version 4.3)1688 - 1030x68Last Roll-forward Log Serial Number (Not used prior to version 4.3)16104 - 1190x78Next Roll-forward Log Serial Number (Not used prior to version 4.3)16120 - 1350x88Incremental Backup Serial Number (Not used prior to version 4.3)16136 - 1510x98Not used2152 - 1530xA8Maximum Data and Rollback File Size (64K units. Not used prior to version 4.3)2154-1550xAA NOTE: Unless otherwise specified, 32 bit (4 byte) numeric values and 16 bit (2 byte) numeric values are stored in little-endian byte order. Current Roll-forward Log File Number. This contains the roll-forward log file number of the log file where the last update transaction ended. Last Transaction Offset in Roll-forward Log File. This contains the offset within the current roll-forward log file where the last update transaction ended. NOTE: This may contain a value of zero, which is a special value to indicate that we have not yet written to the current roll-forward log file. When we do write to it, if the file already exists, we will overwrite it, and the logging will actually start at offset 512. Last Checkpoint Roll-forward Log File Number. This contains the roll-forward log file number of the log file where the last checkpoint ends. Last Checkpoint Offset in Roll-forward Log File. This contains the offset within the last checkpoint roll-forward log file where the last check point ends. After a system crash, FLAIM restores the database to the state of the last checkpoint and then starts from this offset and redoes any transactions in the roll-forward log from this point to the point in the roll-forward log where the last committed transaction was logged. NOTE: This should never be less that 512. Rollback Log EOF. This contains the address in the rollback log where the rollback log ends. Incremental Backup Sequence Number. This is the sequence number that will be assigned to the next incremental backup that is taken. NOTE: This is only used for database versions 4.3 and greater. Last Database Transaction ID. This contains the transaction ID number of the most recently completed (committed or aborted) update transaction. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Committed Transaction Count. This contains a count of the number of update transactions that have successfully committed. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. First Checkpoint Rollback Log Address. This contains the address in the rollback log of the first block that was logged after the last completed checkpoint. After a system crash, FLAIM will start from this point in the rollback log to roll the database back to the state of the last completed checkpoint. Last Roll-forward Log File Deleted. This contains the roll-forward log file number of the last roll-forward log file that FLAIM deleted from the system. When a database s roll-forward log file is in the  no-keep state, FLAIM will automatically delete any log files that may have been generated while it was in the  keep state - up to, but not including, the oldest log file needed for recovery. Minimum Roll-forward Log File Size. This contains the minimum size (in bytes) that a roll-forward log file may grow to. When in the  no-keep state, FLAIM will truncate the one log file that is being used to this size whenever it completes a checkpoint. It will then reset itself and begin logging at the beginning of the file. When in the  keep state, FLAIM will roll to a new roll-forward log file once the minimum size has been exceeded. Normally, once FLAIM knows it has exceeded the minimum, FLAIM will wait to roll to a new file until the current transaction completes, unless the current transaction would cause FLAIM to exceed the maximum roll-forward log file size. In that case, FLAIM will roll to the next roll-forward log file before the maximum log file size is exceeded. If exceeding the minimum would cause FLAIM to also exceed the maximum (for example, if both minimum and maximum are set to the same value or close to the same value), FLAIM will give preference to staying under the maximum. It is therefore possible that roll-forward log files may never exceed the minimum. Log Header Checksum. This contains a two byte checksum of the log header. Database Version Number. This contains the database version number. This is normally the same version number that is stored in the database header. However, when doing database conversions from an older version to a newer version, we needed to be able to update and commit the database version number in the same transaction as any other database changes that needed to be done. Hence, it is now stored in the log header, and the version number in the log header takes precedence over the version number stored in the database header. When doing a database conversion, FLAIM will always attempt to update the FLAIM version number in the database header as well, but it is not possible to perform this update in the same transaction that performs other database conversions. It is possible that a crash could occur between the time the conversion transaction committed and the time the database header could be updated. For this reason, FLAIM always assumes that the version number in the log header is the correct version number if there is a difference between the version number in the log header and the version number in the database header. Last Backup Transaction ID. This is only used in database versions 4.3 and later. It contains the transaction ID that was backed up to in the last full or incremental backup. It basically means that the last backup taken contains all committed transactions up to this transaction ID. This is used when doing an incremental backup to determine which blocks have been modified since the last backup was taken. An incremental backup will backup only blocks that are stamped with a transaction ID that is greater than this value. Blocks Changed Since Last Backup. This is a count of the number of blocks in the database that have been changed since the last full or incremental backup was taken. This number will give an application some idea of what percentage of blocks have been changed since the last backup, so it can determine whether it is worth it to do an incremental or full backup. Last Checkpoint Transaction ID. This is the transaction ID of the last transaction that was successfully checkpointed. When recovering after a crash, FLAIM uses this number to determine which blocks in the roll-back log should be reapplied to roll back to the last checkpoint. Blocks that have a transaction ID greater than this value will be skipped over. First Backchain Address. The avail list contains threaded backchains that are double-linked throughout the avail list. This contains the address of the first backchain block. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Avail List Block Address. This contains the address of the first block in the avail list. When new blocks are needed during a transaction, they are allocated from the avail list. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Logical End Of Database Address. This contains the address of the logical end of the database. When the avail list is empty and new blocks are needed during a transaction, they will be allocated from the end of the database, and the database will be extended. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Keep Aborted Transactions In Roll-forward Log Flag. This flag is used to indicate whether or not FLAIM should keep aborted transactions in the roll-forward log. A non-zero value indicates that we are keeping aborted transactions, while a zero value indicates that we are not. This flag is not used in database versions prior to 4.3. First Backchain Count. This contains the number of inclusive blocks that are between the avail list block address and the first backchain block. This number is used to determine when to assign a new backchain block to the avail list. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Keep Roll-forward Log Files Flag. This flag indicates whether or not we are doing roll-forward logging in a  keep mode or  no-keep mode. A non-zero value indicates a keep mode, while a zero value indicates a no-keep mode. The keep mode causes FLAIM to roll to a new roll-forward log file when the current file fills up. The no-keep mode causes FLAIM to reset and reuse the current roll-forward log file whenever a checkpoint is completed. NOTE: FLAIM has configuration options that allow a database to be switched between the keep and no-keep modes. Whenever FLAIM is switched from keep to no-keep or vice versa, it will roll to a new roll-forward log file and stamp that new file as keep or no-keep. ADDITIONAL NOTE: Database versions prior to 4.3 are not allowed to switch to a keep mode - they always operate in no-keep mode. Auto-Turn-Off Keep of Roll-Forward Log Files Flag. This flag indicates whether or not FLAIM should automatically switch back to  no-keep mode if the disk which contains the roll-forward log files somehow gets full. A non-zero value indicates it should automatically switch back, while a zero value indicates it should not. Although not recommended, this flag may allow database operations to continue up to a point if the roll-forward log disk gets full because it flips the database back into a mode where it will reuse the current roll-forward log file for transaction logging instead of trying to create new roll-forward log files. It will also cause FLAIM to delete older roll-forward log files that are no longer in use, thus freeing up disk space that is needed to continue logging transactions. Number of Avail Blocks. This contains the total number of avail blocks that are in the avail list. NOTE: This number is not written to disk until a checkpoint occurs. Until then, it is updated only in memory. Maximum Roll-forward Log File Size. This contains the maximum size (in bytes) that a roll-forward log file may grow to. When in the  no-keep state, this value is ignored. When in the  keep state, FLAIM will normally roll to a new roll-forward log file once the minimum size has been exceeded. Once FLAIM knows it has exceeded the minimum, FLAIM will wait to roll to a new file until the current transaction completes, unless the current transaction would cause FLAIM to exceed the maximum roll-forward log file size. In that case, FLAIM will roll to the next roll-forward log file before the maximum log file size is exceeded. If exceeding the minimum would cause FLAIM to also exceed the maximum (for example, if both minimum and maximum are set to the same value or close to the same value), FLAIM will give preference to staying under the maximum. It is therefore possible that roll-forward log files may never exceed the minimum. NOTE: This value is only used in database versions 4.3 and greater. Database Serial Number. This contains the database serial number. NOTE: This value is only used in database versions 4.3 and greater. Last Roll-forward Log Serial Number. This contains the serial number of the last roll-forward log file that has been created. NOTE: It is possible that the last roll forward log file is being pointed to by the log header, but it has not yet been created - it will be created when the next transaction begins. In that case, this serial number will be written to the roll-forward log file s header once it is created. NOTE: This value is only used in database versions 4.3 and greater. Next Roll-forward Log Serial Number. This contains the serial number that is to be used for the next roll-forward log file when we roll to the next file. Each roll-forward log file contains its own serial number and the serial number of the next log file in the sequence. NOTE: This value is only used in database versions 4.3 and greater. Incremental Backup Serial Number. This contains the serial number for the next incremental backup. NOTE: This value is only used in database versions 4.3 and greater. Maximum Data and Rollback File Size. This value indicates the maximum size for data files and rollback log files. It specifies the number of 64K units. Thus, a value of 0xFFFF really means 0xFFFF 64K units, which is a maximum size of 0xFFFF0000 bytes. NOTE: This value is only used in database versions 4.3 and greater. Once set, this value should NOT be changed except perhaps by a database conversion utility that knows what it is doing. Logical Files (Containers and Indexes) Internally, both indexes and containers are referred to as  logical files in a FLAIM database. Physically, they are B-Trees. Each B-Tree has a logical file header. Logical file headers are stored inside LFH blocks. All LFH blocks are linked together in a doubly linked chain (via the Next Block Address field in the Block Header structure - see section  REF _Ref512995061 \n \h 4). The first block in the chain is pointed to from the First LFH Block Address field in the Database Header structure (see section  REF _Ref512917491 \n \h 2.1). LFH Block Structure An LFH block contains logical file headers for indexes and containers. It is structured as follows: ITEMS IN LFH BLOCK SIZE IN BYTESOFFSET IN BLOCK Block Header 32 0 - 31 Logical File Header 1 32 32 - 63 Logical File Header 2 32 64 - 95 .   .   .   Logical File Header n 32 (n * 32) to (n + 1) * 32 - 1 Block Header. Each block in the database, except for blocks which belong to log segments, has a block header. For a detailed description of the structure of a block header see section  REF _Ref512995061 \n \h 4. Logical File Header. There is one logical file header for each container and index in the database. For a detailed description of the structure of a logical file header see the following section. Logical File Header Structure The structure for logical file headers is as follows: ITEMS IN LOGICAL FILE HEADER SIZE IN BYTES OFFSET IN LOGICAL FILE HEADERLogical File Number20 - 1Logical File Type12Set, but not used in pre-4.3. Not set or used in 4.313B-Tree Root Block Address44 - 7Set, but not used in pre-4.3. Not set or used in 4.348 - 11Next Container DRN412 - 15Right Split Fill Size (Not used in 4.3)116Delete Minimum Fill Size (Not used in 4.3)117UNUSED (filled with zeroes)1418 - 31 NOTE: Unless otherwise specified, 32 bit (4 byte) numeric values and 16 bit (2 byte) numeric values are stored in little-endian byte order. Logical File Number. This contains the ID number for the logical file. The ID number for user defined indexes and containers is the DRN of their corresponding dictionary definition record. Pre-defined indexes and containers have pre-defined numbers - the logical file number for the data dictionary container is always 32000, the logical file number for the default data container is always 32001, etc. Logical File Type. This byte indicates what type of logical file this is. A value of one (1) indicates that it is a container. A value of two (2) indicates that it is an index. A value of fifteen (15) indicates that this is an unused logical file header (it may be reused when new indexes or containers are created). B-Tree Root Block Address. This contains the address of the root block for the B-Tree. A value of 0xFFFFFFFF means that there is no root block - i.e., the B-Tree is empty. Next Container DRN. This field is used only on container logical files. Its value is the DRN which will be assigned to the next record that is added to the container if the container is currently empty (i.e., B-Tree Root Block Address is 0xFFFFFFFF). If the container is not currently empty, the next DRN is kept in the rightmost leaf block of the B-Tree in a special element known as the DRN_LAST_MARKER element. Right Split Fill Size. This field indicates how full to keep a block when adding records to the rightmost block in the B-Tree. When the rightmost block is split, the old block will retain the percent specified in this field. NOTE: The value in this field is not a straight percentage. The percentage is represented as a ratio of 128. Thus, if the number in this field is 96, the percentage is 96 divided by 128, which is 75 percent. This representation makes it easier for FLAIM to use. In database versions 4.3 and beyond, FLAIM uses a hardcoded value of 116 (which translates to approximately 91% full). Delete Minimum Fill Size. This field indicates the minimum percent a block should be filled. If a block drops below this percent, an attempt will be made to coalesce the block with an adjacent block. NOTE: The value in this field is not a straight percentage. The percentage is represented as a ratio of 128. Thus, if the number in this field is 96, the percentage is 96 divided by 128, which is 75 percent. This representation makes it easier for FLAIM to use. In database versions 4.3 and beyond, FLAIM uses a hardcoded value of 44 (which translates to approximately 34% full). Block Header Structure Every block in the database, regardless of its type, has a block header. The block header begins at offset zero within the block and is 32 bytes long. It is structured as follows: ITEM IN BLOCK HEADER SIZE IN BYTES OFFSET IN BLOCK HEADERXOR Checksum (This is also XORed with low byte of block address)10Block Address - high 3 bytes31 - 3Previous Block Address44 - 7Next Block Address48 - 11Block Type112Block Level (if in B-Tree) (See section  REF _Ref512928970 \n \h 4.1on avail list linkage)113End Of Block214 - 15Block Transaction ID416 - 19Prior Version Transaction ID420 - 23Prior Version Address (in rollback log)424 - 27Logical File Number (See section  REF _Ref512928970 \n \h 4.1on avail list linkage)228 - 29Not Used (See section  REF _Ref512928970 \n \h 4.1on avail list linkage)130AdditionChecksum (This is also summed with the low byte of block address)131 NOTE: Unless otherwise specified, 32 bit (4 byte) numeric values and 16 bit (2 byte) numeric values are stored in little-endian byte order. XOR Checksum. This byte contains the XOR (exclusive OR) checksum for the block. This is calculated by XORing all of the bytes in the block up to the end of block offset. The end of block offset is the value contained in the End Of Block field rounded up to the nearest multiple of 4. The reason we round up to the nearest multiple of 4 is because when blocks used to be encrypted we had to account for the fact that the encryption algorithm consumed up to the nearest multiple of 4. Note that bytes 0 and 31 are NOT included in the XOR because they are the two checksums on the block (the XOR and addition checksums) and are set only after the checksums have been calculated. Note also that the XOR Checksum also XORs in the low byte of the block address. Block Address. This contains the high three bytes of the block address of the block. The block address is stored so that FLAIM can verify that the correct block was fetched. The low byte of the block address is XORed with the XOR Checksum field and is also added to the Addition Checksum field. Thus, the low byte of the block address may be obtained by either: 1) XORing all of the bytes of the block (except bytes 0 and 31) and then XORing that value against the XOR Checksum field that is stored in byte zero, or 2) summing all of the bytes of the block (except bytes 0 and 31) and then subtracting that value from the Addition Checksum field that is stored in byte 31. Previous Block Address. This contains the address of the previous block in a linked list of blocks. It will contain 0xFFFFFFFF if there is no previous block. This field, in conjunction with the Next Block Address field is used to doubly link lists of blocks. All LFH blocks are linked together in a doubly linked list, as are all blocks that reside at the same level in a given B-Tree. There is also a list of available blocks, but it has a special type of linkage mechanism that is somewhat more exotic than the simple doubly linked list. Next Block Address. This contains the address of the next block in a linked list of blocks. It will contain 0xFFFFFFFF if there is no next block. Block Type. This byte contains information about the type of block. The bits in this byte are as follows: Bit 0 (0x80). This bit is set if the block is a root block in a B-Tree. Bit 1 (0x40). This bit is not currently used. Bits 2 through 3 (0x30). These bits are set on blocks in the rollback log if the block was logged so that the current transaction could be aborted. These bits are only set on blocks written to the rollback log. Bits 4 through 7 (0x0F). These low order bits contain one of the following values to indicate the block type: 0 Free block. This indicates that the block is in the avail list. 1 Leaf block. This indicates that the block is a leaf block in a B-Tree. 4 LFH block. This indicates that the block contains logical file headers for the database. The block will be in a doubly linked list of blocks of this type. 5 PCODE block. This indicates that the block contains dictionary pcode. The block will be in a doubly linked list of blocks of this type. NOTE: This type of block is no longer used in version 4.3 and beyond. 6 Non-leaf block, variable size keys. This is a non-leaf block in a B-tree. The block may be a container or an index block for versions of the database prior to 4.0. For 4.0 and beyond, this type of block is only used for non-leaf index blocks. 7 Non-leaf data block, fixed size keys. This is a non-leaf block in a container B-Tree. This type of block was introduced for versions 4.0 and beyond. The keys in this type of a block are all fixed size (eight bytes), making it possible to do binary searches in the block to look up a key. 8 Non-leaf block with counts. This is a non-leaf block in an index B-Tree. It is used for indexes whose index definition record have requested that positioning data be kept. This type of block is only supported in versions 4.3 and beyond. The additional data that is kept in the block allows FLAIM to do absolute positioning within the index. Block Level. This byte indicates the level of the block in the B-Tree. This byte has no meaning for blocks that are not part of a B-Tree (except for avail blocks - see section  REF _Ref512930807 \n \h 4.1). Leaf blocks are always at level zero, while the root block is at the highest level. The maximum levels in any btree can be eight levels. End Of Block. This tells the number of bytes in the block which are currently being used. Block Transaction ID. This contains the transaction ID of the last update transaction that modified the block. Prior Version Transaction ID. This contains the transaction ID of the prior version of the block. Prior Version Address. This contains the address in the rollback log where the prior version of the block is stored. This is used to chain together prior versions of the block so that a read transaction can obtain the version of the block that is required for its read-consistent view of the database. Note that the chain does not extend back through all prior versions. Prior versions are eliminated when they are no longer needed, but the prior version address is not necessarily updated at that point. Logical File Number. This contains the number of the logical file the block belongs to if the block belongs to an index or a container B-Tree. Addition Checksum. This byte contains the addition checksum for the block. This is calculated by summing all of the bytes in the block up to the end of block offset. The end of block offset is the value contained in the End Of Block field rounded up to the nearest multiple of 4. The reason we round up to the nearest multiple of 4 is because when blocks used to be encrypted we had to account for the fact that the encryption algorithm consumed up to the nearest multiple of 4. Note that bytes 0 and 31 are NOT included in the summation because they are the two checksums on the block (the XOR and addtion checksums) and are set only after the checksums have been calculated. Note also that the Addition Checksum also adds in the low byte of the block address. Avail List Block Linkages Blocks in the avail list are linked together in a special manner to improve update performance. In very early versions of FLAIM, the avail list was a fully doubly linked list. Whenever a block was put into the avail list, it would be linked at the front of the list. If there was already a block in the avail list, that block s  previous pointer had to be updated to point to the newly added block. This, of course, resulted in dirtying and writing an additional block, not to mention the before-image of the block to the rollback log. All of this resulted in additional costs for operations that deleted records in the database - particularly bulk deletes. After analyzing the situation, it was concluded that the  previous pointer in the avail list had no use, because blocks were never unlinked from the middle of the avail list, only from the front of the list. Hence, it was decided that blocks in the avail list only had to be singly linked, not doubly linked. This would result in a huge performance gain for bulk delete operations. After deciding to make the avail list a singly linked list, it was discovered that there was one special case where blocks might need to be removed from the middle of the avail list. That was during calls to FlmDbReduceSize. FlmDbReduceSize operates by unlinking blocks at the physical end of the last data file and replacing them with blocks from the avail list that are not at the physical end of the last data file. Blocks at the end of the last data file can then be truncated off and returned to the file system. If the block at the physical end of the last data file happened to be a block in the avail list, it is only necessary to unlink that block from the avail list. There is no need to replace it. Because the block might be in the middle of the avail list, a mechanism was needed for determining the block s previous block. At first, it was thought that we would have to keep the avail list as a doubly linked list after all. However, a compromise was struck. Instead of a previous pointer in every block, it was determined that every 36th block in the avail list would belong to a special chain of blocks. Every 36th blocks would be doubly linked, while still participating in the singly linked list of all blocks in the avail list. This meant that extra overhead would only be incurred every 36th block that was put into the avail list - thus retaining much of the performance advantage of a singly linked list for bulk delete operations. To find a block s previous block, FLAIM would follow the block s  next pointer forward until it found one of these special 36th blocks. It would then follow that block s  previous pointer to the previous 36th block and then go forward again until it found the block whose  next pointer was pointing to the block it started from. This is a somewhat costly way to find a block s previous block, but it was concluded that most of the time when FlmDbReduceSize operates it will not be finding avail blocks at the physical end of the last data file that it needs to unlink from the avail list. It is more probable that it will find other types of blocks at the end of the last data file, which it will switch with blocks in the avail list. In addition, FlmDbReduceSize is not an operation that we anticipate will be called very often, and hence it is a good compromise to have it take an occasional performance hit so that freeing a block into the avail list is not as costly. In conclusion, blocks in the avail list have three pointers: 1) a  next pointer (the Next Block Address field), 2) a  previous 36th block pointer (the Previous Block Address field), and 3) a  next 36th block pointer. Items 2 and 3 are only used every 36th block. In all other blocks in the avail list, they are set to zero. Item 3 was not originally planned for in the block header, so some fancy byte overloading was done. Its bits are found in non-contiguous bytes in the block header, as shown below: Bits 0 through 7 (low bits) - Byte 30 in the block header Bits 8 through 15 - Byte 13 in the block header Bits 16 through 23 - Byte 28 in the block header Bits 24 through 31 (high bits) - Byte 29 in the block header Observe that byte 30 is an unused byte in the block header. Byte 13 is the block level when the block is a B-Tree block - but block level means nothing for blocks in the avail list. Bytes 28 and 29 contain the logical file number when the block is in a B-Tree, but logical file number means nothing for blocks in the avail list. B-Tree Organization Each logical file in the database is stored as a B-Tree. B-Trees consist of database blocks which may be leaf blocks or non-leaf blocks. Leaf blocks are the blocks only at the very bottom of the B-Tree. Non-leaf blocks are all blocks at higher levels in the tree. Each B-Tree has a root block at the highest level. The structure of each block in a B-Tree is as follows: ITEMS IN B-Tree BLOCK SIZE IN BYTES OFFSET IN BLOCK Block Header 32 0 - 31 Element 1 Variable Variable (>= 32) . . . . . . . . . Element n Variable Variable (>= 32) All blocks at any given level of a B-Tree are forward and backward linked via the Next Block Address and Previous Block Address fields in the block header (see section  REF _Ref512995061 \n \h 4 for a detailed description of the block header). A value of 0xFFFFFFFF in the Previous Block Address field indicates that the block is the first block in the chain. A value of 0xFFFFFFFF in the Next Block Address field indicates that the block is the last block in the chain. NOTE: The Root Block of the B-Tree is the only block which will have 0xFFFFFFFF in both fields. There are four types of blocks that may comprise a B-Tree: 1) Non-Leaf Variable Key Size, 2) Non-Leaf Variable Key Size With Counts, 3) Non-Leaf Fixed Key Size, and 4) Leaf. The structure of elements within a block depend on whether the block is a leaf block or a non-leaf block. These are described in the following sections. Non-Leaf Variable Key Size Blocks Non-leaf variable key size blocks are used primarily in index B-Trees. Versions of FLAIM prior to 4.0 also used them in container B-Trees. The elements in this type of block have a key portion and an optional domain number. When this type of block is used in a container B-Tree, the key portion of its elements is a DRN. The domain number is only present in index B-Trees, but even there it is optional. If present, the domain number defines the lower bound DRN number for the key within the child block the element points to. The lower bound DRN number is calculated by multiplying the domain number by 256. This number essentially gives a more refined key so that FLAIM can quickly position to the proper element within a key's reference set when inserting or deleting DRNs. This is particularly useful for positioning within very large reference sets. If the domain number is NOT present, the lower bound DRN for the child block is assumed to be zero. The structure of a non-leaf variable key size element is as follows: Byte(s)Bit(s)Description00 (0x80)Domain Flag. If set, this bit indicates that a three byte domain number follows the key value.1 (0x40)Unused2-3 (0x30)High bits for key length. These should be concatenated to the key length (byte 1) to get the full key length - 10 bits worth.4-7 (0x0F)Previous key continuation count. This indicates the number of bytes from the previous element's key which are part of this element's key. This will ALWAYS be zero in the first element in a block.1Lower 8 bits of key length. Full key length is obtained by concatenating bits 2 and 3 from byte 0 as the high order bits. NOTE: For data records, since the key is always a DRN, the key length plus the previous key continuation count should ALWAYS add up to 4 bytes.2-5Child block address (little endian byte order). This contains the address of the child block this key points to in the B-Tree. All of the keys in the child block will be less than or equal to the key in this element. The key in the LAST element of the child block MUST MATCH the key in this element.6Key starts on sixth byte.6 + Key LengthThree bytes containing the domain number will be stored after the key if the domain flag is set (byte 0, bit 0). Remember, domain number is only used in index B-Trees (see above). Total length of a non-leaf variable key size element is 6 + key length if domain flag is NOT set. If the domain flag is set, total length is 6 + key length + 3. Non-Leaf Variable Key Size With Counts Blocks Non-leaf variable key size with counts blocks are used only in index B-Trees. They are only available in database versions 4.3 and greater. Elements in this type of block are very similar to the variable key size elements described in section  REF _Ref512998915 \n \h 5.1. The only difference is that just prior to the key value, starting at byte #6, there is a 32 bit (four bytes) count value, which is the total number of references found in the sub-tree pointed to by this element. This means that the key value starts at byte #10. The structure of a non-leaf variable key size with counts element is as follows: Byte(s) Description 0-5 See description in section  REF _Ref513001668 \n \h 5.1. 6-9 Reference count (little endian byte order). This is the count of references that are contained in the sub-tree pointed to by this element. 10 Key starts on tenth byte. 10 + Key Length Three bytes containing the domain value will be stored after the key if the domain flag is set (byte 0, bit 0). As with non-leaf variable key size blocks (see section  REF _Ref512998915 \n \h 5.1), domain number is only used in index B-Trees (see above). Total length of a non-leaf variable key size with counts element is 10 + key length if domain flag is NOT set. If the domain flag is set, total length is 10 + key length + 3. Non-Leaf Fixed Key Size Blocks Non-leaf fixed key size blocks are used only for containers. Each element in the block is exactly eight bytes long. The fixed element size allows FLAIM to search for keys in the block using a binary search. This results in a significant performance improvement when searching down a B-Tree. When searching for a key in variable size blocks FLAIM is forced to scan the block sequentially. Each eight byte element is structured as follows: Byte(s) Description 0-3 Key value. This is always a DRN in big endian byte order. 4-7 Child block address (little endian byte order). This contains the address of the child block this element points to in the B-Tree. All of the keys in the child block will be less than or equal to the key in this element. The key in the LAST element of the child block MUST MATCH the key in this element. Leaf Blocks Leaf block elements are formatted the same for both index and container B-Trees. The elements in leaf blocks of a B-Tree have a key portion and a data portion. The maximum data portion space in any given element is 250 bytes. For container B-Trees, the key portion holds the DRN for the record, and the data portion holds the data record identified by that DRN. If the data record is longer than 250 bytes, it will be stored in one or more continuation elements. For index B-Trees, the key portion of an element contains the key value, and the data portion contains the reference set for the key. The structure of leaf elements is as follows: Byte(s)Bit(s)Description00 (0x80)First element bit. If set, this bit indicates that the element is the first element for a data record (only used in container B-Trees). If this bit is NOT set, the element is a continuation element.1 (0x40)Last element bit. If set, this bit indicates that the element is the last element for a data record (only used in container B-Trees).2-3 (0x30)High bits for key length. These should be concatenated to the key length (byte 1) to get the full key length - 10 bits worth.4-7 (0x0F)Previous key continuation count. This indicates the number of bytes from the previous element's key which are part of this element's key. This will ALWAYS be zero in the first element in a block.1Lower 8 bits of key length. Full key length is obtained by concatenating bits 1 and 2 from byte 0 as the high order bits. NOTE: For data records, since the key is always a DRN, the key length plus the previous key continuation count should ALWAYS add up to 4 bytes.2Length of data portion.3Key starts on third byte.3 + Key LengthData portion starts after the key. Total length of a leaf element is 3 + key length + data portion length. Next DRN Marker Element The rightmost leaf block in a container B-Tree has a special element just before the last element. It is called the Next DRN Marker Element. This element is used to store the next DRN value that is to be used for the next record that is added to the container. The key for this element is always 0xFFFFFFFF, so it automatically stays in the rightmost block (which is why 0xFFFFFFFF is not a valid DRN). The data portion of the element contains the next DRN value. If all records are deleted from the container, the next DRN value is then stored in the logical file header for the container, and the last B-Tree block is deleted. Rightmost Elements The rightmost element is the last element of the last block at a given level in a B-Tree. Except for non-leaf fixed key size elements, the key length for these elements will always be zero. In addition, the rightmost element in the leaf level of the B- Tree will have a data portion length of zero. Thus, the length of the rightmost element for non-leaf variable key size blocks will always be 6 bytes, the length of the rightmost element for non-leaf variable key size with counts blocks will always be 10 bytes, and the length of the rightmost element for leaf blocks will always be 3 bytes. Data Portion of Leaf Elements The data portion of leaf elements is used to store one of two kinds of data, depending on whether the leaf block is in a container B-Tree or an index B-Tree. If it is a container B-Tree, the data portion of the element stores the data record that is identified by the DRN key. If it is an index B-Tree, the data portion of the element stores a reference set which is the list of DRNs that identify the records which contain the key. These are described in the following sections. Data Record Structure The data portion of a leaf element in the database records or data dictionary records logical file contains a series of variable length field operations. The first byte of each field operation may be used to determine the type of field operation and what follows. The field operations are described in the following sections. Standard Field This type is generally used to store fields whose value length is less than or equal to 63 bytes and whose field number is less than or equal to 255. The field must be a registered field (one that is defined in the data dictionary). Byte 0 = 0yllllll y Field's relationship to previous field. If one, it is the previous field's child. If zero, it is the previous field's sibling. llllll Length of value portion of field. Byte 1 = Field Number. Byte 2 = Value starts here. Open Field This type is generally used to store fields whose value length is greater than 63 bytes or whose field number is greater than 255. The field must be a registered field (one that is defined in the data dictionary) or one of FLAIM's reserved field numbers (for records in the data dictionary itself). Byte 0 = 1001yxfv y Field's relationship to previous field. If one, it is the previous field's child. If zero, it is the previous field's sibling. x Not used. f Flag indicating whether field number is one byte or two bytes. If set, two bytes are used, otherwise one is used. v Flag indicating whether one or two bytes is used to specify the value length. If set, two bytes are used, otherwise one is used. Field Number. The field number comes immediately after byte zero. Field number will be two bytes if the 'f' bit is set in byte 0 (see above), one byte if not set. Value Length. The value length comes immediately after the field number. Value length will be two bytes if the 'v' bit is set in byte 0 (see above), one byte if not set. Value. The value comes immediately after the value length. Free Field This type is used to store unregistered fields or reserved fields whose type is not TEXT. Note that reserved fields whose type is TEXT are stored as open fields (see section  REF _Ref513868105 \n \h 6.1.2). This type could also be used to store registered fields, but it is never used for that purpose. Byte 0 = 1000yxfv y Field's relationship to previous field. If one, it is the previous field's child. If zero, it is the previous field's sibling. x Not used. f Flag indicating whether field number is one byte or two bytes. If set, two bytes are used, otherwise one is used. v Flag indicating whether one or two bytes is used to specify the value length. If set, two bytes are used, otherwise one is used. Byte 1 = ggggtttt gggg Unused. tttt Field type. It may be one of the following: 0 = TEXT 1 = NUMBER 2 = BINARY 3 = CONTEXT 8 = BLOB Field Number. The field number comes immediately after the field type. Field number will be two bytes if the 'f' bit is set in byte 0 (see above), one byte if not set. Before storing the field number, the high bit (0x8000) is toggled. If it was set, it will be unset. If it was not set, it will be set. Thus, to get the true field number, the high bit must be toggled again upon retrieval. By toggling the high bit, we get the effect of reducing the space needed to store the first 255 unregistered field numbers (0x8001 through 0x80FF). Because the high bit is toggled off, these 255 field numbers can be stored as a single byte. Value Length. The value length comes immediately after the field number. Value length will be two bytes if the 'v' bit is set in byte 0 (see above), one byte if not set. Value. The value comes immediately after the value length. No Value Field This type is used to store fields which have no value. Byte 0 = 10101yf0 y Field's relationship to previous field. If one, it is the previous field's child. If zero, it is the previous field's sibling. f Flag indicating whether field number is one byte or two bytes. If set, two bytes are used, otherwise one is used. Field Number. The field number comes immediately after byte zero. Field number will be two bytes if the 'f' bit is set in byte 0 (see above), one byte if not set. Set Field Level This type does NOT contain field data. It is a special type that is used to store the relationship between the prior field and the next field. When two contiguous fields are siblings or the first field is the parent of the second field, a single bit in byte 0 (the  y bit) of the second field specifies that relationship (y=1 means the 2nd field is a child to the first, y=0 means the 2nd field is a sibling to the first). When the relationship is neither child nor sibling, the Set Field Level type is used. It is only used to jump back up a certain number of levels in the record. The Set Field Level type specifies the number of levels between the previous field and the next field. This type can only specify a maximum of 7 levels. If there is a larger jump, the Set Field Level type will appear multiple times in a row. Byte 0 = 10100nnn nnn Number of levels difference between prior and next field. Field Value Formats Each of the data types (text, binary, number, context, BLOB) has a storage format. These are described in the following sections. Text Storage Format The text format is a sequence of character objects. Each character object is one or more bytes of data that represents a single character. The type of character object is determined by the bit pattern in the first byte of the object. The types of character objects are as follows: ASCII Character. This is a single byte object, where the bits in the byte are as follows: 0ccccccc High bit is zero, remaining seven bits (ccccccc) is the ASCII character. This type is used for all Unicode characters whose character set is zero and whose character is 0x20 (space) through 127. It is also used when putting  native text into the database if the character is between 0x20 and 127. White Space. This is a single byte object used to represent certain  white space characters. It is a relic from prior versions of FLAIM, and technically is not really needed. In future versions of FLAIM, its use will be phased out. It is currently only used when putting  native text into the database, and is only used for tab (0x9), linefeed (0xA), and carriage return( 0xD). The bits in the byte are as follows: 110ccccc The lower five bits contain the white space character, which is always one of the following: 0x7 Carriage Return (Will be converted to 0xD coming out) 0xC Tab (Will be converted to 0x9 coming out) 0xD Linefeed (Will be converted to 0xA coming out) Unknown Type. This is a two byte object used to represent  native characters below 0x20. Like the white space object, this object is also a relic from prior versions of FLAIM, and technically is not really needed. In future versions of FLAIM, its use will be phased out. It is only used when putting  native text into the database. The bytes are as follows: 11110nnn The lower three bits contain type of the second byte. Currently, only a value of 0x2 is used, which means that the second byte is a  native character below 0x20, except for 0x9, 0xA, and 0xD, which are represented as a White Space type (see above). Byte 2. The  native character below 0x20. OEM (Native) Character. This is a two byte object that represents a native (OEM) character above 127. This is only used when putting  native text into the database. Presumably, the character is associated with some machine s code page, which would be needed to properly convert it out to Unicode. However, the code page is not preserved in FLAIM. Furthermore, in future versions of FLAIM, it is likely that storing of  native text will no longer be supported. Hence, this type will be phased out. The two bytes for this type are as follows: First Byte = 11101001 (0xE9) This byte is a special code to indicate that the following byte is an  native (OEM) character. Byte 2. The  native character above 127. Two-Byte WordPerfect Character. This is a two byte object that represents a WordPerfect character that must be converted to the corresponding Unicode character. The two bytes are as follows: First Byte = 10cccccc The lower six bits is the WordPerfect character set. Second Byte = WordPerfect Character. Three-Byte WordPerfect Character. This is a three byte object that represents a WordPerfect character that must be converted to the corresponding Unicode character. The three bytes are as follows: First Byte = 11101000 (0xE8) This special code indicates that the next two bytes are a WordPerfect Character. Second Byte = WordPerfect Character Set. Third Byte = WordPerfect Character. Three-Byte Unicode Character. This is a three byte object that represents a Unicode character. This is used for Unicode characters that cannot be converted to a WordPerfect character. The three bytes are as follows: First Byte = 11101010 (0xEA) This special code indicates that the next two bytes are a Unicode Character. Second Byte = High byte of Unicode character. Third Byte = Low byte of Unicode character. Number Storage Format Numbers are stored in a BCD (binary coded decimal) format. If the number is a negative number, the first nibble will be a 0xB. Each nibble that follows represents a base-10 digit of the number. If there is an odd number of nibbles, the last nibble will be a 0xF, to indicate that the number is terminated. The following examples illustrate: -1 stored as: 0xB1 -10 stored as: 0xB1,0x0F 175 stored as: 0x17,0x5F 1573 stored as: 0x15,0x73 Binary Storage Format Binary fields are stored exactly as they are handed in to FLAIM by the application. Context Storage Format This section refers to context fields that have a DRN value. The DRN value is always stored as a four byte value in little-endian byte order. BLOB Storage Format A BLOB field is formatted as follows: Byte 0 This is the version of the BLOB format. Currently set to 28. Byte 1 This is the BLOB storage type. A value of 0x04 means it is an unowned external BLOB - i.e., the data is stored in a file that FLAIM does not manage. A value of 0x14 means it is an owned external BLOB - i.e., the data is stored in a file that FLAIM manages. The file will be deleted when the field is deleted. Bytes 2,3 Flags. 0x10 = Owned referenced BLOB. 0x1000 = Unowned referenced BLOB. Bytes 4-27 Currently all zero. Byte 28 Character set used for file name. 1=ANSI, 2=UNICODE. Byte 29 Length of file name in bytes. Byte 30 File name begins here. Reference Set Structure The data portion of a leaf element in an index B-Tree is a portion of the reference set for the key. Much of the structure of the reference relies on the usage of SENs (simple encoded numbers). This is described in the following section. SEN (Simple Encoded Number) Format The SEN Format is designed to represent up to 36 bit integer numbers in as few bytes as possible. Numbers may take anywhere from one to 5 bytes. The SEN format has been deliberately designed so that it can ALWAYS be distinguished from a set of special escape codes mentioned that are used in the reference set format. These special escape codes always have a hex F in the first nibble. The first nibble of a SEN will never be a hex F. The various number ranges which can be represented in SEN format are as follows: 1 Byte - 0nnnnnnn (up to 7 bit numbers) This represents a number between 0 and 127 (0x7F). 2 Bytes - 10nnnnnn nnnnnnnn (up to 14 bit numbers) This represents a number between 0 and 16383 (0x3FFF). 3 Bytes - 1100nnnn nnnnnnnn nnnnnnnn (up to 20 bit numbers) This represents a number between 0 and 1048575 (0xFFFFF). 4 Bytes - 1101nnnn nnnnnnnn nnnnnnnn nnnnnnnn (up to 28 bit numbers) This represents a number between 0 and 268435455 (0xFFFFFFF). 5 Bytes - 1110nnnn nnnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn (up to 36 bit numbers) This represents a number between 0 and 0xFFFFFFFFF. Reference Set Format The data portion of a leaf element in an index B-Tree is structured as follows: Element Domain Number (optional) The Element Domain Number is optional, but if present will ALWAYS be at the beginning of the element's data portion. The presence of the Element Domain Number is signaled by a 0xFC in byte 0 of the element's data portion. The 0xFC byte is followed by the domain number in SEN format (see section  REF _Ref513005921 \n \h 6.2.1). This number defines the lower bounds domain for the element. This number, when multiplied by 256, gives the lowest DRN number which can be added to the element's record portion. If the Element Domain Number is not present, the lower bounds domain is assumed to be zero. Base DRN Number The Base DRN Number is the highest DRN number in the element's record portion. Unless there is an Element Domain Number (as signaled by a 0xFC in byte 0), the Base DRN Number will be at byte 0 of the element's data portion. Otherwise, it immediately follows the Element Domain Number. The base DRN number is in SEN format (see section  REF _Ref513005921 \n \h 6.2.1). Delta Numbers The Base DRN Number is followed by a series of values that are used to generate a sequence of DRN numbers based on the Base DRN Number. The algorithm for expanding delta numbers into a sequence of DRN numbers is to traverse the delta numbers and successively subtract one or more values from the previous DRN number to get the next DRN number. The Base DRN Number is the starting DRN to subtract from. The delta numbers are as follows: 0xF0 through 0xF7 These numbers specify a one run. Upon encountering one of these numbers, the DRN generation algorithm will iteratively subtract one from the previous DRN number up to 'n' times to produce up to 'n' new DRN numbers. The number 0xF0 specifies a one run where 'n' is 2, 0xF1 specifies a one run where 'n' is 3, 0xF2 specifies a one run where 'n' is 4, etc. -- 0xF7 specifies a one run where 'n' is 9. 0xF8 This number specifies a one run where 'n' is greater than 9. The 'n' value is specified in a SEN Number (see section  REF _Ref513005921 \n \h 6.2.1) which follows the 0xF8. Single Delta Number A Single Delta Number is a number represented in SEN format (see section  REF _Ref513005921 \n \h 6.2.1). Upon encountering a Single Delta Number, the DRN generation algorithm will subtract the value from the previous DRN to produce a single new DRN number. Index Key Structure Index keys are composed of data from one or more fields as specified in the index definition. Keys are formatted so that they can be compared and sorted using a simple left-to-right byte-for-byte comparison (a memcmp). Depending on the indexing options, keys may or may not be able to generate the original field data from which they were created. Compound Markers If the index is defined as a compound index then each index piece is separated by a compound marker, which is a value of 0x02. An index with only one field has a single index piece and no compound markers. Each index piece is formatted according to one of the following formats: binary, context, field ID, numeric, and text. Truncated Pieces A binary or text piece may be truncated if a limit has been specified for that piece. To indicate that a binary or text piece has been truncated, FLAIM appends a value of 0x0C. NOTE: In Asian indexes, if a text piece is truncated, a two byte value is appended: 0x00, 0x0C. Valueless Pieces In a single-field index (non-compound), if a field is present in a record, but it has no value, FLAIM will create a key with a single byte value of 0x03. If the index is an Asian index on a text field, the key will be two bytes: 0x00,0x03. In a compound index, if a field is present in a record, but it has no value, that piece of the index will be empty - zero bytes of data. This will also be the case for optional fields which are not even present in the record. Thus, in a compound index, it is not possible to tell the difference between a field that is not present and a field that is present but has no value. First Substring Pieces In a sub-string text piece, the first sub-string created from the text field is specially marked. A value of 0x03 is appended to the text piece to indicate it is the first sub-string. In Asian indexes, a two-byte value will be appended: 0x00,0x03. As an example, consider a text field whose value is SMILE. It will generate the following sub-string keys in a sub-string index: SMILE, MILE, ILE, LE The sub-string SMILE is the  first sub-string, and will be marked with the 0x03 value to so indicate. Note that if the sub-string is also truncated, the truncation value (0x0C) described above in section  REF _Ref513947492 \n \h 7.2 will be appended after the 0x03 value. Binary Index Piece The output for a binary index piece is two bytes for every one byte of original binary data. Each nibble of a binary character is added to COLLS (0x20) and output as a byte. For example, the binary value 0x5a would collate to the two bytes 0x25 and 0x2a. The reason for the expansion is that we need to be able to parse for a compound marker within a compound key. If the binary index piece just contained the original binary values it would be impossible to know if you were looking at a compound marker or a binary value of 0x02. Context Index Piece A context index piece contains a DRN pointer to another record. The format for this piece is a 0x1F prefix byte followed by four bytes containing the DRN pointer value in highlow byte order (big endian). For example, the value 786 (decimal) would be the following five bytes (shown in hex): 1F 00 00 03 12 Field ID Index Piece The field ID piece contains the field number of a specified field. The format is a 0x1E prefix byte followed by two bytes containing the tag number in highlow byte order (big endian). For example, the field number 831 would be the following three bytes (shown in hex): 1E 03 3F Numeric Index Piece The first byte of a numeric index piece contains the sign and digit count. Bit 0 (0x80) Sign bit - zero is negative and one is positive. This causes all positive numbers to sort after the negative numbers. Bits 1-7 (0x7F) Bias 63 value of the digit count. 1 digit=0x40, 2 digit=0x41. For negative numbers, this value is XORed with zero. For positive numbers, this causes numbers with lower number of digits to sort before numbers with higher number of digits. For negative numbers, because the value is XORed with zero, it will cause numbers with higher number of digits to sort before numbers with lower number of digits. The bytes that follow contain each numeric BCD value (a nibble) plus 5 so that it will not conflict with the compound marker (0x02). If the entire value is negative each BCD value (a nibble) is subtracted from 9 so that negative values will sort in the correct order. A value of 5 is added to each nibble so it will not conflict with the compound marker. If there is an extra nibble at the end then a high nibble (0x0F) will be stored. For example: 1 = [0xC0 0x6F], 20 = [0xC1 0x75], 300 = [0xC2 0x85 0x5F], -1 = [0x3F 0xDF]. Text Index Piece A text index piece is formatted so that when two text pieces are compared, there is, in effect, a three stage comparison going on. In other words, the formatting allows FLAIM in a single pass to accomplish three logical passes. The first pass only compares collation values corresponding to each character. The second pass compares subcollated values for each character. Subcollating values consist of diacritics, OEM character values and characters that do not have collation values. The third pass compares lower and upper case information. Each text index piece contains collation, subcollation and lower/upper case data. Sub-collation and case information may be compressed out. Tables for Mapping Characters to Collation Values The following tables show how characters are mapped to a collation value. MACRO DEFINITION START OF COLLATION RANGE DESCRIPTIONCOLLS32SpaceCOLS141QuotesCOLS246ParenthesesCOLS352Money SymbolsCOLS458Math OperatorsCOLS566More Math OperatorsCOLS680Other Symbols: %#&@\_|~COLS793GreekCOLS8118NumbersCOLS9128Alphabet (A-Z,a-z)COLS10188Cyrillic, Hebrew and ArabicCOLS11235End of ListCOLSOEM254OEM Character >= 127COLS0255Characters that do not have a collating value For reference reasons, the table below contains a mapping of the alphabet to each letters' collating value. This could be useful during debugging. This table does not include all of the diacritic combinations for different languages. For accurate information on each diacritic collation value see the collation state table in the collation code. COLLATED VALUECHARACTER0x80A0x82B0x83C0x86D0x87E0x88F0x89G0x8AH0x8CI0x8EJ0x8FK0x90L0x92M0x93N0x95O0x97P0x98Q0x99R0x9BS0x9DT0x9EU0x9FV0xA0W0xA1X0xA2Y0xA3Z Sub-Collation Format The subcollation area is prefixed by a 0x07 byte value. If there is not any sub-collation information, the case information may come next (see section  REF _Ref513282791 \n \h 7.9.3). In order to parse through the subcollation area the count of collation bytes, stored before the 0x07 code, must be known. For each and every collation character one of the following bit codes will appear: 0 No subcollation information for the corresponding collation byte. 10 Take the next 5 bits and the corresponding collation byte to compute the diacritic character or digraph. For Japanese the value of the 5 bits defines a table position of an original symbol. 110 Align to the next byte and the next two bytes (little endian) contain the character value or the OEM character value. The OEM character set is zero. This is used for characters that have no collation value from the collation table. 1110 Align to the next byte and the next two bytes (low-high) contain the Hebrew or Arabic  insert character. NOTE: This sub-collation value will NOT correspond to a collation value in the text because it is an  insert character. 11110 Align to the next byte and the next two bytes (little endian) contain a Unicode character. This is used to preserve a Unicode character that cannot be mapped to a WordPerfect character. The collation value for such a Unicode character will be 0xFF. Case Information The lower/upper case area is prefixed by one of the following byte codes: 0x04 All characters in the collation area are lowercase. For Greek, Icelandic and Danish, the 0x04 indicates that all characters in the collation area are uppercase. 0x05 This indicates that there is mixed case for the characters in the collation area. Following the 0x05 is a sequence of bytes whose bits indicate the case for each character in the collation area - one bit per character. Bits in a byte correspond to characters starting from the high order bit (0x80) and going to the low order bit (0x01). Note that the last byte in the sequence may not have all bits used. A bit is set if the corresponding character in the collation area is upper case. This causes upper case to sort after lower case. For the Greek, Icelandic, and Danish languages the bit is set if the corresponding character is lower case, because in those languages they want lower case to sort after upper case. 0x06 All characters in the collation area are uppercase. For Greek, Icelandic and Danish, the 0x06 indicates that all characters in the collation area are lowercase. Post Compound Indexes The post compound indexes save all case bits from every text field and append to the end of the key - causing case to be considered last when doing comparisons of keys containing text pieces. Each field in a post compound index key is separated by a 0x02 byte value. The post case information is prefixed by a 0x01 byte value. The lower/upper text index pieces that were made are then appended end to end. Each piece keeps its original byte alignment. The last byte of the post compound index key contains the number of bytes that are in the post case section. This value does not include the last byte itself. Language Every index in a FLAIM database has an associated language. The index definition can specify a language, or the default language for the database can be used. The language of an index does not dictate what characters can be stored in the index. All Unicode characters can always be stored, regardless of index language. The primary purpose of the language specification is to determine a sort order for characters in the index. However, FLAIM also assumes that the language indicates what set of characters will be most commonly stored and searched. Hence, when Japanese is specified, FLAIM assumes that most characters will probably be Japanese characters, which probably means a significant number of kanjis will be stored. Based on the assumption that language gives a clue as to what characters will be stored and searched most often, FLAIM uses language to make determinations about what key format might be most optimal for text pieces. This is primarily manifested in the fact that a two-byte collation format is used for the Asian languages (see section  REF _Ref513356417 \n \h 7.9.6 for more information). Two-Byte Collation Values (Asian Languages) The collation part of a text index piece normally stores one collation byte per character. This allows for 255 unique collation values. Obviously, the number of Unicode characters exceeds 255. Hence, it is not possible to have a unique collation value for every Unicode character. For non-Asian indexes this problem is handled in one of two ways: 1) In some cases, the same collation value is used for multiple characters. This is generally done for characters with diacritics. Diacritics information is stored in the sub-collation area. Thus, to determine the actual character, both the collation value and the sub-collation value must be retrieved. 2) The collation value of 0xFF is a catch-all collation value for characters which need to be collated after all other characters. When this collation value is used, the actual character is stored in the sub-collation area. Characters stored in this manner include kanjis from the Asian languages (Japanese, Korean, Chinese) and an assortment of other Unicode characters whose usage may be somewhat rare. This latter technique causes kanjis and the other special characters to sort after all other characters, which is entirely adequate as far as sort order goes. However, it creates a problem for doing efficient sub-string searching on such characters if much of the database consists of those characters - such as a database that has mostly Japanese kanjis stored. The search key generated by FLAIM for sub-string searches cannot include a sub-collation part, only a collation part. Since all kanji characters will have the same collation value (0xFF), a search key for a sub-string containing kanjis will be a sequence of 0xFF bytes. This is not a very good search key, because it is likely to span a significant chunk of the index - if not the entire index. Search performance will be poor in such cases. To help with this problem, FLAIM utilizes a two-byte collation format when the language for the index is one of the Asian languages (Japanese, Korean, Chinese). Recall that the specification of a language for an index does not dictate what characters can be stored in the index. All Unicode characters can always be stored, regardless of index language. The primary purpose of the language specification is to determine a sort order for characters in the index. However, FLAIM also uses language as a clue for what characters will be mostly stored and searched (see section  REF _Ref513356611 \n \h 7.9.5). In the case of the Asian languages, FLAIM elects to use two-byte collation values - thus allowing kanjis and other special characters to have their own collation value. The following sections describe the character sets and how they are handled in Asian languages. ASCII (below 127), Latin, Greek, Cyrillic. The first byte of the two-byte collation value will be a zero, and the second byte will be the same collation value that is used for non-Asian languages. Latin, Greek, and Cyrillic are sometimes called hankaku characters. Katakana. This character set may contain two character value voicing marks: dakuten and handakuten. These voicing marks may or may not be combined with other katakana characters to form kankaku characters (see below). The first byte of the two-byte collation value will be a one, and the second byte will be a value assigned to each katakana character. Hiragana. Each hiragana character maps one for one to a matching katakana character. Both hiragana and katakana represent the same character meanings but the display is different. As with katakana, the first byte of the two-byte collation value will be a one, and the second byte will be a value assigned to each hiragana character. Kanji. The two-byte collation value is the Kanji character itself. Collation Markers. Collation markers are values that mark the end and start of a new section in an index text piece. Markers include subcollation (7), case information (4,5,6) and end of text piece (2). In Asian languages, these markers will be two bytes. The high byte will always be zero. Note that the 2 marker is used to separate compound pieces. It is only two bytes when it terminates a text piece. When terminating a non-text piece, it will still be one byte. Subcollation. Subcollation values may be voicings for katakana and hiragana characters, or the position in a table for some characters. If a character does not have a collation value then the original character will be stored as the sub-collation value. The following are the subcollation bits for katakana and hiragana characters: 100b handakuten - half voiced 010b dakuten - voiced 001b set if a large character 000b value of a small character without a voice Case Bits. The case information is extended to two bits for each collation value. If the collation represents a character in the Latin/Greek/Cyrillic range the first bit is set if the character should converted to be a double wide (kankaku) character. The second bit is set if the character is not a lower case value. Hebrew and Arabic Sorting Hebrew and Arabic will sort over the collation values of Cyrillic if the language is Hebrew, Arabic, Farsi or Urdu. The subcollation format is extended to handle these languages. These languages have accents defined that can be combined with most of the base characters. These accents should be ignored during the first pass of the sort and not even represented in the collation portion of the string. This can cause problems with the subcollation area. There is a bit code of 1110 to mean that the next aligned word value should be inserted into the Unicode string at the current position. This means that the subcollation information could have more values than the collation section. The code that reconstructs the original Unicode string will move the remaining characters down and insert the accent character at the current location in the string. As was noted above, there is not a one-for-one correspondence between collation values and the number of bit-code sequences in the subcollation area. This is because accent characters could follow the last character that has a collation value. Therefore, these languages must always have an extra 1 bit zero to terminate the subcollation area so that the code which constructs the original Unicode string will know when it is at the end of the subcollation area. FLAIM 4 Database Format FLAIM DocumentationCopyright © 1991, 2006, Novell, Inc.Page  PAGE 44 FLAIM 4 Database Format FLAIM DocumentationCopyright © 1991, 2006, Novell, Inc.Page  PAGE 44 Packet Packet Packet Packet Packet Header 8 Bytes Body Variable Length Header (512 Bytes) Position Level 1 0 Employee 2 1 Address 3 2 City "Provo" 4 2 State "Utah" 4 1 Name 6 2 First "John" 7 2 Last "Doe" 024ž ¢¤¦¨ª¬®°²´¶âä „†Ö Ø Ü  ˆ Š ¶ ð  6 ˜ > v¤àB¶N®ìÜÜÈÈÈÈÈÈÈÈÈÈÈÈÈÈÜÈÜÈÜÈÈÈÈܶ´´¢¢¢¢¢¢¢¢}%;B*CJmH sH 5PJnH ^JaJ"B*CJmH sH 6PJnH ^JaJ":B*CJmH sH PJnH ^JaJU"B*CJmH sH 5PJnH ^JaJ'B*CJmH sH PJnH ^JaJOJQJB*CJmH sH PJnH ^JaJ%B*CJ$mH sH 5PJnH ^JaJ$\1®ò,ˆÀ HŠÀ€Îö:pºö(R|®â^¸ö,^Â6n¨àŠÈþ>dÐ< 6ÎÜêð – X#Z#n#š#¨#þ/00Œ3îîÛîîÛîÛîîîîÉîÛîÉÉÉÉÉÉîÉÉÛîîîîîîîîîÉÉÉÉÉÉÇɵµµµ³³³µµ³µµ5"B*CJmH sH 5PJnH ^JaJU"B*CJmH sH 6PJnH ^JaJ%;B*CJmH sH 5PJnH ^JaJ":B*CJmH sH PJnH ^JaJAŒ3Ž3Þ346N6P6h6l7Œ77®7ö7ø7Z8Ž<°<´<â<¶=Ö=Ú=ø=Ð?Ò?ø?øGúGJHNL~L‚L°L‚M„MæM(QŠQLR~R€R°R´RâR,SvSÀS TTTžTèT2U4UˆUº]J^øbúbcplrl lnnjno2oìpîpPq‚qª{îîììììîîììììÚÚîîììîîîÊÊîÊîÊÊÊÊÊÊÊÊÚÚÆÚÚÚÚîîìîîÁ B*ph65B*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ5"B*CJmH sH 5PJnH ^JaJFª{¬{l|z|€}Œ}}œ}^~l~Jdìú€€D€H€J€†€”€Ò€ê€ú€ ÀØ‚*‚J‚L‚†‚΃Ѓ „&„D„l„®„²„¶„À„ô„ø„ü„…"…&…,…6…f…¢…¨…®…¸…è…$†*†0†:†t†°†¶†¼†Æ†æ†"‡ìçàçàçàçàçàçàçìËììçàçàçàçàçàç¹¹©¹¹¹¹©©©©©©©©©©©©©©©©©©©©©©©©©©©©©B*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ(B*phCJmH sH 5PJnH ^JaJ B*ph5 B*ph%B*phCJmH sH PJnH ^JaJB"‡$‡*‡,‡2‡4‡>‡B‡L‡\ˆœˆô‰&ŠŒŒLNTV^Œ\’^’’’’˜’š’Ö“”È•–À—ޗƙșꙘœšœÔœHŸJŸLŸNŸPŸRŸTŸVŸXŸZŸ\Ÿ^Ÿ`ŸbŸhŸjŸlŸnŸvŸxŸ”Ÿ¼ŸøŸüŸ   ( B ïïïïïïïíéééçççéçççéééïïÕÕÐÐÐÐÐÐÐÀÐÐаÐаïïïïïïïïïïB*CJmHsHPJnH ^JaJB*CJmHsHPJnH^JaJ jU"B*CJmH sH 5PJnH ^JaJU655B*CJmH sH PJnH ^JaJEB F P x | † Š º¡ ¢2£À£Â£Ú£î£ô£¤¤D¤J¤n¤t¤Š¤¤¬¤²¤Î¤Ô¤ì¤ò¤¥¥,¥4¥H¥P¥l¥t¥Ž¥–¥¶¥¾¥ô¥ü¥¦"¦$¦V¦˜¨š¨¶¨ê¨ ©©©0©4©>©B©D©¦©TªVª^«`«b«~«²«Ò«Ö«à«¬¬$¬(¬Ð­Ò­T® ° °(°ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïÝÝïïïïïïïïïÝÝïÉïïïïïïïïïïïïÝÝïï'B*CJmH sH PJnH ^JaJOJQJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJO(°\°|°€°Š°¤°¨°²°¼°À°Ê°ÂµÄµêµ(¸*¸,¸F¸j¸|¸€¸–¸š¸¨¸¬¸¼¸À¸Ì¸Þ¸â¸~¼€¼¨¼¢ÀÃÃÃ,Ã.Ã0Ã2Ã4ÃÇÇÈ,ÈPÈ\È`ÈtÈxȊȎȤȨȶȺÈÔÈØÈäÈöÈïïïïïïïïïïÝÝïïïïïïïïïïïïïïïïÝÝïï˻˦¡ŸšïïïïïïïïïïïïïïïïOJQJU Uj(jUB*CJmHsHPJnH ^JaJB*CJmHsHPJnH ^JaJ"B*CJmHsHPJnH ^JaJU"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ<öÈæÊ Í Í$ÍHÍTÍXÍlÍp͖͚͂͆ͦ͸ͼͺϼÏÞÏpÑrÑ|јÑÚÑúÑþÑÒÒÒ(Ò>ÒBÒLÒ^ÒbÒlÒpÒÕÕXÕöÚøÚ&ۼݾÝÈÝäÝ&ÞFÞJÞTÞbÞfÞpÞtÞÄÞÆÞüÞ4à6àRà†à¦àªà´àÌàÐàÚàáááââäâãrätääÄäääèäòä ååå4å8åBåïïïïïïïïïïïïïïïÝÝïïïïïïïïïïïïïïïïïïÝÝÝÝïïïïïïïïïïïïÝÝïïïïïïïïïïïïÝÝïïïïïïïïïïïï"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJWBåHåJåœådæfæhæjæ’æ¬æ®æ°æºæÎæÐæÒæææççç.ç2çbçdçjçlçzç|çŠçŽç¤ç¦ç®ç°ç¾çÀçÎçÒçòçôçüçþçèè&è*èZèNêPêpêëë.ë6ì8ìlì î"î$î&î8îZî\î^îhî|îîîÞÞÞÌÌÞÞÌÌÞÞÌÌÞÞÌÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞÞ¹ÞÞ¹ÞÞ¹Þ§§ÞÞÞÞÌÌÞÞÌÌ"B*CJmH sH 5PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJ mH sH 5PJnH ^JaJA|î~î€î”î¶î¸îºîÌîäîèîïïïï"ï$ï2ï6ï^ï`ïdïfïtïvï„ïˆïªï¬ï°ï²ï¸ïºïÈïÌïâïäïèïêïúïüï ðð6ð8ð>ð@ðPðRð`ðdð”ð–ðšðœð¬ð®ð¼ðÀðèðêðòðôðññññ$ñ2ò4òZòXôZô|ôöööúöüö,÷¨÷ïïÝÝïïÝÝïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïÝïÊïïÊïïÊïïÊï%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJO¨÷ª÷Ô÷œøžø øÂøÄøÞøàøüøù*ùvùzù†ùùôùøùúújúnú|ú†úèúìúüúû*û.û>ûHûÐûÔûäûîû*ü.ü>üHü‚ü†ü–ü üîüòüý ýýVýZýjýtý¼ýÀýÐýÚýþþþ"þTþXþhþrþªþêþîþþþÿLÿîîÞÞÞÈÞÈÞÈÈÈ´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´´'B*CJmH sH PJnH ^JaJOJQJ*B*CJmH sH 5PJnH ^JaJOJQJB*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJGLÿŒÿÿ ÿªÿêÿîÿþÿ:>NXŒ ªìð .8àäêô"&,6z~„Ž48>Hx|Œ–"2<¬²ÄÎX^r|  *®´ÈÒæêþª®¾ÈÌÖæ.   ëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëÛÙÆÛÛ%B*CJmH sH 65PJnH ^JaJ5B*CJmH sH PJnH ^JaJ'B*CJmH sH PJnH ^JaJOJQJN h ` b ¼ ~€à46Xòô:~€º‚†¾\^ªÄÆ äæ,‚&†&®&''N' 0"0X0H4J4Œ4$7&7d7ö9ø9(::<<<n<†>Š>Ê>tAvAÜADDDDÐFÒFG^M`MÄM®S°SÞSVUXUžUB]D]r]R^T^œ^$b&bnbÔdÖde&f(fpf¢iìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜÜìÜB*CJmH sH PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJV¢i¤iòi2lVlrlŠl¼l¾lðlòlôlölfm”m°mÐmþmn2n4n:nT‘n‘p‘¢‘¤‘ª‘¬‘Ú‘Þ‘ä‘’’’@’D’T’’”’¤’ö’ú’ “4“N“P“‚“„“Š“Œ“º“¾“Γâ“ü“þ“0”2”8”:”h”l”r”–”• ••• •0–J–ö—˜$œëÙÉÙɤëëëëëëëëëëëëëëëëëÙÉÙɤëëëëëÙÉÙɤëëëëëëë¢ÉÉ%B*CJmH sH 65PJnH ^JaJ5IB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJOJQJB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU'B*CJmH sH PJnH ^JaJOJQJ4$œ&œBœðžHžjžПèŸ ¡,¡p¡r¡ ¡ü¢ £´¥¶¥Ü¥Þ¦à¦ö¦¸§º§L¨N¨¬¨®¨XªZª8«:«>«R«À«Ä«Ø«T¬X¬j¬’­–­¬­8¯<¯€¯(±,±t±r³v³¬³(¶*¶B¶Ž···Ä·Ê·ëØÈØÈØÈØÈØÈëØÈØÈÈØÈÈØÈÈÈÈÈÈÈÈÈØÈÈØÈÈØÈÈØÈÈØÈÈØÈÈØÈØÈ¶È¶È"B*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJ'B*CJmH sH PJnH ^JaJOJQJ;Ê·Ì·è¸ê¸¹ ¹¢¹̹‚º„º¾ºJ»L»x»F¿H¿p¿hÀjÀŽÀ(Â@ÂjÆlÆ ÆÐËÒËÔÎÖÎlÕnÕðáòážâÂâ$ãPãôåöånæÔæ<çºç¼çRêTê|êPëfënëŒë¸ìÌìjíÞÎλÎλÎλÎλÎλÎλλΩ©ÎÎÎÎÎÎÎÎλλÎÎÎÎÎÎÎΗ—λλλÎ"B*CJ mH sH 5PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJB*CJmH sH PJnH ^JaJAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ5jílínípíœíží íªí¾íÀíÂíâíæíîîî îîî0î2îDîFîhîlîrîvîxî~î‚î„îŠîŽî’î˜îœîžî¤î¨îªî°î´î¸î¾îÂîÄîÊîÎîÐîÖîÚîÞîòîôîïï*ï.ïÒïöïð,ðJðbð~ðëëëÕëëÕÕëëÕëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëŲŲŲÅ%B*CJmH sH 65PJnH ^JaJB*CJmH sH PJnH ^JaJ*B*CJmH sH 5PJnH ^JaJOJQJ'B*CJmH sH PJnH ^JaJOJQJ@~ð€ð²ð´ð¶ð¸ðXñ„ñBòfòüòó¨óªó<ö>ö‚öð÷ø(øBø þ–þ˜þ¨þ¶þÎþæþ¦ÿ¼ÿÊÿäÿâüˆ¨´ 6NpÚÞ" $ € îÞîÞ½ÞªÞªÞªÞÞÞ˜˜””ÞÞ|||ÞÞÞÞÞÞªÞÞÞÞÞ˜˜.B*CJmH sH 6>*5PJnH ^JaJ]\65"B*CJmH sH 5PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU0€ j l ž   ¦ ¨ Ü ð bdrz’¢ØÚ ïÝïÝï¼ï©ï•‚p‚ï©[©[©(B*CJmH sH 65PJnH ^JaJU"B*CJmH sH 5PJnH ^JaJ%B*CJmH sH >*5PJnH ^JaJ'B*CJmH sH PJnH ^JaJOJQJ%B*CJmH sH 65PJnH ^JaJAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ<>xzìî "(*¢¤D¼ÊÒêìlnØÞàØÈÈÈÈÈÈȶȶȕÈÈȃƒp^pÈÈÈÈKȃ%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ%B*CJmH sH >*5PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJMB*CJmH sH 65PJnH ^JaJUB*CJmH sH 65PJnH ^JaJàøú6pš !!*!B!Z!ì"#$*$(%B%Î&Ö&î(ö(&).)b)„)Ê)^*`**x+¦+‚/„/ª/²/Ô/v0¤0~1’1`2”2&3r3 44T4V4’4X8Z8†8–9îêêêÒÒÒ°°êîîê®®®®®œœîîÂ"B*CJ mH sH 5PJnH ^JaJ5"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ.B*CJmH sH 6>*5PJnH ^JaJ]\65"B*CJmH sH 5PJnH ^JaJ6–9¶9;;6; = ==0=2=:>Œ>Ž>š>¼>¾>Ê>ö>ø>?fAhAtAŒAŽA–B®B˜C D¢D¼DìEîEFFGHGTGÀGÂGØGII6I8IjIlIvIxI@JBJNJfJhJpKŠKtL|M~MŒM¤MìÜÊÊÜÜÊÜÜÜÜÜÊÜÜÊÜÊÊÜÜÊÜÜÜÜÜÜÜÊÜÜÊÜÜÊÜÊÊÜìܸܸܗÜÜÊÜÜÜÜÜÜÜÊÜAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJ;¤M¦MÂM&N(Nd^dödeXefe`gbgzg²jÄj~kÆlàlm¦m¤o¶opq†q¬q®q¼qrïïïïïïïïïïÝïïÝïïÝïÝÝïïÝïïïïïÝïÝÝïÊïïïÊïÊïïÝïïïÝÝÝÝÆÄÝïÝïÄÄïÄÆÄïÝïïÝï565%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJMrr6rXv’vRwVwdw¬wêw2y^yÎyzz\z¬{æ{Ž|à|ä|,}.}h}æ~ À€ €x€z€¦€Zƒ€ƒ²ƒäƒ„„F„î„ð„…<†>†f†D‡‰ĉjŠlŠªŠ¬Š(‹*‹v‹x‹¶‹¸‹è‹.hÆÈÊŽ|‘~‘ ’"’t’Ú’Ü’ýýíýýýýýýýýýýýýÛÛííííÛÛÛÛÛÛíííííííííííÉÉí¶íÛÛííííÛíí%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ5GÜ’B“°“²“*”ž” ”*•¦•¨•D–¬–®–Ø–x—z—¼—à™ô™ššDšFšPšRšŠš¢šzœ|œœœŸ$Ÿ@ŸBŸtŸvŸ€Ÿ‚ŸˆŸŠŸ¦Ÿþ¡$¢<¢Z¢££>£n£|£@¤F¤^¦`¦j¦&§:§X§Z§Œ§îÞÞîÞÞîÞÞîÞîîÞÞîÞËÞ¹Þ¹Þ˜ÞîÞÞîÞËÞ¹Þ¹Þ˜ÞîîÞËÞËÞÞîÞËÞËÞÞîÞËÞ¹ÞAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU%B*CJmH sH 65PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ;Œ§ާ˜§š§ΧЧø§Z¨n¨ЍŒ¨¾¨À¨ʨ̨֩⩪ª.ªê¬ì¬­¶­Ô­@¯œ¯ž¯À¯æ±è± ²ä¶æ¶·º:ºØ»Ú» ¼¼¼¼:¼îÞ½ÞÞ«Þ˜ÞîÞîÞ½Þ«Þ††ÞttÞ˜Þ˜ttttttÞrrrU"B*CJmH sH 5PJnH ^JaJ"B*CJ mH sH 5PJnH ^JaJ%B*CJmH sH 65PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU+:¼D¼f¼h¼޼¾ÀÀÀèÀ4Ã6ÃZÃ\ÆæŨžÅÀÅèÅ~ƀƈNJÇÔÊÖÊúÎüÎψԊÔîÔ‚Õ„Õ†ÕˆÕªÕ¬Õ®ÕàÕâÕäÕüÕ ÖÖÖ*Ö0Ö>ÖLÖRÖjÖxÖ~ÖšÖ¨Ö®ÖÌÖÚÖàÖ×××L×Z×üêêÚêêÚÚÚêêÚÚÚêêÚÚÚÚÚÚÚêêÚÈÈ´´´ž´´ž´´ž´´´´´´´´´´´´´´´´´´´´´´*B*CJmH sH 5PJnH ^JaJOJQJ'B*CJmH sH PJnH ^JaJOJQJ"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ65>Z×`×l×zׂגנר×Î×Þ׿ר.Ø6ØNØ`ØhؒؠبØÙÙÂÛÄÛâÛöÛÜÜÜÜ"Ü&Ü2Ü6ÜBÜFÜRÜVÜbÜfÜrÜv܂܆ܒܖܢܦܲܶÜÂÜÆÜÒÜÖÜâÜæÜòÜöÜÝÝÝÝ"Ý&Ý2Ý6ÝBÝFÝRÝVÝbÝfÝrÝv݂݆ݒݖÝëëëëëëëëëëëëëëëëëëëëÛÛëÅÅëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëëë*B*CJmH sH 5PJnH ^JaJOJQJB*CJmH sH PJnH ^JaJ'B*CJmH sH PJnH ^JaJOJQJM–ÝšÝœÝÆÝøÞúÞ,ß.ß8ß:ßààâàlánáòâôâÒäÔ䤿¦æ¨èªèÌè`ébé°ê²êfðhð¶ñ¸ñäñ¶ö¸öÊö&ÿ(ÿZÿ\ÿfÿhÿ˜ÿšÿòÿ²FH"$VXbd‚Öp~š¬4` dïÝÝïËïËïªïïïïïïïïïïïÝÝïïïïïïïÝÝïÝݨ¨¨ÝÝïï﨨¨¦¤¦¤¤65UAB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH PJnH ^JaJU"B*CJmH sH 5PJnH ^JaJB*CJmH sH PJnH ^JaJ@dv†–êú.8H’¶F!H!f!æ#è#($X$˜$ü$þ$%T'Z'„'†'º'¢(¦(ò+,v.x.2J2r2¼2Æ2È2Ô2Ö2Ú2ýûûûûýéÙÙéÙÙÙÙÙÙÙéÙÇÙééÙÙÙÇÙÙÙ····£‘£‘#0JB*CJmH sH PJnH ^JaJ&0JB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJ"B*CJmH sH 6PJnH ^JaJB*CJmH sH PJnH ^JaJ"B*CJmH sH 5PJnH ^JaJ65,Ú2Ü2â2ä23<3†33’3ž3 3¤3¦3¬3°3¾3À3Î3Ð3Þ3à3î3ð3þ3444 4*4J4L4Z4r4t4’4®4Ê4ò4505X5|5‚5Úʺººº¦”¦”ÚÊ„„„„„„„„„„„pppppppp'B*CJmH sH PJnH ^JaJOJQJB*CJmH sH PJnH ^JaJ#0JB*CJmH sH PJnH ^JaJ&0JB*CJmH sH PJnH ^JaJUB*CJmH sH PJnH ^JaJB*CJmH sH PJnH ^JaJI0JB*CJmH sH PJnH ^JaJU0JB*CJmH sH PJnH ^JaJ*024ž ¢¤¦¨ª¬®°²´¶îéäßÚÕÐËÆÁ¼·²­¨£žœ$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$$a$„h^„h„]„„`„„†Ö Ø Ú Ü ¶ ð  6 ˜ > úøöÝØÅ¿¹³­§¡›•! ÆÀ! ! ÆÀ!  ÆÀ! ! ÆÀ! ! ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! -„^„„]„„`„¤ð¤x $.$a$$a$1$ Æ €P „°^„°„]„„`„$a$v¤àB¶N®ò,ˆÀ HŠÀùóíçáÛÕÏÉý·±«¥Ÿ™ ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! €Îö:pºö(R|®â^¸ö,ùóíçáÛÕÏÉý·±«¥Ÿ™ ÆÀ! ! ÆÀ! ! ÆÀ!  ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! ! ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! ,^Â6n¨àŠÈþ>dÐùóíçáÛÕÏÉý·±«¥Ÿ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ! ! ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ!  ÆÀ! <6ÊÌÜÞ¤ ¦ X#n#À$Â$¨*ª*þ/0Œ3Þ32646êèêæäâàÞÜÚêØÖÔÒÐêι·µ & F Æ„^„„]„„`„ & F Æ„^„„]„„`„46h6ˆ6"7$7j7l7®7Æ7Þ7ö7Z8Œ<Ž<â<=>=l=n=´=¶=ø=>ýûù÷õóñïíëÖÔÒÐÎÌÊÈÆÄÂÀ & F Æ„^„„]„„`„>(>l>°>ø>@?ˆ?Ð?ø?øGJHLLNL°LöL‡@‡B‡Zˆ\ˆò‰÷ñéñáñÙñ†€~|z$a$1$R$$T4Ö\Ö GT 4Ö4Ös4Ö4Ös ÖÖ ÿÿ$d£G$$d£G$$d£G$$d£$d£G$ ò‰ô‰þ‹Œ\^ԓ֓ƕȕ¾—À—ęƙê™ì™JšLšrštš–œ˜œýûù÷õóñïíëéçå×ÕÓÑÏÍËÉ „^„„]„„Ð`„ИœÔœFŸHŸXŸZŸbŸdŸfŸhŸnŸpŸrŸtŸvŸxŸ”Ÿ¼ŸêèæäâàÞÜÚØÖÔÒÐÈÀ¸$$a$G$$$a$G$$$a$G$ & F Æ„^„„]„„`„¼Ÿ¾ŸøŸüŸ    ( À»¶±rmhc$G$$G$$G$>$$T4ÖF¦Lü4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖF¦Lü4Ö4Öl4Ö4Öl( * B F P R x | † À»¶±rmhc$G$$G$$G$>$$T4ÖF¦Lü4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖF¦Lü4Ö4Öl4Ö4Öl† ˆ Š º¡ ¢2£À£Â£Ú£î£À¾©©©‘Œ„|$$G$$$$G$$$$ & F$ Æh„h^„h„]„„˜þ`„˜þ$ & F Æh„h^„h„]„„˜þ`„˜þ>$$T4ÖF¦Lü4Ö4Öl4Ö4Öl î£ð£ô£¤¤¤D¤F¤Ëû‡wC3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4ÖlF¤J¤n¤p¤t¤Š¤Œ¤¤¬¤÷ﻳ«wog$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$¬¤®¤²¤Î¤Ð¤Ô¤ì¤î¤Ëû‡wC3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Ölî¤ò¤¥¥¥,¥.¥4¥H¥÷ﻳ«wog$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$H¥J¥P¥l¥n¥t¥Ž¥¥Ëû‡wC3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl¥–¥¶¥¸¥¾¥ô¥ö¥ü¥¦÷ﻳ«wog$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl$$G$$$$G$$¦ ¦"¦V¦–¨˜¨š¨¶¨ê¨ì¨ËƱ¯­¥•V>$$T4ÖFBE4Ö4Öl4Ö4Öl$$a$G$$$a$G$$$a$G$ & F Æ„^„„]„„`„$$3$$T4Ö0$õ4Ö4Öl4Ö4Öl ì¨ ©©©©0©4©>©@©B©úõ𱬧¢ca>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$ B©¦©^«`«b«~«²«´«Ò«Ö«à«êåàÕÊ¿€xph$$G$$$$G$$$$G$$>$$T4ÖFBE4Ö4Öl4Ö4Öl $$a$$G$$ $$a$$G$$ $$a$$G$$$$$$ & F Æ„^„„]„„`„ à«â«¬¬$¬&¬(¬Ð­T®À¸°¨idbM & F Æ„^„„]„„`„$$>$$T4ÖFBE4Ö4Öl4Ö4Öl$$G$$$$G$$$$G$$>$$T4ÖFBE4Ö4Öl4Ö4ÖlT®° ° °(°\°^°|°€°Š°ýûóë㤟š•$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$$a$G$$$a$G$$$a$G$ аŒ°¤°¨°²°´°¼°À°Ê°À»¶±rmhc$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Ölʰ̰ΰ°³²³ÀµÂµêµ(¸*¸,¸F¸j¸À¾¼º¸¶¡œš•Š $$a$$G$$ $$a$$G$$$$$$ & F Æ„^„„]„„`„>$$T4ÖFBE4Ö4Öl4Ö4Öl j¸l¸|¸€¸‚¸–¸š¸œ¸Ëû‡wA5$$T”44Ö0x€4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0x4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0x4Ö4Öl4Ö4Ölœ¸¨¸¬¸®¸¼¸À¸Â¸Ì¸Þ¸÷ﻳ«wog$$G$$$$G$$3$$T4Ö0x4Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö0x4Ö4Öl4Ö4Öl$$G$$$$G$$Þ¸à¸â¸~¼¨¼J¾L¾ À¢ÀÃÃ6Ã8Ã2Å4Å ÆÆËÆÄ¯­«©§¢˜–”’Ž$a$$$$$ & F Æ„^„„]„„`„$$3$$T4Ö0x4Ö4Öl4Ö4ÖlÆ^Ç`ÇÈÈ,ÈPÈRÈ\È`ÈbÈtÈxÈýûù÷ïç³®©upk$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$$a$G$$$a$G$ xÈzÈŠÈŽÈȤȨȪȶÈËÆÁˆƒOJ$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl¶ÈºÈ¼ÈÔÈØÈÚÈäÈöÈøÈúÆÁ¼ˆƒ~J3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$$G$3$$T4Ö0’ó4Ö4Öl4Ö4Öl$G$øÈúÈäÊæÊ Í Í$ÍHÍJÍTÍXÍZÍýûùôïäÙ¥•a3$$T4Ö04Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö04Ö4Öl4Ö4Öl $$a$$G$$ $$a$$G$$$$$$ ZÍlÍpÍr͈͖͚͂͆Í÷ﻳ«wog$$G$$$$G$$3$$T4Ö04Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö04Ö4Öl4Ö4Öl$$G$$$$G$$šÍœÍ¦Í¸ÍºÍ¼Í`ÎbκÏÞÏpÑËû‡‚€~|gb$$ & F Æ„^„„]„„`„$$3$$T4Ö04Ö4Öl4Ö4Öl$$G$$$$G$$3$$T4Ö04Ö4Öl4Ö4Öl pÑrÑ|јÑÚÑÜÑúÑþÑÒúïäÙš’Š‚$$G$$$$G$$$$G$$>$$T4ÖFï7‰4Ö4Öl4Ö4Öl $$a$$G$$ $$a$$G$$ $$a$$G$$$$Ò ÒÒÒ(Ò*Ò>ÒBÒLÒÀ¸°¨iaYQ$$G$$$$G$$$$G$$>$$T4ÖFï7‰4Ö4Öl4Ö4Öl$$G$$$$G$$$$G$$>$$T4ÖFï7‰4Ö4Öl4Ö4ÖlLÒNÒ^ÒbÒlÒnÒpÒÕXÕÀ¸°¨idbM & F Æ„^„„]„„`„$$>$$T4ÖFï7‰4Ö4Öl4Ö4Öl$$G$$$$G$$$$G$$>$$T4ÖFï7‰4Ö4Öl4Ö4ÖlXÕöÚ&ۼݾÝÈÝäÝ&Þ(ÞFÞJÞTÞýèãÞÓȽ~vnf$$G$$$$G$$$$G$$>$$T4ÖFZÀ4Ö4Öl4Ö4Öl $$a$$G$$ $$a$$G$$ $$a$$G$$$$$$ & F Æ„^„„]„„`„ TÞVÞbÞfÞpÞrÞtÞÄÞüÞÀ¸°¨idbM & F Æ„^„„]„„`„$$>$$T4ÖFZÀ4Ö4Öl4Ö4Öl$$G$$$$G$$$$G$$>$$T4ÖFZÀ4Ö4Öl4Ö4ÖlüÞ2à4à6àRà†àˆà¦àªà´àýûóë㤟š•$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$$a$G$$$a$G$$$a$G$ ´à¶àÌàÐàÚàÜàáááÀ»¶±rmhc$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öláá áââãpärätääÄäÀ¾¼§¥£›“‹$$a$G$$$a$G$$$a$G$ & F Æ„^„„]„„`„>$$T4ÖFBE4Ö4Öl4Ö4Öl ÄäÆäääèäòäôä åååÀ»¶±rmhc$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Ölåå4å8åBåDåFåHåœåbæÀ»¶±rpnYW & F Æ„^„„]„„`„>$$T4ÖFBE4Ö4Öl4Ö4Öl$G$$G$$G$>$$T4ÖFBE4Ö4Öl4Ö4Öl bædæfæhæ’æ¬æ®æºæÎæÐæææçç.çý÷ïæÙÑÈ»³ª•ˆ $1$ ÆO¤¤:$d7ÿG$ $1$ Æy¤¤:$1$ Æy$d7ÿG$ $1$ Æ+¤¤:$1$ Æ+$d7ÿG$ $1$ ÆÞ¤¤:$1$ ÆÞ$d7ÿG$$a$1$ .ç0ç2çbçdçjçlçzç|çŠçµ­¥•…}u$d£1$$d£G$$d£1$$d£G$$d£1$$d£G$$d£1$$d£G$I$$T4Ö\Ö GT@ 4Ö4Ö4Ö4Ö ŠçŒçŽç¤ç¦ç®ç°ç¾çÀçÎ瑉yqiaYQ$d£1$$d£G$$d£1$$d£G$$d£1$$d£G$$d£1$$d£G$m$$T4Ö\Ö GT@ 2Ös2Ös2Ös2Ös2Ös2Ös2Ös2Ös4Ö4Ö4Ö4Ö ÎçÐçÒçòçôçüçþç舀tl`XL $d£1$¤¤:$d£G$ $d£1$¤¤:$d£G$ $d£1$¤¤:$d£G$v$$T4Ö\Ö GT@ 2Ös2Ös2Ös2Ös2Ös2Ös2Ös2Ös4Ö4Ö4Ö4Ö ÖÖ ÿÿèè&è(è*èNêPêëë6ìlì÷ë}wtwtwt_ & F Æ„^„„]„„`„1$$a$1$m$$T4Ö\Ö GT@ 2Ös2Ös2Ös2Ös2Ös2Ös2Ös2Ös4Ö4Ö4Ö4Ö $d£1$¤¤:$d£G$ lì î"î$î8îZî\îhî|î~î”î¶î¸îÌîùðçÛËç¿¯ç£“ç‡ $$1$ ÆJ$$$1$ ÆJ¤¤:$ $$1$ ÆJ$$$1$ Æ+¤¤:$ $$1$ Æ+$$$1$ Æ­¤¤:$ $$1$ Æ­$ $d7ÿ$G$ $a$$1$$$1$$ Ìîäîæîèîïïïï"ï$ï2ï樂”œŒœ„œ|$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö$$1$ ÆJ¤¤:$ 2ï4ï6ï^ï`ïdïfïtïvï„ﵬ¤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö „ï†ïˆïªï¬ï°ï²ï¸ïºïÈﵬ¤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö ÈïÊïÌïâïäïèïêïúïüï 𵬤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö ð ðð6ð8ð>ð@ðPðRð`𵬤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö `ðbðdð”ð–ðšðœð¬ð®ð¼ðµ¬¤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö ¼ð¾ðÀðèðêðòðôðñññµ¬¤¬œ¬”¬Œ$$1$$$$1$$$$1$$$$1$$ $d]ÿ$G$I$$T4Ö\ …2¶4Ö4Ö4Ö4Ö ñññ2ò4òXôZôööúöüö¨÷Ô÷œøžøµ²²°­§­§­§­‰‰$1$$ & F$ Æ„^„„]„„`„$$a$1$1$$I$$T4Ö\ …2¶4Ö4Ö4Ö4Öžø øÂøÄøÞøàøüøù*ùïâÒŵ¨—‡$$1$ Æï¤¤:$$$1$ ÆïG$¤¤:$ $1$ Æj¤¤:$$1$ Æj¤¤:$ $1$ Ʀ¤¤:$$1$ Ʀ¤¤:$ $1$ Æë¤¤:$$1$ Æë¤¤:$*ù,ùvùzù†ùù« •Š $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$T$$T44Ö\] À©¿4Ö4ÖV4Ö4ÖV ÖÖ ÿÿù’ùôùøùúúúµªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖVújúnú|ú†úˆúèúìúüúûôéÞÓ‰~rg\ $d£$G$$ $d£$G$$ $d£$1$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ ûû*û.û>ûHûJûµªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖVJûÐûÔûäûîûðû*ü.ü>üHüóóóó©ž“ˆ} $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$1$G$$ HüJü‚ü†ü–ü ü¢üµªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV¢üîüòüý ýýýVýZýjýôéÞÓ‰~sh] $d£$G$$ $d£$G$$ $d£$1$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$ jýtývý¼ýÀýÐýÚýôªŸ”‰~ $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ÚýÜýþþþ"þ$þTþµ©©ž“I> $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$1$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖVTþXþhþrþtþªþêþîþþþÿôéÞ”ˆ}ˆrg $d£$G$$ $d£$G$$ $d£$1$$ $d£$1$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ ÿ ÿLÿŒÿÿ ÿªÿµ©ž“ˆ} $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$ $d£$1$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖVªÿ¬ÿêÿîÿþÿ µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV :>NXZŒ ªôéÞÓ‰~sh] $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ ª¬ìð  µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV .8:àäêôôéÞÓ‰~sh] $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ ôö"&,68µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV8z~„Ž48>HôéÞÓ‰~sh] $d£$G$$ $d£$G$$ $d£$G$$ $d£$1$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ HJx|Œ–˜µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV˜"2<>¬²ÄÎôéÞÓ‰€uj_ $d£$G$$ $d£$G$$ $d£$G$$ $d£$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ ÎÐX^r|~µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV~  *,®´ÈÒôéÞÓ‰~sh] $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$ ÒÔæêþ µªŸ”‰?I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$I$$T4Ö\] À©¿4Ö4ÖV4Ö4ÖV ª®¾ÈÊÌäæ  ` b ~€ôéÞÓ‰„‚€}}}}}}1$$$I$$T4Ö\] À©–4Ö4ÖV4Ö4ÖV $d£$G$$ $d£$G$$ $d£$G$$ $d£$G$$€46òô~€‚†\^ÄÆäæ‚&†&'' 0"0H4J4$7&7ö9ø9:<<<†>üüüüüüüüüüüüüüüüüüüüüüüüüüüüü1$†>Š>tAvADDÐFÒF^M`M®S°SVUXUB]D]R^T^$b&bÔdÖd&f(f¢iòiBnüüüüüüüüüüüüüüüüüüüüüüüüçü & F Æ„^„„]„„`„1$Bnln6o8o`o~ožo o¢o¼o¾oêçÞͽ°qiaY$d]ÿG$$$1$$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘ $$1$ ÆÉG$$$$a$$1$ ÆoG$$$$1$ ÆG$¤¤:$ $a$$1$$1$ & F Æ„^„„]„„`„ ¾oÄoÆoÔoÖoØopp ppp÷ï稠˜ˆ€x$$1$$$d]ÿG$$$1$$$d]ÿG$$$1$$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘$$1$$$d]ÿG$$$1$$ p p"pNpPpVpXphpjpÀ¸°¨ ˜Q>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘$$1$$$d]ÿG$$$1$$$d]ÿG$$$1$$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘jplp|p~p€p‚p„p†pˆp˜pšpœp÷ïçß×ψ€xp$$1$$$d]ÿG$$$1$$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘$$1$$$d]ÿG$$$1$$$d]ÿG$$$1$$$d]ÿG$ œpžp p¢p¤p´p¶p¸pºp¼p÷ï°¨ ˜ˆ€$$1$$$d]ÿG$$$1$$$d]ÿG$$$1$$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘$$1$$$d]ÿG$ ¼p¾pÀpìpîpôpöp0qÀ¸¬¤˜„ $$1$¤¤:$$d]ÿG$ $$1$¤¤:$$d]ÿG$ $$1$¤¤:$$d]ÿG$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘0q2q4qærèrtt²tu u"u$uRuÀ¸µ¯µš””‹ƒt$d£$1$ Æ7$$d£G$ $a$$1$$$1$$ & F Æ„^„„]„„`„$a$1$1$$a$$1$>$$T4ÖF@ ìÍ4Ö4Ö‘4Ö4Ö‘ Rubudupu„u†uœuÆuÈuìäÕº«˜Y>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£$1$ Æ÷¤¤:$$d£$1$ Æ÷$$d£G$$d£$1$ Æo¤¤:$$d£$1$ Æo$$d£G$$d£$1$ Æ7¤¤:$Èuðuôuvv&v*v.v0vôì䥚’ŠK>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$0vlvšvžv¢v¤vØvÜvèvóêâÚ›ˆ€$d£G$$d£G$ $d£$1$$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£1$G$ $d£$1$G$$èvêv&wTwXwfwhwŽw’wÀ¸­¥^SK$d£G$ $d£$1$$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$$d£G$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV’w¢w¤wÐwôwøwþwx2x÷¸¬¡™‘R¬>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$ $d£$1$G$$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$2xVxZx`xbxšx x°x²xôì䥚’ŠK>$$T4ÖFª 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$>$$T4ÖFÓ 4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$²x´xÌyÎyú|ü|~€Ü€Þ€ „"„êˆìˆ„´"$&RýûùöðöööîöðöðöÙ×ÎÆ³$d£$1$ Ƥ¤:$$d£G$ $a$$1$$ & F Æ„^„„]„„`„$a$1$1$RT`tvŒ¨ªÄ,÷èÕ;«l`U $d£$1$$ $d£$1$G$$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£$1$ Ƥ¤:$$d£$1$ Æ$$d£G$$d£$1$ Æo¤¤:$$d£$1$ Æo$$d£G$ ,046pt€‚°÷ï°¨ ˜YQ$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$°´ÀÂèìúü‘÷ï°¨ ˜YQ$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$‘‘‘‘T‘Ú‘Þ‘ä‘æ‘÷ï°¤™‘‰J>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$ $d£$1$G$$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$æ‘’’’’@’D’T’V’÷ï稠˜Q>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$V’’”’¤’¦’ö’ú’ “ “÷ï稠˜Q>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$$d£G$ “4“º“¾“ΓГâ“h”l”r”óèàØ™óކ~$d£G$$d£G$ $d£$1$$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$ $d£$1$G$$ r”t”–”• ••••.–0–À´©¡™ZXVT>$$T4ÖF  v4Ö4ÖV4Ö4ÖV$d£G$$d£G$ $d£$1$$ $d£$1$G$$>$$T4ÖF  Ÿ4Ö4ÖV4Ö4ÖV 0–$œ&œp¡r¡´¥¶¥Þ¦à¦¸§º§L¨N¨¬¨®¨XªZª8«:«À«T¬’­8¯üöüöüüüüüüçççåçãçüÔÁÁÁ1$ Æ`ú„p^„p„]„„0ý`„0ý1$„ ^„ „]„„`„1$„Ð^„Є]„„`„$a$1$1$8¯(±r³(¶*¶è¸ê¸ ¹¢¹‚º„ºJ»L»F¿H¿hÀjÀjÆ ÆÐËÒËÔÎÖÎlÕnÕìììêçççççççççççççÒçççççç & F Æ„^„„]„„`„1$1$ Æ`ú„p^„p„]„„0ý`„0ýnÕðáòáôåöånæÔæ<çºç¼çRê|êjílíníœížíªí¾íÀíüüüüüüüüüüçááÙÌÄ»®¦$d7ÿG$ $1$ Æo¤¤:$1$ Æo$d7ÿG$ $1$ Æ7¤¤:$d7ÿG$$a$1$ & F Æ„^„„]„„`„1$Àíâíäíæíîîî îîò³«¦ž™‘Œ$1$$d]ÿG$$1$$d]ÿG$$1$$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘ $1$ Æ÷¤¤:îîî0î2îDîFîhîjîlîÀ¸³«¦ž™ZR$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘$1$$d]ÿG$$1$$d]ÿG$$1$$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘ lîvîxî‚î„îŽîî’îœîžî¨îªî´îúòíåà¡™”Œ‡z$1$$d]ÿG$$1$$d]ÿG$$1$$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘$1$$d]ÿG$$1$$d]ÿG$$1$ ´î¶î¸îÂîÄîÎîÐîÚîÜîÞîÀ¸³«¦ž™ZR$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘$1$$d]ÿG$$1$$d]ÿG$$1$$d]ÿG$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘ Þîòîôîïï*ï,ï.ï¨óªó<ö‚ö þ þöîåÝÔ•“{yw & F Æ„^„„]„„`„1$>$$T4ÖFÓ 4Ö4Ö‘4Ö4Ö‘$1$¤¤:$d]ÿG$$1$¤¤:$d]ÿG$$1$¤¤: þ–þ˜þ¨þ¶þÎþÐþÔþæþ¦ÿùùçáᢟŸŠ$1$ ÆPì„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö*$$a$*$$a$„^„„]„„`„$1$$ ¦ÿ¨ÿªÿ¼ÿÊÿÌÿÎÿäÿâÀ½½®o½½Z$1$ ÆPì„^„„]„„`„>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4ÖâäæüˆŠŽ¨À½½¨i½½T$1$ ÆPì„^„„]„„`„>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$1$ ÆPì„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö¨ª²´NÀ½½¥f½½Q$1$ Æ ï„^„„]„„`„>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$$a$1$ ÆPì„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4ÖNPnpÚÜÞ" € À½½¨if`K & F Æ„^„„]„„`„$a$1$1$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$1$ ÆPì„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö€ bd’<>xz¢¤üöåÒ˸ü¥ü’¸ƒ1$„Ð^„Є]„„`„1$ Æ`ú„^„„]„„Þô`„Þô1$ Æ0ý„^„„]„„Þô`„Þô1$ Æ`ú„^„„]„„Àô`„Àô1$ Æ`ú1$ Æ`ú„^„„]„„`„1$„Ð^„Є]„„`„$$a$1$1$ Dº¼êìlnÞøÀž ¬ ® êèæ×ıª—ê•“‘‹1$ Æ`ú„^„„]„„Àô`„Àô1$ Æ`ú1$ Æ`ú„^„„]„„±ô`„±ô1$ ÆPì„^„„]„„`„1$„Ð^„Є]„„`„ & F Æ„^„„]„„`„®  ! !!*!B!D!H!Z!ì"ýûéã㤡¡’$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö*$$a$*$$a$„^„„]„„`„ ì"î"ð"#$$$*$(%À½½®o½½®>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö(%*%,%B%Î&Ð&Ô&Ö&î(À½½®o½½®>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Öî(ð(ô(ö(&)(),).)b)À½½«l½½]$„^„„]„„`„>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$$a$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Öb)d)‚)„)Ê)Ì)Î)^**‚/À½½®omkVT & F Æ„^„„]„„`„>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö$„^„„]„„`„)$>$$T4ÖFO ˆ4Ö4Ö4Ö4Ö ‚/ª/T4’4X8†8;6; = =0=2=:>Œ>Ž>¼>¾>ö>êèÓÑêιÎΪΗ—ΪΪ1$ Æ`ú„@ ^„@ „]„„`ú`„`ú1$„Ð^„Є]„„`„ & F Æ„^„„]„„`„1$ & F Æ„^„„]„„`„ & F Æ„^„„]„„`„ö>?fAhAŒAŽA–B®B˜C D¢DìEîEFGHGÀGØG@JBJfJhJpKŠKêççØçÅÅÅÅçØçØçØêÀçØçÅÅ$1$1$ Æ`ú„@ ^„@ „]„„`ú`„`ú1$„Ð^„Є]„„`„1$ & F Æ„^„„]„„`„ŠKtL|M~M€M¤M¦MÂM&N(Ndôdöde`gbg®j°jÄj~k€køkZlÄlÆl o¢o¶oýûù÷éÛÙ×ÕÓÅÃÁ¿½»¹·µ „Ð^„Є]„„`„ „Ð^„Є]„„`„ „p^„p„]„„`ú`„`ú¶o¬q®qrrTvVv’vRwTwªw¬w.y0y^yÊyÌyzz¨{ñãÕÓÑÏÍ¿½»¹·µ³±¯­«© „Ð^„Є]„„`„ „Ð^„Є]„„`„ „Ð^„Є]„„`„ „Ð^„Є]„„`„¨{ª{æ{Š|Œ|à|â|,}.}â~ä~ ¼¾€€x€¦€XƒZƒ€ƒýûù÷õóñïíëéçåãáßÊÈÆ¸ „^„„]„„Ð`„Ð & F Æ„^„„]„„`„€ƒ²ƒäƒ„F„î„…<†f†²†´†B‡D‡‰ĉjŠñãÕÀ¾À¼Àº¸¶´¦˜Š „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú & F Æ„^„„]„„`„ „^„„]„„Ð`„Ð „^„„]„„Ð`„Ð „^„„]„„Ð`„ÐjŠlŠªŠ¬Š(‹*‹v‹x‹¶‹è‹ÆÈñãÕǹ«ztr$1$$ & F Æ„^„„]„„`„ „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú „p^„p„]„„`ú`„`ú ÈŽ|‘~‘ ’"’t’Ú’Ü’B“°“²“*”ž” ”*•¦•¨•D–¬–Ø–x—z—¼—êççççØØçØØçØØçØØçØØêÒÍØ$1$$1$$1$„Ð^„Є]„„`„1$ & F Æ„^„„]„„`„¼—zœ|œœœˆŸŠŸ¦Ÿ££>£^¦`¦j¦ΧЧø§ª.ªê¬­œ¯ðíðððððíÞÞíÞÞíÞÞÉí´í & F Æ„^„„]„„`„ & F Æ„^„„]„„`„1$„ ^„ „]„„`„1$1$„Ð^„Є]„„`„œ¯À¯æ± ²ì³î³ä¶·¹ ¹ºº:º<º » »f¼޼¾ÀèÀ4Ã6ÃZÃêèêæäâêàÞÜÚÌÊÈÆÄêÁêÁÁÁ1$ „^„„]„„Ð`„Ð & F Æ„^„„]„„`„ZÆæŨžÅèÅ~ƀƈNJÇÔÊÖÊúÎψÔîÔ‚Õ„ÕêçççêççØØÅçç꿪¨¢$a$1$ & F Æ„^„„]„„`„$1$$1$ Æ`ú„@ ^„@ „]„„÷`„÷1$„@ ^„@ „]„„÷`„÷1$ & F Æ„^„„]„„`„„Õ†ÕªÕ¬ÕàÕâÕüÕþÕ ÖÖÖ÷êâÕÍÀ‚|||$1$G$>$$44ÖF=+i!4Ö4Ö‘4Ö4Ö‘ $1$ Æô¤¤:$d7ÿG$ $1$ Æf¤¤:$d7ÿG$ $1$ Ƥ¤:$d7ÿG$ ÖÖ*Ö0Ö>Ö@ÖLÖRÖjÖ¼¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘jÖlÖxÖ~֚֮֜֨ÖÌÖ¼¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘ÌÖÎÖÚÖàÖ× ×××L×¼¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘L×N×Z×`×l×n×zׂג׼¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘’הנר×Î×Ð×Þ׿ר¼¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘Ø Ø.Ø6ØNØPØ`Øhؒؼ¼¼¼¼¼<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘$1$G$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘’ؔؠبØÙÙÙÂÛÄÛâÛ¸¸¸{ur`X$d7ÿG$$a$1$„ñÿ^„ñÿ„]„„`„1$$a$1$<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘ $1$G$¤¤:<$$4ÖF=+i!4Ö4Ö‘4Ö4Ö‘ âÛöÛøÛÜÜÜÜÜÜ"Ü÷¼¼ˆ¼¼T¼3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$5$$T44Ö0å54Ö4ÖV4Ö4ÖV$d7ÿG$ "Ü&Ü(Ü2Ü6Ü8ÜBÜFÜHÜRÜVÜùÅùù‘ùù]ùù3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$ VÜXÜbÜfÜhÜrÜvÜx܂܆ÜËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV †ÜˆÜ’ܖܘܢܦܨܲܶÜËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV ¶Ü¸ÜÂÜÆÜÈÜÒÜÖÜØÜâÜæÜËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV æÜèÜòÜöÜøÜÝÝÝÝÝËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV ÝÝ"Ý&Ý(Ý2Ý6Ý8ÝBÝFÝËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV FÝHÝRÝVÝXÝbÝfÝhÝrÝvÝËÅÅ‘ÅÅ]ÅÅ3$$T4Ö0å54Ö4ÖV4Ö4ÖV3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖV vÝx݂݆݈ݒݖݘݚÝËÅÅ‘‡‡SM$a$1$3$$T4Ö0å54Ö4ÖV4Ö4ÖV $1$G$¤¤:3$$T4Ö0å54Ö4ÖV4Ö4ÖV$1$G$3$$T4Ö0å54Ö4ÖV4Ö4ÖVšÝÆÝààâàlánáòâôâöâÒäÔ䤿¦æ¨èÌè`ébé°ê²êfðhð¶ñäñêççÔçÔçÔçÔçÔêççÅçÅçÅê1$„ ^„ „]„„0ý`„0ý1$ Æ`ú„ ^„ „]„„0ý`„0ý1$ & F Æ„^„„]„„`„äñ¶öÊö‚ü„ü˜ÿòÿ°²FHœž€‚˜šbüçåãáçßÝÎÀ²°®¬ª¨¦¤ „Ð^„Є]„„0ý`„0ý „Ð^„Є]„„0ý`„0ý1$„Ð^„Є]„„0ý`„0ý & F Æ„^„„]„„`„1$bd’F!H!æ#è#($X$˜$ü$þ$„'º'¢(¦(v.x.2ýûù÷õòòòòßßßÐòò»òòòòò & F Æ„^„„]„„`„1$„^„„]„„Ð`„Ð1$ Æ`ú„ ^„ „]„„`ú`„`ú1$2J2r2¼2Þ2à2â2ä23<3†3¨3úôî諦¤Ÿôîè,$a$,$a$<$$4ÖF÷ ^À!4Ö4Ö4Ö4Ö,$$a$,$$a$,$$a$,$a$ ¨3ª3¬3®3°3¾3À3Î3Ð3Þ3à3î3ð3þ3444½»¹´²­«¦¤Ÿ˜–‘Œ$a$$a$$a$$a$$a$$a$$a$,$a$<$$4ÖF÷ ^À!4Ö4Ö4Ö4Ö4 4*4J4L4Z4r4t4’4®4Ê4ò4505X5|5~5€5‚5„5ýøóñìçåãáßÝÛÙÔÒÐÎÌÈ1$$$$a$$a$$a$$a$"°Ð/ °à=!°"°# $ 3P(20-°Ð/ °à=!°"°° # ° $ 3P(2(2 0-°Ð/ °à=!°"°° # ° $ 3P(2(2 0-°Ð/ °à=!°"°° # ° $ 3P(2(2 0W°Ð/ °à=!°"°° # ° $‚ 3P(2(2°Ð/ °à=!°"°# ° $‚ 3P(200P-°Ð/ °à=!°"°° # ° $‚ 3P(2(2 0-°Ð/ °à=!°"°° # ° $‚ 3P(2(2 0Root Entryÿÿÿÿÿÿÿÿ ÀFÀCompObjÿÿÿÿjOle ÿÿÿÿÿÿÿÿ1TableÿÿÿÿÿÿÿÿÐJData ÿÿÿÿÿÿÿÿÿÿÿÿvSummaryInformation(ÿÿÿÿÿÿÿÿXWordDocumentÿÿÿÿÿÿÿÿ+hÕObjectPoolÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿlibflaim-4.9.966/docs/docs/0000755000175000017500000000000010510774540016745 5ustar ahodgkinsonahodgkinsonlibflaim-4.9.966/docs/docs/Doxyfile0000644000175000017500000001225010510774540020453 0ustar ahodgkinsonahodgkinsonPROJECT_NAME = "FLAIM" PROJECT_NUMBER = "4.9.966" OUTPUT_DIRECTORY = /home/ahodgkinson/work/opensource/flaim/build/docs CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = YES BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = "" STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 3 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = NO SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = INPUT = src/flaim.h \ /home/ahodgkinson/work/opensource/ftk/src/ftk.h FILE_PATTERNS = *.h RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO USE_HTAGS = NO VERBATIM_HEADERS = NO ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES SEARCHENGINE = NO libflaim-4.9.966/docs/docs/html/0000755000175000017500000000000010510774540017711 5ustar ahodgkinsonahodgkinsonlibflaim-4.9.966/docs/docs/html/doxygen.png0000644000175000017500000000240110510774540022071 0ustar ahodgkinsonahodgkinson‰PNG  IHDRd-ok>ÂgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<]PLTEǾÏ"&©ÈÎï¶»ÖÓÚú“¢Þ ¬à¶Âõ‡§ÕÙêÉÊÎáâæ{ŽÔ¡ëˆ™× ²ø§¬¹ÀÀ±ÝÝÎùùéõõçëëåED9×ÖËhg]_X<@:#mhUÿÿÿÝÀ1tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍvÿIDATxÚbC£: d#„„………h` @¡X",***LKˆ.–], ºX@t± €èb @ÑÅ€BµD„6–š%""´° € ˜% ˆ™B:H¢ˆ²Áf@• ˆRPy"K`\PbC(!II!h©…ëƒ(ñ„Ä!ꈬC„Ä…àl!0[X\J\$TMˆ(’>a$S„ Ù@ Ш@R.$‚¬LJBR¢‰AÌG1 ¬ Â(FȃÔPhhÁTÀ¢„%!`€&q°%u P ¹¢ ¬ € ¹CT$B¢à|‚ºW„¤Àl £!B`R$( …Ĉ‘’ž@AÅ%ĤÄ%@,(—ʂڱ%$ÁââRPmB U`1IˆYB  99€\1 yCCCÿf"[N 'Ü=TGÈ’øl8˜^Kû5<êSæRɤ”%î@@ à›Ê b1 qÅAXHˆ¸&ØB’R y n˜P„Ìã–4A €€j¹€€>Ü ˜ t!˜+(.ÈÅWQ±A2ÜÜMUÜ‚’’‚‚â `1 %`19€F< 3cZÄ`óe!\ˆ DÈ+. 83‹³Àä¸!lYYA -6‚EJŠ¢V €@©žXXX 4„å Ê@86Ð`RdB´€4I "Ý "–@xrÊŒ‚H€AÊ`—f ÉȰCŒ"XV0ɲ³C b@2…¬H ¬È“ p)!(ì‚ 0Ž4ˆ)(%RÁÎ ¶$€TÊ€¥Àþb‡b,säÐ@7À üѰ‚Òî?f¥Ö—\PIx!I´¦"”Ȉ’3¨ QY˜ÿt^^ÛØgv- }>WJOAV`$&#”¦8ùøø8€\FF ›SFJ$ÂÆ€ÐƊС䈉ÀÀ 4ª…Èäå -Á§‡ €H²…—ŸŸŸf ?ðâ5„ €k1Âd‰,ŒÃ ³ƒ“€.€"­F™ËË€àñ‚½ÁIÈ€"±Ù4ÉH gx|‚f©m)))9´. aMDƒ& ºX@t± €èb @ÑÅ€¢‹%DKˆ.–], ºX@t± €èb @€d`‚ɽSµOIEND®B`‚libflaim-4.9.966/docs/docs/html/tab_b.gif0000644000175000017500000000004310510774540021444 0ustar ahodgkinsonahodgkinsonGIF89a€„°Ç,D;libflaim-4.9.966/docs/docs/html/tab_l.gif0000644000175000017500000000130210510774540021455 0ustar ahodgkinsonahodgkinsonGIF89a ,Õö÷ùñô÷öøúüýþúûüùúûøùúêïóïóöÆÕßÒÞæØâéÞçíÝæìåìñèîòô÷ùóöø³ÈÕÁÒÝËÙâÏÜäÖá薴ŹɯÂÍ»ÎÙÃÔÞÂÓÝÈ×àÌÚâÕáèÙäê×âèåìðëðó„°ÇÑÞåÜæëãëïëñôîóõ÷úûûüüÿÿÿþþþ, ,ÿ@–P±É`H$!%CqVe2X­ŠÌJ(“Ä +€˜3 2$ÀÆ ¼kvŠä-Ëçõu*…"}ã|}|~q(" $f„ 'Žl(Œ&&$r‘™ › & ! )¢¤›{¨£¥r­ª°©¯„±¯¬´¦·»º³®«§¾¶ÃÂÀ¿²¹ÇÄËÆ²ÌÉεҽͼ„ÔÈÓ×иÙÝÕÏÙÊâÜßãçæê¾äÛÅëÇíáîÖìéïøñ÷õüÑðåùü¤Pß?‚ƒœÇÛBm åAœÎáÀ†%V܈î!Çk÷Ø/áÄ;^¤¨²$Æ–#Mf)f͇(WÎL‰“æKçÒ„° ’I)L:eD ¡Cµ´x*4 U¨h  %A«£^ÁNKb¬Ùe§X±‚´k»x!ÁÖí—2tÝÖ !¯š5tÛæé—À]$¬´%ƒXíâ.i[¬]Y­•ÊfžEëõkg`µ††:zëçÒž;£}ºµj×aa‹–Mš¶é׸cçž½»vïÛºƒóî›8ðáÈ‹'?®¼9óç©G_>Ýyuè¬_ßž]zwêß­‡Ç¾º¼mîæµG~½ûôÞთ/ž>ùööÙ«Ïÿ¿ÿýÿÅà|ÖWà}v;libflaim-4.9.966/docs/docs/html/tab_r.gif0000644000175000017500000000503110510774540021466 0ustar ahodgkinsonahodgkinsonGIF89a,Õö÷ùñô÷öøúüýþúûüùúûøùúêïóïóöÆÕßÒÞæØâéÞçíÝæìåìñèîòô÷ùóöø³ÈÕÁÒÝËÙâÏÜäÖá薴ŹɯÂÍ»ÎÙÃÔÞÂÓÝÈ×àÌÚâÕáèÙäê×âèåìðëðó„°ÇÑÞåÜæëãëïëñôîóõ÷úûûüüÿÿÿþþþ,,ÿ@’pH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬v •h<¬pkL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~ÏwVa+‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ “*)^,*ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂö)'ÆÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæÚ¥(" ðñòóôõö÷øùúûüýþÿ H° ÁƒòK"ƒRHœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\éÅu&@€ Á²¦Í›8sêÜɳ§Oÿ–(±€DУH“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯S84± ‰hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€Ó} âDÌf(^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹m¹ðCÄHœXͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N÷ÃJ” Á®¹óçУKŸN½ºõëØ³kßν»÷ïàËO¾úñ€ dÇ@€‚‚L¤"ÉÈF:ò‘Œ¤$9† (8…&ÉÉNzò“  ¥(G©FB^²!˨)WÉÊVºò•°l¤)1™ wÄò–¸Ì¥.wÊYºäƒà¥0‡IÌbó¾|ÉHpÌf:ó™Ðìe pJ±ˆ€}Ȧ6·ÉÍnzó›à §8û0Â%"¸æ8×ÉÎvºóðŒ§<ÉPÎQ`ò%×$€>÷ÉÏ~úóŸ ¨@JЂô M¨BÊІ:ô¡¨D'ZPKF Ö¼&16ÊÑŽzô£ ©HGJRb ÷Lç5ÏÁÒ–ºô¥ÿ0©LgJÓšš#(e>¯‰Óžúô§@ ªP‡JÔ¢õ¨HMªR—ÊÔ¦:õ©PªT§JÕª&5;%U·ÊÕ®zõ«` «XÇJV«ÂC§‹ÑjY×ÊÖ¶ºõ­p«\ŠU´À¦xÍ«^÷Ê×¾úõ¯ÐÀi)$‚”ô°ˆM¬bËØÆ:vˆ, ಘͬf7ËÙÎzö³  ­hGKÚÒšö´¨M­jWËÚÖºöµ°­*$ÛSPô¶¸Í­nwËÛÞúö·ÀÅm +„â¸ÈM®r—ËÜæ:÷¹ÐE®?±9ÏêZ÷ºØÍ®v¿9€î"‚ºÛ ¯xÇKÞòb—™ÑLÿ¯z×Ë^A¢·½ð¯|ç†÷Ò÷¾øÍ¯0í«ßþú÷¿¡ä/€Là»×ÀN°‚ï(à;øÁ n0„'LaýJ¸ÂÎ0{/¬á{ؘþ°ˆG|Ë“øÄ(¥‰SÌâCrÅ.ޱŒ ãÛøÆv¬1ŽwÌc6ê¸Ç@ÞñƒLd¹ÈHNñ‘“Ìd/¹ÉPÎð“£LeO¹ÊXŽp–·|â+sùËýõ2˜ÇL_1“ùÌí53š×M5³ùÍÇt3œç¼_:ÛÙÂwÎs™õÌgøÊ¹Ï€p ýÌ?úÐ/F´¢ë¼èFãÒÐŽŽt!-éJã‘Ò–Îô1­éN»‘ÓžuÿA-êP“ºÔ>5ª3­êUWºÕ®Ž4¬cÝèYÓZѶ¾õ¡s­ëAóº×€þ5°ù,ìaç¹ØÆ¶3²“=çe3ûÍÎ~öš£-í3S»Úc¾6¶¿¬ímo¹ÛÞÆ2¸ÃMåq“Êæ>7“Ó­n$³»ÝD~7¼,ïyó¸ÞöÆ1¾ómã}óÛÈÿvµ¿Þâ\É/µÁNâ…3ÜÉ÷´Ã#Þá‰S\ÊguÆ-mñO¸ã0ÈC¾à‘“\Ë'_´ÉS^à•³|À.ùc.ó0לÐ4¿9~s®ó=÷¼Ï<ÿy|ƒ.ô4]ÏD?ºz“®ô67]ÙO§3Ó£ÞÌ©SÄW‡vÖÙl>õ­3Úëdî:Øu)ö±?ÚìÙF;˜Ë®öW²½í­|;ÜW)÷¹²îvtÞ˽w¾÷Ý|à×=xÂÞÝA;libflaim-4.9.966/docs/docs/html/tabs.css0000644000175000017500000000333610510774540021361 0ustar ahodgkinsonahodgkinson/* tabs styles, based on http://www.alistapart.com/articles/slidingdoors */ DIV.tabs { float : left; width : 100%; background : url("tab_b.gif") repeat-x bottom; margin-bottom : 4px; } DIV.tabs UL { margin : 0px; padding-left : 10px; list-style : none; } DIV.tabs LI, DIV.tabs FORM { display : inline; margin : 0px; padding : 0px; } DIV.tabs FORM { float : right; } DIV.tabs A { float : left; background : url("tab_r.gif") no-repeat right top; border-bottom : 1px solid #84B0C7; font-size : x-small; font-weight : bold; text-decoration : none; } DIV.tabs A:hover { background-position: 100% -150px; } DIV.tabs A:link, DIV.tabs A:visited, DIV.tabs A:active, DIV.tabs A:hover { color: #1A419D; } DIV.tabs SPAN { float : left; display : block; background : url("tab_l.gif") no-repeat left top; padding : 5px 9px; white-space : nowrap; } DIV.tabs INPUT { float : right; display : inline; font-size : 1em; } DIV.tabs TD { font-size : x-small; font-weight : bold; text-decoration : none; } /* Commented Backslash Hack hides rule from IE5-Mac \*/ DIV.tabs SPAN {float : none;} /* End IE5-Mac hack */ DIV.tabs A:hover SPAN { background-position: 0% -150px; } DIV.tabs LI#current A { background-position: 100% -150px; border-width : 0px; } DIV.tabs LI#current SPAN { background-position: 0% -150px; padding-bottom : 6px; } DIV.nav { background : none; border : none; border-bottom : 1px solid #84B0C7; } libflaim-4.9.966/docs/docs/html/doxygen.css0000644000175000017500000001604610510774540022107 0ustar ahodgkinsonahodgkinsonBODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { font-family: Geneva, Arial, Helvetica, sans-serif; } BODY,TD { font-size: 90%; } H1 { text-align: center; font-size: 160%; } H2 { font-size: 120%; } H3 { font-size: 100%; } CAPTION { font-weight: bold } DIV.qindex { width: 100%; background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.nav { width: 100%; background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.navtab { background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; margin-right: 15px; padding: 2px; } TD.navtab { font-size: 70%; } A.qindex { text-decoration: none; font-weight: bold; color: #1A419D; } A.qindex:visited { text-decoration: none; font-weight: bold; color: #1A419D } A.qindex:hover { text-decoration: none; background-color: #ddddff; } A.qindexHL { text-decoration: none; font-weight: bold; background-color: #6666cc; color: #ffffff; border: 1px double #9295C2; } A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff; } A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} A.codeRef:link { font-weight: normal; color: #0000FF} A.codeRef:visited { font-weight: normal; color: #0000FF} A:hover { text-decoration: none; background-color: #f2f2ff } DL.el { margin-left: -1cm } .fragment { font-family: Fixed, monospace; font-size: 95%; } PRE.fragment { border: 1px solid #CCCCCC; background-color: #f5f5f5; margin-top: 4px; margin-bottom: 4px; margin-left: 2px; margin-right: 8px; padding-left: 6px; padding-right: 6px; padding-top: 4px; padding-bottom: 4px; } DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } TD.md { background-color: #F4F4FB; font-weight: bold; } TD.mdPrefix { background-color: #F4F4FB; color: #606060; font-size: 80%; } TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold; } DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } BODY { background: white; color: black; margin-right: 20px; margin-left: 20px; } TD.indexkey { background-color: #e8eef2; font-weight: bold; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TD.indexvalue { background-color: #e8eef2; font-style: italic; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TR.memlist { background-color: #f0f0f0; } P.formulaDsp { text-align: center; } IMG.formulaDsp { } IMG.formulaInl { vertical-align: middle; } SPAN.keyword { color: #008000 } SPAN.keywordtype { color: #604020 } SPAN.keywordflow { color: #e08000 } SPAN.comment { color: #800000 } SPAN.preprocessor { color: #806020 } SPAN.stringliteral { color: #002080 } SPAN.charliteral { color: #008080 } .mdTable { border: 1px solid #868686; background-color: #F4F4FB; } .mdRow { padding: 8px 10px; } .mdescLeft { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .mdescRight { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .memItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplParams { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; color: #606060; background-color: #FAFAFA; font-size: 80%; } .search { color: #003399; font-weight: bold; } FORM.search { margin-bottom: 0px; margin-top: 0px; } INPUT.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #e8eef2; } TD.tiny { font-size: 75%; } a { color: #1A41A8; } a:visited { color: #2A3798; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #84b0c7; } TH.dirtab { background: #e8eef2; font-weight: bold; } HR { height: 1px; border: none; border-top: 1px solid black; } libflaim-4.9.966/docs/docs/html/main.html0000644000175000017500000000171010510774540021522 0ustar ahodgkinsonahodgkinson FLAIM: Main Page

FLAIM Documentation

4.9.966


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/files.html0000644000175000017500000000261210510774540021702 0ustar ahodgkinsonahodgkinson FLAIM: File Index

FLAIM File List

Here is a list of all documented files with brief descriptions:
flaim.h
ftk.h

Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/flaim_8h.html0000644000175000017500000164012010510774540022272 0ustar ahodgkinsonahodgkinson FLAIM: flaim.h File Reference

flaim.h File Reference

#include "flaimtk.h"

Classes

struct  CREATE_OPTS
 Database create options. This structure is passed to FlmDbCreate() to specify create options for a new database. More...
class  F_NameTable
 Class for mapping names to IDs and vice versa. Methods in this class allow an application to get the dictionary number for a field, index, or container using the field name, index name, or container name. It also allows an application to to get a field name, index name, or field name using the dictionary number. More...
struct  FINDEX_STATUS
 Structure returned from FlmIndexStatus() to report current status of an index. More...
struct  OPT_INFO
 Structure returned when FCURSOR_GET_OPT_INFO_LIST option is passed to FlmCursorGetConfig(). More...
struct  FLM_CACHE_USAGE
 Structure that holds cache usage statistics. The statistics will be for either block cache or record cache. More...
struct  FLM_MEM_INFO
 Structure returned from FlmGetMemoryInfo(). More...
struct  FLM_TRANS_EVENT
 Structure returned to an event handler function whenever transaction events occur. Specifically, this structure is returned for transaction begin, commit, and abort events. More...
struct  FLM_UPDATE_EVENT
 Structure returned to an event handler function whenever database update events occur. Specifically, this structure is returned for record add, modify, and delete events, as well as DRN reserve events. More...
struct  FLM_RFL_SIZE_EVENT
 Structure returned to an event handler function whenever RFL size events occur. Specifically, this structure is returned whenever the RFL exceeds the on-disk size threshold specified for a database. More...
struct  CHECKPOINT_INFO
 Structure that gives the current state of the checkpoint thread. Returned from FlmDbGetConfig() when eDbGetConfigType::FDB_GET_CHECKPOINT_INFO is passed in as the option. More...
struct  SWEEP_INFO
 Structure that reports information on the progress of FlmDbSweep(). More...
struct  FCURSOR_SUBQUERY_STATUS
 Structure that reports query processing progress. More...
struct  DB_COPY_INFO
 Structure that is returned to the callback function that is passed to FlmDbCopy(). More...
struct  CHK_RECORD
 Structure that is returned to the callback function that is passed to FlmDbRebuild(). More...
struct  DB_UPGRADE_INFO
 Structure that is returned to the callback function that is passed to FlmDbUpgrade(). More...
struct  DB_BACKUP_INFO
 Structure that is returned to the callback function that is passed to FlmDbBackup(). More...
struct  DB_RENAME_INFO
 Structure that is returned to the callback function that is passed to FlmDbRename(). More...
struct  NODE
 Structure for nodes used in GEDCOM functions. Nodes are the basic components of GEDCOM trees. More...
class  FlmRecordSet
 This class allows an application to keep a set of FlmRecord objects. More...
class  FlmUserPredicate
 This is an abstract base class which defines the interface that an application must implement to embed its own predicate in a query. More...
struct  DISKIO_STAT
 Structure used in gathering statistics to hold a operation count, a byte count, and an elapsed time. More...
struct  RTRANS_STATS
 Statistics for read transactions. More...
struct  UTRANS_STATS
 Statistics for update transactions. More...
struct  BLOCKIO_STATS
 Statistics for block reads and writes. More...
struct  LFILE_STATS
 Statistics gathered for a particular logical file (index or container). More...
struct  DB_STATS
 Database statistics. More...
struct  FLM_STATS
 FLAIM statistics returned from FlmGetStats(). More...
class  F_UnknownStream
 This is an abstract base class that allows an application to read "unknown" data from the RFL or to write "unknown" data to the RFL. More...
struct  BYTE_PROGRESS
 Structure used to report the progress of a restore operation. More...
class  F_Restore
 This is an abstract base class for reading backup data during a FlmDbRestore() operation. More...
class  FlmBlob
 This class provides an interface for handling binary large objects. More...
class  FlmRecord
 Class for creating and modifying database records. More...
struct  BLOCK_INFO
 Structure containing statistics collected during FlmDbCheck() for a particular category of blocks in the database. More...
struct  REC_KEY
 Structure used to create a linked list of index keys from a record. More...
struct  CORRUPT_INFO
 Structure containing information about a specific corruption that is being reported by FlmDbCheck(). More...
struct  LEVEL_INFO
 Statistics for a particular level in an index or container b-tree. These statistics are gathered by FlmDbCheck(). More...
struct  LF_STATS
 Statistics gathered by FlmDbCheck() for a particular logical file (index or container). More...
struct  DB_CHECK_PROGRESS
 Structure returned during status callback from FlmDbCheck(). More...
struct  REBUILD_INFO
 Structure returned during status callback from FlmDbRebuild(). More...
struct  FMAINT_STATUS
 This structure is returned from FlmMaintenanceStatus(). It contains information about the background maintenance thread. More...

Defines

#define FERR_OK   NE_FLM_OK
 0 - Operation succeeded
#define FERR_BOF_HIT   NE_FLM_BOF_HIT
 0xC001 - Beginning of file or set hit.
#define FERR_EOF_HIT   NE_FLM_EOF_HIT
 0xC002 - End of file or set hit.
#define FERR_END   0xC003
 0xC003 - End of GEDCOM file - this is an internal error.
#define FERR_EXISTS   NE_FLM_EXISTS
 0xC004 - Record already exists.
#define FERR_FAILURE   NE_FLM_FAILURE
 0xC005 - Internal failure.
#define FERR_NOT_FOUND   NE_FLM_NOT_FOUND
 0xC006 - A record, key, or key reference was not found.
#define FERR_BAD_DICT_ID   0xC007
 0xC007 - Invalid dictionary record number -- outside unreserved range.
#define FERR_BAD_CONTAINER   0xC008
 0xC008 - Invalid container number.
#define FERR_NO_ROOT_BLOCK   0xC009
 0xC009 - LFILE does not have a root block - always handled internally - never returned to application.
#define FERR_BAD_DRN   0xC00A
 0xC00A - Cannot pass a zero DRN into modify or delete or 0xFFFFFFFF into add.
#define FERR_BAD_FIELD_NUM   0xC00B
 0xC00B - Bad field number in record being added.
#define FERR_BAD_FIELD_TYPE   0xC00C
 0xC00C - Bad field type in record being added.
#define FERR_BAD_HDL   0xC00D
 0xC00D - Request contained bad db handle.
#define FERR_BAD_IX   0xC00E
 0xC00E - Invalid index number.
#define FERR_BACKUP_ACTIVE   0xC00F
 0xC00F - Operation could not be completed - a backup is being performed.
#define FERR_SERIAL_NUM_MISMATCH   0xC010
 0xC010 - Comparison of serial numbers failed.
#define FERR_BAD_RFL_DB_SERIAL_NUM   0xC011
 0xC011 - Bad database serial number in RFL file header.
#define FERR_BTREE_ERROR   NE_FLM_BTREE_ERROR
 0xC012 - A corruption was found in an index or container b-tree.
#define FERR_BTREE_FULL   NE_FLM_BTREE_FULL
 0xC013 - An index or container b-tree is full.
#define FERR_BAD_RFL_FILE_NUMBER   0xC014
 0xC014 - Bad RFL file number in RFL file header.
#define FERR_CANNOT_DEL_ITEM   0xC015
 0xC015 - Cannot delete field definitions.
#define FERR_CANNOT_MOD_FIELD_TYPE   0xC016
 0xC016 - Cannot modify a field's type.
#define FERR_64BIT_NUMS_NOT_SUPPORTED   0xC017
 0xC017 - 64 bit numbers not supported for databases whose revision is less than 462
#define FERR_CONV_BAD_DEST_TYPE   0xC018
 0xC018 - Bad destination type specified for conversion.
#define FERR_CONV_BAD_DIGIT   0xC019
 0xC019 - Non-numeric digit found in text to numeric conversion.
#define FERR_CONV_BAD_SRC_TYPE   0xC01A
 0xC01A - Bad source type specified for conversion.
#define FERR_RFL_FILE_NOT_FOUND   0xC01B
 0xC01B - Could not open an RFL file.
#define FERR_CONV_DEST_OVERFLOW   NE_FLM_CONV_DEST_OVERFLOW
 0xC01C - Destination buffer not large enough to hold converted data.
#define FERR_CONV_ILLEGAL   NE_FLM_CONV_ILLEGAL
 0xC01D - Illegal conversion -- not supported.
#define FERR_CONV_NULL_SRC   0xC01E
 0xC01E - Source cannot be a NULL pointer in conversion.
#define FERR_CONV_NULL_DEST   0xC01F
 0xC01F - Destination cannot be a NULL pointer in conversion.
#define FERR_CONV_NUM_OVERFLOW   NE_FLM_CONV_NUM_OVERFLOW
 0xC020 - Numeric overflow (GT upper bound) converting to numeric type.
#define FERR_CONV_NUM_UNDERFLOW   0xC021
 0xC021 - Numeric underflow (LT lower bound) converting to numeric type.
#define FERR_DATA_ERROR   NE_FLM_DATA_ERROR
 0xC022 - Database corruption found.
#define FERR_NOT_USED_C023   0xC023
 0xC023 - Not used
#define FERR_DD_ERROR   0xC024
 0xC024 - Corruption found in logical file block chain.
#define FERR_INVALID_FILE_SEQUENCE   0xC025
 0xC025 - Incremental backup file number provided during a restore is invalid.
#define FERR_ILLEGAL_OP   NE_FLM_ILLEGAL_OP
 0xC026 - Illegal operation for database.
#define FERR_DUPLICATE_DICT_REC   0xC027
 0xC027 - Duplicate dictionary record found.
#define FERR_CANNOT_CONVERT   0xC028
 0xC028 - Condition occurred which prevents database conversion.
#define FERR_UNSUPPORTED_VERSION   0xC029
 0xC029 - Database version is not supported.
#define FERR_FILE_ER   0xC02A
 0xC02A - File error in a GEDCOM routine.
#define FERR_BAD_FIELD_LEVEL   0xC02B
 0xC02B - Invalid field level.
#define FERR_GED_BAD_RECID   0xC02C
 0xC02C - Bad record ID syntax.
#define FERR_GED_BAD_VALUE   0xC02D
 0xC02D - Bad or ambiguous/extra value in GEDCOM.
#define FERR_GED_MAXLVLNUM   0xC02E
 0xC02E - Exceeded GED_MAXLVLNUM in gedcom routines.
#define FERR_GED_SKIP_LEVEL   0xC02F
 0xC02F - Bad GEDCOM tree structure -- level skipped.
#define FERR_ILLEGAL_TRANS   0xC030
 0xC030 - Attempt to start an illegal type of transaction.
#define FERR_ILLEGAL_TRANS_OP   0xC031
 0xC031 - Illegal operation for transaction type.
#define FERR_INCOMPLETE_LOG   0xC032
 0xC032 - Incomplete log record encountered during recovery.
#define FERR_INVALID_BLOCK_LENGTH   0xC033
 0xC033 - Invalid block length.
#define FERR_INVALID_TAG   0xC034
 0xC034 - Invalid tag name.
#define FERR_KEY_NOT_FOUND   0xC035
 0xC035 - A key or reference is not found -- modify/delete error.
#define FERR_VALUE_TOO_LARGE   0xC036
 0xC036 - Value too large.
#define FERR_MEM   NE_FLM_MEM
 0xC037 - Memory allocation error.
#define FERR_BAD_RFL_SERIAL_NUM   0xC038
 0xC038 - Bad serial number in RFL file header.
#define FERR_NOT_USED_C039   0xC039
 0xC039 - Not used
#define FERR_NEWER_FLAIM   0xC03A
 0xC03A - Database version newer than this code base will support, must use newer version of code.
#define FERR_CANNOT_MOD_FIELD_STATE   0xC03B
 0xC03B - Attempted to change a field state illegally.
#define FERR_NO_MORE_DRNS   0xC03C
 0xC03C - The highest DRN number has already been used in an add.
#define FERR_NO_TRANS_ACTIVE   0xC03D
 0xC03D - Attempted to updated database outside transaction.
#define FERR_NOT_UNIQUE   NE_FLM_NOT_UNIQUE
 0xC03E - Found duplicate key for unique index.
#define FERR_NOT_FLAIM   0xC03F
 0xC03F - File is not a FLAIM database.
#define FERR_NULL_RECORD   0xC040
 0xC040 - NULL record cannot be passed to add or modify.
#define FERR_NO_HTTP_STACK   0xC041
 0xC041 - No http stack was loaded.
#define FERR_OLD_VIEW   0xC042
 0xC042 - While reading was unable to get previous version of block or record.
#define FERR_PCODE_ERROR   0xC043
 0xC043 - Corruption found in dictionary.
#define FERR_PERMISSION   0xC044
 0xC044 - Invalid permission for file operation.
#define FERR_SYNTAX   NE_FLM_SYNTAX
 0xC045 - Dictionary record has improper syntax, or syntax error in query criteria.
#define FERR_CALLBACK_FAILURE   0xC046
 0xC046 - Callback failure.
#define FERR_TRANS_ACTIVE   0xC047
 0xC047 - Attempted to close database while transaction was active.
#define FERR_RFL_TRANS_GAP   0xC048
 0xC048 - A gap was found in the transaction sequence in the RFL.
#define FERR_BAD_COLLATED_KEY   0xC049
 0xC049 - Something in collated key is bad.
#define FERR_UNSUPPORTED_FEATURE   0xC04A
 0xC04A - Attempting a feature that is not supported for the database version.
#define FERR_MUST_DELETE_INDEXES   0xC04B
 0xC04B - Attempting to delete a container that has indexes defined for it -- indexes must be deleted first.
#define FERR_RFL_INCOMPLETE   0xC04C
 0xC04C - RFL file is incomplete.
#define FERR_CANNOT_RESTORE_RFL_FILES   0xC04D
 0xC04D - Cannot restore RFL files - not using multiple RFL files.
#define FERR_INCONSISTENT_BACKUP   0xC04E
 0xC04E - A problem (corruption, etc) was detected in a backup set.
#define FERR_BLOCK_CHECKSUM   0xC04F
 0xC04F - Block checksum error.
#define FERR_ABORT_TRANS   0xC050
 0xC050 - Attempted operation after a critical error - should abort transaction.
#define FERR_NOT_RFL   0xC051
 0xC051 - Attempted to open file which was not an RFL file.
#define FERR_BAD_RFL_PACKET   0xC052
 0xC052 - RFL packet was bad.
#define FERR_DATA_PATH_MISMATCH   0xC053
 0xC053 - Bad data path specified to open database.
#define FERR_HTTP_REGISTER_FAILURE   0xC054
 0xC054 - Call to FlmConfig() with FLM_HTTP_REGISTER_URL option failed.
#define FERR_HTTP_DEREG_FAILURE   0xC055
 0xC055 - Call to FlmConfig() with FLM_HTTP_DEREGISTER_URL option failed.
#define FERR_IX_FAILURE   0xC056
 0xC056 - Indexing process failed, non-unique data was found when a unique index was being created.
#define FERR_HTTP_SYMS_EXIST   0xC057
 0xC057 - Tried to import new http related symbols before unimporting the old ones.
#define FERR_NOT_USED_C058   0xC058
 0xC058 - Not used
#define FERR_FILE_EXISTS   0xC059
 0xC059 - Attempt to create a database, but the database already exists.
#define FERR_SYM_RESOLVE_FAIL   0xC05A
 0xC05A - Could not resolve a symbol needed to run.
#define FERR_BAD_SERVER_CONNECTION   0xC05B
 0xC05B - Connection to FLAIM server is bad.
#define FERR_CLOSING_DATABASE   0xC05C
 0xC05C - Database is being closed due to a critical error.
#define FERR_INVALID_CRC   0xC05D
 0xC05D - CRC could not be verified.
#define FERR_KEY_OVERFLOW   0xC05E
 0xC05E - Key generated by the record causes the maximum key size to be exceeded.
#define FERR_NOT_IMPLEMENTED   NE_FLM_NOT_IMPLEMENTED
 0xC05F - Functionality not implemented.
#define FERR_MUTEX_OPERATION_FAILED   0xC060
 0xC060 - Mutex operation failed.
#define FERR_MUTEX_UNABLE_TO_LOCK   0xC061
 0xC061 - Unable to get the mutex lock.
#define FERR_SEM_OPERATION_FAILED   0xC062
 0xC062 - Semaphore operation failed.
#define FERR_SEM_UNABLE_TO_LOCK   0xC063
 0xC063 - Unable to get the semaphore lock.
#define FERR_NOT_USED_C064   0xC064
 0xC064 - Not used
#define FERR_NOT_USED_C065   0xC065
 0xC065 - Not used
#define FERR_NOT_USED_C066   0xC066
 0xC066 - Not used
#define FERR_NOT_USED_C067   0xC067
 0xC067 - Not used
#define FERR_NOT_USED_C068   0xC068
 0xC068 - Not used
#define FERR_BAD_REFERENCE   0xC069
 0xC069 - Bad reference in the dictionary.
#define FERR_NOT_USED_C06A   0xC06A
 0xC06A - Not used
#define FERR_NOT_USED_C06B   0xC06B
 0xC06B - Not used
#define FERR_NOT_USED_C06C   0xC06C
 0xC06C - Not used
#define FERR_NOT_USED_C06D   0xC06D
 0xC06D - Not used
#define FERR_NOT_USED_C06E   0xC06E
 0xC06E - Not used
#define FERR_NOT_USED_C06F   0xC06F
 0xC06F - Not used
#define FERR_UNALLOWED_UPGRADE   0xC070
 0xC070 - FlmDbUpgrade cannot upgrade the database.
#define FERR_NOT_USED_C071   0xC071
 0xC071 - Not used
#define FERR_NOT_USED_C072   0xC072
 0xC072 - Not used
#define FERR_NOT_USED_C073   0xC073
 0xC073 - Not used
#define FERR_ID_RESERVED   0xC074
 0xC074 - Attempted to use a dictionary ID that has been reserved.
#define FERR_CANNOT_RESERVE_ID   0xC075
 0xC075 - Attempted to reserve a dictionary ID that has been used.
#define FERR_DUPLICATE_DICT_NAME   0xC076
 0xC076 - Dictionary record with duplicate name found.
#define FERR_CANNOT_RESERVE_NAME   0xC077
 0xC077 - Attempted to reserve a dictionary name that is in use.
#define FERR_BAD_DICT_DRN   0xC078
 0xC078 - Attempted to add, modify, or delete a dictionary DRN >= FLM_RESERVED_TAG_NUMS.
#define FERR_CANNOT_MOD_DICT_REC_TYPE   0xC079
 0xC079 - Cannot modify a dictionary item into another type of item, must delete then add.
#define FERR_PURGED_FLD_FOUND   0xC07A
 0xC07A - Record contained a field whose field definition has been marked as purged.
#define FERR_DUPLICATE_INDEX   0xC07B
 0xC07B - Duplicate index.
#define FERR_TOO_MANY_OPEN_DBS   0xC07C
 0xC07C - Too many open databases.
#define FERR_ACCESS_DENIED   0xC07D
 0xC07D - Cannot access database.
#define FERR_NOT_USED_C07E   0xC07E
 0xC07E - Not used
#define FERR_CACHE_ERROR   0xC07F
 0xC07F - Cache block is corrupt.
#define FERR_NOT_USED_C080   0xC080
 0xC080 - Not used
#define FERR_BLOB_MISSING_FILE   0xC081
 0xC081 - Missing BLOB file on add/modify.
#define FERR_NO_REC_FOR_KEY   0xC082
 0xC082 - Record pointed to by an index key is missing.
#define FERR_DB_FULL   0xC083
 0xC083 - Database is full, cannot create more blocks.
#define FERR_TIMEOUT   0xC084
 0xC084 - Operation timed out (usually a query operation).
#define FERR_CURSOR_SYNTAX   0xC085
 0xC085 - Query criteria had improper syntax.
#define FERR_THREAD_ERR   0xC086
 0xC086 - Thread error.
#define FERR_UNIMPORT_SYMBOL   0xC087
 0xC087 - Failed to unimport a public symbol.
#define FERR_EMPTY_QUERY   0xC088
 0xC088 - Warning: Query has no results.
#define FERR_INDEX_OFFLINE   0xC089
 0xC089 - Warning: Index is offline and being rebuilt.
#define FERR_TRUNCATED_KEY   0xC08A
 0xC08A - Warning: Can't evaluate truncated key against selection criteria.
#define FERR_INVALID_PARM   NE_FLM_INVALID_PARM
 0xC08B - Invalid parameter.
#define FERR_USER_ABORT   0xC08C
 0xC08C - User or application aborted the operation.
#define FERR_RFL_DEVICE_FULL   0xC08D
 0xC08D - No space on RFL device for logging.
#define FERR_MUST_WAIT_CHECKPOINT   0xC08E
 0xC08E - Must wait for a checkpoint before starting transaction - due to disk problems - usually in RFL volume.
#define FERR_NAMED_SEMAPHORE_ERR   0xC08F
 0xC08F - Error occurred while accessing a named semaphore.
#define FERR_LOAD_LIBRARY   0xC090
 0xC090 - Failed to load a shared library module.
#define FERR_UNLOAD_LIBRARY   0xC091
 0xC091 - Failed to unload a shared library module.
#define FERR_IMPORT_SYMBOL   0xC092
 0xC092 - Failed to import a symbol from a shared library module.
#define FERR_BLOCK_FULL   0xC093
 0xC093 - Destination block for insert is full.
#define FERR_BAD_BASE64_ENCODING   0xC094
 0xC094 - Could not perform base 64 encoding.
#define FERR_MISSING_FIELD_TYPE   0xC095
 0xC095 - Field type not specified in field definition record.
#define FERR_BAD_DATA_LENGTH   0xC096
 0xC096 - Invalid field data length.
#define FERR_IO_ACCESS_DENIED   NE_FLM_IO_ACCESS_DENIED
 0xC201 - Access denied. Caller is not allowed access to a file.
#define FERR_IO_BAD_FILE_HANDLE   NE_FLM_IO_BAD_FILE_HANDLE
 0xC202 - Bad file handle.
#define FERR_IO_COPY_ERR   NE_FLM_IO_COPY_ERR
 0xC203 - Copy error.
#define FERR_IO_DISK_FULL   NE_FLM_IO_DISK_FULL
 0xC204 - Disk full.
#define FERR_IO_END_OF_FILE   NE_FLM_IO_END_OF_FILE
 0xC205 - End of file.
#define FERR_IO_OPEN_ERR   NE_FLM_IO_OPEN_ERR
 0xC206 - Error opening file.
#define FERR_IO_SEEK_ERR   NE_FLM_IO_SEEK_ERR
 0xC207 - File seek error.
#define FERR_IO_DIRECTORY_ERR   NE_FLM_IO_DIRECTORY_ERR
 0xC208 - Error occurred while accessing or deleting a directory.
#define FERR_IO_PATH_NOT_FOUND   NE_FLM_IO_PATH_NOT_FOUND
 0xC209 - Path not found.
#define FERR_IO_TOO_MANY_OPEN_FILES   NE_FLM_IO_TOO_MANY_OPEN_FILES
 0xC20A - Too many files open.
#define FERR_IO_PATH_TOO_LONG   NE_FLM_IO_PATH_TOO_LONG
 0xC20B - Path too long.
#define FERR_IO_NO_MORE_FILES   NE_FLM_IO_NO_MORE_FILES
 0xC20C - No more files in directory.
#define FERR_DELETING_FILE   NE_FLM_IO_DELETING_FILE
 0xC20D - Had error deleting a file.
#define FERR_IO_FILE_LOCK_ERR   NE_FLM_IO_FILE_LOCK_ERR
 0xC20E - File lock error.
#define FERR_IO_FILE_UNLOCK_ERR   NE_FLM_IO_FILE_UNLOCK_ERR
 0xC20F - File unlock error.
#define FERR_IO_PATH_CREATE_FAILURE   NE_FLM_IO_PATH_CREATE_FAILURE
 0xC210 - Path create failed.
#define FERR_IO_RENAME_FAILURE   NE_FLM_IO_RENAME_FAILURE
 0xC211 - File rename failed.
#define FERR_IO_INVALID_PASSWORD   NE_FLM_IO_INVALID_PASSWORD
 0xC212 - Invalid file password.
#define FERR_SETTING_UP_FOR_READ   NE_FLM_SETTING_UP_FOR_READ
 0xC213 - Had error setting up to do a read.
#define FERR_SETTING_UP_FOR_WRITE   NE_FLM_SETTING_UP_FOR_WRITE
 0xC214 - Had error setting up to do a write.
#define FERR_IO_AT_PATH_ROOT   NE_FLM_IO_CANNOT_REDUCE_PATH
 0xC215 - Currently positioned at the path root level.
#define FERR_INITIALIZING_IO_SYSTEM   NE_FLM_INITIALIZING_IO_SYSTEM
 0xC216 - Had error initializing the file system.
#define FERR_FLUSHING_FILE   NE_FLM_FLUSHING_FILE
 0xC217 - Had error flushing a file.
#define FERR_IO_INVALID_PATH   NE_FLM_IO_INVALID_FILENAME
 0xC218 - Invalid path.
#define FERR_IO_CONNECT_ERROR   NE_FLM_IO_CONNECT_ERROR
 0xC219 - Failed to connect to a remote network resource.
#define FERR_OPENING_FILE   NE_FLM_OPENING_FILE
 0xC21A - Had error opening a file.
#define FERR_DIRECT_OPENING_FILE   NE_FLM_DIRECT_OPENING_FILE
 0xC21B - Had error opening a file for direct I/O.
#define FERR_CREATING_FILE   NE_FLM_CREATING_FILE
 0xC21C - Had error creating a file.
#define FERR_DIRECT_CREATING_FILE   NE_FLM_DIRECT_CREATING_FILE
 0xC21D - Had error creating a file for direct I/O.
#define FERR_READING_FILE   NE_FLM_READING_FILE
 0xC21E - Had error reading a file.
#define FERR_DIRECT_READING_FILE   NE_FLM_DIRECT_READING_FILE
 0xC21F - Had error reading a file using direct I/O.
#define FERR_WRITING_FILE   NE_FLM_WRITING_FILE
 0xC220 - Had error writing to a file.
#define FERR_DIRECT_WRITING_FILE   NE_FLM_DIRECT_WRITING_FILE
 0xC221 - Had error writing to a file using direct I/O.
#define FERR_POSITIONING_IN_FILE   NE_FLM_POSITIONING_IN_FILE
 0xC222 - Had error positioning within a file.
#define FERR_GETTING_FILE_SIZE   NE_FLM_GETTING_FILE_SIZE
 0xC223 - Had error getting file size.
#define FERR_TRUNCATING_FILE   NE_FLM_TRUNCATING_FILE
 0xC224 - Had error truncating a file.
#define FERR_PARSING_FILE_NAME   NE_FLM_PARSING_FILE_NAME
 0xC225 - Had error parsing a file name.
#define FERR_CLOSING_FILE   NE_FLM_CLOSING_FILE
 0xC226 - Had error closing a file.
#define FERR_GETTING_FILE_INFO   NE_FLM_GETTING_FILE_INFO
 0xC227 - Had error getting file information.
#define FERR_EXPANDING_FILE   NE_FLM_EXPANDING_FILE
 0xC228 - Had error expanding a file (using direct I/O).
#define FERR_GETTING_FREE_BLOCKS   NE_FLM_GETTING_FREE_BLOCKS
 0xC229 - Had error getting free blocks from file system.
#define FERR_CHECKING_FILE_EXISTENCE   NE_FLM_CHECKING_FILE_EXISTENCE
 0xC22A - Had error checking if a file exists.
#define FERR_RENAMING_FILE   NE_FLM_RENAMING_FILE
 0xC22B - Had error renaming a file.
#define FERR_SETTING_FILE_INFO   NE_FLM_SETTING_FILE_INFO
 0xC22C - Had error setting file information.
#define FERR_NICI_CONTEXT   0xC301
 0xC301 - Failed to obtain a NICI context.
#define FERR_NICI_FIND_INIT   0xC302
 0xC302 - CCS_FindInit failed.
#define FERR_NICI_FIND_OBJECT   0xC303
 0xC303 - CCS_FindObject failed.
#define FERR_NICI_WRAPKEY_NOT_FOUND   0xC304
 0xC304 - Could not locate a wrapping key.
#define FERR_NICI_ATTRIBUTE_VALUE   0xC305
 0xC305 - CCS_AttributeValue failed.
#define FERR_NICI_BAD_ATTRIBUTE   0xC306
 0xC306 - Invalid attribute.
#define FERR_NICI_BAD_RANDOM   0xC307
 0xC307 - CCS_GetRandom failed.
#define FERR_NOT_USED_C308   0xC308
 0xC308 - Not used
#define FERR_NICI_WRAPKEY_FAILED   0xC309
 0xC309 - CCS_WrapKey failed.
#define FERR_NICI_GENKEY_FAILED   0xC30A
 0xC30A - CCS_GenerateKey failed.
#define FERR_REQUIRE_PASSWD   0xC30B
 0xC30B - Password required to unwrap key.
#define FERR_NICI_SHROUDKEY_FAILED   0xC30C
 0xC30C - CCS_pbeShroudPrivateKey failed.
#define FERR_NICI_UNSHROUDKEY_FAILED   0xC30D
 0xC30D - CCS_pbdUnshroudPrivateKey failed.
#define FERR_NICI_UNWRAPKEY_FAILED   0xC30E
 0xC30E - CCS_UnrapKey failed.
#define FERR_NICI_ENC_INIT_FAILED   0xC30F
 0xC30F - CCS_DataEncryptInit failed.
#define FERR_NICI_ENCRYPT_FAILED   0xC310
 0xC310 - CCS_DataEncrypt failed.
#define FERR_NICI_DECRYPT_INIT_FAILED   0xC311
 0xC311 - CCS_DataDecryptInit failed.
#define FERR_NICI_DECRYPT_FAILED   0xC312
 0xC312 - CCS_DataDecrypt failed.
#define FERR_NICI_INIT_FAILED   0xC313
 0xC313 - CCS_Init failed.
#define FERR_NICI_KEY_NOT_FOUND   0xC314
 0xC314 - Could not locate encryption/decryption key.
#define FERR_NICI_INVALID_ALGORITHM   0xC315
 0xC315 - Unsupported NICI ecncryption algorithm.
#define FERR_FLD_NOT_ENCRYPTED   0xC316
 0xC316 - Field is not encrypted.
#define FERR_CANNOT_SET_KEY   0xC317
 0xC317 - Attempted to set an encryption key for new encryption definition record.
#define FERR_MISSING_ENC_TYPE   0xC318
 0xC318 - Encryption type not specified in encryption definition record.
#define FERR_CANNOT_MOD_ENC_TYPE   0xC319
 0xC319 - Attempting to change the encryption type in encryption definition record.
#define FERR_MISSING_ENC_KEY   0xC31A
 0xC31A - Encryption key must be present in modified encryption definition record.
#define FERR_CANNOT_CHANGE_KEY   0xC31B
 0xC31B - Attempt to modify the encryption key in an encryption definition record.
#define FERR_BAD_ENC_KEY   0xC31C
 0xC31C - Bad encryption key.
#define FERR_CANNOT_MOD_ENC_STATE   0xC31D
 0xC31D - Illegal state change for an encryption definition record.
#define FERR_DATA_SIZE_MISMATCH   0xC31E
 0xC31E - Calculated encrypted data length does not match the length returned from encryption/decryption routines.
#define FERR_ENCRYPTION_UNAVAILABLE   0xC31F
 0xC31F - Encryption capabilities are not available for encrypting/decrypting data in database.
#define FERR_PURGED_ENCDEF_FOUND   0xC320
 0xC320 - Cannot use encryption ID for encryption of data - encryption definition record is marked as purged.
#define FERR_FLD_NOT_DECRYPTED   0xC321
 0xC321 - Attempting to access data from a field that is encrypted, field could not be decrypted for some reason - probably because encryption/decryption capabilities are not available.
#define FERR_BAD_ENCDEF_ID   0xC322
 0xC322 - Encryption ID is invalid - not defined in dictionary.
#define FERR_PBE_ENCRYPT_FAILED   0xC323
 0xC323 - Call to NICI function CCS_pbeEncrypt failed.
#define FERR_DIGEST_FAILED   0xC324
 0xC324 - Call to NICI function CCS_Digest failed.
#define FERR_DIGEST_INIT_FAILED   0xC325
 0xC325 - Call to NICI function CCS_DigestInit failed.
#define FERR_EXTRACT_KEY_FAILED   0xC326
 0xC326 - Call to NICI function CCS_ExtractKey failed.
#define FERR_INJECT_KEY_FAILED   0xC327
 0xC327 - Call to NICI function CCS_InjectKey failed.
#define FERR_PBE_DECRYPT_FAILED   0xC328
 0xC328 - Call to NICI function CCS_pbeDecrypt failed.
#define FERR_PASSWD_INVALID   0xC329
 0xC329 - Invalid password passed, database could not be opened.
#define FERR_BT_END_OF_DATA   0xFFFF
 0xFFFF - Used internally
#define FLM_SELECT_INDEX   32050
 Value passed to FlmCursorConfig() when FCURSOR_SET_FLM_IX is specified - allows FLAIM to select the index(es) for the query.

Typedefs

typedef void * HFDB
 Database handle.
typedef void * HFCURSOR
 Query object handle.
typedef void * HFBLOB
 BLOB handle.
typedef void * HFBACKUP
 Backup object handle.
typedef RCODE(* CURSOR_GET_FIELD_CB )(void *pvAppData, FlmRecord *pRecord, HFDB hDb, FLMUINT *puiFldPath, FLMUINT uiAction,#define FLM_FLD_FIRST#define FLM_FLD_NEXT#define FLM_FLD_CLEANUP#define FLM_FLD_VALIDATE#define FLM_FLD_RESET FlmRecord **ppFieldRecRV, void **ppFieldRV, FLMUINT *puiResult)
 Typedef for query "get field" function that can be inserted into query criteria wherever a field or field path could be inserted.
typedef void * HFEVENT
 Handle returned from FlmRegisterForEvent() - needed when calling FlmDeregisterForEvent().
typedef void(* FEVENT_CB )(FEventType eEventType, void *pvAppData, void *pvEventData1, void *pvEventData2)
 Typedef for function that is passed into FlmRegisterForEvent().
typedef void(* COMMIT_FUNC )(HFDB hDb, void *pvUserData)
 Function prototype for the commit function that can be set by calling FlmDbConfig() using the eDbConfigType::FDB_SET_COMMIT_CALLBACK option.
typedef FLMBOOL(* REC_VALIDATOR_HOOK )(eFlmFuncs eFlmFuncId, HFDB hDb, FLMUINT uiContainerId, FlmRecord *pRecord, FlmRecord *pOldRecord, void *pvAppData, RCODE *pRCode)
 Record validator function.
typedef RCODE(* IX_CALLBACK )(HFDB hDb, FLMUINT uiIndexNum, FLMUINT uiContainerNum, FLMUINT uiDrn, FlmRecord *pInputRecord, FlmRecord **ppModifiedRecord, void *pvAppData)
 Indexing callback function.
typedef RCODE(* STATUS_HOOK )(eStatusType eStatus, void *pvParm1, void *pvParm2, void *pvAppData)
 General purpose status callback function.
typedef RCODE(* BACKER_WRITE_HOOK )(void *pvBuffer, FLMUINT uiBytesToWrite, void *pvAppData)
 Typedef for callback function that is called from FlmDbBackup() to write out backed up data.

Enumerations

enum  qOptTypes { ,
  QOPT_USING_INDEX, QOPT_USING_PREDICATE, QOPT_SINGLE_RECORD_READ, QOPT_PARTIAL_CONTAINER_SCAN,
  QOPT_FULL_CONTAINER_SCAN
}
 Sub-query optimization types. More...
enum  eFlmFuncs { ,
  FLM_CURSOR_COMPARE_DRNS, FLM_CURSOR_CONFIG, FLM_CURSOR_FIRST, FLM_CURSOR_FIRST_DRN,
  FLM_CURSOR_GET_CONFIG, FLM_CURSOR_LAST, FLM_CURSOR_LAST_DRN, FLM_CURSOR_MOVE_RELATIVE,
  FLM_CURSOR_NEXT, FLM_CURSOR_NEXT_DRN, FLM_CURSOR_PREV, FLM_CURSOR_PREV_DRN,
  FLM_CURSOR_REC_COUNT, FLM_DB_CHECKPOINT, FLM_DB_UPGRADE, FLM_DB_CREATE,
  FLM_DB_GET_COMMIT_CNT, FLM_DB_GET_LOCK_INFO, FLM_DB_GET_LOCK_TYPE, FLM_DB_GET_TRANS_ID,
  FLM_DB_GET_TRANS_TYPE, FLM_DB_LOCK, FLM_DB_OPEN, FLM_DB_REDUCE_SIZE,
  FLM_DB_SWEEP, FLM_DB_TRANS_ABORT, FLM_DB_TRANS_BEGIN, FLM_DB_TRANS_COMMIT,
  FLM_DB_UNLOCK, FLM_INDEX_GET_NEXT, FLM_INDEX_STATUS, FLM_INDEX_RESUME,
  FLM_INDEX_SUSPEND, FLM_KEY_RETRIEVE, FLM_RESERVE_NEXT_DRN, FLM_RECORD_ADD,
  FLM_RECORD_MODIFY, FLM_RECORD_DELETE, FLM_RECORD_RETRIEVE
}
 FLAIM Functions. More...
enum  QTYPES { ,
  FLM_BOOL_VAL = 1, FLM_UINT32_VAL, FLM_INT32_VAL, FLM_REAL_VAL,
  FLM_REC_PTR_VAL, FLM_UINT64_VAL, FLM_INT64_VAL, FLM_BINARY_VAL = 9,
  FLM_STRING_VAL, FLM_UNICODE_VAL, FLM_TEXT_VAL , FLM_AND_OP = 100,
  FLM_OR_OP, FLM_NOT_OP, FLM_EQ_OP, FLM_MATCH_OP,
  FLM_MATCH_BEGIN_OP, FLM_MATCH_END_OP, FLM_CONTAINS_OP, FLM_NE_OP,
  FLM_LT_OP, FLM_LE_OP, FLM_GT_OP, FLM_GE_OP,
  FLM_BITAND_OP, FLM_BITOR_OP, FLM_BITXOR_OP, FLM_MULT_OP,
  FLM_DIV_OP, FLM_MOD_OP, FLM_PLUS_OP, FLM_MINUS_OP,
  FLM_NEG_OP, FLM_LPAREN_OP, FLM_RPAREN_OP
}
 Query value types and operators. More...
enum  eCursorConfigType {
  FCURSOR_CLEAR_QUERY = 2, FCURSOR_GEN_POS_KEYS, FCURSOR_SET_HDB, FCURSOR_SET_FLM_IX,
  FCURSOR_SET_OP_TIME_LIMIT, FCURSOR_SET_PERCENT_POS, FCURSOR_SET_POS, FCURSOR_SET_POS_FROM_DRN,
  FCURSOR_SET_REC_TYPE, FCURSOR_RETURN_KEYS_OK, FCURSOR_DISCONNECT = 14, FCURSOR_ALLOW_DUPS,
  FCURSOR_ELIMINATE_DUPS, FCURSOR_SET_REC_VALIDATOR, FCURSOR_SET_STATUS_HOOK, FCURSOR_SAVE_POSITION,
  FCURSOR_RESTORE_POSITION, FCURSOR_SET_ABS_POS
}
 Configuration options for FlmCursorConfig(). More...
enum  eCursorGetConfigType {
  FCURSOR_GET_OPT_INFO_LIST = 3, FCURSOR_GET_FLM_IX, FCURSOR_GET_OPT_INFO, FCURSOR_GET_PERCENT_POS,
  FCURSOR_GET_REC_TYPE = 9, FCURSOR_GET_FLAGS = 12, FCURSOR_GET_STATE, FCURSOR_GET_POSITIONABLE,
  FCURSOR_AT_BOF, FCURSOR_AT_EOF, FCURSOR_GET_ABS_POSITIONABLE, FCURSOR_GET_ABS_POS,
  FCURSOR_GET_ABS_COUNT
}
 Options for FlmCursorGetConfig(). More...
enum  eFlmConfigTypes {
  FLM_CLOSE_UNUSED_FILES, FLM_CLOSE_ALL_FILES, FLM_OPEN_THRESHOLD, FLM_OPEN_FILES,
  FLM_CACHE_LIMIT, FLM_SCACHE_DEBUG, FLM_START_STATS, FLM_STOP_STATS,
  FLM_RESET_STATS, FLM_TMPDIR, FLM_MAX_CP_INTERVAL, FLM_BLOB_EXT,
  FLM_MAX_TRANS_SECS, FLM_MAX_TRANS_INACTIVE_SECS, FLM_CACHE_ADJUST_INTERVAL, FLM_CACHE_CLEANUP_INTERVAL,
  FLM_UNUSED_CLEANUP_INTERVAL, FLM_MAX_UNUSED_TIME, FLM_BLOCK_CACHE_PERCENTAGE, FLM_CACHE_CHECK,
  FLM_CLOSE_FILE, FLM_LOGGER, FLM_ASSIGN_HTTP_SYMS, FLM_UNASSIGN_HTTP_SYMS,
  FLM_REGISTER_HTTP_URL, FLM_DEREGISTER_HTTP_URL, FLM_KILL_DB_HANDLES, FLM_QUERY_MAX,
  FLM_MAX_DIRTY_CACHE, FLM_DYNA_CACHE_SUPPORTED, FLM_QUERY_STRATIFY_LIMITS, FLM_DIRECT_IO_STATE
}
 Database system configuration options that are passed into FlmConfig() and FlmGetConfig(). More...
enum  FEventCategory { F_EVENT_LOCKS, F_EVENT_UPDATES, F_EVENT_SIZE }
 Event categories that an application can register to catch - see FlmRegisterForEvent(). More...
enum  FEventType {
  F_EVENT_LOCK_WAITING, F_EVENT_LOCK_GRANTED, F_EVENT_LOCK_SUSPENDED, F_EVENT_LOCK_RESUMED,
  F_EVENT_LOCK_RELEASED, F_EVENT_LOCK_TIMEOUT, F_EVENT_BEGIN_TRANS, F_EVENT_COMMIT_TRANS,
  F_EVENT_ABORT_TRANS, F_EVENT_ADD_RECORD, F_EVENT_MODIFY_RECORD, F_EVENT_DELETE_RECORD,
  F_EVENT_RESERVE_DRN, F_EVENT_INDEXING_COMPLETE, F_EVENT_RFL_SIZE
}
 Types of events returned to registered event handling functions. More...
enum  eStatusType { ,
  FLM_INDEXING_STATUS = 2, FLM_DELETING_STATUS, FLM_SWEEP_STATUS = 5, FLM_CHECK_STATUS = 7,
  FLM_SUBQUERY_STATUS = 13, FLM_DB_COPY_STATUS = 21, FLM_REBUILD_STATUS = 22, FLM_PROBLEM_STATUS,
  FLM_CHECK_RECORD_STATUS, FLM_EXAMINE_RECORD_STATUS, FLM_DB_UPGRADE_STATUS, FLM_DB_BACKUP_STATUS,
  FLM_DB_RENAME_STATUS, FLM_DELETING_KEYS, FLM_REBUILD_ADD_DICT_REC_STATUS
}
 Status types returned in the general purpose status callback function (STATUS_HOOK). More...
enum  eDbConfigType {
  FDB_SET_APP_VERSION = 3, FDB_RFL_KEEP_FILES, FDB_RFL_DIR, FDB_RFL_FILE_LIMITS,
  FDB_RFL_ROLL_TO_NEXT_FILE, FDB_KEEP_ABORTED_TRANS_IN_RFL, FDB_AUTO_TURN_OFF_KEEP_RFL, FDB_FILE_EXTEND_SIZE,
  FDB_SET_APP_DATA, FDB_SET_COMMIT_CALLBACK, FDB_ENABLE_FIELD_ID_TABLE, FDB_SET_RFL_SIZE_THRESHOLD,
  FDB_SET_RFL_SIZE_EVENT_INTERVALS, FDB_RFL_FOOTPRINT_SIZE, FDB_RBL_FOOTPRINT_SIZE
}
 Configuration options for FlmDbConfig(). More...
enum  eDbGetConfigType {
  FDB_GET_VERSION = 1, FDB_GET_BLKSIZ, FDB_GET_DEFAULT_LANG, FDB_GET_PATH = 17,
  FDB_GET_TRANS_ID, FDB_GET_CHECKPOINT_INFO, FDB_GET_LOCK_HOLDER, FDB_GET_LOCK_WAITERS,
  FDB_GET_LOCK_WAITERS_EX, FDB_GET_RFL_DIR, FDB_GET_RFL_FILE_NUM, FDB_GET_RFL_HIGHEST_NU,
  FDB_GET_RFL_FILE_SIZE_LIMITS, FDB_GET_RFL_KEEP_FLAG, FDB_GET_LAST_BACKUP_TRANS_ID, FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP,
  FDB_GET_SERIAL_NUMBER, FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG, FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG, FDB_GET_SIZES,
  FDB_GET_FILE_EXTEND_SIZE, FDB_GET_APP_DATA, FDB_GET_NEXT_INC_BACKUP_SEQ_NUM, FDB_GET_DICT_SEQ_NUM,
  FDB_GET_FFILE_ID, FDB_GET_MUST_CLOSE_RC, FDB_GET_RFL_FOOTPRINT_SIZE, FDB_GET_RBL_FOOTPRINT_SIZE
}
 Options for FlmDbGetConfig(). More...
enum  eDiagInfoType {
  FLM_GET_DIAG_INDEX_NUM = 1, FLM_GET_DIAG_DRN, FLM_GET_DIAG_FIELD_NUM, FLM_GET_DIAG_FIELD_TYPE,
  FLM_GET_DIAG_ENC_ID
}
 Types of diagnostic information available from FlmGetDiagInfo(). More...
enum  FBackupType { FLM_FULL_BACKUP = 0, FLM_INCREMENTAL_BACKUP }
 Types of backups supported by FLAIM. This type is passed into FlmDbBackupBegin(). More...
enum  eBackupGetConfigType { FBAK_GET_BACKUP_TRANS_ID = 1, FBAK_GET_LAST_BACKUP_TRANS_ID }
 Backup configuration information that can be requested by FlmBackupGetConfig(). More...
enum  eRestoreStatusType {
  RESTORE_BEGIN_TRANS = 1, RESTORE_COMMIT_TRANS, RESTORE_ABORT_TRANS, RESTORE_ADD_REC,
  RESTORE_DEL_REC, RESTORE_MOD_REC, RESTORE_RESERVE_DRN, RESTORE_INDEX_SET,
  RESTORE_PROGRESS, RESTORE_REDUCE, RESTORE_UPGRADE, RESTORE_ERROR,
  RESTORE_INDEX_SUSPEND, RESTORE_INDEX_RESUME, RESTORE_BLK_CHAIN_DELETE, RESTORE_WRAP_KEY,
  RESTORE_ENABLE_ENCRYPTION, RESTORE_CONFIG_SIZE_EVENT
}
 Restore status types reported through the F_Restore::status() method. More...
enum  eRestoreActionType { RESTORE_ACTION_CONTINUE = 0, RESTORE_ACTION_STOP, RESTORE_ACTION_SKIP, RESTORE_ACTION_RETRY }
 Actions that an application may want to tell FlmDbRestore() to take during a restore operation. More...
enum  FlmLogMessageType { FLM_QUERY_MESSAGE, FLM_TRANSACTION_MESSAGE, FLM_GENERAL_MESSAGE }
 Categories of messages that FLAIM can log and that an application can request to receive. More...
enum  eCorruptionType {
  FLM_NO_CORRUPTION = 0, FLM_BAD_CHAR, FLM_BAD_ASIAN_CHAR, FLM_BAD_CHAR_SET,
  FLM_BAD_TEXT_FIELD, FLM_BAD_NUMBER_FIELD, FLM_BAD_CONTEXT_FIELD, FLM_BAD_FIELD_TYPE,
  FLM_BAD_IX_DEF, FLM_MISSING_REQ_KEY_FIELD, FLM_BAD_TEXT_KEY_COLL_CHAR, FLM_BAD_TEXT_KEY_CASE_MARKER,
  FLM_BAD_NUMBER_KEY, FLM_BAD_CONTEXT_KEY, FLM_BAD_BINARY_KEY, FLM_BAD_DRN_KEY,
  FLM_BAD_KEY_FIELD_TYPE, FLM_BAD_KEY_COMPOUND_MARKER, FLM_BAD_KEY_POST_MARKER, FLM_BAD_KEY_POST_BYTE_COUNT,
  FLM_BAD_KEY_LEN, FLM_BAD_LFH_LIST_PTR, FLM_BAD_LFH_LIST_END, FLM_BAD_PCODE_LIST_END,
  FLM_BAD_BLK_END, FLM_KEY_COUNT_MISMATCH, FLM_REF_COUNT_MISMATCH, FLM_BAD_CONTAINER_IN_KEY,
  FLM_BAD_BLK_HDR_ADDR, FLM_BAD_BLK_HDR_LEVEL, FLM_BAD_BLK_HDR_PREV, FLM_BAD_BLK_HDR_NEXT,
  FLM_BAD_BLK_HDR_TYPE, FLM_BAD_BLK_HDR_ROOT_BIT, FLM_BAD_BLK_HDR_BLK_END, FLM_BAD_BLK_HDR_LF_NUM,
  FLM_BAD_AVAIL_LIST_END, FLM_BAD_PREV_BLK_NEXT, FLM_BAD_FIRST_ELM_FLAG, FLM_BAD_LAST_ELM_FLAG,
  FLM_BAD_LEM, FLM_BAD_ELM_LEN, FLM_BAD_ELM_KEY_SIZE, FLM_BAD_ELM_PKC_LEN,
  FLM_BAD_ELM_KEY_ORDER, FLM_BAD_ELM_KEY_COMPRESS, FLM_BAD_CONT_ELM_KEY, FLM_NON_UNIQUE_FIRST_ELM_KEY,
  FLM_BAD_ELM_FLD_OVERHEAD, FLM_BAD_ELM_FLD_LEVEL_JUMP, FLM_BAD_ELM_FLD_NUM, FLM_BAD_ELM_FLD_LEN,
  FLM_BAD_ELM_FLD_TYPE, FLM_BAD_ELM_END, FLM_BAD_PARENT_KEY, FLM_BAD_ELM_DOMAIN_SEN,
  FLM_BAD_ELM_BASE_SEN, FLM_BAD_ELM_IX_REF, FLM_BAD_ELM_ONE_RUN_SEN, FLM_BAD_ELM_DELTA_SEN,
  FLM_BAD_ELM_DOMAIN, FLM_BAD_LAST_BLK_NEXT, FLM_BAD_FIELD_PTR, FLM_REBUILD_REC_EXISTS,
  FLM_REBUILD_KEY_NOT_UNIQUE, FLM_NON_UNIQUE_ELM_KEY_REF, FLM_OLD_VIEW, FLM_COULD_NOT_SYNC_BLK,
  FLM_IX_REF_REC_NOT_FOUND, FLM_IX_KEY_NOT_FOUND_IN_REC, FLM_DRN_NOT_IN_KEY_REFSET, FLM_BAD_BLK_CHECKSUM,
  FLM_BAD_LAST_DRN, FLM_BAD_FILE_SIZE, FLM_BAD_AVAIL_BLOCK_COUNT, FLM_BAD_DATE_FIELD,
  FLM_BAD_TIME_FIELD, FLM_BAD_TMSTAMP_FIELD, FLM_BAD_DATE_KEY, FLM_BAD_TIME_KEY,
  FLM_BAD_TMSTAMP_KEY, FLM_BAD_BLOB_FIELD, FLM_BAD_PCODE_IXD_TBL, FLM_DICT_REC_ADD_ERR,
  FLM_BAD_FIELD_FLAG, FLM_BAD_FOP
}
 Types of corruption that can be reported by FlmDbCheck(). More...
enum  eCorruptionLocale { ,
  LOCALE_LFH_LIST, LOCALE_AVAIL_LIST = 3, LOCALE_B_TREE, LOCALE_IXD_TBL,
  LOCALE_INDEX
}
 Locations of corruptions in the database. More...
enum  eMaintAction { ,
  FLM_MAINT_IDLE, FLM_MAINT_LOOKING_FOR_WORK, FLM_MAINT_WAITING_FOR_LOCK, FLM_MAINT_ENDING_TRANS,
  FLM_MAINT_TERMINATED, FLM_MAINT_FREEING_BLOCKS
}
 Types of actions the background maintenance thread may currently be doing. More...

Functions

FLMEXP RCODE FLMAPI FlmCursorInit (HFDB hDb, FLMUINT uiContainerNum, HFCURSOR *phCursor)
 Initialize a query object.
FLMEXP RCODE FLMAPI FlmCursorFree (HFCURSOR *phCursor)
 Free a query object.
FLMEXP void FLMAPI FlmCursorReleaseResources (HFCURSOR hCursor)
 Release query object resources.
FLMEXP RCODE FLMAPI FlmCursorClone (HFCURSOR hSource, HFCURSOR *phCursor)
 Clone a query object.
FLMEXP RCODE FLMAPI FlmCursorConfig (HFCURSOR hCursor, eCursorConfigType eConfigType, void *pvValue1, void *pvValue2)
 Configure a query object.
FLMEXP RCODE FLMAPI FlmCursorGetConfig (HFCURSOR hCursor, eCursorGetConfigType eGetConfigType, void *pvValue1, void *pvValue2)
 Get query configuration.
FLMEXP RCODE FLMAPI FlmCursorSetOrderIndex (HFCURSOR hCursor, FLMUINT *puiFieldPaths, FLMUINT *puiIndex)
 Set order index for a query.
FLMEXP RCODE FLMAPI FlmCursorSetMode (HFCURSOR hCursor, FLMUINT uiFlags)
 Set mode for string comparison operations in query criteria.
FLMEXP RCODE FLMAPI FlmParseQuery (HFCURSOR hCursor, F_NameTable *pNameTable, const char *pszQueryCriteria)
 Parse a query string to set query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddField (HFCURSOR hCursor, FLMUINT uiFieldNum, FLMUINT uiFlags)
 Add a field to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddFieldPath (HFCURSOR hCursor, FLMUINT *puiFldPath, FLMUINT uiFlags)
 Add a field path to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddUserPredicate (HFCURSOR hCursor, FlmUserPredicate *pPredicate)
 Add an application defined predicate to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddFieldCB (HFCURSOR hCursor, FLMUINT *puiFldPath, FLMUINT uiFlags, FLMBOOL bValidateOnly, CURSOR_GET_FIELD_CB fnGetField, void *pvAppData, FLMUINT uiUserDataLen)
 Add a field callback function to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddOp (HFCURSOR hCursor, QTYPES eOperator, FLMBOOL bResolveUnknown=FALSE)
 Add a query operator to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddValue (HFCURSOR hCursor, QTYPES eValType, void *pVal, FLMUINT uiValLen)
 Add a value to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorValidate (HFCURSOR hCursor)
 Finalize and validate query syntax.
FLMEXP RCODE FLMAPI FlmStartup (void)
 Startup FLAIM database system.
FLMEXP void FLMAPI FlmShutdown (void)
 Shutdown FLAIM database system.
FLMEXP RCODE FLMAPI FlmConfig (eFlmConfigTypes eConfigType, void *pvValue1, void *pvValue2)
 Configure the FLAIM database system.
FLMEXP RCODE FLMAPI FlmGetConfig (eFlmConfigTypes eConfigType, void *pvValue)
 Get configuration information about the FLAIM database system.
FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit (FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave)
 Set dynamic cache limit.
FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit (FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate=FALSE)
 Set hard cache limit.
FLMEXP void FLMAPI FlmGetMemoryInfo (FLM_MEM_INFO *pMemInfo)
 Get cache information.
FLMEXP RCODE FLMAPI FlmGetThreadInfo (F_Pool *pPool, F_THREAD_INFO **ppThreadInfo, FLMUINT *puiNumThreads, const char *pszUrl=NULL)
 Get information on background threads in the FLAIM database system.
FLMEXP void FLMAPI FlmFreeMem (void *pMem)
 Free memory that was allocated by various functions.
FLMEXP RCODE FLMAPI FlmGetStats (FLM_STATS *pFlmStats)
 Get statistics.
FLMEXP void FLMAPI FlmFreeStats (FLM_STATS *pFlmStats)
 Free statistics.
FLMEXP RCODE FLMAPI FlmRegisterForEvent (FEventCategory eCategory, FEVENT_CB fnEventCB, void *pvAppData, HFEVENT *phEventRV)
 Register to catch events from the database system.
FLMEXP void FLMAPI FlmDeregisterForEvent (HFEVENT *phEventRV)
 Deregister event handling function.
FLMEXP RCODE FLMAPI FlmDbCreate (const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, const char *pszDictFileName, const char *pszDictBuf, CREATE_OPTS *pCreateOpts, HFDB *phDb)
 Create a new database.
FLMEXP RCODE FLMAPI FlmDbOpen (const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, FLMUINT uiOpenFlags, const char *pszPassword, HFDB *phDb)
 Open a database.
FLMEXP RCODE FLMAPI FlmDbClose (HFDB *phDb)
 Close a database.
FLMEXP RCODE FLMAPI FlmDbConfig (HFDB hDb, eDbConfigType eConfigType, void *pvValue1, void *pvValue2)
 Configure an open database.
FLMEXP RCODE FLMAPI FlmDbGetConfig (HFDB hDb, eDbGetConfigType eGetDbConfigType, void *pvValue1, void *pvValue2=NULL, void *pvValue3=NULL)
 Get configuration information on an open database.
FLMEXP void FLMAPI FlmSetIndexingCallback (HFDB hDb, IX_CALLBACK fnIxCallback, void *pvAppData)
 Set indexing callback function.
FLMEXP void FLMAPI FlmGetIndexingCallback (HFDB hDb, IX_CALLBACK *pfnIxCallback, void **ppvAppData)
 Get indexing callback function.
FLMEXP void FLMAPI FlmSetRecValidatorHook (HFDB hDb, REC_VALIDATOR_HOOK fnRecValidatorHook, void *pvAppData)
 Set record validator callback function.
FLMEXP void FLMAPI FlmGetRecValidatorHook (HFDB hDb, REC_VALIDATOR_HOOK *pfnRecValidatorHook, void **ppvAppData)
 Get the record validator callback function.
FLMEXP void FLMAPI FlmSetStatusHook (HFDB hDb, STATUS_HOOK fnStatusHook, void *pvAppData)
 Set the general purpose status callback function.
FLMEXP void FLMAPI FlmGetStatusHook (HFDB hDb, STATUS_HOOK *pfnStatusHook, void **ppvAppData)
 Get the general purpose status callback function.
FLMEXP RCODE FLMAPI FlmIndexStatus (HFDB hDb, FLMUINT uiIndexNum, FINDEX_STATUS *pIndexStatus)
 Retrieve status of an index.
FLMEXP RCODE FLMAPI FlmIndexGetNext (HFDB hDb, FLMUINT *puiIndexNum)
 Retrieve next index.
FLMEXP RCODE FLMAPI FlmIndexSuspend (HFDB hDb, FLMUINT uiIndexNum)
 Suspend an index.
FLMEXP RCODE FLMAPI FlmIndexResume (HFDB hDb, FLMUINT uiIndexNum)
 Resume an index.
FLMEXP FLMBOOL FLMAPI FlmErrorIsFileCorrupt (RCODE rc)
 Determine if a return code (RCODE) indicates a corruption.
FLMEXP const char *FLMAPI FlmErrorString (RCODE rc)
 Convert a return code (RCODE) into a string.
FLMEXP RCODE FLMAPI FlmGetDiagInfo (HFDB hDb, eDiagInfoType eDiagCode, void *pvDiagInfo)
 Get diagnostic information.
FLMEXP RCODE FLMAPI FlmDbTransBegin (HFDB hDb, FLMUINT uiTransType, FLMUINT uiMaxLockWait, FLMBYTE *pucHeader=NULL)
 Begin a transaction on the database.
FLMEXP RCODE FLMAPI FlmDbTransCommit (HFDB hDb, FLMBOOL *pbEmpty=NULL)
 Commit current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbTransAbort (HFDB hDb)
 Abort current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbGetTransType (HFDB hDb, FLMUINT *puiTransType)
 Get type of current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbGetTransId (HFDB hDb, FLMUINT *puiTransID)
 Get current transaction ID.
FLMEXP RCODE FLMAPI FlmDbGetCommitCnt (HFDB hDb, FLMUINT *puiCommitCount)
 Get number of committed transactions for a database.
FLMEXP RCODE FLMAPI FlmDbLock (HFDB hDb, eLockType lockType, FLMINT iPriority, FLMUINT uiTimeout)
 Lock a database.
FLMEXP RCODE FLMAPI FlmDbUnlock (HFDB hDb)
 Unlock a database.
FLMEXP RCODE FLMAPI FlmDbGetLockType (HFDB hDb, eLockType *pLockType, FLMBOOL *pbImplicit)
 Get the type of lock currently in effect on a database (if any).
FLMEXP RCODE FLMAPI FlmDbCheckpoint (HFDB hDb, FLMUINT uiTimeout)
 Perform a checkpoint on the database.
FLMEXP RCODE FLMAPI FlmRecordAdd (HFDB hDb, FLMUINT uiContainerNum, FLMUINT *puiDrn, FlmRecord *pRecord, FLMUINT uiAutoTrans)
 Add a record to the database.
FLMEXP RCODE FLMAPI FlmRecordModify (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FlmRecord *pRecord, FLMUINT uiAutoTrans)
 Modify a record in the database.
FLMEXP RCODE FLMAPI FlmRecordDelete (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FLMUINT uiAutoTrans)
 Delete a record from the database.
FLMEXP RCODE FLMAPI FlmReserveNextDrn (HFDB hDb, FLMUINT uiContainerNum, FLMUINT *puiDrn)
 Reserve the next available DRN in a database container.
FLMEXP RCODE FLMAPI FlmFindUnusedDictDrn (HFDB hDb, FLMUINT uiStartDrn, FLMUINT uiEndDrn, FLMUINT *puiDrn)
 Find an unused DRN in the dictionary.
FLMEXP RCODE FLMAPI FlmGetItemName (HFDB hDb, FLMUINT uiItemId, FLMUINT uiNameBufSize, char *pszNameBuf)
 Get the name of a dictionary item.
FLMEXP RCODE FLMAPI FlmRecordRetrieve (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FLMUINT uiFlag, FlmRecord **ppRecord, FLMUINT *puiDrn)
 Find and retrieve a record in a container.
FLMEXP RCODE FLMAPI FlmKeyRetrieve (HFDB hDb, FLMUINT uiIndex, FLMUINT uiContainerNum, FlmRecord *pSearchKey, FLMUINT uiSearchDrn, FLMUINT uiFlags, FlmRecord **ppFoundKey, FLMUINT *puiFoundDrn)
 Find and retrieve a key in an index.
FLMEXP RCODE FLMAPI FlmDbBackupBegin (HFDB hDb, FBackupType eBackupType, FLMBOOL bHotBackup, HFBACKUP *phBackup)
 Begin a database backup.
FLMEXP RCODE FLMAPI FlmBackupGetConfig (HFBACKUP hBackup, eBackupGetConfigType eConfigType, void *pvValue1, void *pvValue2=NULL)
 Get backup configuration on a backup that was started by FlmDbBackupBegin.
FLMEXP RCODE FLMAPI FlmDbBackup (HFBACKUP hBackup, const char *pszBackupPath, const char *pszPassword, BACKER_WRITE_HOOK fnWrite, STATUS_HOOK fnStatus, void *pvAppData, FLMUINT *puiIncSeqNum)
 Perform a backup that was started by FlmDbBackupBegin.
FLMEXP RCODE FLMAPI FlmDbBackupEnd (HFBACKUP *phBackup)
 End a backup that was started by FlmDbBackupBegin().
FLMEXP RCODE FLMAPI FlmDbRestore (const char *pszDbPath, const char *pszDataDir, const char *pszBackupPath, const char *pszRflDir, const char *pszPassword, F_Restore *pRestoreObj)
 Restore a database from a backup.
FLMEXP RCODE FLMAPI FlmAllocBlob (FlmBlob **ppBlob)
 Allocate a BLOB object that can then be used to create a new BLOB to store in a FlmRecord object.
FLMEXP RCODE FLMAPI FlmUINT2Storage (FLMUINT uiNum, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMUINT value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmUINT64ToStorage (FLMUINT64 ui64Num, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMUINT64 value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmINT2Storage (FLMINT iNum, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMINT value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmINT64ToStorage (FLMINT64 i64Num, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMINT64 value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmStorage2UINT (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiNum)
 Convert a value from FLAIM's internal format to a FLMUINT.
FLMEXP RCODE FLMAPI FlmStorage2UINT32 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT32 *pui32Num)
 Convert a value from FLAIM's internal format to a FLMUINT32.
FLMEXP RCODE FLMAPI FlmStorage2UINT64 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT64 *pui64Num)
 Convert a value from FLAIM's internal format to a FLMUINT64.
FLMEXP RCODE FLMAPI FlmStorage2INT (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT *puiNum)
 Convert a value from FLAIM's internal format to a FLMINT.
FLMEXP RCODE FLMAPI FlmStorage2INT32 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT32 *pui32Num)
 Convert a value from FLAIM's internal format to a FLMINT32.
FLMEXP RCODE FLMAPI FlmStorage2INT64 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT64 *pui64Num)
 Convert a value from FLAIM's internal format to a FLMINT64.
FLMEXP RCODE FLMAPI FlmUnicode2Storage (const FLMUNICODE *puzStr, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a unicode string to FLAIM's internal storage format.
FLMEXP FLMUINT FLMAPI FlmGetUnicodeStorageLength (const FLMUNICODE *puzStr)
 Determine the number of bytes needed to store a unicode string in FLAIM's internal storage format.
FLMEXP RCODE FLMAPI FlmStorage2Unicode (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrBufLen, FLMUNICODE *puzStrBuf)
 Convert a value from FLAIM's internal format to a unicode string.
FINLINE RCODE FlmGetUnicodeLength (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiUniLength)
 Get the number of bytes needed to convert a value from FLAIM's internal format to a unicode string.
FLMEXP RCODE FLMAPI FlmNative2Storage (const char *pszStr, FLMUINT uiStrLen, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a native string to FLAIM's internal storage format.
FLMEXP RCODE FLMAPI FlmStorage2Native (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrBufLen, char *pszStrBuf)
 Convert a value from FLAIM's internal format to a native string.
FLMEXP FLMUINT FLMAPI FlmGetNativeStorageLength (const char *pszStr)
 Determine the number of bytes needed to store a native string in FLAIM's internal storage format.
FINLINE RCODE FlmGetNativeLength (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrLength)
 Get the number of bytes needed to convert a value from FLAIM's internal format to a native string.
FINLINE RCODE FlmCursorFirst (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the first record in the query result set.
FINLINE RCODE FlmCursorLast (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the last record in the query result set.
FINLINE RCODE FlmCursorNext (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the next record in the query result set.
FINLINE RCODE FlmCursorPrev (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the previous record in the query result set.
FINLINE RCODE FlmCursorFirstDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the first record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorLastDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the last record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorNextDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the next record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorPrevDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the previous record in the query result set and retrieves the record's DRN.
FLMEXP RCODE FLMAPI FlmCursorCurrent (HFCURSOR hCursor, FlmRecord **ppRecord)
 Retrieve current record from query result set.
FLMEXP RCODE FLMAPI FlmCursorCurrentDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Retrieve the DRN of the current recrord in query result set.
FLMEXP RCODE FLMAPI FlmCursorMoveRelative (HFCURSOR hCursor, FLMINT *piPosition, FlmRecord **ppRecord)
 Position relative to the current record (forward or backward) in the query result set and retrieve the record positioned to.
FLMEXP RCODE FLMAPI FlmCursorRecCount (HFCURSOR hCursor, FLMUINT *puiCount)
 Get record count for a query result set.
FLMEXP RCODE FLMAPI FlmCursorCompareDRNs (HFCURSOR hCursor, FLMUINT uiDRN1, FLMUINT uiDRN2, FLMUINT uiTimeLimit, FLMINT *piCmpResult, FLMBOOL *pbTimedOut, FLMUINT *puiKeyCount)
 Determine the relative position of two records in a query's result set.
FLMEXP RCODE FLMAPI FlmCursorTestRec (HFCURSOR hCursor, FlmRecord *pRecord, FLMBOOL *pbIsMatch)
 Test a record to see if it passes the query criteria.
FLMEXP RCODE FLMAPI FlmCursorTestDRN (HFCURSOR hCursor, FLMUINT uiDRN, FLMBOOL *pbIsMatch)
 Retrieve and test a record (using a DRN) to see if it passes the query criteria.
FLMEXP const char *FLMAPI FlmVerifyErrToStr (eCorruptionType eCorruption)
 Return an error string for a corruption code.
FLMEXP RCODE FLMAPI FlmDbCheck (HFDB hDb, const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, FLMUINT uiCheckFlags, F_Pool *pPool, DB_CHECK_PROGRESS *pDbStats, STATUS_HOOK fnStatusHook, void *pvAppArg)
 Check a database for corruptions.
FLMEXP RCODE FLMAPI FlmDbRebuild (const char *pszSourceDbPath, const char *pszSourceDataDir, const char *pszDestDbPath, const char *pszDestDataDir, const char *pszDestRflDir, const char *pszDictPath, CREATE_OPTS *pCreateOpts, FLMUINT *puiTotalRecords, FLMUINT *puiRecsRecovered, STATUS_HOOK fnStatusHook, void *pvAppData)
 Rebuild a database.
FLMEXP RCODE FLMAPI FlmDbReduceSize (HFDB hDb, FLMUINT uiCount, FLMUINT *puiCount)
 Reduce the database size - returning unused blocks back to the file system.
FLMEXP RCODE FLMAPI FlmDbSweep (HFDB hDb, FLMUINT uiSweepMode, FLMUINT uiCallbackFlags, STATUS_HOOK fnStatusHook, void *pvAppData)
 Traverse records in the database looking for unused fields.
FLMEXP RCODE FLMAPI FlmDbUpgrade (HFDB hDb, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Upgrade a database.
FLMEXP RCODE FLMAPI FlmMaintenanceStatus (HFDB hDb, FMAINT_STATUS *pMaintStatus)
 Get the current status of the background maintenance thread for a database.
FLMEXP RCODE FLMAPI FlmDbCopy (const char *pszSrcDbName, const char *pszSrcDataDir, const char *pszSrcRflDir, const char *pszDestDbName, const char *pszDestDataDir, const char *pszDestRflDir, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Copy a database.
FLMEXP RCODE FLMAPI FlmDbRename (const char *pszDbName, const char *pszDataDir, const char *pszRflDir, const char *pszNewDbName, FLMBOOL bOverwriteDestOk, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Rename a database.
FLMEXP RCODE FLMAPI FlmDbRemove (const char *pszDbName, const char *pszDataDir, const char *pszRflDir, FLMBOOL bRemoveRflFiles)
 Delete a database.
FLMEXP RCODE FLMAPI FlmEnableEncryption (HFDB hDb, FLMBYTE **ppucWrappingKey, FLMUINT32 *pui32KeyLen)
 Enable encryption for a database.
FLMEXP RCODE FLMAPI FlmDbWrapKey (HFDB hDb, const char *pszPassword)
 Wrap a database's encryption key in a password.

Detailed Description


Typedef Documentation

typedef RCODE(* BACKER_WRITE_HOOK)(void *pvBuffer,FLMUINT uiBytesToWrite,void *pvAppData)
 

Typedef for callback function that is called from FlmDbBackup() to write out backed up data.

It is this function's responsibility to write the data to an appropriate backup medium - tape, disk, etc.

typedef RCODE( * CURSOR_GET_FIELD_CB)(void *pvAppData,FlmRecord *pRecord,HFDB hDb,FLMUINT *puiFldPath,FLMUINT uiAction,#define FLM_FLD_FIRST#define FLM_FLD_NEXT#define FLM_FLD_CLEANUP#define FLM_FLD_VALIDATE#define FLM_FLD_RESET FlmRecord **ppFieldRecRV,void **ppFieldRV,FLMUINT *puiResult)
 

Typedef for query "get field" function that can be inserted into query criteria wherever a field or field path could be inserted.

This type of function is passed as a parameter to the FlmCursorAddFieldCB() function.

typedef RCODE(* IX_CALLBACK)(HFDB hDb,FLMUINT uiIndexNum,FLMUINT uiContainerNum,FLMUINT uiDrn,FlmRecord *pInputRecord,FlmRecord **ppModifiedRecord,void *pvAppData)
 

Indexing callback function.

This function is implemented by an application. It allows an application to modify a record after it has been indexed. This only happens when an index is first created or when its definition is modified.

typedef FLMBOOL(* REC_VALIDATOR_HOOK)(eFlmFuncs eFlmFuncId,HFDB hDb,FLMUINT uiContainerId,FlmRecord *pRecord,FlmRecord *pOldRecord,void *pvAppData,RCODE *pRCode)
 

Record validator function.

A record validator function has several uses. It is used in a query to allow an application to apply tests to records that are not easily expressed with FLAIM's query criteria syntax. The REC_VALIDATOR_HOOK callback returns an RCODE. In the case of an update operation, the RCODE returned by the validator function is ignored. However, in the case of a read operation, the return code is evaluated. If it is FERR_OK, the current record is returned to the application. Otherwise, the record is not returned, but the read operation is allowed to continue.

typedef RCODE(* STATUS_HOOK)(eStatusType eStatus,void *pvParm1,void *pvParm2,void *pvAppData)
 

General purpose status callback function.

This function is implemented by an application. It allows an application to receive all kinds of status information from FLAIM during various FLAIM operations.


Enumeration Type Documentation

enum eBackupGetConfigType
 

Backup configuration information that can be requested by FlmBackupGetConfig().

Enumerator:
FBAK_GET_BACKUP_TRANS_ID  Get backup transaction ID. pvValue1 is a FLMUINT * that returns the transaction ID.
FBAK_GET_LAST_BACKUP_TRANS_ID  Get the last backup transactioN ID. pvValue1 is a FLMUINT * that returns the last transaction ID.

enum eCorruptionLocale
 

Locations of corruptions in the database.

Enumerator:
LOCALE_LFH_LIST  Corruption was found in the list of logical file blocks.
LOCALE_AVAIL_LIST  Corruption was found in the list of available blocks.
LOCALE_B_TREE  Corruption was found in an index or container b-tree block.
LOCALE_IXD_TBL  Corruption was found in index table.
LOCALE_INDEX  Corruption was logical index corruption.

enum eCorruptionType
 

Types of corruption that can be reported by FlmDbCheck().

Enumerator:
FLM_NO_CORRUPTION  0
FLM_BAD_CHAR  1
FLM_BAD_ASIAN_CHAR  2
FLM_BAD_CHAR_SET  3
FLM_BAD_TEXT_FIELD  4
FLM_BAD_NUMBER_FIELD  5
FLM_BAD_CONTEXT_FIELD  6
FLM_BAD_FIELD_TYPE  7
FLM_BAD_IX_DEF  8
FLM_MISSING_REQ_KEY_FIELD  9
FLM_BAD_TEXT_KEY_COLL_CHAR  10
FLM_BAD_TEXT_KEY_CASE_MARKER  11
FLM_BAD_NUMBER_KEY  12
FLM_BAD_CONTEXT_KEY  13
FLM_BAD_BINARY_KEY  14
FLM_BAD_DRN_KEY  15
FLM_BAD_KEY_FIELD_TYPE  16
FLM_BAD_KEY_COMPOUND_MARKER  17
FLM_BAD_KEY_POST_MARKER  18
FLM_BAD_KEY_POST_BYTE_COUNT  19
FLM_BAD_KEY_LEN  20
FLM_BAD_LFH_LIST_PTR  21
FLM_BAD_LFH_LIST_END  22
FLM_BAD_PCODE_LIST_END  23
FLM_BAD_BLK_END  24
FLM_KEY_COUNT_MISMATCH  25
FLM_REF_COUNT_MISMATCH  26
FLM_BAD_CONTAINER_IN_KEY  27
FLM_BAD_BLK_HDR_ADDR  28
FLM_BAD_BLK_HDR_LEVEL  29
FLM_BAD_BLK_HDR_PREV  30
FLM_BAD_BLK_HDR_NEXT  31
FLM_BAD_BLK_HDR_TYPE  32
FLM_BAD_BLK_HDR_ROOT_BIT  33
FLM_BAD_BLK_HDR_BLK_END  34
FLM_BAD_BLK_HDR_LF_NUM  35
FLM_BAD_AVAIL_LIST_END  36
FLM_BAD_PREV_BLK_NEXT  37
FLM_BAD_FIRST_ELM_FLAG  38
FLM_BAD_LAST_ELM_FLAG  39
FLM_BAD_LEM  40
FLM_BAD_ELM_LEN  41
FLM_BAD_ELM_KEY_SIZE  42
FLM_BAD_ELM_PKC_LEN  43
FLM_BAD_ELM_KEY_ORDER  44
FLM_BAD_ELM_KEY_COMPRESS  45
FLM_BAD_CONT_ELM_KEY  46
FLM_NON_UNIQUE_FIRST_ELM_KEY  47
FLM_BAD_ELM_FLD_OVERHEAD  48
FLM_BAD_ELM_FLD_LEVEL_JUMP  49
FLM_BAD_ELM_FLD_NUM  50
FLM_BAD_ELM_FLD_LEN  51
FLM_BAD_ELM_FLD_TYPE  52
FLM_BAD_ELM_END  53
FLM_BAD_PARENT_KEY  54
FLM_BAD_ELM_DOMAIN_SEN  55
FLM_BAD_ELM_BASE_SEN  56
FLM_BAD_ELM_IX_REF  57
FLM_BAD_ELM_ONE_RUN_SEN  58
FLM_BAD_ELM_DELTA_SEN  59
FLM_BAD_ELM_DOMAIN  60
FLM_BAD_LAST_BLK_NEXT  61
FLM_BAD_FIELD_PTR  62
FLM_REBUILD_REC_EXISTS  63
FLM_REBUILD_KEY_NOT_UNIQUE  64
FLM_NON_UNIQUE_ELM_KEY_REF  65
FLM_OLD_VIEW  66
FLM_COULD_NOT_SYNC_BLK  67
FLM_IX_REF_REC_NOT_FOUND  68
FLM_IX_KEY_NOT_FOUND_IN_REC  69
FLM_DRN_NOT_IN_KEY_REFSET  70
FLM_BAD_BLK_CHECKSUM  71
FLM_BAD_LAST_DRN  72
FLM_BAD_FILE_SIZE  73
FLM_BAD_AVAIL_BLOCK_COUNT  74
FLM_BAD_DATE_FIELD  75
FLM_BAD_TIME_FIELD  76
FLM_BAD_TMSTAMP_FIELD  77
FLM_BAD_DATE_KEY  78
FLM_BAD_TIME_KEY  79
FLM_BAD_TMSTAMP_KEY  80
FLM_BAD_BLOB_FIELD  81
FLM_BAD_PCODE_IXD_TBL  82
FLM_DICT_REC_ADD_ERR  83
FLM_BAD_FIELD_FLAG  84
FLM_BAD_FOP  85

enum eCursorConfigType
 

Configuration options for FlmCursorConfig().

Enumerator:
FCURSOR_CLEAR_QUERY  Clear query criteria.
FCURSOR_GEN_POS_KEYS  Generate positioning keys.
FCURSOR_SET_HDB  Set the database handle for the query. pvValue1 is an HFDB - the database handle.
FCURSOR_SET_FLM_IX  Set the index for the query. pvValue1 is a FLMUINT - the index number. A value of zero may be specified to indicate that no index is to be used. A value of FLM_SELECT_INDEX specifies that FLAIM is to select the index. This is the default.
FCURSOR_SET_OP_TIME_LIMIT  Set a time limit for the query. pvValue1 is a FLMUINT - timeout in seconds.
FCURSOR_SET_PERCENT_POS  Position to a percent position in the query result set. pvValue1 is a FLMUINT between 1 and 100.
FCURSOR_SET_POS  Position to the same position another query object is positioned to. pvValue1 is CURSOR * - a pointer to the query object whose position we are to position to.
FCURSOR_SET_POS_FROM_DRN  Position to a specific record in the query result set. pvValue1 is a FLMUINT - the DRN of the record to be positioned to. If the record is not in the result set, an error will be returned.
FCURSOR_SET_REC_TYPE  Set the type of record that this query should return. This basically adds a special criteria for the query that specifies that only records whose root field number matches a certain value should be be returned. pvValue1 is a FLMUINT - the root field number (or record type) to be matched.
FCURSOR_RETURN_KEYS_OK  Sets a flag that specifies whether it is ok for this query to return keys from the index instead of actual records from the container. pvValue1 is a FLMBOOL - TRUE means that index keys may be returned. FALSE means that only real records from the container may be returned. This flag is only used if it is possible to test the query criteria using only information retrieved from an index - provided, of course, that an index is used to perform the query.
FCURSOR_DISCONNECT  Disconnect the cursor from any association with the current database handle, if any. If any internal read transaction is going, it will be aborted.
FCURSOR_ALLOW_DUPS  Allow duplicate records in query result set.
FCURSOR_ELIMINATE_DUPS  Disallow duplicate records in query result set.
FCURSOR_SET_REC_VALIDATOR  Set a record validator function that is to be called for each record that passes the query query criteria. pvValue1 is a REC_VALIDATOR_HOOK - the record validator function. The record validator function may apply additional criteria to determine if the record should really be allowed to be returned or if it should be failed.
FCURSOR_SET_STATUS_HOOK  Set a status function that is called to report progress in evaluating the query. pvValue1 is a STATUS_HOOK - the status function. pvValue2 is application data that will be passed to the status function whenever it is called.
FCURSOR_SAVE_POSITION  Save the current position of the query within its query result set. This option is provided so that an application can temporarily position to some other place in its result set, but then returned to a saved position easily.
FCURSOR_RESTORE_POSITION  Restore the current position of the query within its query result set.
FCURSOR_SET_ABS_POS  Set the absolute position in the query query result set. pvValue1 is a FLMUINT *. On input (FLMUINT *)pvValue1 is the absolute position the query is to be set to. On output, it returns the position actually set to. pvValue2 is a FLMBOOL. If record at the position specified does not pass the query criteria, this flag specifies whether to position to the next or previous record in the result set that does match the criteria.

enum eCursorGetConfigType
 

Options for FlmCursorGetConfig().

Enumerator:
FCURSOR_GET_OPT_INFO_LIST  Get the optimization information for the query. pvValue1 is a pointer to an array of OPT_INFO structures. If a NULL is passed, no optimization information is returned, but a count of OPT_INFO structures needed will be returned in the pvValue2 parameter. pvValue2 is a FLMUINT *. It returns the number of elements needed in the OPT_INFO array. One OPT_INFO structure is returned for each sub-query that is optimized separately from other sub-queries. Typically, an application should call FlmCursorGetConfig() twice for this option - the first time with a NULL in pvValue1 to get the size of the OPT_INFO array needed. Then, allocate memory for the array and call it again.
FCURSOR_GET_FLM_IX  Get the index being used for the query. pvValue1 is a FLMUINT *. The index number, if any, is returned here. A zero means that no index is being used. If multiple indexes are being used, pvValue1 will only return the first index. pvValue2 is a FLMUINT *. It returns flags which indicates if more than one index is being used. It will be one of the following:
  • HAVE_NO_INDEX - Query was not using an index (pvValue1 should return 0 in this case)
  • HAVE_ONE_INDEX - Query is using exactly one index (pvValue1 will return index number)
  • HAVE_ONE_INDEX_MULT_PARTS - Query is using exactly one index, but there are multiple parts of the index that are being searched
  • HAVE_MULTIPLE_INDEXES - Query is using multiple indexes (pvValue1 will return only the first index).
FCURSOR_GET_OPT_INFO  Get optimization information for the query. pvValue1 is a pointer to an OPT_INFO structure where the optimization information will be returned. This option only returns the first OPT_INFO structure. If there are multiple sub-queries, use eCursorGetConfigType::FCURSOR_GET_OPT_INFO_LIST.
FCURSOR_GET_PERCENT_POS  Get the current percent position of the query in the query result set. pvValue1 is a FLMUINT *. It returns the current percent position. NOTE: This option should only be called if the query is percent positionable - see eCursorGetConfigType::FCURSOR_GET_POSITIONABLE.
FCURSOR_GET_REC_TYPE  Get the record type that has been set for the query, if any. If a record type was set, it would have been set using the eCursorConfigType::FCURSOR_SET_REC_TYPE option of the FlmCursorConfig() function. pvValue1 is a FLMUINT *. It returns the record type.
FCURSOR_GET_FLAGS  Get the current mode flags for the query. These are the mode flags that would have been set using the FlmCursorSetMode() function. pvValue1 is a FLMUINT *. It returns the flags.
FCURSOR_GET_STATE  Get the current state of the query. pvValue1 is a FLMUINT *. It returns flags indicating what the current state of the query is. The flags are as follows:
  • FCURSOR_HAVE_CRITERIA - Indicates that some query criteria has been set. Query criteria may or may not be complete
  • FCURSOR_EXPECTING_OPERATOR - Indicates that the query criteria is expecting an operator to be submitted next
  • FCURSOR_QUERY_COMPLETE - Indicates that the query criteria is in a "complete" state - that is, it is syntatically complete and could be used to retrieve records
  • FCURSOR_QUERY_OPTIMIZED - Indicates that the query has already been optimized
  • FCURSOR_READ_PERFORMED - Indicates that the query is ready to have records retrieved.
FCURSOR_GET_POSITIONABLE  Get whether or not the query is "percentage" positionable. pvValue1 is a FLMBOOL *. It returns a TRUE/FALSE flag indicating whether the query can be percentage positioned.
FCURSOR_AT_BOF  Get whether or not the query is positioned at BOF. pvValue1 is a FLMBOOL *. It returns a TRUE/FALSE flag indicating whether the query is at BOF.
FCURSOR_AT_EOF  Get whether or not the query is positioned at EOF. pvValue1 is a FLMBOOL *. It returns a TRUE/FALSE flag indicating whether the query is at EOF.
FCURSOR_GET_ABS_POSITIONABLE  Get whether or not the query is "absolute" positionable. pvValue1 is a FLMBOOL *. It returns a TRUE/FALSE flag indicating whether the query is absolute positionable.
FCURSOR_GET_ABS_POS  Get the current absolute position of the query in the query result set. pvValue1 is a FLMUINT *. It returns the current absolute position. NOTE: This option should only be used if the query is absolute positionable - see eCursorGetConfigType::FCURSOR_GET_ABS_POSITIONABLE.
FCURSOR_GET_ABS_COUNT  Get the absolute count of the query result set. pvValue1 is a FLMBOOL *. It returns the absolute count. NOTE: This option should only be used if the query is absolute positionable - see eCursorGetConfigType::FCURSOR_GET_ABS_POSITIONABLE.

enum eDbConfigType
 

Configuration options for FlmDbConfig().

Enumerator:
FDB_SET_APP_VERSION  Set the application version numbers into the database header. pvValue1 is a FLMUINT which holds the major version number. pvValue2 is a FLMUINT which holds the minor version number.
FDB_RFL_KEEP_FILES  Sets flag which specifies whether or not to keep roll-forward log files. pvValue1 is a FLMBOOL which is TRUE or FALSE.
FDB_RFL_DIR  Set the roll-forward log directory. pvValue1 is a char * which contains the name of the RFL directory.
FDB_RFL_FILE_LIMITS  Set the minimum and maximum sizes for RFL files. pvValue1 is a FLMUINT which contains the minimum RFL file size (in bytes). pvValue2 is a FLMUINT which contains the maximum RFL file size (in bytes).
FDB_RFL_ROLL_TO_NEXT_FILE  Force the database to create and start using the next RFL file in the sequence.
FDB_KEEP_ABORTED_TRANS_IN_RFL  Set flag which specifies whether or not to keep aborted transactions in roll-forward log files. pvValue1 is a FLMBOOL which is TRUE or FALSE.
FDB_AUTO_TURN_OFF_KEEP_RFL  Set flag which specifies whether or not to automatically turn off keeping of RFL files when the RFL volume gets full. pvValue1 is a FLMBOOL which is TRUE or FALSE.
FDB_FILE_EXTEND_SIZE  Set the extend size for data files in the database. pvValue1 is a FLMUINT which specifies the extend size (in bytes). Whenever data files need to be extended, they will be extended by this amount.
FDB_SET_APP_DATA  Allows an application to have the database object remember some data. pvValue1 contains a pointer to the data to be remembered. An application may retrieve this pointer at any time by calling FlmDbGetConfig() with the eDbGetConfigType::FDB_GET_APP_DATA option.
FDB_SET_COMMIT_CALLBACK  Set a callback function that is to be called whenever this database handle commits an update transaction. pvValue1 is a pointer to the callback function - a COMMIT_FUNC. pvValue2 is a pointer to application data that will be passed into the function whenever it is called.
FDB_ENABLE_FIELD_ID_TABLE  Enable the creating of a field ID table for level-one fields in cached records for a specific container. pvValue1 is a FLMUINT that holds the container number. pvValue2 is a FLMBOOL indicating whether the field id table is to be enabled or disabled.
FDB_SET_RFL_SIZE_THRESHOLD  Sets the RFL size threshold (in K bytes).

If registered to receive RFL size events, an event will be reported when the on-disk size of the RFL exceeds this value. pvValue1 is a FLMUINT which specifies the threshold value in K bytes.

FDB_SET_RFL_SIZE_EVENT_INTERVALS  Sets the criteria for determining how often to report RFL size events once the RFL exceeds the size threshold. pvValue1 is a FLMUINT which specifies the minimum number of seconds between events. pvValue2 is a FLMUINT which specifies the minimum increase in K bytes of the RFL between events.
FDB_RFL_FOOTPRINT_SIZE  Set the footprint size of the RFL when files are not being kept. pvValue1 is a FLMUINT which specifies the footprint size (in bytes).
FDB_RBL_FOOTPRINT_SIZE  Set the footprint size of the roll-back log. pvValue1 is a FLMUINT which specifies the footprint size (in bytes).

enum eDbGetConfigType
 

Options for FlmDbGetConfig().

Enumerator:
FDB_GET_VERSION  Get the FLAIM database version. pvValue1 is a FLMUINT * that returns database version.
FDB_GET_BLKSIZ  Get the database block size. pvValue1 is a FLMUINT * that returns database block size.
FDB_GET_DEFAULT_LANG  Get the default language for the database. pvValue1 is a FLMUINT * that returns the language.
FDB_GET_PATH  Get the database file name. pvValue1 is a char * that points to a buffer where the file name is to be returned. Buffer should be large enough to hold the largest possible file name.
FDB_GET_TRANS_ID  Get the current transaction ID for the database. pvValue1 is a FLMUINT * that returns the transaction ID. NOTE: If no transaction is active, this option will return the last committed update transaction ID if this database handle has the database locked. Otherwise it will return zero.
FDB_GET_CHECKPOINT_INFO  Get the current state of the checkpoint thread. pvValue1 is a pointer to a CHECKPOINT_INFO structure where the checkpoint information is to be returned.
FDB_GET_LOCK_HOLDER  Get the current lock holder for the database. pvValue1 is a pointer to a F_LOCK_USER structure where the lock information is to be returned.
FDB_GET_LOCK_WAITERS  Get the entire list of threads that are either holding the lock on the database or are waiting to obtain the lock on the database. pvValue1 is a F_LOCK_USER **. This option will allocate an array of F_LOCK_USER structures and return a pointer to them. The zeroeth element of the array contains the lock holder. All other elements contain lock waiters. The last element in the array will be zeroed out. NOTE: The memory allocated by this function should be freed by calling FlmFreeMem().
FDB_GET_LOCK_WAITERS_EX  Get the lock holders and waiters using a IF_LockInfoClient object. pvValue1 is a pointer to the IF_LockInfoClient object that is to be used.
FDB_GET_RFL_DIR  Get the directory where RFL files are stored. pvValue1 is a char * that points to a buffer where the file name is to be returned. Buffer should be large enough to hold the largest possible directory name.
FDB_GET_RFL_FILE_NUM  Get the current RFL file number. pvValue1 is a FLMUINT * that returns the file number.
FDB_GET_RFL_HIGHEST_NU  Get the highest RFL file number that is not currently in use. pvValue1 is a FLMUINT * that returns the file number.
FDB_GET_RFL_FILE_SIZE_LIMITS  Get the minimum and maximum RFL file sizes. pvValue1 is a FLMUINT * that returns the minimum file size (in bytes). pvValue2 is a FLMUINT * that returns the maximum file file size (in bytes). NOTE: These sizes may be set by calling FlmDbConfig() using the eDbConfigType::FDB_RFL_FILE_LIMITS option.
FDB_GET_RFL_KEEP_FLAG  Get the flag which tells whether the database is configured to keep RFL files. pvValue1 is a FLMBOOL * which returns a TRUE or FALSE.
FDB_GET_LAST_BACKUP_TRANS_ID  Get the last backup transaction ID. pvValue1 is a FLMUINT * which returns the transaction ID.
FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP  Get the number of blocks in the database that have changes since the last backup. pvValue1 is a FLMUINT * that returns the number of blocks.
FDB_GET_SERIAL_NUMBER  Get the database serial number. pvValue1 is a FLMBYTE * that points to a buffer where the serial number will be returned. The buffer should be at least F_SERIAL_NUM_SIZE bytes.
FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG  Get the flag which tells whether the database is configured to automatically turn off the keeping of RFL files when the RFL volume gets full. pvValue1 is a FLMBOOL * which returns a TRUE or FALSE. NOTE: This flag may be set by calling FlmDbConfig() using the eDbConfigType::FDB_AUTO_TURN_OFF_KEEP_RFL option.
FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG  Get the flag which tells whether the database is configured to keep aborted transactions in the roll-forward log. pvValue1 is a FLMBOOL * which returns a TRUE or FALSE. NOTE: This flag may be set by calling FlmDbConfig() using the eDbConfigType::FDB_KEEP_ABORTED_TRANS_IN_RFL option.
FDB_GET_SIZES  Get database file sizes. pvValue1 is a FLMUINT64 * which returns the total size of all data files. pvValue2 is a FLMUINT64 * which returns the total size of all rollback files. pvValue3 is a FLMUINT64 * which returns the total size of all roll-forward log files.
FDB_GET_FILE_EXTEND_SIZE  Get the database file extend size. pvValue1 is a FLMUINT * which returns the file extend size. NOTE: This size may be set by calling FlmDbConfig() using the eDbConfigType::FDB_FILE_EXTEND_SIZE option.
FDB_GET_APP_DATA  Get the application data pointer that was set using the eDbConfigType::FDB_SET_APP_DATA option in FlmDbConfig(). pvValue1 is a void ** which returns the pointer.
FDB_GET_NEXT_INC_BACKUP_SEQ_NUM  Get the next incremental backup sequence number for the database. pvValue1 is a FLMUINT * that returns the sequence number.
FDB_GET_DICT_SEQ_NUM  Get the dictionary sequence number. pvValue1 is a FLMUINT * that returns the sequence number.
FDB_GET_FFILE_ID  Get the database's unique ID number. pvValue1 is a FLMUINT * that returns the number.
FDB_GET_MUST_CLOSE_RC  Get the RCODE that caused the "must close" flag to be set on the database. pvValue1 is an RCODE * that returns the RCODE.
FDB_GET_RFL_FOOTPRINT_SIZE  Get the RFL footprint size. pvValue1 is a FLMUINT * which returns the RFL footprint size. NOTE: This size may be set by calling FlmDbConfig() using the eDbConfigType::FDB_RFL_FOOTPRINT_SIZE option.
FDB_GET_RBL_FOOTPRINT_SIZE  Get the roll-back log footprint size. pvValue1 is a FLMUINT * which returns the RBL footprint size. NOTE: This size may be set by calling FlmDbConfig() using the eDbConfigType::FDB_RBL_FOOTPRINT_SIZE option.

enum eDiagInfoType
 

Types of diagnostic information available from FlmGetDiagInfo().

Enumerator:
FLM_GET_DIAG_INDEX_NUM  Get the index number. pvDiagInfo is a FLMUINT * that returns index number. This diagnostic is available when the RCODE::FERR_NOT_UNIQUE error code is returned.
FLM_GET_DIAG_DRN  Get the DRN. pvDiagInfo is a FLMUINT * that returns DRN. This diagnostic is available after attempting to add or modify a dictionary definition record to the dictionary. It is available for the following error codes:
FLM_GET_DIAG_FIELD_NUM  Get the field number. pvDiagInfo is a FLMUINT * that returns field number. This diagnostic is available after attempting to add or modify a record in the database. It is available for the following error codes:
FLM_GET_DIAG_FIELD_TYPE  Get the field type. pvDiagInfo is a FLMUINT * that returns field type. This diagnostics is available when the RCODE::FERR_BAD_FIELD_NUM error code is returned from a record add (FlmRecordAdd()) or record modify (FlmRecordModify()) operation.
FLM_GET_DIAG_ENC_ID  Get the encryption ID. pvDiagInfo is a FLMUINT * that returns encryption ID. This diagnostics is available when the RCODE::FERR_PURGED_ENCDEF_FOUND error code is returned.

enum eFlmConfigTypes
 

Database system configuration options that are passed into FlmConfig() and FlmGetConfig().

Enumerator:
FLM_CLOSE_UNUSED_FILES  FlmConfig(). Close all files that have not been used for the specified number of seconds.
Input: pvValue1 is (FLMUINT), unused seconds.
FLM_CLOSE_ALL_FILES  FlmConfig(). Close all available file handles as well as all used files as as they are available. Files opened after this call will not be immediately closed after use.
FLM_OPEN_THRESHOLD  FlmConfig(). Set maximum number of file handles.
Input: pvValue1 is (FLMUINT), maximum file handles.
FlmGetConfig(). Returns maximum number of files handles.
Output: pvValue is (FLMUINT *), returns maximum file handles.
FLM_OPEN_FILES  FlmGetConfig(). Returns number of open file handles.
Output: pvValue is (FLMUINT *), returns number of open file handles.
FLM_CACHE_LIMIT  FlmConfig(). Set maximum cache size in bytes.
Input: pvValue1 is (FLMUINT), maximum cache size in bytes.
Input: pvValue2 is (FLMBOOL), pre-allocate cache?
FLM_SCACHE_DEBUG  FlmConfig(). Enable/disable cache debugging.
Input: pvValue1 is (FLMBOOL), TRUE=enable, FALSE=disable.
FLM_START_STATS  FlmConfig(). Start gathering statistics.
FLM_STOP_STATS  FlmConfig(). Stop gathering statistics.
FLM_RESET_STATS  FlmConfig(). Reset statistics.
FLM_TMPDIR  FlmConfig(). Set temporary directory.
Input: pvValue1 is (char *), name of temporary directory.
FLM_MAX_CP_INTERVAL  FlmConfig(). Set maximum seconds between checkpoints.
Input: pvValue1 is (FLMUINT), maximum seconds between checkpoints.
FlmGetConfig(). Get maximum seconds between checkpoints.
Output: pvValue is (FLMUINT *), maximum seconds between checkpoints.
FLM_BLOB_EXT  FlmConfig(). Set BLOB override file extension.
Input: pvValue1 is (char *), file extension.
NULL or empty string disables override.
FlmGetConfig(). Get BLOB override fle extension.
Output: pvValue is (char *), at least a 4 byte buffer for extension.
FLM_MAX_TRANS_SECS  FlmConfig(). Set maximum transaction time limit. Used to determine whether a transaction should be killed.
Input: pvValue1 is (FLMUINT), maximum seconds.
FlmGetConfig(). Get maximum transaction time limit.
Output: pvValue is (FLMUINT *), maximum seconds.
FLM_MAX_TRANS_INACTIVE_SECS  FlmConfig(). Set maximum time a transaction can be inactive before it will be killed.
Input: pvValue1 is (FLMUINT), maximum seconds.
FlmGetConfig(). Get maximum time a transaction can be inactive before it will be killed.
Output: pvValue is (FLMUINT *), maximum seconds.
FLM_CACHE_ADJUST_INTERVAL  FlmConfig(). Set interval for dynamically adjusting cache limit.
Input: pvValue1 is (FLMUINT), interval in seconds.
FlmGetConfig(). Get interval for dynamically adjusting cache limit.
Output: pvValue is (FLMUINT *), interval in seconds.
FLM_CACHE_CLEANUP_INTERVAL  FlmConfig(). Set interval for dynamically cleaning out old cache blocks and records.
Input: pvValue1 is (FLMUINT), interval in seconds.
FlmGetConfig(). Get interval for dynamically cleaning out old cache blocks and records.
Output: pvValue is (FLMUINT *), interval in seconds.
FLM_UNUSED_CLEANUP_INTERVAL  FlmConfig(). Set interval for cleaning up unused structures.
Input: pvValue1 is (FLMUINT), interval in seconds.
FlmGetConfig(). Get interval for cleaning up unused structures.
Output: pvValue is (FLMUINT *), interval in seconds.
FLM_MAX_UNUSED_TIME  FlmConfig(). Set maximum time for an item to be unused.
Input: pvValue1 is (FLMUINT), maximum time in seconds.
FlmGetConfig(). Get maximum time for an item to be unused.
Output: pvValue is (FLMUINT *), maximum time in seconds.
FLM_BLOCK_CACHE_PERCENTAGE  FlmConfig(). Set percentage of cache to be allocated to block cache.
Input: pvValue1 is (FLMUINT), percent (0 to 100).
FlmGetConfig(). Get percentage of cache allocated to block cache.
Output: pvValue is (FLMUINT *), percent.
FLM_CACHE_CHECK  FlmConfig(). Enable/disable cache checking.
Input: pvValue1 is (FLMUINT), 0=disable,other=enable.
FlmGetConfig(). Get cache checking state.
Output: pvValue is (FLMBOOL *), FALSE=disabled,TRUE=enabled.
FLM_CLOSE_FILE  FlmConfig(). Force a database to close all of its files.
Input: pvValue1 is (char *), name of database.
FLM_LOGGER  FlmConfig(). Set the logger object.
Input: pvValue1 is (IF_LoggerClient *), pointer to logger object.
NULL means disable logging.
FLM_ASSIGN_HTTP_SYMS  FlmConfig(). Set function pointers for HTTP server.
Input: pvValue1 is (HTTPCONFIGPARAMS *), pointer to structure containing HTTP handling functions.
FLM_UNASSIGN_HTTP_SYMS  FlmConfig(). Unset function pointers for HTTP server functions to NULL. This effectively disables the HTTP server.
FLM_REGISTER_HTTP_URL  FlmConfig(). Set the base URL the HTTP server is going to handle.
Input: pvValue1 is (char *), base URL to start handling.
FLM_DEREGISTER_HTTP_URL  FlmConfig(). Unset the base URL the HTTP server was handling. Tells the HTTP server to no longer call our HTTP functions when a request for the specified URL comes in.
Input: pvValue1 is (char *), base URL to stop handling.
FLM_KILL_DB_HANDLES  FlmConfig(). Invalidate open database handles, forcing the database to (eventually) be closed.
Input: pvValue1 is (char *), name of database.
Input: pvValue2 is (char *), name of directory for database data files.
NOTE: Passing NULL for pvValue1 and pvValue2 will cause all active database handles to be forced to close.
FLM_QUERY_MAX  FlmConfig(). Set maximum number of queries to save for statistics.
Input: pvValue1 is (FLMUINT), maximum number of queries to save.
FlmGetConfig(). Get maximum number of queries to save for statistics.
Output: pvValue is (FLMUINT *), maximum number of queries to save.
FLM_MAX_DIRTY_CACHE  FlmConfig(). Set maximum dirty cache.
Input: pvValue1 is (FLMUINT), maximum dirty cache (bytes).
Input: pvValue2 is (FLMUINT), low dirty cache (bytes).
FlmGetConfig(). Get maximum dirty cache.
Output: pvValue is (FLMUINT *), maximum dirty cache (bytes).
FLM_DYNA_CACHE_SUPPORTED  FlmGetConfig(). Get whether dynamic cache limits are supported.
Output: pvValue is (FLMBOOL *), TRUE if dynamic cache limits are supported, FALSE if not.
FLM_QUERY_STRATIFY_LIMITS  FlmConfig(). Set maximum query stratify iterations and query stratify time limit.
Input: pvValue1 is (FLMUINT), maximum query stratify iterations.
Input: pvValue2 is (FLMUINT), query stratify time limit (seconds).
FlmGetConfig(). Get maximum query stratify iterations.
Output: pvValue is (FLMUINT *), maximum query stratify iterations.
FLM_DIRECT_IO_STATE  FlmConfig(). Enable or disable direct I/O.
Input: pvValue1 is (FLMBOOL), TRUE = enable, FALSE = disable.
FlmGetConfig(). Get direct I/O state.
Output: pvValue is (FLMBOOL *), TRUE = enabled, FALSE = disabled.

enum eFlmFuncs
 

FLAIM Functions.

Enumerator:
FLM_CURSOR_COMPARE_DRNS  FlmCursorCompareDRNs().
FLM_CURSOR_CONFIG  FlmCursorConfig().
FLM_CURSOR_FIRST  FlmCursorFirst().
FLM_CURSOR_FIRST_DRN  FlmCursorFirstDRN().
FLM_CURSOR_GET_CONFIG  FlmCursorGetConfig().
FLM_CURSOR_LAST  FlmCursorLast().
FLM_CURSOR_LAST_DRN  FlmCursorLastDRN().
FLM_CURSOR_MOVE_RELATIVE  FlmCursorMoveRelative().
FLM_CURSOR_NEXT  FlmCursorNext().
FLM_CURSOR_NEXT_DRN  FlmCursorNextDRN().
FLM_CURSOR_PREV  FlmCursorPrev().
FLM_CURSOR_PREV_DRN  FlmCursorPrevDRN().
FLM_CURSOR_REC_COUNT  FlmCursorRecCount().
FLM_DB_CHECKPOINT  FlmDbCheckpoint().
FLM_DB_UPGRADE  FlmDbUpgrade().
FLM_DB_CREATE  FlmDbCreate().
FLM_DB_GET_COMMIT_CNT  FlmDbGetCommitCnt().
FLM_DB_GET_LOCK_INFO  FlmDbGetLockInfo().
FLM_DB_GET_LOCK_TYPE  FlmDbGetLockType().
FLM_DB_GET_TRANS_ID  FlmDbGetTransId().
FLM_DB_GET_TRANS_TYPE  FlmDbGetTransType().
FLM_DB_LOCK  FlmDbLock().
FLM_DB_OPEN  FlmDbOpen().
FLM_DB_REDUCE_SIZE  FlmDbReduceSize().
FLM_DB_SWEEP  FlmDbSweep().
FLM_DB_TRANS_ABORT  FlmDbTransAbort().
FLM_DB_TRANS_BEGIN  FlmDbTransBegin().
FLM_DB_TRANS_COMMIT  FlmDbTransCommit().
FLM_DB_UNLOCK  FlmDbUnlock().
FLM_INDEX_GET_NEXT  FlmIndexGetNext().
FLM_INDEX_STATUS  FlmIndexStatus().
FLM_INDEX_RESUME  FlmIndexResume().
FLM_INDEX_SUSPEND  FlmIndexSuspend().
FLM_KEY_RETRIEVE  FlmKeyRetrieve().
FLM_RESERVE_NEXT_DRN  FlmReserveNextDrn().
FLM_RECORD_ADD  FlmRecordAdd().
FLM_RECORD_MODIFY  FlmRecordModify().
FLM_RECORD_DELETE  FlmRecordDelete().
FLM_RECORD_RETRIEVE  FlmRecordRetrieve().

enum eMaintAction
 

Types of actions the background maintenance thread may currently be doing.

Enumerator:
FLM_MAINT_IDLE  Thread is idle.
FLM_MAINT_LOOKING_FOR_WORK  Thread is looking for work to do.
FLM_MAINT_WAITING_FOR_LOCK  Thread is waiting to get the database lock.
FLM_MAINT_ENDING_TRANS  Thread is committing an update transaction.
FLM_MAINT_TERMINATED  Thread is not currently running.
FLM_MAINT_FREEING_BLOCKS  Thread is freeing blocks.

enum eRestoreActionType
 

Actions that an application may want to tell FlmDbRestore() to take during a restore operation.

These action codes are returned from the F_Restore::status() method.

Enumerator:
RESTORE_ACTION_CONTINUE  Continue restore.
RESTORE_ACTION_STOP  Abort the restore.
RESTORE_ACTION_SKIP  Skip the current operation. NOTE: FlmDbRestore does not currently do anything if this code is returned.
RESTORE_ACTION_RETRY  Retry the operation. This should only be returned when the F_Restore::status() method passes an eRestoreStatusType::RESTORE_ERROR and the application wants FlmDbRestore() to retry whatever it was that caused the error.

enum eRestoreStatusType
 

Restore status types reported through the F_Restore::status() method.

Enumerator:
RESTORE_BEGIN_TRANS  Restoring a FlmDbTransBegin() operation. pvValue1 is a FLMUINT that contains the transaction start time.
RESTORE_COMMIT_TRANS  Restoring a FlmDbTransCommit() operation.
RESTORE_ABORT_TRANS  Restoring a FlmDbTransAbort() operation.
RESTORE_ADD_REC  Restoring a FlmRecordAdd() operation. pvValue1 is a FLMUINT that contains the container number. pvValue2 is a FLMUINT that contains the DRN. pvValue3 is a FlmRecord * that points to the record object to be added.
RESTORE_DEL_REC  Restoring a FlmRecordDelete() operation. pvValue1 is a FLMUINT that contains the container number. pvValue2 is a FLMUINT that contains the DRN.
RESTORE_MOD_REC  Restoring a FlmRecordModify() operation. pvValue1 is a FLMUINT that contains the container number. pvValue2 is a FLMUINT that contains the DRN. pvValue3 is a FlmRecord * that points to the modified record object.
RESTORE_RESERVE_DRN  Restoring a FlmReserveNextDrn() operation. pvValue1 is a FLMUINT that contains the container number. pvValue2 is a FLMUINT that contains the DRN that was reserved.
RESTORE_INDEX_SET  Restoring index set of records operation. pvValue1 is a FLMUINT that contains the index number. pvValue2 is a FLMUINT that contains the start DRN to be indexed. pvValue3 is a FLMUINT that contains the end DRN to be indexed.
RESTORE_PROGRESS  Report restore progress. pvValue1 is a BYTE_PROGRESS *.
RESTORE_REDUCE  Restoring a FlmDbReduceSize() operation. pvValue1 is a FLMUINT that contains the count of blocks reduced.
RESTORE_UPGRADE  Restoring a FlmDbUpgrade() operation. pvValue1 is a FLMUINT that contains the old version being upgraded from. pvValue2 is a FLMUINT that contains the new version being upgraded to.
RESTORE_ERROR  Report an error that occurred during the restore. pvValue1 is a RCODE that contains the error which occurred.
RESTORE_INDEX_SUSPEND  Restoring a FlmIndexSuspend() operation. pvValue1 is a FLMUINT that contains the index number.
RESTORE_INDEX_RESUME  Restoring a FlmIndexResume() operation. pvValue1 is a FLMUINT that contains the index number.
RESTORE_BLK_CHAIN_DELETE  Restoring a block chaine delete operation - deleting blocks from an index or container. pvValue1 is a FLMUINT that contains the DRN of the record in the tracker that is being used to track this operation. pvValue2 is a FLMUINT that contains the number of blocks to delete. pvValue3 is a FLMUINT that contains the block address of the last block that was deleted.
RESTORE_WRAP_KEY  Restoring a FlmDbWrapKey() operation. pvValue1 is a FLMUINT that contains the length of the database key.
RESTORE_ENABLE_ENCRYPTION  Restoring a FlmEnableEncryption() operation. pvValue1 is a FLMUINT that contains the length of the database key.
RESTORE_CONFIG_SIZE_EVENT  Restoring a FlmSetSizeEventThreshold() operation. pvValue1 is a FLMUINT ....

enum eStatusType
 

Status types returned in the general purpose status callback function (STATUS_HOOK).

Enumerator:
FLM_INDEXING_STATUS  Reports indexing progress. pvParm1 is a FLMUINT that contains the last DRN that was indexed.
FLM_DELETING_STATUS  Reports progress of deleting an index or container. pvParm1 is a FLMUINT that contains the the number of blocks deleted so far. pvParm2 is a FLMUINT that contains the database block size.
FLM_SWEEP_STATUS  Reports the progress of a call to FlmDbSweep(). pvParm1 is a pointer to a SWEEP_INFO structure. pvParm2 is a FLMUINT that indicates why the callback is being called. It may be one of the following:
  • EACH_CONTAINER - callback happens once for each container that is traversed
  • EACH_RECORD - callback happens once for each record that is read
  • EACH_FIELD - callback happens for each field in the record
  • EACH_CHANGE - callback happens whenever a field definition that was marked as check is changed to unused, or whenever a field that was marked as purged is deleted.
FLM_CHECK_STATUS  Reports status of a database check - called from within FlmDbCheck(). This is returned to the callback function that is passed into FlmDbCheck(). pvParm1 is a pointer to a DB_CHECK_PROGRESS structure.
FLM_SUBQUERY_STATUS  Reports status of query processing. This is returned to the callback function that is set by calling FlmCursorConfig() with the eCursorConfigType::FCURSOR_SET_STATUS_HOOK option. pvParm1 is a pointer to a FCURSOR_SUBQUERY_STATUS structure. pvParm2 is a FLMBOOL that indicates whether a particular subquery is finished processing or not. TRUE is returned if it is finished, FALSE if not.
FLM_DB_COPY_STATUS  Reports status of a database copy - called from within FlmDbCopy(). This is returned to the callback function that is passed into FlmDbCopy(). pvParm1 is a pointer to a DB_COPY_INFO structure.
FLM_REBUILD_STATUS  Reports the status of a database rebuild operation - called from within FlmDbRebuild(). This is returned to the callback function that is passed into FlmDbRebuild(). pvParm1 is a pointer to a REBUILD_INFO structure.
FLM_PROBLEM_STATUS  Reports corruption found by FlmDbCheck(). This is returned to the callback fnction that that is passed into FlmDbCheck(). pvParm1 is a pointer to a CORRUPT_INFO structure. pvParm2 is a FLMBOOL *. If non-NULL, it means that this particular corruption is a logical index corruption that FlmDbCheck() can probably repair. The callback function should return a TRUE if it wants FlmDbCheck() to repair the corruption, FALSE otherwise.
FLM_CHECK_RECORD_STATUS  Reports each non-dictionary record found by FlmDbRebuild(). This is returned to the callback function that is passed into FlmDbRebuild(). It is only reported during the phase when FlmDbRebuild() is collecting dictionary records. pvParm1 is a pointer to a CHK_RECORD structure. The purpose of this status callback is to allow an application to extract dictionary information from non-dictionary records and supply them to FLAIM to inject into the dictionary it is rebuilding. This allows an application a backup mechanism for creating needed dictionary records if those records cannot be recovered from the dictionary container.
FLM_EXAMINE_RECORD_STATUS  Reports each non-dictionary record that is recovered by FlmDbRebuild(). This is returned to the callback function that is passed into FlmDbRebuild(). It is only reported during the phase when FlmDbRebuild() is recovering non-dictionary records. pvParm1 is a pointer to a CHK_RECORD structure. This callback status gives an applicatoin an opportunity to examine each non-dictionary record that is recovered - usually so that the application can collect whatever information it might need to as records are recovered. In this way, the application is not required to make another pass through the recovered records in the rebuilt database if it wants to extract information from the records that were recovered.
FLM_DB_UPGRADE_STATUS  Reports the status of a database upgrade operation - called from within FlmDbUpgrade(). This is returned to the callback funcgtion that is passed into FlmDbUpgrade(). pvParm1 is a pointer to a DB_UPGRADE_INFO structure.
FLM_DB_BACKUP_STATUS  Reports status of a backup - called from within FlmDbBackup(). This is returned to the callback function that is passed into FlmDbBackup(). pvParm1 is a pointer to a DB_BACKUP_INFO structure.
FLM_DB_RENAME_STATUS  Reports status of a rename - called from within FlmDbRename(). This is returned to the callback function that is passed into FlmDbRename(). pvParm1 is a pointer to a DB_RENAME_INFO structure.
FLM_DELETING_KEYS  Reports status of removing keys from an index that spans multiple containers. This is called when a container is deleted that the index spans. When that happens, all keys in the index that point to records in the container must be deleted from the index. This callback status reports the progress of removing those keys. pvParm1 is a FLMUINT that contains the index number whose keys are being removed. pvParm2 is a FLMUINT that contains the total number of elements that have been traversed in the index so far. The callback function that is called is the callback function that is registered when FlmSetStatusHook() is called to set the database handle's STATUS_HOOK callback function.
FLM_REBUILD_ADD_DICT_REC_STATUS  Called from within FlmDbRebuild() prior to adding a dictionary record to the dictionary container in the rebuilt database. This gives an application an opportunity to modify the dictionary record before it is added to the dictionary. pvParm1 is a pointer to the FlmRecord object that is to be added to the dictionary.

enum FBackupType
 

Types of backups supported by FLAIM. This type is passed into FlmDbBackupBegin().

Enumerator:
FLM_FULL_BACKUP  Full backup.
FLM_INCREMENTAL_BACKUP  Incremental backup.

enum FEventCategory
 

Event categories that an application can register to catch - see FlmRegisterForEvent().

Enumerator:
F_EVENT_LOCKS  Catch all database lock events.
F_EVENT_UPDATES  Catch all transaction and update event events.
F_EVENT_SIZE  Catch all size threshold events.

enum FEventType
 

Types of events returned to registered event handling functions.

See FlmRegisterForEvent() for information on how to register an event handling function.

Enumerator:
F_EVENT_LOCK_WAITING  Thread waiting for lock to be granted, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_LOCK_GRANTED  Lock granted to thread, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_LOCK_SUSPENDED  Thread suspended waiting for lock to be granted, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_LOCK_RESUMED  Lock granted to suspended thread, thread resumed, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_LOCK_RELEASED  Lock released by thread, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_LOCK_TIMEOUT  Thread timed out waiting for lock, pvEventData1 == database handle, pvEventData2 == thread id.
F_EVENT_BEGIN_TRANS  Transaction started, pvEventData1 == FLM_TRANS_EVENT *.
F_EVENT_COMMIT_TRANS  Transaction committed, pvEventData1 == FLM_TRANS_EVENT *.
F_EVENT_ABORT_TRANS  Transaction aborted, pvEventData1 == FLM_TRANS_EVENT *.
F_EVENT_ADD_RECORD  Record added to database, pvEventData1 == FLM_UPDATE_EVENT *.
F_EVENT_MODIFY_RECORD  Record modified in database, pvEventData1 == FLM_UPDATE_EVENT *.
F_EVENT_DELETE_RECORD  Record deleted from database, pvEventData1 == FLM_UPDATE_EVENT *.
F_EVENT_RESERVE_DRN  DRN reserved in database, pvEventData1 == FLM_UPDATE_EVENT *.
F_EVENT_INDEXING_COMPLETE  Background indexing status, pvEventData1 (FLMUINT) == index number, pvEventData2 (FLMUINT) == last drn indexed, if zero indexing is complete and the index is now online.
F_EVENT_RFL_SIZE  RFL size threshold has been exceeded, pvEventData1 == FLM_RFL_SIZE_EVENT *.

enum FlmLogMessageType
 

Categories of messages that FLAIM can log and that an application can request to receive.

Enumerator:
FLM_QUERY_MESSAGE  Query information is logged using this message type.
FLM_TRANSACTION_MESSAGE  Transactional messages, including updates.
FLM_GENERAL_MESSAGE  General category of messages - anything not belonging to the other message categories.

enum qOptTypes
 

Sub-query optimization types.

Enumerator:
QOPT_USING_INDEX  Sub-query was optimized using an index.
QOPT_USING_PREDICATE  Sub-query was optimized using an application-defined predicate.
QOPT_SINGLE_RECORD_READ  Sub-query was optimized to retrieve a single record.
QOPT_PARTIAL_CONTAINER_SCAN  Sub-query was optimized to scan through a subset of records in a container.
QOPT_FULL_CONTAINER_SCAN  Sub-query was optimized to scan through all of the records in a container.

enum QTYPES
 

Query value types and operators.

Enumerator:
FLM_BOOL_VAL  1 - Boolean value.
FLM_UINT32_VAL  2 - 32 bit unsigned integer value.
FLM_INT32_VAL  3 - 32 bit signed integer value.
FLM_REAL_VAL  4 - Real number value. NOTE: Not currently supported.
FLM_REC_PTR_VAL  5 - DRN value.
FLM_UINT64_VAL  6 - 64 bit unsigned integer value.
FLM_INT64_VAL  7 - 64 bit signed integer value.
FLM_BINARY_VAL  9 - Binary value.
FLM_STRING_VAL  10 - ASCII string value.
FLM_UNICODE_VAL  11 - Unicode string value.
FLM_TEXT_VAL  12 - Internal FLAIM string value.
FLM_AND_OP  100 - Logical AND operator.
FLM_OR_OP  101 - Logical OR operator.
FLM_NOT_OP  102 - Logical NOT operator.
FLM_EQ_OP  103 - Equals comparison operator.
FLM_MATCH_OP  104 - String match comparison operator.
FLM_MATCH_BEGIN_OP  105 - Sring match-begin comparison operator.
FLM_MATCH_END_OP  106 - String match-end comparison operator.
FLM_CONTAINS_OP  107 - String contains comparison operator.
FLM_NE_OP  108 - Not equal comparison operator.
FLM_LT_OP  109 - Less than comparison operator.
FLM_LE_OP  110 - Less than or equal comparison operator.
FLM_GT_OP  111 - Greater than comparison operator.
FLM_GE_OP  112 - Greater than or equal comparison operator.
FLM_BITAND_OP  113 - Bitwise-AND arithmetic operator.
FLM_BITOR_OP  114 - Bitwise-OR arithmetic operator.
FLM_BITXOR_OP  115 - Bitwise-XOR arithmetic operator.
FLM_MULT_OP  116 - Multiply arithmetic operator.
FLM_DIV_OP  117 - Divide arithmetic operator.
FLM_MOD_OP  118 - Modulo arithmetic operator.
FLM_PLUS_OP  119 - Addition arithmetic operator.
FLM_MINUS_OP  120 - Subtraction arithmetic operator.
FLM_NEG_OP  121 - Unary minus (negative) arithmetic operator.
FLM_LPAREN_OP  122 - Left parentheses.
FLM_RPAREN_OP  123 - Right parentheses.


Function Documentation

FLMEXP const char* FLMAPI FlmVerifyErrToStr eCorruptionType  eCorruption  ) 
 

Return an error string for a corruption code.

Parameters:
eCorruption  Corruption code.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/ftk_8h.html0000644000175000017500000030631110510774540021766 0ustar ahodgkinsonahodgkinson FLAIM: ftk.h File Reference

ftk.h File Reference

#include <stdarg.h>

Classes

struct  PoolMemoryBlock
 Header for blocks in a memory pool. More...
struct  POOL_STATS
 Pool memory manager. More...

Defines

#define NE_FLM_OK   0
 0 - Operation was successful.
#define NE_FLM_BOF_HIT   0xC001
 0xC001 - Beginning of results encountered.
#define NE_FLM_EOF_HIT   0xC002
 0xC002 - End of results encountered.
#define NE_FLM_EXISTS   0xC004
 0xC004 - Object already exists.
#define NE_FLM_FAILURE   0xC005
 0xC005 - Internal failure.
#define NE_FLM_NOT_FOUND   0xC006
 0xC006 - An object was not found.
#define NE_FLM_BTREE_ERROR   0xC012
 0xC012 - Corruption found in b-tree.
#define NE_FLM_BTREE_FULL   0xC013
 0xC013 - B-tree cannot grow beyond current size.
#define NE_FLM_CONV_DEST_OVERFLOW   0xC01C
 0xC01C - Destination buffer not large enough to hold data.
#define NE_FLM_CONV_ILLEGAL   0xC01D
 0xC01D - Attempt to convert between data types is an unsupported conversion.
#define NE_FLM_CONV_NUM_OVERFLOW   0xC020
 0xC020 - Numeric overflow (> upper bound) converting to numeric type.
#define NE_FLM_DATA_ERROR   0xC022
 0xC022 - Corruption found in b-tree.
#define NE_FLM_ILLEGAL_OP   0xC026
 0xC026 - Illegal operation
#define NE_FLM_MEM   0xC037
 0xC037 - Attempt to allocate memory failed.
#define NE_FLM_NOT_UNIQUE   0xC03E
 0xC03E - Non-unique key.
#define NE_FLM_SYNTAX   0xC045
 0xC045 - Syntax error while parsing.
#define NE_FLM_NOT_IMPLEMENTED   0xC05F
 0xC05F - Attempt was made to use a feature that is not implemented.
#define NE_FLM_INVALID_PARM   0xC08B
 0xC08B - Invalid parameter passed into a function.
#define NE_FLM_IO_ACCESS_DENIED   0xC201
 0xC201 - Access to file is denied. Caller is not allowed access to a file.
#define NE_FLM_IO_BAD_FILE_HANDLE   0xC202
 0xC202 - Bad file handle or file descriptor.
#define NE_FLM_IO_COPY_ERR   0xC203
 0xC203 - Error occurred while copying a file.
#define NE_FLM_IO_DISK_FULL   0xC204
 0xC204 - Disk full.
#define NE_FLM_IO_END_OF_FILE   0xC205
 0xC205 - End of file reached while reading from the file.
#define NE_FLM_IO_OPEN_ERR   0xC206
 0xC206 - Error while opening the file.
#define NE_FLM_IO_SEEK_ERR   0xC207
 0xC207 - Error occurred while positioning (seeking) within a file.
#define NE_FLM_IO_DIRECTORY_ERR   0xC208
 0xC208 - Error occurred while accessing or deleting a directory.
#define NE_FLM_IO_PATH_NOT_FOUND   0xC209
 0xC209 - File not found.
#define NE_FLM_IO_TOO_MANY_OPEN_FILES   0xC20A
 0xC20A - Too many files open.
#define NE_FLM_IO_PATH_TOO_LONG   0xC20B
 0xC20B - File name too long.
#define NE_FLM_IO_NO_MORE_FILES   0xC20C
 0xC20C - No more files in directory.
#define NE_FLM_IO_DELETING_FILE   0xC20D
 0xC20D - Error occurred while deleting a file.
#define NE_FLM_IO_FILE_LOCK_ERR   0xC20E
 0xC20E - Error attempting to acquire a byte-range lock on a file.
#define NE_FLM_IO_FILE_UNLOCK_ERR   0xC20F
 0xC20F - Error attempting to release a byte-range lock on a file.
#define NE_FLM_IO_PATH_CREATE_FAILURE   0xC210
 0xC210 - Error occurred while attempting to create a directory or sub-directory.
#define NE_FLM_IO_RENAME_FAILURE   0xC211
 0xC211 - Error occurred while renaming a file.
#define NE_FLM_IO_INVALID_PASSWORD   0xC212
 0xC212 - Invalid file password.
#define NE_FLM_SETTING_UP_FOR_READ   0xC213
 0xC213 - Error occurred while setting up to perform a file read operation.
#define NE_FLM_SETTING_UP_FOR_WRITE   0xC214
 0xC214 - Error occurred while setting up to perform a file write operation.
#define NE_FLM_IO_CANNOT_REDUCE_PATH   0xC215
 0xC215 - Cannot reduce file name into more components.
#define NE_FLM_INITIALIZING_IO_SYSTEM   0xC216
 0xC216 - Error occurred while setting up to access the file system.
#define NE_FLM_FLUSHING_FILE   0xC217
 0xC217 - Error occurred while flushing file data buffers to disk.
#define NE_FLM_IO_INVALID_FILENAME   0xC218
 0xC218 - Invalid file name.
#define NE_FLM_IO_CONNECT_ERROR   0xC219
 0xC219 - Error connecting to a remote network resource.
#define NE_FLM_OPENING_FILE   0xC21A
 0xC21A - Unexpected error occurred while opening a file.
#define NE_FLM_DIRECT_OPENING_FILE   0xC21B
 0xC21B - Unexpected error occurred while opening a file in direct access mode.
#define NE_FLM_CREATING_FILE   0xC21C
 0xC21C - Unexpected error occurred while creating a file.
#define NE_FLM_DIRECT_CREATING_FILE   0xC21D
 0xC21D - Unexpected error occurred while creating a file in direct access mode.
#define NE_FLM_READING_FILE   0xC21E
 0xC21E - Unexpected error occurred while reading a file.
#define NE_FLM_DIRECT_READING_FILE   0xC21F
 0xC21F - Unexpected error occurred while reading a file in direct access mode.
#define NE_FLM_WRITING_FILE   0xC220
 0xC220 - Unexpected error occurred while writing to a file.
#define NE_FLM_DIRECT_WRITING_FILE   0xC221
 0xC221 - Unexpected error occurred while writing a file in direct access mode.
#define NE_FLM_POSITIONING_IN_FILE   0xC222
 0xC222 - Unexpected error occurred while positioning within a file.
#define NE_FLM_GETTING_FILE_SIZE   0xC223
 0xC223 - Unexpected error occurred while getting a file's size.
#define NE_FLM_TRUNCATING_FILE   0xC224
 0xC224 - Unexpected error occurred while truncating a file.
#define NE_FLM_PARSING_FILE_NAME   0xC225
 0xC225 - Unexpected error occurred while parsing a file's name.
#define NE_FLM_CLOSING_FILE   0xC226
 0xC226 - Unexpected error occurred while closing a file.
#define NE_FLM_GETTING_FILE_INFO   0xC227
 0xC227 - Unexpected error occurred while getting information about a file.
#define NE_FLM_EXPANDING_FILE   0xC228
 0xC228 - Unexpected error occurred while expanding a file.
#define NE_FLM_GETTING_FREE_BLOCKS   0xC229
 0xC229 - Unexpected error getting free blocks from file system.
#define NE_FLM_CHECKING_FILE_EXISTENCE   0xC22A
 0xC22A - Unexpected error occurred while checking to see if a file exists.
#define NE_FLM_RENAMING_FILE   0xC22B
 0xC22B - Unexpected error occurred while renaming a file.
#define NE_FLM_SETTING_FILE_INFO   0xC22C
 0xC22C - Unexpected error occurred while setting a file's information.
#define NE_FLM_IO_PENDING   0xC22D
 0xC22D - I/O has not yet completed
#define NE_FLM_ASYNC_FAILED   0xC22E
 0xC22E - An async I/O operation failed
#define NE_FLM_MISALIGNED_IO   0xC22F
 0xC22F - Misaligned buffer or offset encountered during I/O request
#define NE_FLM_STREAM_DECOMPRESS_ERROR   0xC400
 0xC400 - Error decompressing data stream.
#define NE_FLM_STREAM_NOT_COMPRESSED   0xC401
 0xC401 - Attempting to decompress a data stream that is not compressed.
#define NE_FLM_STREAM_TOO_MANY_FILES   0xC402
 0xC402 - Too many files in input stream.
#define NE_FLM_COULD_NOT_CREATE_SEMAPHORE   0xC500
 0xC500 - Could not create a semaphore.
#define NE_FLM_BAD_UTF8   0xC501
 0xC501 - An invalid byte sequence was found in a UTF-8 string
#define NE_FLM_ERROR_WAITING_ON_SEMAPHORE   0xC502
 0xC502 - Error occurred while waiting on a sempahore.
#define NE_FLM_BAD_SEN   0xC503
 0xC503 - Invalid simple encoded number.
#define NE_FLM_COULD_NOT_START_THREAD   0xC504
 0xC504 - Problem starting a new thread.
#define NE_FLM_BAD_BASE64_ENCODING   0xC505
 0xC505 - Invalid base64 sequence encountered.
#define NE_FLM_STREAM_EXISTS   0xC506
 0xC506 - Stream file already exists.
#define NE_FLM_MULTIPLE_MATCHES   0xC507
 0xC507 - Multiple items matched but only one match was expected.
#define NE_FLM_BTREE_KEY_SIZE   0xC508
 0xC508 - Invalid b-tree key size.
#define NE_FLM_BTREE_BAD_STATE   0xC509
 0xC509 - B-tree operation cannot be completed.
#define NE_FLM_COULD_NOT_CREATE_MUTEX   0xC50A
 0xC50A - Error occurred while creating or initializing a mutex.
#define NE_FLM_BAD_PLATFORM_FORMAT   0xC50B
 0xC50B - In-memory alignment of disk structures is incorrect
#define NE_FLM_LOCK_REQ_TIMEOUT   0xC50C
 0xC50C - Timeout while waiting for a lock object
#define NE_FLM_WAIT_TIMEOUT   0xC50D
 0xC50D - Timeout while waiting on a semaphore, condition variable, or reader/writer lock
#define NE_FLM_NOIP_ADDR   0xC900
 0xC900 - IP address not found
#define NE_FLM_SOCKET_FAIL   0xC901
 0xC901 - IP socket failure
#define NE_FLM_CONNECT_FAIL   0xC902
 0xC902 - TCP/IP connection failure
#define NE_FLM_BIND_FAIL   0xC903
 0xC903 - The TCP/IP services on your system may not be configured or installed.
#define NE_FLM_LISTEN_FAIL   0xC904
 0xC904 - TCP/IP listen failed
#define NE_FLM_ACCEPT_FAIL   0xC905
 0xC905 - TCP/IP accept failed
#define NE_FLM_SELECT_ERR   0xC906
 0xC906 - TCP/IP select failed
#define NE_FLM_SOCKET_SET_OPT_FAIL   0xC907
 0xC907 - TCP/IP socket operation failed
#define NE_FLM_SOCKET_DISCONNECT   0xC908
 0xC908 - TCP/IP disconnected
#define NE_FLM_SOCKET_READ_FAIL   0xC909
 0xC909 - TCP/IP read failed
#define NE_FLM_SOCKET_WRITE_FAIL   0xC90A
 0xC90A - TCP/IP write failed
#define NE_FLM_SOCKET_READ_TIMEOUT   0xC90B
 0xC90B - TCP/IP read timeout
#define NE_FLM_SOCKET_WRITE_TIMEOUT   0xC90C
 0xC90C - TCP/IP write timeout
#define NE_FLM_SOCKET_ALREADY_CLOSED   0xC90D
 0xC90D - Connection already closed
#define FLM_US_LANG   0
 English, United States.
#define FLM_AF_LANG   1
 Afrikaans.
#define FLM_AR_LANG   2
 Arabic.
#define FLM_CA_LANG   3
 Catalan.
#define FLM_HR_LANG   4
 Croatian.
#define FLM_CZ_LANG   5
 Czech.
#define FLM_DK_LANG   6
 Danish.
#define FLM_NL_LANG   7
 Dutch.
#define FLM_OZ_LANG   8
 English, Australia.
#define FLM_CE_LANG   9
 English, Canada.
#define FLM_UK_LANG   10
 English, United Kingdom.
#define FLM_FA_LANG   11
 Farsi.
#define FLM_SU_LANG   12
 Finnish.
#define FLM_CF_LANG   13
 French, Canada.
#define FLM_FR_LANG   14
 French, France.
#define FLM_GA_LANG   15
 Galician.
#define FLM_DE_LANG   16
 German, Germany.
#define FLM_SD_LANG   17
 German, Switzerland.
#define FLM_GR_LANG   18
 Greek.
#define FLM_HE_LANG   19
 Hebrew.
#define FLM_HU_LANG   20
 Hungarian.
#define FLM_IS_LANG   21
 Icelandic.
#define FLM_IT_LANG   22
 Italian.
#define FLM_NO_LANG   23
 Norwegian.
#define FLM_PL_LANG   24
 Polish.
#define FLM_BR_LANG   25
 Portuguese, Brazil.
#define FLM_PO_LANG   26
 Portuguese, Portugal.
#define FLM_RU_LANG   27
 Russian.
#define FLM_SL_LANG   28
 Slovak.
#define FLM_ES_LANG   29
 Spanish.
#define FLM_SV_LANG   30
 Swedish.
#define FLM_YK_LANG   31
 Ukrainian.
#define FLM_UR_LANG   32
 Urdu.
#define FLM_TK_LANG   33
 Turkey.
#define FLM_JP_LANG   34
 Japanese.
#define FLM_KO_LANG   35
 Korean.
#define FLM_CT_LANG   36
 Chinese-Traditional.
#define FLM_CS_LANG   37
 Chinese-Simplified.
#define FLM_LA_LANG   38
 another Asian language
#define FLM_COMP_CASE_INSENSITIVE   0x0001
 0x0001 = Do case sensitive comparison.
#define FLM_COMP_COMPRESS_WHITESPACE   0x0002
 0x0002 = Compare multiple whitespace characters as a single space.
#define FLM_COMP_NO_WHITESPACE   0x0004
 0x0004 = Ignore all whitespace during comparison.
#define FLM_COMP_NO_UNDERSCORES   0x0008
 0x0008 = Ignore all underscore characters during comparison.
#define FLM_COMP_NO_DASHES   0x0010
 0x0010 = Ignore all dash characters during comparison.
#define FLM_COMP_WHITESPACE_AS_SPACE   0x0020
 0x0020 = Treat newlines and tabs as spaces during comparison.
#define FLM_COMP_IGNORE_LEADING_SPACE   0x0040
 0x0040 = Ignore leading space characters during comparison.
#define FLM_COMP_IGNORE_TRAILING_SPACE   0x0080
 0x0080 = Ignore trailing space characters during comparison.

Typedefs

typedef FLMINT32 RCODE
 Return code.

Enumerations

enum  eColorType {
  FLM_BLACK = 0, FLM_BLUE, FLM_GREEN, FLM_CYAN,
  FLM_RED, FLM_MAGENTA, FLM_BROWN, FLM_LIGHTGRAY,
  FLM_DARKGRAY, FLM_LIGHTBLUE, FLM_LIGHTGREEN, FLM_LIGHTCYAN,
  FLM_LIGHTRED, FLM_LIGHTMAGENTA, FLM_YELLOW, FLM_WHITE
}
 Colors used for logging messages. More...
enum  eLogMessageSeverity {
  F_FATAL_MESSAGE = 0, F_WARN_MESSAGE, F_ERR_MESSAGE, F_INFO_MESSAGE,
  F_DEBUG_MESSAGE
}
 Message severity. More...
enum  eLockType { , FLM_LOCK_EXCLUSIVE, FLM_LOCK_SHARED }
 Types of locks that may be requested. More...

Functions

virtual void FLMAPI appendString (const char *pszStr)=0
 Append a string to the message.
virtual void FLMAPI newline (void)=0
 Append a newline to the message.
virtual void FLMAPI endMessage (void)=0
 End the current message.
FLMUINT FLMAPI f_languageToNum (const char *pszLanguage)
 Get the language string from a language code.
void FLMAPI f_languageToStr (FLMINT iLangNum, char *pszLanguage)
 Convert a language string to the appropriate language code.
virtual FLMBOOL FLMAPI addLockInfo (FLMUINT uiLockNum, FLMUINT uiThreadID, FLMUINT uiTime)=0
 Return lock information for a lock holder or waiter.

Detailed Description


Enumeration Type Documentation

enum eColorType
 

Colors used for logging messages.

Enumerator:
FLM_BLACK  0 = Black
FLM_BLUE  1 = Blue
FLM_GREEN  2 = Green
FLM_CYAN  3 = Cyan
FLM_RED  4 = Red
FLM_MAGENTA  5 = Magenta
FLM_BROWN  6 = Brown
FLM_LIGHTGRAY  7 = Light Gray
FLM_DARKGRAY  8 = Dark Gray
FLM_LIGHTBLUE  9 = Light Blue
FLM_LIGHTGREEN  10 = Light Green
FLM_LIGHTCYAN  11 = Light Cyan
FLM_LIGHTRED  12 = Light Red
FLM_LIGHTMAGENTA  13 = Light Magenta
FLM_YELLOW  14 = Light Yellow
FLM_WHITE  15 = White

enum eLockType
 

Types of locks that may be requested.

Enumerator:
FLM_LOCK_EXCLUSIVE  1 = Exclusive lock.
FLM_LOCK_SHARED  2 = Shared lock.

enum eLogMessageSeverity
 

Message severity.

Enumerator:
F_FATAL_MESSAGE  Indicates that a fatal error occurred - the kind that would normally require a shutdown or other corrective action by an administrator.
F_WARN_MESSAGE  Warning message.
F_ERR_MESSAGE  Non-fatal error message.
F_INFO_MESSAGE  Information-only message.
F_DEBUG_MESSAGE  Debug message.


Function Documentation

virtual FLMBOOL FLMAPI addLockInfo FLMUINT  uiLockNum,
FLMUINT  uiThreadID,
FLMUINT  uiTime
[pure virtual]
 

Return lock information for a lock holder or waiter.

This method is called for each thread that is either holding the lock or waiting to obtain the lock. The application should return TRUE from this method in order to continue, FALSE if it wants to stop and return from the IF_LockObject::getLockInfo() function.

Parameters:
uiLockNum  Position in queue (0 = lock holder, 1..n = lock waiter).
uiThreadID  Thread ID of the lock holder/waiter.
uiTime  For the lock holder, this is the amount of time the lock has been held. For a lock waiter, this is the amount of time the thread has been waiting to obtain the lock. Both times are milliseconds.

virtual void FLMAPI appendString const char *  pszStr  )  [pure virtual]
 

Append a string to the message.

This method may be called multiple times by to format a complete message. The message is not complete until the endMessage() method is called.

virtual void FLMAPI endMessage void   )  [pure virtual]
 

End the current message.

The application should finish logging, displaying, storing, etc. (whatever) the message. The object should be reset in case a new message is subsequently started.

virtual void FLMAPI newline void   )  [pure virtual]
 

Append a newline to the message.

This method is called when a multi-line message is being created. Rather than embedding a newline character, this method is used. This allows an application to recognize the fact that there are multiple lines in the message and to log, display, store, etc. (whatever) them accordingly.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/annotated.html0000644000175000017500000003066710510774540022570 0ustar ahodgkinsonahodgkinson FLAIM: Class List

FLAIM Class List

Here are the classes, structs, unions and interfaces with brief descriptions:
BLOCK_INFOStructure containing statistics collected during FlmDbCheck() for a particular category of blocks in the database
BLOCKIO_STATSStatistics for block reads and writes
BYTE_PROGRESSStructure used to report the progress of a restore operation
CHECKPOINT_INFOStructure that gives the current state of the checkpoint thread. Returned from FlmDbGetConfig() when eDbGetConfigType::FDB_GET_CHECKPOINT_INFO is passed in as the option
CHK_RECORDStructure that is returned to the callback function that is passed to FlmDbRebuild()
CORRUPT_INFOStructure containing information about a specific corruption that is being reported by FlmDbCheck()
CREATE_OPTSDatabase create options. This structure is passed to FlmDbCreate() to specify create options for a new database
DB_BACKUP_INFOStructure that is returned to the callback function that is passed to FlmDbBackup()
DB_CHECK_PROGRESSStructure returned during status callback from FlmDbCheck()
DB_COPY_INFOStructure that is returned to the callback function that is passed to FlmDbCopy()
DB_RENAME_INFOStructure that is returned to the callback function that is passed to FlmDbRename()
DB_STATSDatabase statistics
DB_UPGRADE_INFOStructure that is returned to the callback function that is passed to FlmDbUpgrade()
DISKIO_STATStructure used in gathering statistics to hold a operation count, a byte count, and an elapsed time
F_NameTableClass for mapping names to IDs and vice versa. Methods in this class allow an application to get the dictionary number for a field, index, or container using the field name, index name, or container name. It also allows an application to to get a field name, index name, or field name using the dictionary number
F_RestoreThis is an abstract base class for reading backup data during a FlmDbRestore() operation
F_UnknownStreamThis is an abstract base class that allows an application to read "unknown" data from the RFL or to write "unknown" data to the RFL
FCURSOR_SUBQUERY_STATUSStructure that reports query processing progress
FINDEX_STATUSStructure returned from FlmIndexStatus() to report current status of an index
FLM_CACHE_USAGEStructure that holds cache usage statistics. The statistics will be for either block cache or record cache
FLM_MEM_INFOStructure returned from FlmGetMemoryInfo()
FLM_RFL_SIZE_EVENTStructure returned to an event handler function whenever RFL size events occur. Specifically, this structure is returned whenever the RFL exceeds the on-disk size threshold specified for a database
FLM_STATSFLAIM statistics returned from FlmGetStats()
FLM_TRANS_EVENTStructure returned to an event handler function whenever transaction events occur. Specifically, this structure is returned for transaction begin, commit, and abort events
FLM_UPDATE_EVENTStructure returned to an event handler function whenever database update events occur. Specifically, this structure is returned for record add, modify, and delete events, as well as DRN reserve events
FlmBlobThis class provides an interface for handling binary large objects
FlmRecordClass for creating and modifying database records
FlmRecordSetThis class allows an application to keep a set of FlmRecord objects
FlmUserPredicateThis is an abstract base class which defines the interface that an application must implement to embed its own predicate in a query
FMAINT_STATUSThis structure is returned from FlmMaintenanceStatus(). It contains information about the background maintenance thread
LEVEL_INFOStatistics for a particular level in an index or container b-tree. These statistics are gathered by FlmDbCheck()
LF_STATSStatistics gathered by FlmDbCheck() for a particular logical file (index or container)
LFILE_STATSStatistics gathered for a particular logical file (index or container)
NODEStructure for nodes used in GEDCOM functions. Nodes are the basic components of GEDCOM trees
OPT_INFOStructure returned when FCURSOR_GET_OPT_INFO_LIST option is passed to FlmCursorGetConfig()
POOL_STATSPool memory manager
PoolMemoryBlockHeader for blocks in a memory pool
REBUILD_INFOStructure returned during status callback from FlmDbRebuild()
REC_KEYStructure used to create a linked list of index keys from a record
RTRANS_STATSStatistics for read transactions
SWEEP_INFOStructure that reports information on the progress of FlmDbSweep()
UTRANS_STATSStatistics for update transactions

Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/classes.html0000644000175000017500000002051610510774540022240 0ustar ahodgkinsonahodgkinson FLAIM: Alphabetical List

FLAIM Class Index

B | C | D | F | L | N | O | P | R | S | U

  B  
DB_CHECK_PROGRESS   FINDEX_STATUS   FMAINT_STATUS   POOL_STATS   
BLOCK_INFO   DB_COPY_INFO   FLM_CACHE_USAGE   
  L  
PoolMemoryBlock   
BLOCKIO_STATS   DB_RENAME_INFO   FLM_MEM_INFO   LEVEL_INFO   
  R  
BYTE_PROGRESS   DB_STATS   FLM_RFL_SIZE_EVENT   LF_STATS   REBUILD_INFO   
  C  
DB_UPGRADE_INFO   FLM_STATS   LFILE_STATS   REC_KEY   
CHECKPOINT_INFO   DISKIO_STAT   FLM_TRANS_EVENT   
  N  
RTRANS_STATS   
CHK_RECORD   
  F  
FLM_UPDATE_EVENT   NODE   
  S  
CORRUPT_INFO   F_NameTable   FlmBlob   
  O  
SWEEP_INFO   
CREATE_OPTS   F_Restore   FlmRecord   OPT_INFO   
  U  
  D  
F_UnknownStream   FlmRecordSet   
  P  
UTRANS_STATS   
DB_BACKUP_INFO   FCURSOR_SUBQUERY_STATUS   FlmUserPredicate   

B | C | D | F | L | N | O | P | R | S | U


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/hierarchy.html0000644000175000017500000001075310510774540022563 0ustar ahodgkinsonahodgkinson FLAIM: Hierarchical Index

FLAIM Class Hierarchy

This inheritance list is sorted roughly, but not completely, alphabetically:
Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions.html0000644000175000017500000000746210510774540022620 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- a -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x62.html0000644000175000017500000001347110510774540023374 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- b -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x63.html0000644000175000017500000001057410510774540023376 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- c -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x65.html0000644000175000017500000000743410510774540023401 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- e -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x66.html0000644000175000017500000000723210510774540023376 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- f -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x67.html0000644000175000017500000001720410510774540023377 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- g -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x68.html0000644000175000017500000000726610510774540023407 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- h -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x69.html0000644000175000017500000001146210510774540023401 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- i -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x6c.html0000644000175000017500000001020710510774540023447 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- l -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x6d.html0000644000175000017500000000616410510774540023457 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- m -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x6e.html0000644000175000017500000000715310510774540023457 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- n -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x6f.html0000644000175000017500000000761110510774540023457 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- o -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x70.html0000644000175000017500000001441510510774540023372 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- p -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x72.html0000644000175000017500000001143410510774540023372 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- r -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x73.html0000644000175000017500000001323010510774540023367 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- s -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x74.html0000644000175000017500000000636310510774540023401 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- t -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x75.html0000644000175000017500000005756110510774540023410 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- u -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x76.html0000644000175000017500000000612410510774540023376 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- v -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_0x77.html0000644000175000017500000000615310510774540023401 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented class members with links to the class documentation for each member:

- w -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_func.html0000644000175000017500000004302410510774540023625 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Functions

 

- a -

- b -

- c -

- e -

- f -

- g -

- h -

- i -

- l -

- n -

- o -

- p -

- r -

- s -

- t -

- w -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars.html0000644000175000017500000000643310510774540023650 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- a -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x62.html0000644000175000017500000001277110510774540024431 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- b -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x63.html0000644000175000017500000000601410510774540024423 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- c -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x65.html0000644000175000017500000000654510510774540024436 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- e -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x67.html0000644000175000017500000000564010510774540024433 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- g -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x68.html0000644000175000017500000000656710510774540024445 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- h -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x69.html0000644000175000017500000000646610510774540024444 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- i -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x6c.html0000644000175000017500000000713710510774540024512 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- l -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x6d.html0000644000175000017500000000563710510774540024516 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- m -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x6e.html0000644000175000017500000000607310510774540024512 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- n -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x6f.html0000644000175000017500000000564610510774540024520 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- o -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x70.html0000644000175000017500000001226610510774540024427 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- p -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x72.html0000644000175000017500000000732410510774540024430 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- r -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x73.html0000644000175000017500000000662710510774540024436 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- s -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x75.html0000644000175000017500000005723410510774540024440 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- u -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/functions_vars_0x76.html0000644000175000017500000000557710510774540024444 0ustar ahodgkinsonahodgkinson FLAIM: Class Members - Variables

 

- v -


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_l_o_c_k___i_n_f_o.html0000644000175000017500000001351010510774540026037 0ustar ahodgkinsonahodgkinson FLAIM: BLOCK_INFO Struct Reference

BLOCK_INFO Struct Reference

Structure containing statistics collected during FlmDbCheck() for a particular category of blocks in the database. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiBlockCount
 Total blocks found in the database that were in the in the block category.
FLMUINT64 ui64BytesUsed
 Total bytes used in the blocks.
FLMUINT64 ui64ElementCount
 Total elements in the blocks. NOTE: This only applies to b-tree blocks.
FLMUINT64 ui64ContElementCount
 Total continuation elements in the blocks. NOTE: This only applies to b-tree blocks.
FLMUINT64 ui64ContElmBytes
 Total bytes in continuation elements. NOTE: This only applies to b-tree blocks.
eCorruptionType eCorruption
 First corruption error found in blocks in this block category.
FLMUINT uiNumErrors
 Total corruption errors found in blocks in this block category.


Detailed Description

Structure containing statistics collected during FlmDbCheck() for a particular category of blocks in the database.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_l_o_c_k___i_n_f_o-members.html0000644000175000017500000000571110510774540027473 0ustar ahodgkinsonahodgkinson FLAIM: Member List

BLOCK_INFO Member List

This is the complete list of members for BLOCK_INFO, including all inherited members.

eCorruptionBLOCK_INFO
ui64BytesUsedBLOCK_INFO
ui64ContElementCountBLOCK_INFO
ui64ContElmBytesBLOCK_INFO
ui64ElementCountBLOCK_INFO
uiBlockCountBLOCK_INFO
uiNumErrorsBLOCK_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_l_o_c_k_i_o___s_t_a_t_s.html0000644000175000017500000001246410510774540027236 0ustar ahodgkinsonahodgkinson FLAIM: BLOCKIO_STATS Struct Reference

BLOCKIO_STATS Struct Reference

Statistics for block reads and writes. More...

#include <flaim.h>

List of all members.

Public Attributes

DISKIO_STAT BlockReads
 Statistics on block reads.
DISKIO_STAT OldViewBlockReads
 Statistics on block reads that resulted in an old view error.
FLMUINT uiBlockChkErrs
 Number of times we had errors checking the block after it was read in - either checksum errors or other problems validating data in the block.
FLMUINT uiOldViewBlockChkErrs
 Number of times we had errors checking the block after it was read in - either checksum errors or other problems validating data in the block. This statistic is for older versions of a block as opposed to the current version.
FLMUINT uiOldViewErrors
 Number of times we had an old view error when reading blocks.
DISKIO_STAT BlockWrites
 Statistics on block writes.


Detailed Description

Statistics for block reads and writes.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_l_o_c_k_i_o___s_t_a_t_s-members.html0000644000175000017500000000552010510774540030661 0ustar ahodgkinsonahodgkinson FLAIM: Member List

BLOCKIO_STATS Member List

This is the complete list of members for BLOCKIO_STATS, including all inherited members.

BlockReadsBLOCKIO_STATS
BlockWritesBLOCKIO_STATS
OldViewBlockReadsBLOCKIO_STATS
uiBlockChkErrsBLOCKIO_STATS
uiOldViewBlockChkErrsBLOCKIO_STATS
uiOldViewErrorsBLOCKIO_STATS


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_y_t_e___p_r_o_g_r_e_s_s.html0000644000175000017500000000405410510774540027261 0ustar ahodgkinsonahodgkinson FLAIM: BYTE_PROGRESS Struct Reference

BYTE_PROGRESS Struct Reference

Structure used to report the progress of a restore operation. More...

#include <flaim.h>

List of all members.


Detailed Description

Structure used to report the progress of a restore operation.

This structure is returned to the F_Restore::status() method when it is called with the eRestoreStatusType::RESTORE_PROGRESS status code.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_b_y_t_e___p_r_o_g_r_e_s_s-members.html0000644000175000017500000000264610510774540030716 0ustar ahodgkinsonahodgkinson FLAIM: Member List

BYTE_PROGRESS Member List

This is the complete list of members for BYTE_PROGRESS, including all inherited members.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_h_e_c_k_p_o_i_n_t___i_n_f_o.html0000644000175000017500000002273210510774540030035 0ustar ahodgkinsonahodgkinson FLAIM: CHECKPOINT_INFO Struct Reference

CHECKPOINT_INFO Struct Reference

Structure that gives the current state of the checkpoint thread. Returned from FlmDbGetConfig() when eDbGetConfigType::FDB_GET_CHECKPOINT_INFO is passed in as the option. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMBOOL bRunning
 Is checkpoint thread currently running?
FLMUINT uiRunningTime
 Time (milliseconds) the checkpoint thread has been running (if bRunning is TRUE).
FLMBOOL bForcingCheckpoint
 Is a checkpoint being forced?
FLMUINT uiForceCheckpointRunningTime
 Time (milliseconds) the checkpoint thread has been forcing a checkpoint (only valid if both bRunning and bForcingCheckpoint are TRUE).
FLMINT iForceCheckpointReason
 Reason checkpoint is being forced (only valid if bForcingCheckpoint is TRUE). It may be one of the following:
  • CP_TIME_INTERVAL_REASON - Maximum time since last completed checkpoint has elapsed
  • CP_SHUTTING_DOWN_REASON - Database is being closed
  • CP_RFL_VOLUME_PROBLEM - Had problems writing to the roll-forward log.

FLMBOOL bWritingDataBlocks
 TRUE if checkpoint thread is currently writing out dirty data blocks.
FLMUINT uiLogBlocksWritten
 Total number of blocks written to the rollback log.
FLMUINT uiDataBlocksWritten
 Total number of dirty data blocks written.
FLMUINT uiDirtyCacheBytes
 Total bytes of dirty cache that still needs to be written.
FLMUINT uiBlockSize
 Block size for database (in bytes).
FLMUINT uiWaitTruncateTime
 Time (milliseconds) the checkpoint thread has been waiting to truncate the rollback log.


Detailed Description

Structure that gives the current state of the checkpoint thread. Returned from FlmDbGetConfig() when eDbGetConfigType::FDB_GET_CHECKPOINT_INFO is passed in as the option.


Member Data Documentation

FLMUINT CHECKPOINT_INFO::uiWaitTruncateTime
 

Time (milliseconds) the checkpoint thread has been waiting to truncate the rollback log.

If zero, the checkpoint thread is not currently waiting to truncate the rollback log.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_h_e_c_k_p_o_i_n_t___i_n_f_o-members.html0000644000175000017500000001023310510774540031456 0ustar ahodgkinsonahodgkinson FLAIM: Member List

CHECKPOINT_INFO Member List

This is the complete list of members for CHECKPOINT_INFO, including all inherited members.

bForcingCheckpointCHECKPOINT_INFO
bRunningCHECKPOINT_INFO
bWritingDataBlocksCHECKPOINT_INFO
iForceCheckpointReasonCHECKPOINT_INFO
uiBlockSizeCHECKPOINT_INFO
uiDataBlocksWrittenCHECKPOINT_INFO
uiDirtyCacheBytesCHECKPOINT_INFO
uiForceCheckpointRunningTimeCHECKPOINT_INFO
uiLogBlocksWrittenCHECKPOINT_INFO
uiRunningTimeCHECKPOINT_INFO
uiWaitTruncateTimeCHECKPOINT_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_h_k___r_e_c_o_r_d.html0000644000175000017500000001306710510774540026044 0ustar ahodgkinsonahodgkinson FLAIM: CHK_RECORD Struct Reference

CHK_RECORD Struct Reference

Structure that is returned to the callback function that is passed to FlmDbRebuild(). More...

#include <flaim.h>

List of all members.

Public Attributes

FlmRecordpRecord
 Record to examine to see if it contains dictionary information.
FLMUINT uiContainer
 Container the record came from.
FLMUINT uiDrn
 DRN of record.
FlmRecordSetpDictRecSet
 Callback function may create a FlmRecordSet object that is returned to FlmDbRebuild(). This object contains a set of dictionary records the application wants FlmDbRebuild() to inject into the dictionary it is rebuilding. FlmDbRebuild() will only use these records if it cannot recover the actual dictionary record from the dictionary container. FlmDbRebuild() will free the FlmRecordSet object pointed to by pDictRecSet when it is done using the records in the set. NOTE: FlmDbRebuild() does not do anything with records that are returned in this set if the status being reported is eStatusType::FLM_EXAMINE_RECORD_STATUS.


Detailed Description

Structure that is returned to the callback function that is passed to FlmDbRebuild().

This structure is passed to the callback function when the eStatusType::FLM_CHECK_RECORD_STATUS or eStatusType::FLM_EXAMINE_RECORD_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_h_k___r_e_c_o_r_d-members.html0000644000175000017500000000441410510774540027470 0ustar ahodgkinsonahodgkinson FLAIM: Member List

CHK_RECORD Member List

This is the complete list of members for CHK_RECORD, including all inherited members.

pDictRecSetCHK_RECORD
pRecordCHK_RECORD
uiContainerCHK_RECORD
uiDrnCHK_RECORD


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_o_r_r_u_p_t___i_n_f_o.html0000644000175000017500000003123110510774540026741 0ustar ahodgkinsonahodgkinson FLAIM: CORRUPT_INFO Struct Reference

CORRUPT_INFO Struct Reference

Structure containing information about a specific corruption that is being reported by FlmDbCheck(). More...

#include <flaim.h>

List of all members.

Public Attributes

eCorruptionType eCorruption
 Type of corruption being reported.
eCorruptionLocale eErrLocale
 Location of the corruption in the database.
FLMUINT uiErrLfNumber
 If eErrLocale is eCorruptionLocale::LOCALE_B_TREE or eCorruptionLocale::LOCALE_IXD_TBL or eCorruptionLocale::LOCALE_INDEX this will contain the index or container number.
FLMUINT uiErrLfType
 If eErrLocale is eCorruptionLocale::LOCALE_B_TREE, this will contain either LF_INDEX or LF_CONTAINER.
FLMUINT uiErrBTreeLevel
 If eErrLocale is eCorruptionLocale::LOCALE_B_TREE, this will contain the level in the b-tree where the corruption was found. A value of 0xFF means that the b-tree level is unknown.
FLMUINT uiErrBlkAddress
 If non-zero, this contains the address of the block where the corruption was found.
FLMUINT uiErrParentBlkAddress
 If non-zero, this contains the address of the parent block of the block where the corruption was found. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_B_TREE.
FLMUINT uiErrElmOffset
 If non-zero, this is the offset of the element within the block where the corruption was found. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_B_TREE.
FLMUINT uiErrDrn
 If non-zero, this is the DRN of the record where the corruption was found. NOTE: It may also be set to indicate a reference from an index if the corruption is in an index.
FLMUINT uiErrElmRecOffset
 If non-zero, this is the offset within the "record" part of the element where the corruption was found. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_B_TREE.
FLMUINT uiErrFieldNum
 If non-zero, this is the field number where the corruption was found.
const FLMBYTE * pBlk
 If non-NULL, this is a pointer to block where corruption was found.
FlmRecordpErrIxKey
 If non-NULL, this will contain a pointer to the key from an index for an index logical corruption. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_INDEX.
FlmRecordpErrRecord
 If non-NULL, this will contain a pointer to the record involved in an index logical corruption. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_INDEX.
REC_KEYpErrRecordKeyList
 If non-NULL, this will contain a pointer to a linked list of keys from the record that was involved in an index logical corruption. NOTE: This will only be set when eErrLocale is eCorruptionLocale::LOCALE_INDEX.


Detailed Description

Structure containing information about a specific corruption that is being reported by FlmDbCheck().

This structure is passed to the callback function when eStatusType::FLM_PROBLEM_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_o_r_r_u_p_t___i_n_f_o-members.html0000644000175000017500000001154010510774540030372 0ustar ahodgkinsonahodgkinson FLAIM: Member List

CORRUPT_INFO Member List

This is the complete list of members for CORRUPT_INFO, including all inherited members.

eCorruptionCORRUPT_INFO
eErrLocaleCORRUPT_INFO
pBlkCORRUPT_INFO
pErrIxKeyCORRUPT_INFO
pErrRecordCORRUPT_INFO
pErrRecordKeyListCORRUPT_INFO
uiErrBlkAddressCORRUPT_INFO
uiErrBTreeLevelCORRUPT_INFO
uiErrDrnCORRUPT_INFO
uiErrElmOffsetCORRUPT_INFO
uiErrElmRecOffsetCORRUPT_INFO
uiErrFieldNumCORRUPT_INFO
uiErrLfNumberCORRUPT_INFO
uiErrLfTypeCORRUPT_INFO
uiErrParentBlkAddressCORRUPT_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_r_e_a_t_e___o_p_t_s.html0000644000175000017500000001502110510774540026400 0ustar ahodgkinsonahodgkinson FLAIM: CREATE_OPTS Struct Reference

CREATE_OPTS Struct Reference

Database create options. This structure is passed to FlmDbCreate() to specify create options for a new database. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiBlockSize
 Block size for the database.
FLMUINT uiVersionNum
 Database version number.
FLMUINT uiMinRflFileSize
 Minimum bytes per RFL file.
FLMUINT uiMaxRflFileSize
 Maximum bytes per RFL file.
FLMBOOL bKeepRflFiles
 Keep RFL files?
FLMBOOL bLogAbortedTransToRfl
 Log aborted transactions to RFL?
FLMUINT uiDefaultLanguage
 Default language for the database.
FLMUINT uiAppMajorVer
 The application's major version number.
FLMUINT uiAppMinorVer
 The application's minor version number.


Detailed Description

Database create options. This structure is passed to FlmDbCreate() to specify create options for a new database.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_c_r_e_a_t_e___o_p_t_s-members.html0000644000175000017500000000670310510774540030037 0ustar ahodgkinsonahodgkinson FLAIM: Member List

CREATE_OPTS Member List

This is the complete list of members for CREATE_OPTS, including all inherited members.

bKeepRflFilesCREATE_OPTS
bLogAbortedTransToRflCREATE_OPTS
uiAppMajorVerCREATE_OPTS
uiAppMinorVerCREATE_OPTS
uiBlockSizeCREATE_OPTS
uiDefaultLanguageCREATE_OPTS
uiMaxRflFileSizeCREATE_OPTS
uiMinRflFileSizeCREATE_OPTS
uiVersionNumCREATE_OPTS


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___b_a_c_k_u_p___i_n_f_o.html0000644000175000017500000000633310510774540027460 0ustar ahodgkinsonahodgkinson FLAIM: DB_BACKUP_INFO Struct Reference

DB_BACKUP_INFO Struct Reference

Structure that is returned to the callback function that is passed to FlmDbBackup(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT64 ui64BytesToDo
 Total bytes to be backed up.
FLMUINT64 ui64BytesDone
 Total bytes backed up so far.


Detailed Description

Structure that is returned to the callback function that is passed to FlmDbBackup().

This structure is passed to the callback function when the eStatusType::FLM_DB_BACKUP_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___b_a_c_k_u_p___i_n_f_o-members.html0000644000175000017500000000362410510774540031110 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_BACKUP_INFO Member List

This is the complete list of members for DB_BACKUP_INFO, including all inherited members.

ui64BytesDoneDB_BACKUP_INFO
ui64BytesToDoDB_BACKUP_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___c_h_e_c_k___p_r_o_g_r_e_s_s.html0000644000175000017500000005132310510774540030635 0ustar ahodgkinsonahodgkinson FLAIM: DB_CHECK_PROGRESS Struct Reference

DB_CHECK_PROGRESS Struct Reference

Structure returned during status callback from FlmDbCheck(). More...

#include <flaim.h>

List of all members.

Public Attributes

void * AppArg
 Application data. This is the application data pointer that was passed into FlmDbCheck().
FLMINT iCheckPhase
 Phase of the check currently being performed by FlmDbCheck(). It may be one of the following:
  • CHECK_LFH_BLOCKS - Checking logical file header blocks
  • CHECK_B_TREE - Checking container and index b-trees
  • CHECK_AVAIL_BLOCKS - Checking available block list
  • CHECK_RS_SORT - Sorting result set for index keys
  • CHECK_FINISHED - Database check is finished.

FLMBOOL bStartFlag
 Flag indicating if we are at the beginning of the check phase specified by iCheckPhase.
FLMUINT uiCurrLF
 Logical file currently being processed.
FLMUINT uiLfNumber
 Current logical file number.
FLMUINT uiLfType
 Logical file type. May be one of the following:
  • LF_INDEX - Index
  • LF_CONTAINER - Container.

FLMUINT64 ui64DatabaseSize
 Total database size.
FLMUINT64 ui64BytesExamined
 Total bytes examined so far in the database.
FLMUINT uiNumProblemsFixed
 Total problems fixed. NOTE: This count only refers to logical index corruptions.
FLMBOOL bPhysicalCorrupt
 Flag indicating if physical database corruptions were found.
FLMBOOL bLogicalIndexCorrupt
 Flag indicating if logical index corruptions were found.
FLMUINT uiLogicalIndexCorruptions
 Total number of logical index corruptions that were found.
FLMUINT uiLogicalIndexRepairs
 Total number of logical index corruptions that were repaired.
FLMUINT uiNumFields
 Total fields defined in the dictionary.
FLMUINT uiNumIndexes
 Total indexes in the database.
FLMUINT uiNumContainers
 Total containers in the database.
FLMUINT uiNumLogicalFiles
 Total logical files (indexes & containers) in the database.
LF_STATSpLfStats
 Statistics collected for all logical files (indexes and containers) in the database.
BLOCK_INFO AvailBlocks
 Statistics collected on available blocks.
BLOCK_INFO LFHBlocks
 Statistics collected on logical file header blocks.
FLMBOOL bUniqueIndex
 Is index being checked a unique index?
FLMUINT64 ui64NumKeys
 Total keys gathered from records.
FLMUINT64 ui64NumDuplicateKeys
 Total duplicate keys found in records.
FLMUINT64 ui64NumKeysExamined
 Total keys examined so far.
FLMUINT64 ui64NumKeysNotFound
 Total keys found in index but not in record.
FLMUINT64 ui64NumRecKeysNotFound
 Total keys found in record but not in index.
FLMUINT64 ui64NumNonUniqueKeys
 Total non-unique keys found in records for unique indexes.
FLMUINT64 ui64NumConflicts
 Number of key inconsistencies that turned out not to be inconsistent when FLAIM attempted to repair them.
FLMUINT64 ui64NumRSUnits
 Total number of "units" in the key result set that need to be sorted. NOTE: This only applies when iCheckPhase == CHECK_RS_SORT.
FLMUINT64 ui64NumRSUnitsDone
 Number of "units" in the key result set that have been sorted so far. NOTE: This only applies when iCheckPhase == CHECK_RS_SORT.
FLMUINT uiVersionNum
 Database version.
FLMUINT uiBlockSize
 Database block size.
FLMUINT uiDefaultLanguage
 Database default language.


Detailed Description

Structure returned during status callback from FlmDbCheck().

This structure is passed to the callback function when the eStatusType::FLM_CHECK_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___c_h_e_c_k___p_r_o_g_r_e_s_s-members.html0000644000175000017500000002355510510774540032273 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_CHECK_PROGRESS Member List

This is the complete list of members for DB_CHECK_PROGRESS, including all inherited members.

AppArgDB_CHECK_PROGRESS
AvailBlocksDB_CHECK_PROGRESS
bLogicalIndexCorruptDB_CHECK_PROGRESS
bPhysicalCorruptDB_CHECK_PROGRESS
bStartFlagDB_CHECK_PROGRESS
bUniqueIndexDB_CHECK_PROGRESS
iCheckPhaseDB_CHECK_PROGRESS
LFHBlocksDB_CHECK_PROGRESS
pLfStatsDB_CHECK_PROGRESS
ui64BytesExaminedDB_CHECK_PROGRESS
ui64DatabaseSizeDB_CHECK_PROGRESS
ui64NumConflictsDB_CHECK_PROGRESS
ui64NumDuplicateKeysDB_CHECK_PROGRESS
ui64NumKeysDB_CHECK_PROGRESS
ui64NumKeysExaminedDB_CHECK_PROGRESS
ui64NumKeysNotFoundDB_CHECK_PROGRESS
ui64NumNonUniqueKeysDB_CHECK_PROGRESS
ui64NumRecKeysNotFoundDB_CHECK_PROGRESS
ui64NumRSUnitsDB_CHECK_PROGRESS
ui64NumRSUnitsDoneDB_CHECK_PROGRESS
uiBlockSizeDB_CHECK_PROGRESS
uiCurrLFDB_CHECK_PROGRESS
uiDefaultLanguageDB_CHECK_PROGRESS
uiLfNumberDB_CHECK_PROGRESS
uiLfTypeDB_CHECK_PROGRESS
uiLogicalIndexCorruptionsDB_CHECK_PROGRESS
uiLogicalIndexRepairsDB_CHECK_PROGRESS
uiNumContainersDB_CHECK_PROGRESS
uiNumFieldsDB_CHECK_PROGRESS
uiNumIndexesDB_CHECK_PROGRESS
uiNumLogicalFilesDB_CHECK_PROGRESS
uiNumProblemsFixedDB_CHECK_PROGRESS
uiVersionNumDB_CHECK_PROGRESS


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___c_o_p_y___i_n_f_o.html0000644000175000017500000001150410510774540026663 0ustar ahodgkinsonahodgkinson FLAIM: DB_COPY_INFO Struct Reference

DB_COPY_INFO Struct Reference

Structure that is returned to the callback function that is passed to FlmDbCopy(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT64 ui64BytesToCopy
 Total bytes to copy.
FLMUINT64 ui64BytesCopied
 Total bytes copied so far.
FLMBOOL bNewSrcFile
 If TRUE, we are starting to copy a new file. szSrcFileName and szDestFileName will be set.
char szSrcFileName [F_PATH_MAX_SIZE]
 Name of source file currently being copied.
char szDestFileName [F_PATH_MAX_SIZE]
 Name of destination file being copied to.


Detailed Description

Structure that is returned to the callback function that is passed to FlmDbCopy().

This structure is passed to the callback function when the eStatusType::FLM_DB_COPY_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___c_o_p_y___i_n_f_o-members.html0000644000175000017500000000507410510774540030320 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_COPY_INFO Member List

This is the complete list of members for DB_COPY_INFO, including all inherited members.

bNewSrcFileDB_COPY_INFO
szDestFileNameDB_COPY_INFO
szSrcFileNameDB_COPY_INFO
ui64BytesCopiedDB_COPY_INFO
ui64BytesToCopyDB_COPY_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___r_e_n_a_m_e___i_n_f_o.html0000644000175000017500000000641710510774540027465 0ustar ahodgkinsonahodgkinson FLAIM: DB_RENAME_INFO Struct Reference

DB_RENAME_INFO Struct Reference

Structure that is returned to the callback function that is passed to FlmDbRename(). More...

#include <flaim.h>

List of all members.

Public Attributes

char szSrcFileName [F_PATH_MAX_SIZE]
 File being renamed.
char szDstFileName [F_PATH_MAX_SIZE]
 Name the file is to be renamed to.


Detailed Description

Structure that is returned to the callback function that is passed to FlmDbRename().

This structure is passed to the callback function when the eStatusType::FLM_DB_RENAME_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___r_e_n_a_m_e___i_n_f_o-members.html0000644000175000017500000000362410510774540031112 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_RENAME_INFO Member List

This is the complete list of members for DB_RENAME_INFO, including all inherited members.

szDstFileNameDB_RENAME_INFO
szSrcFileNameDB_RENAME_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___s_t_a_t_s.html0000644000175000017500000003512210510774540025242 0ustar ahodgkinsonahodgkinson FLAIM: DB_STATS Struct Reference

DB_STATS Struct Reference

Database statistics. More...

#include <flaim.h>

List of all members.

Public Attributes

const char * pszDbName
 Name of database these statistics are for.
FLMBOOL bHaveStats
 Flag indicating whether or not there are statistics for this database.
RTRANS_STATS ReadTransStats
 Read transaction statistics for the database.
UTRANS_STATS UpdateTransStats
 Update transaction statistics for the database.
FLMUINT64 ui64NumCursors
 Number of times a query object was created for this database. This is the number of times FlmCursorInit() was called.
FLMUINT64 ui64NumCursorReads
 Number of query operations that have been performed on this database. This includes counts for FlmCursorFirst(), FlmCursorLast(), FlmCursorNext(), FlmCursorPrev(), and FlmCursorCurrent().
F_COUNT_TIME_STAT RecordAdds
 Number of record add operations (FlmRecordAdd()) that have been performed on this database.
F_COUNT_TIME_STAT RecordDeletes
 Number of record delete operations (FlmRecordDelete()) that have been performed on this database.
F_COUNT_TIME_STAT RecordModifies
 Number of record modify operations (FlmRecordModify()) that have been performed on this database.
FLMUINT64 ui64NumRecordReads
 Number of record read operations (FlmRecordRetrieve()) that have been performed on this database.
FLMUINT uiLFileAllocSeq
 Allocation sequence number for pLFileStats array - used internally.
LFILE_STATSpLFileStats
 Logical file statistics for this database.
FLMUINT uiLFileStatArraySize
 Number of logical files in the pLFileStats array - used internally.
FLMUINT uiNumLFileStats
 Number of elements in the pLFileStats array currently in use.
DISKIO_STAT LogHdrWrites
 Statistics for writes to the database's log header.
DISKIO_STAT LogBlockWrites
 Statistics for writes of blocks to the rollback log.
DISKIO_STAT LogBlockRestores
 Statistics for writing of blocks from the rollback log back into data files (done during database recovery or when aborting a transaction).
DISKIO_STAT LogBlockReads
 Statistics on reading blocks from the rollback log.
FLMUINT uiLogBlockChkErrs
 Number of times we had checksum errors reading blocks from the rollback log.
FLMUINT uiReadErrors
 Number of times we got read errors.
FLMUINT uiWriteErrors
 Number of times we got write errors.
F_LOCK_STATS LockStats
 Database lock statistics.


Detailed Description

Database statistics.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___s_t_a_t_s-members.html0000644000175000017500000001403510510774540026672 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_STATS Member List

This is the complete list of members for DB_STATS, including all inherited members.

bHaveStatsDB_STATS
LockStatsDB_STATS
LogBlockReadsDB_STATS
LogBlockRestoresDB_STATS
LogBlockWritesDB_STATS
LogHdrWritesDB_STATS
pLFileStatsDB_STATS
pszDbNameDB_STATS
ReadTransStatsDB_STATS
RecordAddsDB_STATS
RecordDeletesDB_STATS
RecordModifiesDB_STATS
ui64NumCursorReadsDB_STATS
ui64NumCursorsDB_STATS
ui64NumRecordReadsDB_STATS
uiLFileAllocSeqDB_STATS
uiLFileStatArraySizeDB_STATS
uiLogBlockChkErrsDB_STATS
uiNumLFileStatsDB_STATS
uiReadErrorsDB_STATS
uiWriteErrorsDB_STATS
UpdateTransStatsDB_STATS


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___u_p_g_r_a_d_e___i_n_f_o.html0000644000175000017500000000731310510774540030000 0ustar ahodgkinsonahodgkinson FLAIM: DB_UPGRADE_INFO Struct Reference

DB_UPGRADE_INFO Struct Reference

Structure that is returned to the callback function that is passed to FlmDbUpgrade(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiDrn
 Current DRN being processed.
FLMUINT uiLastDrn
 Highest DRN in this container.
FLMUINT uiContainer
 Container being processed.


Detailed Description

Structure that is returned to the callback function that is passed to FlmDbUpgrade().

This structure is passed to the callback function when the eStatusType::FLM_DB_UPGRADE_STATUS status is reported.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_b___u_p_g_r_a_d_e___i_n_f_o-members.html0000644000175000017500000000421610510774540031427 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DB_UPGRADE_INFO Member List

This is the complete list of members for DB_UPGRADE_INFO, including all inherited members.

uiContainerDB_UPGRADE_INFO
uiDrnDB_UPGRADE_INFO
uiLastDrnDB_UPGRADE_INFO


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_i_s_k_i_o___s_t_a_t.html0000644000175000017500000000712010510774540026406 0ustar ahodgkinsonahodgkinson FLAIM: DISKIO_STAT Struct Reference

DISKIO_STAT Struct Reference

Structure used in gathering statistics to hold a operation count, a byte count, and an elapsed time. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT64 ui64Count
 Number of times operation was performed.
FLMUINT64 ui64TotalBytes
 Total number of bytes involved in the operations. This usually represents bytes read from or written to disk.
FLMUINT64 ui64ElapMilli
 Total elapsed time (milliseconds) for the operations.


Detailed Description

Structure used in gathering statistics to hold a operation count, a byte count, and an elapsed time.

This is typically used for I/O operations where it is useful to know the number of bytes that were read or written by the operation.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_d_i_s_k_i_o___s_t_a_t-members.html0000644000175000017500000000411510510774540030037 0ustar ahodgkinsonahodgkinson FLAIM: Member List

DISKIO_STAT Member List

This is the complete list of members for DISKIO_STAT, including all inherited members.

ui64CountDISKIO_STAT
ui64ElapMilliDISKIO_STAT
ui64TotalBytesDISKIO_STAT


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___name_table.html0000644000175000017500000010475010510774540024665 0ustar ahodgkinsonahodgkinson FLAIM: F_NameTable Class Reference

F_NameTable Class Reference

Class for mapping names to IDs and vice versa. Methods in this class allow an application to get the dictionary number for a field, index, or container using the field name, index name, or container name. It also allows an application to to get a field name, index name, or field name using the dictionary number. More...

#include <flaim.h>

List of all members.

Public Member Functions

void clearTable (void)
 Clear the name table.
RCODE setupFromDb (HFDB hDb)
 Populate a name table from the dictionary of the specified database.
FLMBOOL getNextTagNumOrder (FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)
 Get the next item from the the table in dictionary number order.
FLMBOOL getNextTagNameOrder (FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)
 Get the next item from the the table in name order.
FLMBOOL getFromTagType (FLMUINT uiType, FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiSubType=NULL)
 Get the next item from the the table of the specified type.
FLMBOOL getFromTagNum (FLMUINT uiTagNum, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)
 Get the item from the table with the specified dictionary number.
FLMBOOL getFromTagName (const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT *puiTagNum, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)
 Get the item from the table with the specified name.
FLMBOOL getFromTagTypeAndName (const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT uiType, FLMUINT *puiTagNum, FLMUINT *puiSubType=NULL)
 Get the item from the table with the specified type (field, index, or container) and name.
RCODE addTag (const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLMBOOL bCheckDuplicates=TRUE)
 Insert an item into the table.
void sortTags (void)
 Sort the items in the table.


Detailed Description

Class for mapping names to IDs and vice versa. Methods in this class allow an application to get the dictionary number for a field, index, or container using the field name, index name, or container name. It also allows an application to to get a field name, index name, or field name using the dictionary number.


Member Function Documentation

RCODE F_NameTable::addTag const FLMUNICODE *  puzTagName,
const char *  pszTagName,
FLMUINT  uiTagNum,
FLMUINT  uiType,
FLMUINT  uiSubType,
FLMBOOL  bCheckDuplicates = TRUE
 

Insert an item into the table.

Parameters:
puzTagName  If non-NULL, specifies name of item being added to the table.
pszTagName  If non-NULL, specifies name of item being added to the table. NOTE: If puzTagName is also non-NULL, the pszTagName parameter will be ignored. It is illegal for both puzTagName and pszTagName to be NULL.
uiTagNum  Specifies the dictionary number of the item being added to the table.
uiType  Specifies the dictionary type (field, index,or container) of the item being added to the table.
uiSubType  Specifies the dictionary sub-type of the item being added to the table. NOTE: This is only needed for field items, in which case the sub-type should be the data type for the field.
bCheckDuplicates  Flag specifying whether to check for duplicates. Checks will be made for duplicate name, duplicate type+name, and duplicate dictionary number. If FALSE, duplicate checking is not done. If TRUE, this call becomes more expensive because the table must be sorted in order to check for duplicates.

FLMBOOL F_NameTable::getFromTagName const FLMUNICODE *  puzTagName,
const char *  pszTagName,
FLMUINT *  puiTagNum,
FLMUINT *  puiType = NULL,
FLMUINT *  puiSubType = NULL
 

Get the item from the table with the specified name.

Parameters:
puzTagName  If non-NULL, specifies name of item to find.
pszTagName  If non-NULL, specifies name of item to find. NOTE: If puzTagName is also non-NULL, the pszTagName parameter will be ignored.
puiTagNum  Dictionary number for item is returned here - must be non-NULL.
puiType  If non-NULL, dictionary type (field, index, container) is returned here.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

FLMBOOL F_NameTable::getFromTagNum FLMUINT  uiTagNum,
FLMUNICODE *  puzTagName,
char *  pszTagName,
FLMUINT  uiNameBufSize,
FLMUINT *  puiType = NULL,
FLMUINT *  puiSubType = NULL
 

Get the item from the table with the specified dictionary number.

Parameters:
uiTagNum  Dictionary number of item to be retrieved.
puzTagName  If non-NULL, name is returned here as a Unicode string.
pszTagName  If non-NULL, name is returned here as an ASCII string. NOTE: If both pszTagName and puzTagName are non-NULL, only puzTagName will be populated.
uiNameBufSize  Size, in bytes, of the puzTagName or pszTagName buffer. Needs to be big enough include a null terminator character.
puiType  If non-NULL, dictionary type (field, index, container) is returned here.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

FLMBOOL F_NameTable::getFromTagType FLMUINT  uiType,
FLMUINT *  puiNextPos,
FLMUNICODE *  puzTagName,
char *  pszTagName,
FLMUINT  uiNameBufSize,
FLMUINT *  puiTagNum = NULL,
FLMUINT *  puiSubType = NULL
 

Get the next item from the the table of the specified type.

This method allows an application to traverse through all of the items of a particular type (field, index, container). Items will be returned in name order.

Parameters:
uiType  Type of items to be returned.
puiNextPos  Points to a variable that keeps the position of the last item returned. Returns the position of the next item. Initialize to zero to retrieve the first item of the specified type.
puzTagName  If non-NULL, name is returned here as a Unicode string.
pszTagName  If non-NULL, name is returned here as an ASCII string. NOTE: If both pszTagName and puzTagName are non-NULL, only puzTagName will be populated.
uiNameBufSize  Size, in bytes, of the puzTagName or pszTagName buffer. Needs to be big enough include a null terminator character.
puiTagNum  If non-NULL, dictionary number for item is returned here.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

FLMBOOL F_NameTable::getFromTagTypeAndName const FLMUNICODE *  puzTagName,
const char *  pszTagName,
FLMUINT  uiType,
FLMUINT *  puiTagNum,
FLMUINT *  puiSubType = NULL
 

Get the item from the table with the specified type (field, index, or container) and name.

Parameters:
puzTagName  If non-NULL, specifies name of item to find.
pszTagName  If non-NULL, specifies name of item to find. NOTE: If puzTagName is also non-NULL, the pszTagName parameter will be ignored.
uiType  Type of item to be found.
puiTagNum  Dictionary number for item is returned here - must be non-NULL.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

FLMBOOL F_NameTable::getNextTagNameOrder FLMUINT *  puiNextPos,
FLMUNICODE *  puzTagName,
char *  pszTagName,
FLMUINT  uiNameBufSize,
FLMUINT *  puiTagNum = NULL,
FLMUINT *  puiType = NULL,
FLMUINT *  puiSubType = NULL
 

Get the next item from the the table in name order.

This method allows an application to traverse through all of the names in a table in name order.

Parameters:
puiNextPos  Points to a variable that keeps the position of the last item returned. Returns the position of the next item. Initialize to zero to retrieve the first item.
puzTagName  If non-NULL, name is returned here as a Unicode string.
pszTagName  If non-NULL, name is returned here as an ASCII string. NOTE: If both pszTagName and puzTagName are non-NULL, only puzTagName will be populated.
uiNameBufSize  Size, in bytes, of the puzTagName or pszTagName buffer. Needs to be big enough include a null terminator character.
puiTagNum  If non-NULL, dictionary number for item is returned here.
puiType  If non-NULL, dictionary type (field, index, container) is returned here.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

FLMBOOL F_NameTable::getNextTagNumOrder FLMUINT *  puiNextPos,
FLMUNICODE *  puzTagName,
char *  pszTagName,
FLMUINT  uiNameBufSize,
FLMUINT *  puiTagNum = NULL,
FLMUINT *  puiType = NULL,
FLMUINT *  puiSubType = NULL
 

Get the next item from the the table in dictionary number order.

This method allows an application to traverse through all of the items in a table in dictionary number order.

Parameters:
puiNextPos  Points to a variable that keeps the position of the last item returned. Returns the position of the next item. Initialize to zero to retrieve the first item.
puzTagName  If non-NULL, name is returned here as a Unicode string.
pszTagName  If non-NULL, name is returned here as an ASCII string. NOTE: If both pszTagName and puzTagName are non-NULL, only puzTagName will be populated.
uiNameBufSize  Size, in bytes, of the puzTagName or pszTagName buffer. Needs to be big enough include a null terminator character.
puiTagNum  If non-NULL, dictionary number for item is returned here.
puiType  If non-NULL, dictionary type (field, index, container) is returned here.
puiSubType  If non-NULL, dictionary sub-type is returned here. NOTE: This only applies to field items, in which case the sub-type is the data type for the field.

RCODE F_NameTable::setupFromDb HFDB  hDb  ) 
 

Populate a name table from the dictionary of the specified database.

Parameters:
hDb  Database whose dictionary is to be used to populate the name table.

void F_NameTable::sortTags void   ) 
 

Sort the items in the table.

This method is typically called after adding a group of items to the table via the F_NameTable::addTag() method.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___name_table-members.html0000644000175000017500000001074210510774540026312 0ustar ahodgkinsonahodgkinson FLAIM: Member List

F_NameTable Member List

This is the complete list of members for F_NameTable, including all inherited members.

addTag(const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT uiTagNum, FLMUINT uiType, FLMUINT uiSubType, FLMBOOL bCheckDuplicates=TRUE)F_NameTable
clearTable(void)F_NameTable
getFromTagName(const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT *puiTagNum, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)F_NameTable
getFromTagNum(FLMUINT uiTagNum, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)F_NameTable
getFromTagType(FLMUINT uiType, FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiSubType=NULL)F_NameTable
getFromTagTypeAndName(const FLMUNICODE *puzTagName, const char *pszTagName, FLMUINT uiType, FLMUINT *puiTagNum, FLMUINT *puiSubType=NULL)F_NameTable
getNextTagNameOrder(FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)F_NameTable
getNextTagNumOrder(FLMUINT *puiNextPos, FLMUNICODE *puzTagName, char *pszTagName, FLMUINT uiNameBufSize, FLMUINT *puiTagNum=NULL, FLMUINT *puiType=NULL, FLMUINT *puiSubType=NULL)F_NameTable
setupFromDb(HFDB hDb)F_NameTable
sortTags(void)F_NameTable


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___restore.html0000644000175000017500000006131310510774540024256 0ustar ahodgkinsonahodgkinson FLAIM: F_Restore Class Reference

F_Restore Class Reference

This is an abstract base class for reading backup data during a FlmDbRestore() operation. More...

#include <flaim.h>

List of all members.

Public Member Functions

virtual RCODE openBackupSet (void)=0
 FlmDbRestore() calls this method to give the application an opportunity to open the backup media, if needed.
virtual RCODE openIncFile (FLMUINT uiIncBackupSeqNum)=0
 FlmDbRestore() calls this method to tell the application to open an incremental backup.
virtual RCODE openRflFile (FLMUINT uiRFLFileNum)=0
 FlmDbRestore() calls this method to tell the application to open an RFL file that it wants to replay to recover transactions.
virtual RCODE read (FLMUINT uiLength, void *pvBuffer, FLMUINT *puiBytesRead)=0
 Read data from the current file.
virtual RCODE close (void)=0
 Close the current file.
virtual RCODE abortFile (void)=0
 Abort the current file.
virtual RCODE processUnknown (F_UnknownStream *pUnkStrm)=0
 Process an "unknown" object that was encountered while reading an RFL file.
virtual RCODE status (eRestoreStatusType eStatusType, FLMUINT uiTransId, void *pvValue1, void *pvValue2, void *pvValue3, eRestoreActionType *peRestoreAction)=0
 FlmDbRestore() calls this method to report restore progress.


Detailed Description

This is an abstract base class for reading backup data during a FlmDbRestore() operation.

This class must be implemented by an application. The FlmDbRestore() function calls methods of this class to read backup data from the backup medium. This object and the write callback function of FlmDbBackup() (see its fnWrite parameter) allow an application to have complete control over writing and reading of backup data. Backup data could be streamed directly to a tape device, or any other media the application chooses.


Member Function Documentation

virtual RCODE F_Restore::abortFile void   )  [pure virtual]
 

Abort the current file.

The current "file" will either be the regular backup file set, an incremental backup, or an RFL file. The action to be taken by the application is similar to what it should do for a call to the F_Restore::close() method. The subtle distinction lies in the fact that FlmDbRestore() encountered some kind of error and may want to retry. For most implementations, it will be sufficient to simply call the F_Restore::close() method from within this method.

virtual RCODE F_Restore::close void   )  [pure virtual]
 

Close the current file.

The current "file" will either be the regular backup file set, an incremental backup, or an RFL file.

virtual RCODE F_Restore::openBackupSet void   )  [pure virtual]
 

FlmDbRestore() calls this method to give the application an opportunity to open the backup media, if needed.

Subsequent calls to the F_Restore::read() method should return data from the backup that is being restored. When FlmDbRestore() is done reading from the backup set, it will call the F_Restore::close() method.

virtual RCODE F_Restore::openIncFile FLMUINT  uiIncBackupSeqNum  )  [pure virtual]
 

FlmDbRestore() calls this method to tell the application to open an incremental backup.

FlmDbRestore() attempts to restore incremental backups after it is finished restoring the regular (non-incremental backup). It will restore incremental backups until this method returns RCODE::FERR_IO_PATH_NOT_FOUND. This method should return RCODE::FERR_IO_PATH_NOT_FOUND if the requested incremental backup does not exist, or if it does not want FlmDbRestore() to restore any more incremental backups. If this method returns FERR_OK, subsequent calls to the F_Restore::read() method are expected to return data from the incremental backup. When FlmDbRestore() is done reading from the incremental backup, it will call the F_Restore::close() method.

Parameters:
uiIncBackupSeqNum  Sequence number of incremental backup that is to be opened.

virtual RCODE F_Restore::openRflFile FLMUINT  uiRFLFileNum  )  [pure virtual]
 

FlmDbRestore() calls this method to tell the application to open an RFL file that it wants to replay to recover transactions.

FlmDbRestore() attempts to restore RFL files after it is finished restoring the regular backup and any incremental backups. It will restore RFL files until this method returns RCODE::FERR_IO_PATH_NOT_FOUND. This method should return RCODE_FERR_IO_PATH_NOT_FOUND if the requested RFL file file does not exist, or if it does not want FlmDbRestore() to restore any more RFL files. If this method returns FERR_OK, subsequent calls to the F_Restore::read() method are expected to return data from the RFL file. When FlmDbRestore() is done reading from the RFL file, it will call the F_Restore::close() method.

Parameters:
uiRFLFileNum  Sequence number of RFL file that is to be opened.

virtual RCODE F_Restore::processUnknown F_UnknownStream pUnkStrm  )  [pure virtual]
 

Process an "unknown" object that was encountered while reading an RFL file.

When FlmDbRestore calls this method, it passes an F_UnknownStream object to the application, which allows the application to read the unknown data from the RFL file and process it as needed. Unknown data in the RFL file is data put there by the application as part of a transaction. It is generally data that is in some way associated with data in the database, but is not actually stored in the database. Even though it is not stored in the database, it is desireable to restore it during a FlmDbRestore() operation.

Parameters:
pUnkStrm  Object that allows the application to read the unknown data.

virtual RCODE F_Restore::read FLMUINT  uiLength,
void *  pvBuffer,
FLMUINT *  puiBytesRead
[pure virtual]
 

Read data from the current file.

The current "file" will either be the regular backup file set, an incremental backup, or an RFL file.

Parameters:
uiLength  Number of bytes of data to read.
pvBuffer  Buffer to place read data into.
puiBytesRead  Returns the actual number of bytes read.

virtual RCODE F_Restore::status eRestoreStatusType  eStatusType,
FLMUINT  uiTransId,
void *  pvValue1,
void *  pvValue2,
void *  pvValue3,
eRestoreActionType peRestoreAction
[pure virtual]
 

FlmDbRestore() calls this method to report restore progress.

An application may abort the database restore operation by returning RESTORE_ACTION_STOP in *puiAction.

Parameters:
eStatusType  Restore status types.
uiTransId  Transaction id of RFL operation being restored.
pvValue1  Value for RFL operation being restored - details defined for each eRestoreStatusType.
pvValue2  Value for RFL operation being restored - details defined for each eRestoreStatusType.
pvValue3  Value for RFL operation being restored - details defined for each eRestoreStatusType.
peRestoreAction  Action the application wants FlmDbRestore() to take.


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___restore-members.html0000644000175000017500000000675710510774540025721 0ustar ahodgkinsonahodgkinson FLAIM: Member List

F_Restore Member List

This is the complete list of members for F_Restore, including all inherited members.

abortFile(void)=0F_Restore [pure virtual]
close(void)=0F_Restore [pure virtual]
openBackupSet(void)=0F_Restore [pure virtual]
openIncFile(FLMUINT uiIncBackupSeqNum)=0F_Restore [pure virtual]
openRflFile(FLMUINT uiRFLFileNum)=0F_Restore [pure virtual]
processUnknown(F_UnknownStream *pUnkStrm)=0F_Restore [pure virtual]
read(FLMUINT uiLength, void *pvBuffer, FLMUINT *puiBytesRead)=0F_Restore [pure virtual]
status(eRestoreStatusType eStatusType, FLMUINT uiTransId, void *pvValue1, void *pvValue2, void *pvValue3, eRestoreActionType *peRestoreAction)=0F_Restore [pure virtual]


Generated on Wed Oct 4 12:11:42 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___unknown_stream.html0000644000175000017500000002021010510774540025634 0ustar ahodgkinsonahodgkinson FLAIM: F_UnknownStream Class Reference

F_UnknownStream Class Reference

This is an abstract base class that allows an application to read "unknown" data from the RFL or to write "unknown" data to the RFL. More...

#include <flaim.h>

List of all members.

Public Member Functions

virtual RCODE read (FLMUINT uiLength, void *pvBuffer, FLMUINT *puiBytesRead)=0
 Read data for an "unknown" object from the RFL.
virtual RCODE write (FLMUINT uiLength, void *pvBuffer)=0
 Write data to an "unknown" object in the RFL.
virtual RCODE close (void)=0
 Close the stream.


Detailed Description

This is an abstract base class that allows an application to read "unknown" data from the RFL or to write "unknown" data to the RFL.

The application must implement this class.


Member Function Documentation

virtual RCODE F_UnknownStream::close void   )  [pure virtual]
 

Close the stream.

If this is an input stream (read-only), the object should read to the end of the stream, discarding any remaining data.

virtual RCODE F_UnknownStream::read FLMUINT  uiLength,
void *  pvBuffer,
FLMUINT *  puiBytesRead
[pure virtual]
 

Read data for an "unknown" object from the RFL.

Parameters:
uiLength  Number of bytes to read.
pvBuffer  Buffer to place read bytes into.
puiBytesRead  Number of bytes actually read.

virtual RCODE F_UnknownStream::write FLMUINT  uiLength,
void *  pvBuffer
[pure virtual]
 

Write data to an "unknown" object in the RFL.

Parameters:
uiLength  Number of bytes to write.
pvBuffer  Data to be written out.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_f___unknown_stream-members.html0000644000175000017500000000435210510774540027275 0ustar ahodgkinsonahodgkinson FLAIM: Member List

F_UnknownStream Member List

This is the complete list of members for F_UnknownStream, including all inherited members.

close(void)=0F_UnknownStream [pure virtual]
read(FLMUINT uiLength, void *pvBuffer, FLMUINT *puiBytesRead)=0F_UnknownStream [pure virtual]
write(FLMUINT uiLength, void *pvBuffer)=0F_UnknownStream [pure virtual]


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_c_u_r_s_o_r___s_u_b_q_u_e_r_y___s_t_a_t_u_s.html0000644000175000017500000002700310510774540033304 0ustar ahodgkinsonahodgkinson FLAIM: FCURSOR_SUBQUERY_STATUS Struct Reference

FCURSOR_SUBQUERY_STATUS Struct Reference

Structure that reports query processing progress. More...

#include <flaim.h>

List of all members.

Public Attributes

HFDB hDb
 Database handle.
FLMUINT uiContainerNum
 Container number records are being retrieved from.
FLMUINT uiIndexNum
 Index being used, if any. Zero if none.
FLMUINT uiProcessedCnt
 Total records processed so far.
FLMUINT uiMatchedCnt
 Total records that matched the query criteria so far.
FLMUINT uiNumRejectedByCallback
 Total records rejected by the record validator callback function. The record validator callback function is set by calling FlmCursorConfig() using the eCursorConfigType::FCURSOR_SET_REC_VALIDATOR option.
FLMUINT uiDupsEliminated
 Total duplicate records eliminated.
FLMUINT uiKeysTraversed
 Total index keys traversed.
FLMUINT uiKeysRejected
 Total index keys rejected.
FLMUINT uiRefsTraversed
 Total index key references traversed.
FLMUINT uiRefsRejected
 Total index key references rejected.
FLMUINT uiRecsFetchedForEval
 Total records read from the container to be evaluated.
FLMUINT uiRecsRejected
 Total records rejected.
FLMUINT uiRecsFetchedForView
 Total records retrieved after key passes criteria.
FLMUINT uiRecsNotFound
 Total records that were pointed to from an index key that could not be found. NOTE: If this number is non-zero, there may be a logical index corruption in the database.


Detailed Description

Structure that reports query processing progress.

This is returned to the callback function that is set by the FlmCursorConfig() function using the eCursorConfigType::FCURSOR_SET_STATUS_HOOK option. The callback function is called from within FlmCursorFirst(), FlmCursorLast(), FlmCursorNext(), FlmCursorPrev(), and any other functions that retrieve records and evaluates them against the query criteria. This structure is passed to the callback function when the eStatusType::FLM_SUBQUERY_STATUS status is reported.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_c_u_r_s_o_r___s_u_b_q_u_e_r_y___s_t_a_t_u_s-members.html0000644000175000017500000001334310510774540034736 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FCURSOR_SUBQUERY_STATUS Member List

This is the complete list of members for FCURSOR_SUBQUERY_STATUS, including all inherited members.

hDbFCURSOR_SUBQUERY_STATUS
uiContainerNumFCURSOR_SUBQUERY_STATUS
uiDupsEliminatedFCURSOR_SUBQUERY_STATUS
uiIndexNumFCURSOR_SUBQUERY_STATUS
uiKeysRejectedFCURSOR_SUBQUERY_STATUS
uiKeysTraversedFCURSOR_SUBQUERY_STATUS
uiMatchedCntFCURSOR_SUBQUERY_STATUS
uiNumRejectedByCallbackFCURSOR_SUBQUERY_STATUS
uiProcessedCntFCURSOR_SUBQUERY_STATUS
uiRecsFetchedForEvalFCURSOR_SUBQUERY_STATUS
uiRecsFetchedForViewFCURSOR_SUBQUERY_STATUS
uiRecsNotFoundFCURSOR_SUBQUERY_STATUS
uiRecsRejectedFCURSOR_SUBQUERY_STATUS
uiRefsRejectedFCURSOR_SUBQUERY_STATUS
uiRefsTraversedFCURSOR_SUBQUERY_STATUS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_i_n_d_e_x___s_t_a_t_u_s.html0000644000175000017500000001326710510774540027260 0ustar ahodgkinsonahodgkinson FLAIM: FINDEX_STATUS Struct Reference

FINDEX_STATUS Struct Reference

Structure returned from FlmIndexStatus() to report current status of an index. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiIndexNum
 Index number.
FLMBOOL bSuspended
 If TRUE, index is suspended.
FLMUINT uiStartTime
 Time (GMT) that the background indexing thread started - if zero, no background thread is running.
FLMUINT uiLastRecordIdIndexed
 If RECID_UNDEFINED then index is online, otherwise this is the value of the last DRN that was indexed.
FLMUINT uiKeysProcessed
 Keys processed by background indexing thread.
FLMUINT uiRecordsProcessed
 Records processed by background indexing thread.
FLMUINT uiTransactions
 Number of transactions started by the background indexing thread.


Detailed Description

Structure returned from FlmIndexStatus() to report current status of an index.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_i_n_d_e_x___s_t_a_t_u_s-members.html0000644000175000017500000000607610510774540030710 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FINDEX_STATUS Member List

This is the complete list of members for FINDEX_STATUS, including all inherited members.

bSuspendedFINDEX_STATUS
uiIndexNumFINDEX_STATUS
uiKeysProcessedFINDEX_STATUS
uiLastRecordIdIndexedFINDEX_STATUS
uiRecordsProcessedFINDEX_STATUS
uiStartTimeFINDEX_STATUS
uiTransactionsFINDEX_STATUS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___c_a_c_h_e___u_s_a_g_e.html0000644000175000017500000001614410510774540027760 0ustar ahodgkinsonahodgkinson FLAIM: FLM_CACHE_USAGE Struct Reference

FLM_CACHE_USAGE Struct Reference

Structure that holds cache usage statistics. The statistics will be for either block cache or record cache. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiMaxBytes
 Maximum bytes allowed in cache.
FLMUINT uiTotalBytesAllocated
 Total bytes currently allocated in cache.
FLMUINT uiCount
 Number of items cached (blocks or records).
FLMUINT uiOldVerCount
 Number of items cached that are prior versions.
FLMUINT uiOldVerBytes
 Total bytes in prior versions.
FLMUINT uiCacheHits
 Total number of times an item was found in cache.
FLMUINT uiCacheHitLooks
 Total number of items traversed to find items in cache.
FLMUINT uiCacheFaults
 Total number of times an item was not found in cache.
FLMUINT uiCacheFaultLooks
 Total number of items traversed to determine that an item was not in cache.
FLM_SLAB_USAGE SlabUsage
 Slab usage statistics.


Detailed Description

Structure that holds cache usage statistics. The statistics will be for either block cache or record cache.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___c_a_c_h_e___u_s_a_g_e-members.html0000644000175000017500000000756110510774540031413 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_CACHE_USAGE Member List

This is the complete list of members for FLM_CACHE_USAGE, including all inherited members.

SlabUsageFLM_CACHE_USAGE
uiCacheFaultLooksFLM_CACHE_USAGE
uiCacheFaultsFLM_CACHE_USAGE
uiCacheHitLooksFLM_CACHE_USAGE
uiCacheHitsFLM_CACHE_USAGE
uiCountFLM_CACHE_USAGE
uiMaxBytesFLM_CACHE_USAGE
uiOldVerBytesFLM_CACHE_USAGE
uiOldVerCountFLM_CACHE_USAGE
uiTotalBytesAllocatedFLM_CACHE_USAGE


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___m_e_m___i_n_f_o.html0000644000175000017500000002666510510774540026676 0ustar ahodgkinsonahodgkinson FLAIM: FLM_MEM_INFO Struct Reference

FLM_MEM_INFO Struct Reference

Structure returned from FlmGetMemoryInfo(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMBOOL bDynamicCacheAdjust
 Flag indicating if FLAIM is using a dynamic cache limit or a hard cache limit. TRUE if dynamic.
FLMUINT uiCacheAdjustPercent
 If using a dynamic cache limit, this will tell the percent of available memory to use for the limit.
FLMUINT uiCacheAdjustMin
 If using a dynamic cache limit, this will tell the minimum limit (in bytes) that can be set.
FLMUINT uiCacheAdjustMax
 If using a dynamic cache limit, this will tell the maximum limit (in bytes) that can be set.
FLMUINT uiCacheAdjustMinToLeave
 If using a dynamic cache limit, this tells the minimum amount of memory that must be left after setting a limit. NOTE: This is only used if FLM_MEM_INFO::uiCacheAdjustMax is zero.
FLMUINT uiDirtyCount
 Number of blocks in block cache that are currently dirty.
FLMUINT uiDirtyBytes
 Total number of bytes in block cache that are currently dirty.
FLMUINT uiNewCount
 Number of blocks in block cache that are new blocks - blocks that were created new at the end of the database.
FLMUINT uiNewBytes
 Total number of bytes for the new blocks.
FLMUINT uiLogCount
 Total number of blocks in the block cache that need to be logged to the rollback log.
FLMUINT uiLogBytes
 Total number of bytes in the log blocks.
FLMUINT uiFreeCount
 Total number of blocks in the block cache that are no longer associated with a particular database. They can be reused.
FLMUINT uiFreeBytes
 Total number of bytes in the free blocks.
FLMUINT uiReplaceableCount
 Total number of blocks that can be replaced without having to write them to disk.
FLMUINT uiReplaceableBytes
 Total number of bytes in the replaceable blocks.
FLM_CACHE_USAGE RecordCache
 Record cache usage statistics.
FLM_CACHE_USAGE BlockCache
 Block cache usage statistics.


Detailed Description

Structure returned from FlmGetMemoryInfo().


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___m_e_m___i_n_f_o-members.html0000644000175000017500000001251510510774540030313 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_MEM_INFO Member List

This is the complete list of members for FLM_MEM_INFO, including all inherited members.

bDynamicCacheAdjustFLM_MEM_INFO
BlockCacheFLM_MEM_INFO
RecordCacheFLM_MEM_INFO
uiCacheAdjustMaxFLM_MEM_INFO
uiCacheAdjustMinFLM_MEM_INFO
uiCacheAdjustMinToLeaveFLM_MEM_INFO
uiCacheAdjustPercentFLM_MEM_INFO
uiDirtyBytesFLM_MEM_INFO
uiDirtyCountFLM_MEM_INFO
uiFreeBytesFLM_MEM_INFO
uiFreeCountFLM_MEM_INFO
uiLogBytesFLM_MEM_INFO
uiLogCountFLM_MEM_INFO
uiNewBytesFLM_MEM_INFO
uiNewCountFLM_MEM_INFO
uiReplaceableBytesFLM_MEM_INFO
uiReplaceableCountFLM_MEM_INFO


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___r_f_l___s_i_z_e___e_v_e_n_t.html0000644000175000017500000000613010510774540031176 0ustar ahodgkinsonahodgkinson FLAIM: FLM_RFL_SIZE_EVENT Struct Reference

FLM_RFL_SIZE_EVENT Struct Reference

Structure returned to an event handler function whenever RFL size events occur. Specifically, this structure is returned whenever the RFL exceeds the on-disk size threshold specified for a database. More...

#include <flaim.h>

List of all members.

Public Attributes

const char * pszRflDir
 RFL directory path.
FLMUINT64 ui64RflDiskUsage
 Size of the RFL.


Detailed Description

Structure returned to an event handler function whenever RFL size events occur. Specifically, this structure is returned whenever the RFL exceeds the on-disk size threshold specified for a database.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___r_f_l___s_i_z_e___e_v_e_n_t-members.html0000644000175000017500000000371310510774540032632 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_RFL_SIZE_EVENT Member List

This is the complete list of members for FLM_RFL_SIZE_EVENT, including all inherited members.

pszRflDirFLM_RFL_SIZE_EVENT
ui64RflDiskUsageFLM_RFL_SIZE_EVENT


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___s_t_a_t_s.html0000644000175000017500000001436710510774540025602 0ustar ahodgkinsonahodgkinson FLAIM: FLM_STATS Struct Reference

FLM_STATS Struct Reference

FLAIM statistics returned from FlmGetStats(). More...

#include <flaim.h>

List of all members.

Public Attributes

F_MUTEX hMutex
 Mutex for controlling access to this structure - only used internally.
DB_STATSpDbStats
 Pointer to array of database statistics. There will be a DB_STATS structure for every database currently open.
FLMUINT uiDBAllocSeq
 Allocation sequence number for the pDbStats array - only used internally.
FLMUINT uiDbStatArraySize
 Size of the pDbStats array.
FLMUINT uiNumDbStats
 Number of elements in the pDbStats array that are currently in use.
FLMBOOL bCollectingStats
 Flag indicating whether or not we are currently collecting statistics.
FLMUINT uiStartTime
 Time we started collecting statistics. This is GMT time - seconds since January 1, 1970 midnight.
FLMUINT uiStopTime
 Time we stopped collecting statistics. This is GMT time - seconds since January 1, 1970 midnight.


Detailed Description

FLAIM statistics returned from FlmGetStats().


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___s_t_a_t_s-members.html0000644000175000017500000000615210510774540027223 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_STATS Member List

This is the complete list of members for FLM_STATS, including all inherited members.

bCollectingStatsFLM_STATS
hMutexFLM_STATS
pDbStatsFLM_STATS
uiDBAllocSeqFLM_STATS
uiDbStatArraySizeFLM_STATS
uiNumDbStatsFLM_STATS
uiStartTimeFLM_STATS
uiStopTimeFLM_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___t_r_a_n_s___e_v_e_n_t.html0000644000175000017500000001022210510774540030050 0ustar ahodgkinsonahodgkinson FLAIM: FLM_TRANS_EVENT Struct Reference

FLM_TRANS_EVENT Struct Reference

Structure returned to an event handler function whenever transaction events occur. Specifically, this structure is returned for transaction begin, commit, and abort events. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiThreadId
 Thread id of thread that generated the event.
HFDB hDb
 Database handle of database that generated the event.
FLMUINT uiTransID
 Transaction id.
RCODE rc
 Return code of the event.


Detailed Description

Structure returned to an event handler function whenever transaction events occur. Specifically, this structure is returned for transaction begin, commit, and abort events.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___t_r_a_n_s___e_v_e_n_t-members.html0000644000175000017500000000457210510774540031513 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_TRANS_EVENT Member List

This is the complete list of members for FLM_TRANS_EVENT, including all inherited members.

hDbFLM_TRANS_EVENT
rcFLM_TRANS_EVENT
uiThreadIdFLM_TRANS_EVENT
uiTransIDFLM_TRANS_EVENT


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___u_p_d_a_t_e___e_v_e_n_t.html0000644000175000017500000001464010510774540030352 0ustar ahodgkinsonahodgkinson FLAIM: FLM_UPDATE_EVENT Struct Reference

FLM_UPDATE_EVENT Struct Reference

Structure returned to an event handler function whenever database update events occur. Specifically, this structure is returned for record add, modify, and delete events, as well as DRN reserve events. More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiThreadId
 Thread id of the thread that generated the event.
HFDB hDb
 Database handle of the database that generated the event.
FLMUINT uiTransID
 Transaction id the update occurred in.
RCODE rc
 Return code of the update event.
FLMUINT uiDrn
 DRN of the record that was added, modified, deleted, or reserved.
FLMUINT uiContainer
 Container number where the update occurred.
FlmRecordpNewRecord
 New record (adds, modifies).
FlmRecordpOldRecord
 Old record (modifies, deletes).


Detailed Description

Structure returned to an event handler function whenever database update events occur. Specifically, this structure is returned for record add, modify, and delete events, as well as DRN reserve events.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_l_m___u_p_d_a_t_e___e_v_e_n_t-members.html0000644000175000017500000000657610510774540032013 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FLM_UPDATE_EVENT Member List

This is the complete list of members for FLM_UPDATE_EVENT, including all inherited members.

hDbFLM_UPDATE_EVENT
pNewRecordFLM_UPDATE_EVENT
pOldRecordFLM_UPDATE_EVENT
rcFLM_UPDATE_EVENT
uiContainerFLM_UPDATE_EVENT
uiDrnFLM_UPDATE_EVENT
uiThreadIdFLM_UPDATE_EVENT
uiTransIDFLM_UPDATE_EVENT


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_blob.html0000644000175000017500000002024510510774540023543 0ustar ahodgkinsonahodgkinson FLAIM: FlmBlob Class Reference

FlmBlob Class Reference

This class provides an interface for handling binary large objects. More...

#include <flaim.h>

List of all members.

Public Member Functions

virtual RCODE referenceFile (HFDB hDb, const char *pszFilePath, FLMBOOL bOwned=FALSE)=0
 Setup the blob object to reference an external file.
virtual FLMINT compareFileName (const char *pszFileName)=0
 Compare the file name refered to by the BLOB object with the passed in file name.
virtual RCODE buildFileName (char *pszFileName)=0
 Return the file name referred to by the BLOB object.


Detailed Description

This class provides an interface for handling binary large objects.

Currently, FLAIM only supports referencing of external files. BLOB data is not actually stored "in" the database.


Member Function Documentation

virtual RCODE FlmBlob::buildFileName char *  pszFileName  )  [pure virtual]
 

Return the file name referred to by the BLOB object.

Parameters:
pszFileName  File name is returned here.

virtual FLMINT FlmBlob::compareFileName const char *  pszFileName  )  [pure virtual]
 

Compare the file name refered to by the BLOB object with the passed in file name.

Will return zero if the file names are equal, non-zero otherwise.

Parameters:
pszFileName  File name to compare to.

virtual RCODE FlmBlob::referenceFile HFDB  hDb,
const char *  pszFilePath,
FLMBOOL  bOwned = FALSE
[pure virtual]
 

Setup the blob object to reference an external file.

Parameters:
hDb  Database handle.
pszFilePath  Name of file the blob is to reference.
bOwned  Is the external file "owned" by the database? If TRUE, when the record referencing the BLOB is deleted, FLAIM will automatically delete the file.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_blob-members.html0000644000175000017500000000423310510774540025172 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FlmBlob Member List

This is the complete list of members for FlmBlob, including all inherited members.

buildFileName(char *pszFileName)=0FlmBlob [pure virtual]
compareFileName(const char *pszFileName)=0FlmBlob [pure virtual]
referenceFile(HFDB hDb, const char *pszFilePath, FLMBOOL bOwned=FALSE)=0FlmBlob [pure virtual]


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_record.html0000644000175000017500000052563110510774540024114 0ustar ahodgkinsonahodgkinson FLAIM: FlmRecord Class Reference

FlmRecord Class Reference

Class for creating and modifying database records. More...

#include <flaim.h>

List of all members.

Public Member Functions

void * operator new (FLMSIZET uiSize) throw ()
 Overloaded new operator for FlmRecord objects.
void * operator new (FLMSIZET uiSize, const char *pszFile, int iLine) throw ()
 Overloaded new operator for ::FlmRecord objects (with source file and line number).
void * operator new[] (FLMSIZET uiSize) throw ()
 Overloaded new operator (array) for FlmRecord objects.
void * operator new[] (FLMSIZET uiSize, const char *pszFile, int iLine) throw ()
 Overloaded new operator (array) for FlmRecord objects (with source file and line number).
void operator delete (void *ptr)
 Overloaded delete operator for FlmRecord objects. Pointer to FlmRecord object being freed.
void operator delete[] (void *ptr)
 Overloaded delete operator (array) for FlmRecord objects. Pointer to array of FlmRecord objects being freed.
FLMINT FLMAPI AddRef (void)
 Increment the reference count for this FlmRecord object.
FINLINE FLMINT FLMAPI Release (void)
 Decrement the reference count for this FlmRecord object.
FlmRecordcopy (void)
 Make a writeable copy of this FlmRecord object.
RCODE clear (FLMBOOL bReleaseMemory=FALSE)
 Clear all fields in this FlmRecord object.
RCODE getINT (void *pvField, FLMINT *piNumber)
 Get a field's value as a FLMINT. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
RCODE getINT32 (void *pvField, FLMINT32 *pi32Number)
 Get a field's value as a FLMINT32. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
RCODE getINT64 (void *pvField, FLMINT64 *pi64Number)
 Get a field's value as a FLMINT64. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
RCODE getUINT (void *pvField, FLMUINT *puiNumber)
 Get a field's value as a FLMUINT. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
RCODE getUINT32 (void *pvField, FLMUINT32 *pui32Number)
 Get a field's value as a FLMUINT32. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
RCODE getUINT64 (void *pvField, FLMUINT64 *pui64Number)
 Get a field's value as a FLMUINT64. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.
FINLINE RCODE getUnicodeLength (void *pvField, FLMUINT *puiLength)
 Get the number of bytes needed to retrieve a field's value as a unicode string.
RCODE getUnicode (void *pvField, FLMUNICODE *puzStrBuf, FLMUINT *puiStrBufLen)
 Get a field's value as a unicode string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.
FINLINE RCODE getNativeLength (void *pvField, FLMUINT *puiLength)
 Get the number of bytes needed to retrieve a field's value as a native string.
RCODE getNative (void *pvField, char *pszStrBuf, FLMUINT *puiStrBufLen)
 Get a field's value as a native string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.
FINLINE RCODE getBinaryLength (void *pvField, FLMUINT *puiLength)
 Get the number of bytes needed to retrieve a field's value as binary data.
RCODE getRecPointer (void *pvField, FLMUINT *puiRecPointer)
 Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT.
FINLINE RCODE getRecPointer32 (void *pvField, FLMUINT32 *pui32RecPointer)
 Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT32.
RCODE getBinary (void *pvField, void *pvBuf, FLMUINT *puiBufLen)
 Get a field's value as binary data.
RCODE getBlob (void *pvField, FlmBlob **ppBlob)
 Get a field's value as a FlmBlob object. This method may only be used on field's whose data type is FLM_BLOB_TYPE.
RCODE setINT (void *pvField, FLMINT iNumber, FLMUINT uiEncId=0)
 Set a field's value to a FLMINT value. The resulting data type for the field will be FLM_NUMBER_TYPE.
RCODE setINT64 (void *pvField, FLMINT64 i64Number, FLMUINT uiEncId=0)
 Set a field's value to a FLMINT64 value. The resulting data type for the field will be FLM_NUMBER_TYPE.
RCODE setUINT (void *pvField, FLMUINT uiNumber, FLMUINT uiEncId=0)
 Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_NUMBER_TYPE.
RCODE setUINT64 (void *pvField, FLMUINT64 ui64Number, FLMUINT uiEncId=0)
 Set a field's value to a FLMUINT64 value. The resulting data type for the field will be FLM_NUMBER_TYPE.
RCODE setRecPointer (void *pvField, FLMUINT uiRecPointer, FLMUINT uiEncId=0)
 Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_CONTEXT_TYPE.
RCODE setUnicode (void *pvField, const FLMUNICODE *puzUnicode, FLMUINT uiEncId=0)
 Set a field's value to a Unicode string value. The resulting data type for the field will be FLM_TEXT_TYPE.
RCODE setNative (void *pvField, const char *pszString, FLMUINT uiEncId=0)
 Set a field's value to a native string value. The resulting data type for the field will be FLM_TEXT_TYPE.
RCODE setBinary (void *pvField, const void *pvBuf, FLMUINT uiBufLen, FLMUINT uiEncId=0)
 Set a field's value to a binary value. The resulting data type for the field will be FLM_BINARY_TYPE.
RCODE setBlob (void *pvField, FlmBlob *pBlob, FLMUINT uiEncId=0)
 Set a field's value to a BLOB value. The resulting data type for the field will be FLM_BLOB_TYPE.
RCODE insert (void *pvField, FLMUINT uiInsertAt, FLMUINT uiFieldID, FLMUINT uiDataType, void **ppvField)
 Insert a new field into the FlmRecord object. Insertion is relative to an already existing field.
RCODE insertLast (FLMUINT uiLevel, FLMUINT uiFieldID, FLMUINT uiDataType, void **ppvField)
 Insert a new field into the FlmRecord object.
FINLINE RCODE remove (void *pvField)
 Remove a field from a record. The field and all of its descendant fields will be removed from the record.
FINLINE void * root (void)
 Return the root field of a record. The root field is the field that is at level zero in the record.
FINLINE void * nextSibling (void *pvField)
 Return the next sibling field of a field. Returns NULL if there is no next sibling field.
void * prevSibling (void *pvField)
 Return the previous sibling field of a field. Returns NULL if there is no previous sibling field.
FINLINE void * firstChild (void *pvField)
 Return the first child field of a field. Returns NULL if field has no child fields.
FINLINE void * lastChild (void *pvField)
 Return the last child field of a field. Returns NULL if field has no child fields.
FINLINE void * parent (void *pvField)
 Return the parent field of a field. Returns NULL if the field is the "root" field of the record.
FINLINE void * next (void *pvField)
 Return the "next" field of a field.
FINLINE void * prev (void *pvField)
 Return the "previous" field of a field.
void * find (void *pvStartField, FLMUINT uiFieldID, FLMUINT uiOccur=1, FLMUINT uiFindOption=SEARCH_FOREST)
 Find a field in a record that has a particular field number. Search is conducted relative to some other field in the record.
void * find (void *pvStartField, FLMUINT *puiFieldPath, FLMUINT uiOccur=1, FLMUINT uiFindOption=SEARCH_FOREST)
 Find a field in a record that has a particular field path. Search is conducted relative to some other field in the record.
FINLINE FLMUINT getLevel (void *pvField)
 Get the nesting level of a field in the record.
FINLINE FLMUINT getFieldID (void *pvField)
 Get the field number for a field.
FINLINE void setFieldID (void *pvField, FLMUINT uiFieldID)
 Set the field number for a field.
FINLINE FLMUINT getDataType (void *pvField)
 Get a field's data type.
FINLINE FLMUINT getDataLength (void *pvField)
 Get a field's data length. NOTE: This is the field's internal storage length.
FINLINE FLMBOOL hasChild (void *pvField)
 Determine if a field has any child fields.
FINLINE FLMBOOL isLast (void *pvField)
 Determine if a field has a next sibling field.
FINLINE void setRightTruncated (void *pvField, FLMBOOL bTrueFalse)
 Set a flag for a field indicating if its value has been "right truncated." Right truncation really only applies to binary data or strings.
FINLINE FLMBOOL isRightTruncated (void *pvField)
 Determine if field is marked as "right truncated." This really only applies to binary data or strings.
FINLINE void setLeftTruncated (void *pvField, FLMBOOL bTrueFalse)
 Set a flag for a field indicating if its value has been "left truncated." Left truncation really only applies to binary data or strings.
FINLINE FLMBOOL isLeftTruncated (void *pvField)
 Determine if field is marked as "left truncated." This really only applies to binary data or strings.
FINLINE RCODE getFieldInfo (void *pvField, FLMUINT *puiFieldNum, FLMUINT *puiLevel, FLMUINT *puiDataType, FLMUINT *puiLength, FLMUINT *puiEncLength, FLMUINT *puiEncId)
 Get information about a field.
FLMUINT getTotalMemory (void)
 Get the total memory being consumed by this FlmRecord object.
FINLINE FLMUINT getFreeMemory (void)
 Get the amount of memory allocated to the FlmRecord object that is currently not used.
RCODE compressMemory (void)
 Compress out unused memory within the FlmRecord object. See also FlmRecord::getFreeMemory().
FINLINE FLMUINT getID (void)
 Get the record ID (DRN) for this FlmRecord object.
FINLINE void setID (FLMUINT uiRecordID)
 Set the record ID (DRN) for this FlmRecord object.
FINLINE FLMUINT getContainerID (void)
 Get the container this FlmRecord object belongs to.
FINLINE void setContainerID (FLMUINT uiContainerID)
 Set the container this FlmRecord object belongs to.
RCODE importRecord (IF_FileHdl *pFileHdl, F_NameTable *pNameTable)
 Import a record from a file.
RCODE importRecord (const char **ppszBuffer, FLMUINT uiBufSize, F_NameTable *pNameTable)
 Import a record from a string buffer.
RCODE importRecord (NODE *pNode)
 Import a record from a Gedcom NODE tree.
RCODE exportRecord (HFDB hDb, F_Pool *pPool, NODE **ppNode)
 Export a record to a Gedcom NODE tree.
FINLINE FLMBOOL isReadOnly (void)
 Determine if the FlmRecord object is read-only.
FINLINE FLMBOOL isCached (void)
 Determine if the FlmRecord object is cached in FLAIM's record cache.
FINLINE FLMBOOL isOldVersion (void)
 Determine if the FlmRecord object is a prior version of the record or if it is the current version.
FINLINE const FLMBYTE * getDataPtr (void *pvField)
 Get a pointer to a field's data.
FINLINE FLMBOOL isEncryptedField (void *pvField)
 Determine if a field is encrypted.
FINLINE const FLMBYTE * getEncryptionDataPtr (void *pvField)
 Get a pointer to a field's encrypted data.
FINLINE FLMUINT getEncryptedDataLength (void *pvField)
 Get a field's encrypted data length.
FLMUINT getEncryptionID (void *pvField)
 Get a field's encryption id.
FLMUINT getEncFlags (void *pvField)
 Get a field's encryption flags.
FINLINE FLMBOOL fieldIdTableEnabled (void)
 Determine if a record has a level one field ID table.
FINLINE void enableFieldIdTable (void)
 Set a flag in the record that will cause it to generate a level one field ID table.
RCODE createFieldIdTable (FLMBOOL bTruncateTable)
 Create a level one field ID table in a record.
void * findLevelOneField (FLMUINT uiFieldID, FLMBOOL bFindInclusive, FLMUINT *puiFieldPos)
 Find a level one field ID in a record.
void * getLevelOneField (FLMUINT uiFieldId, FLMUINT uiLevelOnePosition)
 Determine if the level one field at the specified position in the field ID table matches the passed in field ID. If so, return that field pointer.
void * nextLevelOneField (FLMUINT *puiFieldPos, FLMBOOL bFieldIdsMustMatch)
 Get the next level one field after the position specified. Make sure that the next field has the same field ID as the current field.
FLMUINT getLevelOneFieldId (FLMUINT uiLevelOnePosition)
 Get the field ID of the field that is in the specified position in the field ID table.


Detailed Description

Class for creating and modifying database records.


Member Function Documentation

FLMINT FLMAPI FlmRecord::AddRef void   ) 
 

Increment the reference count for this FlmRecord object.

The reference count is the number of pointers that are referencing this FlmRecord object. Return value is the incremented reference count.

RCODE FlmRecord::clear FLMBOOL  bReleaseMemory = FALSE  ) 
 

Clear all fields in this FlmRecord object.

Parameters:
bReleaseMemory  If TRUE, free the memory buffer used to hold field data. If FALSE, the memory buffer will be cleared without freeing it.

RCODE FlmRecord::createFieldIdTable FLMBOOL  bTruncateTable  ) 
 

Create a level one field ID table in a record.

Parameters:
bTruncateTable  Truncate the field id table after sorting?

RCODE FlmRecord::exportRecord HFDB  hDb,
F_Pool *  pPool,
NODE **  ppNode
 

Export a record to a Gedcom NODE tree.

Parameters:
hDb  Database handle. The root node of the Gedcom tree will be associated with this handle.
pPool  Memory pool for allocating NODE structures and space for field data.
ppNode  Root of the Gedcom NODE tree will be returned here.

void* FlmRecord::find void *  pvStartField,
FLMUINT *  puiFieldPath,
FLMUINT  uiOccur = 1,
FLMUINT  uiFindOption = SEARCH_FOREST
 

Find a field in a record that has a particular field path. Search is conducted relative to some other field in the record.

Parameters:
pvStartField  field where search is to start.
puiFieldPath  Field path being searched for.
uiOccur  Occurrence being searched for.
uiFindOption  Specifies how much of the record to search in (relative to pvStartField). It may be one of the following:
  • SEARCH_FOREST - Search the sub-tree beginning at pvStartField, and all subtrees that are next siblings to pvStartField
  • SEARCH_TREE - Search only the sub-tree beginning at pvStartField

void* FlmRecord::find void *  pvStartField,
FLMUINT  uiFieldID,
FLMUINT  uiOccur = 1,
FLMUINT  uiFindOption = SEARCH_FOREST
 

Find a field in a record that has a particular field number. Search is conducted relative to some other field in the record.

Parameters:
pvStartField  Field where search is to start.
uiFieldID  Field number being searched for.
uiOccur  Occurrence being searched for.
uiFindOption  Specifies how much of the record to search in (relative to pvStartField). It may be one of the following:
  • SEARCH_FOREST - Search the sub-tree beginning at pvStartField, and all subtrees that are next siblings to pvStartField
  • SEARCH_TREE - Search only the sub-tree beginning at pvStartField

void* FlmRecord::findLevelOneField FLMUINT  uiFieldID,
FLMBOOL  bFindInclusive,
FLMUINT *  puiFieldPos
 

Find a level one field ID in a record.

Parameters:
uiFieldID  Field number of field to be found.
bFindInclusive  OK to find next field after uiFieldID?
puiFieldPos  Field position in field ID table is returned here.

FINLINE void* FlmRecord::firstChild void *  pvField  )  [inline]
 

Return the first child field of a field. Returns NULL if field has no child fields.

Parameters:
pvField  Field whose first child is to be returned.

RCODE FlmRecord::getBinary void *  pvField,
void *  pvBuf,
FLMUINT *  puiBufLen
 

Get a field's value as binary data.

This method may be used for any type of field, but it really only makes sense for fields whose data type is FLM_BINARY_TYPE. If it is used on a field whose data type is not FLM_BINARY_TYPE, the data returned is in FLAIM's internal storage format.

Parameters:
pvField  Field whose value is to be returned as binary data.
pvBuf  Buffer to hold the binary data.
puiBufLen  On input *puiBufLen is the size of pvBuf (in bytes). On output, it returns the number of bytes in the value. NOTE: If the value length is greater than the *puiBufLen that was passed in, the returned value will be truncated to fit in that buffer. However, no error will be returned.

FINLINE RCODE FlmRecord::getBinaryLength void *  pvField,
FLMUINT *  puiLength
[inline]
 

Get the number of bytes needed to retrieve a field's value as binary data.

This method may be called for any type of field, but it really only makes sense for fields whose data type is FLM_BINARY_TYPE.

Parameters:
pvField  Field whose length is to be returned.
puiLength  Field's length is returned here. NOTE: For fields whose data type is not FLM_BINARY_TYPE, this returns the internal storage length. It really doesn't make much sense to call this method for fields whose data type is not FLM_BINARY_TYPE.

RCODE FlmRecord::getBlob void *  pvField,
FlmBlob **  ppBlob
 

Get a field's value as a FlmBlob object. This method may only be used on field's whose data type is FLM_BLOB_TYPE.

Parameters:
pvField  Field whose value is to be returned as a FlmBlob object.
ppBlob  A pointer to the FlmBlob object is returned here.

FINLINE FLMUINT FlmRecord::getDataLength void *  pvField  )  [inline]
 

Get a field's data length. NOTE: This is the field's internal storage length.

Parameters:
pvField  Field whose data length is to be returned.

FINLINE const FLMBYTE* FlmRecord::getDataPtr void *  pvField  )  [inline]
 

Get a pointer to a field's data.

The returned pointer will be pointing to the data in FLAIM's internal storage format.

Parameters:
pvField  Field whose data pointer is to be returned.

FINLINE FLMUINT FlmRecord::getDataType void *  pvField  )  [inline]
 

Get a field's data type.

Parameters:
pvField  Field whose data type is to be returned.

FLMUINT FlmRecord::getEncFlags void *  pvField  )  [inline]
 

Get a field's encryption flags.

NOTE: This method should NOT be called if the field is not encrypted. Call FlmRecord::isEncryptedField() to determine if the field is encrypted. If called for a non-encrypted field, it will return 0. In debug mode it will also assert.

Parameters:
pvField  Field whose encryption flags are to be returned.

FINLINE FLMUINT FlmRecord::getEncryptedDataLength void *  pvField  )  [inline]
 

Get a field's encrypted data length.

NOTE: This method should NOT be called if the field is not encrypted. Call FlmRecord::isEncryptedField() to determine if the field is encrypted. If called for a non-encrypted field, it will return 0. In debug mode it will also assert.

Parameters:
pvField  Field whose encrypted data length is to be retrieved.

FINLINE const FLMBYTE* FlmRecord::getEncryptionDataPtr void *  pvField  )  [inline]
 

Get a pointer to a field's encrypted data.

NOTE: This method should NOT be called if the field is not encrypted. Call FlmRecord::isEncryptedField() to determine if the field is encrypted. If called for a non-encrypted field, it will return NULL. In debug mode it will also assert.

Parameters:
pvField  Field whose encrypted data pointer is to be retrieved.

FLMUINT FlmRecord::getEncryptionID void *  pvField  )  [inline]
 

Get a field's encryption id.

NOTE: This method should NOT be called if the field is not encrypted. Call FlmRecord::isEncryptedField() to determine if the field is encrypted. If called for a non-encrypted field, it will return 0. In debug mode it will also assert.

Parameters:
pvField  Field whose encryption ID is to be retrieved.

FINLINE FLMUINT FlmRecord::getFieldID void *  pvField  )  [inline]
 

Get the field number for a field.

Parameters:
pvField  Field whose field number is to be returned.

FINLINE RCODE FlmRecord::getFieldInfo void *  pvField,
FLMUINT *  puiFieldNum,
FLMUINT *  puiLevel,
FLMUINT *  puiDataType,
FLMUINT *  puiLength,
FLMUINT *  puiEncLength,
FLMUINT *  puiEncId
[inline]
 

Get information about a field.

Information includes field number, level, data type, data length, encryption length, and encryption id.

Parameters:
pvField  Field whose information is to be retrieved.
puiFieldNum  Field number is returned here.
puiLevel  Field's level is returned here.
puiDataType  Field's data type is returned here.
puiLength  Field's data length is returned here.
puiEncLength  Field's encryption length is returned here. NOTE: This parameter may be NULL.
puiEncId  Field's encryption id is returned here. NOTE: This parameter may be NULL.

RCODE FlmRecord::getINT void *  pvField,
FLMINT *  piNumber
 

Get a field's value as a FLMINT. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
piNumber  Number is returned here.

RCODE FlmRecord::getINT32 void *  pvField,
FLMINT32 *  pi32Number
 

Get a field's value as a FLMINT32. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
pi32Number  Number is returned here.

RCODE FlmRecord::getINT64 void *  pvField,
FLMINT64 *  pi64Number
 

Get a field's value as a FLMINT64. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
pi64Number  Number is returned here.

FINLINE FLMUINT FlmRecord::getLevel void *  pvField  )  [inline]
 

Get the nesting level of a field in the record.

The nesting level begins at 0 (root field), 1 (children of the root field), 2 (grandchildren of the root field), etc.

Parameters:
pvField  Field whose nesting level is to be returned.

void* FlmRecord::getLevelOneField FLMUINT  uiFieldId,
FLMUINT  uiLevelOnePosition
 

Determine if the level one field at the specified position in the field ID table matches the passed in field ID. If so, return that field pointer.

Parameters:
uiFieldId  Field id to be matched.
uiLevelOnePosition  Level one field position to be checked.

FLMUINT FlmRecord::getLevelOneFieldId FLMUINT  uiLevelOnePosition  ) 
 

Get the field ID of the field that is in the specified position in the field ID table.

Parameters:
uiLevelOnePosition  Level one field position whose field ID is to be returned.

RCODE FlmRecord::getNative void *  pvField,
char *  pszStrBuf,
FLMUINT *  puiStrBufLen
 

Get a field's value as a native string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose data is to be returned as a native string.
pszStrBuf  Buffer to hold the native string. NOTE: If this parameter is NULL then *puiStrBufLen will return the number of bytes needed to hold the string. However, that does NOT count the byte needed to null-terminate the string. Thus, if the application is calling this routine to find out how big of a buffer to allocate to hold the string, it should add 1 to the value returned in *puiStrBufLen.
puiStrBufLen  On input *puiStrBufLen should be the number of bytes available in pszStrBuf. pszStrBuf should be large enough to hold a null terminating character. On output *puiStrBufLen returns the number of bytes needed to hold the converted native string. NOTE: The null termination byte is NOT included in this value.

FINLINE RCODE FlmRecord::getNativeLength void *  pvField,
FLMUINT *  puiLength
[inline]
 

Get the number of bytes needed to retrieve a field's value as a native string.

The value may be either a FLM_NUMBER_TYPE or a FLM_TEXT_TYPE. The length returned does NOT account for null-termination, so if the application is calling this routine to determine how big of a buffer to allocate, it should add 1 to the size returned from this routine.

Parameters:
pvField  Field containing the value whose native string length is to be determined.
puiLength  Native length is returned (in bytes). The length does not include what it would take for null-termination.

RCODE FlmRecord::getRecPointer void *  pvField,
FLMUINT *  puiRecPointer
 

Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT.

Parameters:
pvField  Field whose value is to be returned.
puiRecPointer  Value is returned here.

FINLINE RCODE FlmRecord::getRecPointer32 void *  pvField,
FLMUINT32 *  pui32RecPointer
[inline]
 

Get the value for a FLM_CONTEXT_TYPE field as a FLMUINT32.

Parameters:
pvField  Field whose value is to be returned.
pui32RecPointer  Value is returned here.

RCODE FlmRecord::getUINT void *  pvField,
FLMUINT *  puiNumber
 

Get a field's value as a FLMUINT. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
puiNumber  Number is returned here.

RCODE FlmRecord::getUINT32 void *  pvField,
FLMUINT32 *  pui32Number
 

Get a field's value as a FLMUINT32. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
pui32Number  Number is returned here.

RCODE FlmRecord::getUINT64 void *  pvField,
FLMUINT64 *  pui64Number
 

Get a field's value as a FLMUINT64. Data conversions will be done if the field is a FLM_CONTEXT_TYPE or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be retrieved (and converted if necessary).
pui64Number  Number is returned here.

RCODE FlmRecord::getUnicode void *  pvField,
FLMUNICODE *  puzStrBuf,
FLMUINT *  puiStrBufLen
 

Get a field's value as a unicode string. Note that the field may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.

Parameters:
pvField  Field whose data is to be returned as a unicode string.
puzStrBuf  Buffer to hold the Unicode string. NOTE: If this parameter is NULL then *puiStrBufLen will return the number of bytes needed to hold the string. However, that does NOT count the two bytes needed to null-terminate the string. Thus, if the application is calling this routine to find out how big of a buffer to allocate to hold the string, it should add 2 to the value returned in *puiStrBufLen.
puiStrBufLen  On input *puiStrBufLen should be the number of bytes available in puzStrBuf. puzStrBuf should be large enough to hold a unicode null terminating character (2 bytes). On output *puiStrBufLen returns the number of bytes needed to hold the converted Unicode string. NOTE: The two null termination bytes are NOT included in this value.

FINLINE RCODE FlmRecord::getUnicodeLength void *  pvField,
FLMUINT *  puiLength
[inline]
 

Get the number of bytes needed to retrieve a field's value as a unicode string.

The value may be either a FLM_NUMBER_TYPE or a FLM_TEXT_TYPE. The length returned does NOT account for null-termination, so if the application is calling this routine to determine how big of a buffer to allocate, it should add 2 to the size returned from this routine.

Parameters:
pvField  Field containing the value whose unicode string length is to be determined.
puiLength  Unicode length is returned (in bytes). The length does not include what it would take for null-termination.

FINLINE FLMBOOL FlmRecord::hasChild void *  pvField  )  [inline]
 

Determine if a field has any child fields.

Parameters:
pvField  Field that is to be checked for child fields.

RCODE FlmRecord::importRecord NODE pNode  ) 
 

Import a record from a Gedcom NODE tree.

Parameters:
pNode  Node that is the root of a Gedcom tree to be imported.

RCODE FlmRecord::importRecord const char **  ppszBuffer,
FLMUINT  uiBufSize,
F_NameTable pNameTable
 

Import a record from a string buffer.

The record in the string should be formatted according to the specification for GEDCOM.

Parameters:
ppszBuffer  Buffer containing the data that is to be imported.
uiBufSize  Number of bytes in the buffer.
pNameTable  Name table object that is to be used to translate field names to field numbers.

RCODE FlmRecord::importRecord IF_FileHdl *  pFileHdl,
F_NameTable pNameTable
 

Import a record from a file.

The record in the file should be formatted according to the specification for GEDCOM.

Parameters:
pFileHdl  Open file handle where the data for the record is to be read from.
pNameTable  Name table object that is to be used to translate field names to field numbers.

RCODE FlmRecord::insert void *  pvField,
FLMUINT  uiInsertAt,
FLMUINT  uiFieldID,
FLMUINT  uiDataType,
void **  ppvField
 

Insert a new field into the FlmRecord object. Insertion is relative to an already existing field.

Parameters:
pvField  Field that already exists in the record. New field is created relative to this field.
uiInsertAt  Relative position with respect to pvField where new field is to be created. It may be one of the following:
  • INSERT_PREV_SIB - insert new field as the previous sibling of pvField
  • INSERT_NEXT_SIB - insert new field as the next sibling of pvField
  • INSERT_FIRST_CHILD - insert new field as the first child of pvField
  • INSERT_LAST_CHILD - insert new field as the last child of pvField
uiFieldID  Field number for new field. This should be a valid field number as defined in the data dictionary.
uiDataType  Data type for new field. This may be one of the following:
  • FLM_TEXT_TYPE
  • FLM_NUMBER_TYPE
  • FLM_BINARY_TYPE
  • FLM_CONTEXT_TYPE
  • FLM_BLOB_TYPE
ppvField  Pointer to newly created field is returned here.

RCODE FlmRecord::insertLast FLMUINT  uiLevel,
FLMUINT  uiFieldID,
FLMUINT  uiDataType,
void **  ppvField
 

Insert a new field into the FlmRecord object.

New field is always inserted as the last field in the record.

Parameters:
uiLevel  Nesting level for the new field. NOTE: If this is the first field created in the record, the nesting level must be zero. Only the first field added may have a nesting level of zero. If this is not the first field in the record, the nesting level must be between 1 and N+1, where N is the level of the current last field in the record.
uiFieldID  Field number for new field. This should be a valid field number as defined in the data dictionary.
uiDataType  Data type for new field. This may be one of the following:
  • FLM_TEXT_TYPE
  • FLM_NUMBER_TYPE
  • FLM_BINARY_TYPE
  • FLM_CONTEXT_TYPE
  • FLM_BLOB_TYPE
ppvField  Pointer to newly created field is returned here.

FINLINE FLMBOOL FlmRecord::isEncryptedField void *  pvField  )  [inline]
 

Determine if a field is encrypted.

Parameters:
pvField  Field that is to be checked to see if it is encrypted.

FINLINE FLMBOOL FlmRecord::isLast void *  pvField  )  [inline]
 

Determine if a field has a next sibling field.

Parameters:
pvField  Field that is to be checked for next sibling fields.

FINLINE FLMBOOL FlmRecord::isLeftTruncated void *  pvField  )  [inline]
 

Determine if field is marked as "left truncated." This really only applies to binary data or strings.

It indicates that data at the beginning of the binary value or string value is missing.

Parameters:
pvField  Field that is to be checked to see if it is "left truncated."

FINLINE FLMBOOL FlmRecord::isRightTruncated void *  pvField  )  [inline]
 

Determine if field is marked as "right truncated." This really only applies to binary data or strings.

It indicates that data at the end of the binary value or string value is missing.

Parameters:
pvField  Field that is to be checked to see if it is "right truncated."

FINLINE void* FlmRecord::lastChild void *  pvField  )  [inline]
 

Return the last child field of a field. Returns NULL if field has no child fields.

Parameters:
pvField  Field whose last child is to be returned.

FINLINE void* FlmRecord::next void *  pvField  )  [inline]
 

Return the "next" field of a field.

If the field has child fields, then "next" means first child. If the field has no child fields, but has siblings, then "next" means next sibling. If the field has no next sibling field, then "next" means the next sibling of the field's parent field (if any), or the next sibling of the grandparent field (if any), etc. If there is no next sibling to a parent, grandparent, etc., then NULL will be returned.

Parameters:
pvField  Field whose "next" field is to be returned.

void* FlmRecord::nextLevelOneField FLMUINT *  puiFieldPos,
FLMBOOL  bFieldIdsMustMatch
 

Get the next level one field after the position specified. Make sure that the next field has the same field ID as the current field.

Parameters:
puiFieldPos  Current level one field position. Returns the next level one field position.
bFieldIdsMustMatch  Specifies whether the field ID of the next field in the field ID table must match the field ID of the current field position.

FINLINE void* FlmRecord::nextSibling void *  pvField  )  [inline]
 

Return the next sibling field of a field. Returns NULL if there is no next sibling field.

Parameters:
pvField  Field whose next sibling is to be returned.

void FlmRecord::operator delete[] void *  ptr  ) 
 

Overloaded delete operator (array) for FlmRecord objects. Pointer to array of FlmRecord objects being freed.

This method is called when an array of FlmRecord objects is freed.

void* FlmRecord::operator new FLMSIZET  uiSize,
const char *  pszFile,
int  iLine
throw ()
 

Overloaded new operator for ::FlmRecord objects (with source file and line number).

This new operator passes in the current file and line number. This information is useful in tracking memory allocations to determine where memory leaks are coming from.

Parameters:
uiSize  Number of bytes to allocate - should be sizeof( ::FlmRecord).
pszFile  Name of source file where this allocation is made.
iLine  Line number in source file where this allocation request is made.

void* FlmRecord::operator new FLMSIZET  uiSize  )  throw ()
 

Overloaded new operator for FlmRecord objects.

Parameters:
uiSize  Number of bytes to allocate - should be sizeof( ::FlmRecord).

void* FlmRecord::operator new[] FLMSIZET  uiSize,
const char *  pszFile,
int  iLine
throw ()
 

Overloaded new operator (array) for FlmRecord objects (with source file and line number).

This new operator is called when an array of FlmRecord objects is allocated. This new operator passes in the current file and line number. This information is useful in tracking memory allocations to determine where memory leaks are coming from.

Parameters:
uiSize  Number of bytes to allocate - should be a multiple of sizeof( ::FlmRecord).
pszFile  Name of source file where this allocation is made.
iLine  Line number in source file where this allocation request is made.

void* FlmRecord::operator new[] FLMSIZET  uiSize  )  throw ()
 

Overloaded new operator (array) for FlmRecord objects.

This method is called when an array of FlmRecord objects is allocated.

Parameters:
uiSize  Number of bytes to allocate - should be a multiple of sizeof( ::FlmRecord).

FINLINE void* FlmRecord::parent void *  pvField  )  [inline]
 

Return the parent field of a field. Returns NULL if the field is the "root" field of the record.

Parameters:
pvField  Field whose parent is to be returned.

FINLINE void* FlmRecord::prev void *  pvField  )  [inline]
 

Return the "previous" field of a field.

If the field has a previous sibling field, and that previous sibling has children, grandchildren, etc., then "previous" means previous sibling's last child's, last child, last child (etc.). If the previous sibling field has no children, then "previous" means previous sibling. If there is no previous sibling, then "previous" means parent field. If the field is the "root" field, then NULL will be returned.

Parameters:
pvField  Field whose "previous" field is to be returned.

void* FlmRecord::prevSibling void *  pvField  ) 
 

Return the previous sibling field of a field. Returns NULL if there is no previous sibling field.

Parameters:
pvField  Field whose previous sibling is to be returned.

FINLINE FLMINT FLMAPI FlmRecord::Release void   )  [inline]
 

Decrement the reference count for this FlmRecord object.

The reference count is the number of pointers that are referencing this FlmRecord object. Return value is the decremented reference count. If the reference count goes to zero, the FlmRecord object will be deleted.

FINLINE RCODE FlmRecord::remove void *  pvField  )  [inline]
 

Remove a field from a record. The field and all of its descendant fields will be removed from the record.

Parameters:
pvField  Field to be removed. NOTE: If the field has descendant fields, all of those fields will also be removed.

RCODE FlmRecord::setBinary void *  pvField,
const void *  pvBuf,
FLMUINT  uiBufLen,
FLMUINT  uiEncId = 0
 

Set a field's value to a binary value. The resulting data type for the field will be FLM_BINARY_TYPE.

Parameters:
pvField  Field whose value is to be set.
pvBuf  Binary value to set.
uiBufLen  Length of binary data (in bytes).
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

RCODE FlmRecord::setBlob void *  pvField,
FlmBlob pBlob,
FLMUINT  uiEncId = 0
 

Set a field's value to a BLOB value. The resulting data type for the field will be FLM_BLOB_TYPE.

Parameters:
pvField  Field whose value is to be set.
pBlob  BLOB value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

FINLINE void FlmRecord::setFieldID void *  pvField,
FLMUINT  uiFieldID
[inline]
 

Set the field number for a field.

Parameters:
pvField  Field whose field number is to be set.
uiFieldID  Field number to be set.

RCODE FlmRecord::setINT void *  pvField,
FLMINT  iNumber,
FLMUINT  uiEncId = 0
 

Set a field's value to a FLMINT value. The resulting data type for the field will be FLM_NUMBER_TYPE.

Parameters:
pvField  Field whose value is to be set.
iNumber  Value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

RCODE FlmRecord::setINT64 void *  pvField,
FLMINT64  i64Number,
FLMUINT  uiEncId = 0
 

Set a field's value to a FLMINT64 value. The resulting data type for the field will be FLM_NUMBER_TYPE.

Parameters:
pvField  Field whose value is to be set.
i64Number  Value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

FINLINE void FlmRecord::setLeftTruncated void *  pvField,
FLMBOOL  bTrueFalse
[inline]
 

Set a flag for a field indicating if its value has been "left truncated." Left truncation really only applies to binary data or strings.

It specifies that data at the beginning of the binary value or string value is missing.

Parameters:
pvField  Field that is to be marked as "left truncated" or "not left truncated."
bTrueFalse  Flag indicating if field is to be marked as "left truncated" or "not left truncated."

RCODE FlmRecord::setNative void *  pvField,
const char *  pszString,
FLMUINT  uiEncId = 0
 

Set a field's value to a native string value. The resulting data type for the field will be FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be set.
pszString  Value to set. This should be a null-terminated native string.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

RCODE FlmRecord::setRecPointer void *  pvField,
FLMUINT  uiRecPointer,
FLMUINT  uiEncId = 0
 

Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_CONTEXT_TYPE.

Parameters:
pvField  Field whose value is to be set.
uiRecPointer  Value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

FINLINE void FlmRecord::setRightTruncated void *  pvField,
FLMBOOL  bTrueFalse
[inline]
 

Set a flag for a field indicating if its value has been "right truncated." Right truncation really only applies to binary data or strings.

It specifies that data at the end of the binary value or string value is missing.

Parameters:
pvField  Field that is to be marked as "right truncated" or "not right truncated."
bTrueFalse  Flag indicating if field is to be marked as "right truncated" or "not right truncated."

RCODE FlmRecord::setUINT void *  pvField,
FLMUINT  uiNumber,
FLMUINT  uiEncId = 0
 

Set a field's value to a FLMUINT value. The resulting data type for the field will be FLM_NUMBER_TYPE.

Parameters:
pvField  Field whose value is to be set.
uiNumber  Value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

RCODE FlmRecord::setUINT64 void *  pvField,
FLMUINT64  ui64Number,
FLMUINT  uiEncId = 0
 

Set a field's value to a FLMUINT64 value. The resulting data type for the field will be FLM_NUMBER_TYPE.

Parameters:
pvField  Field whose value is to be set.
ui64Number  Value to set.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.

RCODE FlmRecord::setUnicode void *  pvField,
const FLMUNICODE *  puzUnicode,
FLMUINT  uiEncId = 0
 

Set a field's value to a Unicode string value. The resulting data type for the field will be FLM_TEXT_TYPE.

Parameters:
pvField  Field whose value is to be set.
puzUnicode  Value to set. This should be a null-terminated Unicode string.
uiEncId  Encryption ID. If zero, the value will not be encrypted. If non-zero, the number should be the ID of an encryption definition record in the data dictionary. The encryption key for that encryption definition will be used to encrypt this value.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_record-members.html0000644000175000017500000005364110510774540025541 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FlmRecord Member List

This is the complete list of members for FlmRecord, including all inherited members.

AddRef(void)FlmRecord
clear(FLMBOOL bReleaseMemory=FALSE)FlmRecord
compressMemory(void)FlmRecord
copy(void)FlmRecord
createFieldIdTable(FLMBOOL bTruncateTable)FlmRecord
enableFieldIdTable(void)FlmRecord [inline]
exportRecord(HFDB hDb, F_Pool *pPool, NODE **ppNode)FlmRecord
fieldIdTableEnabled(void)FlmRecord [inline]
find(void *pvStartField, FLMUINT uiFieldID, FLMUINT uiOccur=1, FLMUINT uiFindOption=SEARCH_FOREST)FlmRecord
find(void *pvStartField, FLMUINT *puiFieldPath, FLMUINT uiOccur=1, FLMUINT uiFindOption=SEARCH_FOREST)FlmRecord
findLevelOneField(FLMUINT uiFieldID, FLMBOOL bFindInclusive, FLMUINT *puiFieldPos)FlmRecord
firstChild(void *pvField)FlmRecord [inline]
getBinary(void *pvField, void *pvBuf, FLMUINT *puiBufLen)FlmRecord
getBinaryLength(void *pvField, FLMUINT *puiLength)FlmRecord [inline]
getBlob(void *pvField, FlmBlob **ppBlob)FlmRecord
getContainerID(void)FlmRecord [inline]
getDataLength(void *pvField)FlmRecord [inline]
getDataPtr(void *pvField)FlmRecord [inline]
getDataType(void *pvField)FlmRecord [inline]
getEncFlags(void *pvField)FlmRecord [inline]
getEncryptedDataLength(void *pvField)FlmRecord [inline]
getEncryptionDataPtr(void *pvField)FlmRecord [inline]
getEncryptionID(void *pvField)FlmRecord [inline]
getFieldID(void *pvField)FlmRecord [inline]
getFieldInfo(void *pvField, FLMUINT *puiFieldNum, FLMUINT *puiLevel, FLMUINT *puiDataType, FLMUINT *puiLength, FLMUINT *puiEncLength, FLMUINT *puiEncId)FlmRecord [inline]
getFreeMemory(void)FlmRecord [inline]
getID(void)FlmRecord [inline]
getINT(void *pvField, FLMINT *piNumber)FlmRecord
getINT32(void *pvField, FLMINT32 *pi32Number)FlmRecord
getINT64(void *pvField, FLMINT64 *pi64Number)FlmRecord
getLevel(void *pvField)FlmRecord [inline]
getLevelOneField(FLMUINT uiFieldId, FLMUINT uiLevelOnePosition)FlmRecord
getLevelOneFieldId(FLMUINT uiLevelOnePosition)FlmRecord
getNative(void *pvField, char *pszStrBuf, FLMUINT *puiStrBufLen)FlmRecord
getNativeLength(void *pvField, FLMUINT *puiLength)FlmRecord [inline]
getRecPointer(void *pvField, FLMUINT *puiRecPointer)FlmRecord
getRecPointer32(void *pvField, FLMUINT32 *pui32RecPointer)FlmRecord [inline]
getTotalMemory(void)FlmRecord
getUINT(void *pvField, FLMUINT *puiNumber)FlmRecord
getUINT32(void *pvField, FLMUINT32 *pui32Number)FlmRecord
getUINT64(void *pvField, FLMUINT64 *pui64Number)FlmRecord
getUnicode(void *pvField, FLMUNICODE *puzStrBuf, FLMUINT *puiStrBufLen)FlmRecord
getUnicodeLength(void *pvField, FLMUINT *puiLength)FlmRecord [inline]
hasChild(void *pvField)FlmRecord [inline]
importRecord(IF_FileHdl *pFileHdl, F_NameTable *pNameTable)FlmRecord
importRecord(const char **ppszBuffer, FLMUINT uiBufSize, F_NameTable *pNameTable)FlmRecord
importRecord(NODE *pNode)FlmRecord
insert(void *pvField, FLMUINT uiInsertAt, FLMUINT uiFieldID, FLMUINT uiDataType, void **ppvField)FlmRecord
insertLast(FLMUINT uiLevel, FLMUINT uiFieldID, FLMUINT uiDataType, void **ppvField)FlmRecord
isCached(void)FlmRecord [inline]
isEncryptedField(void *pvField)FlmRecord [inline]
isLast(void *pvField)FlmRecord [inline]
isLeftTruncated(void *pvField)FlmRecord [inline]
isOldVersion(void)FlmRecord [inline]
isReadOnly(void)FlmRecord [inline]
isRightTruncated(void *pvField)FlmRecord [inline]
lastChild(void *pvField)FlmRecord [inline]
next(void *pvField)FlmRecord [inline]
nextLevelOneField(FLMUINT *puiFieldPos, FLMBOOL bFieldIdsMustMatch)FlmRecord
nextSibling(void *pvField)FlmRecord [inline]
operator delete(void *ptr)FlmRecord
operator delete[](void *ptr)FlmRecord
operator new(FLMSIZET uiSize)FlmRecord
operator new(FLMSIZET uiSize, const char *pszFile, int iLine)FlmRecord
operator new[](FLMSIZET uiSize)FlmRecord
operator new[](FLMSIZET uiSize, const char *pszFile, int iLine)FlmRecord
parent(void *pvField)FlmRecord [inline]
prev(void *pvField)FlmRecord [inline]
prevSibling(void *pvField)FlmRecord
Release(void)FlmRecord [inline]
remove(void *pvField)FlmRecord [inline]
root(void)FlmRecord [inline]
setBinary(void *pvField, const void *pvBuf, FLMUINT uiBufLen, FLMUINT uiEncId=0)FlmRecord
setBlob(void *pvField, FlmBlob *pBlob, FLMUINT uiEncId=0)FlmRecord
setContainerID(FLMUINT uiContainerID)FlmRecord [inline]
setFieldID(void *pvField, FLMUINT uiFieldID)FlmRecord [inline]
setID(FLMUINT uiRecordID)FlmRecord [inline]
setINT(void *pvField, FLMINT iNumber, FLMUINT uiEncId=0)FlmRecord
setINT64(void *pvField, FLMINT64 i64Number, FLMUINT uiEncId=0)FlmRecord
setLeftTruncated(void *pvField, FLMBOOL bTrueFalse)FlmRecord [inline]
setNative(void *pvField, const char *pszString, FLMUINT uiEncId=0)FlmRecord
setRecPointer(void *pvField, FLMUINT uiRecPointer, FLMUINT uiEncId=0)FlmRecord
setRightTruncated(void *pvField, FLMBOOL bTrueFalse)FlmRecord [inline]
setUINT(void *pvField, FLMUINT uiNumber, FLMUINT uiEncId=0)FlmRecord
setUINT64(void *pvField, FLMUINT64 ui64Number, FLMUINT uiEncId=0)FlmRecord
setUnicode(void *pvField, const FLMUNICODE *puzUnicode, FLMUINT uiEncId=0)FlmRecord


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_record_set.html0000644000175000017500000002266010510774540024761 0ustar ahodgkinsonahodgkinson FLAIM: FlmRecordSet Class Reference

FlmRecordSet Class Reference

This class allows an application to keep a set of FlmRecord objects. More...

#include <flaim.h>

List of all members.

Public Member Functions

RCODE insert (FlmRecord *pRecord)
 Insert a FlmRecord into the set.
void clear (void)
 Clear all records in the set.
FINLINE FlmRecordfirst (void)
 Position to and return a pointer to the first FlmRecord object in the set.
FINLINE FlmRecordlast (void)
 Position to and return a pointer to the last FlmRecord object in the set.
FlmRecordnext (void)
 Position to and return a pointer to the next FlmRecord object in the set.
FINLINE FlmRecordprev (void)
 Position to and return a pointer to the next FlmRecord object in the set.
FINLINE FLMINT count (void)
 Return the total number of records in the set.


Detailed Description

This class allows an application to keep a set of FlmRecord objects.


Member Function Documentation

RCODE FlmRecordSet::insert FlmRecord pRecord  ) 
 

Insert a FlmRecord into the set.

Parameters:
pRecord  Pointer to FlmRecord that is to be inserted into the set.

FlmRecord* FlmRecordSet::next void   ) 
 

Position to and return a pointer to the next FlmRecord object in the set.

NOTE: This method will return the first record in the set if no previous calls have been made to retrieve records from the set.

FINLINE FlmRecord* FlmRecordSet::prev void   )  [inline]
 

Position to and return a pointer to the next FlmRecord object in the set.

NOTE: This method will return NULL if no previous calls have been made to retrieve records from the set.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_user_predicate.html0000644000175000017500000013352110510774540025625 0ustar ahodgkinsonahodgkinson FLAIM: FlmUserPredicate Class Reference

FlmUserPredicate Class Reference

This is an abstract base class which defines the interface that an application must implement to embed its own predicate in a query. More...

#include <flaim.h>

List of all members.

Public Member Functions

virtual RCODE searchCost (HFDB hDb, FLMBOOL bNotted, FLMBOOL bExistential, FLMUINT *puiCost, FLMUINT *puiDrnCost, FLMUINT *puiTestRecordCost, FLMBOOL *pbPassesEmptyRec)=0
 Method that returns the search cost of this object in providing records for a query.
virtual RCODE testAllRecordCost (HFDB hDb, FLMUINT *puiCost)=0
 Method that returns the cost of testing ALL record.
virtual RCODE firstRecord (HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0
 Position to and return the first record that satisfies the predicate.
virtual RCODE lastRecord (HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0
 Position to and return the last record that satisfies the predicate.
virtual RCODE nextRecord (HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0
 Position to and return the next record that satisfies the predicate.
virtual RCODE prevRecord (HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0
 Position to and return the previous record that satisfies the predicate.
virtual RCODE testRecord (HFDB hDb, FlmRecord *pRecord, FLMUINT uiDrn, FLMUINT *puiResult)=0
 Test a record to see if it passes the criteria of the predicate.
virtual FLMUINT getIndex (FLMUINT *puiIndex)=0
 Return index being used for this predicate.
virtual FlmUserPredicatecopy (void)=0
 Copy the predicate.
virtual HFCURSOR getCursor (void)=0
 Return predicate's FLAIM query handle, if any.
virtual RCODE positionTo (HFDB hDb, FlmUserPredicate *pPredicate)=0
 Position this predicate to the same position as another predicate.
virtual RCODE savePosition (void)=0
 Save current position of predicate.
virtual RCODE restorePosition (void)=0
 Restore last saved position of predicate.
virtual RCODE isAbsPositionable (HFDB hDb, FLMBOOL *pbIsAbsPositionable)=0
 Determine if predicate is absolute positionable.
virtual RCODE getAbsCount (HFDB hDb, FLMUINT *puiCount)=0
 Get absolute record count.
virtual RCODE getAbsPosition (HFDB hDb, FLMUINT *puiPosition)=0
 Get absolute position.
virtual RCODE positionToAbs (HFDB hDb, FLMUINT uiPosition, FLMBOOL bFallForward, FLMUINT uiTimeLimit, FLMUINT *puiPosition, FLMUINT *puiDrn)=0
 Set absolute position.
virtual void releaseResources (void)=0
 Release any resources held by this predicate.


Detailed Description

This is an abstract base class which defines the interface that an application must implement to embed its own predicate in a query.


Member Function Documentation

virtual FlmUserPredicate* FlmUserPredicate::copy void   )  [pure virtual]
 

Copy the predicate.

In the new object, it should be as if the predicate had just been defined. The current position, calculated score, etc. should not be preserved in the new predicate. Only the predicate criteria - however that is represented - should be preserved. This method should return NULL if a new predicate cannot be created. FLAIM will call this method whenever it needs to copy an application-defined predicate - such as when a query or some part of a query is cloned.

virtual RCODE FlmUserPredicate::firstRecord HFDB  hDb,
FLMUINT *  puiDrn,
FlmRecord **  ppRecord
[pure virtual]
 

Position to and return the first record that satisfies the predicate.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
puiDrn  If non-NULL, record's DRN is returned here.
ppRecord  If non-NULL, record is returned here.

virtual RCODE FlmUserPredicate::getAbsCount HFDB  hDb,
FLMUINT *  puiCount
[pure virtual]
 

Get absolute record count.

NOTE: This method should only be called if the predicate is "absolute" positionable (see FlmUserPredicate::isAbsPositionable()).

Parameters:
hDb  Database handle.
puiCount  Returns total number of records this predicate would return.

virtual RCODE FlmUserPredicate::getAbsPosition HFDB  hDb,
FLMUINT *  puiPosition
[pure virtual]
 

Get absolute position.

NOTE: This method should only be called if the predicate is "absolute" positionable (see FlmUserPredicate::isAbsPositionable()).

Parameters:
hDb  Database handle.
puiPosition  Returns the current "absolute" position of this predicate.

virtual HFCURSOR FlmUserPredicate::getCursor void   )  [pure virtual]
 

Return predicate's FLAIM query handle, if any.

This is useful if an application-defined predicate is implemented as a FLAIM query. It is often very useful to embed one FLAIM query inside another.

virtual FLMUINT FlmUserPredicate::getIndex FLMUINT *  puiIndex  )  [pure virtual]
 

Return index being used for this predicate.

Parameters:
puiIndex  Index number is returned here. If no index is being used, a zero should be returned.

virtual RCODE FlmUserPredicate::isAbsPositionable HFDB  hDb,
FLMBOOL *  pbIsAbsPositionable
[pure virtual]
 

Determine if predicate is absolute positionable.

Parameters:
hDb  Database handle.
pbIsAbsPositionable  Returns TRUE/FALSE indicating if predicate is "absolute" positionable.

virtual RCODE FlmUserPredicate::lastRecord HFDB  hDb,
FLMUINT *  puiDrn,
FlmRecord **  ppRecord
[pure virtual]
 

Position to and return the last record that satisfies the predicate.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
puiDrn  If non-NULL, record's DRN is returned here.
ppRecord  If non-NULL, record is returned here.

virtual RCODE FlmUserPredicate::nextRecord HFDB  hDb,
FLMUINT *  puiDrn,
FlmRecord **  ppRecord
[pure virtual]
 

Position to and return the next record that satisfies the predicate.

If no prior positioning has been done, position to and return the first record.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
puiDrn  If non-NULL, record's DRN is returned here.
ppRecord  If non-NULL, record is returned here.

virtual RCODE FlmUserPredicate::positionTo HFDB  hDb,
FlmUserPredicate pPredicate
[pure virtual]
 

Position this predicate to the same position as another predicate.

Parameters:
hDb  Database handle.
pPredicate  Predicate whose position this predicate is to be positioned to.

virtual RCODE FlmUserPredicate::positionToAbs HFDB  hDb,
FLMUINT  uiPosition,
FLMBOOL  bFallForward,
FLMUINT  uiTimeLimit,
FLMUINT *  puiPosition,
FLMUINT *  puiDrn
[pure virtual]
 

Set absolute position.

NOTE: This method should only be called if the predicate is "absolute" positionable (see FlmUserPredicate::isAbsPositionable()).

Parameters:
hDb  Database handle.
uiPosition  Absolute position to position this predicate to.
bFallForward  If the record at the position specified cannot be returned, this flag indicates whether the method should attempt to "fall forward" to the next record in the result set that can be returned.
uiTimeLimit  Time limit (seconds) for this operation.
puiPosition  Returns the actual position that was positioned to. The only time this could be different than the position requested in the uiPosition parameter is if the record at the requested position cannot be returned and the bFallForward flag is TRUE.
puiDrn  Returns the DRN of the record at the position we ended getting positioned to.

virtual RCODE FlmUserPredicate::prevRecord HFDB  hDb,
FLMUINT *  puiDrn,
FlmRecord **  ppRecord
[pure virtual]
 

Position to and return the previous record that satisfies the predicate.

If no prior positioning has been done, position to and return the last record.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
puiDrn  If non-NULL, record's DRN is returned here.
ppRecord  If non-NULL, record is returned here.

virtual void FlmUserPredicate::releaseResources void   )  [pure virtual]
 

Release any resources held by this predicate.

This method should release any resources (memory, etc.) consumed by the predicate except those that would be needed to show the predicate's query criteria. At the point in time where FLAIM calls this method, the predicate is no longer going to be used to retrieve or test records, but the query is being kept around to allow a user to see information about queries that have been run. Thus, enough information should be preserved to allow the predicate to show its query criteria and any statistics it may have collected if it was called to get records (firstRecord, lastRecord, nextRecord, prevRecord).

virtual RCODE FlmUserPredicate::searchCost HFDB  hDb,
FLMBOOL  bNotted,
FLMBOOL  bExistential,
FLMUINT *  puiCost,
FLMUINT *  puiDrnCost,
FLMUINT *  puiTestRecordCost,
FLMBOOL *  pbPassesEmptyRec
[pure virtual]
 

Method that returns the search cost of this object in providing records for a query.

FLAIM uses the information returned from this method to determine how to optimize the query.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
bNotted  Flag indicating predicate is notted in the search criteria.
bExistential  Flag indicating predicate is "existential" (TRUE) or "universal" (FALSE).
puiCost  Estimated cost is returned here.
puiDrnCost  Estimated DRN cost is returned here.
puiTestRecordCost  Test record cost is returned here.
pbPassesEmptyRec  Returns flag indicating whether the predicate would pass or fail an empty record.

virtual RCODE FlmUserPredicate::testAllRecordCost HFDB  hDb,
FLMUINT *  puiCost
[pure virtual]
 

Method that returns the cost of testing ALL record.

Parameters:
hDb  Database handle.
puiCost  Estimated cost is returned here.

virtual RCODE FlmUserPredicate::testRecord HFDB  hDb,
FlmRecord pRecord,
FLMUINT  uiDrn,
FLMUINT *  puiResult
[pure virtual]
 

Test a record to see if it passes the criteria of the predicate.

Parameters:
hDb  Database handle. NOTE: Application should NOT save this handle. It may be changed from one call to the next.
pRecord  Record to be tested.
uiDrn  DRN of record to be tested.
puiResult  Result is returned here. Should be one of the following:
  • FLM_FALSE - should be returned if predicate fails the record
  • FLM_TRUE - should be returned if the predicate passes the record
  • FLM_UNK - should be returned if it cannot be determined whether the record passes or fails


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_record_set-members.html0000644000175000017500000000571710510774540026415 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FlmRecordSet Member List

This is the complete list of members for FlmRecordSet, including all inherited members.

clear(void)FlmRecordSet
count(void)FlmRecordSet [inline]
first(void)FlmRecordSet [inline]
insert(FlmRecord *pRecord)FlmRecordSet
last(void)FlmRecordSet [inline]
next(void)FlmRecordSet
prev(void)FlmRecordSet [inline]


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/class_flm_user_predicate-members.html0000644000175000017500000001517110510774540027255 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FlmUserPredicate Member List

This is the complete list of members for FlmUserPredicate, including all inherited members.

copy(void)=0FlmUserPredicate [pure virtual]
firstRecord(HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0FlmUserPredicate [pure virtual]
getAbsCount(HFDB hDb, FLMUINT *puiCount)=0FlmUserPredicate [pure virtual]
getAbsPosition(HFDB hDb, FLMUINT *puiPosition)=0FlmUserPredicate [pure virtual]
getCursor(void)=0FlmUserPredicate [pure virtual]
getIndex(FLMUINT *puiIndex)=0FlmUserPredicate [pure virtual]
isAbsPositionable(HFDB hDb, FLMBOOL *pbIsAbsPositionable)=0FlmUserPredicate [pure virtual]
lastRecord(HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0FlmUserPredicate [pure virtual]
nextRecord(HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0FlmUserPredicate [pure virtual]
positionTo(HFDB hDb, FlmUserPredicate *pPredicate)=0FlmUserPredicate [pure virtual]
positionToAbs(HFDB hDb, FLMUINT uiPosition, FLMBOOL bFallForward, FLMUINT uiTimeLimit, FLMUINT *puiPosition, FLMUINT *puiDrn)=0FlmUserPredicate [pure virtual]
prevRecord(HFDB hDb, FLMUINT *puiDrn, FlmRecord **ppRecord)=0FlmUserPredicate [pure virtual]
releaseResources(void)=0FlmUserPredicate [pure virtual]
restorePosition(void)=0FlmUserPredicate [pure virtual]
savePosition(void)=0FlmUserPredicate [pure virtual]
searchCost(HFDB hDb, FLMBOOL bNotted, FLMBOOL bExistential, FLMUINT *puiCost, FLMUINT *puiDrnCost, FLMUINT *puiTestRecordCost, FLMBOOL *pbPassesEmptyRec)=0FlmUserPredicate [pure virtual]
testAllRecordCost(HFDB hDb, FLMUINT *puiCost)=0FlmUserPredicate [pure virtual]
testRecord(HFDB hDb, FlmRecord *pRecord, FLMUINT uiDrn, FLMUINT *puiResult)=0FlmUserPredicate [pure virtual]


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_m_a_i_n_t___s_t_a_t_u_s.html0000644000175000017500000000645210510774540027257 0ustar ahodgkinsonahodgkinson FLAIM: FMAINT_STATUS Struct Reference

FMAINT_STATUS Struct Reference

This structure is returned from FlmMaintenanceStatus(). It contains information about the background maintenance thread. More...

#include <flaim.h>

List of all members.

Public Attributes

eMaintAction eDoing
 Current action of the maintenance thread.
FLMUINT64 ui64BlocksFreed
 Total blocks freed. NOTE: Only valid if eDoing == eMaintAction::FLM_MAINT_FREEING_BLOCKS.


Detailed Description

This structure is returned from FlmMaintenanceStatus(). It contains information about the background maintenance thread.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_f_m_a_i_n_t___s_t_a_t_u_s-members.html0000644000175000017500000000360110510774540030700 0ustar ahodgkinsonahodgkinson FLAIM: Member List

FMAINT_STATUS Member List

This is the complete list of members for FMAINT_STATUS, including all inherited members.

eDoingFMAINT_STATUS
ui64BlocksFreedFMAINT_STATUS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_e_v_e_l___i_n_f_o.html0000644000175000017500000000613110510774540026055 0ustar ahodgkinsonahodgkinson FLAIM: LEVEL_INFO Struct Reference

LEVEL_INFO Struct Reference

Statistics for a particular level in an index or container b-tree. These statistics are gathered by FlmDbCheck(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT64 ui64KeyCount
 Total keys at this level of the b-tree.
BLOCK_INFO BlockInfo
 Statistics for blocks at this level of the b-tree.


Detailed Description

Statistics for a particular level in an index or container b-tree. These statistics are gathered by FlmDbCheck().


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_e_v_e_l___i_n_f_o-members.html0000644000175000017500000000352710510774540027513 0ustar ahodgkinsonahodgkinson FLAIM: Member List

LEVEL_INFO Member List

This is the complete list of members for LEVEL_INFO, including all inherited members.

BlockInfoLEVEL_INFO
ui64KeyCountLEVEL_INFO


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_f___s_t_a_t_s.html0000644000175000017500000001233210510774540025254 0ustar ahodgkinsonahodgkinson FLAIM: LF_STATS Struct Reference

LF_STATS Struct Reference

Statistics gathered by FlmDbCheck() for a particular logical file (index or container). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMUINT uiLfType
 Logical file type. Will be LF_INDEX or LF_CONTAINER.
FLMUINT uiIndexNum
 Index number. Only set if uiLfType == LF_INDEX.
FLMUINT uiContainerNum
 Container number. If uiLfType == LF_INDEX, this is the container number that is associated with the index.
FLMUINT64 ui64FldRefCount
 If uiLfType == LF_INDEX, this is the number of records referenced from the index. If uiLfType == LF_CONTAINER, this is the number of fields in the records in the container.
FLMUINT uiNumLevels
 Number of levels in the b-tree for this index or container.
LEVEL_INFOpLevelInfo
 Statistics for each level of the b-tree.


Detailed Description

Statistics gathered by FlmDbCheck() for a particular logical file (index or container).


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_f___s_t_a_t_s-members.html0000644000175000017500000000522210510774540026704 0ustar ahodgkinsonahodgkinson FLAIM: Member List

LF_STATS Member List

This is the complete list of members for LF_STATS, including all inherited members.

pLevelInfoLF_STATS
ui64FldRefCountLF_STATS
uiContainerNumLF_STATS
uiIndexNumLF_STATS
uiLfTypeLF_STATS
uiNumLevelsLF_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_f_i_l_e___s_t_a_t_s.html0000644000175000017500000001537310510774540026413 0ustar ahodgkinsonahodgkinson FLAIM: LFILE_STATS Struct Reference

LFILE_STATS Struct Reference

Statistics gathered for a particular logical file (index or container). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMBOOL bHaveStats
 Flag indicating whether or not there are statistics for this logical file.
FLMUINT uiLFileNum
 Logical file number.
FLMUINT uiFlags
 Flags for logical file. These may be ORed together, and are as follows:
  • LFILE_IS_INDEX - If set, specifies that the logical file is an index. If not set, specifies that the logical file is a container or the type is unknown. If the logical file's type is unknown, then LFILE_TYPE_UNKNOWN bit will be set
  • LFILE_TYPE_UNKNOWN - Type of the logical file is not known
  • LFILE_LEVEL_MASK - The the number of levels in the logical file's b-tree is contained in the lower four bits of the flags. This mask (0xF) allows an application to mask out the other bits to retrieve the level.

BLOCKIO_STATS RootBlockStats
 Block I/O statistics for the logical file's root blocks.
BLOCKIO_STATS MiddleBlockStats
 Block I/O statistics for for the blocks in the logical file that are not root blocks or leaf blocks.
BLOCKIO_STATS LeafBlockStats
 Block I/O statistics for the logical file's leaf blocks.
FLMUINT64 ui64BlockSplits
 Number of block splits that have occurred in this logical file.
FLMUINT64 ui64BlockCombines
 Number of block combines that have occurred in this logical file.


Detailed Description

Statistics gathered for a particular logical file (index or container).


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_l_f_i_l_e___s_t_a_t_s-members.html0000644000175000017500000000631510510774540030037 0ustar ahodgkinsonahodgkinson FLAIM: Member List

LFILE_STATS Member List

This is the complete list of members for LFILE_STATS, including all inherited members.

bHaveStatsLFILE_STATS
LeafBlockStatsLFILE_STATS
MiddleBlockStatsLFILE_STATS
RootBlockStatsLFILE_STATS
ui64BlockCombinesLFILE_STATS
ui64BlockSplitsLFILE_STATS
uiFlagsLFILE_STATS
uiLFileNumLFILE_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_n_o_d_e.html0000644000175000017500000001641610510774540023575 0ustar ahodgkinsonahodgkinson FLAIM: NODE Struct Reference

NODE Struct Reference

Structure for nodes used in GEDCOM functions. Nodes are the basic components of GEDCOM trees. More...

#include <flaim.h>

List of all members.

Public Attributes

NODEnext
 Pointer to child, next sib, or uncle (compare levels).
NODEprior
 Pointer to parent, prior sib, or nephew (compare levels).
FLMBYTE * value
 Value of node (if length <= 4), or pointer to value.
FLMUINT32 ui32Length
 Length of value (in bytes).
FLMUINT16 ui16TagNum
 Tag number.
FLMUINT8 ui8Level
 Hierarchy level (0 = root).
FLMUINT8 ui8Type
 Value's data type. This should be one of the following:
  • FLM_TEXT_TYPE (0)
  • FLM_NUMBER_TYPE (1)
  • FLM_BINARY_TYPE (2)
  • FLM_CONTEXT_TYPE (3)
  • FLM_BLOB_TYPE (8).

FLMUINT32 ui32EncFlags
 Encryption flags.
FLMUINT32 ui32EncLength
 The length of the encrypted data.
FLMUINT32 ui32EncId
 The DRN of the encryption definition record in the dictionary.
FLMBYTE * pucEncValue
 The encrypted value.


Detailed Description

Structure for nodes used in GEDCOM functions. Nodes are the basic components of GEDCOM trees.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_n_o_d_e-members.html0000644000175000017500000000666210510774540025227 0ustar ahodgkinsonahodgkinson FLAIM: Member List

NODE Member List

This is the complete list of members for NODE, including all inherited members.

nextNODE
priorNODE
pucEncValueNODE
ui16TagNumNODE
ui32EncFlagsNODE
ui32EncIdNODE
ui32EncLengthNODE
ui32LengthNODE
ui8LevelNODE
ui8TypeNODE
valueNODE


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_o_p_t___i_n_f_o.html0000644000175000017500000001444210510774540025256 0ustar ahodgkinsonahodgkinson FLAIM: OPT_INFO Struct Reference

OPT_INFO Struct Reference

Structure returned when FCURSOR_GET_OPT_INFO_LIST option is passed to FlmCursorGetConfig(). More...

#include <flaim.h>

List of all members.

Public Attributes

qOptTypes eOptType
 Type of optimization done for sub-query.
FLMUINT uiCost
 Cost calculated for sub-query.
FLMUINT uiDrnCost
 DRN cost for sub-query.
FLMUINT uiIxNum
 Index used to execute query if eOptType is qOptTypes::QOPT_USING_INDEX.
FLMBOOL bDoRecMatch
 Record must be retrieved to test against query criteria. Only valid if OPT_INFO::eOptType is qOptTypes::QOPT_USING_INDEX.
FLMUINT bDoKeyMatch
 Must match against index keys. Only valid if OPT_INFO::eOptType is qOptTypes::QOPT_USING_INDEX.
FLMUINT uiDrn
 DRN to read if OPT_INFO::eOptType is qOptTypes::QOPT_SINGLE_RECORD_READ.


Detailed Description

Structure returned when FCURSOR_GET_OPT_INFO_LIST option is passed to FlmCursorGetConfig().


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_o_p_t___i_n_f_o-members.html0000644000175000017500000000552110510774540026704 0ustar ahodgkinsonahodgkinson FLAIM: Member List

OPT_INFO Member List

This is the complete list of members for OPT_INFO, including all inherited members.

bDoKeyMatchOPT_INFO
bDoRecMatchOPT_INFO
eOptTypeOPT_INFO
uiCostOPT_INFO
uiDrnOPT_INFO
uiDrnCostOPT_INFO
uiIxNumOPT_INFO


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_p_o_o_l___s_t_a_t_s.html0000644000175000017500000000551610510774540026130 0ustar ahodgkinsonahodgkinson FLAIM: POOL_STATS Struct Reference

POOL_STATS Struct Reference

Pool memory manager. More...

#include <ftk.h>

List of all members.

Public Attributes

FLMUINT uiAllocBytes
 Total number of bytes requested from GedPoolAlloc and GedPoolCalloc calls.
FLMUINT uiCount
 Number of frees and resets performed on the pool.


Detailed Description

Pool memory manager.

This structure is used to keep track of a pool of memory blocks that are used for pool memory allocation.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_p_o_o_l___s_t_a_t_s-members.html0000644000175000017500000000352510510774540027556 0ustar ahodgkinsonahodgkinson FLAIM: Member List

POOL_STATS Member List

This is the complete list of members for POOL_STATS, including all inherited members.

uiAllocBytesPOOL_STATS
uiCountPOOL_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_pool_memory_block.html0000644000175000017500000000763710510774540025733 0ustar ahodgkinsonahodgkinson FLAIM: PoolMemoryBlock Struct Reference

PoolMemoryBlock Struct Reference

Header for blocks in a memory pool. More...

#include <ftk.h>

List of all members.

Public Attributes

PoolMemoryBlockpPrevBlock
 Points to the previous memory block in the memory pool.
FLMUINT uiBlockSize
 Total size of the memory block.
FLMUINT uiFreeOffset
 Offset in block where next allocation should be made.
FLMUINT uiFreeSize
 Amount of free memory left in block - from uiFreeOfs.


Detailed Description

Header for blocks in a memory pool.

This structure is at the head of each block that belongs to a pool of memory.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_pool_memory_block-members.html0000644000175000017500000000444110510774540027351 0ustar ahodgkinsonahodgkinson FLAIM: Member List

PoolMemoryBlock Member List

This is the complete list of members for PoolMemoryBlock, including all inherited members.

pPrevBlockPoolMemoryBlock
uiBlockSizePoolMemoryBlock
uiFreeOffsetPoolMemoryBlock
uiFreeSizePoolMemoryBlock


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_e_b_u_i_l_d___i_n_f_o.html0000644000175000017500000001405210510774540026673 0ustar ahodgkinsonahodgkinson FLAIM: REBUILD_INFO Struct Reference

REBUILD_INFO Struct Reference

Structure returned during status callback from FlmDbRebuild(). More...

#include <flaim.h>

List of all members.

Public Attributes

FLMINT iDoingFlag
 This indicates what the rebuild operation is currently doing. It may be one of the following:
  • REBUILD_GET_BLK_SIZ - FlmDbRebuild() is trying to determine the database's block size
  • REBUILD_RECOVER_DICT - FlmDbRebuild() is recovering dictionary records
  • REBUILD_RECOVER_DATA - FlmDbRebuild() is recovering non-dictionary records
  • REBUILD_FINISHED - FlmDbRebuild() is done rebuilding the database.

FLMBOOL bStartFlag
 This flag is TRUE when FlmDbRebuild() is just starting its current operation (the one specified in iDoingFlag), FALSE otherwise.
FLMUINT64 ui64DatabaseSize
 Total size of the database data files (in bytes).
FLMUINT64 ui64BytesExamined
 Total bytes examined in the data files so far.
FLMUINT uiTotRecs
 Total records traversed.
FLMUINT uiRecsRecov
 Total records recovered so far.


Detailed Description

Structure returned during status callback from FlmDbRebuild().

This structure is passed to the callback function when the eStatusType::FLM_REBUILD_STATUS status is reported.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_e_b_u_i_l_d___i_n_f_o-members.html0000644000175000017500000000543710510774540030332 0ustar ahodgkinsonahodgkinson FLAIM: Member List

REBUILD_INFO Member List

This is the complete list of members for REBUILD_INFO, including all inherited members.

bStartFlagREBUILD_INFO
iDoingFlagREBUILD_INFO
ui64BytesExaminedREBUILD_INFO
ui64DatabaseSizeREBUILD_INFO
uiRecsRecovREBUILD_INFO
uiTotRecsREBUILD_INFO


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_e_c___k_e_y.html0000644000175000017500000000552510510774540024725 0ustar ahodgkinsonahodgkinson FLAIM: REC_KEY Struct Reference

REC_KEY Struct Reference

Structure used to create a linked list of index keys from a record. More...

#include <flaim.h>

List of all members.

Public Attributes

FlmRecordpKey
 Pointer to index key that was generated from a record.
REC_KEYpNextKey
 Pointer to next key in the record.


Detailed Description

Structure used to create a linked list of index keys from a record.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_e_c___k_e_y-members.html0000644000175000017500000000344410510774540026353 0ustar ahodgkinsonahodgkinson FLAIM: Member List

REC_KEY Member List

This is the complete list of members for REC_KEY, including all inherited members.

pKeyREC_KEY
pNextKeyREC_KEY


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_t_r_a_n_s___s_t_a_t_s.html0000644000175000017500000000644310510774540026766 0ustar ahodgkinsonahodgkinson FLAIM: RTRANS_STATS Struct Reference

RTRANS_STATS Struct Reference

Statistics for read transactions. More...

#include <flaim.h>

List of all members.

Public Attributes

F_COUNT_TIME_STAT CommittedTrans
 Statistics for read transactions committed.
F_COUNT_TIME_STAT AbortedTrans
 Statistics for read transactions aborted.
F_COUNT_TIME_STAT InvisibleTrans
 Statistics for invisible read transactions.


Detailed Description

Statistics for read transactions.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_r_t_r_a_n_s___s_t_a_t_s-members.html0000644000175000017500000000414410510774540030412 0ustar ahodgkinsonahodgkinson FLAIM: Member List

RTRANS_STATS Member List

This is the complete list of members for RTRANS_STATS, including all inherited members.

AbortedTransRTRANS_STATS
CommittedTransRTRANS_STATS
InvisibleTransRTRANS_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_s_w_e_e_p___i_n_f_o.html0000644000175000017500000001221310510774540026067 0ustar ahodgkinsonahodgkinson FLAIM: SWEEP_INFO Struct Reference

SWEEP_INFO Struct Reference

Structure that reports information on the progress of FlmDbSweep(). More...

#include <flaim.h>

List of all members.

Public Attributes

HFDB hDb
 Handle to database being traversed by FlmDbSweep().
FLMUINT uiRecId
 DRN of the current record being traversed by FlmDbSweep().
FLMUINT uiContainer
 Current container being traversed by FlmDbSweep().
FlmRecordpRecord
 Pointer to current record being traversed by FlmDbSweep().
void * pvField
 Current field with the record being traversed by FlmDbSweep().


Detailed Description

Structure that reports information on the progress of FlmDbSweep().

The FlmDbSweep() status callback function is called and passed a pointer to this structure.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_s_w_e_e_p___i_n_f_o-members.html0000644000175000017500000000474110510774540027526 0ustar ahodgkinsonahodgkinson FLAIM: Member List

SWEEP_INFO Member List

This is the complete list of members for SWEEP_INFO, including all inherited members.

hDbSWEEP_INFO
pRecordSWEEP_INFO
pvFieldSWEEP_INFO
uiContainerSWEEP_INFO
uiRecIdSWEEP_INFO


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_u_t_r_a_n_s___s_t_a_t_s.html0000644000175000017500000000756010510774540026772 0ustar ahodgkinsonahodgkinson FLAIM: UTRANS_STATS Struct Reference

UTRANS_STATS Struct Reference

Statistics for update transactions. More...

#include <flaim.h>

List of all members.

Public Attributes

F_COUNT_TIME_STAT CommittedTrans
 Statistics for update transactions committed.
F_COUNT_TIME_STAT GroupCompletes
 Statistics for number of times multiple transactions were committed together.
FLMUINT64 ui64GroupFinished
 Total update transactions that were committed in a group.
F_COUNT_TIME_STAT AbortedTrans
 Statistics for update transactions aborted.


Detailed Description

Statistics for update transactions.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/struct_u_t_r_a_n_s___s_t_a_t_s-members.html0000644000175000017500000000452310510774540030416 0ustar ahodgkinsonahodgkinson FLAIM: Member List

UTRANS_STATS Member List

This is the complete list of members for UTRANS_STATS, including all inherited members.

AbortedTransUTRANS_STATS
CommittedTransUTRANS_STATS
GroupCompletesUTRANS_STATS
ui64GroupFinishedUTRANS_STATS


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbsystem.html0000644000175000017500000000356010510774540024010 0ustar ahodgkinsonahodgkinson FLAIM: FLAIM System Functions

FLAIM System Functions


Modules

 FLAIM System Startup/Shutdown
 FLAIM System Configuration/Information
 Cache Configuration Functions
 Statistics Collection Functions

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__startupshutdown.html0000644000175000017500000000440410510774540025452 0ustar ahodgkinsonahodgkinson FLAIM: FLAIM System Startup/Shutdown

FLAIM System Startup/Shutdown
[FLAIM System Functions]


Functions

FLMEXP RCODE FLMAPI FlmStartup (void)
 Startup FLAIM database system.
FLMEXP void FLMAPI FlmShutdown (void)
 Shutdown FLAIM database system.

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__systemconfiguration.html0000644000175000017500000002366310510774540026300 0ustar ahodgkinsonahodgkinson FLAIM: FLAIM System Configuration/Information

FLAIM System Configuration/Information
[FLAIM System Functions]


Functions

FLMEXP RCODE FLMAPI FlmConfig (eFlmConfigTypes eConfigType, void *pvValue1, void *pvValue2)
 Configure the FLAIM database system.
FLMEXP RCODE FLMAPI FlmGetConfig (eFlmConfigTypes eConfigType, void *pvValue)
 Get configuration information about the FLAIM database system.
FLMEXP RCODE FLMAPI FlmGetThreadInfo (F_Pool *pPool, F_THREAD_INFO **ppThreadInfo, FLMUINT *puiNumThreads, const char *pszUrl=NULL)
 Get information on background threads in the FLAIM database system.

Function Documentation

FLMEXP RCODE FLMAPI FlmConfig eFlmConfigTypes  eConfigType,
void *  pvValue1,
void *  pvValue2
 

Configure the FLAIM database system.

Parameters:
eConfigType  Specified what is to be configured.
pvValue1  Parameter for configuration - see documentation for eFlmConfigTypes for specifics.
pvValue2  Parameter for configuration - see documentation for eFlmConfigTypes for specifics.

FLMEXP RCODE FLMAPI FlmGetConfig eFlmConfigTypes  eConfigType,
void *  pvValue
 

Get configuration information about the FLAIM database system.

Parameters:
eConfigType  Configuration information to be retrieved.
pvValue  Configuration information is returned here - see documentation for eFlmConfigTypes for what will be returned for each configuration type.

FLMEXP RCODE FLMAPI FlmGetThreadInfo F_Pool *  pPool,
F_THREAD_INFO **  ppThreadInfo,
FLMUINT *  puiNumThreads,
const char *  pszUrl = NULL
 

Get information on background threads in the FLAIM database system.

Parameters:
pPool  Memory pool for allocating memory. This pool is used to allocate the structures and other buffers that will contain the thread information. To free all of the information, the application only needs to call GedPoolFree().
ppThreadInfo  Pointer to array of thread information structures is returned here. The memory for these structures is allocated from the memory pool.
puiNumThreads  Number of structures in the array is returned here.
pszUrl  URL to use to send thread information request to a remote system (via TCP). This allows information to be collected from a remote FLAIM database system, as opposed to the local FLAIM system running inside the process space.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__cacheconfiguration.html0000644000175000017500000002517710510774540026021 0ustar ahodgkinsonahodgkinson FLAIM: Cache Configuration Functions

Cache Configuration Functions
[FLAIM System Functions]


Functions

FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit (FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave)
 Set dynamic cache limit.
FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit (FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate=FALSE)
 Set hard cache limit.
FLMEXP void FLMAPI FlmGetMemoryInfo (FLM_MEM_INFO *pMemInfo)
 Get cache information.

Function Documentation

FLMEXP void FLMAPI FlmGetMemoryInfo FLM_MEM_INFO pMemInfo  ) 
 

Get cache information.

Parameters:
pMemInfo  Memory information is returned here.

FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit FLMUINT  uiCacheAdjustPercent,
FLMUINT  uiCacheAdjustMin,
FLMUINT  uiCacheAdjustMax,
FLMUINT  uiCacheAdjustMinToLeave
 

Set dynamic cache limit.

Parameters:
uiCacheAdjustPercent  Percent of available memory to set cache limit to.
uiCacheAdjustMin  Minimum cache limit (bytes) to allow.
uiCacheAdjustMax  Maximum cache limit (bytes) to allow.
uiCacheAdjustMinToLeave  Minumum memory that must be left after setting cache limit. NOTE: This is an alternative to setting maximum. This parameter is ignored if uiCacheAdjustMax is non-zero.

FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit FLMUINT  uiPercent,
FLMBOOL  bPercentOfAvail,
FLMUINT  uiMin,
FLMUINT  uiMax,
FLMUINT  uiMinToLeave,
FLMBOOL  bPreallocate = FALSE
 

Set hard cache limit.

Parameters:
uiPercent  If non-zero, the hard limit is calculated as a percentage of either available memory or total physical memory. If zero, the uiMax parameter is the hard limit.
bPercentOfAvail  Only used if uiPercent is non-zero. If TRUE, the limit is calculated as a percentage of currently available memory. If FALSE, the limit is calculated as a percentage of total physical memory.
uiMin  Only used if uiPercent is non-zero. This is the minimum hard limit that can be set. If the calculated hard limit is less than this, it will be adjusted up to this minimum.
uiMax  If uiPercent is zero, this is the hard limit to set. Otherwise, this is the maximum limit that should be set. If the limit is calculated as a percentage of either available memory or total physical memory, and it is over this maximum, it will be adjusted down to this maximum. NOTE: When a calculation is being done, FLAIM will first adjust down to the maximum, and then, if necessary, up to the minimum.
uiMinToLeave  Only used if uiPercent is non-zero and uiMax is zero. In that scenario, the hard limit is being calculated, but no maximum limit was specified, so uiMinToLeave is used to calculate a maximum. The maximum will be calculated as the available memory (if bPercentOfAvail is TRUE) or total physical memory (if bPercentOfAvail is FALSE) minus uiMinToLeave.
bPreallocate  Preallocate all of the memory once the limit is calculated.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__stats.html0000644000175000017500000001177010510774540023316 0ustar ahodgkinsonahodgkinson FLAIM: Statistics Collection Functions

Statistics Collection Functions
[FLAIM System Functions]


Functions

FLMEXP RCODE FLMAPI FlmGetStats (FLM_STATS *pFlmStats)
 Get statistics.
FLMEXP void FLMAPI FlmFreeStats (FLM_STATS *pFlmStats)
 Free statistics.

Function Documentation

FLMEXP void FLMAPI FlmFreeStats FLM_STATS pFlmStats  ) 
 

Free statistics.

This function should be called to free whatever memory was allocated to retrieve statistics when FlmGetStats() was called.

Parameters:
pFlmStats  Statistics to be freed.

FLMEXP RCODE FLMAPI FlmGetStats FLM_STATS pFlmStats  ) 
 

Get statistics.

This function will allocate memory to return statistics. FlmFreeStats() should be called to free that memory once the application has processed the statistics.

Parameters:
pFlmStats  Statistics are returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__database.html0000644000175000017500000000631410510774540023722 0ustar ahodgkinsonahodgkinson FLAIM: Database Functions

Database Functions


Modules

 Database Create, Open, Close
 Transaction Functions
 Database Update Functions
 Record and Key Retrieval Functions
 Database Dictionary Functions
 Index Management Functions
 Database Configuration Functions
 Database Backup/Restore
 Database Maintenance Functions
 Database Copy, Rename, Delete Functions
 Database Encryption Key Management Functions

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbcreateopen.html0000644000175000017500000003236210510774540024613 0ustar ahodgkinsonahodgkinson FLAIM: Database Create, Open, Close

Database Create, Open, Close
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbCreate (const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, const char *pszDictFileName, const char *pszDictBuf, CREATE_OPTS *pCreateOpts, HFDB *phDb)
 Create a new database.
FLMEXP RCODE FLMAPI FlmDbOpen (const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, FLMUINT uiOpenFlags, const char *pszPassword, HFDB *phDb)
 Open a database.
FLMEXP RCODE FLMAPI FlmDbClose (HFDB *phDb)
 Close a database.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbClose HFDB phDb  ) 
 

Close a database.

Parameters:
phDb  Pointer to database handle that is to be closed. The database handle will be set back to HFDB_NULL.

FLMEXP RCODE FLMAPI FlmDbCreate const char *  pszDbFileName,
const char *  pszDataDir,
const char *  pszRflDir,
const char *  pszDictFileName,
const char *  pszDictBuf,
CREATE_OPTS pCreateOpts,
HFDB phDb
 

Create a new database.

Parameters:
pszDbFileName  Name of database to be created. May be full path name or partial path name.
pszDataDir  Name of directory where data files are to be created. If NULL, data files will be in the same directory as the main database file - pszDbFileName.
pszRflDir  Name of the directory where RFL files are to be created. If NULL, RFL files will be in the same directory as the main database file - pszDbFileName.
pszDictFileName  Name of a file containing dictionary definitions that are to be read in and put into the database's dictionary. This is only used if the pszDictBuf parameter is NULL. If both pszDictFileName and pszDictBuf parameters are NULL, the database's dictionary will not be populated.
pszDictBuf  String buffer containing dictionary definitions that are to be put into the database's dictionary. If this parameter is NULL, then pszDictFileName is used. If both pszDictFileName and pszDictBuf parameters are NULL, the database's dictionary will not be populated.
pCreateOpts  Create options for the database.
phDb  If database is successfully created, a database handle is returned here. It is not necessary to call FlmDbOpen() to get a database handle.

FLMEXP RCODE FLMAPI FlmDbOpen const char *  pszDbFileName,
const char *  pszDataDir,
const char *  pszRflDir,
FLMUINT  uiOpenFlags,
const char *  pszPassword,
HFDB phDb
 

Open a database.

Parameters:
pszDbFileName  Name of database to be opened. May be full path name or partial path name.
pszDataDir  Name of directory where data files for the database are located. If NULL, data files are assumed to be in the same directory as the main database file - pszDbFileName.
pszRflDir  Name of the directory where RFL files are located. If NULL, RFL files are assumed to be in the same directory as the main database file - pszDbFileName.
uiOpenFlags  Flags for opening the database. They are as follows:
  • FO_ALLOW_LIMITED - Allow limited access to database even if the database key cannot be accessed for some reason. It may be that NICI is not available, but the application would still like to be able to access non-encrypted data
  • FO_DONT_RESUME_BACKGROUND_THREADS - Tells FLAIM to NOT restart any indexing background threads. This should only be used when the application does not want modifications made to the database. This flag is only recognized on the first open of the database. If the database has already been opened elsewhere, this flag is ignored
  • FO_DONT_REDO_LOG - Don't replay the RFL log to recover transactions. This should only be performed if the application does not want the database to be changed in any way, including replaying of roll-forward logs. NOTE: The checkpoint thread will not be started for this database if this flag is set. This flag is only recognized on the first open of the database. If the database has already been opened elsewhere, this flag is ignored
pszPassword  Password for opening the database. This parameter is normally NULL. It should only be specified if the database's database key is currently wrapped in a password.
phDb  If database is successfully opened, database handle is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__trans.html0000644000175000017500000006636110510774540023315 0ustar ahodgkinsonahodgkinson FLAIM: Transaction Functions

Transaction Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbTransBegin (HFDB hDb, FLMUINT uiTransType, FLMUINT uiMaxLockWait, FLMBYTE *pucHeader=NULL)
 Begin a transaction on the database.
FLMEXP RCODE FLMAPI FlmDbTransCommit (HFDB hDb, FLMBOOL *pbEmpty=NULL)
 Commit current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbTransAbort (HFDB hDb)
 Abort current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbGetTransType (HFDB hDb, FLMUINT *puiTransType)
 Get type of current transaction (if any) on a database.
FLMEXP RCODE FLMAPI FlmDbGetTransId (HFDB hDb, FLMUINT *puiTransID)
 Get current transaction ID.
FLMEXP RCODE FLMAPI FlmDbGetCommitCnt (HFDB hDb, FLMUINT *puiCommitCount)
 Get number of committed transactions for a database.
FLMEXP RCODE FLMAPI FlmDbLock (HFDB hDb, eLockType lockType, FLMINT iPriority, FLMUINT uiTimeout)
 Lock a database.
FLMEXP RCODE FLMAPI FlmDbUnlock (HFDB hDb)
 Unlock a database.
FLMEXP RCODE FLMAPI FlmDbGetLockType (HFDB hDb, eLockType *pLockType, FLMBOOL *pbImplicit)
 Get the type of lock currently in effect on a database (if any).
FLMEXP RCODE FLMAPI FlmDbCheckpoint (HFDB hDb, FLMUINT uiTimeout)
 Perform a checkpoint on the database.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbCheckpoint HFDB  hDb,
FLMUINT  uiTimeout
 

Perform a checkpoint on the database.

Parameters:
hDb  Database handle.
uiTimeout  Specifies the maximum number of seconds to wait to obtain the database lock. An exclusive lock must be obtained to do a checkpoint. NOTE: A value of FLM_NO_TIMEOUT specifies that it should wait forever - until the lock becomes available.

FLMEXP RCODE FLMAPI FlmDbGetCommitCnt HFDB  hDb,
FLMUINT *  puiCommitCount
 

Get number of committed transactions for a database.

Parameters:
hDb  Database handle.
puiCommitCount  Number of transactions that have been committed is returned here.

FLMEXP RCODE FLMAPI FlmDbGetLockType HFDB  hDb,
eLockType pLockType,
FLMBOOL *  pbImplicit
 

Get the type of lock currently in effect on a database (if any).

Parameters:
hDb  Database handle.
pLockType  Type of lock currently held returned here.
pbImplicit  Flag indicating if the lock is an implicit lock. An implicit lock is one that FLAIM obtained automatically when it started an update transaction. An implicit lock will be released automatically when the transaction commits or aborts. An explicit lock is one which was obtained by calling FlmDbLock(). An explicit lock is released when the application calls FlmDbUnlock().

FLMEXP RCODE FLMAPI FlmDbGetTransId HFDB  hDb,
FLMUINT *  puiTransID
 

Get current transaction ID.

Parameters:
hDb  Database handle.
puiTransID  Current transaction ID is returned here. If no transaction is currently active, the function will return RCODE::FERR_NO_TRANS_ACTIVE.

FLMEXP RCODE FLMAPI FlmDbGetTransType HFDB  hDb,
FLMUINT *  puiTransType
 

Get type of current transaction (if any) on a database.

Parameters:
hDb  Database handle.
puiTransType  Transaction type is returned here. It will be one of the following:
  • FLM_NO_TRANS - No transaction currently active on this database handle
  • FLM_UPDATE_TRANS - Update transaction is active on this database handle
  • FLM_READ_TRANS - Read transaction is active on this database handle

FLMEXP RCODE FLMAPI FlmDbLock HFDB  hDb,
eLockType  lockType,
FLMINT  iPriority,
FLMUINT  uiTimeout
 

Lock a database.

Parameters:
hDb  Database handle.
lockType  Type of lock being requested.
iPriority  Priority of lock being requested.
uiTimeout  Specifies the maximum number of seconds to wait to obtain the lock. NOTE: A value of FLM_NO_TIMEOUT specifies that it should wait forever - until the lock becomes available.

FLMEXP RCODE FLMAPI FlmDbTransAbort HFDB  hDb  ) 
 

Abort current transaction (if any) on a database.

Parameters:
hDb  Database handle.

FLMEXP RCODE FLMAPI FlmDbTransBegin HFDB  hDb,
FLMUINT  uiTransType,
FLMUINT  uiMaxLockWait,
FLMBYTE *  pucHeader = NULL
 

Begin a transaction on the database.

Parameters:
hDb  Database handle.
uiTransType  Type of transaction to start. May be FLM_UPDATE_TRANS or FLM_READ_TRANS. The following flags may also be ORed into the transaction type to get special behaviors during the transaction:
  • FLM_DONT_KILL_TRANS - Marks a read transaction as one that cannot be killed by FLAIM. FLAIM will occasionally be forced to kill a long-running read transaction so that a checkpoint can be finished. It is NOT recommended that applications use this flag
  • FLM_DONT_POISON_CACHE - Marks the transaction so that any items the transaction brings into cache (records or blocks) will not poison cache. That is, they will not be allowed to replace other items. If an application has a transaction that is going to read through all of the records in the database, it would probably be wise to set this flag - so that it won't poison cache. However, generally it is not necessary to set this flag
uiMaxLockWait  Only applicable for update transactions. Specifies the maximum number of seconds to wait to obtain the database lock. NOTE: A value of FLM_NO_TIMEOUT specifies that it should wait forever - until the lock becomes available.
pucHeader  If non-NULL, the entire log header is returned in this buffer. The buffer should be at least F_TRANS_HEADER_SIZE bytes.

FLMEXP RCODE FLMAPI FlmDbTransCommit HFDB  hDb,
FLMBOOL *  pbEmpty = NULL
 

Commit current transaction (if any) on a database.

Parameters:
hDb  Database handle.
pbEmpty  If non-NULL, this returns a flag indicating whether or not the transaction was empty. This is only returned for update transactions. If TRUE, it means that no updates were performed inside the transaction, and hence, the transaction was not logged to the roll-forward log. Furthermore, the transaction ID and the count of committed transactions will not have been altered. In short, it will be as if the transaction never happened.

FLMEXP RCODE FLMAPI FlmDbUnlock HFDB  hDb  ) 
 

Unlock a database.

Parameters:
hDb  Database handle.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__update.html0000644000175000017500000005402310510774540023440 0ustar ahodgkinsonahodgkinson FLAIM: Database Update Functions

Database Update Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmRecordAdd (HFDB hDb, FLMUINT uiContainerNum, FLMUINT *puiDrn, FlmRecord *pRecord, FLMUINT uiAutoTrans)
 Add a record to the database.
FLMEXP RCODE FLMAPI FlmRecordModify (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FlmRecord *pRecord, FLMUINT uiAutoTrans)
 Modify a record in the database.
FLMEXP RCODE FLMAPI FlmRecordDelete (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FLMUINT uiAutoTrans)
 Delete a record from the database.
FLMEXP RCODE FLMAPI FlmReserveNextDrn (HFDB hDb, FLMUINT uiContainerNum, FLMUINT *puiDrn)
 Reserve the next available DRN in a database container.
FLMEXP RCODE FLMAPI FlmFindUnusedDictDrn (HFDB hDb, FLMUINT uiStartDrn, FLMUINT uiEndDrn, FLMUINT *puiDrn)
 Find an unused DRN in the dictionary.
FLMEXP RCODE FLMAPI FlmAllocBlob (FlmBlob **ppBlob)
 Allocate a BLOB object that can then be used to create a new BLOB to store in a FlmRecord object.

Function Documentation

FLMEXP RCODE FLMAPI FlmAllocBlob FlmBlob **  ppBlob  ) 
 

Allocate a BLOB object that can then be used to create a new BLOB to store in a FlmRecord object.

Parameters:
ppBlob  Pointer to newly allocated BLOB object is returned here.

FLMEXP RCODE FLMAPI FlmFindUnusedDictDrn HFDB  hDb,
FLMUINT  uiStartDrn,
FLMUINT  uiEndDrn,
FLMUINT *  puiDrn
 

Find an unused DRN in the dictionary.

Parameters:
hDb  Database handle.
uiStartDrn  Beginning of range of DRNs to look for an non-used DRN.
uiEndDrn  Ending of range of DRNs to look for a non-used DRN.
puiDrn  Unused DRN, if any, is returned here.

FLMEXP RCODE FLMAPI FlmRecordAdd HFDB  hDb,
FLMUINT  uiContainerNum,
FLMUINT *  puiDrn,
FlmRecord pRecord,
FLMUINT  uiAutoTrans
 

Add a record to the database.

Parameters:
hDb  Database handle.
uiContainerNum  Container record is to be added to.
puiDrn  On input, *puiDrn contains the DRN to be assigned to the record. If *puiDrn == 0 FLAIM will assign the DRN - it will be one higher than the highest DRN that was ever assigned in this container. In this case, *puiDrn will return the DRN that was assigned.
pRecord  Record to be added to the database. NOTE: After this record has been added to the database, the object pointed to by pRecord will have been inserted into FLAIM's record cache (unless the FLM_DONT_INSERT_IN_CACHE flag is set in the uiAutoTrans parameter). Once the object is cached, it is marked as read-only. This prevents it from being altered by the application. If the application desires to use pRecord to make subsequent modifications to the record, it must call the copy() method (FlmRecord::copy()) to obtain a writeable copy of the object.
uiAutoTrans  This is a set of flags and a timeout that can be used to accomplish the following things:
  • FLM_AUTO_TRANS - Specifies that if there is not already an update transaction going, this operation should have one auto-started and auto-committed for it. If this flag is set, the lower 8 bits of the parameter is assumed to be the timeout for obtaining the lock. A value of FLM_NO_TIMEOUT may be ORed in to specify that the operation should wait forever to obtain the database lock
  • FLM_DO_IN_BACKGROUND - This flag is only applicable when adding or modifying an index definition record in the dictionary. It specifies that the operation is to be performed in a background thread after the transaction in which this operation is performed has been committed. If the transaction aborts, no background thread will be launched
  • FLM_DONT_INSERT_IN_CACHE - This flag specifies that the record is not to be inserted into FLAIM's record cache
  • FLM_SUSPENDED - This flag is only applicable when adding or modifying an index definition record in the dictionary. It specifies that the index is to be immediately suspended after it is added or modified. This means that the index will not be populated until it is explicitly resumed by the application (see FlmIndexResume()).

FLMEXP RCODE FLMAPI FlmRecordDelete HFDB  hDb,
FLMUINT  uiContainerNum,
FLMUINT  uiDrn,
FLMUINT  uiAutoTrans
 

Delete a record from the database.

Parameters:
hDb  Database handle.
uiContainerNum  Container number the record is to be deleted from.
uiDrn  DRN of record to be deleted.
uiAutoTrans  See documentation for the uiAutoTrans parameter in the FlmRecordAdd() function. NOTE: The only flag that applies to FlmRecordDelete() is the FLM_AUTO_TRANS flag.

FLMEXP RCODE FLMAPI FlmRecordModify HFDB  hDb,
FLMUINT  uiContainerNum,
FLMUINT  uiDrn,
FlmRecord pRecord,
FLMUINT  uiAutoTrans
 

Modify a record in the database.

Parameters:
hDb  Database handle.
uiContainerNum  Container number record is to be modified in.
uiDrn  DRN of record to be modified.
pRecord  Record that is to replace the existing record. This is basically a "blind" update - this record will replace, in its entirety, the existing record. NOTE: After this record has been modified in the database, the object pointed to by pRecord will have been inserted into FLAIM's record cache (unless the FLM_DONT_INSERT_IN_CACHE flag is set in the uiAutoTrans parameter). Once the object is cached, it is marked as read-only. This prevents it from being altered by the application. If the application desires to use pRecord to make additional modifications to the record, it must call the copy() method (FlmRecord::copy()) to obtain a writeable copy of the object.
uiAutoTrans  See documentation for the uiAutoTrans parameter in the FlmRecordAdd() function.

FLMEXP RCODE FLMAPI FlmReserveNextDrn HFDB  hDb,
FLMUINT  uiContainerNum,
FLMUINT *  puiDrn
 

Reserve the next available DRN in a database container.

This allows an application to get a DRN before calling FlmRecordAdd(). It has the same effect as passing a zero into FlmRecordAdd(), except that no record is added to the database. The DRN returned from this function may then be passed into FlmRecordAdd() to assign the DRN to the record being added.

Parameters:
hDb  Database handle.
uiContainerNum  Container number the DRN is to be reserved from.
puiDrn  The reserved DRN is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__retrieval.html0000644000175000017500000003116610510774540024156 0ustar ahodgkinsonahodgkinson FLAIM: Record and Key Retrieval Functions

Record and Key Retrieval Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmRecordRetrieve (HFDB hDb, FLMUINT uiContainerNum, FLMUINT uiDrn, FLMUINT uiFlag, FlmRecord **ppRecord, FLMUINT *puiDrn)
 Find and retrieve a record in a container.
FLMEXP RCODE FLMAPI FlmKeyRetrieve (HFDB hDb, FLMUINT uiIndex, FLMUINT uiContainerNum, FlmRecord *pSearchKey, FLMUINT uiSearchDrn, FLMUINT uiFlags, FlmRecord **ppFoundKey, FLMUINT *puiFoundDrn)
 Find and retrieve a key in an index.

Function Documentation

FLMEXP RCODE FLMAPI FlmKeyRetrieve HFDB  hDb,
FLMUINT  uiIndex,
FLMUINT  uiContainerNum,
FlmRecord pSearchKey,
FLMUINT  uiSearchDrn,
FLMUINT  uiFlags,
FlmRecord **  ppFoundKey,
FLMUINT *  puiFoundDrn
 

Find and retrieve a key in an index.

Parameters:
hDb  Database handle.
uiIndex  Index the key is to be retrieved from.
uiContainerNum  If the index is a cross-container index, this may be used to specify a particular container the found key should be pointing to. A value of zero indicates that any container will do.
pSearchKey  Key to be searched for. NOTE: The actual key retrieved depends on the uiFlags parameter as well.
uiSearchDrn  DRN in the key's reference set that is to be searched for. If a zero is passed in this parameter, only a key search is done, not a key+reference search.
uiFlags  Flags used in conjunction with the pSearchKey and uiSearchDrn parameters to determine the key/DRN that should be retrieved. Flags may be ORed together and are as follows:
  • FO_INCL - Return either the exact key+reference specified by pSearchKey and uiSearchDrn or the next key+reference after. NOTE: This flag may be used in conjunction with the FO_KEY_EXACT flag - see documentation below
  • FO_EXCL - Retrieve the key+reference that comes after the key+reference specified by pSearchKey and uiSearchDrn. NOTE: This flag may be used in conjunction with the FO_KEY_EXACT flag - see documentation below
  • FO_KEY_EXACT - This flag is used in conjunction with the FO_INCL and FO_EXCL flags. It specifies that FlmKeyRetrieve() is NOT to go to the next key in the index, but confine itself to references for the key specified in pSearchKey. If there are no more references for the key specified in pSearchKey, FlmKeyRetrieve() will return RCODE::FERR_EOF_HIT, even if there are keys that come after pSearchKey
  • FO_EXACT - Retrieve the exact key+reference specified by pSearchKey and uiSearchDrn. If there is no such key+reference, the function should return RCODE::FERR_NOT_FOUND
  • FO_FIRST - Retrieve the first key+reference in the index. If this flag is passed in, all other flags will be ignored, as will pSearchKey and uiSearchDrn
  • FO_LAST - Retrieve the last key+reference in the index. If this flag is passed in, all other flags will be ignored except for FO_FIRST (which takes precedence over FO_LAST if they are both set), as will pSearchKey and uiSearchDrn
ppFoundKey  If non-NULL, found key is returned here.
puiFoundDrn  If non-NULL, found reference (DRN) is returned here.

FLMEXP RCODE FLMAPI FlmRecordRetrieve HFDB  hDb,
FLMUINT  uiContainerNum,
FLMUINT  uiDrn,
FLMUINT  uiFlag,
FlmRecord **  ppRecord,
FLMUINT *  puiDrn
 

Find and retrieve a record in a container.

Parameters:
hDb  Database handle.
uiContainerNum  Container the record is to be retrieved from.
uiDrn  DRN of record to be retrieved. NOTE: The actual record retrieved depends on the uiFlag parameter as well.
uiFlag  Flag that is used in conjunction with the uiDrn parameter to determine the record that should be retrieved. Flag may be one of the following:
  • FO_INCL - If the record specified by uiDrn is not found, find the record with the next highest DRN after it
  • FO_EXCL - Retrieve the record whose DRN is the next highest after the DRN specified in uiDrn
  • FO_EXACT - Retrieve the record whose DRN is specified by uiDrn. If there is no record with that DRN, the function should return RCODE::FERR_NOT_FOUND
  • FO_FIRST - Retrieve the first record in the container - the record with the lowest DRN. The uiDrn parameter is ignored if this flag is passed in
  • FO_LAST - Retrieve the last record in the container - the record with the highest DRN. The uiDrn parameter is ignored if this flag is passed in
ppRecord  If non-NULL, pointer to found record object is returned here.
puiDrn  If non-NULL, DRN of found record is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbdict.html0000644000175000017500000001025610510774540023407 0ustar ahodgkinsonahodgkinson FLAIM: Database Dictionary Functions

Database Dictionary Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmGetItemName (HFDB hDb, FLMUINT uiItemId, FLMUINT uiNameBufSize, char *pszNameBuf)
 Get the name of a dictionary item.

Function Documentation

FLMEXP RCODE FLMAPI FlmGetItemName HFDB  hDb,
FLMUINT  uiItemId,
FLMUINT  uiNameBufSize,
char *  pszNameBuf
 

Get the name of a dictionary item.

Parameters:
hDb  Database handle.
uiItemId  Dictionary ID whose name is to be returned.
uiNameBufSize  Size of pszNameBuf in bytes. Buffer should be large enough to hold the name of the dictionary item plus a null terminating character.
pszNameBuf  Dictionary name is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__indexing.html0000644000175000017500000002612010510774540023760 0ustar ahodgkinsonahodgkinson FLAIM: Index Management Functions

Index Management Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmIndexStatus (HFDB hDb, FLMUINT uiIndexNum, FINDEX_STATUS *pIndexStatus)
 Retrieve status of an index.
FLMEXP RCODE FLMAPI FlmIndexGetNext (HFDB hDb, FLMUINT *puiIndexNum)
 Retrieve next index.
FLMEXP RCODE FLMAPI FlmIndexSuspend (HFDB hDb, FLMUINT uiIndexNum)
 Suspend an index.
FLMEXP RCODE FLMAPI FlmIndexResume (HFDB hDb, FLMUINT uiIndexNum)
 Resume an index.

Function Documentation

FLMEXP RCODE FLMAPI FlmIndexGetNext HFDB  hDb,
FLMUINT *  puiIndexNum
 

Retrieve next index.

Parameters:
hDb  Database handle - see FlmDbOpen() or FlmDbCreate().
puiIndexNum  Index number is returned here.

FLMEXP RCODE FLMAPI FlmIndexResume HFDB  hDb,
FLMUINT  uiIndexNum
 

Resume an index.

Parameters:
hDb  Database handle - see FlmDbOpen() or FlmDbCreate().
uiIndexNum  Number of index to resume.

FLMEXP RCODE FLMAPI FlmIndexStatus HFDB  hDb,
FLMUINT  uiIndexNum,
FINDEX_STATUS pIndexStatus
 

Retrieve status of an index.

Parameters:
hDb  Database handle - see FlmDbOpen() or FlmDbCreate().
uiIndexNum  Index number to return status on.
pIndexStatus  Index status is returned in structure pointed to.

FLMEXP RCODE FLMAPI FlmIndexSuspend HFDB  hDb,
FLMUINT  uiIndexNum
 

Suspend an index.

Parameters:
hDb  Database handle - see FlmDbOpen() or FlmDbCreate().
uiIndexNum  Number of index to suspend.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbconfig.html0000644000175000017500000006352610510774540023741 0ustar ahodgkinsonahodgkinson FLAIM: Database Configuration Functions

Database Configuration Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbConfig (HFDB hDb, eDbConfigType eConfigType, void *pvValue1, void *pvValue2)
 Configure an open database.
FLMEXP RCODE FLMAPI FlmDbGetConfig (HFDB hDb, eDbGetConfigType eGetDbConfigType, void *pvValue1, void *pvValue2=NULL, void *pvValue3=NULL)
 Get configuration information on an open database.
FLMEXP void FLMAPI FlmSetIndexingCallback (HFDB hDb, IX_CALLBACK fnIxCallback, void *pvAppData)
 Set indexing callback function.
FLMEXP void FLMAPI FlmGetIndexingCallback (HFDB hDb, IX_CALLBACK *pfnIxCallback, void **ppvAppData)
 Get indexing callback function.
FLMEXP void FLMAPI FlmSetRecValidatorHook (HFDB hDb, REC_VALIDATOR_HOOK fnRecValidatorHook, void *pvAppData)
 Set record validator callback function.
FLMEXP void FLMAPI FlmGetRecValidatorHook (HFDB hDb, REC_VALIDATOR_HOOK *pfnRecValidatorHook, void **ppvAppData)
 Get the record validator callback function.
FLMEXP void FLMAPI FlmSetStatusHook (HFDB hDb, STATUS_HOOK fnStatusHook, void *pvAppData)
 Set the general purpose status callback function.
FLMEXP void FLMAPI FlmGetStatusHook (HFDB hDb, STATUS_HOOK *pfnStatusHook, void **ppvAppData)
 Get the general purpose status callback function.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbConfig HFDB  hDb,
eDbConfigType  eConfigType,
void *  pvValue1,
void *  pvValue2
 

Configure an open database.

Parameters:
hDb  Database handle of database that is to be configured.
eConfigType  Configuration option.
pvValue1  Configuration parameter. Type of value here depends on the eConfigType parameter. See documentation on eDbConfigType for details.
pvValue2  Configuration parameter. Type of value here depends on the eConfigType parameter. See documentation on eDbConfigType for details.

FLMEXP RCODE FLMAPI FlmDbGetConfig HFDB  hDb,
eDbGetConfigType  eGetDbConfigType,
void *  pvValue1,
void *  pvValue2 = NULL,
void *  pvValue3 = NULL
 

Get configuration information on an open database.

Parameters:
hDb  Database handle of database whose configuration information is to be retrieved.
eGetDbConfigType  Specifies what information is to be retrieved.
pvValue1  Information is returned via this parameter. Type of value required depends on the eGetDbConfigType parameter. See documentation on eDbGetConfigType for details.
pvValue2  Information is returned via this parameter. Type of value required depends on the eGetDbConfigType parameter. See documentation on eDbGetConfigType for details.
pvValue3  Information is returned via this parameter. Type of value required depends on the eGetDbConfigType parameter. See documentation on eDbGetConfigType for details.

FLMEXP void FLMAPI FlmGetIndexingCallback HFDB  hDb,
IX_CALLBACK pfnIxCallback,
void **  ppvAppData
 

Get indexing callback function.

Parameters:
hDb  Database handle whose indexing callback function is to be retrieved.
pfnIxCallback  Callback function is returned here. This is the function that was set using the FlmSetIndexingCallback() function.
ppvAppData  This returns the pointer to application data that was passed into the FlmSetIndexingCallback() function when the indexing callback function was set.

FLMEXP void FLMAPI FlmGetRecValidatorHook HFDB  hDb,
REC_VALIDATOR_HOOK pfnRecValidatorHook,
void **  ppvAppData
 

Get the record validator callback function.

Parameters:
hDb  Database handle whose record validator function is to be returned.
pfnRecValidatorHook  Record validator function is returned here. This is the function that was set using the FlmSetRecValidatorHook() function.
ppvAppData  This returns the pointer to application data that was passed into the FlmSetRecValidatorHook() function when the record validator function was set.

FLMEXP void FLMAPI FlmGetStatusHook HFDB  hDb,
STATUS_HOOK pfnStatusHook,
void **  ppvAppData
 

Get the general purpose status callback function.

Parameters:
hDb  Database handle whose general purpose status callback function is to be returned.
pfnStatusHook  Status callback function is returned here. This is the function that was set using the FlmSetStatusHook() function.
ppvAppData  This returns the pointer to application data that was passed into the FlmSetStatusHook() function when the status callback function was set.

FLMEXP void FLMAPI FlmSetIndexingCallback HFDB  hDb,
IX_CALLBACK  fnIxCallback,
void *  pvAppData
 

Set indexing callback function.

Parameters:
hDb  Database handle whose indexing callback function is to be set.
fnIxCallback  Indexing callback function.
pvAppData  Pointer to application data that will be passed into the callback function when it is called by FLAIM.

FLMEXP void FLMAPI FlmSetRecValidatorHook HFDB  hDb,
REC_VALIDATOR_HOOK  fnRecValidatorHook,
void *  pvAppData
 

Set record validator callback function.

Parameters:
hDb  Database handle whose record validator function is to be set.
fnRecValidatorHook  Record validator callback function. If this is NULL, record validation is disabled.
pvAppData  Pointer to application data that will be passed into the record validator function when it is called by FLAIM.

FLMEXP void FLMAPI FlmSetStatusHook HFDB  hDb,
STATUS_HOOK  fnStatusHook,
void *  pvAppData
 

Set the general purpose status callback function.

Parameters:
hDb  Database handle whose general purpose status callback function is to be set.
fnStatusHook  General purpose status callback function. If this is NULL, the general purpose status callback is disabled.
pvAppData  Pointer to application data that will be passed into the status callback function when it is called by FLAIM.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbbackup.html0000644000175000017500000005546210510774540023741 0ustar ahodgkinsonahodgkinson FLAIM: Database Backup/Restore

Database Backup/Restore
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbBackupBegin (HFDB hDb, FBackupType eBackupType, FLMBOOL bHotBackup, HFBACKUP *phBackup)
 Begin a database backup.
FLMEXP RCODE FLMAPI FlmBackupGetConfig (HFBACKUP hBackup, eBackupGetConfigType eConfigType, void *pvValue1, void *pvValue2=NULL)
 Get backup configuration on a backup that was started by FlmDbBackupBegin.
FLMEXP RCODE FLMAPI FlmDbBackup (HFBACKUP hBackup, const char *pszBackupPath, const char *pszPassword, BACKER_WRITE_HOOK fnWrite, STATUS_HOOK fnStatus, void *pvAppData, FLMUINT *puiIncSeqNum)
 Perform a backup that was started by FlmDbBackupBegin.
FLMEXP RCODE FLMAPI FlmDbBackupEnd (HFBACKUP *phBackup)
 End a backup that was started by FlmDbBackupBegin().
FLMEXP RCODE FLMAPI FlmDbRestore (const char *pszDbPath, const char *pszDataDir, const char *pszBackupPath, const char *pszRflDir, const char *pszPassword, F_Restore *pRestoreObj)
 Restore a database from a backup.

Function Documentation

FLMEXP RCODE FLMAPI FlmBackupGetConfig HFBACKUP  hBackup,
eBackupGetConfigType  eConfigType,
void *  pvValue1,
void *  pvValue2 = NULL
 

Get backup configuration on a backup that was started by FlmDbBackupBegin.

Parameters:
hBackup  Backup handle that was returned from FlmDbBackupBegin().
eConfigType  Type of configuration information being requested.
pvValue1  Configuration information is returned here. See documentation on eBackupGetConfigType for details.
pvValue2  Configuration information is returned here. See documentation on eBackupGetConfigType for details.

FLMEXP RCODE FLMAPI FlmDbBackup HFBACKUP  hBackup,
const char *  pszBackupPath,
const char *  pszPassword,
BACKER_WRITE_HOOK  fnWrite,
STATUS_HOOK  fnStatus,
void *  pvAppData,
FLMUINT *  puiIncSeqNum
 

Perform a backup that was started by FlmDbBackupBegin.

Parameters:
hBackup  Backup handle that was returned from FlmDbBackupBegin().
pszBackupPath  This specifieds the directory where FlmDbBackup() is to create a backup file set for the backed up data. The files in the backup set will be named 00000001.64, 00000002.64, etc. This parameter is only used if the fnWrite parameter is NULL. If fnWrite is non-NULL, all backed up data will be passed to that function to be written to a backup medium.
pszPassword  Password used to shroud the database encryption key in the backup. If NULL, the database encryption key will remain wrapped in the NICI local storage key. A NULL password means that the backup can only be restored to the same server the backup was taken from, because the database key can only be unwrapped using the NICI local storage key of that server.
fnWrite  This is the callback function that FlmDbBackup() will call to write data to the backup medium (tape, disk, etc.). If NULL, FlmDbBackup() will create a backup file set in the directory specified by the pszBackupPath parameter. If a callback function is specified, the application will also want to have a corresponding implementation for the F_Restore class so that it can read data back during a FlmDbRestore() operation.
fnStatus  This is a callback function that FlmDbBackup() calls to report backup progress.
pvAppData  Pointer to application data. This pointer will be passed into the fnWrite callback function as well as the fnStatus callback function whenever they are called.
puiIncSeqNum  If the backup is an incremental backup, this returns the incremental backup sequence number.

FLMEXP RCODE FLMAPI FlmDbBackupBegin HFDB  hDb,
FBackupType  eBackupType,
FLMBOOL  bHotBackup,
HFBACKUP phBackup
 

Begin a database backup.

Parameters:
hDb  Database handle.
eBackupType  Type of backup being requested.
bHotBackup  Specifies whether backup should be "hot" or "warm". A hot backup is one where the database is not locked during the backup. A "warm" backup is one where the database is locked during during the backup to prevent any updates from happening.
phBackup  A handle to a database backup object is returned here. This object is basically used to maintain state during the backup. It is passed into FlmBackupGetConfig(), FlmDbBackup(), and FlmDbBackupEnd().

FLMEXP RCODE FLMAPI FlmDbBackupEnd HFBACKUP phBackup  ) 
 

End a backup that was started by FlmDbBackupBegin().

This is necessary to free any resources (such as memory) that may have been allocated during the backup. This should always be called if FlmDbBackupBegin() is successful, even if FlmDbBackup() is never called, or if it fails with an error code.

Parameters:
phBackup  Pointer to backup handle that is to be freed.

FLMEXP RCODE FLMAPI FlmDbRestore const char *  pszDbPath,
const char *  pszDataDir,
const char *  pszBackupPath,
const char *  pszRflDir,
const char *  pszPassword,
F_Restore pRestoreObj
 

Restore a database from a backup.

Parameters:
pszDbPath  Name of database FlmDbRestore() is to create from the backup.
pszDataDir  Directory where the restored database's data files are to be created.
pszBackupPath  Directory where backup file set is located. If NULL, the backup data is read by calling various methods on the F_Restore object specified in the pRestoreObj parameter. Otherwise, the backup data is read from files in this directory that are named 00000001.64, 00000002.64, etc. These are files that would have been created by FlmDbBackup() if it was passed a backup path instead of a writer callback function.
pszRflDir  This is only used if pRestoreObj is NULL and pszBackupPath is non-NULL. It specifies the directory where RFL files are located. If possible, FlmDbRestore() will attempt to replay any RFL files that were created after the backup was taken. NOTE: The RFL files are actually in a subdirectory to the directory specified in this parameter. The subdirectory name is <dbname>.rfl, where dbname is the base name found in pszDbPath. For example, the dbname for abc/xyz.db would be xyz.db. Thus, the subdirectory would be xyz.rfl. If pszRflDir is NULL, FlmDbRestore() assumes that the RFL directory is the same as the directory for pszDbPath. <dbname>.rfl is still assumed to be the subdirectory.
pszPassword  Password for unshrouding the database key. This should be the same password that was used to create the backup - i.e. the password that was passed into FlmDbBackupBegin().
pRestoreObj  Pointer to the object whose methods will be called to read data from the backup medium. This object should know how to read data from the backup medium. It should understand whatever formatting was used by the fnWrite callback function (see FlmDbBackup()) to write the data out to the backup medium.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbmaint.html0000644000175000017500000006725110510774540023603 0ustar ahodgkinsonahodgkinson FLAIM: Database Maintenance Functions

Database Maintenance Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbCheck (HFDB hDb, const char *pszDbFileName, const char *pszDataDir, const char *pszRflDir, FLMUINT uiCheckFlags, F_Pool *pPool, DB_CHECK_PROGRESS *pDbStats, STATUS_HOOK fnStatusHook, void *pvAppArg)
 Check a database for corruptions.
FLMEXP RCODE FLMAPI FlmDbRebuild (const char *pszSourceDbPath, const char *pszSourceDataDir, const char *pszDestDbPath, const char *pszDestDataDir, const char *pszDestRflDir, const char *pszDictPath, CREATE_OPTS *pCreateOpts, FLMUINT *puiTotalRecords, FLMUINT *puiRecsRecovered, STATUS_HOOK fnStatusHook, void *pvAppData)
 Rebuild a database.
FLMEXP RCODE FLMAPI FlmDbReduceSize (HFDB hDb, FLMUINT uiCount, FLMUINT *puiCount)
 Reduce the database size - returning unused blocks back to the file system.
FLMEXP RCODE FLMAPI FlmDbSweep (HFDB hDb, FLMUINT uiSweepMode, FLMUINT uiCallbackFlags, STATUS_HOOK fnStatusHook, void *pvAppData)
 Traverse records in the database looking for unused fields.
FLMEXP RCODE FLMAPI FlmDbUpgrade (HFDB hDb, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Upgrade a database.
FLMEXP RCODE FLMAPI FlmMaintenanceStatus (HFDB hDb, FMAINT_STATUS *pMaintStatus)
 Get the current status of the background maintenance thread for a database.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbCheck HFDB  hDb,
const char *  pszDbFileName,
const char *  pszDataDir,
const char *  pszRflDir,
FLMUINT  uiCheckFlags,
F_Pool *  pPool,
DB_CHECK_PROGRESS pDbStats,
STATUS_HOOK  fnStatusHook,
void *  pvAppArg
 

Check a database for corruptions.

Parameters:
hDb  Database handle of database to be checked. If HFDB_NULL, FlmDbCheck will call FlmDbOpen() using the pszDbFileName, pszDataDir, pszRflDir, and uiFlags parameters.
pszDbFileName  Name of database to be checked. This is only used if hDb is HFDB_NULL.
pszDataDir  Directory where database's data files are located. This is only used if hDb is HFDB_NULL. If this parameter is NULL, data files are assumed to be in the same directory as pszDbFileName.
pszRflDir  Directory where database's RFL files are located. This is only used if hDb is HFDB_NULL. If this parameter is NULL, RFL files are assumed to be in the same directory as pszDbFileName.
uiCheckFlags  Checking options. May include one or more of the following flags ORed together:
  • FLM_CHK_INDEX_REFERENCING - Check logical integrity of indexes to make sure all keys in the index are in the referenced records and that all keys generated from records are in the index
  • FLM_CHK_FIELDS - Check fields in records
pPool  Memory pool for allocating memory to hold various statistics in the pDbStats parameter.
pDbStats  Statistics collected about the database during the check.
fnStatusHook  Callback status function.
pvAppArg  Pointer to application data. This pointer is passed into fnStatusHook whenever it is called.

FLMEXP RCODE FLMAPI FlmDbRebuild const char *  pszSourceDbPath,
const char *  pszSourceDataDir,
const char *  pszDestDbPath,
const char *  pszDestDataDir,
const char *  pszDestRflDir,
const char *  pszDictPath,
CREATE_OPTS pCreateOpts,
FLMUINT *  puiTotalRecords,
FLMUINT *  puiRecsRecovered,
STATUS_HOOK  fnStatusHook,
void *  pvAppData
 

Rebuild a database.

This function creates a new database from an existing database by extracting records from the existing database and inserting them into the new database. Any corrupted parts of the existing database are ignored. In this way, if a database is corrupt, a new database may be built from it that has no corruptions. In addition, indexes in the new database will be rebuilt.

Parameters:
pszSourceDbPath  Name of database to be rebuilt.
pszSourceDataDir  Directory where database's data files are located. If NULL, it is assumed that the database's data files are located in the same directory as pszSourceDbPath.
pszDestDbPath  Name of new database that is to be created.
pszDestDataDir  Directory where new database's data files are to be created. If NULL, data files will be created in the same directory as pszDestDbPath.
pszDestRflDir  Directory where new database's RFL files are to be created. If NULL, RFL files will be created in the same directory as pszDestDbPath.
pszDictPath  If non-NULL, this is the name of a file that has dictionary definitions which are to be loaded into the new database's dictionary container.
pCreateOpts  Create options for the new database.
puiTotalRecords  Returns the total number of records that were found in the source database.
puiRecsRecovered  Returns the total number of records that were recovered from the source database.
fnStatusHook  Callback function. FlmDbRebuild() calls this function to report rebuild progress.
pvAppData  Pointer to application data which will be passed into the callback function whenever it is called.

FLMEXP RCODE FLMAPI FlmDbReduceSize HFDB  hDb,
FLMUINT  uiCount,
FLMUINT *  puiCount
 

Reduce the database size - returning unused blocks back to the file system.

Parameters:
hDb  Database handle.
uiCount  Maximum number of unused blocks to be returned to file system.
puiCount  Number of blocks actually returned. This should be the same as the number of blocks requested, unless there are not that many unused blocks.

FLMEXP RCODE FLMAPI FlmDbSweep HFDB  hDb,
FLMUINT  uiSweepMode,
FLMUINT  uiCallbackFlags,
STATUS_HOOK  fnStatusHook,
void *  pvAppData
 

Traverse records in the database looking for unused fields.

Parameters:
hDb  Database handle.
uiSweepMode  Flags indicating what actions FlmDbSweep() should do while it is traversing the database. It may be any of the following flags ORed together:
  • SWEEP_CHECKING_FLDS - Look for field definitions marked as "checking". These fields should be checked to see if they are still in use
  • SWEEP_PURGED_FLDS - Look for field definitions marke as "purged". These fields should be removed from records
  • SWEEP_STATS - Only calls the callback function. This provides a way for an application to collect database statistics
uiCallbackFlags  Flags indicating what what events FlmDbSweep() should report through the callback function. It may be any of the following flags ORed together:
  • EACH_CONTAINER - Callback function is called whenever FlmDbSweep() begins traversing a new container
  • EACH_RECORD - Callback function is called for each record traversed
  • EACH_FIELD - Callback function is called for each field traversed
  • EACH_CHANGE - Callback function is called whenever a field that was marked as "checking" is found in the database and the state is changed back to "unused". It is also calld whenever a field that was marked as "purged" is removed from a record
fnStatusHook  Callback function. See eStatusType::FLM_SWEEP_STATUS for documentation on data that is passed to the callback function when it is called.
pvAppData  Pointer to application data that will be passed to the callback function whenever it is called.

FLMEXP RCODE FLMAPI FlmDbUpgrade HFDB  hDb,
FLMUINT  uiNewVersion,
STATUS_HOOK  fnStatusCallback,
void *  pvAppData
 

Upgrade a database.

Parameters:
hDb  Database handle.
uiNewVersion  Version database is to be upgraded to. This must be greater than the current version of the database.
fnStatusCallback  Callback function that is called while the database is being upgraded. See documentaiton eStatusType::FLM_DB_UPGRADE_STATUS for documentation on data that is passed to the callback function when it is called.
pvAppData  Pointer to application data that will be passed to the callback function whenever it is called.

FLMEXP RCODE FLMAPI FlmMaintenanceStatus HFDB  hDb,
FMAINT_STATUS pMaintStatus
 

Get the current status of the background maintenance thread for a database.

Parameters:
hDb  Database handle.
pMaintStatus  Status is returned in this structure.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__dbcopy.html0000644000175000017500000003572410510774540023445 0ustar ahodgkinsonahodgkinson FLAIM: Database Copy, Rename, Delete Functions

Database Copy, Rename, Delete Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmDbCopy (const char *pszSrcDbName, const char *pszSrcDataDir, const char *pszSrcRflDir, const char *pszDestDbName, const char *pszDestDataDir, const char *pszDestRflDir, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Copy a database.
FLMEXP RCODE FLMAPI FlmDbRename (const char *pszDbName, const char *pszDataDir, const char *pszRflDir, const char *pszNewDbName, FLMBOOL bOverwriteDestOk, STATUS_HOOK fnStatusCallback, void *pvAppData)
 Rename a database.
FLMEXP RCODE FLMAPI FlmDbRemove (const char *pszDbName, const char *pszDataDir, const char *pszRflDir, FLMBOOL bRemoveRflFiles)
 Delete a database.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbCopy const char *  pszSrcDbName,
const char *  pszSrcDataDir,
const char *  pszSrcRflDir,
const char *  pszDestDbName,
const char *  pszDestDataDir,
const char *  pszDestRflDir,
STATUS_HOOK  fnStatusCallback,
void *  pvAppData
 

Copy a database.

Parameters:
pszSrcDbName  Name of database to be copied. May be full path name or partial path name.
pszSrcDataDir  Name of directory where data files for the database are located. If NULL, data files are assumed to be in the same directory as the main database file - pszSrcDbName.
pszSrcRflDir  Name of the directory where RFL files are located. If NULL, RFL files are assumed to be in the same directory as the main database file - pszSrcDbName.
pszDestDbName  Name of destination database. May be full path name or partial path name.
pszDestDataDir  Name of desitnation directory where data files for the new database are to be copied. If NULL, destination data files will be copied to the same directory as the main destination database file - pszDestDbName.
pszDestRflDir  Name of desitnation directory where RFL files for the new database are to be copied. If NULL, destination RFL files will be copied to the same directory as the main destination database file - pszDestDbName.
fnStatusCallback  Callback function called by FlmDbCopy() to show copy progress. See documentation on eStatusType::FLM_DB_COPY_STATUS for information on the data that FlmDbCopy() will pass to the callback function.
pvAppData  Pointer to application data that will be passed to the callback function whenever it is called.

FLMEXP RCODE FLMAPI FlmDbRemove const char *  pszDbName,
const char *  pszDataDir,
const char *  pszRflDir,
FLMBOOL  bRemoveRflFiles
 

Delete a database.

Parameters:
pszDbName  Name of database to be deleted. May be full path name or partial path name.
pszDataDir  Name of directory where data files for the database are located. If NULL, data files are assumed to be in the same directory as the main database file - pszDbName.
pszRflDir  Name of the directory where RFL files are located. If NULL, RFL files are assumed to be in the same directory as the main database file - pszDbName.
bRemoveRflFiles  Flag indicating whether or not RFL files should be deleted.

FLMEXP RCODE FLMAPI FlmDbRename const char *  pszDbName,
const char *  pszDataDir,
const char *  pszRflDir,
const char *  pszNewDbName,
FLMBOOL  bOverwriteDestOk,
STATUS_HOOK  fnStatusCallback,
void *  pvAppData
 

Rename a database.

Parameters:
pszDbName  Name of database to be renamed. May be full path name or partial path name.
pszDataDir  Name of directory where data files for the database are located. If NULL, data files are assumed to be in the same directory as the main database file - pszDbName.
pszRflDir  Name of the directory where RFL files are located. If NULL, RFL files are assumed to be in the same directory as the main database file - pszDbName.
pszNewDbName  New name to be given to the database. NOTE: All data files and RFL subdirectories will be renamed using this name as the template.
bOverwriteDestOk  Flag indicating whether or not it is ok to overwrite the destination database if a database already exists with the new name.
fnStatusCallback  Callback function called by FlmDbRename() to show copy progress. See documentation on eStatusType::FLM_DB_RENAME_STATUS for information on the data that FlmDbRename() will pass to the callback function.
pvAppData  Pointer to application data that will be passed to the callback function whenever it is called.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__encryption.html0000644000175000017500000001516210510774540024351 0ustar ahodgkinsonahodgkinson FLAIM: Database Encryption Key Management Functions

Database Encryption Key Management Functions
[Database Functions]


Functions

FLMEXP RCODE FLMAPI FlmEnableEncryption (HFDB hDb, FLMBYTE **ppucWrappingKey, FLMUINT32 *pui32KeyLen)
 Enable encryption for a database.
FLMEXP RCODE FLMAPI FlmDbWrapKey (HFDB hDb, const char *pszPassword)
 Wrap a database's encryption key in a password.

Function Documentation

FLMEXP RCODE FLMAPI FlmDbWrapKey HFDB  hDb,
const char *  pszPassword
 

Wrap a database's encryption key in a password.

Parameters:
hDb  Database handle.
pszPassword  Password to wrap the database key in. May be NULL to wrap the key in the NICI local storage key. NOTE: Once the database key has been wrapped in a password, that password must be supplied to FlmDbOpen() when opening the database.

FLMEXP RCODE FLMAPI FlmEnableEncryption HFDB  hDb,
FLMBYTE **  ppucWrappingKey,
FLMUINT32 *  pui32KeyLen
 

Enable encryption for a database.

Parameters:
hDb  Database handle.
ppucWrappingKey  This returns a pointer to a buffer containing the database key wrapped in the NICI local storage key. FlmEnableEncryption() allocates memory for this buffer. The memory must be freed by calling FlmFreeMem().
pui32KeyLen  Length of data in *ppucWrappingKey.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__query.html0000644000175000017500000000402510510774540023320 0ustar ahodgkinsonahodgkinson FLAIM: Query Functions

Query Functions


Modules

 Query Object Creation/Initialization/Deletion
 Query Criteria Definition Functions
 Query Result Set Retrieval Functions
 Query Configuration
 Comparison Functions

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__queryobj.html0000644000175000017500000002456510510774540024026 0ustar ahodgkinsonahodgkinson FLAIM: Query Object Creation/Initialization/Deletion

Query Object Creation/Initialization/Deletion
[Query Functions]


Functions

FLMEXP RCODE FLMAPI FlmCursorInit (HFDB hDb, FLMUINT uiContainerNum, HFCURSOR *phCursor)
 Initialize a query object.
FLMEXP RCODE FLMAPI FlmCursorFree (HFCURSOR *phCursor)
 Free a query object.
FLMEXP void FLMAPI FlmCursorReleaseResources (HFCURSOR hCursor)
 Release query object resources.
FLMEXP RCODE FLMAPI FlmCursorClone (HFCURSOR hSource, HFCURSOR *phCursor)
 Clone a query object.

Function Documentation

FLMEXP RCODE FLMAPI FlmCursorClone HFCURSOR  hSource,
HFCURSOR phCursor
 

Clone a query object.

The new cloned query object should be set up with the same query criteria as the query object being cloned, but it should not be optimized yet.

Parameters:
hSource  Handle to query object that is to be cloned.
phCursor  Newly cloned query object handle is returned here.

FLMEXP RCODE FLMAPI FlmCursorFree HFCURSOR phCursor  ) 
 

Free a query object.

Parameters:
phCursor  Pointer to query handle to be freed. Should be the handle returned from FlmCursorInit().

FLMEXP RCODE FLMAPI FlmCursorInit HFDB  hDb,
FLMUINT  uiContainerNum,
HFCURSOR phCursor
 

Initialize a query object.

Parameters:
hDb  Database handle.
uiContainerNum  Container to be searched.
phCursor  Query handle is returned here.

FLMEXP void FLMAPI FlmCursorReleaseResources HFCURSOR  hCursor  ) 
 

Release query object resources.

NOTE: This will free all of the resources for a query object except those needed to display the query's criteria and any statistics for the query that were collected while it was running. After this method is called, the query object is no longer in a state where it can be used to retrieve records from the query result set.

Parameters:
hCursor  Handle to query object whose resources are to be released.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__querydef.html0000644000175000017500000006327410510774540024012 0ustar ahodgkinsonahodgkinson FLAIM: Query Criteria Definition Functions

Query Criteria Definition Functions
[Query Functions]


Functions

FLMEXP RCODE FLMAPI FlmParseQuery (HFCURSOR hCursor, F_NameTable *pNameTable, const char *pszQueryCriteria)
 Parse a query string to set query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddField (HFCURSOR hCursor, FLMUINT uiFieldNum, FLMUINT uiFlags)
 Add a field to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddFieldPath (HFCURSOR hCursor, FLMUINT *puiFldPath, FLMUINT uiFlags)
 Add a field path to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddUserPredicate (HFCURSOR hCursor, FlmUserPredicate *pPredicate)
 Add an application defined predicate to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddFieldCB (HFCURSOR hCursor, FLMUINT *puiFldPath, FLMUINT uiFlags, FLMBOOL bValidateOnly, CURSOR_GET_FIELD_CB fnGetField, void *pvAppData, FLMUINT uiUserDataLen)
 Add a field callback function to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddOp (HFCURSOR hCursor, QTYPES eOperator, FLMBOOL bResolveUnknown=FALSE)
 Add a query operator to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorAddValue (HFCURSOR hCursor, QTYPES eValType, void *pVal, FLMUINT uiValLen)
 Add a value to the query criteria.
FLMEXP RCODE FLMAPI FlmCursorValidate (HFCURSOR hCursor)
 Finalize and validate query syntax.

Function Documentation

FLMEXP RCODE FLMAPI FlmCursorAddField HFCURSOR  hCursor,
FLMUINT  uiFieldNum,
FLMUINT  uiFlags
 

Add a field to the query criteria.

Parameters:
hCursor  Handle to query object.
uiFieldNum  Field number that is to be added to query criteria.
uiFlags  Flags for field. Flags may be any of the following ORed together:
  • FLM_USE_DEFAULT_VALUE - This specifies that if the field is not found in a record that is being evaluated, FLAIM should use a default value. The default value is calculated based on the field's data type
  • FLM_SINGLE_VALUED - This tells FLAIM that the field is known to never have more than one value per record. This allows FLAIM to evaluate the predicate using information from an index key, without having to fetch the record to evaluate all possible values

FLMEXP RCODE FLMAPI FlmCursorAddFieldCB HFCURSOR  hCursor,
FLMUINT *  puiFldPath,
FLMUINT  uiFlags,
FLMBOOL  bValidateOnly,
CURSOR_GET_FIELD_CB  fnGetField,
void *  pvAppData,
FLMUINT  uiUserDataLen
 

Add a field callback function to the query criteria.

This function can be called anywhere an application might call FlmCursorAddField() or FlmCursorAddFieldPath(). Added a field callback function gives the application more flexibility in how fields are retrieved from records and validated. Special processing rules may be applied to determine if a field is valid. In addition, the callback function may be used to determine the field to be returned - even fetching it from other records or external sources.

Parameters:
hCursor  Handle to query object.
puiFldPath  Field path. This field path is passed into the field callback when it is called.
uiFlags  Flags for field. See documentation on uiFlags parameter of FlmCursorAddField().
bValidateOnly  Field should only be validated by the callback. Instances of the field to be validated will be found by FLAIM.
fnGetField  Field callback function.
pvAppData  Pointer to application data. This will be passed into the callback function when it is called.
uiUserDataLen  Length of data pointed to by pvAppData. FLAIM will copy the application data as needed. It uses memcpy to do this, so it should always be a simple structure instead of a structure with pointers to other structures - because FLAIM will not know to follow those pointers when copying the data.

FLMEXP RCODE FLMAPI FlmCursorAddFieldPath HFCURSOR  hCursor,
FLMUINT *  puiFldPath,
FLMUINT  uiFlags
 

Add a field path to the query criteria.

Parameters:
hCursor  Handle to query object.
puiFldPath  Field path that is to be added to query criteria. Field path is an array of zero-terminated field numbers.
uiFlags  Flags for field. See documentation on uiFlags parameter of FlmCursorAddField().

FLMEXP RCODE FLMAPI FlmCursorAddOp HFCURSOR  hCursor,
QTYPES  eOperator,
FLMBOOL  bResolveUnknown = FALSE
 

Add a query operator to the query criteria.

Parameters:
hCursor  Handle to query object.
eOperator  Operator to be added to the query criteria.
bResolveUnknown  Resolve comparison operators to TRUE or FALSE even if one of the operands is unknown.

FLMEXP RCODE FLMAPI FlmCursorAddUserPredicate HFCURSOR  hCursor,
FlmUserPredicate pPredicate
 

Add an application defined predicate to the query criteria.

Parameters:
hCursor  Handle to query object.
pPredicate  User defined predicate object.

FLMEXP RCODE FLMAPI FlmCursorAddValue HFCURSOR  hCursor,
QTYPES  eValType,
void *  pVal,
FLMUINT  uiValLen
 

Add a value to the query criteria.

A value is generally added where an operand would appear - such as in a comparison expression.

Parameters:
hCursor  Handle to query object.
eValType  Type of value being added to the query criteria.
pVal  Pointer to the value being added. This should point to a value that corresponds to the type specified in eValType.
uiValLen  For binary values, this will contain the value length. It is not used for any other type of value. String values are expected to be null-terminated.

FLMEXP RCODE FLMAPI FlmCursorValidate HFCURSOR  hCursor  ) 
 

Finalize and validate query syntax.

After this function has been called, no more query criteria may be added.

Parameters:
hCursor  Handle to query object.

FLMEXP RCODE FLMAPI FlmParseQuery HFCURSOR  hCursor,
F_NameTable pNameTable,
const char *  pszQueryCriteria
 

Parse a query string to set query criteria.

Parameters:
hCursor  Handle to query object whose criteria is to be set.
pNameTable  Name table to use when looking up field names.
pszQueryCriteria  Query criteria.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__queryret.html0000644000175000017500000007453110510774540024044 0ustar ahodgkinsonahodgkinson FLAIM: Query Result Set Retrieval Functions

Query Result Set Retrieval Functions
[Query Functions]


Functions

FINLINE RCODE FlmCursorFirst (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the first record in the query result set.
FINLINE RCODE FlmCursorLast (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the last record in the query result set.
FINLINE RCODE FlmCursorNext (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the next record in the query result set.
FINLINE RCODE FlmCursorPrev (HFCURSOR hCursor, FlmRecord **ppRecord)
 Positions to and retrieves the previous record in the query result set.
FINLINE RCODE FlmCursorFirstDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the first record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorLastDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the last record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorNextDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the next record in the query result set and retrieves the record's DRN.
FINLINE RCODE FlmCursorPrevDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Positions to the previous record in the query result set and retrieves the record's DRN.
FLMEXP RCODE FLMAPI FlmCursorCurrent (HFCURSOR hCursor, FlmRecord **ppRecord)
 Retrieve current record from query result set.
FLMEXP RCODE FLMAPI FlmCursorCurrentDRN (HFCURSOR hCursor, FLMUINT *puiDrn)
 Retrieve the DRN of the current recrord in query result set.
FLMEXP RCODE FLMAPI FlmCursorMoveRelative (HFCURSOR hCursor, FLMINT *piPosition, FlmRecord **ppRecord)
 Position relative to the current record (forward or backward) in the query result set and retrieve the record positioned to.
FLMEXP RCODE FLMAPI FlmCursorRecCount (HFCURSOR hCursor, FLMUINT *puiCount)
 Get record count for a query result set.

Function Documentation

FLMEXP RCODE FLMAPI FlmCursorCurrent HFCURSOR  hCursor,
FlmRecord **  ppRecord
 

Retrieve current record from query result set.

Parameters:
hCursor  Handle to query object.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FLMEXP RCODE FLMAPI FlmCursorCurrentDRN HFCURSOR  hCursor,
FLMUINT *  puiDrn
 

Retrieve the DRN of the current recrord in query result set.

Parameters:
hCursor  Handle to query object.
puiDrn  DRN is returned here.

FINLINE RCODE FlmCursorFirst HFCURSOR  hCursor,
FlmRecord **  ppRecord
 

Positions to and retrieves the first record in the query result set.

Parameters:
hCursor  Handle to query object.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FINLINE RCODE FlmCursorFirstDRN HFCURSOR  hCursor,
FLMUINT *  puiDrn
 

Positions to the first record in the query result set and retrieves the record's DRN.

Parameters:
hCursor  Handle to query object.
puiDrn  DRN is returned here.

FINLINE RCODE FlmCursorLast HFCURSOR  hCursor,
FlmRecord **  ppRecord
 

Positions to and retrieves the last record in the query result set.

Parameters:
hCursor  Handle to query object.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FINLINE RCODE FlmCursorLastDRN HFCURSOR  hCursor,
FLMUINT *  puiDrn
 

Positions to the last record in the query result set and retrieves the record's DRN.

Parameters:
hCursor  Handle to query object.
puiDrn  DRN is returned here.

FLMEXP RCODE FLMAPI FlmCursorMoveRelative HFCURSOR  hCursor,
FLMINT *  piPosition,
FlmRecord **  ppRecord
 

Position relative to the current record (forward or backward) in the query result set and retrieve the record positioned to.

Parameters:
hCursor  Handle to query object.
piPosition  On input *piPosition indicates the relative position to move within the query result set. A negative value will move the position back *piPosition records and a positive value will move the position forward *piPosition records. On output, *piPosition will return the relative position after the move. This should always equal the input position unless an error is returned.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FINLINE RCODE FlmCursorNext HFCURSOR  hCursor,
FlmRecord **  ppRecord
 

Positions to and retrieves the next record in the query result set.

Parameters:
hCursor  Handle to query object.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FINLINE RCODE FlmCursorNextDRN HFCURSOR  hCursor,
FLMUINT *  puiDrn
 

Positions to the next record in the query result set and retrieves the record's DRN.

Parameters:
hCursor  Handle to query object.
puiDrn  DRN is returned here.

FINLINE RCODE FlmCursorPrev HFCURSOR  hCursor,
FlmRecord **  ppRecord
 

Positions to and retrieves the previous record in the query result set.

Parameters:
hCursor  Handle to query object.
ppRecord  Pointer to found record, if any, is returned here. NULL is returned if no record was found.

FINLINE RCODE FlmCursorPrevDRN HFCURSOR  hCursor,
FLMUINT *  puiDrn
 

Positions to the previous record in the query result set and retrieves the record's DRN.

Parameters:
hCursor  Handle to query object.
puiDrn  DRN is returned here.

FLMEXP RCODE FLMAPI FlmCursorRecCount HFCURSOR  hCursor,
FLMUINT *  puiCount
 

Get record count for a query result set.

NOTE: This function generates the query result set, counting the records as it goes. Therefore, it may take a long time to compute, depending on the size of the result set and whether or not indexes can be used to optimize the query.

Parameters:
hCursor  Handle to query object.
puiCount  Count of records in the query result set is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__queryconfig.html0000644000175000017500000003300310510774540024504 0ustar ahodgkinsonahodgkinson FLAIM: Query Configuration

Query Configuration
[Query Functions]


Functions

FLMEXP RCODE FLMAPI FlmCursorConfig (HFCURSOR hCursor, eCursorConfigType eConfigType, void *pvValue1, void *pvValue2)
 Configure a query object.
FLMEXP RCODE FLMAPI FlmCursorGetConfig (HFCURSOR hCursor, eCursorGetConfigType eGetConfigType, void *pvValue1, void *pvValue2)
 Get query configuration.
FLMEXP RCODE FLMAPI FlmCursorSetOrderIndex (HFCURSOR hCursor, FLMUINT *puiFieldPaths, FLMUINT *puiIndex)
 Set order index for a query.
FLMEXP RCODE FLMAPI FlmCursorSetMode (HFCURSOR hCursor, FLMUINT uiFlags)
 Set mode for string comparison operations in query criteria.

Function Documentation

FLMEXP RCODE FLMAPI FlmCursorConfig HFCURSOR  hCursor,
eCursorConfigType  eConfigType,
void *  pvValue1,
void *  pvValue2
 

Configure a query object.

Parameters:
hCursor  Handle to query object that is to be configured.
eConfigType  Specifies what is to be configured in the query object.
pvValue1  Configuration parameter - depends on eConfigType - see documentation on eCursorConfigType.
pvValue2  Configuration parameter - depends on eConfigType - see documentation on eCursorConfigType.

FLMEXP RCODE FLMAPI FlmCursorGetConfig HFCURSOR  hCursor,
eCursorGetConfigType  eGetConfigType,
void *  pvValue1,
void *  pvValue2
 

Get query configuration.

Parameters:
hCursor  Handle to query object whose configuration information is to be retrieved.
eGetConfigType  Specifies what configuration information is to be retrieved.
pvValue1  Configuration parameter - depends on eGetConfigType - see documentation on eCursorGetConfigType.
pvValue2  Configuration parameter - depends on eGetConfigType - see documentation on eCursorGetConfigType.

FLMEXP RCODE FLMAPI FlmCursorSetMode HFCURSOR  hCursor,
FLMUINT  uiFlags
 

Set mode for string comparison operations in query criteria.

Parameters:
hCursor  Handle to query object whose order index is to be set.
uiFlags  Mode flags to be set for the query. Multiple flags may be ORed together. Valid flags are as follows:
  • FLM_WILD - Treat '*' as a wildcard
  • FLM_NOCASE - Case-insensitive comparison
  • FLM_NO_SPACE - Ignore all white space
  • FLM_NO_DASH - Ignore all dash characters (-)
  • FLM_NO_UNDERSCORE - Treat underscores as white space
  • FLM_MIN_SPACES - Ignore leading and trailing white space, and compress consecutive white space into a single space character

FLMEXP RCODE FLMAPI FlmCursorSetOrderIndex HFCURSOR  hCursor,
FLMUINT *  puiFieldPaths,
FLMUINT *  puiIndex
 

Set order index for a query.

Parameters:
hCursor  Handle to query object whose order index is to be set.
puiFieldPaths  List of field paths that specify the desired ordering. Each field path is terminated with a single zero, and the entire list is terminated by two zeroes.
puiIndex  Index is returned here. If zero is returned, it means that no index could be found that matched the specified field order.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__querycomp.html0000644000175000017500000002647610510774540024215 0ustar ahodgkinsonahodgkinson FLAIM: Comparison Functions

Comparison Functions
[Query Functions]


Functions

FLMEXP RCODE FLMAPI FlmCursorCompareDRNs (HFCURSOR hCursor, FLMUINT uiDRN1, FLMUINT uiDRN2, FLMUINT uiTimeLimit, FLMINT *piCmpResult, FLMBOOL *pbTimedOut, FLMUINT *puiKeyCount)
 Determine the relative position of two records in a query's result set.
FLMEXP RCODE FLMAPI FlmCursorTestRec (HFCURSOR hCursor, FlmRecord *pRecord, FLMBOOL *pbIsMatch)
 Test a record to see if it passes the query criteria.
FLMEXP RCODE FLMAPI FlmCursorTestDRN (HFCURSOR hCursor, FLMUINT uiDRN, FLMBOOL *pbIsMatch)
 Retrieve and test a record (using a DRN) to see if it passes the query criteria.

Function Documentation

FLMEXP RCODE FLMAPI FlmCursorCompareDRNs HFCURSOR  hCursor,
FLMUINT  uiDRN1,
FLMUINT  uiDRN2,
FLMUINT  uiTimeLimit,
FLMINT *  piCmpResult,
FLMBOOL *  pbTimedOut,
FLMUINT *  puiKeyCount
 

Determine the relative position of two records in a query's result set.

This only makes sense if the query is optimized using an index. The function does the following:

  1. Reads the two records from the database
  2. Uses the query's index to get the index keys contained in the two records
  3. Compares the keys to determine which is greater
  4. Optionally gets an count of the keys between the two keys (count is inclusive).
Parameters:
hCursor  Handle to query object.
uiDRN1  DRN of first record to be compared.
uiDRN2  DRN of second record to be compated.
uiTimeLimit  Timeout for this operation. Timeout is in seconds.
piCmpResult  Comparison results is returned here.
pbTimedOut  Did the function time out?
puiKeyCount  Count of index keys betwen the two records (inclusive).

FLMEXP RCODE FLMAPI FlmCursorTestDRN HFCURSOR  hCursor,
FLMUINT  uiDRN,
FLMBOOL *  pbIsMatch
 

Retrieve and test a record (using a DRN) to see if it passes the query criteria.

Parameters:
hCursor  Handle to query object.
uiDRN  DRN of record to be tested against the query criteria. FLAIM will retrieve the record and test it. The container is the container that was passed into FlmCursorInit().
pbIsMatch  Flag is returned here indicating whether or not the record matches the criteria.

FLMEXP RCODE FLMAPI FlmCursorTestRec HFCURSOR  hCursor,
FlmRecord pRecord,
FLMBOOL *  pbIsMatch
 

Test a record to see if it passes the query criteria.

Parameters:
hCursor  Handle to query object.
pRecord  Record to be tested against the query criteria.
pbIsMatch  Flag is returned here indicating whether or not the record matches the criteria.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__misc.html0000644000175000017500000000467010510774540023114 0ustar ahodgkinsonahodgkinson FLAIM: Miscellaneous Functions

Miscellaneous Functions


Modules

 Error Handling Functions
 Storage Format Conversion Functions
 Language To String Conversion Functions
 String Comparison Functions
 Event Registration/Deregistration Functions
 Pool Memory Functions
 Memory Functions

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__errhandling.html0000644000175000017500000001666010510774540024460 0ustar ahodgkinsonahodgkinson FLAIM: Error Handling Functions

Error Handling Functions
[Miscellaneous Functions]


Functions

FLMEXP FLMBOOL FLMAPI FlmErrorIsFileCorrupt (RCODE rc)
 Determine if a return code (RCODE) indicates a corruption.
FLMEXP const char *FLMAPI FlmErrorString (RCODE rc)
 Convert a return code (RCODE) into a string.
FLMEXP RCODE FLMAPI FlmGetDiagInfo (HFDB hDb, eDiagInfoType eDiagCode, void *pvDiagInfo)
 Get diagnostic information.

Function Documentation

FLMEXP FLMBOOL FLMAPI FlmErrorIsFileCorrupt RCODE  rc  ) 
 

Determine if a return code (RCODE) indicates a corruption.

Parameters:
rc  Error code to be tested.

FLMEXP const char* FLMAPI FlmErrorString RCODE  rc  ) 
 

Convert a return code (RCODE) into a string.

Parameters:
rc  Error code that is to be converted to a string.

FLMEXP RCODE FLMAPI FlmGetDiagInfo HFDB  hDb,
eDiagInfoType  eDiagCode,
void *  pvDiagInfo
 

Get diagnostic information.

Parameters:
hDb  Database handle.
eDiagCode  Diagnostic desired.
pvDiagInfo  Diagnostic information returned here. See documentation on eDiagInfoType for more detailed information.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__storageconversion.html0000644000175000017500000015541010510774540025732 0ustar ahodgkinsonahodgkinson FLAIM: Storage Format Conversion Functions

Storage Format Conversion Functions
[Miscellaneous Functions]


Functions

FLMEXP RCODE FLMAPI FlmUINT2Storage (FLMUINT uiNum, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMUINT value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmUINT64ToStorage (FLMUINT64 ui64Num, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMUINT64 value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmINT2Storage (FLMINT iNum, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMINT value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmINT64ToStorage (FLMINT64 i64Num, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a FLMINT64 value to FLAIM's internal storage format for numbers.
FLMEXP RCODE FLMAPI FlmStorage2UINT (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiNum)
 Convert a value from FLAIM's internal format to a FLMUINT.
FLMEXP RCODE FLMAPI FlmStorage2UINT32 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT32 *pui32Num)
 Convert a value from FLAIM's internal format to a FLMUINT32.
FLMEXP RCODE FLMAPI FlmStorage2UINT64 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT64 *pui64Num)
 Convert a value from FLAIM's internal format to a FLMUINT64.
FLMEXP RCODE FLMAPI FlmStorage2INT (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT *puiNum)
 Convert a value from FLAIM's internal format to a FLMINT.
FLMEXP RCODE FLMAPI FlmStorage2INT32 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT32 *pui32Num)
 Convert a value from FLAIM's internal format to a FLMINT32.
FLMEXP RCODE FLMAPI FlmStorage2INT64 (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMINT64 *pui64Num)
 Convert a value from FLAIM's internal format to a FLMINT64.
FLMEXP RCODE FLMAPI FlmUnicode2Storage (const FLMUNICODE *puzStr, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a unicode string to FLAIM's internal storage format.
FLMEXP FLMUINT FLMAPI FlmGetUnicodeStorageLength (const FLMUNICODE *puzStr)
 Determine the number of bytes needed to store a unicode string in FLAIM's internal storage format.
FLMEXP RCODE FLMAPI FlmStorage2Unicode (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrBufLen, FLMUNICODE *puzStrBuf)
 Convert a value from FLAIM's internal format to a unicode string.
FINLINE RCODE FlmGetUnicodeLength (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiUniLength)
 Get the number of bytes needed to convert a value from FLAIM's internal format to a unicode string.
FLMEXP RCODE FLMAPI FlmNative2Storage (const char *pszStr, FLMUINT uiStrLen, FLMUINT *puiStorageLen, FLMBYTE *pucStorageBuf)
 Convert a native string to FLAIM's internal storage format.
FLMEXP RCODE FLMAPI FlmStorage2Native (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrBufLen, char *pszStrBuf)
 Convert a value from FLAIM's internal format to a native string.
FLMEXP FLMUINT FLMAPI FlmGetNativeStorageLength (const char *pszStr)
 Determine the number of bytes needed to store a native string in FLAIM's internal storage format.
FINLINE RCODE FlmGetNativeLength (FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE *pucValue, FLMUINT *puiStrLength)
 Get the number of bytes needed to convert a value from FLAIM's internal format to a native string.

Function Documentation

FINLINE RCODE FlmGetNativeLength FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT *  puiStrLength
 

Get the number of bytes needed to convert a value from FLAIM's internal format to a native string.

The value may be either a FLM_NUMBER_TYPE or a FLM_TEXT_TYPE. The length returned does NOT account for null-termination, so if the application is calling this routine to determine how big of a buffer to allocate, it should add 1 to the size returned from this routine.

Parameters:
uiValueType  Data type of value to be converted. May be either FLM_NUMBER_TYPE or FLM_TEXT_TYPE.
uiValueLength  Length of value in bytes.
pucValue  Data value.
puiStrLength  String length is returned (in bytes). The length does not include what it would take for null-termination.

FLMEXP FLMUINT FLMAPI FlmGetNativeStorageLength const char *  pszStr  ) 
 

Determine the number of bytes needed to store a native string in FLAIM's internal storage format.

Parameters:
pszStr  Native string whose internal storage length is to be determined. It is expected that the string will be null-terminated.

FINLINE RCODE FlmGetUnicodeLength FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT *  puiUniLength
 

Get the number of bytes needed to convert a value from FLAIM's internal format to a unicode string.

The value may be either a FLM_NUMBER_TYPE or a FLM_TEXT_TYPE. The length returned does NOT account for null-termination, so if the application is calling this routine to determine how big of a buffer to allocate, it should add 2 to the size returned from this routine.

Parameters:
uiValueType  Data type of value to be converted. May be either FLM_NUMBER_TYPE or FLM_TEXT_TYPE.
uiValueLength  Length of value in bytes.
pucValue  Data value.
puiUniLength  Unicode length is returned (in bytes). The length does not include what it would take for null-termination.

FLMEXP FLMUINT FLMAPI FlmGetUnicodeStorageLength const FLMUNICODE *  puzStr  ) 
 

Determine the number of bytes needed to store a unicode string in FLAIM's internal storage format.

Parameters:
puzStr  Unicode string whose internal storage length is to be determined. It is expected that the string will be null-terminated.

FLMEXP RCODE FLMAPI FlmINT2Storage FLMINT  iNum,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a FLMINT value to FLAIM's internal storage format for numbers.

Parameters:
iNum  Number to convert.
puiStorageLen  On input, *puiStorageLen is the size of pucStorageBuf. It must be atleast F_MAX_NUM_BUF bytes. On output *puiStorageLen is set to the number of bytes used in pucStorageBuf.
pucStorageBuf  Number converted to FLAIM's internal storage format is returned here.

FLMEXP RCODE FLMAPI FlmINT64ToStorage FLMINT64  i64Num,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a FLMINT64 value to FLAIM's internal storage format for numbers.

Parameters:
i64Num  Number to convert.
puiStorageLen  On input, *puiStorageLen is the size of pucStorageBuf. It must be atleast F_MAX_NUM64_BUF bytes. On output *puiStorageLen is set to the number of bytes used in pucStorageBuf.
pucStorageBuf  Number converted to FLAIM's internal storage format is returned here.

FLMEXP RCODE FLMAPI FlmNative2Storage const char *  pszStr,
FLMUINT  uiStrLen,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a native string to FLAIM's internal storage format.

Parameters:
pszStr  Native string that is to be converted.
uiStrLen  Length (in bytes) of the native string. If zero, the string is expected to be NULL-terminated. *puiStorageLen contains number of bytes returned.
puiStorageLen  On input, *puiStorageLen is length (in bytes) of pucStorageBuf. On output, *puiStorageLen contains number of bytes returned.
pucStorageBuf  Converted string, in FLAIM's internal storage format, is returned here.

FLMEXP RCODE FLMAPI FlmStorage2INT FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMINT *  puiNum
 

Convert a value from FLAIM's internal format to a FLMINT.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
puiNum  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2INT32 FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMINT32 *  pui32Num
 

Convert a value from FLAIM's internal format to a FLMINT32.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
pui32Num  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2INT64 FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMINT64 *  pui64Num
 

Convert a value from FLAIM's internal format to a FLMINT64.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
pui64Num  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2Native FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT *  puiStrBufLen,
char *  pszStrBuf
 

Convert a value from FLAIM's internal format to a native string.

Note that the value may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE or FLM_TEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
puiStrBufLen  On input *puiStrBufLen should be the number of bytes available in buffer. The buffer should be large enough to hold a terminating null byte. On output *puiStrBufLen returns the number of bytes needed to hold the converted native string. The null termination byte is NOT included in this value.
pszStrBuf  Buffer to hold the native string. NOTE: If this parameter is NULL then *puiStrBufLen will return the number of bytes needed to hold the string. However, that does NOT count the byte needed to null-terminate the string. Thus, if the application is calling this routine to find out how big of a buffer to allocate to hold the string, it should add 1 to the value returned in *puiStrBufLen.

FLMEXP RCODE FLMAPI FlmStorage2UINT FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT *  puiNum
 

Convert a value from FLAIM's internal format to a FLMUINT.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
puiNum  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2UINT32 FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT32 *  pui32Num
 

Convert a value from FLAIM's internal format to a FLMUINT32.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
pui32Num  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2UINT64 FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT64 *  pui64Num
 

Convert a value from FLAIM's internal format to a FLMUINT64.

Note that the value may be a FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.

Parameters:
uiValueType  Data type of value being converted. May be FLM_NUMBER_TYPE, FLM_TEXT_TYPE, or FLM_CONTEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
pui64Num  Converted number is returned here.

FLMEXP RCODE FLMAPI FlmStorage2Unicode FLMUINT  uiValueType,
FLMUINT  uiValueLength,
const FLMBYTE *  pucValue,
FLMUINT *  puiStrBufLen,
FLMUNICODE *  puzStrBuf
 

Convert a value from FLAIM's internal format to a unicode string.

Note that the value may be a FLM_NUMBER_TYPE, or FLM_TEXT_TYPE.

Parameters:
uiValueType  Data type of data being converted. May be FLM_NUMBER_TYPE or FLM_TEXT_TYPE.
uiValueLength  Length of value to be converted (in bytes).
pucValue  Value to be converted. Data is expected to be in FLAIM's internal format.
puiStrBufLen  On input *puiStrBufLen should be the number of bytes available in puzStrBuf. puzStrBuf should be large enough to hold a unicode null terminating character (2 bytes). On output *puiStrBufLen returns the number of bytes needed to hold the converted Unicode string. NOTE: The two null termination bytes are NOT included in this value.
puzStrBuf  Buffer to hold the Unicode string. NOTE: If this parameter is NULL then *puiStrBufLen will return the number of bytes needed to hold the string. However, that does NOT count the two bytes needed to null-terminate the string. Thus, if the application is calling this routine to find out how big of a buffer to allocate to hold the string, it should add 2 to the value returned in *puiStrBufLen.

FLMEXP RCODE FLMAPI FlmUINT2Storage FLMUINT  uiNum,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a FLMUINT value to FLAIM's internal storage format for numbers.

Parameters:
uiNum  Number to convert.
puiStorageLen  On input, *puiStorageLen is the size of pucStorageBuf. It must be atleast F_MAX_NUM_BUF bytes. On output *puiStorageLen is set to the number of bytes used in pucStorageBuf.
pucStorageBuf  Number converted to FLAIM's internal storage format is returned here.

FLMEXP RCODE FLMAPI FlmUINT64ToStorage FLMUINT64  ui64Num,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a FLMUINT64 value to FLAIM's internal storage format for numbers.

Parameters:
ui64Num  Number to convert.
puiStorageLen  On input, *puiStorageLen is the size of pucStorageBuf. It must be atleast F_MAX_NUM64_BUF bytes. On output *puiStorageLen is set to the number of bytes used in pucStorageBuf.
pucStorageBuf  Number converted to FLAIM's internal storage format is returned here.

FLMEXP RCODE FLMAPI FlmUnicode2Storage const FLMUNICODE *  puzStr,
FLMUINT *  puiStorageLen,
FLMBYTE *  pucStorageBuf
 

Convert a unicode string to FLAIM's internal storage format.

Parameters:
puzStr  Unicode string that is to be converted. FLAIM expects the string to be null-terminated.
puiStorageLen  On input, *puiStorageLen is length (in bytes) of pucStorageBuf. On output, *puiStorageLen contains number of bytes returned.
pucStorageBuf  Converted string, in FLAIM's internal storage format, is returned here.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__language.html0000644000175000017500000000720210510774540023736 0ustar ahodgkinsonahodgkinson FLAIM: Language To String Conversion Functions

Language To String Conversion Functions
[Miscellaneous Functions]


Functions

FLMUINT FLMAPI f_languageToNum (const char *pszLanguage)
 Get the language string from a language code.
void FLMAPI f_languageToStr (FLMINT iLangNum, char *pszLanguage)
 Convert a language string to the appropriate language code.

Function Documentation

void FLMAPI f_languageToStr FLMINT  iLangNum,
char *  pszLanguage
 

Convert a language string to the appropriate language code.

Parameters:
pszLanguage  Language string that is to be converted to a code.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__stringcompare.html0000644000175000017500000000213010510774540025023 0ustar ahodgkinsonahodgkinson FLAIM: String Comparison Functions

String Comparison Functions
[Miscellaneous Functions]


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__event.html0000644000175000017500000001503010510774540023272 0ustar ahodgkinsonahodgkinson FLAIM: Event Registration/Deregistration Functions

Event Registration/Deregistration Functions
[Miscellaneous Functions]


Functions

FLMEXP RCODE FLMAPI FlmRegisterForEvent (FEventCategory eCategory, FEVENT_CB fnEventCB, void *pvAppData, HFEVENT *phEventRV)
 Register to catch events from the database system.
FLMEXP void FLMAPI FlmDeregisterForEvent (HFEVENT *phEventRV)
 Deregister event handling function.

Function Documentation

FLMEXP void FLMAPI FlmDeregisterForEvent HFEVENT phEventRV  ) 
 

Deregister event handling function.

Parameters:
phEventRV  Event handle that was returned by FlmRegisterForEvent().

FLMEXP RCODE FLMAPI FlmRegisterForEvent FEventCategory  eCategory,
FEVENT_CB  fnEventCB,
void *  pvAppData,
HFEVENT phEventRV
 

Register to catch events from the database system.

Parameters:
eCategory  Category of events to be caught.
fnEventCB  Function to be called when events of the specified category happen.
pvAppData  Application supplied data that is to be passed to the registered function whenever it is called.
phEventRV  Event handle. This handle should be passed to FlmDeregisterForEvent() to deregister event handling.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__pool.html0000644000175000017500000002402310510774540023124 0ustar ahodgkinsonahodgkinson FLAIM: Pool Memory Functions

Pool Memory Functions
[Miscellaneous Functions]


Functions

FINLINE void FLMAPI F_Pool::poolInit (FLMUINT uiBlockSize)
 Initialize memory pool.
RCODE FLMAPI F_Pool::poolAlloc (FLMUINT uiSize, void **ppvPtr)
 Allocate memory from a memory pool.
RCODE FLMAPI F_Pool::poolCalloc (FLMUINT uiSize, void **ppvPtr)
 Allocate memory from a memory pool and initialize memory to zeroes. Pointer to the allocation.
void FLMAPI F_Pool::poolFree (void)
 Free all memory blocks in a memory pool.
void FLMAPI F_Pool::poolReset (void *pvMark=NULL, FLMBOOL bReduceFirstBlock=FALSE)
 Reset a memory pool back to a mark. Free all memory blocks allocated after the mark.
FINLINE void *FLMAPI F_Pool::poolMark (void)
 Obtain a mark in a memory pool. Returned mark remembers a location in the pool which can later be passed to poolReset() to free all memory that was allocated after the mark.

Function Documentation

RCODE FLMAPI F_Pool::poolAlloc FLMUINT  uiSize,
void **  ppvPtr
[inherited]
 

Allocate memory from a memory pool.

Parameters:
uiSize  Requested allocation size (in bytes).
ppvPtr  Pointer to the allocation

RCODE FLMAPI F_Pool::poolCalloc FLMUINT  uiSize,
void **  ppvPtr
[inherited]
 

Allocate memory from a memory pool and initialize memory to zeroes. Pointer to the allocation.

Parameters:
uiSize  Requested allocation size (in bytes).

FINLINE void FLMAPI F_Pool::poolInit FLMUINT  uiBlockSize  )  [inline, inherited]
 

Initialize memory pool.

Parameters:
uiBlockSize  Default block size for the memory pool.

void FLMAPI F_Pool::poolReset void *  pvMark = NULL,
FLMBOOL  bReduceFirstBlock = FALSE
[inherited]
 

Reset a memory pool back to a mark. Free all memory blocks allocated after the mark.

Parameters:
pvMark  Mark that was obtained from GedPoolMark().


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__memoryalloc.html0000644000175000017500000000531310510774540024477 0ustar ahodgkinsonahodgkinson FLAIM: Memory Functions

Memory Functions
[Miscellaneous Functions]


Functions

FLMEXP void FLMAPI FlmFreeMem (void *pMem)
 Free memory that was allocated by various functions.

Function Documentation

FLMEXP void FLMAPI FlmFreeMem void *  pMem  ) 
 

Free memory that was allocated by various functions.

Parameters:
pMem  Pointer to memory to be freed.


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__retcodes.html0000644000175000017500000055334610510774540024002 0ustar ahodgkinsonahodgkinson FLAIM: Return Codes

Return Codes


Defines

#define FERR_OK   NE_FLM_OK
 0 - Operation succeeded
#define FERR_BOF_HIT   NE_FLM_BOF_HIT
 0xC001 - Beginning of file or set hit.
#define FERR_EOF_HIT   NE_FLM_EOF_HIT
 0xC002 - End of file or set hit.
#define FERR_END   0xC003
 0xC003 - End of GEDCOM file - this is an internal error.
#define FERR_EXISTS   NE_FLM_EXISTS
 0xC004 - Record already exists.
#define FERR_FAILURE   NE_FLM_FAILURE
 0xC005 - Internal failure.
#define FERR_NOT_FOUND   NE_FLM_NOT_FOUND
 0xC006 - A record, key, or key reference was not found.
#define FERR_BAD_DICT_ID   0xC007
 0xC007 - Invalid dictionary record number -- outside unreserved range.
#define FERR_BAD_CONTAINER   0xC008
 0xC008 - Invalid container number.
#define FERR_NO_ROOT_BLOCK   0xC009
 0xC009 - LFILE does not have a root block - always handled internally - never returned to application.
#define FERR_BAD_DRN   0xC00A
 0xC00A - Cannot pass a zero DRN into modify or delete or 0xFFFFFFFF into add.
#define FERR_BAD_FIELD_NUM   0xC00B
 0xC00B - Bad field number in record being added.
#define FERR_BAD_FIELD_TYPE   0xC00C
 0xC00C - Bad field type in record being added.
#define FERR_BAD_HDL   0xC00D
 0xC00D - Request contained bad db handle.
#define FERR_BAD_IX   0xC00E
 0xC00E - Invalid index number.
#define FERR_BACKUP_ACTIVE   0xC00F
 0xC00F - Operation could not be completed - a backup is being performed.
#define FERR_SERIAL_NUM_MISMATCH   0xC010
 0xC010 - Comparison of serial numbers failed.
#define FERR_BAD_RFL_DB_SERIAL_NUM   0xC011
 0xC011 - Bad database serial number in RFL file header.
#define FERR_BTREE_ERROR   NE_FLM_BTREE_ERROR
 0xC012 - A corruption was found in an index or container b-tree.
#define FERR_BTREE_FULL   NE_FLM_BTREE_FULL
 0xC013 - An index or container b-tree is full.
#define FERR_BAD_RFL_FILE_NUMBER   0xC014
 0xC014 - Bad RFL file number in RFL file header.
#define FERR_CANNOT_DEL_ITEM   0xC015
 0xC015 - Cannot delete field definitions.
#define FERR_CANNOT_MOD_FIELD_TYPE   0xC016
 0xC016 - Cannot modify a field's type.
#define FERR_64BIT_NUMS_NOT_SUPPORTED   0xC017
 0xC017 - 64 bit numbers not supported for databases whose revision is less than 462
#define FERR_CONV_BAD_DEST_TYPE   0xC018
 0xC018 - Bad destination type specified for conversion.
#define FERR_CONV_BAD_DIGIT   0xC019
 0xC019 - Non-numeric digit found in text to numeric conversion.
#define FERR_CONV_BAD_SRC_TYPE   0xC01A
 0xC01A - Bad source type specified for conversion.
#define FERR_RFL_FILE_NOT_FOUND   0xC01B
 0xC01B - Could not open an RFL file.
#define FERR_CONV_DEST_OVERFLOW   NE_FLM_CONV_DEST_OVERFLOW
 0xC01C - Destination buffer not large enough to hold converted data.
#define FERR_CONV_ILLEGAL   NE_FLM_CONV_ILLEGAL
 0xC01D - Illegal conversion -- not supported.
#define FERR_CONV_NULL_SRC   0xC01E
 0xC01E - Source cannot be a NULL pointer in conversion.
#define FERR_CONV_NULL_DEST   0xC01F
 0xC01F - Destination cannot be a NULL pointer in conversion.
#define FERR_CONV_NUM_OVERFLOW   NE_FLM_CONV_NUM_OVERFLOW
 0xC020 - Numeric overflow (GT upper bound) converting to numeric type.
#define FERR_CONV_NUM_UNDERFLOW   0xC021
 0xC021 - Numeric underflow (LT lower bound) converting to numeric type.
#define FERR_DATA_ERROR   NE_FLM_DATA_ERROR
 0xC022 - Database corruption found.
#define FERR_NOT_USED_C023   0xC023
 0xC023 - Not used
#define FERR_DD_ERROR   0xC024
 0xC024 - Corruption found in logical file block chain.
#define FERR_INVALID_FILE_SEQUENCE   0xC025
 0xC025 - Incremental backup file number provided during a restore is invalid.
#define FERR_ILLEGAL_OP   NE_FLM_ILLEGAL_OP
 0xC026 - Illegal operation for database.
#define FERR_DUPLICATE_DICT_REC   0xC027
 0xC027 - Duplicate dictionary record found.
#define FERR_CANNOT_CONVERT   0xC028
 0xC028 - Condition occurred which prevents database conversion.
#define FERR_UNSUPPORTED_VERSION   0xC029
 0xC029 - Database version is not supported.
#define FERR_FILE_ER   0xC02A
 0xC02A - File error in a GEDCOM routine.
#define FERR_BAD_FIELD_LEVEL   0xC02B
 0xC02B - Invalid field level.
#define FERR_GED_BAD_RECID   0xC02C
 0xC02C - Bad record ID syntax.
#define FERR_GED_BAD_VALUE   0xC02D
 0xC02D - Bad or ambiguous/extra value in GEDCOM.
#define FERR_GED_MAXLVLNUM   0xC02E
 0xC02E - Exceeded GED_MAXLVLNUM in gedcom routines.
#define FERR_GED_SKIP_LEVEL   0xC02F
 0xC02F - Bad GEDCOM tree structure -- level skipped.
#define FERR_ILLEGAL_TRANS   0xC030
 0xC030 - Attempt to start an illegal type of transaction.
#define FERR_ILLEGAL_TRANS_OP   0xC031
 0xC031 - Illegal operation for transaction type.
#define FERR_INCOMPLETE_LOG   0xC032
 0xC032 - Incomplete log record encountered during recovery.
#define FERR_INVALID_BLOCK_LENGTH   0xC033
 0xC033 - Invalid block length.
#define FERR_INVALID_TAG   0xC034
 0xC034 - Invalid tag name.
#define FERR_KEY_NOT_FOUND   0xC035
 0xC035 - A key or reference is not found -- modify/delete error.
#define FERR_VALUE_TOO_LARGE   0xC036
 0xC036 - Value too large.
#define FERR_MEM   NE_FLM_MEM
 0xC037 - Memory allocation error.
#define FERR_BAD_RFL_SERIAL_NUM   0xC038
 0xC038 - Bad serial number in RFL file header.
#define FERR_NOT_USED_C039   0xC039
 0xC039 - Not used
#define FERR_NEWER_FLAIM   0xC03A
 0xC03A - Database version newer than this code base will support, must use newer version of code.
#define FERR_CANNOT_MOD_FIELD_STATE   0xC03B
 0xC03B - Attempted to change a field state illegally.
#define FERR_NO_MORE_DRNS   0xC03C
 0xC03C - The highest DRN number has already been used in an add.
#define FERR_NO_TRANS_ACTIVE   0xC03D
 0xC03D - Attempted to updated database outside transaction.
#define FERR_NOT_UNIQUE   NE_FLM_NOT_UNIQUE
 0xC03E - Found duplicate key for unique index.
#define FERR_NOT_FLAIM   0xC03F
 0xC03F - File is not a FLAIM database.
#define FERR_NULL_RECORD   0xC040
 0xC040 - NULL record cannot be passed to add or modify.
#define FERR_NO_HTTP_STACK   0xC041
 0xC041 - No http stack was loaded.
#define FERR_OLD_VIEW   0xC042
 0xC042 - While reading was unable to get previous version of block or record.
#define FERR_PCODE_ERROR   0xC043
 0xC043 - Corruption found in dictionary.
#define FERR_PERMISSION   0xC044
 0xC044 - Invalid permission for file operation.
#define FERR_SYNTAX   NE_FLM_SYNTAX
 0xC045 - Dictionary record has improper syntax, or syntax error in query criteria.
#define FERR_CALLBACK_FAILURE   0xC046
 0xC046 - Callback failure.
#define FERR_TRANS_ACTIVE   0xC047
 0xC047 - Attempted to close database while transaction was active.
#define FERR_RFL_TRANS_GAP   0xC048
 0xC048 - A gap was found in the transaction sequence in the RFL.
#define FERR_BAD_COLLATED_KEY   0xC049
 0xC049 - Something in collated key is bad.
#define FERR_UNSUPPORTED_FEATURE   0xC04A
 0xC04A - Attempting a feature that is not supported for the database version.
#define FERR_MUST_DELETE_INDEXES   0xC04B
 0xC04B - Attempting to delete a container that has indexes defined for it -- indexes must be deleted first.
#define FERR_RFL_INCOMPLETE   0xC04C
 0xC04C - RFL file is incomplete.
#define FERR_CANNOT_RESTORE_RFL_FILES   0xC04D
 0xC04D - Cannot restore RFL files - not using multiple RFL files.
#define FERR_INCONSISTENT_BACKUP   0xC04E
 0xC04E - A problem (corruption, etc) was detected in a backup set.
#define FERR_BLOCK_CHECKSUM   0xC04F
 0xC04F - Block checksum error.
#define FERR_ABORT_TRANS   0xC050
 0xC050 - Attempted operation after a critical error - should abort transaction.
#define FERR_NOT_RFL   0xC051
 0xC051 - Attempted to open file which was not an RFL file.
#define FERR_BAD_RFL_PACKET   0xC052
 0xC052 - RFL packet was bad.
#define FERR_DATA_PATH_MISMATCH   0xC053
 0xC053 - Bad data path specified to open database.
#define FERR_HTTP_REGISTER_FAILURE   0xC054
 0xC054 - Call to FlmConfig() with FLM_HTTP_REGISTER_URL option failed.
#define FERR_HTTP_DEREG_FAILURE   0xC055
 0xC055 - Call to FlmConfig() with FLM_HTTP_DEREGISTER_URL option failed.
#define FERR_IX_FAILURE   0xC056
 0xC056 - Indexing process failed, non-unique data was found when a unique index was being created.
#define FERR_HTTP_SYMS_EXIST   0xC057
 0xC057 - Tried to import new http related symbols before unimporting the old ones.
#define FERR_NOT_USED_C058   0xC058
 0xC058 - Not used
#define FERR_FILE_EXISTS   0xC059
 0xC059 - Attempt to create a database, but the database already exists.
#define FERR_SYM_RESOLVE_FAIL   0xC05A
 0xC05A - Could not resolve a symbol needed to run.
#define FERR_BAD_SERVER_CONNECTION   0xC05B
 0xC05B - Connection to FLAIM server is bad.
#define FERR_CLOSING_DATABASE   0xC05C
 0xC05C - Database is being closed due to a critical error.
#define FERR_INVALID_CRC   0xC05D
 0xC05D - CRC could not be verified.
#define FERR_KEY_OVERFLOW   0xC05E
 0xC05E - Key generated by the record causes the maximum key size to be exceeded.
#define FERR_NOT_IMPLEMENTED   NE_FLM_NOT_IMPLEMENTED
 0xC05F - Functionality not implemented.
#define FERR_MUTEX_OPERATION_FAILED   0xC060
 0xC060 - Mutex operation failed.
#define FERR_MUTEX_UNABLE_TO_LOCK   0xC061
 0xC061 - Unable to get the mutex lock.
#define FERR_SEM_OPERATION_FAILED   0xC062
 0xC062 - Semaphore operation failed.
#define FERR_SEM_UNABLE_TO_LOCK   0xC063
 0xC063 - Unable to get the semaphore lock.
#define FERR_NOT_USED_C064   0xC064
 0xC064 - Not used
#define FERR_NOT_USED_C065   0xC065
 0xC065 - Not used
#define FERR_NOT_USED_C066   0xC066
 0xC066 - Not used
#define FERR_NOT_USED_C067   0xC067
 0xC067 - Not used
#define FERR_NOT_USED_C068   0xC068
 0xC068 - Not used
#define FERR_BAD_REFERENCE   0xC069
 0xC069 - Bad reference in the dictionary.
#define FERR_NOT_USED_C06A   0xC06A
 0xC06A - Not used
#define FERR_NOT_USED_C06B   0xC06B
 0xC06B - Not used
#define FERR_NOT_USED_C06C   0xC06C
 0xC06C - Not used
#define FERR_NOT_USED_C06D   0xC06D
 0xC06D - Not used
#define FERR_NOT_USED_C06E   0xC06E
 0xC06E - Not used
#define FERR_NOT_USED_C06F   0xC06F
 0xC06F - Not used
#define FERR_UNALLOWED_UPGRADE   0xC070
 0xC070 - FlmDbUpgrade cannot upgrade the database.
#define FERR_NOT_USED_C071   0xC071
 0xC071 - Not used
#define FERR_NOT_USED_C072   0xC072
 0xC072 - Not used
#define FERR_NOT_USED_C073   0xC073
 0xC073 - Not used
#define FERR_ID_RESERVED   0xC074
 0xC074 - Attempted to use a dictionary ID that has been reserved.
#define FERR_CANNOT_RESERVE_ID   0xC075
 0xC075 - Attempted to reserve a dictionary ID that has been used.
#define FERR_DUPLICATE_DICT_NAME   0xC076
 0xC076 - Dictionary record with duplicate name found.
#define FERR_CANNOT_RESERVE_NAME   0xC077
 0xC077 - Attempted to reserve a dictionary name that is in use.
#define FERR_BAD_DICT_DRN   0xC078
 0xC078 - Attempted to add, modify, or delete a dictionary DRN >= FLM_RESERVED_TAG_NUMS.
#define FERR_CANNOT_MOD_DICT_REC_TYPE   0xC079
 0xC079 - Cannot modify a dictionary item into another type of item, must delete then add.
#define FERR_PURGED_FLD_FOUND   0xC07A
 0xC07A - Record contained a field whose field definition has been marked as purged.
#define FERR_DUPLICATE_INDEX   0xC07B
 0xC07B - Duplicate index.
#define FERR_TOO_MANY_OPEN_DBS   0xC07C
 0xC07C - Too many open databases.
#define FERR_ACCESS_DENIED   0xC07D
 0xC07D - Cannot access database.
#define FERR_NOT_USED_C07E   0xC07E
 0xC07E - Not used
#define FERR_CACHE_ERROR   0xC07F
 0xC07F - Cache block is corrupt.
#define FERR_NOT_USED_C080   0xC080
 0xC080 - Not used
#define FERR_BLOB_MISSING_FILE   0xC081
 0xC081 - Missing BLOB file on add/modify.
#define FERR_NO_REC_FOR_KEY   0xC082
 0xC082 - Record pointed to by an index key is missing.
#define FERR_DB_FULL   0xC083
 0xC083 - Database is full, cannot create more blocks.
#define FERR_TIMEOUT   0xC084
 0xC084 - Operation timed out (usually a query operation).
#define FERR_CURSOR_SYNTAX   0xC085
 0xC085 - Query criteria had improper syntax.
#define FERR_THREAD_ERR   0xC086
 0xC086 - Thread error.
#define FERR_UNIMPORT_SYMBOL   0xC087
 0xC087 - Failed to unimport a public symbol.
#define FERR_EMPTY_QUERY   0xC088
 0xC088 - Warning: Query has no results.
#define FERR_INDEX_OFFLINE   0xC089
 0xC089 - Warning: Index is offline and being rebuilt.
#define FERR_TRUNCATED_KEY   0xC08A
 0xC08A - Warning: Can't evaluate truncated key against selection criteria.
#define FERR_INVALID_PARM   NE_FLM_INVALID_PARM
 0xC08B - Invalid parameter.
#define FERR_USER_ABORT   0xC08C
 0xC08C - User or application aborted the operation.
#define FERR_RFL_DEVICE_FULL   0xC08D
 0xC08D - No space on RFL device for logging.
#define FERR_MUST_WAIT_CHECKPOINT   0xC08E
 0xC08E - Must wait for a checkpoint before starting transaction - due to disk problems - usually in RFL volume.
#define FERR_NAMED_SEMAPHORE_ERR   0xC08F
 0xC08F - Error occurred while accessing a named semaphore.
#define FERR_LOAD_LIBRARY   0xC090
 0xC090 - Failed to load a shared library module.
#define FERR_UNLOAD_LIBRARY   0xC091
 0xC091 - Failed to unload a shared library module.
#define FERR_IMPORT_SYMBOL   0xC092
 0xC092 - Failed to import a symbol from a shared library module.
#define FERR_BLOCK_FULL   0xC093
 0xC093 - Destination block for insert is full.
#define FERR_BAD_BASE64_ENCODING   0xC094
 0xC094 - Could not perform base 64 encoding.
#define FERR_MISSING_FIELD_TYPE   0xC095
 0xC095 - Field type not specified in field definition record.
#define FERR_BAD_DATA_LENGTH   0xC096
 0xC096 - Invalid field data length.
#define FERR_IO_ACCESS_DENIED   NE_FLM_IO_ACCESS_DENIED
 0xC201 - Access denied. Caller is not allowed access to a file.
#define FERR_IO_BAD_FILE_HANDLE   NE_FLM_IO_BAD_FILE_HANDLE
 0xC202 - Bad file handle.
#define FERR_IO_COPY_ERR   NE_FLM_IO_COPY_ERR
 0xC203 - Copy error.
#define FERR_IO_DISK_FULL   NE_FLM_IO_DISK_FULL
 0xC204 - Disk full.
#define FERR_IO_END_OF_FILE   NE_FLM_IO_END_OF_FILE
 0xC205 - End of file.
#define FERR_IO_OPEN_ERR   NE_FLM_IO_OPEN_ERR
 0xC206 - Error opening file.
#define FERR_IO_SEEK_ERR   NE_FLM_IO_SEEK_ERR
 0xC207 - File seek error.
#define FERR_IO_DIRECTORY_ERR   NE_FLM_IO_DIRECTORY_ERR
 0xC208 - Error occurred while accessing or deleting a directory.
#define FERR_IO_PATH_NOT_FOUND   NE_FLM_IO_PATH_NOT_FOUND
 0xC209 - Path not found.
#define FERR_IO_TOO_MANY_OPEN_FILES   NE_FLM_IO_TOO_MANY_OPEN_FILES
 0xC20A - Too many files open.
#define FERR_IO_PATH_TOO_LONG   NE_FLM_IO_PATH_TOO_LONG
 0xC20B - Path too long.
#define FERR_IO_NO_MORE_FILES   NE_FLM_IO_NO_MORE_FILES
 0xC20C - No more files in directory.
#define FERR_DELETING_FILE   NE_FLM_IO_DELETING_FILE
 0xC20D - Had error deleting a file.
#define FERR_IO_FILE_LOCK_ERR   NE_FLM_IO_FILE_LOCK_ERR
 0xC20E - File lock error.
#define FERR_IO_FILE_UNLOCK_ERR   NE_FLM_IO_FILE_UNLOCK_ERR
 0xC20F - File unlock error.
#define FERR_IO_PATH_CREATE_FAILURE   NE_FLM_IO_PATH_CREATE_FAILURE
 0xC210 - Path create failed.
#define FERR_IO_RENAME_FAILURE   NE_FLM_IO_RENAME_FAILURE
 0xC211 - File rename failed.
#define FERR_IO_INVALID_PASSWORD   NE_FLM_IO_INVALID_PASSWORD
 0xC212 - Invalid file password.
#define FERR_SETTING_UP_FOR_READ   NE_FLM_SETTING_UP_FOR_READ
 0xC213 - Had error setting up to do a read.
#define FERR_SETTING_UP_FOR_WRITE   NE_FLM_SETTING_UP_FOR_WRITE
 0xC214 - Had error setting up to do a write.
#define FERR_IO_AT_PATH_ROOT   NE_FLM_IO_CANNOT_REDUCE_PATH
 0xC215 - Currently positioned at the path root level.
#define FERR_INITIALIZING_IO_SYSTEM   NE_FLM_INITIALIZING_IO_SYSTEM
 0xC216 - Had error initializing the file system.
#define FERR_FLUSHING_FILE   NE_FLM_FLUSHING_FILE
 0xC217 - Had error flushing a file.
#define FERR_IO_INVALID_PATH   NE_FLM_IO_INVALID_FILENAME
 0xC218 - Invalid path.
#define FERR_IO_CONNECT_ERROR   NE_FLM_IO_CONNECT_ERROR
 0xC219 - Failed to connect to a remote network resource.
#define FERR_OPENING_FILE   NE_FLM_OPENING_FILE
 0xC21A - Had error opening a file.
#define FERR_DIRECT_OPENING_FILE   NE_FLM_DIRECT_OPENING_FILE
 0xC21B - Had error opening a file for direct I/O.
#define FERR_CREATING_FILE   NE_FLM_CREATING_FILE
 0xC21C - Had error creating a file.
#define FERR_DIRECT_CREATING_FILE   NE_FLM_DIRECT_CREATING_FILE
 0xC21D - Had error creating a file for direct I/O.
#define FERR_READING_FILE   NE_FLM_READING_FILE
 0xC21E - Had error reading a file.
#define FERR_DIRECT_READING_FILE   NE_FLM_DIRECT_READING_FILE
 0xC21F - Had error reading a file using direct I/O.
#define FERR_WRITING_FILE   NE_FLM_WRITING_FILE
 0xC220 - Had error writing to a file.
#define FERR_DIRECT_WRITING_FILE   NE_FLM_DIRECT_WRITING_FILE
 0xC221 - Had error writing to a file using direct I/O.
#define FERR_POSITIONING_IN_FILE   NE_FLM_POSITIONING_IN_FILE
 0xC222 - Had error positioning within a file.
#define FERR_GETTING_FILE_SIZE   NE_FLM_GETTING_FILE_SIZE
 0xC223 - Had error getting file size.
#define FERR_TRUNCATING_FILE   NE_FLM_TRUNCATING_FILE
 0xC224 - Had error truncating a file.
#define FERR_PARSING_FILE_NAME   NE_FLM_PARSING_FILE_NAME
 0xC225 - Had error parsing a file name.
#define FERR_CLOSING_FILE   NE_FLM_CLOSING_FILE
 0xC226 - Had error closing a file.
#define FERR_GETTING_FILE_INFO   NE_FLM_GETTING_FILE_INFO
 0xC227 - Had error getting file information.
#define FERR_EXPANDING_FILE   NE_FLM_EXPANDING_FILE
 0xC228 - Had error expanding a file (using direct I/O).
#define FERR_GETTING_FREE_BLOCKS   NE_FLM_GETTING_FREE_BLOCKS
 0xC229 - Had error getting free blocks from file system.
#define FERR_CHECKING_FILE_EXISTENCE   NE_FLM_CHECKING_FILE_EXISTENCE
 0xC22A - Had error checking if a file exists.
#define FERR_RENAMING_FILE   NE_FLM_RENAMING_FILE
 0xC22B - Had error renaming a file.
#define FERR_SETTING_FILE_INFO   NE_FLM_SETTING_FILE_INFO
 0xC22C - Had error setting file information.
#define FERR_NICI_CONTEXT   0xC301
 0xC301 - Failed to obtain a NICI context.
#define FERR_NICI_FIND_INIT   0xC302
 0xC302 - CCS_FindInit failed.
#define FERR_NICI_FIND_OBJECT   0xC303
 0xC303 - CCS_FindObject failed.
#define FERR_NICI_WRAPKEY_NOT_FOUND   0xC304
 0xC304 - Could not locate a wrapping key.
#define FERR_NICI_ATTRIBUTE_VALUE   0xC305
 0xC305 - CCS_AttributeValue failed.
#define FERR_NICI_BAD_ATTRIBUTE   0xC306
 0xC306 - Invalid attribute.
#define FERR_NICI_BAD_RANDOM   0xC307
 0xC307 - CCS_GetRandom failed.
#define FERR_NOT_USED_C308   0xC308
 0xC308 - Not used
#define FERR_NICI_WRAPKEY_FAILED   0xC309
 0xC309 - CCS_WrapKey failed.
#define FERR_NICI_GENKEY_FAILED   0xC30A
 0xC30A - CCS_GenerateKey failed.
#define FERR_REQUIRE_PASSWD   0xC30B
 0xC30B - Password required to unwrap key.
#define FERR_NICI_SHROUDKEY_FAILED   0xC30C
 0xC30C - CCS_pbeShroudPrivateKey failed.
#define FERR_NICI_UNSHROUDKEY_FAILED   0xC30D
 0xC30D - CCS_pbdUnshroudPrivateKey failed.
#define FERR_NICI_UNWRAPKEY_FAILED   0xC30E
 0xC30E - CCS_UnrapKey failed.
#define FERR_NICI_ENC_INIT_FAILED   0xC30F
 0xC30F - CCS_DataEncryptInit failed.
#define FERR_NICI_ENCRYPT_FAILED   0xC310
 0xC310 - CCS_DataEncrypt failed.
#define FERR_NICI_DECRYPT_INIT_FAILED   0xC311
 0xC311 - CCS_DataDecryptInit failed.
#define FERR_NICI_DECRYPT_FAILED   0xC312
 0xC312 - CCS_DataDecrypt failed.
#define FERR_NICI_INIT_FAILED   0xC313
 0xC313 - CCS_Init failed.
#define FERR_NICI_KEY_NOT_FOUND   0xC314
 0xC314 - Could not locate encryption/decryption key.
#define FERR_NICI_INVALID_ALGORITHM   0xC315
 0xC315 - Unsupported NICI ecncryption algorithm.
#define FERR_FLD_NOT_ENCRYPTED   0xC316
 0xC316 - Field is not encrypted.
#define FERR_CANNOT_SET_KEY   0xC317
 0xC317 - Attempted to set an encryption key for new encryption definition record.
#define FERR_MISSING_ENC_TYPE   0xC318
 0xC318 - Encryption type not specified in encryption definition record.
#define FERR_CANNOT_MOD_ENC_TYPE   0xC319
 0xC319 - Attempting to change the encryption type in encryption definition record.
#define FERR_MISSING_ENC_KEY   0xC31A
 0xC31A - Encryption key must be present in modified encryption definition record.
#define FERR_CANNOT_CHANGE_KEY   0xC31B
 0xC31B - Attempt to modify the encryption key in an encryption definition record.
#define FERR_BAD_ENC_KEY   0xC31C
 0xC31C - Bad encryption key.
#define FERR_CANNOT_MOD_ENC_STATE   0xC31D
 0xC31D - Illegal state change for an encryption definition record.
#define FERR_DATA_SIZE_MISMATCH   0xC31E
 0xC31E - Calculated encrypted data length does not match the length returned from encryption/decryption routines.
#define FERR_ENCRYPTION_UNAVAILABLE   0xC31F
 0xC31F - Encryption capabilities are not available for encrypting/decrypting data in database.
#define FERR_PURGED_ENCDEF_FOUND   0xC320
 0xC320 - Cannot use encryption ID for encryption of data - encryption definition record is marked as purged.
#define FERR_FLD_NOT_DECRYPTED   0xC321
 0xC321 - Attempting to access data from a field that is encrypted, field could not be decrypted for some reason - probably because encryption/decryption capabilities are not available.
#define FERR_BAD_ENCDEF_ID   0xC322
 0xC322 - Encryption ID is invalid - not defined in dictionary.
#define FERR_PBE_ENCRYPT_FAILED   0xC323
 0xC323 - Call to NICI function CCS_pbeEncrypt failed.
#define FERR_DIGEST_FAILED   0xC324
 0xC324 - Call to NICI function CCS_Digest failed.
#define FERR_DIGEST_INIT_FAILED   0xC325
 0xC325 - Call to NICI function CCS_DigestInit failed.
#define FERR_EXTRACT_KEY_FAILED   0xC326
 0xC326 - Call to NICI function CCS_ExtractKey failed.
#define FERR_INJECT_KEY_FAILED   0xC327
 0xC327 - Call to NICI function CCS_InjectKey failed.
#define FERR_PBE_DECRYPT_FAILED   0xC328
 0xC328 - Call to NICI function CCS_pbeDecrypt failed.
#define FERR_PASSWD_INVALID   0xC329
 0xC329 - Invalid password passed, database could not be opened.
#define FERR_BT_END_OF_DATA   0xFFFF
 0xFFFF - Used internally
#define NE_FLM_OK   0
 0 - Operation was successful.
#define NE_FLM_BOF_HIT   0xC001
 0xC001 - Beginning of results encountered.
#define NE_FLM_EOF_HIT   0xC002
 0xC002 - End of results encountered.
#define NE_FLM_EXISTS   0xC004
 0xC004 - Object already exists.
#define NE_FLM_FAILURE   0xC005
 0xC005 - Internal failure.
#define NE_FLM_NOT_FOUND   0xC006
 0xC006 - An object was not found.
#define NE_FLM_BTREE_ERROR   0xC012
 0xC012 - Corruption found in b-tree.
#define NE_FLM_BTREE_FULL   0xC013
 0xC013 - B-tree cannot grow beyond current size.
#define NE_FLM_CONV_DEST_OVERFLOW   0xC01C
 0xC01C - Destination buffer not large enough to hold data.
#define NE_FLM_CONV_ILLEGAL   0xC01D
 0xC01D - Attempt to convert between data types is an unsupported conversion.
#define NE_FLM_CONV_NUM_OVERFLOW   0xC020
 0xC020 - Numeric overflow (> upper bound) converting to numeric type.
#define NE_FLM_DATA_ERROR   0xC022
 0xC022 - Corruption found in b-tree.
#define NE_FLM_ILLEGAL_OP   0xC026
 0xC026 - Illegal operation
#define NE_FLM_MEM   0xC037
 0xC037 - Attempt to allocate memory failed.
#define NE_FLM_NOT_UNIQUE   0xC03E
 0xC03E - Non-unique key.
#define NE_FLM_SYNTAX   0xC045
 0xC045 - Syntax error while parsing.
#define NE_FLM_NOT_IMPLEMENTED   0xC05F
 0xC05F - Attempt was made to use a feature that is not implemented.
#define NE_FLM_INVALID_PARM   0xC08B
 0xC08B - Invalid parameter passed into a function.
#define NE_FLM_IO_ACCESS_DENIED   0xC201
 0xC201 - Access to file is denied. Caller is not allowed access to a file.
#define NE_FLM_IO_BAD_FILE_HANDLE   0xC202
 0xC202 - Bad file handle or file descriptor.
#define NE_FLM_IO_COPY_ERR   0xC203
 0xC203 - Error occurred while copying a file.
#define NE_FLM_IO_DISK_FULL   0xC204
 0xC204 - Disk full.
#define NE_FLM_IO_END_OF_FILE   0xC205
 0xC205 - End of file reached while reading from the file.
#define NE_FLM_IO_OPEN_ERR   0xC206
 0xC206 - Error while opening the file.
#define NE_FLM_IO_SEEK_ERR   0xC207
 0xC207 - Error occurred while positioning (seeking) within a file.
#define NE_FLM_IO_DIRECTORY_ERR   0xC208
 0xC208 - Error occurred while accessing or deleting a directory.
#define NE_FLM_IO_PATH_NOT_FOUND   0xC209
 0xC209 - File not found.
#define NE_FLM_IO_TOO_MANY_OPEN_FILES   0xC20A
 0xC20A - Too many files open.
#define NE_FLM_IO_PATH_TOO_LONG   0xC20B
 0xC20B - File name too long.
#define NE_FLM_IO_NO_MORE_FILES   0xC20C
 0xC20C - No more files in directory.
#define NE_FLM_IO_DELETING_FILE   0xC20D
 0xC20D - Error occurred while deleting a file.
#define NE_FLM_IO_FILE_LOCK_ERR   0xC20E
 0xC20E - Error attempting to acquire a byte-range lock on a file.
#define NE_FLM_IO_FILE_UNLOCK_ERR   0xC20F
 0xC20F - Error attempting to release a byte-range lock on a file.
#define NE_FLM_IO_PATH_CREATE_FAILURE   0xC210
 0xC210 - Error occurred while attempting to create a directory or sub-directory.
#define NE_FLM_IO_RENAME_FAILURE   0xC211
 0xC211 - Error occurred while renaming a file.
#define NE_FLM_IO_INVALID_PASSWORD   0xC212
 0xC212 - Invalid file password.
#define NE_FLM_SETTING_UP_FOR_READ   0xC213
 0xC213 - Error occurred while setting up to perform a file read operation.
#define NE_FLM_SETTING_UP_FOR_WRITE   0xC214
 0xC214 - Error occurred while setting up to perform a file write operation.
#define NE_FLM_IO_CANNOT_REDUCE_PATH   0xC215
 0xC215 - Cannot reduce file name into more components.
#define NE_FLM_INITIALIZING_IO_SYSTEM   0xC216
 0xC216 - Error occurred while setting up to access the file system.
#define NE_FLM_FLUSHING_FILE   0xC217
 0xC217 - Error occurred while flushing file data buffers to disk.
#define NE_FLM_IO_INVALID_FILENAME   0xC218
 0xC218 - Invalid file name.
#define NE_FLM_IO_CONNECT_ERROR   0xC219
 0xC219 - Error connecting to a remote network resource.
#define NE_FLM_OPENING_FILE   0xC21A
 0xC21A - Unexpected error occurred while opening a file.
#define NE_FLM_DIRECT_OPENING_FILE   0xC21B
 0xC21B - Unexpected error occurred while opening a file in direct access mode.
#define NE_FLM_CREATING_FILE   0xC21C
 0xC21C - Unexpected error occurred while creating a file.
#define NE_FLM_DIRECT_CREATING_FILE   0xC21D
 0xC21D - Unexpected error occurred while creating a file in direct access mode.
#define NE_FLM_READING_FILE   0xC21E
 0xC21E - Unexpected error occurred while reading a file.
#define NE_FLM_DIRECT_READING_FILE   0xC21F
 0xC21F - Unexpected error occurred while reading a file in direct access mode.
#define NE_FLM_WRITING_FILE   0xC220
 0xC220 - Unexpected error occurred while writing to a file.
#define NE_FLM_DIRECT_WRITING_FILE   0xC221
 0xC221 - Unexpected error occurred while writing a file in direct access mode.
#define NE_FLM_POSITIONING_IN_FILE   0xC222
 0xC222 - Unexpected error occurred while positioning within a file.
#define NE_FLM_GETTING_FILE_SIZE   0xC223
 0xC223 - Unexpected error occurred while getting a file's size.
#define NE_FLM_TRUNCATING_FILE   0xC224
 0xC224 - Unexpected error occurred while truncating a file.
#define NE_FLM_PARSING_FILE_NAME   0xC225
 0xC225 - Unexpected error occurred while parsing a file's name.
#define NE_FLM_CLOSING_FILE   0xC226
 0xC226 - Unexpected error occurred while closing a file.
#define NE_FLM_GETTING_FILE_INFO   0xC227
 0xC227 - Unexpected error occurred while getting information about a file.
#define NE_FLM_EXPANDING_FILE   0xC228
 0xC228 - Unexpected error occurred while expanding a file.
#define NE_FLM_GETTING_FREE_BLOCKS   0xC229
 0xC229 - Unexpected error getting free blocks from file system.
#define NE_FLM_CHECKING_FILE_EXISTENCE   0xC22A
 0xC22A - Unexpected error occurred while checking to see if a file exists.
#define NE_FLM_RENAMING_FILE   0xC22B
 0xC22B - Unexpected error occurred while renaming a file.
#define NE_FLM_SETTING_FILE_INFO   0xC22C
 0xC22C - Unexpected error occurred while setting a file's information.
#define NE_FLM_IO_PENDING   0xC22D
 0xC22D - I/O has not yet completed
#define NE_FLM_ASYNC_FAILED   0xC22E
 0xC22E - An async I/O operation failed
#define NE_FLM_MISALIGNED_IO   0xC22F
 0xC22F - Misaligned buffer or offset encountered during I/O request
#define NE_FLM_STREAM_DECOMPRESS_ERROR   0xC400
 0xC400 - Error decompressing data stream.
#define NE_FLM_STREAM_NOT_COMPRESSED   0xC401
 0xC401 - Attempting to decompress a data stream that is not compressed.
#define NE_FLM_STREAM_TOO_MANY_FILES   0xC402
 0xC402 - Too many files in input stream.
#define NE_FLM_COULD_NOT_CREATE_SEMAPHORE   0xC500
 0xC500 - Could not create a semaphore.
#define NE_FLM_BAD_UTF8   0xC501
 0xC501 - An invalid byte sequence was found in a UTF-8 string
#define NE_FLM_ERROR_WAITING_ON_SEMAPHORE   0xC502
 0xC502 - Error occurred while waiting on a sempahore.
#define NE_FLM_BAD_SEN   0xC503
 0xC503 - Invalid simple encoded number.
#define NE_FLM_COULD_NOT_START_THREAD   0xC504
 0xC504 - Problem starting a new thread.
#define NE_FLM_BAD_BASE64_ENCODING   0xC505
 0xC505 - Invalid base64 sequence encountered.
#define NE_FLM_STREAM_EXISTS   0xC506
 0xC506 - Stream file already exists.
#define NE_FLM_MULTIPLE_MATCHES   0xC507
 0xC507 - Multiple items matched but only one match was expected.
#define NE_FLM_BTREE_KEY_SIZE   0xC508
 0xC508 - Invalid b-tree key size.
#define NE_FLM_BTREE_BAD_STATE   0xC509
 0xC509 - B-tree operation cannot be completed.
#define NE_FLM_COULD_NOT_CREATE_MUTEX   0xC50A
 0xC50A - Error occurred while creating or initializing a mutex.
#define NE_FLM_BAD_PLATFORM_FORMAT   0xC50B
 0xC50B - In-memory alignment of disk structures is incorrect
#define NE_FLM_LOCK_REQ_TIMEOUT   0xC50C
 0xC50C - Timeout while waiting for a lock object
#define NE_FLM_WAIT_TIMEOUT   0xC50D
 0xC50D - Timeout while waiting on a semaphore, condition variable, or reader/writer lock
#define NE_FLM_NOIP_ADDR   0xC900
 0xC900 - IP address not found
#define NE_FLM_SOCKET_FAIL   0xC901
 0xC901 - IP socket failure
#define NE_FLM_CONNECT_FAIL   0xC902
 0xC902 - TCP/IP connection failure
#define NE_FLM_BIND_FAIL   0xC903
 0xC903 - The TCP/IP services on your system may not be configured or installed.
#define NE_FLM_LISTEN_FAIL   0xC904
 0xC904 - TCP/IP listen failed
#define NE_FLM_ACCEPT_FAIL   0xC905
 0xC905 - TCP/IP accept failed
#define NE_FLM_SELECT_ERR   0xC906
 0xC906 - TCP/IP select failed
#define NE_FLM_SOCKET_SET_OPT_FAIL   0xC907
 0xC907 - TCP/IP socket operation failed
#define NE_FLM_SOCKET_DISCONNECT   0xC908
 0xC908 - TCP/IP disconnected
#define NE_FLM_SOCKET_READ_FAIL   0xC909
 0xC909 - TCP/IP read failed
#define NE_FLM_SOCKET_WRITE_FAIL   0xC90A
 0xC90A - TCP/IP write failed
#define NE_FLM_SOCKET_READ_TIMEOUT   0xC90B
 0xC90B - TCP/IP read timeout
#define NE_FLM_SOCKET_WRITE_TIMEOUT   0xC90C
 0xC90C - TCP/IP write timeout
#define NE_FLM_SOCKET_ALREADY_CLOSED   0xC90D
 0xC90D - Connection already closed

Typedefs

typedef FLMINT32 RCODE
 Return code.

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__flm__languages.html0000644000175000017500000005023510510774540025122 0ustar ahodgkinsonahodgkinson FLAIM: Flm_languages

Flm_languages


Defines

#define FLM_US_LANG   0
 English, United States.
#define FLM_AF_LANG   1
 Afrikaans.
#define FLM_AR_LANG   2
 Arabic.
#define FLM_CA_LANG   3
 Catalan.
#define FLM_HR_LANG   4
 Croatian.
#define FLM_CZ_LANG   5
 Czech.
#define FLM_DK_LANG   6
 Danish.
#define FLM_NL_LANG   7
 Dutch.
#define FLM_OZ_LANG   8
 English, Australia.
#define FLM_CE_LANG   9
 English, Canada.
#define FLM_UK_LANG   10
 English, United Kingdom.
#define FLM_FA_LANG   11
 Farsi.
#define FLM_SU_LANG   12
 Finnish.
#define FLM_CF_LANG   13
 French, Canada.
#define FLM_FR_LANG   14
 French, France.
#define FLM_GA_LANG   15
 Galician.
#define FLM_DE_LANG   16
 German, Germany.
#define FLM_SD_LANG   17
 German, Switzerland.
#define FLM_GR_LANG   18
 Greek.
#define FLM_HE_LANG   19
 Hebrew.
#define FLM_HU_LANG   20
 Hungarian.
#define FLM_IS_LANG   21
 Icelandic.
#define FLM_IT_LANG   22
 Italian.
#define FLM_NO_LANG   23
 Norwegian.
#define FLM_PL_LANG   24
 Polish.
#define FLM_BR_LANG   25
 Portuguese, Brazil.
#define FLM_PO_LANG   26
 Portuguese, Portugal.
#define FLM_RU_LANG   27
 Russian.
#define FLM_SL_LANG   28
 Slovak.
#define FLM_ES_LANG   29
 Spanish.
#define FLM_SV_LANG   30
 Swedish.
#define FLM_YK_LANG   31
 Ukrainian.
#define FLM_UR_LANG   32
 Urdu.
#define FLM_TK_LANG   33
 Turkey.
#define FLM_JP_LANG   34
 Japanese.
#define FLM_KO_LANG   35
 Korean.
#define FLM_CT_LANG   36
 Chinese-Traditional.
#define FLM_CS_LANG   37
 Chinese-Simplified.
#define FLM_LA_LANG   38
 another Asian language

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/group__compare__rules.html0000644000175000017500000001306310510774540025154 0ustar ahodgkinsonahodgkinson FLAIM: Compare_rules

Compare_rules


Defines

#define FLM_COMP_CASE_INSENSITIVE   0x0001
 0x0001 = Do case sensitive comparison.
#define FLM_COMP_COMPRESS_WHITESPACE   0x0002
 0x0002 = Compare multiple whitespace characters as a single space.
#define FLM_COMP_NO_WHITESPACE   0x0004
 0x0004 = Ignore all whitespace during comparison.
#define FLM_COMP_NO_UNDERSCORES   0x0008
 0x0008 = Ignore all underscore characters during comparison.
#define FLM_COMP_NO_DASHES   0x0010
 0x0010 = Ignore all dash characters during comparison.
#define FLM_COMP_WHITESPACE_AS_SPACE   0x0020
 0x0020 = Treat newlines and tabs as spaces during comparison.
#define FLM_COMP_IGNORE_LEADING_SPACE   0x0040
 0x0040 = Ignore leading space characters during comparison.
#define FLM_COMP_IGNORE_TRAILING_SPACE   0x0080
 0x0080 = Ignore trailing space characters during comparison.

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/modules.html0000644000175000017500000000721510510774540022254 0ustar ahodgkinsonahodgkinson FLAIM: Module Index

FLAIM Modules

Here is a list of all modules:
Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals.html0000644000175000017500000000530310510774540022223 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- a -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x62.html0000644000175000017500000000515310510774540023005 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- b -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x63.html0000644000175000017500000000531610510774540023007 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- c -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x65.html0000644000175000017500000001041010510774540023000 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- e -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x66.html0000644000175000017500000027161610510774540023022 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- f -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x68.html0000644000175000017500000000572310510774540023016 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- h -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x69.html0000644000175000017500000000514510510774540023015 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- i -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x6c.html0000644000175000017500000000623110510774540023064 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- l -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x6e.html0000644000175000017500000003243310510774540023071 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- n -

  • NE_FLM_ACCEPT_FAIL : ftk.h
  • NE_FLM_ASYNC_FAILED : ftk.h
  • NE_FLM_BAD_BASE64_ENCODING : ftk.h
  • NE_FLM_BAD_PLATFORM_FORMAT : ftk.h
  • NE_FLM_BAD_SEN : ftk.h
  • NE_FLM_BAD_UTF8 : ftk.h
  • NE_FLM_BIND_FAIL : ftk.h
  • NE_FLM_BOF_HIT : ftk.h
  • NE_FLM_BTREE_BAD_STATE : ftk.h
  • NE_FLM_BTREE_ERROR : ftk.h
  • NE_FLM_BTREE_FULL : ftk.h
  • NE_FLM_BTREE_KEY_SIZE : ftk.h
  • NE_FLM_CHECKING_FILE_EXISTENCE : ftk.h
  • NE_FLM_CLOSING_FILE : ftk.h
  • NE_FLM_CONNECT_FAIL : ftk.h
  • NE_FLM_CONV_DEST_OVERFLOW : ftk.h
  • NE_FLM_CONV_ILLEGAL : ftk.h
  • NE_FLM_CONV_NUM_OVERFLOW : ftk.h
  • NE_FLM_COULD_NOT_CREATE_MUTEX : ftk.h
  • NE_FLM_COULD_NOT_CREATE_SEMAPHORE : ftk.h
  • NE_FLM_COULD_NOT_START_THREAD : ftk.h
  • NE_FLM_CREATING_FILE : ftk.h
  • NE_FLM_DATA_ERROR : ftk.h
  • NE_FLM_DIRECT_CREATING_FILE : ftk.h
  • NE_FLM_DIRECT_OPENING_FILE : ftk.h
  • NE_FLM_DIRECT_READING_FILE : ftk.h
  • NE_FLM_DIRECT_WRITING_FILE : ftk.h
  • NE_FLM_EOF_HIT : ftk.h
  • NE_FLM_ERROR_WAITING_ON_SEMAPHORE : ftk.h
  • NE_FLM_EXISTS : ftk.h
  • NE_FLM_EXPANDING_FILE : ftk.h
  • NE_FLM_FAILURE : ftk.h
  • NE_FLM_FLUSHING_FILE : ftk.h
  • NE_FLM_GETTING_FILE_INFO : ftk.h
  • NE_FLM_GETTING_FILE_SIZE : ftk.h
  • NE_FLM_GETTING_FREE_BLOCKS : ftk.h
  • NE_FLM_ILLEGAL_OP : ftk.h
  • NE_FLM_INITIALIZING_IO_SYSTEM : ftk.h
  • NE_FLM_INVALID_PARM : ftk.h
  • NE_FLM_IO_ACCESS_DENIED : ftk.h
  • NE_FLM_IO_BAD_FILE_HANDLE : ftk.h
  • NE_FLM_IO_CANNOT_REDUCE_PATH : ftk.h
  • NE_FLM_IO_CONNECT_ERROR : ftk.h
  • NE_FLM_IO_COPY_ERR : ftk.h
  • NE_FLM_IO_DELETING_FILE : ftk.h
  • NE_FLM_IO_DIRECTORY_ERR : ftk.h
  • NE_FLM_IO_DISK_FULL : ftk.h
  • NE_FLM_IO_END_OF_FILE : ftk.h
  • NE_FLM_IO_FILE_LOCK_ERR : ftk.h
  • NE_FLM_IO_FILE_UNLOCK_ERR : ftk.h
  • NE_FLM_IO_INVALID_FILENAME : ftk.h
  • NE_FLM_IO_INVALID_PASSWORD : ftk.h
  • NE_FLM_IO_NO_MORE_FILES : ftk.h
  • NE_FLM_IO_OPEN_ERR : ftk.h
  • NE_FLM_IO_PATH_CREATE_FAILURE : ftk.h
  • NE_FLM_IO_PATH_NOT_FOUND : ftk.h
  • NE_FLM_IO_PATH_TOO_LONG : ftk.h
  • NE_FLM_IO_PENDING : ftk.h
  • NE_FLM_IO_RENAME_FAILURE : ftk.h
  • NE_FLM_IO_SEEK_ERR : ftk.h
  • NE_FLM_IO_TOO_MANY_OPEN_FILES : ftk.h
  • NE_FLM_LISTEN_FAIL : ftk.h
  • NE_FLM_LOCK_REQ_TIMEOUT : ftk.h
  • NE_FLM_MEM : ftk.h
  • NE_FLM_MISALIGNED_IO : ftk.h
  • NE_FLM_MULTIPLE_MATCHES : ftk.h
  • NE_FLM_NOIP_ADDR : ftk.h
  • NE_FLM_NOT_FOUND : ftk.h
  • NE_FLM_NOT_IMPLEMENTED : ftk.h
  • NE_FLM_NOT_UNIQUE : ftk.h
  • NE_FLM_OK : ftk.h
  • NE_FLM_OPENING_FILE : ftk.h
  • NE_FLM_PARSING_FILE_NAME : ftk.h
  • NE_FLM_POSITIONING_IN_FILE : ftk.h
  • NE_FLM_READING_FILE : ftk.h
  • NE_FLM_RENAMING_FILE : ftk.h
  • NE_FLM_SELECT_ERR : ftk.h
  • NE_FLM_SETTING_FILE_INFO : ftk.h
  • NE_FLM_SETTING_UP_FOR_READ : ftk.h
  • NE_FLM_SETTING_UP_FOR_WRITE : ftk.h
  • NE_FLM_SOCKET_ALREADY_CLOSED : ftk.h
  • NE_FLM_SOCKET_DISCONNECT : ftk.h
  • NE_FLM_SOCKET_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_TIMEOUT : ftk.h
  • NE_FLM_SOCKET_SET_OPT_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_TIMEOUT : ftk.h
  • NE_FLM_STREAM_DECOMPRESS_ERROR : ftk.h
  • NE_FLM_STREAM_EXISTS : ftk.h
  • NE_FLM_STREAM_NOT_COMPRESSED : ftk.h
  • NE_FLM_STREAM_TOO_MANY_FILES : ftk.h
  • NE_FLM_SYNTAX : ftk.h
  • NE_FLM_TRUNCATING_FILE : ftk.h
  • NE_FLM_WAIT_TIMEOUT : ftk.h
  • NE_FLM_WRITING_FILE : ftk.h
  • newline() : ftk.h

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x71.html0000644000175000017500000000657310510774540023014 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- q -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x72.html0000644000175000017500000001321210510774540023001 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- r -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_0x73.html0000644000175000017500000000514510510774540023010 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- s -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_func.html0000644000175000017500000003671410510774540023250 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- a -

- e -

- f -

- n -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_type.html0000644000175000017500000000541510510774540023270 0ustar ahodgkinsonahodgkinson FLAIM: Class Members  


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_enum.html0000644000175000017500000000742010510774540023251 0ustar ahodgkinsonahodgkinson FLAIM: Class Members  


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_eval.html0000644000175000017500000013627110510774540023243 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- f -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_eval_0x6c.html0000644000175000017500000000510510510774540024072 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- l -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_eval_0x71.html0000644000175000017500000000515410510774540024015 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- q -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_eval_0x72.html0000644000175000017500000001155510510774540024020 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- r -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_defs.html0000644000175000017500000010255210510774540023230 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- f -


Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/globals_defs_0x6e.html0000644000175000017500000003073210510774540024072 0ustar ahodgkinsonahodgkinson FLAIM: Class Members

 

- n -

  • NE_FLM_ACCEPT_FAIL : ftk.h
  • NE_FLM_ASYNC_FAILED : ftk.h
  • NE_FLM_BAD_BASE64_ENCODING : ftk.h
  • NE_FLM_BAD_PLATFORM_FORMAT : ftk.h
  • NE_FLM_BAD_SEN : ftk.h
  • NE_FLM_BAD_UTF8 : ftk.h
  • NE_FLM_BIND_FAIL : ftk.h
  • NE_FLM_BOF_HIT : ftk.h
  • NE_FLM_BTREE_BAD_STATE : ftk.h
  • NE_FLM_BTREE_ERROR : ftk.h
  • NE_FLM_BTREE_FULL : ftk.h
  • NE_FLM_BTREE_KEY_SIZE : ftk.h
  • NE_FLM_CHECKING_FILE_EXISTENCE : ftk.h
  • NE_FLM_CLOSING_FILE : ftk.h
  • NE_FLM_CONNECT_FAIL : ftk.h
  • NE_FLM_CONV_DEST_OVERFLOW : ftk.h
  • NE_FLM_CONV_ILLEGAL : ftk.h
  • NE_FLM_CONV_NUM_OVERFLOW : ftk.h
  • NE_FLM_COULD_NOT_CREATE_MUTEX : ftk.h
  • NE_FLM_COULD_NOT_CREATE_SEMAPHORE : ftk.h
  • NE_FLM_COULD_NOT_START_THREAD : ftk.h
  • NE_FLM_CREATING_FILE : ftk.h
  • NE_FLM_DATA_ERROR : ftk.h
  • NE_FLM_DIRECT_CREATING_FILE : ftk.h
  • NE_FLM_DIRECT_OPENING_FILE : ftk.h
  • NE_FLM_DIRECT_READING_FILE : ftk.h
  • NE_FLM_DIRECT_WRITING_FILE : ftk.h
  • NE_FLM_EOF_HIT : ftk.h
  • NE_FLM_ERROR_WAITING_ON_SEMAPHORE : ftk.h
  • NE_FLM_EXISTS : ftk.h
  • NE_FLM_EXPANDING_FILE : ftk.h
  • NE_FLM_FAILURE : ftk.h
  • NE_FLM_FLUSHING_FILE : ftk.h
  • NE_FLM_GETTING_FILE_INFO : ftk.h
  • NE_FLM_GETTING_FILE_SIZE : ftk.h
  • NE_FLM_GETTING_FREE_BLOCKS : ftk.h
  • NE_FLM_ILLEGAL_OP : ftk.h
  • NE_FLM_INITIALIZING_IO_SYSTEM : ftk.h
  • NE_FLM_INVALID_PARM : ftk.h
  • NE_FLM_IO_ACCESS_DENIED : ftk.h
  • NE_FLM_IO_BAD_FILE_HANDLE : ftk.h
  • NE_FLM_IO_CANNOT_REDUCE_PATH : ftk.h
  • NE_FLM_IO_CONNECT_ERROR : ftk.h
  • NE_FLM_IO_COPY_ERR : ftk.h
  • NE_FLM_IO_DELETING_FILE : ftk.h
  • NE_FLM_IO_DIRECTORY_ERR : ftk.h
  • NE_FLM_IO_DISK_FULL : ftk.h
  • NE_FLM_IO_END_OF_FILE : ftk.h
  • NE_FLM_IO_FILE_LOCK_ERR : ftk.h
  • NE_FLM_IO_FILE_UNLOCK_ERR : ftk.h
  • NE_FLM_IO_INVALID_FILENAME : ftk.h
  • NE_FLM_IO_INVALID_PASSWORD : ftk.h
  • NE_FLM_IO_NO_MORE_FILES : ftk.h
  • NE_FLM_IO_OPEN_ERR : ftk.h
  • NE_FLM_IO_PATH_CREATE_FAILURE : ftk.h
  • NE_FLM_IO_PATH_NOT_FOUND : ftk.h
  • NE_FLM_IO_PATH_TOO_LONG : ftk.h
  • NE_FLM_IO_PENDING : ftk.h
  • NE_FLM_IO_RENAME_FAILURE : ftk.h
  • NE_FLM_IO_SEEK_ERR : ftk.h
  • NE_FLM_IO_TOO_MANY_OPEN_FILES : ftk.h
  • NE_FLM_LISTEN_FAIL : ftk.h
  • NE_FLM_LOCK_REQ_TIMEOUT : ftk.h
  • NE_FLM_MEM : ftk.h
  • NE_FLM_MISALIGNED_IO : ftk.h
  • NE_FLM_MULTIPLE_MATCHES : ftk.h
  • NE_FLM_NOIP_ADDR : ftk.h
  • NE_FLM_NOT_FOUND : ftk.h
  • NE_FLM_NOT_IMPLEMENTED : ftk.h
  • NE_FLM_NOT_UNIQUE : ftk.h
  • NE_FLM_OK : ftk.h
  • NE_FLM_OPENING_FILE : ftk.h
  • NE_FLM_PARSING_FILE_NAME : ftk.h
  • NE_FLM_POSITIONING_IN_FILE : ftk.h
  • NE_FLM_READING_FILE : ftk.h
  • NE_FLM_RENAMING_FILE : ftk.h
  • NE_FLM_SELECT_ERR : ftk.h
  • NE_FLM_SETTING_FILE_INFO : ftk.h
  • NE_FLM_SETTING_UP_FOR_READ : ftk.h
  • NE_FLM_SETTING_UP_FOR_WRITE : ftk.h
  • NE_FLM_SOCKET_ALREADY_CLOSED : ftk.h
  • NE_FLM_SOCKET_DISCONNECT : ftk.h
  • NE_FLM_SOCKET_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_TIMEOUT : ftk.h
  • NE_FLM_SOCKET_SET_OPT_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_TIMEOUT : ftk.h
  • NE_FLM_STREAM_DECOMPRESS_ERROR : ftk.h
  • NE_FLM_STREAM_EXISTS : ftk.h
  • NE_FLM_STREAM_NOT_COMPRESSED : ftk.h
  • NE_FLM_STREAM_TOO_MANY_FILES : ftk.h
  • NE_FLM_SYNTAX : ftk.h
  • NE_FLM_TRUNCATING_FILE : ftk.h
  • NE_FLM_WAIT_TIMEOUT : ftk.h
  • NE_FLM_WRITING_FILE : ftk.h

Generated on Wed Oct 4 12:11:43 2006 for FLAIM by  doxygen 1.4.6
libflaim-4.9.966/docs/docs/html/index.html0000644000175000017500000000045510510774540021712 0ustar ahodgkinsonahodgkinson FLAIM libflaim-4.9.966/docs/docs/html/tree.html0000644000175000017500000011303010510774540021534 0ustar ahodgkinsonahodgkinson TreeView

FLAIM

o+File List

o+Class List

o+Class Hierarchy

o*Class Members

o+Modules

\*File Members

libflaim-4.9.966/docs/docs/html/ftv2blank.png0000644000175000017500000000025610510774540022313 0ustar ahodgkinsonahodgkinson‰PNG  IHDR–ÖGtRNS”ý®tEXtSoftwaregif2png 2.4.2£^G%tEXtCommentUlead GIF SmartSaver Ver 2.0!ø×^SIDATxÚc8À€€0àBx<2Ër|IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2doc.png0000644000175000017500000000037710510774540021775 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÿÿÀÀÀ€€€ÿ³½ûÈtRNS@æØftEXtSoftwaregif2png 2.4.2£^GvIDATxÚc````c``d'0bqâ$8`qÊ'3001£2 pT2Si`q'Ê€\\À”¢ˆRKRSiÈ”„RRÅi 6त¤h‚ÄQ‚kqMaNUò`'E$pgc 0Åo#õÐàUƒG½IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2folderclosed.png0000644000175000017500000000040310510774540023663 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÀÀÀ€€€€€Bî@átRNS@æØftEXtSoftwaregif2png 2.4.2£^G}IDATxÚŽÁ „0C#ŒwûB½+øemï¢ÍÿÿŠ3­îZö°Úð†¦ˆ@S‚š×QB%®zv íêyyIsáò2_I¹í#` Í6fä@K´ÛÛåîx ù–m‹¢nùPd¦p¶P Óx†˜¦Þ]%Íþ߯ ˜`½ IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2folderopen.png0000644000175000017500000000040510510774540023355 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÿÿÀÀÀ€€€€€’2ŒåtRNS@æØftEXtSoftwaregif2png 2.4.2£^G|IDATxÚ…A‚0E_õ-SÖÆ××Ò ±Þÿ: !¢oóþÿ‹€ j<*aÅÓCiÁ´«ùp¬ƒÊ»û¤‹®&F¶LèÃú³}¨ë¥FTFN*âΕ=º°d“…› sqƒu×ò§ò ûÒëCùýò ¤)ËÚG¬DIEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2lastnode.png0000644000175000017500000000035110510774540023031 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO È3$§ˆÊ3=IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2link.png0000644000175000017500000000054610510774540022163 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-0PLTEÿÿÿ€ÿÿ€ÿ€€€€€ÀÀÀ~›tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0Ù åJIDATxÚ}Œ± Â0Eo3´c~¡SW‹~€n®Z²¾:d5¸dì/Ÿy!PÐÉÍᾨ‚àf}ÀžQp@Ï­tÛbâ ÁÌa;j»;éhº¬Q$ìÜKhíÒ^~ý’¨m„²((8ÊÒLF-YÌ8\‹Ìãþ+qúy¼gy…TiÞÞ/øÏq•+¡xÐÿIEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2mlastnode.png0000644000175000017500000000024010510774540023203 0ustar ahodgkinsonahodgkinson‰PNG  IHDRÃÃÄy PLTEÿÿÿ€€€<^»,tRNS@æØftEXtSoftwaregif2png 2.4.2£^G#IDATxÚc`   `„Œ¡¡ ɨµPʉamºÀÜiÈIEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2mnode.png0000644000175000017500000000030210510774540022316 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù$PLTEÀÀÀ€€€S¾™tRNS@æØftEXtSoftwaregif2png 2.4.2£^G*IDATxÚc` .àBt§RT÷n €ñÁbÜLJJÜÜÜÈ"05˜ÚÑ·y'ª÷–IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2node.png0000644000175000017500000000035310510774540022147 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO ˆVŒÂ.RâúE:áIEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2plastnode.png0000644000175000017500000000024510510774540023213 0ustar ahodgkinsonahodgkinson‰PNG  IHDRÃÃÄy PLTEÿÿÿ€€€<^»,tRNS@æØftEXtSoftwaregif2png 2.4.2£^G(IDATxÚc` 0ach(`2µ BY 1,nÁÂåíu§IEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2pnode.png0000644000175000017500000000031010510774540022320 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù$PLTEÀÀÀ€€€S¾™tRNS@æØftEXtSoftwaregif2png 2.4.2£^G0IDATxÚc` .àBn&8ââTŠÊàÞ >DŒ›II‰››Y¦S;:ºk/ªËoIEND®B`‚libflaim-4.9.966/docs/docs/html/ftv2vertline.png0000644000175000017500000000034510510774540023053 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO[!¦å<è:IEND®B`‚libflaim-4.9.966/src/0000755000175000017500000000000010510774540015654 5ustar ahodgkinsonahodgkinsonlibflaim-4.9.966/src/fscomblk.cpp0000644000175000017500000004355210510774540020171 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: B-tree block combining // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscomblk.cpp 12283 2006-01-19 14:53:15 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC FLMINT FSBlkCompressPKC( BTSK * pStack, FLMBYTE * pTempPKCBuf); /**************************************************************************** Desc: Build a PKC buffer for a single element Return: Length used within the PKC buffer ****************************************************************************/ FINLINE FLMUINT FSElmBuildPKC( FLMBYTE * pPkcBuf, FLMBYTE * pElement, FLMBYTE * pElmPkcBuf, FLMUINT uiElmOvhd) { FLMUINT uiPkc; FLMUINT uiKeyLen; if (uiElmOvhd == BNE_DATA_OVHD) { return (0); } uiKeyLen = (FLMUINT) (BBE_GET_KL( pElement)); if ((uiPkc = (FLMUINT) (BBE_GET_PKC( pElement))) != 0) { f_memmove( pPkcBuf, pElmPkcBuf, uiPkc); } if (uiPkc + uiKeyLen > BBE_PKC_MAX) { uiKeyLen = (FLMUINT) (BBE_PKC_MAX - uiPkc); } f_memmove( &pPkcBuf[uiPkc], &pElement[uiElmOvhd], uiKeyLen); return (uiPkc + uiKeyLen); } /**************************************************************************** Desc: Try to combine two blocks into a single block. The algorithm will alternate tring the block to the right or uiLeft of the 'target' block. The bsCurElm MUST be positioned to the current element. The bsCurElm may be at the very end of the block not pointing to an element. If so then if blocks are combine bsCurElm should then be valid. ****************************************************************************/ RCODE FSCombineBlks( FDB * pDb, LFILE * pLFile, BTSK ** pStackRV) { RCODE rc = FERR_OK; BTSK * pStack = *pStackRV; SCACHE * pLeftCache; SCACHE * pRightCache; FLMBYTE * pLeftBlk; FLMBYTE * pRightBlk; FLMBYTE * pBlk = pStack->pBlk; FLMBOOL bReleaseLeft = FALSE; FLMBOOL bReleaseRight = FALSE; FLMUINT uiLeftBlkAddr; FLMUINT uiRightBlkAddr; FLMUINT uiLeftBlkEnd; FLMUINT uiRightBlkEnd; FLMUINT uiBlkAddr = pStack->uiBlkAddr; FLMUINT uiBlkEnd = pStack->uiBlkEnd; FLMUINT uiCurElm = pStack->uiCurElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT uiBlkSize; FLMUINT uiPosToElm = 0; FLMUINT uiPosToBlk = 0; FLMUINT uiSplitPoint; FLMUINT uiTargetSplitPoint; FLMINT iTemp; FLMINT iDelta; BTSK tempStack; FLMBYTE pPkcBuf[BBE_PKC_MAX]; DB_STATS * pDbStats; // Return if either block is leftmost or rightmost block uiLeftBlkAddr = (FLMUINT) FB2UD( &pBlk[BH_PREV_BLK]); uiRightBlkAddr = (FLMUINT) FB2UD( &pBlk[BH_NEXT_BLK]); if ((uiLeftBlkAddr == BT_END) || (uiRightBlkAddr == BT_END)) { goto Exit; } uiBlkSize = pDb->pFile->FileHdr.uiBlockSize - uiElmOvhd; // Read in left and right blocks - make sure all cache ptrs are valid if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiLeftBlkAddr, NULL, &pLeftCache))) { goto Exit; } bReleaseLeft = TRUE; if (RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiRightBlkAddr, NULL, &pRightCache))) { goto Exit; } bReleaseRight = TRUE; // Determine if there is room without compressing first elm in current blk pLeftBlk = pLeftCache->pucBlk; uiLeftBlkEnd = (FLMUINT) FB2UW( &pLeftBlk[BH_BLK_END]); pRightBlk = pRightCache->pucBlk; uiRightBlkEnd = (FLMUINT) FB2UW( &pRightBlk[BH_BLK_END]); // Don't want to fill too tight - so don't subtract BH_OVHD from sum if (uiLeftBlkEnd + uiBlkEnd + uiRightBlkEnd > uiBlkSize + uiBlkSize) { FSCB_Unpin: if (bReleaseRight) { ScaReleaseCache( pRightCache, FALSE); bReleaseRight = FALSE; } if (bReleaseLeft) { ScaReleaseCache( pLeftCache, FALSE); bReleaseLeft = FALSE; } if (RC_OK( rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack))) { pStack->uiCurElm = uiCurElm; FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM); } goto Exit; } // This is a very good yet extreamly tricky algorithm! If the delta // (difference in size) of the left and right blocks is more than the // size of the middle block the entire middle will will be moved to the // left or right block that is not very full. Otherwise, // uiTargetSplitPoint will be computed to be around the point that will // place elements in both blocks to fill the left and right blocks to // about the same point. NOTE: Read the comments if all is fuzzy. iTemp = uiBlkEnd - BH_OVHD + BBE_PKC_MAX; uiTargetSplitPoint = 0; if (uiLeftBlkEnd < uiRightBlkEnd) { iDelta = uiRightBlkEnd - uiLeftBlkEnd; if (iTemp <= iDelta) { pStack->uiCurElm = uiBlkEnd; } else { uiTargetSplitPoint = BH_OVHD + iDelta + ((iTemp - iDelta) >> 1); } } else { iDelta = uiLeftBlkEnd - uiRightBlkEnd; if (iTemp <= iDelta) { pStack->uiCurElm = BH_OVHD; } else { uiTargetSplitPoint = BH_OVHD + ((iTemp - iDelta) >> 1); } } if (uiTargetSplitPoint) { pStack->uiCurElm = uiTargetSplitPoint; // Scan AFTER targetSplitPoint if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } // Last check to see if elements will fit in both blocks. This is // still a chance that all elements will go to one block if ((uiLeftBlkEnd + (pStack->uiCurElm - BH_OVHD) > uiBlkSize) || (uiRightBlkEnd + (uiBlkEnd - pStack->uiCurElm) + BBE_PKC_MAX > uiBlkSize)) { goto FSCB_Unpin; } } // We are now guarenteed to fit!!! - log blocks and start moving if ((pDbStats = pDb->pDbStats) != NULL) { LFILE_STATS * pLFileStats; if ((pLFileStats = fdbGetLFileStatPtr( pDb, pLFile)) != NULL) { pLFileStats->bHaveStats = pDbStats->bHaveStats = TRUE; pLFileStats->ui64BlockCombines++; } } if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pLeftCache))) { goto Exit; } pLeftBlk = pLeftCache->pucBlk; uiSplitPoint = pStack->uiCurElm; if (uiSplitPoint != BH_OVHD) { FLMUINT uiCompressBytes; FLMUINT uiBytesAdded = uiSplitPoint - BH_OVHD; tempStack.pSCache = pLeftCache; tempStack.pBlk = pLeftCache->pucBlk; FSBlkToStack( &tempStack); tempStack.uiKeyBufSize = MAX_KEY_SIZ; // Algorithm could call the move routine if it wasn't for // uiCompressBytes f_memmove( &pLeftBlk[uiLeftBlkEnd], &pBlk[BH_OVHD], uiBytesAdded); tempStack.uiCurElm = uiLeftBlkEnd; uiLeftBlkEnd += uiBytesAdded; tempStack.uiBlkEnd = uiLeftBlkEnd; UW2FBA( (FLMUINT16)uiLeftBlkEnd, &pLeftBlk[BH_BLK_END]); uiCompressBytes = FSBlkCompressPKC( &tempStack, pPkcBuf); if (uiCompressBytes == 0xFFFF) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } if (uiCurElm < uiSplitPoint) { uiPosToBlk = uiLeftBlkAddr; uiPosToElm = (FLMUINT) ((uiLeftBlkEnd - uiBytesAdded) + uiCurElm - BH_OVHD); if (uiCurElm != BH_OVHD) { uiPosToElm -= uiCompressBytes; } } } UD2FBA( (FLMUINT32)uiRightBlkAddr, &pLeftBlk[BH_NEXT_BLK]); ScaReleaseCache( pLeftCache, FALSE); bReleaseLeft = FALSE; // Done with the left block. Move the rest of the data into the // right block. Be carefull to compress the first element of the right // block and decompress the element that is at the split point in the // middle (deleted) block. if (RC_BAD( rc = ScaLogPhysBlk( pDb, &pRightCache))) { goto Exit; } pRightBlk = pRightCache->pucBlk; if (uiSplitPoint != uiBlkEnd) { FLMBYTE * pSplitPoint = &pBlk[uiSplitPoint]; tempStack.pSCache = pRightCache; tempStack.pBlk = pRightCache->pucBlk; FSBlkToStack( &tempStack); tempStack.uiKeyBufSize = MAX_KEY_SIZ; // Setup to position to current block and element at end of routine if (uiCurElm >= uiSplitPoint) { uiPosToBlk = uiRightBlkAddr; uiPosToElm = (FLMUINT) ((uiCurElm - uiSplitPoint) + BH_OVHD); // No PKC in fixed element blocks. if (uiCurElm != uiSplitPoint && tempStack.uiElmOvhd != BNE_DATA_OVHD) { uiPosToElm += BBE_GET_PKC( pSplitPoint); } } // If ScanTo() doesn't match uiCurElm exact then current element // does not have the proper stuff in the pkc buffer or bsKeyBuf FSBlkBuildPKC( pStack, pPkcBuf, FSBBPKC_AT_CURELM); if (RC_BAD( rc = FSBlkMoveElms( &tempStack, pSplitPoint, (FLMUINT) (uiBlkEnd - uiSplitPoint), pPkcBuf))) { goto Exit; } } UD2FBA( (FLMUINT32)uiLeftBlkAddr, &pRightBlk[BH_PREV_BLK]); ScaReleaseCache( pRightCache, FALSE); bReleaseRight = FALSE; // Now we can free the current block rc = FSBlockFree( pDb, pStack->pSCache); pStack->pSCache = NULL; pStack->pBlk = NULL; if (RC_BAD( rc)) { return (rc); } // Now the hard part. Go to the parent and delete the current element. // Go to the previous element and modify to reflect the new last element // in the left block. if (RC_BAD( rc = FSDelParentElm( pDb, pLFile, &pStack))) { return (rc); } if (uiSplitPoint != BH_OVHD) { // Position and fixup the parent elements if (RC_OK( rc = FSGetBlock( pDb, pLFile, uiLeftBlkAddr, pStack))) { rc = FSNewLastBlkElm( pDb, pLFile, &pStack, FSNLBE_GREATER | FSNLBE_POSITION); } } // Position the pStack to where you should be. The parent element is // pointing to the right block. if (RC_OK( rc)) { // Read in position block and position to current element if (RC_OK( rc = FSGetBlock( pDb, pLFile, uiPosToBlk, pStack))) { pStack->uiCurElm = uiPosToElm; if (uiPosToBlk == uiLeftBlkAddr) { rc = FSAdjustStack( pDb, pLFile, pStack, FALSE); } // This line must be explained! We should really be replacing the // original PKC buffer into what was there before this routine // was called. This works because delete/insert pairs call scanTo // before the insert. FSBlkBuildPKC( pStack, pStack->pKeyBuf, FSBBPKC_AT_CURELM); } } *pStackRV = pStack; Exit: if (bReleaseLeft) { ScaReleaseCache( pLeftCache, FALSE); } if (bReleaseRight) { ScaReleaseCache( pRightCache, FALSE); } return (rc); } /**************************************************************************** Desc: Move 1 or more elements into the bsCurElm location within a block. ****************************************************************************/ RCODE FSBlkMoveElms( BTSK * pStack, // Stack containing block to accept data FLMBYTE * pInsertElm, // Element(s) to insert into block FLMUINT uiInsElmLen, // Length of the Element(s) FLMBYTE * pElmPkcBuf) // PKC buffer for element if elm has PKC { FLMBYTE * pBlk = pStack->pBlk; FLMUINT uiCurElm = pStack->uiCurElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT uiBytesInPkc; FLMBYTE pInsertElmPckBuf[BBE_PKC_MAX]; FLMUINT uiMovedKeyLen; FLMUINT uiMovedPkc; FLMUINT uiTemp; FLMUINT uiNewBlkEnd; FLMUINT uiInsertElmKeyLen; FLMINT iDistanceToShiftDown; FLMUINT uiAreaToShiftDown; FLMBYTE pPkcBuf[BBE_PKC_MAX]; FLMUINT uiInsertElmPkc; FLMUINT uiInsertElmPkcLen; if (uiElmOvhd == BNE_DATA_OVHD) { if ((uiAreaToShiftDown = (pStack->uiBlkEnd - uiCurElm)) > 0) { shiftN( &pBlk[uiCurElm], uiAreaToShiftDown, uiInsElmLen); } f_memmove( &pBlk[uiCurElm], pInsertElm, uiInsElmLen); pStack->uiBlkEnd += uiInsElmLen; UW2FBA( (FLMUINT16)pStack->uiBlkEnd, &pBlk[BH_BLK_END]); goto Exit; } // ELSE Normal complex move with previos key count (PKC); // Puts up to BBE_PKC_MAX bytes in pPkcBuf[] only uiBytesInPkc = FSBlkBuildPKC( pStack, pPkcBuf, FSBBPKC_BEFORE_CURELM); // Compute real pkc for element uiInsertElmPkcLen = FSElmBuildPKC( pInsertElmPckBuf, pInsertElm, pElmPkcBuf, uiElmOvhd); uiMovedPkc = FSElmComparePKC( pPkcBuf, uiBytesInPkc, pInsertElmPckBuf, uiInsertElmPkcLen); // Compute how much area pInsertElm[] will take when moved, compute // uiBlkEnd and move most of the element except the key uiInsertElmKeyLen = (FLMUINT) (BBE_GET_KL( pInsertElm)); uiInsertElmPkc = (FLMUINT) (BBE_GET_PKC( pInsertElm)); uiMovedKeyLen = (FLMUINT) (uiInsertElmKeyLen + uiInsertElmPkc - uiMovedPkc); iDistanceToShiftDown = (FLMINT) (uiInsElmLen + uiMovedKeyLen - uiInsertElmKeyLen); if ((uiAreaToShiftDown = (FLMUINT) (pStack->uiBlkEnd - uiCurElm)) > 0) { shiftN( &pBlk[uiCurElm], uiAreaToShiftDown, iDistanceToShiftDown); } uiNewBlkEnd = (FLMUINT) (pStack->uiBlkEnd + iDistanceToShiftDown); UW2FBA( (FLMUINT16)uiNewBlkEnd, &pBlk[BH_BLK_END]); pStack->uiBlkEnd = uiNewBlkEnd; // Move the first pInsertElm[] overhead values and key to where to be // inserted FSSetElmOvhd( &pBlk[uiCurElm], uiElmOvhd, uiMovedPkc, uiMovedKeyLen, pInsertElm); // The tricky part is to move the key! The key could move in 2 parts // pInsertElmPckBuf and pInsertElm[] if (uiMovedKeyLen + uiMovedPkc > BBE_PKC_MAX) { // Move all that is in the pPkcBuf[] f_memcpy( &pBlk[uiCurElm + uiElmOvhd], &pInsertElmPckBuf[uiMovedPkc], uiTemp = (FLMUINT) (BBE_PKC_MAX - uiMovedPkc)); // Move the rest that is in the element f_memmove( &pBlk[uiCurElm + uiElmOvhd + uiTemp], &pInsertElm[uiElmOvhd + uiInsertElmKeyLen - (uiMovedKeyLen - uiTemp)], uiMovedKeyLen - uiTemp); } else if (uiMovedKeyLen) { // Entire key fits within the pPkcBuf[] f_memcpy( &pBlk[uiCurElm + uiElmOvhd], &pInsertElmPckBuf[uiMovedPkc], uiMovedKeyLen); } // Move the rest of the element(s) over to the block uiTemp = uiElmOvhd + uiInsertElmKeyLen; f_memmove( &pBlk[uiCurElm + uiElmOvhd + uiMovedKeyLen], &pInsertElm[uiTemp], uiInsElmLen - uiTemp); // Now if uiAreaToShiftDown has a value then position to the old // uiCurElm and try to compress more out of the element if (uiAreaToShiftDown) { pStack->uiCurElm = uiCurElm + iDistanceToShiftDown; // Could change pStack->wBlkEnd FSBlkCompressPKC( pStack, pPkcBuf); } // Points to start of inserted element(s) pStack->uiCurElm = uiCurElm; Exit: return (FERR_OK); } /**************************************************************************** Desc: Build the PKC portion scanning in a block to but not including pStack->uiCurElm ****************************************************************************/ FLMUINT FSBlkBuildPKC( BTSK * pStack, FLMBYTE * pPkcBuf, FLMUINT uiFlags) { FLMUINT uiMoveArea; FLMUINT uiPkc; FLMUINT uiTargetCurElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT uiCurElm; FLMUINT uiElmKeyLen; FLMBYTE * pCurElm; if (uiElmOvhd == BNE_DATA_OVHD) { return (0); } // Code below is fastest way to position to bsCurElm uiTargetCurElm = pStack->uiCurElm; uiMoveArea = uiPkc = 0; uiCurElm = BH_OVHD; while (uiCurElm < uiTargetCurElm) { FSBB_one_more_time: pCurElm = &pStack->pBlk[uiCurElm]; uiElmKeyLen = (FLMUINT) (BBE_GET_KL( pCurElm)); if (uiElmKeyLen) { // Move minimum data over to the pPkcBuf[] uiPkc = (FLMUINT) (BBE_GET_PKC( pCurElm)); uiMoveArea = ((uiPkc + uiElmKeyLen) > BBE_PKC_MAX) ? (BBE_PKC_MAX - uiPkc) : uiElmKeyLen; // Most common uiMoveArea value for data records and numeric keys if (uiMoveArea == 1) { pPkcBuf[uiPkc] = pCurElm[uiElmOvhd]; } else if (uiMoveArea) { f_memmove( &pPkcBuf[uiPkc], &pCurElm[uiElmOvhd], uiMoveArea); } } if (pStack->uiBlkType == BHT_LEAF) { // Goto the next element in the block uiCurElm += BBE_GET_RL( pCurElm); } else if (BNE_IS_DOMAIN( pCurElm)) { uiCurElm += BNE_DOMAIN_LEN; } uiCurElm += uiElmOvhd + uiElmKeyLen; } // Hit the target current element if (uiFlags == FSBBPKC_AT_CURELM) { // Copy the current element into the pPkcBuf[] uiFlags = FSBBPKC_BEFORE_CURELM; goto FSBB_one_more_time; } return (uiPkc + uiMoveArea); } /**************************************************************************** Desc: Compress out (or in) the PKC bytes in the current element ****************************************************************************/ FSTATIC FLMINT FSBlkCompressPKC( BTSK * pStack, FLMBYTE * pTempPkcBuf) { FLMUINT uiTempPkcLen; FLMUINT uiPkcBufLen; FLMUINT uiCurPkc; FLMUINT uiTruePkc; FLMINT iCompressBytes = 0; FLMBYTE * pCurElm; FLMBYTE pPkcBuf[BBE_PKC_MAX]; if (pStack->uiElmOvhd == BNE_DATA_OVHD) { goto Exit; } // Build the PKC buffer from the block uiTempPkcLen = FSBlkBuildPKC( pStack, pTempPkcBuf, FSBBPKC_BEFORE_CURELM); // Position to the current element and build its own pkc buffer. // Compare and see if equals the current element's pkc value. If not // compress/decompress. pCurElm = &pStack->pBlk[pStack->uiCurElm]; uiCurPkc = (FLMUINT) (BBE_GET_PKC( pCurElm)); uiPkcBufLen = FSElmBuildPKC( pPkcBuf, pCurElm, pTempPkcBuf, pStack->uiElmOvhd); uiTruePkc = FSElmComparePKC( pTempPkcBuf, uiTempPkcLen, pPkcBuf, uiPkcBufLen); if (uiTruePkc != uiCurPkc) { FLMBYTE * pBlk = pStack->pBlk; FLMUINT uiCurElm = pStack->uiCurElm; FLMUINT uiBlkEnd = pStack->uiBlkEnd; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT keyLen = (FLMUINT) (BBE_GET_KL( pCurElm)); FLMUINT uiTemp; if (uiTruePkc > uiCurPkc) { // Need to compress out some more bytes iCompressBytes = uiTruePkc - uiCurPkc; uiTemp = uiCurElm + uiElmOvhd + iCompressBytes; shiftN( &pBlk[uiTemp], (FLMUINT) (uiBlkEnd - uiTemp), (FLMINT) (-iCompressBytes)); // Reassign the element overhead FSSetElmOvhd( pCurElm, uiElmOvhd, (FLMUINT) (uiCurPkc + iCompressBytes), (FLMUINT) (keyLen - iCompressBytes), pCurElm); uiBlkEnd -= iCompressBytes; } else { return (0xFFFF); } UW2FBA( (FLMUINT16)uiBlkEnd, &pBlk[BH_BLK_END]); pStack->uiBlkEnd = uiBlkEnd; } Exit: return (iCompressBytes); } libflaim-4.9.966/src/fdict.h0000644000175000017500000004052510510774540017124 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Typedefs for strucures needed to build pcode. // Tabs: 3 // // Copyright (c) 1991-1992,1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id$ //------------------------------------------------------------------------- #ifndef FDICT_H #define FDICT_H #include "fpackon.h" // IMPORTANT NOTE: No other include files should follow this one except // for fpackoff.h // Logical File Save Area Layout for 4.x files. #define LFH_LF_NUMBER_OFFSET 0 // Logical file number #define LFH_TYPE_OFFSET 2 // Type of logical file #define LFH_STATUS_OFFSET 3 // Contains status bits #define LFH_ROOT_BLK_OFFSET 4 // B-TREE root block address //#define LFH_FUTURE1 8 // Not necessarily zeroes - Code bases // 31 and 40 put stuff here. #define LFH_NEXT_DRN_OFFSET 12 // Next DRN for containers #define LFH_MAX_FILL_OFFSET 16 // Max fill % after rightmost split. #define LFH_MIN_FILL_OFFSET 17 // Min fill % in blk after normal delete //#define LFH_FUTURE2 18 // Filled with zeros #define LFH_SIZE 32 // Maximum size of LFH. #define FFILE_MIN_FILL 35 #define FFILE_MAX_FILL 91 struct TDICT; struct LFILE; RCODE fdictRebuild( FDB * pDb); RCODE fdictBuildTables( TDICT * pTDict, FLMBOOL bRereadLFiles, FLMBOOL bNewDict); RCODE fdictInitTDict( FDB * pDb, TDICT * pTDict); RCODE fdictCopySkeletonDict( FDB * pDb); RCODE fdictCloneDict( FDB * pDb); RCODE fdictFixupLFileTbl( FDICT * pDict); RCODE fdictProcessAllDictRecs( FDB * pDb, TDICT * pTDict); RCODE fdictProcessRec( TDICT * pTDict, FlmRecord * pRecord, FLMUINT uiDictRecNum); RCODE DDGetFieldType( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo); RCODE DDGetEncType( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo); RCODE fdictCreateNewDict( FDB * pDb); RCODE fdictCreate( FDB * pDb, const char * pszDictPath, const char * pDictBuf); RCODE flmAddRecordToDict( FDB * pDb, FlmRecord * pRecord, FLMUINT uiDictId, FLMBOOL bRereadLFiles); /**************************************************************************** Desc: Structure for type, DRN and name for data dictionary entries ****************************************************************************/ typedef struct DDENTRY { DDENTRY * pNextEntry; void * vpDef; FLMUINT uiEntryNum; FLMUINT uiType; } DDENTRY; /**************************************************************************** Desc: Temporary field info used during a database create or dictionary modification. This field is pointed to by the DDEntry structure. ****************************************************************************/ typedef struct TFIELD { FLMUINT uiFldNum; FLMUINT uiFldInfo; } TFIELD; /**************************************************************************** Desc: Temporary encryption definition info used during a database create or dictionary modification. This field is pointed to by the DDEntry structure. ****************************************************************************/ typedef struct TENCDEF { FLMUINT uiRecNum; FLMUINT uiState; FLMUINT uiAlgType; FLMBYTE * pucKeyInfo; FLMUINT uiLength; } TENCDEF; /**************************************************************************** Desc: Used as temporary storage for index definitions during a database create or dictionary modification. This field is pointed to by the DDEntry structure. ****************************************************************************/ typedef struct TIFP { TIFP * pNextTIfp; // Linked list of IFPs FLMBOOL bFieldInThisDict; // Was field reference found in the // dictionary we are updating? FLMUINT uiFldNum; // Fixedup field ID value } TIFP; /**************************************************************************** Desc: Used as temporary storage for index definitions during a database create or dictionary modification. This field is pointed to by the DDEntry structure. ****************************************************************************/ typedef struct TIFD { TIFP * pTIfp; // Linked list of temporary field paths TIFD * pNextTIfd; // Linked List FLMUINT uiFlags; // Field type & processing flags FLMUINT uiNextFixupPos; // Next fixup position FLMUINT uiLimit; // Zero or limit of characters/bytes FLMUINT uiCompoundPos; // Position of this field is in // the compound key. Zero based number. } TIFD; /**************************************************************************** Desc: Used as temporary storage for index definitions during a database create or dictionary modification. This field is pointed to by the DDEntry structure. ****************************************************************************/ typedef struct TIXD { TIFD * pNextTIfd; // Linked list of TIFDs FLMUINT uiFlags; // Index attributes FLMUINT uiContainerNum; // Container number of data records FLMUINT uiNumFlds; // Number of field definitions FLMUINT uiLanguage; // Index language FLMUINT uiEncId; // Encryption Definition } TIXD; /**************************************************************************** Desc: Contains the dictionary entries through parsing all of the dictionary records. Used for expanding record definitions, checking index definitions, building fixup position values and last of all BUILDING THE PCODE. ****************************************************************************/ typedef struct TDICT { FDB * pDb; F_Pool pool; // Pool for the DDENTRY allocations. LFILE * pLFile; // Dictionary container LFile FDICT * pDict; // Pointer to new dictionary. FLMBOOL bWriteToDisk; // Flag indicating if PCODE should be // written to disk after being generated. // Variables for building dictionaries FLMUINT uiCurPcodeAddr; // Current pcode block we are adding to FLMUINT uiBlockSize; // PCODE Block size // Used in building the temporary structures FLMUINT uiVersionNum; // Version number of database. DDENTRY * pFirstEntry; DDENTRY * pLastEntry; FLMUINT uiNewIxds; FLMUINT uiNewIfds; FLMUINT uiNewFldPaths; FLMUINT uiNewLFiles; FLMUINT uiTotalItts; FLMUINT uiTotalIxds; FLMUINT uiTotalIfds; FLMUINT uiTotalFldPaths; FLMUINT uiTotalLFiles; FLMUINT uiBadField; // Set to field number on most errors. FLMUINT uiBadReference; // Same FLMUINT uiDefaultLanguage;// Default language to set in each index. } TDICT; /**************************************************************************** Desc: A Item Type consists of a byte that describes the type of item like a field, index or container. For fields a ITT will also indicate the fields delete status. ****************************************************************************/ typedef struct ITT { FLMUINT uiType; void * pvItem; // Points to LFILE if index or container // If field, is NULL or points to first IFD. } ITT; // Bit values for uiType. The 4 low bits contain the field type. // See FLM_XXXX_TYPE in FLAIM.H for lower four bits. #define ITT_FLD_GET_TYPE( pItt) (((pItt)->uiType) & 0x0F) #define ITT_FLD_IS_INDEXED( pItt) (((pItt)->pvItem) ? TRUE : FALSE) #define ITT_FLD_GET_STATE( pItt) (((pItt)->uiType) & 0x30) #define ITT_FLD_STATE_MASK 0x30 #define ITT_FLD_STATE_ACTIVE 0x00 // Normal active field #define ITT_FLD_STATE_CHECKING 0x10 // Field has been marked to be checked #define ITT_FLD_STATE_UNUSED 0x30 // Field is not used. #define ITT_FLD_STATE_PURGE 0x20 // Purge this field from the database. // And delete the dictionary definition #define ITT_ENC_STATE_MASK 0x30 #define ITT_ENC_STATE_ACTIVE 0x00 // Normal active field #define ITT_ENC_STATE_CHECKING 0x10 // EncDef has been marked to be checked #define ITT_ENC_STATE_UNUSED 0x30 // EncDef is not used. #define ITT_ENC_STATE_PURGE 0x20 // EncDef record is being deleted. Decrypt the // encrypted field as it can no longer be // encrypted. #define ITT_ENCDEF_TYPE 0xAF // Encrypted Definition Record #define ITT_INDEX_TYPE 0xBF #define ITT_CONTAINER_TYPE 0xCF #define ITT_EMPTY_SLOT 0xEF #define ITT_INFO_MASK 0x0F #define ITT_IS_FIELD(pItt) (((pItt)->uiType & ITT_INFO_MASK) != ITT_INFO_MASK) #define ITT_IS_CONTAINER(pItt) ((pItt)->uiType == ITT_CONTAINER_TYPE) #define ITT_IS_INDEX(pItt) ((pItt)->uiType == ITT_INDEX_TYPE) #define ITT_IS_ENCDEF(pItt) ((pItt)->uiType == ITT_ENCDEF_TYPE) /**************************************************************************** Desc: This structure holds the information for an index definition. There may be multiple IXDs for the same index number. ****************************************************************************/ typedef struct IXD { FLMUINT uiIndexNum; // Index number. FLMUINT uiContainerNum; // Container number being indexed. IFD * pFirstIfd; // Points to first IFD FLMUINT uiNumFlds; // Number of index fields in the IFD. FLMUINT uiFlags; #define IXD_UNIQUE 0x00001 // Unique index #define IXD_COUNT 0x00002 // Count keys and references #define IXD_EACHWORD 0x00100 // FUTURE: FLAIMs fulltext indexing. #define IXD_HAS_POST 0x01000 // Has post keys parts. #define IXD_HAS_SUBSTRING 0x02000 #define IXD_POSITIONING 0x04000 // The index has positioning counts. #define IXD_OFFLINE 0x08000 #define IXD_SUSPENDED 0x10000 FLMUINT uiLanguage; // WP.LRS language number (not code!) #define US_LANG 0 #define DEFAULT_LANG US_LANG #define TRANS_ID_OFFLINE TRANS_ID_HIGH_VALUE #define TRANS_ID_ALWAYS_ONLINE TRANS_ID_LOW_VALUE FLMUINT uiLastContainerIndexed; // Last container indexed if index // covers multiple containers. FLMUINT uiLastDrnIndexed; // If value is not DRN_LAST_MARKER then // update index with keys from a record // update if drn of record is <= of // this value. FLMUINT uiEncId; // The ID / Drn of the Encryption record (if used) } IXD; /**************************************************************************** Desc: This structure contains an index field definition. ****************************************************************************/ typedef struct IFD { FLMUINT uiFldNum; // Field being indexed. FLMUINT uiIndexNum; // Index number. IXD * pIxd; // IXD corresponding to wIndexNum FLMUINT uiFlags; // The first 4 bits contain field type // Use FLM_XXXXX_TYPE definitions. IFD * pNextInChain; // Next IFD in the chain that has this // field number and is used in another index. FLMUINT * pFieldPathCToP; // Child to parent field path (zero term) FLMUINT * pFieldPathPToC; // Parent to child field path (zero term) FLMUINT uiLimit; // Zero or # of characters/bytes to limit. #define IFD_DEFAULT_LIMIT 256 #define IFD_DEFAULT_SUBSTRING_LIMIT 48 FLMUINT uiCompoundPos; // Position of this field is in // the compound key. Zero based number. } IFD; #define IFD_GET_FIELD_TYPE(pIfd) ((pIfd)->uiFlags & 0x0F) #define IFD_SET_FIELD_TYPE(pIfd,type) ((pIfd)->uiFlags = ((pIfd)->uiFlags & 0xFFFFFFF0) | (type)) #define IFD_FIELD 0x00000010 // There must always be some value #define IFD_VALUE 0x00000010 // Value agrees with parsing syntax #define IFD_EACHWORD 0x00000020 // Index each and every word in the field #define IFD_CONTEXT 0x00000040 // Index the tag and NOT the value #define IFD_COMPOUND 0x00000080 // Index multiple fields #define IFD_POST 0x00000100 // Place case info at end of compound key #define IFD_UPPER 0x00000200 // Uppercase keys only #define IFD_OPTIONAL 0x00000400 // This field is optional (compound) // Phasing this value out. // Note: the unique flag is for future compatiblity. #define IFD_UNIQUE_PIECE 0x00000800 // Better name #define IFD_REQUIRED_PIECE 0x00001000 // Required piece (not optional) #define IFD_REQUIRED_IN_SET 0x0002000 // Required within a set of fields. #define IFD_LAST 0x00008000 // Last IFD for this index definition #define IFD_SUBSTRING 0x00040000 // Index all substrings pieces #define IFD_DRN 0x00080000 // index DRN value #define IFD_FIELDID_PAIR 0x00200000 // Data | fieldID pair. #define IFD_MIN_SPACES 0x00400000 // Removed leading/trailing spaces. // Combine multiple spaces into 1 space. // Minimize spaces #define IFD_NO_SPACE 0x00800000 // Remove all spaces #define IFD_NO_DASH 0x01000000 // Remove all dashes #define IFD_NO_UNDERSCORE 0x02000000 // Change underscores to spaces, // Must be applied before nospace/minspace #define IFD_ESC_CHAR 0x04000000 // Placehold so that a query can parse the input // string and find a literal '*' or '\\'. #define IFD_IS_POST_TEXT(pIfd) (((pIfd)->uiFlags & IFD_POST) && \ (IFD_GET_FIELD_TYPE(pIfd) == FLM_TEXT_TYPE)) #define IFD_DEFAULT_LIMIT 256 #define IFD_DEFAULT_SUBSTRING_LIMIT 48 /************************************************************************** Desc: This structure is a header for a FLAIM dictionary. All of the information in this structure is static. **************************************************************************/ typedef struct FDICT { FDICT * pNext; // Pointer to next FDICT structure in the list, // if any. All versions of a dictionary that // are currently in use are linked together. // Usually, there will be only one local // dictionary in the list. FDICT * pPrev; // Previous FDICT structure in the list. FFILE * pFile; // File this dictionary is associated with. // A null value means it is not yet linked // to a file. FLMUINT uiDictSeq; // This is the sequence number of the dictionary // Local Dictionary Tables. LFILE * pLFileTbl; // Logical file (index or container) FLMUINT uiLFileCnt; #define LFILE_DATA_CONTAINER_OFFSET 0 #define LFILE_DICT_CONTAINER_OFFSET 1 #define LFILE_DICT_INDEX_OFFSET 2 #define LFILE_TRACKER_CONTAINER_OFFSET 3 ITT * pIttTbl; FLMUINT uiIttCnt; IXD * pIxdTbl; FLMUINT uiIxdCnt; IFD * pIfdTbl; FLMUINT uiIfdCnt; FLMUINT * pFldPathsTbl; FLMUINT uiFldPathsCnt; FLMUINT uiUseCount; // Number of FDB structures currently // pointing to this dictionary. } FDICT; /**************************************************************************** Desc: This is a temporary structure that is used when building compound keys. ****************************************************************************/ typedef struct CDL { void * pField; // Field to be included in a compound key void * pRootContext; // Points to root context of field path CDL * pNext; // Pointer to the next CDL entry. } CDL; /**************************************************************************** Desc: This keeps track of the logical file information for an index or a container. ****************************************************************************/ typedef struct LFILE { FLMUINT uiRootBlk; // Address of root block. FLMUINT uiNextDrn; // Next DRN - only use when root is null FLMUINT uiBlkAddress; // Block address of LFile entry. FLMUINT uiOffsetInBlk; // Offset within block of entry. FLMUINT uiLfNum; // Index number or container number. FLMUINT uiLfType; // Type of logical file. */ FLMBOOL bMakeFieldIdTable; // Boolean that indicates whether or not // for this container when we create // records in cache we should create a // field id table for the level-1 fields. IXD * pIxd; // If an index, points to the IXD. } LFILE; #include "fpackoff.h" #endif libflaim-4.9.966/src/fmisc.cpp0000644000175000017500000001151110510774540017460 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Miscellaneous functions. // Tabs: 3 // // Copyright (c) 1995-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fmisc.cpp 12266 2006-01-19 14:45:33 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Returns TRUE if the passed in RCODE indicates that a corruption has occured in a FLAIM database file. ****************************************************************************/ FLMEXP FLMBOOL FLMAPI FlmErrorIsFileCorrupt( RCODE rc) { FLMBOOL b = FALSE; switch( rc) { case FERR_BTREE_ERROR : case FERR_DATA_ERROR : case FERR_DD_ERROR : case FERR_NOT_FLAIM : case FERR_PCODE_ERROR : case FERR_BLOCK_CHECKSUM : case FERR_INCOMPLETE_LOG : case FERR_KEY_NOT_FOUND : case FERR_NO_REC_FOR_KEY: b = TRUE; break; default : break; } return( b); } /**************************************************************************** Desc: Returns specific information about the most recent error that occured within FLAIM. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmGetDiagInfo( HFDB hDb, eDiagInfoType eDiagCode, void * pvDiagInfo) { RCODE rc = FERR_OK; FDB * pDb; if ((pDb = (FDB *)hDb) == NULL) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } fdbUseCheck( pDb); /* Now, copy over the data into the users variable */ switch( eDiagCode) { case FLM_GET_DIAG_INDEX_NUM : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_INDEX_NUM)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiIndexNum; } break; case FLM_GET_DIAG_DRN : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_DRN)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiDrn; } break; case FLM_GET_DIAG_FIELD_NUM : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_FIELD_NUM)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiFieldNum; } break; case FLM_GET_DIAG_FIELD_TYPE : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_FIELD_TYPE)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiFieldType; } break; case FLM_GET_DIAG_ENC_ID : if (!(pDb->Diag.uiInfoFlags & FLM_DIAG_ENC_ID)) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } else { *((FLMUINT *)pvDiagInfo) = pDb->Diag.uiEncId; } break; default: flmAssert( 0); rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: if( pDb) { fdbUnuse( pDb); } return( rc); } /**************************************************************************** Desc: Get the total bytes represented by a particular block address. ****************************************************************************/ FLMUINT64 FSGetSizeInBytes( FLMUINT uiMaxFileSize, FLMUINT uiBlkAddress) { FLMUINT uiFileNum; FLMUINT uiFileOffset; FLMUINT64 ui64Size; uiFileNum = FSGetFileNumber( uiBlkAddress); uiFileOffset = FSGetFileOffset( uiBlkAddress); if( uiFileNum > 1) { ui64Size = (FLMUINT64)(((FLMUINT64)uiFileNum - (FLMUINT64)1) * (FLMUINT64)uiMaxFileSize + (FLMUINT64)uiFileOffset); } else { ui64Size = (FLMUINT64)uiFileOffset; } return( ui64Size); } /**************************************************************************** Desc: Converts a UNICODE string consisting of 7-bit ASCII characters to a 7-bit ASCII string. The conversion is done in place, so that only one buffer is needed *****************************************************************************/ RCODE flmUnicodeToAscii( FLMUNICODE * puzString) // Unicode in, Ascii out { FLMBYTE * pucDest; pucDest = (FLMBYTE *)puzString; while( *puzString) { if( *puzString > 0x007F) { *pucDest = 0xFF; } else { *pucDest = (FLMBYTE)*puzString; } pucDest++; puzString++; } *pucDest = '\0'; return( FERR_OK); } libflaim-4.9.966/src/fdbcnfig.cpp0000644000175000017500000012317510510774540020133 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for database configuration. // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbcnfig.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmDbGetSizes( FDB * pDb, FLMUINT64 * pui64DbFileSize, FLMUINT64 * pui64RollbackFileSize, FLMUINT64 * pui64RflFileSize); void flmGetCPInfo( void * pFilePtr, CHECKPOINT_INFO * pCheckpointInfo); /******************************************************************************* Desc: Sets indexing callback function *******************************************************************************/ FLMEXP void FLMAPI FlmSetIndexingCallback( HFDB hDb, IX_CALLBACK fnIxCallback, void * pvAppData) { ((FDB *)hDb)->fnIxCallback = fnIxCallback; ((FDB *)hDb)->IxCallbackData = pvAppData; } /******************************************************************************* Desc: Returns indexing callback function *******************************************************************************/ FLMEXP void FLMAPI FlmGetIndexingCallback( HFDB hDb, IX_CALLBACK * pfnIxCallback, void ** ppvAppData) { if (pfnIxCallback) { *pfnIxCallback = ((FDB *)hDb)->fnIxCallback; } if (ppvAppData) { *ppvAppData = ((FDB *)hDb)->IxCallbackData; } } /******************************************************************************* Desc : Configures a callback function which allows validation of records before they are returned to the user or committed to the database. Notes: This function stores a pointer to a callback function which is called whenever a record is added, deleted, modified or retrieved. This allows an application to validate record operations before they are committed to the database (update operations) or before records are returned to the application (read operations). By default, no record validation is performed by FLAIM. *******************************************************************************/ FLMEXP void FLMAPI FlmSetRecValidatorHook( HFDB hDb, REC_VALIDATOR_HOOK fnRecValidatorHook, void * pvAppData) { ((FDB *)hDb)->fnRecValidator = fnRecValidatorHook; ((FDB *)hDb)->RecValData = pvAppData; } /******************************************************************************* Desc : Returns to the user the sessions current Rec Validator Hook values. *******************************************************************************/ FLMEXP void FLMAPI FlmGetRecValidatorHook( HFDB hDb, REC_VALIDATOR_HOOK * pfnRecValidatorHook, void ** ppvAppData) { if (pfnRecValidatorHook) { *pfnRecValidatorHook = ((FDB *)hDb)->fnRecValidator; } if (ppvAppData) { *ppvAppData = ((FDB *)hDb)->RecValData; } } /******************************************************************************* Desc : Configures a callback function which is called to return general purpose information. *******************************************************************************/ FLMEXP void FLMAPI FlmSetStatusHook( HFDB hDb, STATUS_HOOK fnStatusHook, void * pvAppData) { ((FDB *)hDb)->fnStatus = fnStatusHook; ((FDB *)hDb)->StatusData = pvAppData; } /******************************************************************************* Desc : Returns to the user the session's current status hook values. *******************************************************************************/ FLMEXP void FLMAPI FlmGetStatusHook( HFDB hDb, STATUS_HOOK * pfnStatusHook, void ** ppvAppData) { if (pfnStatusHook) { *pfnStatusHook = ((FDB *)hDb)->fnStatus; } if (ppvAppData) { *ppvAppData = ((FDB *)hDb)->StatusData; } } /******************************************************************************* Desc: Allows an application to configure various options for a database. *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbConfig( HFDB hDb, eDbConfigType eConfigType, void * pvValue1, void * pvValue2) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FFILE * pFile = pDb->pFile; LFILE * pLFile; FLMBOOL bDbInitialized = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bDbLocked = FALSE; // Process the client/server request if( IsInCSMode( hDb)) { fdbInitCS( pDb); bDbInitialized = TRUE; CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_CONFIG))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_TYPE, (FLMUINT)eConfigType))) { goto Transmission_Error; } switch( eConfigType) { case FDB_SET_APP_VERSION: case FDB_RFL_KEEP_FILES: case FDB_KEEP_ABORTED_TRANS_IN_RFL: case FDB_AUTO_TURN_OFF_KEEP_RFL: case FDB_SET_APP_DATA: { if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER2, (FLMUINT)pvValue1))) { goto Transmission_Error; } break; } case FDB_RFL_DIR: { FLMUNICODE * puzRflDir; if( RC_BAD( rc = fcsConvertNativeToUnicode( Wire.getPool(), (const char *)pvValue1, &puzRflDir))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendString( WIRE_VALUE_FILE_PATH, puzRflDir))) { goto Transmission_Error; } break; } case FDB_RFL_FILE_LIMITS: { if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, (FLMUINT)pvValue1))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER2, (FLMUINT)pvValue2))) { goto Transmission_Error; } break; } case FDB_FILE_EXTEND_SIZE: case FDB_RFL_FOOTPRINT_SIZE: case FDB_RBL_FOOTPRINT_SIZE: { if( RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, (FLMUINT)pvValue1))) { goto Transmission_Error; } break; } case FDB_RFL_ROLL_TO_NEXT_FILE: { break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } // See if the database is being forced to close if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } // Process the local (non-C/S) request switch( eConfigType) { case FDB_RFL_KEEP_FILES: { FLMBOOL bKeepFiles = (FLMBOOL)(pvValue1 ? TRUE : FALSE); // This operation is not legal for pre 4.3 databases. if (pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Make sure we don't have a transaction going if( pDb->uiTransType != FLM_NO_TRANS) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } // Make sure there is no active backup running f_mutexLock( gv_FlmSysData.hShareMutex); if( pFile->bBackupActive) { f_mutexUnlock( gv_FlmSysData.hShareMutex); rc = RC_SET( FERR_BACKUP_ACTIVE); goto Exit; } f_mutexUnlock( gv_FlmSysData.hShareMutex); // Need to lock the database but not start a transaction yet. if( !(pDb->uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) { if( RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; } // If we aren't changing the keep flag, jump to exit without doing // anything. if ((bKeepFiles && pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]) || (!bKeepFiles && !pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES])) { goto Exit; // Will return FERR_OK; } // Force a checkpoint and roll to the next RFL file numbers. // When changing from keep to no-keep or vice versa, we need to // go to a new RFL file so that the new RFL file gets new // serial numbers and a new keep or no-keep flag. if (RC_BAD( rc = FlmDbCheckpoint( hDb, FLM_NO_TIMEOUT))) { goto Exit; } f_memcpy( pFile->ucUncommittedLogHdr, pFile->ucLastCommittedLogHdr, LOG_HEADER_SIZE); pFile->ucUncommittedLogHdr [LOG_KEEP_RFL_FILES] = (FLMBYTE)((bKeepFiles) ? (FLMBYTE)1 : (FLMBYTE)0); // Force a new RFL file - this will also write out the entire // log header - including the changes we made above. if (RC_BAD( rc = pFile->pRfl->finishCurrFile( pDb, TRUE))) { goto Exit; } // Update the RFL size if( bKeepFiles) { FLMUINT64 ui64RflDiskUsage; if( RC_BAD( rc = flmRflCalcDiskUsage( pFile->pRfl->getRflDirPtr(), pFile->pRfl->getDbPrefixPtr(), pFile->FileHdr.uiVersionNum, &ui64RflDiskUsage))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); pFile->ui64RflDiskUsage = ui64RflDiskUsage; f_mutexUnlock( gv_FlmSysData.hShareMutex); } break; } case FDB_RFL_DIR: { const char * pszNewRflDir = (const char *)pvValue1; // This operation is not legal for pre 4.3 databases. if (pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Make sure we don't have a transaction going if( pDb->uiTransType != FLM_NO_TRANS) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } // Make sure there is no active backup running f_mutexLock( gv_FlmSysData.hShareMutex); if( pFile->bBackupActive) { f_mutexUnlock( gv_FlmSysData.hShareMutex); rc = RC_SET( FERR_BACKUP_ACTIVE); goto Exit; } f_mutexUnlock( gv_FlmSysData.hShareMutex); // Make sure the path exists and that it is a directory // rather than a file. if (pszNewRflDir && *pszNewRflDir) { if( !gv_FlmSysData.pFileSystem->isDir( pszNewRflDir)) { rc = RC_SET( FERR_IO_INVALID_PATH); goto Exit; } } // Need to lock the database because we can't change the RFL // directory until after the checkpoint has completed. The // checkpoint code will unlock the transaction, but not the // file if we have an explicit lock. We need to do this to // prevent another transaction from beginning before we have // changed the RFL directory. if( !(pDb->uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) { if( RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; } // Force a checkpoint and roll to the next RFL file numbers. Both // of these steps are necessary to ensure that we won't have to do // any recovery using the current RFL file - because we do not // move the current RFL file to the new directory. Forcing the // checkpoint ensures that we have no transactions that will need // to be recovered if we were to crash. Rolling the RFL file number // ensures that no more transactions will be logged to the current // RFL file. if (RC_BAD( rc = FlmDbCheckpoint( hDb, FLM_NO_TIMEOUT))) { goto Exit; } // Force a new RFL file. if (RC_BAD( rc = pFile->pRfl->finishCurrFile( pDb, FALSE))) { goto Exit; } // Set the RFL directory to the new value now that we have // finished the checkpoint and rolled to the next RFL file. f_mutexLock( gv_FlmSysData.hShareMutex); rc = pFile->pRfl->setRflDir( pszNewRflDir); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FDB_RFL_FILE_LIMITS: { FLMUINT uiMinRflSize = (FLMUINT)pvValue1; FLMUINT uiMaxRflSize = (FLMUINT)pvValue2; // Make sure the limits are valid. if (pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) { // Maximum must be enough to hold at least one packet plus // the RFL header. Minimum must not be greater than the // maximum. NOTE: Minimum and maximum are allowed to be // equal, but in all cases, maximum takes precedence over // minimum. We will first NOT exceed the maximum. Then, // if possible, we will go above the minimum. if (uiMaxRflSize < RFL_MAX_PACKET_SIZE + 512) { uiMaxRflSize = RFL_MAX_PACKET_SIZE + 512; } if (uiMaxRflSize > gv_FlmSysData.uiMaxFileSize) { uiMaxRflSize = gv_FlmSysData.uiMaxFileSize; } if (uiMinRflSize > uiMaxRflSize) { uiMinRflSize = uiMaxRflSize; } } // Start an update transaction. Must not already be one going. bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } // Commit the transaction. UD2FBA( (FLMUINT32)uiMinRflSize, &pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); if (pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) { UD2FBA( (FLMUINT32)uiMaxRflSize, &pFile->ucUncommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); } if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { goto Exit; } bStartedTrans = FALSE; break; } case FDB_RFL_ROLL_TO_NEXT_FILE: { // This operation is not legal for pre 4.3 databases. if (pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // NOTE: finishCurrFile will not roll to the next file if the current // file has not been created. if (RC_BAD( rc = pFile->pRfl->finishCurrFile( pDb, FALSE))) { goto Exit; } break; } case FDB_SET_APP_VERSION: { FLMUINT uiOldMajorVer; FLMUINT uiOldMinorVer; // Start an update transaction. bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans))) { goto Exit; } // Set the version. f_mutexLock( gv_FlmSysData.hShareMutex); uiOldMajorVer = pFile->FileHdr.uiAppMajorVer; pFile->FileHdr.uiAppMajorVer = (FLMUINT)pvValue1; uiOldMinorVer = pFile->FileHdr.uiAppMinorVer; pFile->FileHdr.uiAppMinorVer = (FLMUINT)pvValue2; f_mutexUnlock( gv_FlmSysData.hShareMutex); // Commit the transaction. NOTE: This will always cause // us to write out the application version numbers, because // we always write out the prefix - first 512 bytes. if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { // Undo the changes made above f_mutexLock( gv_FlmSysData.hShareMutex); pFile->FileHdr.uiAppMajorVer = uiOldMajorVer; pFile->FileHdr.uiAppMinorVer = uiOldMinorVer; f_mutexUnlock( gv_FlmSysData.hShareMutex); goto Exit; } bStartedTrans = FALSE; break; } case FDB_KEEP_ABORTED_TRANS_IN_RFL: case FDB_AUTO_TURN_OFF_KEEP_RFL: { // These operations are not legal for pre 4.3 databases. if (pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Start an update transaction. Must not already be one going. bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } // Change the uncommitted log header if (eConfigType == FDB_KEEP_ABORTED_TRANS_IN_RFL) { pFile->ucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL] = (FLMBYTE)(pvValue1 ? (FLMBYTE)1 : (FLMBYTE)0); } else { pFile->ucUncommittedLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL] = (FLMBYTE)(pvValue1 ? (FLMBYTE)1 : (FLMBYTE)0); } // Commit the transaction. if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { goto Exit; } bStartedTrans = FALSE; break; } case FDB_FILE_EXTEND_SIZE: { pFile->uiFileExtendSize = (FLMUINT)pvValue1; break; } case FDB_RFL_FOOTPRINT_SIZE: { pFile->uiRflFootprintSize = (FLMUINT)f_roundUp( (FLMUINT)pvValue1, 512); break; } case FDB_RBL_FOOTPRINT_SIZE: { pFile->uiRblFootprintSize = (FLMUINT)f_roundUp( (FLMUINT)pvValue1, pFile->FileHdr.uiBlockSize); break; } case FDB_SET_APP_DATA: { pDb->pvAppData = pvValue1; break; } case FDB_SET_COMMIT_CALLBACK: { pDb->fnCommit = (COMMIT_FUNC)((FLMUINT)pvValue1); pDb->pvCommitData = pvValue2; break; } case FDB_SET_RFL_SIZE_THRESHOLD: { if( RC_BAD( rc = flmSetRflSizeThreshold( hDb, (FLMUINT)pvValue1, FLM_MAX_UINT, FLM_MAX_UINT))) { goto Exit; } break; } case FDB_SET_RFL_SIZE_EVENT_INTERVALS: { FLMUINT uiTimeInterval = (FLMUINT)pvValue1; FLMUINT uiSizeInterval = (FLMUINT)pvValue2; if( RC_BAD( rc = flmSetRflSizeThreshold( hDb, FLM_MAX_UINT, uiTimeInterval, uiSizeInterval))) { goto Exit; } break; } case FDB_ENABLE_FIELD_ID_TABLE: { if (pDb->pDict) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, (FLMUINT)pvValue1, &pLFile))) { goto Exit; } pLFile->bMakeFieldIdTable = (FLMBOOL)((FLMUINT)pvValue2); } else if (pDb->pFile->pDictList) { if (RC_BAD( rc = fdictGetContainer( pDb->pFile->pDictList, (FLMUINT)pvValue1, &pLFile))) { goto Exit; } pLFile->bMakeFieldIdTable = (FLMBOOL)((FLMUINT)pvValue2); } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } Exit: if( bStartedTrans) { flmAbortDbTrans( pDb); } if( bDbLocked) { FlmDbUnlock( hDb); } if( bDbInitialized) { fdbExit( pDb); } return( rc); } /**************************************************************************** Desc: Returns database, rollback, and rollforward sizes. We are guaranteed to be inside an update transaction at this point. ****************************************************************************/ FSTATIC RCODE flmDbGetSizes( FDB * pDb, FLMUINT64 * pui64DbFileSize, FLMUINT64 * pui64RollbackFileSize, FLMUINT64 * pui64RflFileSize) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiDbVersion = pFile->FileHdr.uiVersionNum; FLMUINT uiEndAddress; FLMUINT uiLastFileNumber; FLMUINT64 ui64LastFileSize; char szTmpName[ F_PATH_MAX_SIZE]; char szRflDir[ F_PATH_MAX_SIZE]; char szPrefix[ F_FILENAME_SIZE]; IF_FileHdl * pFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; // Better be inside an update transaction at this point. flmAssert( pDb->uiTransType == FLM_UPDATE_TRANS); // See if they want the database files sizes. if (pui64DbFileSize) { uiEndAddress = pDb->LogHdr.uiLogicalEOF; uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( uiLastFileNumber >= 1 && uiLastFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)); // Get the actual size of the last file. if (RC_BAD( rc = pDb->pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { if (uiLastFileNumber > 1) { rc = FERR_OK; ui64LastFileSize = 0; } else { // Should always be a data file #1 flmAssert( 0); goto Exit; } } else { goto Exit; } } // One of two situations exists with respect to the last // file: 1) it has not been fully written out yet (blocks // are still cached, or 2) it has been written out and // extended beyond what the logical EOF shows. We want // the larger of these two possibilities. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } if (uiLastFileNumber == 1) { // Only one file - use last file's size. *pui64DbFileSize = ui64LastFileSize; } else { // Size is the sum of full size for all files except the last one, // plus the calculated (or actual) size of the last one. (*pui64DbFileSize) = (FLMUINT64)(uiLastFileNumber - 1) * (FLMUINT64)pFile->uiMaxFileSize + ui64LastFileSize; } } // See if they want the rollback files sizes. if (pui64RollbackFileSize) { uiEndAddress = (FLMUINT)FB2UD( &pFile->ucUncommittedLogHdr [LOG_ROLLBACK_EOF]); uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( !uiLastFileNumber || (uiLastFileNumber >= FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion) && uiLastFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion))); // Get the size of the last file number. if (RC_BAD( rc = pDb->pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { if (uiLastFileNumber) { rc = FERR_OK; ui64LastFileSize = 0; } else { // Should always have rollback file #0 flmAssert( 0); goto Exit; } } else { goto Exit; } } // If the EOF offset for the last file is greater than the // actual file size, use it instead of the actual file size. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } // Special case handling here because rollback file numbers start with // zero and then skip to a file number that is one beyond the // highest data file number - so the calculation for file size needs // to account for this. if (!uiLastFileNumber) { *pui64RollbackFileSize = ui64LastFileSize; } else { FLMUINT uiFirstLogFileNum = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); // Add full size of file zero plus a full size for every file // except the last one. (*pui64RollbackFileSize) = (FLMUINT64)(uiLastFileNumber - uiFirstLogFileNum + 1) * (FLMUINT64)pFile->uiMaxFileSize + ui64LastFileSize; } } // See if they want the roll-forward log file sizes. if (pui64RflFileSize) { char * pszDbFileName = pFile->pszDbPath; *pui64RflFileSize = 0; if (uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to get the size for one // RFL file. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDbFileName, NULL, 1, szTmpName))) { goto Exit; } // Open the file and get its size. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( szTmpName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; ui64LastFileSize = 0; } else { goto Exit; } } else { if (RC_BAD( rc = pFileHdl->size( &ui64LastFileSize))) { goto Exit; } } if (pFileHdl) { pFileHdl->Release(); pFileHdl = NULL; } *pui64RflFileSize = ui64LastFileSize; } else { // For 4.3 and greater, need to scan the RFL directory for // RFL files. The call below to rflGetDirAndPrefix is done // to get the prefix. It will not return the correct // RFL directory name, because we are passing in a NULL // RFL directory path (which may or may not be correct). // That's OK, because we get the RFL directory directly // from the F_Rfl object anyway. if (RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDbFileName, NULL, szRflDir, szPrefix))) { goto Exit; } // We need to get the RFL directory from the F_Rfl object. f_strcpy( szRflDir, pFile->pRfl->getRflDirPtr()); // See if the directory exists. If not, we are done. if (gv_FlmSysData.pFileSystem->isDir( szRflDir)) { // Open the directory and scan for RFL files. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( szRflDir, "*", &pDirHdl))) { goto Exit; } for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } pDirHdl->currentItemPath( szTmpName); // If the item looks like an RFL file name, get // its size. if (!pDirHdl->currentItemIsDir() && rflGetFileNum( uiDbVersion, szPrefix, szTmpName, &uiLastFileNumber)) { // Open the file and get its size. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( szTmpName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; ui64LastFileSize = 0; } else { goto Exit; } } else { if( RC_BAD( rc = pFileHdl->size( &ui64LastFileSize))) { goto Exit; } } if (pFileHdl) { pFileHdl->Release(); pFileHdl = NULL; } (*pui64RflFileSize) += ui64LastFileSize; } } } } } Exit: if (pFileHdl) { pFileHdl->Release(); } if (pDirHdl) { pDirHdl->Release(); } return( rc); } /******************************************************************************* Desc: Returns information about a particular database. *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbGetConfig( HFDB hDb, eDbGetConfigType eGetConfigType, void * pvValue1, void * pvValue2, void * pvValue3) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FFILE * pFile = pDb->pFile; FLMBOOL bDbInitialized = FALSE; FLMBOOL bStartedTrans = FALSE; FLMUINT uiTransType = FLM_NO_TRANS; CHECKPOINT_INFO * pCheckpointInfo; if( IsInCSMode( hDb)) { fdbInitCS( pDb); bDbInitialized = TRUE; CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); CREATE_OPTS createOpts; if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_GET_CONFIG))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_TYPE, (FLMUINT)eGetConfigType))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } switch( eGetConfigType) { case FDB_GET_VERSION: { Wire.copyCreateOpts( &createOpts); *((FLMUINT *)pvValue1) = createOpts.uiVersionNum; break; } case FDB_GET_BLKSIZ: { Wire.copyCreateOpts( &createOpts); *((FLMUINT *)pvValue1) = createOpts.uiBlockSize; break; } case FDB_GET_DEFAULT_LANG: { Wire.copyCreateOpts( &createOpts); *((FLMUINT *)pvValue1) = createOpts.uiDefaultLanguage; break; } case FDB_GET_PATH: case FDB_GET_RFL_DIR: { char * pszPath; F_Pool * pPool = Wire.getPool(); void * pvMark = pPool->poolMark(); if( RC_BAD( rc = fcsConvertUnicodeToNative( pPool, (FLMUNICODE *)Wire.getFilePath(), &pszPath))) { goto Exit; } f_strcpy( (char *)pvValue1, pszPath); pPool->poolReset( pvMark); break; } case FDB_GET_TRANS_ID: case FDB_GET_RFL_FILE_NUM: case FDB_GET_RFL_HIGHEST_NU: case FDB_GET_LAST_BACKUP_TRANS_ID: case FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: case FDB_GET_FILE_EXTEND_SIZE: case FDB_GET_RFL_FOOTPRINT_SIZE: case FDB_GET_RBL_FOOTPRINT_SIZE: case FDB_GET_APP_DATA: case FDB_GET_NEXT_INC_BACKUP_SEQ_NUM: case FDB_GET_DICT_SEQ_NUM: case FDB_GET_FFILE_ID: case FDB_GET_MUST_CLOSE_RC: { *((FLMUINT *)pvValue1) = (FLMUINT)Wire.getNumber1(); break; } case FDB_GET_RFL_FILE_SIZE_LIMITS: { if (pvValue1) { *((FLMUINT *)pvValue1) = (FLMUINT)Wire.getNumber1(); } if (pvValue2) { *((FLMUINT *)pvValue2) = (FLMUINT)Wire.getNumber2(); } break; } case FDB_GET_RFL_KEEP_FLAG: case FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG: case FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG: { *((FLMBOOL *)pvValue1) = Wire.getBoolean(); break; } case FDB_GET_CHECKPOINT_INFO: { rc = fcsExtractCheckpointInfo( Wire.getHTD(), (CHECKPOINT_INFO *)pvValue1); break; } case FDB_GET_LOCK_HOLDER: { rc = fcsExtractLockUser( Wire.getHTD(), FALSE, ((F_LOCK_USER *)pvValue1)); break; } case FDB_GET_LOCK_WAITERS: { rc = fcsExtractLockUser( Wire.getHTD(), TRUE, ((void *)pvValue1)); break; } case FDB_GET_SERIAL_NUMBER: { f_memcpy( (FLMBYTE *)pvValue1, Wire.getSerialNum(), F_SERIAL_NUM_SIZE); break; } case FDB_GET_SIZES: { if (pvValue1) { *((FLMUINT64 *)pvValue1) = (FLMUINT64)Wire.getNumber1(); } if (pvValue2) { *((FLMUINT64 *)pvValue2) = (FLMUINT64)Wire.getNumber2(); } if (pvValue3) { *((FLMUINT64 *)pvValue3) = (FLMUINT64)Wire.getNumber3(); } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (eGetConfigType == FDB_GET_RFL_FILE_NUM || eGetConfigType == FDB_GET_RFL_HIGHEST_NU || eGetConfigType == FDB_GET_RFL_FILE_SIZE_LIMITS || eGetConfigType == FDB_GET_RFL_KEEP_FLAG || eGetConfigType == FDB_GET_LAST_BACKUP_TRANS_ID || eGetConfigType == FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP || eGetConfigType == FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG || eGetConfigType == FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG || eGetConfigType == FDB_GET_SIZES || eGetConfigType == FDB_GET_NEXT_INC_BACKUP_SEQ_NUM) { uiTransType = FLM_UPDATE_TRANS; } bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, uiTransType, FDB_TRANS_GOING_OK | FDB_DONT_RESET_DIAG, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } switch( eGetConfigType) { case FDB_GET_VERSION: { *((FLMUINT *)pvValue1) = pFile->FileHdr.uiVersionNum; break; } case FDB_GET_BLKSIZ: { *((FLMUINT *)pvValue1) = pFile->FileHdr.uiBlockSize; break; } case FDB_GET_DEFAULT_LANG: { *((FLMUINT *)pvValue1) = pFile->FileHdr.uiDefaultLanguage; break; } case FDB_GET_PATH: { if( RC_BAD( rc = flmGetFilePath( pFile, ((char *)pvValue1)))) { goto Exit; } break; } case FDB_GET_TRANS_ID: { if (pDb->uiTransType != FLM_NO_TRANS) { *((FLMUINT *)pvValue1) = pDb->LogHdr.uiCurrTransID; } else if (pDb->uiFlags & FDB_HAS_FILE_LOCK) { // Get last committed value. *((FLMUINT *)pvValue1) = FB2UD( &pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID]); } else { *((FLMUINT *)pvValue1) = 0; } break; } case FDB_GET_CHECKPOINT_INFO: { pCheckpointInfo = (CHECKPOINT_INFO *)pvValue1; f_mutexLock( gv_FlmSysData.hShareMutex); flmGetCPInfo( pFile, pCheckpointInfo); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FDB_GET_LOCK_HOLDER: { F_LOCK_USER * pLockUser = (F_LOCK_USER *)pvValue1; if (pFile->pFileLockObj) { rc = pFile->pFileLockObj->getLockInfo( 0, NULL, &pLockUser->uiThreadId, &pLockUser->uiThreadId); } else { ((F_LOCK_USER *)pvValue1)->uiThreadId = 0; ((F_LOCK_USER *)pvValue1)->uiTime = 0; } break; } case FDB_GET_LOCK_WAITERS: { if (pFile->pFileLockObj) { rc = pFile->pFileLockObj->getLockQueue( (F_LOCK_USER **)pvValue1); } else { *((F_LOCK_USER **)pvValue1) = NULL; } break; } case FDB_GET_LOCK_WAITERS_EX: { IF_LockInfoClient * pLockInfo = (IF_LockInfoClient *)pvValue1; if (pFile->pFileLockObj) { rc = pFile->pFileLockObj->getLockInfo( pLockInfo); } else { pLockInfo->setLockCount( 0); } break; } case FDB_GET_RFL_DIR: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); f_strcpy( (char *)pvValue1, pDb->pFile->pRfl->getRflDirPtr()); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FDB_GET_RFL_FILE_NUM: { FLMUINT uiLastCPFile; FLMUINT uiLastTransFile; // Get the CP and last trans RFL file numbers. Need to // return the higher of the two. No need to lock the // mutex because we are in an update transaction. uiLastCPFile = FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]); uiLastTransFile = FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_RFL_FILE_NUM]); *((FLMUINT *)pvValue1) = uiLastCPFile > uiLastTransFile ? uiLastCPFile : uiLastTransFile; break; } case FDB_GET_RFL_HIGHEST_NU: { FLMUINT uiLastCPFile; FLMUINT uiLastTransFile; // Get the CP and last trans RFL file numbers. Need to // return the lower of the two minus 1. uiLastCPFile = FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]); uiLastTransFile = FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_RFL_FILE_NUM]); *((FLMUINT *)pvValue1) = (FLMUINT)((uiLastCPFile < uiLastTransFile ? uiLastCPFile : uiLastTransFile) - 1); break; } case FDB_GET_RFL_FILE_SIZE_LIMITS: { if (pvValue1) { *((FLMUINT *)pvValue1) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); } if (pvValue2) { if (pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) { *((FLMUINT *)pvValue2) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]); } else { *((FLMUINT *)pvValue2) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]); } } break; } case FDB_GET_RFL_KEEP_FLAG: { *((FLMBOOL *)pvValue1) = pDb->pFile->ucUncommittedLogHdr [LOG_KEEP_RFL_FILES] ? TRUE : FALSE; break; } case FDB_GET_LAST_BACKUP_TRANS_ID: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } *((FLMUINT *)pvValue1) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID]); break; } case FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } *((FLMUINT *)pvValue1) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_BLK_CHG_SINCE_BACKUP]); break; } case FDB_GET_SERIAL_NUMBER: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy( (FLMBYTE *)pvValue1, &pDb->pFile->ucLastCommittedLogHdr [LOG_DB_SERIAL_NUM], F_SERIAL_NUM_SIZE); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { *((FLMBOOL *)pvValue1) = FALSE; } else { *((FLMBOOL *)pvValue1) = pDb->pFile->ucUncommittedLogHdr [LOG_AUTO_TURN_OFF_KEEP_RFL] ? TRUE : FALSE; } break; } case FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { *((FLMBOOL *)pvValue1) = FALSE; } else { *((FLMBOOL *)pvValue1) = pDb->pFile->ucUncommittedLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL] ? TRUE : FALSE; } break; } case FDB_GET_SIZES: { rc = flmDbGetSizes( pDb, (FLMUINT64 *)pvValue1, (FLMUINT64 *)pvValue2, (FLMUINT64 *)pvValue3); break; } case FDB_GET_FILE_EXTEND_SIZE: { *((FLMUINT *)pvValue1) = pDb->pFile->uiFileExtendSize; break; } case FDB_GET_RFL_FOOTPRINT_SIZE: { *((FLMUINT *)pvValue1) = pDb->pFile->uiRflFootprintSize; break; } case FDB_GET_RBL_FOOTPRINT_SIZE: { *((FLMUINT *)pvValue1) = pDb->pFile->uiRblFootprintSize; break; } case FDB_GET_APP_DATA: { *((void **)pvValue1) = pDb->pvAppData; break; } case FDB_GET_NEXT_INC_BACKUP_SEQ_NUM: { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } *((FLMUINT *)pvValue1) = (FLMUINT)FB2UD( &pDb->pFile->ucUncommittedLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); break; } case FDB_GET_DICT_SEQ_NUM: { if( pDb->pDict) { *((FLMUINT *)pvValue1) = pDb->pDict->uiDictSeq; } else { *((FLMUINT *)pvValue1) = pDb->pFile->pDictList->uiDictSeq; } break; } case FDB_GET_FFILE_ID: { *((FLMUINT *)pvValue1) = pDb->pFile->uiFFileId; break; } case FDB_GET_MUST_CLOSE_RC: { *((RCODE *)pvValue1) = pDb->pFile->rcMustClose; break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } } Exit: if( bStartedTrans) { flmAbortDbTrans( pDb); } if( bDbInitialized) { fdbExit( pDb); } return( rc); } /**************************************************************************** Desc: Retrieves the Checkpoint info for the pFile passed in. This assumes the hShareMutex has already been locked. *****************************************************************************/ void flmGetCPInfo( void * pFilePtr, CHECKPOINT_INFO * pCheckpointInfo) { FFILE * pFile; FLMUINT uiElapTime; FLMUINT uiCurrTime; flmAssert( pFilePtr); flmAssert( pCheckpointInfo); pFile = (FFILE *)pFilePtr; f_memset( pCheckpointInfo, 0, sizeof( CHECKPOINT_INFO)); if (pFile->pCPInfo) { pCheckpointInfo->bRunning = pFile->pCPInfo->bDoingCheckpoint; if (pCheckpointInfo->bRunning) { if (pFile->pCPInfo->uiStartTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pFile->pCPInfo->uiStartTime); pCheckpointInfo->uiRunningTime = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->uiRunningTime = 0; } pCheckpointInfo->bForcingCheckpoint = pFile->pCPInfo->bForcingCheckpoint; if (pFile->pCPInfo->uiForceCheckpointStartTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pFile->pCPInfo->uiForceCheckpointStartTime); pCheckpointInfo->uiForceCheckpointRunningTime = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->uiForceCheckpointRunningTime = 0; } pCheckpointInfo->iForceCheckpointReason = pFile->pCPInfo->iForceCheckpointReason; pCheckpointInfo->bWritingDataBlocks = pFile->pCPInfo->bWritingDataBlocks; pCheckpointInfo->uiLogBlocksWritten = pFile->pCPInfo->uiLogBlocksWritten; pCheckpointInfo->uiDataBlocksWritten = pFile->pCPInfo->uiDataBlocksWritten; } pCheckpointInfo->uiBlockSize = (FLMUINT)pFile->FileHdr.uiBlockSize; pCheckpointInfo->uiDirtyCacheBytes = pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; if (pFile->pCPInfo->uiStartWaitTruncateTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pFile->pCPInfo->uiStartWaitTruncateTime); pCheckpointInfo->uiWaitTruncateTime = FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->uiWaitTruncateTime = 0; } } } /**************************************************************************** Desc: *****************************************************************************/ RCODE flmSetRflSizeThreshold( HFDB hDb, FLMUINT uiSizeThreshold, FLMUINT uiTimeInterval, FLMUINT uiSizeInterval) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FFILE * pFile = pDb->pFile; FLMBOOL bDbInitialized = FALSE; FLMBOOL bStartedTrans = FALSE; // Start an update transaction. Must not already be one going. bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } if (pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Set the size threshold and event intervals if( uiSizeThreshold == FLM_MAX_UINT) { uiSizeThreshold = FB2UD( &pFile->ucUncommittedLogHdr [LOG_RFL_DISK_SPACE_THRESHOLD]); } else { UD2FBA( (FLMUINT32)uiSizeThreshold, &pFile->ucUncommittedLogHdr [LOG_RFL_DISK_SPACE_THRESHOLD]); } if( uiTimeInterval == FLM_MAX_UINT) { uiTimeInterval = FB2UD( &pFile->ucUncommittedLogHdr [LOG_RFL_LIMIT_TIME_FREQ]); } else { UD2FBA( (FLMUINT32)uiTimeInterval, &pFile->ucUncommittedLogHdr [LOG_RFL_LIMIT_TIME_FREQ]); } if( uiSizeInterval == FLM_MAX_UINT) { uiSizeInterval = FB2UD( &pFile->ucUncommittedLogHdr [LOG_RFL_LIMIT_SPACE_FREQ]); } else { UD2FBA( (FLMUINT32)uiSizeInterval, &pFile->ucUncommittedLogHdr [LOG_RFL_LIMIT_SPACE_FREQ]); } // Log the change to the RFL if( RC_BAD( rc = pFile->pRfl->logSizeEventConfig( pDb->LogHdr.uiCurrTransID, uiSizeThreshold, uiTimeInterval, uiSizeInterval))) { goto Exit; } // Commit the transaction. if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { goto Exit; } bStartedTrans = FALSE; Exit: if( bStartedTrans) { flmAbortDbTrans( pDb); } if( bDbInitialized) { fdbExit( pDb); } return( rc); } libflaim-4.9.966/src/checksum.cpp0000644000175000017500000001602710510774540020170 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Calculate block checksum // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: checksum.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /******************************************************************** Desc: Compares or sets the checksum value in a block. Operates to compare the block checksum with the actual checksum. Ret: if (Compare) returns FERR_BLOCK_CHECKSUM block checksum does not agree with checksum header values. *********************************************************************/ RCODE BlkCheckSum( FLMBYTE * pucBlkPtr, FLMINT iCompare, FLMUINT uiBlkAddress, FLMUINT uiBlkSize) { RCODE rc = FERR_OK; FLMUINT uiCurrChecksum = 0; FLMUINT uiNewChecksum; FLMUINT uiEncryptSize; FLMUINT uiAdds = 0; FLMUINT uiXORs = 0; FLMBYTE * pucSaveBlkPtr = pucBlkPtr; // Check the block length against the maximum block size uiEncryptSize = (FLMUINT)getEncryptSize( pucBlkPtr); if( uiEncryptSize > uiBlkSize || uiEncryptSize < BH_OVHD) { rc = RC_SET( FERR_BLOCK_CHECKSUM); goto Exit; } // If we are comparing, but there is no current checksum just return. // The next time the checksum is modified, the comparison will be performed. // Version 3.x will store the full block address or if // a checksum is used, the lost low byte of block address is checksummed. if( iCompare == CHECKSUM_CHECK) { uiCurrChecksum = (FLMUINT)(((FLMUINT)pucBlkPtr[ BH_CHECKSUM_HIGH] << 8) + (FLMUINT)pucBlkPtr[ BH_CHECKSUM_LOW]); } // We need to checksum the data that is encrypted. // This is done by the getEncryptSize() call. // Check all of block, except for embedded checksum bytes. // For speed, the initial values of uiAdds and uiXORs effectively ignore/skip // the checksum values already embedded in the source: (a - a) == 0 and // (a ^ a) == 0 so the initial values, net of the 2nd operations, equal zero // too. uiAdds = 0 - (pucBlkPtr[ BH_CHECKSUM_LOW] + pucBlkPtr[ BH_CHECKSUM_HIGH]); uiXORs = pucBlkPtr[ BH_CHECKSUM_LOW] ^ pucBlkPtr[ BH_CHECKSUM_HIGH]; // The 3.x version checksums the low byte of the address. if( uiBlkAddress != BT_END) { uiAdds += (FLMBYTE)uiBlkAddress; uiXORs ^= (FLMBYTE)uiBlkAddress; } f_calcFastChecksum( pucBlkPtr, uiEncryptSize, &uiAdds, &uiXORs); uiNewChecksum = (((uiAdds << 8) + uiXORs) & 0xFFFF); // Set the checksum if (iCompare == CHECKSUM_SET) { pucSaveBlkPtr[ BH_CHECKSUM_HIGH] = (FLMBYTE)(uiNewChecksum >> 8); pucSaveBlkPtr[ BH_CHECKSUM_LOW] = (FLMBYTE)uiNewChecksum; goto Exit; } // The checksum is different from the stored checksum. // For version 3.x database we don't store the low byte of the // address. Thus, it will have to be computed from the checksum. if( uiBlkAddress == BT_END) { FLMBYTE byXor; FLMBYTE byAdd; FLMBYTE byDelta; // If there is a one byte value that will satisfy both // sides of the checksum, the checksum is OK and that value // is the first byte value. byXor = (FLMBYTE) uiNewChecksum; byAdd = (FLMBYTE) (uiNewChecksum >> 8); byDelta = byXor ^ pucSaveBlkPtr [BH_CHECKSUM_LOW]; // Here is the big check, if byDelta is also what is // off with the add portion of the checksum, we have // a good value. if( ((FLMBYTE) (byAdd + byDelta)) == pucSaveBlkPtr[ BH_CHECKSUM_HIGH] ) { // Set the low checksum value with the computed value. pucSaveBlkPtr[ BH_CHECKSUM_LOW] = byDelta; goto Exit; } } else { // This has the side effect of setting the low block address byte // in the block thus getting rid of the low checksum byte. // NOTE: We are allowing the case where the calculated checksum is // zero and the stored checksum is one because we used to change // a calculated zero to a one in old databases and store the one. // This is probably a somewhat rare case (1 out of 65536 checksums // will be zero), so forgiving it will be OK most of the time. // So that those don't cause us to report block checksum errors, // we just allow it - checksumming isn't a perfect check anyway. if (uiNewChecksum == uiCurrChecksum || ((!uiNewChecksum) && (uiCurrChecksum == 1))) { pucSaveBlkPtr [BH_CHECKSUM_LOW] = (FLMBYTE) uiBlkAddress; goto Exit; } } // Otherwise, we have a checksum error. rc = RC_SET( FERR_BLOCK_CHECKSUM); Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ FLMUINT lgHdrCheckSum( FLMBYTE * pucLogHdr, FLMBOOL bCompare) { FLMUINT uiCnt; FLMUINT uiTempSum; FLMUINT uiCurrChecksum; FLMUINT uiTempSum2; FLMUINT uiBytesToChecksum; uiBytesToChecksum = (FB2UW( &pucLogHdr [LOG_FLAIM_VERSION]) < FLM_FILE_FORMAT_VER_4_3) ? LOG_HEADER_SIZE_VER40 : LOG_HEADER_SIZE; // If we are comparing, but there is no current checksum, return // zero to indicate success. The next time the checksum is // modified, the comparison will be performed. // // Unconverted databases may have a 0xFFFF or a zero in the checksum // If 0xFFFF, change to a zero so we only have to deal with one value. if( (uiCurrChecksum = (FLMUINT)FB2UW( &pucLogHdr[ LOG_HDR_CHECKSUM])) == 0xFFFF) { uiCurrChecksum = 0; } if( bCompare && !uiCurrChecksum) { return( 0); } // Check all of log header except for the bytes which contain the // checksum. // // For speed, uiTempSum is initialized to effectively ignore or skip // the checksum embedded in the source: (a - a) == 0 so we store a negative // that the later addition clears out. Also, the loop counter, i, // is 1 larger than the number of FLMUINT16's so that we can // pre-decrement by "for(;--i != 0;)" -- basically "loop-non-zero". for( uiTempSum = 0 - (FLMUINT)FB2UW( &pucLogHdr[ LOG_HDR_CHECKSUM]), uiCnt = 1 + uiBytesToChecksum / sizeof( FLMUINT16); --uiCnt != 0; ) { uiTempSum += (FLMUINT)FB2UW( pucLogHdr); pucLogHdr += sizeof( FLMUINT16); } // Don't want a zero or 0xFFFF checksum - change to 1 if( (0 == (uiTempSum2 = (uiTempSum & 0xFFFF))) || (uiTempSum2 == 0xFFFF)) { uiTempSum2 = 1; } return( (FLMUINT)(((bCompare) && (uiTempSum2 == uiCurrChecksum)) ? (FLMUINT)0 : uiTempSum2) ); } libflaim-4.9.966/src/fsrecget.cpp0000644000175000017500000004707210510774540020174 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Read a record from the database. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsrecget.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define NUM_FIELDS_IN_ARRAY 16 typedef struct Field_State { FLMBYTE * pElement; // Points to the element within block FLMUINT uiFieldType; // Storage field type FLMUINT uiFieldLen; // Length of storage if known FLMUINT uiPosInElm; // Position within the element FLMUINT uiTagNum; // Field tag/data dictionary number FLMUINT uiLevel; // Current level number FLMUINT uiEncId; // EncDef ID if encrypted FLMUINT uiEncFieldLen; // Encrypted field length } FSTATE; typedef struct DATAPIECE { FLMBYTE * pData; // Points to data within the block. FLMUINT uiLength; // Length of this data piece DATAPIECE * pNext; // Next data piece or NULL } DATAPIECE; typedef struct Temporary_Field { FLMUINT uiLevel; FLMUINT uiFieldID; FLMUINT uiFieldType; FLMUINT uiFieldLen; FLMUINT uiEncId; FLMUINT uiEncFieldLen; DATAPIECE DataPiece; } TEMPFIELD; typedef struct FLDGROUP { TEMPFIELD pFields[ NUM_FIELDS_IN_ARRAY]; FLDGROUP * pNext; } FLDGROUP; typedef struct LOCKED_BLOCK { SCACHE * pSCache; LOCKED_BLOCK * pNext; } LOCKED_BLOCK; FSTATIC RCODE FSGetFldOverhead( FDB * pDb, FSTATE * fState); /**************************************************************************** Desc: Retrieves a record given a DRN ****************************************************************************/ RCODE FSReadRecord( FDB * pDb, LFILE * pLFile, FLMUINT uiDrn, FlmRecord ** ppRecord, FLMUINT * puiRecTransId, FLMBOOL * pbMostCurrent) { RCODE rc = FERR_OK; BTSK stackBuf[BH_MAX_LEVELS]; BTSK * pStack = NULL; FLMBYTE pKeyBuf[DIN_KEY_SIZ]; FLMBYTE pDrnBuf[DIN_KEY_SIZ]; FSInitStackCache( &stackBuf[0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; // Search the B-TREE for the record f_UINT32ToBigEndian( (FLMUINT32)uiDrn, pDrnBuf); if (RC_OK( rc = FSBtSearch( pDb, pLFile, &pStack, pDrnBuf, 4, 0))) { rc = RC_SET( FERR_NOT_FOUND); if ((pStack->uiCmpStatus == BT_EQ_KEY) && (uiDrn != DRN_LAST_MARKER)) { rc = FSReadElement( pDb, &pDb->TempPool, pLFile, uiDrn, pStack, TRUE, ppRecord, puiRecTransId, pbMostCurrent); } } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return (rc); } /**************************************************************************** Desc: Low-level routine to retrieve and build an internal tree record. ****************************************************************************/ RCODE FSReadElement( FDB * pDb, F_Pool * pPool, LFILE * pLFile, FLMUINT uiDrn, BTSK * pStack, FLMBOOL bOkToPreallocSpace, FlmRecord ** ppRecord, FLMUINT * puiRecTransId, FLMBOOL * pbMostCurrent) { RCODE rc = FERR_OK; FlmRecord * pRecord = NULL; FLMBYTE * pCurElm; void * pvPoolMark = pPool->poolMark(); FLMUINT uiElmRecLen; FLMUINT uiFieldLen; FLMUINT uiLowestTransId; FLMUINT uiFieldCount; FLMUINT uiTrueDataSpace; FLMUINT uiFieldPos; FLMBOOL bMostCurrent; TEMPFIELD * pField; FLDGROUP * pFldGroup = NULL; FLDGROUP* pFirstFldGroup = NULL; DATAPIECE * pDataPiece; LOCKED_BLOCK * pLockedBlock = NULL; FSTATE fState; FLMUINT uiEncFieldLen; // Initialize variables fState.uiLevel = 0; uiFieldCount = 0; uiTrueDataSpace = 0; uiFieldPos = NUM_FIELDS_IN_ARRAY; // Check to make sure we are positioned at the first element. pCurElm = CURRENT_ELM( pStack); if (!BBE_IS_FIRST( pCurElm)) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]); bMostCurrent = (pStack->pSCache->uiHighTransID == 0xFFFFFFFF) ? TRUE : FALSE; // Loop on each element in the record for (;;) { // Setup all variables to process the current element uiElmRecLen = BBE_GET_RL( pCurElm); if (uiElmRecLen == 0) { rc = RC_SET( FERR_EOF_HIT); break; } pCurElm += BBE_REC_OFS( pCurElm); fState.uiPosInElm = 0; // Loop on each field within this element. while (fState.uiPosInElm < uiElmRecLen) { fState.pElement = pCurElm; if (RC_BAD( rc = FSGetFldOverhead( pDb, &fState))) { goto Exit; } uiFieldLen = fState.uiFieldLen; uiEncFieldLen = fState.uiEncFieldLen; // Old record info data - skip past for now if (fState.uiTagNum == 0) { fState.uiPosInElm += (uiEncFieldLen ? uiEncFieldLen : uiFieldLen); continue; } if (!pRecord) { // Create a new data record or use the existing data record. if (*ppRecord) { if ((*ppRecord)->isReadOnly()) { (*ppRecord)->Release(); *ppRecord = NULL; if ((pRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else { // Reuse the existing FlmRecord object. pRecord = *ppRecord; *ppRecord = NULL; pRecord->clear(); } } else { if ((pRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } pRecord->setContainerID( pLFile->uiLfNum); pRecord->setID( uiDrn); if (pLFile->bMakeFieldIdTable) { pRecord->enableFieldIdTable(); } } // Check if out of fields in the tempoary field group. if (uiFieldPos >= NUM_FIELDS_IN_ARRAY) { FLDGROUP * pTempFldGroup; uiFieldPos = 0; // Allocate the first field group from the pool. if( RC_BAD( rc = pPool->poolAlloc( sizeof( FLDGROUP), (void **)&pTempFldGroup))) { goto Exit; } pTempFldGroup->pNext = NULL; if (pFldGroup) { pFldGroup->pNext = pTempFldGroup; } else { pFirstFldGroup = pTempFldGroup; } pFldGroup = pTempFldGroup; } uiFieldCount++; pField = &pFldGroup->pFields[uiFieldPos++]; pField->uiLevel = fState.uiLevel; pField->uiFieldID = fState.uiTagNum; pField->uiFieldType = fState.uiFieldType; pField->uiFieldLen = fState.uiFieldLen; pField->uiEncId = fState.uiEncId; pField->uiEncFieldLen = fState.uiEncFieldLen; pDataPiece = &pField->DataPiece; if (uiFieldLen || uiEncFieldLen) { FLMUINT uiDataPos = 0; if (fState.uiEncFieldLen) { uiTrueDataSpace += FLM_ENC_FLD_OVERHEAD; // Binary data needs to account for alignment issues. if (fState.uiFieldType == FLM_BINARY_TYPE) { // Adjust for the decrypted data. uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); } uiTrueDataSpace += fState.uiFieldLen + fState.uiEncFieldLen; // Store the encrypted field length rather than the // decrypted field length This will allow the gathering of // the encrypted or decrypted field data to use the same // code. uiFieldLen = uiEncFieldLen; } else if (fState.uiFieldLen > 4) { // Binary data needs to account for alignment issues. if (fState.uiFieldType == FLM_BINARY_TYPE) { if (fState.uiFieldLen >= 0xFF) { // Align so that the data is aligned - not the length uiTrueDataSpace += sizeof(FLMUINT32); uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); uiTrueDataSpace -= sizeof(FLMUINT32); } else { uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); } } uiTrueDataSpace += fState.uiFieldLen; // Field values with lengths greater than 255 bytes are // stored length-preceded. A single byte flags field // precedes the length. if (fState.uiFieldLen >= 0xFF) { uiTrueDataSpace += sizeof(FLMUINT32) + 1; } } // Value may start in the next element. while (uiDataPos < uiFieldLen) { // Need to read next element for the value portion? if (fState.uiPosInElm >= uiElmRecLen) { if (BBE_IS_LAST( CURRENT_ELM( pStack))) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } // If we are going to the next block, lock down this // block because data pointers are pointing to it. if (RC_BAD( FSBlkNextElm( pStack))) { LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; if( RC_BAD( rc = pPool->poolAlloc( sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) { goto Exit; } ScaHoldCache( pStack->pSCache); pLockedBlock->pSCache = pStack->pSCache; pLockedBlock->pNext = pLastLockedBlock; if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack))) { rc = (rc == FERR_BT_END_OF_DATA) ? RC_SET( FERR_DATA_ERROR) : rc; goto Exit; } if (uiLowestTransId > FB2UD( &pStack->pBlk[BH_TRANS_ID])) { uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]); } if (!bMostCurrent) { bMostCurrent = (pStack->pSCache->uiHighTransID == 0xFFFFFFFF) ? TRUE : FALSE; } } pCurElm = CURRENT_ELM( pStack); uiElmRecLen = BBE_GET_RL( pCurElm); pCurElm += BBE_REC_OFS( pCurElm); fState.uiPosInElm = 0; } // Compare number of bytes left if value <= # bytes left // in element if (uiFieldLen - uiDataPos <= uiElmRecLen - fState.uiPosInElm) { FLMUINT uiDelta = uiFieldLen - uiDataPos; pDataPiece->pData = &pCurElm[fState.uiPosInElm]; pDataPiece->uiLength = uiDelta; fState.uiPosInElm += uiDelta; pDataPiece->pNext = NULL; break; } else { // Take what is there and get next element to grab some // more. FLMUINT uiBytesToMove = uiElmRecLen - fState.uiPosInElm; DATAPIECE * pNextDataPiece; pDataPiece->pData = &pCurElm[fState.uiPosInElm]; pDataPiece->uiLength = uiBytesToMove; fState.uiPosInElm += uiBytesToMove; uiDataPos += uiBytesToMove; if( RC_BAD( rc = pPool->poolAlloc( sizeof( DATAPIECE), (void **)&pNextDataPiece))) { goto Exit; } pDataPiece->pNext = pNextDataPiece; pDataPiece = pNextDataPiece; } } } } // Done? if (BBE_IS_LAST( CURRENT_ELM( pStack))) { break; } // Position to next element if (RC_BAD( FSBlkNextElm( pStack))) { LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; if( RC_BAD( rc = pPool->poolAlloc( sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) { goto Exit; } ScaHoldCache( pStack->pSCache); pLockedBlock->pSCache = pStack->pSCache; pLockedBlock->pNext = pLastLockedBlock; if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack))) { if (rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_DATA_ERROR); } goto Exit; } if (uiLowestTransId > FB2UD( &pStack->pBlk[BH_TRANS_ID])) { uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]); } if (!bMostCurrent) { bMostCurrent = (pStack->pSCache->uiHighTransID == 0xFFFFFFFF) ? TRUE : FALSE; } } // Corruption Check. pCurElm = CURRENT_ELM( pStack); if (BBE_IS_FIRST( pCurElm)) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } } if (pRecord) { void * pvField; if (bOkToPreallocSpace) { if (RC_BAD( rc = pRecord->preallocSpace( uiFieldCount, uiTrueDataSpace))) { goto Exit; } } pFldGroup = pFirstFldGroup; for (uiFieldPos = 0; uiFieldCount--; uiFieldPos++) { if (uiFieldPos >= NUM_FIELDS_IN_ARRAY) { uiFieldPos = 0; if ((pFldGroup = pFldGroup->pNext) == NULL) { break; } } pField = &pFldGroup->pFields[uiFieldPos]; if (RC_BAD( rc = pRecord->insertLast( pField->uiLevel, pField->uiFieldID, pField->uiFieldType, &pvField))) { goto Exit; } if (pField->uiFieldLen) { FLMBYTE * pDataPtr; FLMBYTE * pEncDataPtr; pDataPiece = &pField->DataPiece; if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, pField->uiFieldType, pField->uiFieldLen, pField->uiEncFieldLen, pField->uiEncId, (pField->uiEncId ? FLD_HAVE_ENCRYPTED_DATA : 0), &pDataPtr, &pEncDataPtr))) { goto Exit; } do { if (pField->uiEncId) { f_memcpy( pEncDataPtr, pDataPiece->pData, pDataPiece->uiLength); pEncDataPtr += pDataPiece->uiLength; } else { f_memcpy( pDataPtr, pDataPiece->pData, pDataPiece->uiLength); pDataPtr += pDataPiece->uiLength; } pDataPiece = pDataPiece->pNext; } while (pDataPiece); // If the field is encrypted, we must decrypt it here. if (pField->uiEncId && !pDb->pFile->bInLimitedMode) { if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, pvField, pField->uiEncId, &pDb->TempPool))) { goto Exit; } } } } } if (puiRecTransId) { *puiRecTransId = uiLowestTransId; } if (pbMostCurrent) { *pbMostCurrent = bMostCurrent; } if (*ppRecord) { flmAssert( 0); (*ppRecord)->Release(); } pRecord->sortFieldIdTable(); *ppRecord = pRecord; pRecord = NULL; Exit: // Release all locked down blocks except the current block. while (pLockedBlock) { ScaReleaseCache( pLockedBlock->pSCache, FALSE); pLockedBlock = pLockedBlock->pNext; } pPool->poolReset( pvPoolMark); if (pRecord) { pRecord->Release(); } // You are now positioned to the last element in the record return (rc); } /**************************************************************************** Desc: Read field overhead (level, field drn, and length information. This isolates the complexity of the storage formats. ****************************************************************************/ FSTATIC RCODE FSGetFldOverhead( FDB * pDb, FSTATE * fState) { RCODE rc = FERR_OK; FLMBYTE * pFieldOvhd = &fState->pElement[fState->uiPosInElm]; FLMBYTE * pElement = fState->pElement; FLMBYTE * pTmp; FLMBOOL bDoesntHaveFieldDef = TRUE; FLMUINT uiFieldLen; FLMUINT uiFieldType = 0; FLMUINT uiTagNum; FLMBYTE ucBaseFlags; FLMUINT uiEncId = 0; FLMUINT uiEncFieldLen = 0; if (FOP_IS_STANDARD( pFieldOvhd)) { if (FSTA_LEVEL( pFieldOvhd)) { fState->uiLevel++; } uiFieldLen = FSTA_FLD_LEN( pFieldOvhd); uiTagNum = FSTA_FLD_NUM( pFieldOvhd); pFieldOvhd += FSTA_OVHD; } else if (FOP_IS_OPEN( pFieldOvhd)) { if (FOPE_LEVEL( pFieldOvhd)) { fState->uiLevel++; } ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd++)); uiTagNum = (FLMUINT) * pFieldOvhd++; if (FOP_2BYTE_FLDNUM( ucBaseFlags)) { uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8; } uiFieldLen = (FLMUINT) * pFieldOvhd++; if (FOP_2BYTE_FLDLEN( ucBaseFlags)) { uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8; } } else if (FOP_IS_NO_VALUE( pFieldOvhd)) { if (FNOV_LEVEL( pFieldOvhd)) { fState->uiLevel++; } ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd++)); uiTagNum = (FLMUINT) * pFieldOvhd++; if (FOP_2BYTE_FLDNUM( ucBaseFlags)) { uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8; } uiFieldLen = uiFieldType = 0; } else if (FOP_IS_SET_LEVEL( pFieldOvhd)) { // SET THE LEVEL Must be continuous with the next field fState->uiLevel -= FSLEV_GET( pFieldOvhd++); fState->uiPosInElm = (FLMUINT) (pFieldOvhd - pElement); rc = FSGetFldOverhead( pDb, fState); goto Exit; } else if (FOP_IS_TAGGED( pFieldOvhd)) { bDoesntHaveFieldDef = FALSE; if (FTAG_LEVEL( pFieldOvhd)) { fState->uiLevel++; } ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd)); pFieldOvhd++; uiFieldType = (FLMUINT) (FTAG_GET_FLD_TYPE( *pFieldOvhd)); pFieldOvhd++; uiTagNum = (FLMUINT) * pFieldOvhd++; if (FOP_2BYTE_FLDNUM( ucBaseFlags)) { uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8; } // When storing the unregistered fields we cleared the high bit to // save on storage for VER11. The problem is that if a tag that is // not in the unregistered range (FLAIM TAGS) cannot be represented. // SO, we will XOR the high bit so 0x0111 is stored as 0x8111 and // 0x8222 is stored as 0x0222. uiTagNum ^= 0x8000; uiFieldLen = (FLMUINT) * pFieldOvhd++; if (FOP_2BYTE_FLDLEN( ucBaseFlags)) { uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8; } } else if (FOP_IS_RECORD_INFO( pFieldOvhd)) { bDoesntHaveFieldDef = FALSE; ucBaseFlags = *pFieldOvhd++; uiFieldLen = *pFieldOvhd++; if (FOP_2BYTE_FLDLEN( ucBaseFlags)) { uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8; } uiTagNum = 0; } else if (FOP_IS_ENCRYPTED( pFieldOvhd)) { FLMBOOL bTagSz; FLMBOOL bLenSz; FLMBOOL bENumSz; FLMBOOL bELenSz; if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } bDoesntHaveFieldDef = FALSE; if (FENC_LEVEL( pFieldOvhd)) { fState->uiLevel++; } uiFieldType = (FLMUINT) (FENC_FLD_TYPE( pFieldOvhd)); bTagSz = FENC_TAG_SZ( pFieldOvhd); bLenSz = FENC_LEN_SZ( pFieldOvhd); bENumSz = FENC_ETAG_SZ( pFieldOvhd); bELenSz = FENC_ELEN_SZ( pFieldOvhd); pFieldOvhd += 2; uiTagNum = (FLMUINT) * pFieldOvhd++; if (bTagSz) { uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8; } uiFieldLen = (FLMUINT) * pFieldOvhd++; if (bLenSz) { uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8; } uiEncId = (FLMUINT) * pFieldOvhd++; if (bENumSz) { uiEncId += ((FLMUINT) * pFieldOvhd++) << 8; } uiEncFieldLen = (FLMUINT) * pFieldOvhd++; if (bELenSz) { uiEncFieldLen += ((FLMUINT) * pFieldOvhd++) << 8; } } else if (FOP_IS_LARGE( pFieldOvhd)) { if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } bDoesntHaveFieldDef = FALSE; pTmp = pFieldOvhd; if (FLARGE_LEVEL( pFieldOvhd)) { fState->uiLevel++; } pTmp++; uiFieldType = FLARGE_FLD_TYPE( pFieldOvhd); pTmp++; uiTagNum = FLARGE_TAG_NUM( pFieldOvhd); pTmp += 2; if ((uiFieldLen = FLARGE_DATA_LEN( pFieldOvhd)) <= 0x0000FFFF) { flmAssert( 0); rc = RC_SET( FERR_DATA_ERROR); goto Exit; } pTmp += 4; if (FLARGE_ENCRYPTED( pFieldOvhd)) { uiEncId = FLARGE_ETAG_NUM( pFieldOvhd); pTmp += 2; uiEncFieldLen = FLARGE_EDATA_LEN( pFieldOvhd); pTmp += 4; } pFieldOvhd = pTmp; } else { flmAssert( 0); rc = RC_SET( FERR_DATA_ERROR); goto Exit; } if (bDoesntHaveFieldDef) { // Get the field's storage type. if (RC_BAD( fdictGetField( pDb->pDict, uiTagNum, &uiFieldType, NULL, NULL))) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } } // Set the fState return values fState->uiFieldType = uiFieldType; fState->uiFieldLen = uiFieldLen; fState->uiPosInElm = (FLMUINT) (pFieldOvhd - pElement); fState->uiTagNum = uiTagNum; fState->uiEncId = uiEncId; fState->uiEncFieldLen = uiEncFieldLen; Exit: return (rc); } libflaim-4.9.966/src/imonfsys.cpp0000644000175000017500000005753610510774540020247 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying the gv_FlmSysData structure in HTML on a web page. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonfsys.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: This implements the display method of the F_FlmSysDataPage class *****************************************************************************/ RCODE F_FlmSysDataPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMBOOL bRefresh = FALSE; stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); // Determine if we are being requested to refresh this page or not. if ((bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh")) == TRUE) { fnPrintf( m_pHRequest, "" "" "Database iMonitor - gv_FlmSysData\n"); } else { fnPrintf( m_pHRequest, "\n"); } printStyle(); popupFrame(); //Spits out a Javascript function that will open a new window.. fnPrintf( m_pHRequest, "\n"); // Insert a new table into the page to display the gv_FlmSysData fields fnPrintf( m_pHRequest, "\n"); write_data(bRefresh); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /**************************************************************************** Desc: Generate the HTML that will display the contents of the gv_FlmSysData structure. ****************************************************************************/ void F_FlmSysDataPage::write_data( FLMBOOL bRefresh) { RCODE rc = FERR_OK; char * pszTemp; char * pszTemp2; char szAddress[20]; FLMBOOL bHighlight = TRUE; if( RC_BAD( rc = f_alloc( 150, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } if( RC_BAD( rc = f_alloc( 150, &pszTemp2))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // If we are not to refresh this page, then don't include the // refresh meta command if (!bRefresh) { f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString); } else { f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString); } // Print out a formal header and the refresh option. printTableStart("Database System Data", 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString); fnPrintf( m_pHRequest, "%s\n", pszTemp); printColumnHeadingClose(); printTableRowEnd(); printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); // pMrnuFile - Most recently used file address if (gv_FlmSysData.pMrnuFile) { printAddress( (void *)gv_FlmSysData.pMrnuFile, szAddress); f_sprintf( (char *)pszTemp, "%s/FFile?From=FlmSysData?Link=pMrnuFile?Address=%s", m_pszURLString, szAddress); } printHTMLLink( "pMrnuFile", "FFILE *", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pMrnuFile, (void *)gv_FlmSysData.pMrnuFile, (char *)pszTemp, (bHighlight = !bHighlight)); // pLrnuFile - Least recently used file address if (gv_FlmSysData.pLrnuFile) { printAddress( (void *)gv_FlmSysData.pLrnuFile, szAddress); f_sprintf( (char *)pszTemp, "%s/FFile?From=FlmSysData?Link=pLrnuFile?Address=%s", m_pszURLString, szAddress); } printHTMLLink( "pLrnuFile", "FFILE *", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pLrnuFile, (void *)gv_FlmSysData.pLrnuFile, (char *)pszTemp, (bHighlight = !bHighlight)); // pFileHashTbl - File name hash table f_sprintf( (char *)pszTemp, "%s/FileHashTbl", m_pszURLString); printHTMLLink( "pFileHashTbl", "FFILE *", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pFileHashTbl, (void *)gv_FlmSysData.pFileHashTbl, (char *)pszTemp, (bHighlight = !bHighlight)); // hShareMutex - Shared File Mutex printAddress( (void *)&gv_FlmSysData.hShareMutex, szAddress); printHTMLString( "hShareMutex", "F_MUTEX", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.hShareMutex, (char *)szAddress, (bHighlight = !bHighlight)); // pFileSystem - File system printAddress( (void *)gv_FlmSysData.pFileSystem, szAddress); printHTMLString( "pFileSystem", "F_FileSystem *", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pFileSystem, (char *)szAddress, (bHighlight = !bHighlight)); // bTempDirSet - Temporary directory printHTMLString( "bTempDirSet", "FLMBOOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.bTempDirSet, (char *)(gv_FlmSysData.bTempDirSet ? "Yes" : "No"), (bHighlight = !bHighlight)); // bCheckCache - Check cache printHTMLString( "bCheckCache", "FLMBOOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.bCheckCache, (char *)(gv_FlmSysData.bCheckCache ? "Yes" : "No"), (bHighlight = !bHighlight)); // uiMaxCPInterval - Maximum checkpoint interval printHTMLUint( "uiMaxCPInterval", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxCPInterval, gv_FlmSysData.uiMaxCPInterval, (bHighlight = !bHighlight)); // uiMaxTransTime - Maximum Transaction Time printHTMLUint( "uiMaxTransTime", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxTransTime, gv_FlmSysData.uiMaxTransTime, (bHighlight = !bHighlight)); // uiMaxTransInactiveTime - Maximum transaction inactive time printHTMLUint( "uiMaxTransInactiveTime", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxTransInactiveTime, gv_FlmSysData.uiMaxTransInactiveTime, (bHighlight = !bHighlight)); // bDynamicCacheAdjust - Dynamic Cache Adjust printHTMLString( "bDynamicCacheAdjust", "FLMBOOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.bDynamicCacheAdjust, (char *)(gv_FlmSysData.bDynamicCacheAdjust ? "Yes" : "No"), (bHighlight = !bHighlight)); // uiBlockCachePercentage - Block cache percentage printHTMLUint( "uiBlockCachePercentage", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiBlockCachePercentage, gv_FlmSysData.uiBlockCachePercentage, (bHighlight = !bHighlight)); // uiCacheAdjustPercent - Cache adjust percentage printHTMLUint( "uiCacheAdjustPercent", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheAdjustPercent, gv_FlmSysData.uiCacheAdjustPercent, (bHighlight = !bHighlight)); // uiCacheAdjustMin - Cache adjust minimum printHTMLUint( "uiCacheAdjustMin", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMin, (bHighlight = !bHighlight)); // uiCacheAdjustMax - Cache Adjust Maximum printHTMLUint( "uiCacheAdjustMax", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheAdjustMax, gv_FlmSysData.uiCacheAdjustMax, (bHighlight = !bHighlight)); // uiCacheAdjustMinToLeave - Cache adjust minimum to leave printHTMLUint( "uiCacheAdjustMinToLeave", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheAdjustMinToLeave, gv_FlmSysData.uiCacheAdjustMinToLeave, (bHighlight = !bHighlight)); // uiCacheAdjustInterval - Cache adjust interval printHTMLUint( "uiCacheAdjustInterval", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheAdjustInterval, gv_FlmSysData.uiCacheAdjustInterval, (bHighlight = !bHighlight)); // uiCacheCleanupInterval - Cache Cleanup Interval printHTMLUint( "uiCacheCleanupInterval", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiCacheCleanupInterval, gv_FlmSysData.uiCacheCleanupInterval, (bHighlight = !bHighlight)); // uiUnusedCleanupInterval - Unused cleanup interval printHTMLUint( "uiUnusedCleanupInterval", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiUnusedCleanupInterval, gv_FlmSysData.uiUnusedCleanupInterval, (bHighlight = !bHighlight)); // SCacheMgr - Block Cache Manager f_sprintf( (char *)pszTemp, "%s/SCacheMgr", m_pszURLString); printHTMLLink( "SCacheMgr", "SCACHE_MGR", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.SCacheMgr, (void *)&gv_FlmSysData.SCacheMgr, (char *)pszTemp, (bHighlight = !bHighlight)); // RCacheMgr - Record cache manager f_sprintf( (char *)pszTemp, "%s/RCacheMgr", m_pszURLString); printHTMLLink( "RCacheMgr", "RCACHE_MGR", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.RCacheMgr, (void *)&gv_FlmSysData.RCacheMgr, (char *)pszTemp, (bHighlight = !bHighlight)); // pMonitorThrd - Monitor Thread f_sprintf( (char *)pszTemp, "%s/MonitorThrd", m_pszURLString); printHTMLLink( "pMonitorThrd", "F_Thread *", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pMonitorThrd, (void *)gv_FlmSysData.pMonitorThrd, (char *)pszTemp, (bHighlight = !bHighlight)); // Stats f_sprintf( (char *)pszTemp, "Stats", m_pszURLString); printAddress( (void *)&gv_FlmSysData.Stats, szAddress); f_sprintf( (char *)pszTemp2, "%s", m_pszURLString, szAddress); printHTMLString( (char *)pszTemp, "FLM_STATS", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.Stats, (char *)pszTemp2, (bHighlight = !bHighlight)); // hQueryMutex printAddress( (void *)&gv_FlmSysData.hQueryMutex, szAddress); printHTMLString( "hQueryMutex", "F_MUTEX", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.hQueryMutex, (char *)szAddress, (bHighlight = !bHighlight)); // pNewestQuery printAddress( (void *)&gv_FlmSysData.pNewestQuery, szAddress); printHTMLString( "pNewestQuery", "QUERY_HDR_p", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pNewestQuery, (char *)szAddress, (bHighlight = !bHighlight)); // pOldestQuery printAddress( (void *)&gv_FlmSysData.pOldestQuery, szAddress); printHTMLString( "pOldestQuery", "QUERY_HDR_p", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.pOldestQuery, (char *)szAddress, (bHighlight = !bHighlight)); // uiQueryCnt printHTMLUint( "uiQueryCnt", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiQueryCnt, gv_FlmSysData.uiQueryCnt, (bHighlight = !bHighlight)); // uiMaxQueries printHTMLUint( "uiMaxQueries", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxQueries, gv_FlmSysData.uiMaxQueries, (bHighlight = !bHighlight)); // bNeedToUnsetMaxQueries printHTMLString( "bNeedToUnsetMaxQueries", "FLMBOOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.bNeedToUnsetMaxQueries, (char *)(gv_FlmSysData.bNeedToUnsetMaxQueries ? "Yes" : "No"), (bHighlight = !bHighlight)); // bStatsInitialized - Statistics initialized printHTMLString( "bStatsInitialized", "FLMBOOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.bStatsInitialized, (char *)(gv_FlmSysData.bStatsInitialized ? "Yes" : "No"), (bHighlight = !bHighlight)); // pszTempDir - Temporary Working Directory printHTMLString( "pszTempDir", "FLMBYTE", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.szTempDir[0], (char *)gv_FlmSysData.szTempDir, (bHighlight = !bHighlight)); // uiMaxUnusedTime - Maximum unused structures time printHTMLUint( "uiMaxUnusedTime", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxUnusedTime, gv_FlmSysData.uiMaxUnusedTime, (bHighlight = !bHighlight)); // ucBlobExt - Blob Override Extension printHTMLString( "ucBlobExt", "FLMBYTE", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.ucBlobExt[0], (char *)gv_FlmSysData.ucBlobExt, (bHighlight = !bHighlight)); // KRefPool - Update Pool printAddress( (void *)&gv_FlmSysData.KRefPool, szAddress); printHTMLString( "KRefPool", "POOL", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.KRefPool, (char *)szAddress, (bHighlight = !bHighlight)); // HttpConfigParms f_sprintf( (char *)pszTemp, "HttpConfigParms", m_pszURLString); printAddress( (void *)&gv_FlmSysData.Stats, szAddress); f_sprintf( (char *)pszTemp2, "%s", m_pszURLString, szAddress); printAddress( (void *)&gv_FlmSysData.HttpConfigParms, szAddress); printHTMLString( (char *)pszTemp, "HTTPCONFIGPARMS", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.HttpConfigParms, (char *)pszTemp2, (bHighlight = !bHighlight)); // uiMaxFileSize - Maximum File Size printHTMLUint( "uiMaxFileSize", "FLMUINT", (void *)&gv_FlmSysData, (void *)&gv_FlmSysData.uiMaxFileSize, gv_FlmSysData.uiMaxFileSize, (bHighlight = !bHighlight)); printTableEnd(); Exit: if (pszTemp) { f_free( &pszTemp); } if (pszTemp2) { f_free( &pszTemp2); } return; } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_HttpConfigParmsPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; char szAddress[20]; char szOffset[8]; FLMBOOL bHighlight = FALSE; F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); printDocStart( "HttpConfigParams"); printStyle(); //printTableStart("HttpConfigParms", 4); fnPrintf( m_pHRequest, "\n"); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printAddress( (void *)gv_FlmSysData.HttpConfigParms.hMutex, szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.hMutex, szOffset); printTableRowStart(); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.uiUseCount, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_lu, szOffset, gv_FlmSysData.HttpConfigParms.uiUseCount); printTableRowEnd(); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.pszURLString, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, gv_FlmSysData.HttpConfigParms.pszURLString); printTableRowEnd(); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.uiURLStringLen, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_lu, szOffset, gv_FlmSysData.HttpConfigParms.uiURLStringLen); printTableRowEnd(); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.bRegistered, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, (char *)(gv_FlmSysData.HttpConfigParms.bRegistered ? "Yes" : "No")); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReg), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReg, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnDereg), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnDereg, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReqPath), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReqPath, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReqQuery), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReqQuery, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReqHdrValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReqHdrValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSetHdrValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSetHdrValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnPrintf), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnPrintf, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnEmit), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnEmit, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSetNoCache), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSetNoCache, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSendHeader), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSendHeader, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSetIOMode), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSetIOMode, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSendBuffer), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSendBuffer, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnAcquireSession), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnAcquireSession, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReleaseSession), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReleaseSession, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnAcquireUser), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnAcquireUser, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnReleaseUser), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnReleaseUser, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSetSessionValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSetSessionValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnGetSessionValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnGetSessionValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnGetGblValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnGetGblValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnSetGblValue), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnSetGblValue, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printAddress( *((void **)&gv_FlmSysData.HttpConfigParms.fnRecvBuffer), szAddress); printOffset( &gv_FlmSysData.HttpConfigParms, &gv_FlmSysData.HttpConfigParms.fnRecvBuffer, szOffset); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, TD_s " " TD_s, szOffset, szAddress); printTableRowEnd(); printTableEnd(); fnPrintf( m_pHRequest, " \n"); fnEmit(); return( rc); } libflaim-4.9.966/src/fcs.cpp0000644000175000017500000043252410510774540017145 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: FLAIM C/S client interface // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fcs_bios.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC FLMBOOL flmGetNextHexPacketSlot( FLMBYTE * pucUsedMap, FLMUINT uiMapSize, IF_RandomGenerator * pRandGen, FLMUINT * puiSlot); FSTATIC RCODE flmGetNextHexPacketBytes( FLMBYTE * pucUsedMap, FLMUINT uiMapSize, FLMBYTE * pucPacket, IF_RandomGenerator * pRandGen, FLMBYTE * pucBuf, FLMUINT uiCount); /**************************************************************************** Desc: *****************************************************************************/ FCS_BIOS::FCS_BIOS( void) { m_pool.poolInit( (FCS_BIOS_BLOCK_SIZE + sizeof( FCSBIOSBLOCK)) * 2); m_bMessageActive = FALSE; m_pRootBlock = NULL; m_pCurrWriteBlock = NULL; m_pCurrReadBlock = NULL; m_bAcceptingData = FALSE; m_pEventHook = NULL; m_pvUserData = 0; } /**************************************************************************** Desc: *****************************************************************************/ FCS_BIOS::~FCS_BIOS() { m_pool.poolFree(); } /**************************************************************************** Desc: Clears all pending data *****************************************************************************/ RCODE FCS_BIOS::reset( void) { return( close()); } /**************************************************************************** Desc: Flushes any pending data and closes the stream. *****************************************************************************/ RCODE FCS_BIOS::close( void) { RCODE rc = FERR_OK; m_pool.poolReset(); m_bMessageActive = FALSE; m_pRootBlock = NULL; m_pCurrWriteBlock = NULL; m_pCurrReadBlock = NULL; m_bAcceptingData = FALSE; return( rc); } /**************************************************************************** Desc: Writes the requested amount of data to the stream. *****************************************************************************/ RCODE FCS_BIOS::write( FLMBYTE * pucData, FLMUINT uiLength) { FLMUINT uiCopySize; FLMUINT uiDataPos = 0; FCSBIOSBLOCK * pPrevBlock = NULL; RCODE rc = FERR_OK; if( !m_bAcceptingData) { m_pool.poolReset(); m_pCurrWriteBlock = NULL; m_pCurrReadBlock = NULL; m_pRootBlock = NULL; m_bAcceptingData = TRUE; } while( uiLength) { if( !m_pCurrWriteBlock || m_pCurrWriteBlock->uiCurrWriteOffset == FCS_BIOS_BLOCK_SIZE) { pPrevBlock = m_pCurrWriteBlock; if( RC_BAD( rc = m_pool.poolCalloc( sizeof( FCSBIOSBLOCK), (void **)&m_pCurrWriteBlock))) { goto Exit; } if( RC_BAD( rc = m_pool.poolAlloc( FCS_BIOS_BLOCK_SIZE, (void **)&m_pCurrWriteBlock->pucBlock))) { goto Exit; } if( pPrevBlock) { pPrevBlock->pNextBlock = m_pCurrWriteBlock; } else { m_pRootBlock = m_pCurrWriteBlock; m_pCurrReadBlock = m_pCurrWriteBlock; } } uiCopySize = f_min( uiLength, (FLMUINT)(FCS_BIOS_BLOCK_SIZE - m_pCurrWriteBlock->uiCurrWriteOffset)); flmAssert( uiCopySize != 0); f_memcpy( &(m_pCurrWriteBlock->pucBlock[ m_pCurrWriteBlock->uiCurrWriteOffset]), &(pucData[ uiDataPos]), uiCopySize); m_pCurrWriteBlock->uiCurrWriteOffset += uiCopySize; uiDataPos += uiCopySize; uiLength -= uiCopySize; } Exit: return( rc); } /**************************************************************************** Desc: Terminates the current message *****************************************************************************/ RCODE FCS_BIOS::endMessage( void) { RCODE rc = FERR_OK; if( !m_bAcceptingData) { goto Exit; } if( m_pEventHook) { if( RC_BAD( rc = m_pEventHook( this, FCS_BIOS_EOM_EVENT, m_pvUserData))) { goto Exit; } } Exit: m_bAcceptingData = FALSE; return( rc); } /**************************************************************************** Desc: Reads the requested amount of data from the stream. *****************************************************************************/ RCODE FCS_BIOS::read( FLMBYTE * pucData, FLMUINT uiLength, FLMUINT * puiBytesRead) { FLMUINT uiCopySize; FLMUINT uiDataPos = 0; RCODE rc = FERR_OK; if( puiBytesRead) { *puiBytesRead = 0; } if( m_bAcceptingData) { m_bAcceptingData = FALSE; } while( uiLength) { if( m_pCurrReadBlock && m_pCurrReadBlock->uiCurrReadOffset == m_pCurrReadBlock->uiCurrWriteOffset) { m_pCurrReadBlock = m_pCurrReadBlock->pNextBlock; } if( !m_pCurrReadBlock) { m_pool.poolReset(); rc = RC_SET( FERR_EOF_HIT); goto Exit; } uiCopySize = f_min( uiLength, m_pCurrReadBlock->uiCurrWriteOffset - m_pCurrReadBlock->uiCurrReadOffset); f_memcpy( &(pucData[ uiDataPos]), &(m_pCurrReadBlock->pucBlock[ m_pCurrReadBlock->uiCurrReadOffset]), uiCopySize); m_pCurrReadBlock->uiCurrReadOffset += uiCopySize; uiDataPos += uiCopySize; if( puiBytesRead) { (*puiBytesRead) += uiCopySize; } uiLength -= uiCopySize; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FCS_BIOS::isDataAvailable( void) { if( m_bAcceptingData) { if( m_pRootBlock && m_pRootBlock->uiCurrWriteOffset) { return( TRUE); } } else if( m_pCurrReadBlock && ((m_pCurrReadBlock->uiCurrReadOffset < m_pCurrReadBlock->uiCurrWriteOffset) || (m_pCurrReadBlock->pNextBlock))) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: Returns the amount of data available for reading *****************************************************************************/ FLMUINT FCS_BIOS::getAvailable( void) { FLMUINT uiAvail = 0; FCSBIOSBLOCK * pBlk; if( m_bAcceptingData) { if( m_pRootBlock && m_pRootBlock->uiCurrWriteOffset) { pBlk = m_pRootBlock; while( pBlk) { uiAvail += pBlk->uiCurrWriteOffset; pBlk = pBlk->pNextBlock; } } } else if( m_pCurrReadBlock && ((m_pCurrReadBlock->uiCurrReadOffset < m_pCurrReadBlock->uiCurrWriteOffset) || (m_pCurrReadBlock->pNextBlock))) { pBlk = m_pCurrReadBlock; while( pBlk) { uiAvail += (pBlk->uiCurrWriteOffset - pBlk->uiCurrReadOffset); pBlk = pBlk->pNextBlock; } } return( uiAvail); } /**************************************************************************** Desc: ****************************************************************************/ FCS_DIS::FCS_DIS( void) { m_pIStream = NULL; m_uiBOffset = m_uiBDataSize = 0; m_bSetupCalled = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ FCS_DIS::~FCS_DIS( void) { if( m_bSetupCalled) { (void)close(); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::setup( FCS_ISTM * pIStream) { m_pIStream = pIStream; m_bSetupCalled = TRUE; return( FERR_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readByte( FLMBYTE * pValue) { return( read( pValue, 1, NULL)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readShort( FLMINT16 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL))) { *pValue = f_bigEndianToINT16( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readUShort( FLMUINT16 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 2, NULL))) { *pValue = f_bigEndianToUINT16( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readInt( FLMINT32 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) { *pValue = f_bigEndianToINT32( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readUInt( FLMUINT32 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 4, NULL))) { *pValue = f_bigEndianToUINT32( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readInt64( FLMINT64 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) { *pValue = f_bigEndianToINT64( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::readUInt64( FLMUINT64 * pValue) { RCODE rc; if( RC_OK( rc = read( (FLMBYTE *)pValue, 8, NULL))) { *pValue = f_bigEndianToUINT64( (FLMBYTE *)pValue); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FCS_DIS::skip( FLMUINT uiBytesToSkip) { return( read( NULL, uiBytesToSkip, NULL)); } /**************************************************************************** Desc: Flushes any pending data and closes the DIS ****************************************************************************/ RCODE FCS_DIS::close( void) { RCODE rc = FERR_OK; // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); // Terminate and flush. if( RC_BAD( rc = endMessage())) { goto Exit; } // Reset the member variables. m_pIStream = NULL; Exit: return( rc); } /**************************************************************************** Desc: Returns the state of the stream (open == TRUE, closed == FALSE) ****************************************************************************/ FLMBOOL FCS_DIS::isOpen( void) { // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); if( m_pIStream && m_pIStream->isOpen()) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: Flushes and terminates the current parent stream message ****************************************************************************/ RCODE FCS_DIS::endMessage( void) { RCODE rc = FERR_OK; // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); if( !m_pIStream) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Flush any pending data. if( RC_BAD( rc = flush())) { goto Exit; } // Terminate the message. if( RC_BAD( rc = m_pIStream->endMessage())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Flushes any pending data ****************************************************************************/ RCODE FCS_DIS::flush( void) { RCODE rc = FERR_OK; // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); if( !m_pIStream) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Flush the passed-in input stream. if( RC_BAD( rc = m_pIStream->flush())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Reads the specified number of bytes. ****************************************************************************/ RCODE FCS_DIS::read( FLMBYTE * pucData, FLMUINT uiLength, FLMUINT * puiBytesRead) { FLMUINT uiCopySize; FLMUINT uiReadLen; FLMBYTE * pucPos = NULL; RCODE rc = FERR_OK; // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); if( !m_pIStream) { rc = RC_SET( FERR_FAILURE); goto Exit; } if( puiBytesRead) { *puiBytesRead = uiLength; } pucPos = pucData; while( uiLength) { if( m_uiBOffset == m_uiBDataSize) { m_uiBOffset = m_uiBDataSize = 0; if( RC_BAD( rc = m_pIStream->read( m_pucBuffer, FCS_DIS_BUFFER_SIZE, &uiReadLen))) { if( uiReadLen) { rc = FERR_OK; } else { goto Exit; } } m_uiBDataSize = uiReadLen; } uiCopySize = m_uiBDataSize - m_uiBOffset; if( uiLength < uiCopySize) { uiCopySize = uiLength; } if( pucPos) { f_memcpy( pucPos, &(m_pucBuffer[ m_uiBOffset]), uiCopySize); pucPos += uiCopySize; } m_uiBOffset += uiCopySize; uiLength -= uiCopySize; } Exit: if( RC_OK( rc) && uiLength) { // Unable to satisfy the read request. rc = RC_SET( FERR_EOF_HIT); } if( puiBytesRead) { (*puiBytesRead) -= uiLength; } return( rc); } /**************************************************************************** Desc: Reads a binary token from the stream. The token is tagged with a length. ****************************************************************************/ RCODE FCS_DIS::readBinary( F_Pool * pPool, FLMBYTE ** ppValue, FLMUINT * puiDataSize) { FLMUINT16 ui16DataSize; RCODE rc = FERR_OK; if( RC_BAD( rc = readUShort( &ui16DataSize))) { goto Exit; } if( pPool) { // If the data size is non-zero, allocate a buffer and // read the entire binary value. if( ui16DataSize) { if( RC_BAD( rc = pPool->poolAlloc( ui16DataSize, (void **)ppValue))) { goto Exit; } if( RC_BAD( rc = read( *ppValue, ui16DataSize, NULL))) { goto Exit; } } else { *ppValue = NULL; } } else { // The application is not interested in the value. Just skip the // to the end of the value. if( RC_BAD( rc = skip( ui16DataSize))) { goto Exit; } } Exit: if( puiDataSize) { *puiDataSize = ui16DataSize; } return( rc); } /**************************************************************************** Desc: Reads a large binary token from the stream. The token is tagged with a length. ****************************************************************************/ RCODE FCS_DIS::readLargeBinary( F_Pool * pPool, FLMBYTE ** ppValue, FLMUINT * puiDataSize) { FLMUINT32 ui32DataSize; RCODE rc = FERR_OK; if( RC_BAD( rc = readUInt( &ui32DataSize))) { goto Exit; } if( pPool) { // If the data size is non-zero, allocate a buffer and // read the entire binary value. if( ui32DataSize) { if( RC_BAD(rc = pPool->poolAlloc( ui32DataSize, (void **)ppValue))) { goto Exit; } if( RC_BAD( rc = read( *ppValue, ui32DataSize, NULL))) { goto Exit; } } else { *ppValue = NULL; } } else { // The application is not interested in the value. Just skip the // to the end of the value. if( RC_BAD( rc = skip( ui32DataSize))) { goto Exit; } } Exit: if( puiDataSize) { *puiDataSize = (FLMUINT)ui32DataSize; } return( rc); } /**************************************************************************** Desc: Reads a UTF-8 string from the stream. ****************************************************************************/ RCODE FCS_DIS::readUTF( F_Pool * pPool, FLMUNICODE ** ppValue) { FLMBYTE ucByte1; FLMBYTE ucByte2; FLMBYTE ucByte3; FLMBYTE ucLoByte; FLMBYTE ucHiByte; FLMUINT16 ui16UTFLen; FLMUINT uiOffset = 0; RCODE rc = FERR_OK; // Read the data. if( RC_BAD( rc = readUShort( &ui16UTFLen))) { goto Exit; } // Check the size of the UTF string. FLAIM does not support // strings that are larger than 32K characters. if( ui16UTFLen >= 32767) { rc = RC_SET( FERR_MEM); goto Exit; } // Allocate space for the string. if( pPool) { if( RC_BAD( rc = pPool->poolAlloc( (FLMUINT)((FLMUINT)sizeof( FLMUNICODE) * (FLMUINT)(ui16UTFLen + 1)), (void **)ppValue))) { goto Exit; } } else if( ppValue) { *ppValue = NULL; } while( ui16UTFLen) { // Read and decode the bytes. if( RC_BAD( rc = read( &ucByte1, 1, NULL))) { goto Exit; } if( (ucByte1 & 0xC0) != 0xC0) { ucHiByte = 0; ucLoByte = ucByte1; } else { if( RC_BAD( rc = read( &ucByte2, 1, NULL))) { goto Exit; } if( (ucByte1 & 0xE0) == 0xE0) { if( RC_BAD( rc = read( &ucByte3, 1, NULL))) { goto Exit; } ucHiByte = (FLMBYTE)(((ucByte1 & 0x0F) << 4) | ((ucByte2 & 0x3C) >> 2)); ucLoByte = (FLMBYTE)(((ucByte2 & 0x03) << 6) | (ucByte3 & 0x3F)); } else { ucHiByte = (FLMBYTE)(((ucByte1 & 0x1C) >> 2)); ucLoByte = (FLMBYTE)(((ucByte1 & 0x03) << 6) | (ucByte2 & 0x3F)); } } if( pPool) { (*ppValue)[ uiOffset] = (FLMUNICODE)(((((FLMUNICODE)(ucHiByte)) << 8) | ((FLMUNICODE)(ucLoByte)))); } uiOffset++; ui16UTFLen--; } if( pPool) { (*ppValue)[ uiOffset] = 0; } Exit: return( rc); } /**************************************************************************** Desc: Reads an Hierarchical Tagged Data record from the stream. ****************************************************************************/ RCODE FCS_DIS::readHTD( F_Pool * pPool, FLMUINT uiContainer, FLMUINT uiDrn, NODE ** ppNode, FlmRecord ** ppRecord) { FLMBYTE ucType; FLMBYTE ucLevel = 0; FLMBYTE ucPrevLevel = 0; FLMBYTE ucDescriptor; FLMBYTE ucFlags; FLMUINT16 ui16Tag; FLMBOOL bHasValue; FLMBOOL bChild; FLMBOOL bSibling; FLMBOOL bLeftTruncated; FLMBOOL bRightTruncated; NODE * pRoot = NULL; NODE * pNode = NULL; NODE * pPrevNode = NULL; void * pField = NULL; void * pvMark = NULL; RCODE rc = FERR_OK; if( pPool) { pvMark = pPool->poolMark(); } for( ;;) { // Reset variables. bChild = FALSE; bSibling = FALSE; // Read the attribute's tag number. if( RC_BAD( rc = readUShort( &ui16Tag))) { goto Exit; } // A tag number of 0 indicates that the end of the HTD data // stream has been reached. if( !ui16Tag) { break; } // Read the attribute's descriptor. if( RC_BAD(rc = read( &ucDescriptor, 1, NULL))) { goto Exit; } // Set the flag indicating whether or not the // attribute has a value. bHasValue = (FLMBOOL)((ucDescriptor & HTD_HAS_VALUE_FLAG) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); // Set the value type. ucType = (FLMBYTE)((ucDescriptor & HTD_VALUE_TYPE_MASK)); // Get the attribute's level. switch( (ucDescriptor & HTD_LEVEL_MASK) >> HTD_LEVEL_POS) { case HTD_LEVEL_SIBLING: { bSibling = TRUE; ucLevel = ucPrevLevel; break; } case HTD_LEVEL_CHILD: { if( ucLevel < 0xFF) { bChild = TRUE; ucLevel = (FLMBYTE)(ucPrevLevel + 1); } else { rc = RC_SET( FERR_BAD_FIELD_LEVEL); goto Exit; } break; } case HTD_LEVEL_BACK: { if( ucLevel > 0) { ucLevel = (FLMBYTE)(ucPrevLevel - 1); } else { rc = RC_SET( FERR_BAD_FIELD_LEVEL); goto Exit; } break; } case HTD_LEVEL_BACK_X: { FLMBYTE ucLevelsBack; if( RC_BAD(rc = read( &ucLevelsBack, 1, NULL))) { goto Exit; } if( ucPrevLevel >= ucLevelsBack) { ucLevel = (FLMBYTE)(ucPrevLevel - ucLevelsBack); } else { rc = RC_SET( FERR_BAD_FIELD_LEVEL); goto Exit; } break; } } // Allocate the record object if( ppRecord && ucLevel == 0) { if( *ppRecord) { if( (*ppRecord)->isReadOnly() || (*ppRecord)->getRefCount() > 1) { (*ppRecord)->Release(); if( (*ppRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else { // Reuse the existing FlmRecord object. (*ppRecord)->clear(); } } else { if( (*ppRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } (*ppRecord)->setContainerID( uiContainer); (*ppRecord)->setID( uiDrn); } // Allocate the attribute. if( pPool && ppNode) { pNode = GedNodeMake( pPool, ui16Tag, &rc); if( RC_BAD( rc)) { goto Exit; } } bLeftTruncated = FALSE; bRightTruncated = FALSE; // Read the attribute's value. switch( ucType) { case HTD_TYPE_UNICODE: { FLMUNICODE * pUTF; if( pNode) { GedValTypeSet( pNode, FLM_TEXT_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_TEXT_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read UNICODE text in UTF-8 format. if( pPool) { if( RC_BAD( rc = readUTF( pPool, &pUTF))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutUNICODE( pPool, pNode, pUTF))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setUnicode( pField, pUTF))) { goto Exit; } } } else { if( RC_BAD( rc = readUTF( NULL, NULL))) { goto Exit; } } break; } case HTD_TYPE_UINT: { FLMUINT32 ui32Value; if( pNode) { GedValTypeSet( pNode, FLM_NUMBER_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_NUMBER_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read an unsigned 32-bit integer. if( RC_BAD( rc = readUInt( &ui32Value))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutUINT( pPool, pNode, ui32Value))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setUINT( pField, ui32Value))) { goto Exit; } } break; } case HTD_TYPE_UINT64: { FLMUINT64 ui64Value; if( pNode) { GedValTypeSet( pNode, FLM_NUMBER_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_NUMBER_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read an unsigned 64-bit integer. if( RC_BAD( rc = readUInt64( &ui64Value))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutUINT64( pPool, pNode, ui64Value))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setUINT64( pField, ui64Value))) { goto Exit; } } break; } case HTD_TYPE_INT: { FLMINT32 i32Value; if( pNode) { GedValTypeSet( pNode, FLM_NUMBER_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_NUMBER_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read a signed 32-bit integer. if( RC_BAD( rc = readInt( &i32Value))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutINT( pPool, pNode, i32Value))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setINT( pField, i32Value))) { goto Exit; } } break; } case HTD_TYPE_INT64: { FLMINT64 i64Value; if( pNode) { GedValTypeSet( pNode, FLM_NUMBER_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_NUMBER_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read a signed 64-bit integer. if( RC_BAD( rc = readInt64( &i64Value))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutINT64( pPool, pNode, i64Value))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setINT64( pField, i64Value))) { goto Exit; } } break; } case HTD_TYPE_CONTEXT: { FLMUINT32 ui32Value; if( pNode) { GedValTypeSet( pNode, FLM_CONTEXT_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_CONTEXT_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read an unsigned 32-bit integer. if( RC_BAD( rc = readUInt( &ui32Value))) { goto Exit; } if( pNode) { if( RC_BAD( rc = GedPutRecPtr( pPool, pNode, ui32Value))) { goto Exit; } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setRecPointer( pField, ui32Value))) { goto Exit; } } break; } case HTD_TYPE_BINARY: { FLMUINT16 ui16DataSize; FLMBYTE * pucData = NULL; if( pNode) { GedValTypeSet( pNode, FLM_BINARY_TYPE); } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, FLM_BINARY_TYPE, &pField))) { goto Exit; } } if( !bHasValue) { break; } // Read a binary data stream. if( RC_BAD( rc = readUShort( &ui16DataSize))) { goto Exit; } if( pPool) { if( pNode) { if( (pucData = (FLMBYTE *)GedAllocSpace( pPool, pNode, FLM_BINARY_TYPE, ui16DataSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else if( ppRecord) { if( RC_BAD(rc = (*ppRecord)->allocStorageSpace( pField, FLM_BINARY_TYPE, ui16DataSize, 0, 0, 0, &pucData, NULL))) { goto Exit; } } if( RC_BAD( rc = read( pucData, ui16DataSize, NULL))) { goto Exit; } if( pNode) { if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setBinary( pField, pucData, ui16DataSize))) { goto Exit; } } } } else { if( RC_BAD( rc = skip( ui16DataSize))) { goto Exit; } } break; } case HTD_TYPE_DATE: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } case HTD_TYPE_GEDCOM: { FLMBYTE ucGedType; FLMUINT16 ui16DataSize; FLMBYTE * pucData = NULL; // Read the GEDCOM data type and flags if( RC_BAD( rc = read( &ucGedType, 1, NULL))) { goto Exit; } ucFlags = ucGedType & 0xF0; ucGedType &= 0x0F; if( ucFlags & 0x10) { bLeftTruncated = TRUE; } if( ucFlags & 0x20) { bRightTruncated = TRUE; } if( ucGedType != FLM_TEXT_TYPE && ucGedType != FLM_NUMBER_TYPE && ucGedType != FLM_BINARY_TYPE && ucGedType != FLM_BLOB_TYPE && ucGedType != FLM_CONTEXT_TYPE) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } if( pNode) { GedValTypeSet( pNode, ucGedType); if( bLeftTruncated) { GedSetLeftTruncated( pNode); } if( bRightTruncated) { GedSetRightTruncated( pNode); } } if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->insertLast( ucLevel, ui16Tag, ucGedType, &pField))) { goto Exit; } if( bLeftTruncated) { (*ppRecord)->setLeftTruncated( pField, TRUE); } if( bRightTruncated) { (*ppRecord)->setRightTruncated( pField, TRUE); } } if( !bHasValue) { break; } // Read the data size. if( RC_BAD( rc = readUShort( &ui16DataSize))) { goto Exit; } // Read the data value. if( pPool) { if( pNode) { if( (pucData = (FLMBYTE *)GedAllocSpace( pPool, pNode, ucGedType, ui16DataSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else if( ppRecord) { if (RC_BAD( rc = (*ppRecord)->allocStorageSpace( pField, ucGedType, ui16DataSize, 0, 0, 0, &pucData, NULL))) { goto Exit; } } if( RC_BAD( rc = read( pucData, ui16DataSize, NULL))) { goto Exit; } if( pNode) { if( ppRecord) { if( RC_BAD( rc = (*ppRecord)->setBinary( pField, pucData, ui16DataSize))) { goto Exit; } } } } else { if( RC_BAD( rc = skip( ui16DataSize))) { goto Exit; } } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } // Set the truncation flags if( ucType != HTD_TYPE_GEDCOM) { if( pNode) { if( bLeftTruncated) { GedSetLeftTruncated( pNode); } if( bRightTruncated) { GedSetRightTruncated( pNode); } } else if( pField) { if( bLeftTruncated) { (*ppRecord)->setLeftTruncated( pField, TRUE); } if( bRightTruncated) { (*ppRecord)->setRightTruncated( pField, TRUE); } } } // Graft the attribute into the tree. if( pNode) { if( pRoot == NULL) { pRoot = pNode; } else { if( bSibling) { pPrevNode->next = pNode; pNode->prior = pPrevNode; GedNodeLevelSet( pNode, GedNodeLevel( pPrevNode)); } else if( bChild) { pPrevNode->next = pNode; pNode->prior = pPrevNode; GedNodeLevelSet( pNode, GedNodeLevel( pPrevNode) + 1); } else { pPrevNode->next = pNode; pNode->prior = pPrevNode; GedNodeLevelSet( pNode, ucLevel); } } } ucPrevLevel = ucLevel; pPrevNode = pNode; // Reset the pool if a GEDCOM record is not going to be returned. if( pPool && !ppNode) { pPool->poolReset( pvMark); } } Exit: if( RC_OK( rc)) { if( ppNode) { *ppNode = pRoot; } } else { if( ppRecord && *ppRecord) { (*ppRecord)->Release(); } } if( pPool && !ppNode) { pPool->poolReset( pvMark); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FCS_DOS::FCS_DOS( void) { m_pOStream = NULL; m_uiBOffset = 0; m_tmpPool.poolInit( 512); m_bSetupCalled = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ FCS_DOS::~FCS_DOS( void) { if( m_bSetupCalled) { (void)close(); } m_tmpPool.poolFree(); } /**************************************************************************** Desc: Writes a specified number of bytes from a buffer to the output stream. ****************************************************************************/ RCODE FCS_DOS::write( FLMBYTE * pucData, FLMUINT uiLength) { RCODE rc = FERR_OK; // Verify that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Write the data. Retry_Write: if( FCS_DOS_BUFFER_SIZE - m_uiBOffset >= uiLength) { f_memcpy( &(m_pucBuffer[ m_uiBOffset]), pucData, uiLength); m_uiBOffset += uiLength; } else { if( m_uiBOffset > 0) { if( RC_BAD( rc = flush())) { goto Exit; } } if( uiLength <= FCS_DOS_BUFFER_SIZE) { goto Retry_Write; } if( RC_BAD( rc = m_pOStream->write( pucData, uiLength))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Writes a UNICODE string to the stream in the UTF-8 format. ****************************************************************************/ RCODE FCS_DOS::writeUTF( FLMUNICODE * puzValue) { FLMUINT uiUTFLen; FLMUNICODE * puzTmp; RCODE rc = FERR_OK; // Verify that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Verify pValue is valid. flmAssert( puzValue != NULL); // Determine the size of the string. uiUTFLen = 0; puzTmp = puzValue; while( *puzTmp) { uiUTFLen++; puzTmp++; } if( RC_BAD( rc = writeUShort( (FLMUINT16)uiUTFLen))) { goto Exit; } puzTmp = puzValue; while( *puzTmp) { if( *puzTmp <= 0x007F) { if( RC_BAD( rc = writeByte( (FLMBYTE)(*puzTmp)))) { goto Exit; } } else if( *puzTmp >= 0x0080 && *puzTmp <= 0x07FF) { if( RC_BAD( rc = writeUShort((FLMUINT16) ((((FLMUINT16)(0xC0 | (FLMBYTE)((*puzTmp & 0x07C0) >> 6))) << 8) | (FLMUINT16)(0x80 | (FLMBYTE)(*puzTmp & 0x003F)))))) { goto Exit; } } else { if( RC_BAD( rc = writeUShort((FLMUINT16) ((((FLMUINT16)(0xE0 | (FLMBYTE)((*puzTmp & 0xF000) >> 12))) << 8) | (FLMUINT16)(0x80 | (FLMBYTE)((*puzTmp & 0x0FC0) >> 6)))))) { goto Exit; } if( RC_BAD( rc = writeByte( (0x80 | (FLMBYTE)(*puzTmp & 0x003F))))) { goto Exit; } } puzTmp++; } Exit: return( rc); } /**************************************************************************** Desc: Writes a binary token (including length) to the stream. ****************************************************************************/ RCODE FCS_DOS::writeBinary( FLMBYTE * pucValue, FLMUINT uiSize) { RCODE rc = FERR_OK; flmAssert( uiSize <= 0x0000FFFF); if( RC_BAD( rc = writeUShort( (FLMUINT16)uiSize))) { goto Exit; } if( uiSize) { if( RC_BAD( rc = write( pucValue, uiSize))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Writes a large binary token (including length) to the stream. ****************************************************************************/ RCODE FCS_DOS::writeLargeBinary( FLMBYTE * pucValue, FLMUINT uiSize) { RCODE rc = FERR_OK; if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiSize))) { goto Exit; } if( uiSize) { if( RC_BAD( rc = write( pucValue, uiSize))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Writes a Hierarchical Tagged Data record to the stream. ****************************************************************************/ RCODE FCS_DOS::writeHTD( NODE * pHTD, FlmRecord * pRecord, FLMBOOL bSendForest, FLMBOOL bSendAsGedcom) { FLMUINT uiPrevLevel = 0; FLMUINT uiLevelsBack = 0; FLMUINT uiDescriptor = 0; FLMUINT uiCurLevel = 0; FLMUINT uiCurValType = 0; FLMUINT uiCurDataLen = 0; FLMBOOL bLeftTruncated; FLMBOOL bRightTruncated; FLMBYTE * pucCurData = NULL; FLMBYTE pucTmpBuf[ 32]; void * pvMark = m_tmpPool.poolMark(); NODE * pCurNode = NULL; void * pCurField = NULL; RCODE rc = FERR_OK; // Verify that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Set the current node or field if( pHTD) { pCurNode = pHTD; } else { pCurField = pRecord->root(); } while( pCurNode || pCurField) { // See if we are done sending the tree/forest. if( pCurNode) { if( !bSendForest && (pCurNode != pHTD) && (GedNodeLevel( pCurNode) == GedNodeLevel( pHTD))) { break; } } // Output the attribute's tag number. if( pCurNode) { f_UINT16ToBigEndian( (FLMUINT16)GedTagNum( pCurNode), pucTmpBuf); } else if( pCurField) { f_UINT16ToBigEndian( (FLMUINT16)pRecord->getFieldID( pCurField), pucTmpBuf); } if( RC_BAD( rc = write( pucTmpBuf, 2))) { goto Exit; } // Setup the attribute's descriptor. uiDescriptor = 0; uiLevelsBack = 0; if( pCurNode) { uiCurLevel = GedNodeLevel( pCurNode); } else { uiCurLevel = pRecord->getLevel( pCurField); } if( uiCurLevel == uiPrevLevel) { (void)(uiDescriptor |= (HTD_LEVEL_SIBLING << HTD_LEVEL_POS)); } else if( uiCurLevel == uiPrevLevel + 1) { uiDescriptor |= (HTD_LEVEL_CHILD << HTD_LEVEL_POS); } else if( uiCurLevel == uiPrevLevel - 1) { uiDescriptor |= (HTD_LEVEL_BACK << HTD_LEVEL_POS); } else if( uiCurLevel < uiPrevLevel) { uiDescriptor |= (HTD_LEVEL_BACK_X << HTD_LEVEL_POS); uiLevelsBack = uiPrevLevel - uiCurLevel; } else { flmAssert( 0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( pCurNode) { uiCurDataLen = GedValLen( pCurNode); uiCurValType = GedValType( pCurNode) & 0x0F; bLeftTruncated = GedIsLeftTruncated( pCurNode); bRightTruncated = GedIsRightTruncated( pCurNode); pucCurData = (FLMBYTE *)GedValPtr( pCurNode); } else { uiCurDataLen = pRecord->getDataLength( pCurField); uiCurValType = (FLMUINT)pRecord->getDataType( pCurField); bLeftTruncated = pRecord->isLeftTruncated( pCurField); bRightTruncated = pRecord->isRightTruncated( pCurField); pucCurData = (FLMBYTE *)(pRecord->getDataPtr( pCurField)); } if( uiCurDataLen) { uiDescriptor |= HTD_HAS_VALUE_FLAG; } if( bSendAsGedcom) { uiDescriptor |= HTD_TYPE_GEDCOM; } else { switch( uiCurValType) { case FLM_TEXT_TYPE: { uiDescriptor |= HTD_TYPE_UNICODE; break; } case FLM_NUMBER_TYPE: { // To save conversion time, cheat to determine if // the number is negative. if( ((*pucCurData & 0xF0) == 0xB0)) { FLMINT64 i64Value; if( (pCurNode && RC_BAD( rc = GedGetINT64( pCurNode, &i64Value))) || (pCurField && RC_BAD( rc = pRecord->getINT64( pCurField, &i64Value)))) { goto Exit; } if (i64Value >= (FLMINT64)(FLM_MIN_INT32) && i64Value <= (FLMINT64)(FLM_MAX_INT32)) { uiDescriptor |= HTD_TYPE_INT; } else { uiDescriptor |= HTD_TYPE_INT64; } } else { FLMUINT64 ui64Value; if( (pCurNode && RC_BAD( rc = GedGetUINT64( pCurNode, &ui64Value))) || (pCurField && RC_BAD( rc = pRecord->getUINT64( pCurField, &ui64Value)))) { goto Exit; } if (ui64Value <= (FLMUINT64)(FLM_MAX_UINT32)) { uiDescriptor |= HTD_TYPE_UINT; } else { uiDescriptor |= HTD_TYPE_UINT64; } } break; } case FLM_CONTEXT_TYPE: { uiDescriptor |= HTD_TYPE_CONTEXT; break; } case FLM_BINARY_TYPE: { uiDescriptor |= HTD_TYPE_BINARY; break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } } // Output the attribute's descriptor. pucTmpBuf[ 0] = (FLMBYTE)uiDescriptor; if( RC_BAD( rc = write( pucTmpBuf, 1))) { goto Exit; } // Output the "levels back" value (if available). if( uiLevelsBack) { flmAssert( uiLevelsBack <= 0xFF); pucTmpBuf[ 0] = (FLMBYTE)uiLevelsBack; if( RC_BAD( rc = write( pucTmpBuf, 1))) { goto Exit; } } // Output the attribute's value. if( bSendAsGedcom) { // Output the GEDCOM data type and flags pucTmpBuf[ 0] = (FLMBYTE)uiCurValType; if( bLeftTruncated) { pucTmpBuf[ 0] |= 0x10; } if( bRightTruncated) { pucTmpBuf[ 0] |= 0x20; } if( RC_BAD( rc = write( pucTmpBuf, 1))) { goto Exit; } if( uiCurDataLen) { // Output the data size. flmAssert( uiCurDataLen <= 0x0000FFFF); f_UINT16ToBigEndian( (FLMUINT16)uiCurDataLen, pucTmpBuf); if( RC_BAD( rc = write( pucTmpBuf, 2))) { goto Exit; } // Send the data. if( RC_BAD( rc = write( pucCurData, uiCurDataLen))) { goto Exit; } } } else { // Send the value. switch( uiCurValType) { case FLM_TEXT_TYPE: { // Extract the value. if( uiCurDataLen) { FLMUINT uiBufSize; FLMUNICODE * puzValue; // Reset the temporary pool. m_tmpPool.poolReset( pvMark); if( uiCurDataLen <= 32751) { // Allocate a buffer that is twice the size of the // attribute's value length. This is necessary because the // UNICODE conversion will may double the size of the // attribute's value. A "safety" zone of 32 bytes is added // to the buffer size to allow for strings that may require // more than 2x the attribute's size and to account for // null-termination bytes. uiBufSize = (2 * uiCurDataLen) + 32; } else { // Allocate a full 64K. uiBufSize = 65535; } if( RC_BAD( rc = m_tmpPool.poolAlloc( uiBufSize, (void **)&puzValue))) { goto Exit; } // Extract UNICODE from the attribute. if( (pCurNode && RC_BAD( rc = GedGetUNICODE( pCurNode, puzValue, &uiBufSize))) || (pCurField && RC_BAD( rc = pRecord->getUnicode( pCurField, puzValue, &uiBufSize)))) { if( rc == FERR_CONV_DEST_OVERFLOW) { // Since we did not correctly guess the buffer size, // try again. This time, take the slow (but safe) // approach of calculating the size of the UNICODE string. if( (pCurNode && RC_BAD( rc = GedGetUNICODE( pCurNode, NULL, &uiBufSize))) || (pCurField && RC_BAD( rc = pRecord->getUnicodeLength( pCurField, &uiBufSize)))) { goto Exit; } // Add two bytes to account for null-termination. uiBufSize += 2; // Reset the pool to clear the prior allocation. m_tmpPool.poolReset( pvMark); // Allocate the new buffer. if( RC_BAD( rc = m_tmpPool.poolAlloc( uiBufSize, (void **)&puzValue))) { goto Exit; } // Extract the UNICODE string. if( (pCurNode && RC_BAD( rc = GedGetUNICODE( pCurNode, puzValue, &uiBufSize))) || (pCurField && RC_BAD( rc = pRecord->getUnicode( pCurField, puzValue, &uiBufSize)))) { goto Exit; } } else { goto Exit; } } // Write the attribute's value. if( RC_BAD( rc = writeUTF( puzValue))) { goto Exit; } } break; } case FLM_NUMBER_TYPE: { if( uiCurDataLen) { if( uiDescriptor & HTD_TYPE_INT64) { // Since the number is negative, extract and send it // as a signed 64-bit value. FLMINT64 i64Value; if( (pCurNode && RC_BAD( rc = GedGetINT64( pCurNode, &i64Value))) || (pCurField && RC_BAD( rc = pRecord->getINT64( pCurField, &i64Value)))) { goto Exit; } // Write the value. if( RC_BAD( rc = writeInt64( i64Value))) { goto Exit; } } else if( uiDescriptor & HTD_TYPE_INT) { // Since the number is negative, extract and send it // as a signed 32-bit value. FLMINT32 i32Value; if( (pCurNode && RC_BAD( rc = GedGetINT32( pCurNode, &i32Value))) || (pCurField && RC_BAD( rc = pRecord->getINT32( pCurField, &i32Value)))) { goto Exit; } // Write the value. if( RC_BAD( rc = writeInt32( i32Value))) { goto Exit; } } else if( uiDescriptor & HTD_TYPE_UINT64) { // The number is non-negative 64 bit FLMUINT64 ui64Value; if( (pCurNode && RC_BAD( rc = GedGetUINT64( pCurNode, &ui64Value))) || (pCurField && RC_BAD( rc = pRecord->getUINT64( pCurField, &ui64Value)))) { goto Exit; } // Write the value. if( RC_BAD( rc = writeUInt64( ui64Value))) { goto Exit; } } else { flmAssert( uiDescriptor & HTD_TYPE_UINT); // The number is non-negative 32 bit FLMUINT32 ui32Value; if( (pCurNode && RC_BAD( rc = GedGetUINT32( pCurNode, &ui32Value))) || (pCurField && RC_BAD( rc = pRecord->getUINT32( pCurField, &ui32Value)))) { goto Exit; } // Write the value. if( RC_BAD( rc = writeUInt32( ui32Value))) { goto Exit; } } } break; } case FLM_CONTEXT_TYPE: { // Extract the value. if( uiCurDataLen) { // The context node has a DRN value associated with // it. Send the value as an unsigned 32-bit number. FLMUINT uiDrn; if( (pCurNode && RC_BAD( rc = GedGetRecPtr( pCurNode, &uiDrn))) || (pCurField && RC_BAD( rc = pRecord->getUINT( pCurField, &uiDrn)))) { goto Exit; } if( RC_BAD( rc = writeUInt32( (FLMUINT32)uiDrn))) { goto Exit; } } break; } case FLM_BINARY_TYPE: { // Extract the value. if( uiCurDataLen) { if( RC_BAD( rc = writeUShort( (FLMUINT16)uiCurDataLen))) { goto Exit; } if( RC_BAD( rc = write( pucCurData, uiCurDataLen))) { goto Exit; } } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } } uiPrevLevel = uiCurLevel; if( pCurNode) { pCurNode = GedNodeNext( pCurNode); } else { pCurField = pRecord->next( pCurField); } } // Write a zero tag to indicate the end of the transmission. if( RC_BAD( rc = writeUShort( 0))) { goto Exit; } Exit: m_tmpPool.poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Flushes any pending data and closes the stream. ****************************************************************************/ RCODE FCS_DOS::close( void) { RCODE rc = FERR_OK; // Verify that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Flush and terminate any pending message. if( RC_BAD( rc = endMessage())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Flushes pending data. ****************************************************************************/ RCODE FCS_DOS::flush( void) { // Verify that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Flush the output buffer. if( m_uiBOffset > 0) { m_pOStream->write( m_pucBuffer, m_uiBOffset); m_uiBOffset = 0; } // Flush the parent stream. return( m_pOStream->flush()); } /**************************************************************************** Desc: Flushes and terminates the current parent stream message ****************************************************************************/ RCODE FCS_DOS::endMessage( void) { RCODE rc = FERR_OK; // Verify that Setup has been called. flmAssert( m_bSetupCalled == TRUE); if( !m_pOStream) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Flush any pending data. if( RC_BAD( rc = flush())) { goto Exit; } // Terminate the message. if( RC_BAD( rc = m_pOStream->endMessage())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ FCS_FIS::FCS_FIS( void) { m_pFileHdl = NULL; m_pucBufPos = NULL; m_pucBuffer = NULL; m_uiFileOffset = 0; m_uiBlockSize = 0; m_uiBlockEnd = 0; } /**************************************************************************** Desc: *****************************************************************************/ FCS_FIS::~FCS_FIS( void) { if( m_pFileHdl) { m_pFileHdl->Release(); } if( m_pucBuffer) { f_free( &m_pucBuffer); } } /**************************************************************************** Desc: Configures the input stream for use *****************************************************************************/ RCODE FCS_FIS::setup( const char * pszFilePath, FLMUINT uiBlockSize) { RCODE rc = FERR_OK; flmAssert( uiBlockSize); if( RC_BAD( rc = close())) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszFilePath, FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &m_pFileHdl))) { goto Exit; } m_uiBlockSize = uiBlockSize; if( RC_BAD( rc = f_alloc( m_uiBlockSize, &m_pucBuffer))) { goto Exit; } m_pucBufPos = m_pucBuffer; Exit: return( rc); } /**************************************************************************** Desc: Closes the input stream and frees any resources *****************************************************************************/ RCODE FCS_FIS::close( void) { if( m_pFileHdl) { m_pFileHdl->Release(); m_pFileHdl = NULL; } if( m_pucBuffer) { f_free( &m_pucBuffer); } return( FERR_OK); } /**************************************************************************** Desc: Reads the requested amount of data from the stream. *****************************************************************************/ RCODE FCS_FIS::read( FLMBYTE * pucData, FLMUINT uiLength, FLMUINT * puiBytesRead) { RCODE rc = FERR_OK; FLMUINT uiBytesRead = 0; FLMUINT uiMaxSize; if( puiBytesRead) { *puiBytesRead = 0; } if( !m_pFileHdl) { rc = RC_SET( FERR_READING_FILE); goto Exit; } while( uiLength) { uiMaxSize = m_uiBlockEnd - (FLMUINT)(m_pucBufPos - m_pucBuffer); if( !uiMaxSize) { if( RC_BAD( rc = getNextPacket())) { goto Exit; } } else if( uiLength <= uiMaxSize) { f_memcpy( pucData, m_pucBufPos, uiLength); m_pucBufPos += uiLength; uiBytesRead += uiLength; uiLength = 0; } else { f_memcpy( pucData, m_pucBufPos, uiMaxSize); m_pucBufPos += uiMaxSize; pucData += uiMaxSize; uiLength -= uiMaxSize; uiBytesRead += uiMaxSize; } } Exit: if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /**************************************************************************** Desc: Flushes any pending data. *****************************************************************************/ RCODE FCS_FIS::flush( void) { return( FERR_OK); } /**************************************************************************** Desc: Flushes any pending data. *****************************************************************************/ RCODE FCS_FIS::endMessage( void) { return( FERR_OK); } /**************************************************************************** Desc: Returns TRUE if the stream is open *****************************************************************************/ FLMBOOL FCS_FIS::isOpen( void) { return( TRUE); } /**************************************************************************** Desc: Reads the next block from the file *****************************************************************************/ RCODE FCS_FIS::getNextPacket( void) { RCODE rc = FERR_OK; if( RC_BAD( rc = m_pFileHdl->read( m_uiFileOffset, m_uiBlockSize, m_pucBuffer, &m_uiBlockEnd))) { if( rc == FERR_IO_END_OF_FILE) { if( !m_uiBlockEnd) { goto Exit; } else { rc = FERR_OK; } } } m_uiFileOffset += m_uiBlockEnd; m_pucBufPos = m_pucBuffer; Exit: return( rc); } /**************************************************************************** Desc: Converts a UNICODE string consisting of 7-bit ASCII characters to a native string. *****************************************************************************/ RCODE fcsConvertUnicodeToNative( F_Pool * pPool, const FLMUNICODE * puzUnicode, char ** ppucNative) { RCODE rc = FERR_OK; char * pucDest = NULL; FLMUINT uiCount; uiCount = 0; while( puzUnicode[ uiCount]) { if( puzUnicode[ uiCount] > 0x007F) { rc = RC_SET( FERR_CONV_ILLEGAL); goto Exit; } uiCount++; } if( RC_BAD( rc = pPool->poolAlloc( uiCount + 1, (void **)&pucDest))) { goto Exit; } uiCount = 0; while( puzUnicode[ uiCount]) { pucDest[ uiCount] = f_tonative( (FLMBYTE)puzUnicode[ uiCount]); uiCount++; } pucDest[ uiCount] = '\0'; Exit: *ppucNative = pucDest; return( rc); } /**************************************************************************** Desc: Converts a native string to a double-byte UNICODE string. *****************************************************************************/ RCODE fcsConvertNativeToUnicode( F_Pool * pPool, const char * pszNative, FLMUNICODE ** ppuzUnicode) { RCODE rc = FERR_OK; FLMUNICODE * puzDest; FLMUINT uiCount; uiCount = f_strlen( pszNative); if( RC_BAD( rc = pPool->poolAlloc( uiCount + 1, (void **)&puzDest))) { goto Exit; } uiCount = 0; while( pszNative[ uiCount]) { puzDest[ uiCount] = (FLMUNICODE)f_toascii( pszNative[ uiCount]); uiCount++; } puzDest[ uiCount] = 0; Exit: *ppuzUnicode = puzDest; return( rc); } /**************************************************************************** Desc: Initializes members of a CREATE_OPTS structure to their default values *****************************************************************************/ void fcsInitCreateOpts( CREATE_OPTS * pCreateOptsRV) { f_memset( pCreateOptsRV, 0, sizeof( CREATE_OPTS)); pCreateOptsRV->uiBlockSize = DEFAULT_BLKSIZ; pCreateOptsRV->uiMinRflFileSize = DEFAULT_MIN_RFL_FILE_SIZE; pCreateOptsRV->uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE; pCreateOptsRV->bKeepRflFiles = DEFAULT_KEEP_RFL_FILES_FLAG; pCreateOptsRV->bLogAbortedTransToRfl = DEFAULT_LOG_ABORTED_TRANS_FLAG; pCreateOptsRV->uiDefaultLanguage = DEFAULT_LANG; pCreateOptsRV->uiVersionNum = FLM_CUR_FILE_FORMAT_VER_NUM; } /**************************************************************************** Desc: Converts a CHECKPOINT_INFO structure to an HTD tree *****************************************************************************/ RCODE fcsBuildCheckpointInfo( CHECKPOINT_INFO * pChkptInfo, F_Pool * pPool, NODE ** ppTree) { NODE * pRootNd = NULL; void * pMark = pPool->poolMark(); FLMUINT uiTmp; RCODE rc = FERR_OK; *ppTree = NULL; // Build the root node of the tree. if( (pRootNd = GedNodeMake( pPool, FCS_CPI_CONTEXT, &rc)) == NULL) { goto Exit; } // Add fields to the tree. if( pChkptInfo->bRunning) { uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_RUNNING, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiRunningTime) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_START_TIME, (void *)&pChkptInfo->uiRunningTime, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->bForcingCheckpoint) { uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_FORCING_CP, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiForceCheckpointRunningTime) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_FORCE_CP_START_TIME, (void *)&pChkptInfo->uiForceCheckpointRunningTime, 4, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->iForceCheckpointReason) { uiTmp = pChkptInfo->iForceCheckpointReason; if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_FORCE_CP_REASON, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->bWritingDataBlocks) { uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_WRITING_DATA_BLOCKS, (void *)&uiTmp, 4, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiLogBlocksWritten) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_LOG_BLOCKS_WRITTEN, (void *)&pChkptInfo->uiLogBlocksWritten, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiDataBlocksWritten) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_DATA_BLOCKS_WRITTEN, (void *)&pChkptInfo->uiDataBlocksWritten, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiDirtyCacheBytes) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_DIRTY_CACHE_BYTES, (void *)&pChkptInfo->uiDirtyCacheBytes, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiBlockSize) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_BLOCK_SIZE, (void *)&pChkptInfo->uiBlockSize, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pChkptInfo->uiWaitTruncateTime) { if( RC_BAD( rc = gedAddField( pPool, pRootNd, FCS_CPI_WAIT_TRUNC_TIME, (void *)&pChkptInfo->uiWaitTruncateTime, 0, FLM_NUMBER_TYPE))) { goto Exit; } } *ppTree = pRootNd; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Converts a F_LOCK_USER structure (or list of structures) to an HTD tree *****************************************************************************/ RCODE fcsBuildLockUser( F_LOCK_USER * pLockUser, FLMBOOL bList, F_Pool * pPool, NODE ** ppTree) { NODE * pRootNd = NULL; NODE * pContextNd = NULL; void * pMark = pPool->poolMark(); RCODE rc = FERR_OK; *ppTree = NULL; if( !pLockUser) { goto Exit; } // Add fields to the tree. for( ;;) { if( (pContextNd = GedNodeMake( pPool, FCS_LUSR_CONTEXT, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_LUSR_THREAD_ID, (void *)&pLockUser->uiThreadId, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_LUSR_TIME, (void *)&pLockUser->uiTime, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( pRootNd == NULL) { pRootNd = pContextNd; } else { GedSibGraft( pRootNd, pContextNd, GED_LAST); } if( !bList) { break; } pLockUser++; if( !pLockUser->uiTime) { break; } } *ppTree = pRootNd; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Converts an HTD tree to a F_LOCK_USER structure (or list of structures) *****************************************************************************/ RCODE fcsExtractLockUser( NODE * pTree, FLMBOOL bExtractAsList, void * pvLockUser) { NODE * pTmpNd; FLMUINT uiItemCount = 0; FLMUINT fieldPath[ 8]; F_LOCK_USER * pLockUser = NULL; FLMUINT uiLoop; RCODE rc = FERR_OK; if( !pTree) { if( bExtractAsList) { *((F_LOCK_USER **)pvLockUser) = NULL; } else { f_memset( (F_LOCK_USER *)pvLockUser, 0, sizeof( F_LOCK_USER)); } goto Exit; } if( bExtractAsList) { pTmpNd = pTree; while( pTmpNd != NULL) { if( GedTagNum( pTmpNd) == FCS_LUSR_CONTEXT) { uiItemCount++; } pTmpNd = pTmpNd->next; } if( RC_BAD( rc = f_alloc( sizeof( F_LOCK_USER) * (uiItemCount + 1), &pLockUser))) { goto Exit; } *((F_LOCK_USER **)pvLockUser) = pLockUser; } else { pLockUser = (F_LOCK_USER *)pvLockUser; f_memset( pLockUser, 0, sizeof( F_LOCK_USER)); uiItemCount = 1; } // Parse the tree and extract the values. for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++) { fieldPath[ 0] = FCS_LUSR_CONTEXT; fieldPath[ 1] = FCS_LUSR_THREAD_ID; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pLockUser[ uiLoop].uiThreadId); } fieldPath[ 0] = FCS_LUSR_CONTEXT; fieldPath[ 1] = FCS_LUSR_TIME; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pLockUser[ uiLoop].uiTime); } pTree = GedSibNext( pTree); } if( bExtractAsList) { f_memset( &(pLockUser[ uiItemCount]), 0, sizeof( F_LOCK_USER)); } Exit: return( rc); } /**************************************************************************** Desc: Extracts a CHECKPOINT_INFO structure from an HTD tree. *****************************************************************************/ RCODE fcsExtractCheckpointInfo( NODE * pTree, CHECKPOINT_INFO * pChkptInfo) { NODE * pTmpNd; FLMUINT fieldPath[ 8]; FLMUINT uiTmp; RCODE rc = FERR_OK; // Initialize the structure f_memset( pChkptInfo, 0, sizeof( CHECKPOINT_INFO)); // Parse the tree and extract the values. fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_RUNNING; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); pChkptInfo->bRunning = uiTmp ? TRUE : FALSE; } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_START_TIME; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiRunningTime); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_FORCING_CP; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); pChkptInfo->bForcingCheckpoint = uiTmp ? TRUE : FALSE; } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_FORCE_CP_START_TIME; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiForceCheckpointRunningTime); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_FORCE_CP_REASON; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetINT( pTmpNd, &pChkptInfo->iForceCheckpointReason); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_WRITING_DATA_BLOCKS; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); pChkptInfo->bWritingDataBlocks = uiTmp ? TRUE : FALSE; } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_LOG_BLOCKS_WRITTEN; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiLogBlocksWritten); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_DATA_BLOCKS_WRITTEN; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiDataBlocksWritten); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_DIRTY_CACHE_BYTES; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiDirtyCacheBytes); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_BLOCK_SIZE; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiBlockSize); } fieldPath[ 0] = FCS_CPI_CONTEXT; fieldPath[ 1] = FCS_CPI_WAIT_TRUNC_TIME; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pChkptInfo->uiWaitTruncateTime); } return( rc); } /**************************************************************************** Desc: Translates a FLAIM query operator to a c/s query operator *****************************************************************************/ RCODE fcsTranslateQFlmToQCSOp( QTYPES eFlmOp, FLMUINT * puiCSOp) { RCODE rc = FERR_OK; switch( eFlmOp) { case FLM_AND_OP: *puiCSOp = FCS_ITERATOR_AND_OP; break; case FLM_OR_OP: *puiCSOp = FCS_ITERATOR_OR_OP; break; case FLM_NOT_OP: *puiCSOp = FCS_ITERATOR_NOT_OP; break; case FLM_EQ_OP: *puiCSOp = FCS_ITERATOR_EQ_OP; break; case FLM_MATCH_OP: *puiCSOp = FCS_ITERATOR_MATCH_OP; break; case FLM_MATCH_BEGIN_OP: *puiCSOp = FCS_ITERATOR_MATCH_BEGIN_OP; break; case FLM_CONTAINS_OP: *puiCSOp = FCS_ITERATOR_CONTAINS_OP; break; case FLM_NE_OP: *puiCSOp = FCS_ITERATOR_NE_OP; break; case FLM_LT_OP: *puiCSOp = FCS_ITERATOR_LT_OP; break; case FLM_LE_OP: *puiCSOp = FCS_ITERATOR_LE_OP; break; case FLM_GT_OP: *puiCSOp = FCS_ITERATOR_GT_OP; break; case FLM_GE_OP: *puiCSOp = FCS_ITERATOR_GE_OP; break; case FLM_BITAND_OP: *puiCSOp = FCS_ITERATOR_BITAND_OP; break; case FLM_BITOR_OP: *puiCSOp = FCS_ITERATOR_BITOR_OP; break; case FLM_BITXOR_OP: *puiCSOp = FCS_ITERATOR_BITXOR_OP; break; case FLM_MULT_OP: *puiCSOp = FCS_ITERATOR_MULT_OP; break; case FLM_DIV_OP: *puiCSOp = FCS_ITERATOR_DIV_OP; break; case FLM_MOD_OP: *puiCSOp = FCS_ITERATOR_MOD_OP; break; case FLM_PLUS_OP: *puiCSOp = FCS_ITERATOR_PLUS_OP; break; case FLM_MINUS_OP: *puiCSOp = FCS_ITERATOR_MINUS_OP; break; case FLM_NEG_OP: *puiCSOp = FCS_ITERATOR_NEG_OP; break; case FLM_LPAREN_OP: *puiCSOp = FCS_ITERATOR_LPAREN_OP; break; case FLM_RPAREN_OP: *puiCSOp = FCS_ITERATOR_RPAREN_OP; break; default: rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } return( rc); } /**************************************************************************** Desc: Translates a FLAIM query operator to a c/s query operator *****************************************************************************/ RCODE fcsTranslateQCSToQFlmOp( FLMUINT uiCSOp, QTYPES * peFlmOp) { RCODE rc = FERR_OK; switch( uiCSOp) { case FCS_ITERATOR_AND_OP: *peFlmOp = FLM_AND_OP; break; case FCS_ITERATOR_OR_OP: *peFlmOp = FLM_OR_OP; break; case FCS_ITERATOR_NOT_OP: *peFlmOp = FLM_NOT_OP; break; case FCS_ITERATOR_EQ_OP: *peFlmOp = FLM_EQ_OP; break; case FCS_ITERATOR_MATCH_OP: *peFlmOp = FLM_MATCH_OP; break; case FCS_ITERATOR_MATCH_BEGIN_OP: *peFlmOp = FLM_MATCH_BEGIN_OP; break; case FCS_ITERATOR_CONTAINS_OP: *peFlmOp = FLM_CONTAINS_OP; break; case FCS_ITERATOR_NE_OP: *peFlmOp = FLM_NE_OP; break; case FCS_ITERATOR_LT_OP: *peFlmOp = FLM_LT_OP; break; case FCS_ITERATOR_LE_OP: *peFlmOp = FLM_LE_OP; break; case FCS_ITERATOR_GT_OP: *peFlmOp = FLM_GT_OP; break; case FCS_ITERATOR_GE_OP: *peFlmOp = FLM_GE_OP; break; case FCS_ITERATOR_BITAND_OP: *peFlmOp = FLM_BITAND_OP; break; case FCS_ITERATOR_BITOR_OP: *peFlmOp = FLM_BITOR_OP; break; case FCS_ITERATOR_BITXOR_OP: *peFlmOp = FLM_BITXOR_OP; break; case FCS_ITERATOR_MULT_OP: *peFlmOp = FLM_MULT_OP; break; case FCS_ITERATOR_DIV_OP: *peFlmOp = FLM_DIV_OP; break; case FCS_ITERATOR_MOD_OP: *peFlmOp = FLM_MOD_OP; break; case FCS_ITERATOR_PLUS_OP: *peFlmOp = FLM_PLUS_OP; break; case FCS_ITERATOR_MINUS_OP: *peFlmOp = FLM_MINUS_OP; break; case FCS_ITERATOR_NEG_OP: *peFlmOp = FLM_NEG_OP; break; case FCS_ITERATOR_LPAREN_OP: *peFlmOp = FLM_LPAREN_OP; break; case FCS_ITERATOR_RPAREN_OP: *peFlmOp = FLM_RPAREN_OP; break; default: rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } return( rc); } /**************************************************************************** Desc: Converts an FINDEX_STATUS structure to an HTD tree *****************************************************************************/ RCODE fcsBuildIndexStatus( FINDEX_STATUS * pIndexStatus, F_Pool * pPool, NODE ** ppTree) { NODE * pContextNd = NULL; void * pMark = pPool->poolMark(); FLMUINT uiTmp; RCODE rc = FERR_OK; *ppTree = NULL; if( !pIndexStatus) { goto Exit; } // Add fields to the tree. if( (pContextNd = GedNodeMake( pPool, FCS_IXSTAT_CONTEXT, &rc)) == NULL) { goto Exit; } if( pIndexStatus->uiIndexNum) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_INDEX_NUM, (void *)&pIndexStatus->uiIndexNum, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pIndexStatus->uiStartTime) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_START_TIME, (void *)&pIndexStatus->uiStartTime, 0, FLM_NUMBER_TYPE))) { goto Exit; } // Send the "auto-online" flag for backwards compatibility uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_AUTO_ONLINE, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } // Send the priority (high) for backwards compatibility uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_PRIORITY, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } // Set the suspended time field (for backwards compatibility) // if the index is suspended if( pIndexStatus->bSuspended) { f_timeGetSeconds( &uiTmp); if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_SUSPEND_TIME, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pIndexStatus->uiLastRecordIdIndexed) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_LAST_REC_INDEXED, (void *)&pIndexStatus->uiLastRecordIdIndexed, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pIndexStatus->uiKeysProcessed) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_KEYS_PROCESSED, (void *)&pIndexStatus->uiKeysProcessed, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pIndexStatus->uiRecordsProcessed) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_RECS_PROCESSED, (void *)&pIndexStatus->uiRecordsProcessed, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pIndexStatus->bSuspended) { uiTmp = (FLMUINT)pIndexStatus->bSuspended; if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_IXSTAT_STATE, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } *ppTree = pContextNd; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Extracts an FINDEX_STATUS structure from an HTD tree. *****************************************************************************/ RCODE fcsExtractIndexStatus( NODE * pTree, FINDEX_STATUS * pIndexStatus) { NODE * pTmpNd; FLMUINT fieldPath[ 8]; RCODE rc = FERR_OK; // Initialize the structure f_memset( pIndexStatus, 0, sizeof( FINDEX_STATUS)); // Make sure pTree is non-null if( !pTree) { goto Exit; } // Parse the tree and extract the values. fieldPath[ 0] = FCS_IXSTAT_CONTEXT; fieldPath[ 1] = FCS_IXSTAT_INDEX_NUM; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pIndexStatus->uiIndexNum); } fieldPath[ 1] = FCS_IXSTAT_START_TIME; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pIndexStatus->uiStartTime); } fieldPath[ 1] = FCS_IXSTAT_LAST_REC_INDEXED; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pIndexStatus->uiLastRecordIdIndexed); } fieldPath[ 1] = FCS_IXSTAT_KEYS_PROCESSED; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pIndexStatus->uiKeysProcessed); } fieldPath[ 1] = FCS_IXSTAT_RECS_PROCESSED; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pIndexStatus->uiRecordsProcessed); } fieldPath[ 1] = FCS_IXSTAT_STATE; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { FLMUINT uiTmp; (void)GedGetUINT( pTmpNd, &uiTmp); pIndexStatus->bSuspended = uiTmp ? TRUE : FALSE; } Exit: return( rc); } /**************************************************************************** Desc: Converts an FLM_MEM_INFO structure to an HTD tree *****************************************************************************/ RCODE fcsBuildMemInfo( FLM_MEM_INFO * pMemInfo, F_Pool * pPool, NODE ** ppTree) { FLMUINT uiTmp; NODE * pContextNd = NULL; NODE * pSubContext = NULL; void * pMark = pPool->poolMark(); FLM_CACHE_USAGE * pUsage; RCODE rc = FERR_OK; *ppTree = NULL; if( !pMemInfo) { goto Exit; } // Add fields to the tree. if( (pContextNd = GedNodeMake( pPool, FCS_MEMINFO_CONTEXT, &rc)) == NULL) { goto Exit; } if( pMemInfo->bDynamicCacheAdjust) { uiTmp = 1; if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_MEMINFO_DYNA_CACHE_ADJ, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pMemInfo->uiCacheAdjustPercent) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_MEMINFO_CACHE_ADJ_PERCENT, (void *)&pMemInfo->uiCacheAdjustPercent, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pMemInfo->uiCacheAdjustMin) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_MEMINFO_CACHE_ADJ_MIN, (void *)&pMemInfo->uiCacheAdjustMin, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pMemInfo->uiCacheAdjustMax) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_MEMINFO_CACHE_ADJ_MAX, (void *)&pMemInfo->uiCacheAdjustMax, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pMemInfo->uiCacheAdjustMinToLeave) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_MEMINFO_CACHE_ADJ_MIN_LEAVE, (void *)&pMemInfo->uiCacheAdjustMinToLeave, 0, FLM_NUMBER_TYPE))) { goto Exit; } } pUsage = &pMemInfo->RecordCache; if( (pSubContext = GedNodeMake( pPool, FCS_MEMINFO_RECORD_CACHE, &rc)) == NULL) { goto Exit; } add_usage: if( pUsage->uiMaxBytes) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_MAX_BYTES, (void *)&pUsage->uiMaxBytes, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiCount) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_COUNT, (void *)&pUsage->uiCount, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiOldVerCount) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_OLD_VER_COUNT, (void *)&pUsage->uiOldVerCount, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiTotalBytesAllocated) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_TOTAL_BYTES_ALLOC, (void *)&pUsage->uiTotalBytesAllocated, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiOldVerBytes) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_OLD_VER_BYTES, (void *)&pUsage->uiOldVerBytes, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiCacheHits) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_CACHE_HITS, (void *)&pUsage->uiCacheHits, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiCacheHitLooks) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_CACHE_HIT_LOOKS, (void *)&pUsage->uiCacheHitLooks, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiCacheFaults) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_CACHE_FAULTS, (void *)&pUsage->uiCacheFaults, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pUsage->uiCacheFaultLooks) { if( RC_BAD( rc = gedAddField( pPool, pSubContext, FCS_MEMINFO_CACHE_FAULT_LOOKS, (void *)&pUsage->uiCacheFaultLooks, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( GedChild( pSubContext)) { GedChildGraft( pContextNd, pSubContext, GED_LAST); } if( pUsage != &pMemInfo->BlockCache) { pUsage = &pMemInfo->BlockCache; if( (pSubContext = GedNodeMake( pPool, FCS_MEMINFO_BLOCK_CACHE, &rc)) == NULL) { goto Exit; } goto add_usage; } *ppTree = pContextNd; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Extracts a FLM_MEM_INFO structure from an HTD tree. *****************************************************************************/ RCODE fcsExtractMemInfo( NODE * pTree, FLM_MEM_INFO * pMemInfo) { NODE * pTmpNd; FLMUINT fieldPath[ 8]; FLMUINT uiTmp; FLM_CACHE_USAGE * pUsage; FLMUINT uiUsageTag; RCODE rc = FERR_OK; // Initialize the structure f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO)); // Make sure pTree is non-null if( !pTree) { goto Exit; } // Parse the tree and extract the values. fieldPath[ 0] = FCS_MEMINFO_CONTEXT; fieldPath[ 1] = FCS_MEMINFO_DYNA_CACHE_ADJ; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); pMemInfo->bDynamicCacheAdjust = (FLMBOOL)(uiTmp ? TRUE : FALSE); } fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_PERCENT; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustPercent); } fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MIN; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMin); } fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MAX; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMax); } fieldPath[ 1] = FCS_MEMINFO_CACHE_ADJ_MIN_LEAVE; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pMemInfo->uiCacheAdjustMinToLeave); } pUsage = &pMemInfo->RecordCache; uiUsageTag = FCS_MEMINFO_RECORD_CACHE; get_usage: fieldPath[ 0] = FCS_MEMINFO_CONTEXT; fieldPath[ 1] = uiUsageTag; fieldPath[ 2] = FCS_MEMINFO_MAX_BYTES; fieldPath[ 3] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiMaxBytes); } fieldPath[ 2] = FCS_MEMINFO_COUNT; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiCount); } fieldPath[ 2] = FCS_MEMINFO_OLD_VER_COUNT; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiOldVerCount); } fieldPath[ 2] = FCS_MEMINFO_TOTAL_BYTES_ALLOC; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiTotalBytesAllocated); } fieldPath[ 2] = FCS_MEMINFO_OLD_VER_BYTES; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiOldVerBytes); } fieldPath[ 2] = FCS_MEMINFO_CACHE_HITS; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiCacheHits); } fieldPath[ 2] = FCS_MEMINFO_CACHE_HIT_LOOKS; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiCacheHitLooks); } fieldPath[ 2] = FCS_MEMINFO_CACHE_FAULTS; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiCacheFaults); } fieldPath[ 2] = FCS_MEMINFO_CACHE_FAULT_LOOKS; if( (pTmpNd = GedPathFind( GED_TREE, pTree, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pUsage->uiCacheFaultLooks); } if( pUsage != &pMemInfo->BlockCache) { pUsage = &pMemInfo->BlockCache; uiUsageTag = FCS_MEMINFO_BLOCK_CACHE; goto get_usage; } Exit: return( rc); } /**************************************************************************** Desc: Builds a GEDCOM tree containing information on all FLAIM threads *****************************************************************************/ RCODE fcsBuildThreadInfo( F_Pool * pPool, NODE ** ppTree) { NODE * pContextNd = NULL; NODE * pRootNd = NULL; void * pMark = pPool->poolMark(); F_THREAD_INFO * pThreadInfo = NULL; FLMUINT uiNumThreads; FLMUINT uiLoop; RCODE rc = FERR_OK; *ppTree = NULL; // Query FLAIM for available threads if( RC_BAD( rc = FlmGetThreadInfo( pPool, &pThreadInfo, &uiNumThreads))) { goto Exit; } if( (pRootNd = GedNodeMake( pPool, FCS_THREAD_INFO_ROOT, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = GedPutRecPtr( pPool, pRootNd, uiNumThreads))) { goto Exit; } for( uiLoop = 0; uiLoop < uiNumThreads; uiLoop++) { // Add fields to the tree. if( (pContextNd = GedNodeMake( pPool, FCS_THREAD_INFO_CONTEXT, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNd, pContextNd, GED_LAST); if( pThreadInfo->uiThreadId) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_THREAD_ID, (void *)&pThreadInfo->uiThreadId, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pThreadInfo->uiThreadGroup) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_THREAD_GROUP, (void *)&pThreadInfo->uiThreadGroup, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pThreadInfo->uiAppId) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_APP_ID, (void *)&pThreadInfo->uiAppId, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pThreadInfo->uiStartTime) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_START_TIME, (void *)&pThreadInfo->uiStartTime, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if( pThreadInfo->pszThreadName) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_THREAD_NAME, (void *)pThreadInfo->pszThreadName, 0, FLM_TEXT_TYPE))) { goto Exit; } } if( pThreadInfo->pszThreadStatus) { if( RC_BAD( rc = gedAddField( pPool, pContextNd, FCS_THREADINFO_THREAD_STATUS, (void *)pThreadInfo->pszThreadStatus, 0, FLM_TEXT_TYPE))) { goto Exit; } } pThreadInfo++; } *ppTree = pRootNd; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Extracts a list of F_THREAD_INFO structure from an HTD tree. *****************************************************************************/ RCODE fcsExtractThreadInfo( NODE * pTree, F_Pool * pPool, F_THREAD_INFO ** ppThreadInfo, FLMUINT * puiNumThreads) { NODE * pTmpNd; NODE * pContextNd; void * pMark = pPool->poolMark(); FLMUINT uiTmp; F_THREAD_INFO * pThreadInfo; F_THREAD_INFO * pCurThread; FLMUINT uiNumThreads; FLMUINT uiLoop; RCODE rc = FERR_OK; *ppThreadInfo = NULL; *puiNumThreads = 0; if( GedTagNum( pTree) != FCS_THREAD_INFO_ROOT) { rc = RC_SET( FERR_SYNTAX); goto Exit; } if( RC_BAD( rc = GedGetUINT( pTree, &uiNumThreads))) { goto Exit; } if( !uiNumThreads) { goto Exit; } if( RC_BAD( rc = pPool->poolAlloc( uiNumThreads * sizeof( F_THREAD_INFO), (void **)&pThreadInfo))) { goto Exit; } if( (pContextNd = GedFind( 1, pTree, FCS_THREAD_INFO_CONTEXT, 1)) == NULL) { rc = RC_SET( FERR_SYNTAX); goto Exit; } for( uiLoop = 0, pCurThread = pThreadInfo; uiLoop < uiNumThreads; uiLoop++, pCurThread++) { if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_THREAD_ID, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pCurThread->uiThreadId); } if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_THREAD_GROUP, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pCurThread->uiThreadGroup); } if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_APP_ID, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pCurThread->uiAppId); } if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_START_TIME, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &pCurThread->uiStartTime); } if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_THREAD_NAME, 1)) != NULL) { if( RC_BAD( rc = GedGetNATIVE( pTmpNd, NULL, &uiTmp))) { goto Exit; } if( uiTmp) { uiTmp++; if( RC_BAD( rc = pPool->poolAlloc( uiTmp, (void **)&pCurThread->pszThreadName))) { goto Exit; } } if( RC_BAD( rc = GedGetNATIVE( pTmpNd, pCurThread->pszThreadName, &uiTmp))) { goto Exit; } } if( (pTmpNd = GedFind( 1, pContextNd, FCS_THREADINFO_THREAD_STATUS, 1)) != NULL) { if( RC_BAD( rc = GedGetNATIVE( pTmpNd, NULL, &uiTmp))) { goto Exit; } if( uiTmp) { uiTmp++; if( RC_BAD( rc = pPool->poolAlloc( uiTmp, (void **)pCurThread->pszThreadStatus))) { goto Exit; } } if( RC_BAD( rc = GedGetNATIVE( pTmpNd, pCurThread->pszThreadStatus, &uiTmp))) { goto Exit; } } if( (pContextNd = GedSibNext( pContextNd)) != NULL) { if( GedTagNum( pContextNd) != FCS_THREAD_INFO_CONTEXT) { rc = RC_SET( FERR_SYNTAX); goto Exit; } } } *ppThreadInfo = pThreadInfo; *puiNumThreads = uiNumThreads; Exit: if( RC_BAD( rc)) { pPool->poolReset( pMark); } return( rc); } /**************************************************************************** Desc: Reads a block from a remote database *****************************************************************************/ RCODE fcsGetBlock( HFDB hDb, FLMUINT uiAddress, FLMUINT uiMinTransId, FLMUINT * puiCount, FLMUINT * puiBlocksExamined, FLMUINT * puiNextBlkAddr, FLMUINT uiFlags, FLMBYTE * pucBlock) { FDB * pDb = (FDB *)hDb; RCODE rc = FERR_OK; flmAssert( IsInCSMode( hDb)); fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_GET_BLOCK))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_ADDRESS, uiAddress))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_TRANSACTION_ID, uiMinTransId))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_COUNT, *puiCount))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiFlags))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { if( rc != FERR_IO_END_OF_FILE) { goto Exit; } } *puiBlocksExamined = (FLMUINT)Wire.getNumber2(); *puiCount = (FLMUINT)Wire.getCount(); *puiNextBlkAddr = Wire.getAddress(); if( *puiCount) { f_memcpy( pucBlock, Wire.getBlock(), Wire.getBlockSize()); } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; Exit: fdbExit( pDb); return( rc); } /**************************************************************************** Desc: Instructs the server to generate a serial number *****************************************************************************/ RCODE fcsCreateSerialNumber( void * pvCSContext, FLMBYTE * pucSerialNum) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = (CS_CONTEXT *)pvCSContext; FCL_WIRE Wire( pCSContext); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_MISC, FCS_OP_CREATE_SERIAL_NUM))) { goto Exit; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } if( !Wire.getSerialNum()) { rc = RC_SET( FERR_FAILURE); goto Exit; } f_memcpy( pucSerialNum, Wire.getSerialNum(), F_SERIAL_NUM_SIZE); goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; Exit: return( rc); } /**************************************************************************** Desc: Sets or clears the backup active flag for the database Note: This should only be called internally from the backup routines. *****************************************************************************/ RCODE fcsSetBackupActiveFlag( HFDB hDb, FLMBOOL bBackupActive) { FDB * pDb = (FDB *)hDb; RCODE rc = FERR_OK; flmAssert( IsInCSMode( hDb)); fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DB_SET_BACKUP_FLAG))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_BOOLEAN, bBackupActive))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } /* Read the response. */ if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; Exit: fdbExit( pDb); return( rc); } /**************************************************************************** Desc: Commits an update transaction and updates the log header. Note: This should only be called internally from the backup routines. *****************************************************************************/ RCODE fcsDbTransCommitEx( HFDB hDb, FLMBOOL bForceCheckpoint, FLMBYTE * pucLogHdr) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bInitializedFdb = FALSE; if( IsInCSMode( hDb)) { fdbInitCS( pDb); bInitializedFdb = TRUE; FCL_WIRE Wire( pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { rc = Wire.doTransOp( FCS_OP_TRANSACTION_COMMIT_EX, 0, 0, 0, pucLogHdr, bForceCheckpoint); } } else { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } Exit: if( bInitializedFdb) { fdbExit( pDb); } return( rc); } /**************************************************************************** Desc: Generates a hex-encoded, obfuscated string consisting of characters 0-9, A-F from the passed-in data buffer. *****************************************************************************/ RCODE flmGenerateHexPacket( FLMBYTE * pucData, FLMUINT uiDataSize, FLMBYTE ** ppucPacket) { RCODE rc = FERR_OK; FLMBYTE * pucBinPacket = NULL; FLMBYTE * pucHexPacket = NULL; FLMBYTE * pucUsedMap = NULL; FLMUINT32 ui32Tmp; FLMUINT uiLoop; FLMUINT uiSlot = 0; FLMBYTE ucTmp[ 32]; FLMUINT uiBinPacketSize; FLMBOOL bTmp; IF_RandomGenerator * pRandGen = NULL; // Determine the packet size. Make the minimum packet size 128 bytes // to account for the 64-byte "header" and for the overhead of the // CRC bytes, etc. Round the packet size up to the nearest 64-byte // boundary after adding on the data size. uiBinPacketSize = 128 + uiDataSize; if( (uiBinPacketSize % 64) != 0) { uiBinPacketSize += (64 - (uiBinPacketSize % 64)); } // Allocate buffers for building the packet if( RC_BAD( rc = f_alloc( uiBinPacketSize, &pucBinPacket))) { goto Exit; } if( RC_BAD( rc = f_calloc( uiBinPacketSize, &pucUsedMap))) { goto Exit; } // First 64-bytes of the packet are reserved as a header f_memset( pucUsedMap, 0xFF, 64); // Initialize the random number generator and seed with the current // time. if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen))) { goto Exit; } // Fill the packet with random "noise" for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop += 4) { ui32Tmp = pRandGen->getUINT32(); UD2FBA( ui32Tmp, &pucBinPacket[ uiLoop]); } for( uiLoop = 0; uiLoop < 512; uiLoop++) { ui32Tmp = pRandGen->getUINT32(); UD2FBA( ui32Tmp, &pucBinPacket[ pRandGen->getUINT32( 1, (int)(uiBinPacketSize / 4)) - 1]); } // Determine a new random seed based on bytes in the // packet header if( (ui32Tmp = (FLMUINT32)FB2UD( &pucBinPacket[ pRandGen->getUINT32( 1, 61) - 1])) == 0) { ui32Tmp = 1; } pRandGen->setSeed( ui32Tmp); // Use the CRC of the header and the also first four bytes // of the header as an 8-byte validation signature. This will // be needed to decode the packet. // Initialize the CRC to 0xFFFFFFFF and then compute the 1's // complement of the returned CRC. This implements the // "standard" CRC used by PKZIP, etc. ui32Tmp = 0xFFFFFFFF; f_updateCRC( pucBinPacket, 64, &ui32Tmp); ui32Tmp = ~ui32Tmp; UD2FBA( ui32Tmp, &ucTmp[ 0]); f_memcpy( &ucTmp[ 4], pucBinPacket, 4); for( uiLoop = 0; uiLoop < 8; uiLoop++) { bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, pRandGen, &uiSlot); flmAssert( bTmp); pucBinPacket[ uiSlot] = ucTmp[ uiLoop]; } // Encode the data size UD2FBA( (FLMUINT32)uiDataSize, &ucTmp[ 0]); for( uiLoop = 0; uiLoop < 4; uiLoop++) { bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, pRandGen, &uiSlot); flmAssert( bTmp); pucBinPacket[ uiSlot] = ucTmp[ uiLoop]; } // Randomly dispurse the data throughout the buffer. Obfuscate the // data using the first 64-bytes of the buffer. for( uiLoop = 0; uiLoop < uiDataSize; uiLoop++) { bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, pRandGen, &uiSlot); flmAssert( bTmp); pucBinPacket[ uiSlot] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64]; } // Calculate and encode the data CRC ui32Tmp = 0xFFFFFFFF; f_updateCRC( pucData, uiDataSize, &ui32Tmp); ui32Tmp = ~ui32Tmp; UD2FBA( ui32Tmp, &ucTmp[ 0]); for( uiLoop = 0; uiLoop < 4; uiLoop++) { bTmp = flmGetNextHexPacketSlot( pucUsedMap, uiBinPacketSize, pRandGen, &uiSlot); flmAssert( bTmp); pucBinPacket[ uiSlot] = ucTmp[ uiLoop]; } // Hex encode the binary packet if( RC_BAD( rc = f_alloc( (uiBinPacketSize * 2) + 1, &pucHexPacket))) { goto Exit; } for( uiLoop = 0; uiLoop < uiBinPacketSize; uiLoop++) { FLMBYTE ucLowNibble = pucBinPacket[ uiLoop] & 0x0F; FLMBYTE ucHighNibble = (pucBinPacket[ uiLoop] & 0xF0) >> 4; pucHexPacket[ uiLoop << 1] = (ucHighNibble <= 9 ? (ucHighNibble + '0') : ((ucHighNibble - 0xA) + 'A')); pucHexPacket[ (uiLoop << 1) + 1] = (ucLowNibble <= 9 ? (ucLowNibble + '0') : ((ucLowNibble - 0xA) + 'A')); } pucHexPacket[ uiBinPacketSize * 2] = 0; *ppucPacket = pucHexPacket; pucHexPacket = NULL; Exit: if( pucUsedMap) { f_free( &pucUsedMap); } if( pucBinPacket) { f_free( &pucBinPacket); } if( pucHexPacket) { f_free( &pucHexPacket); } if( pRandGen) { pRandGen->Release(); } return( rc); } /**************************************************************************** Desc: Extracts a data buffer from the passed-in hex-encoded, obfuscated string. *****************************************************************************/ RCODE flmExtractHexPacketData( FLMBYTE * pucPacket, FLMBYTE ** ppucData, FLMUINT * puiDataSize) { RCODE rc = FERR_OK; FLMBYTE * pucUsedMap = NULL; FLMBYTE * pucData = NULL; FLMBYTE * pucBinPacket = NULL; FLMBYTE * pucTmp; FLMUINT32 ui32Tmp; FLMUINT32 ui32FirstCRC; FLMUINT32 ui32Seed; FLMUINT uiPacketSize; FLMUINT uiLoop; FLMUINT uiDataSize; FLMBYTE ucTmp[ 32]; FLMBYTE ucVal = 0; FLMBOOL bValid; IF_RandomGenerator * pRandGen = NULL; if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen))) { goto Exit; } // Determine the packet size, ignoring all characters except 0-9, A-F uiPacketSize = 0; pucTmp = pucPacket; while( *pucTmp) { if( (*pucTmp >= '0' && *pucTmp <= '9') || (*pucTmp >= 'A' && *pucTmp <= 'F')) { uiPacketSize++; } pucTmp++; } if( uiPacketSize & 0x00000001 || (uiPacketSize % 4) != 0 || uiPacketSize < 128) { rc = RC_SET( FERR_INVALID_CRC); goto Exit; } // Get the actual size of the decoded binary data by dividing // the packet size by 2 uiPacketSize >>= 1; // Allocate a buffer and convert the data from hex ASCII to binary if( RC_BAD( rc = f_calloc( uiPacketSize, &pucBinPacket))) { goto Exit; } uiLoop = 0; pucTmp = pucPacket; while( *pucTmp) { bValid = FALSE; if( *pucTmp >= '0' && *pucTmp <= '9') { ucVal = *pucTmp - '0'; bValid = TRUE; } else if( *pucTmp >= 'A' && *pucTmp <= 'F') { ucVal = (*pucTmp - 'A') + 0x0A; bValid = TRUE; } if( bValid) { if( (uiLoop & 0x00000001) == 0) { ucVal <<= 4; } pucBinPacket[ uiLoop >> 1] |= ucVal; uiLoop++; } pucTmp++; } // Allocate the data map if( RC_BAD( rc = f_calloc( uiPacketSize, &pucUsedMap))) { goto Exit; } // First 64-bytes of the packet are reserved f_memset( pucUsedMap, 0xFF, 64); // Determine the CRC of the 1st 64-bytes ui32FirstCRC = 0xFFFFFFFF; f_updateCRC( pucBinPacket, 64, &ui32FirstCRC); ui32FirstCRC = ~ui32FirstCRC; // Search for the random seed within the first 64 bytes ui32Seed = 0; for( uiLoop = 0; uiLoop < 61; uiLoop++) { ui32Tmp = FB2UD( &pucBinPacket[ uiLoop]); pRandGen->setSeed( ui32Tmp); if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, ucTmp, 8))) { goto Exit; } if( FB2UD( &ucTmp[ 0]) == ui32FirstCRC && f_memcmp( &ucTmp[ 4], &pucBinPacket[ 0], 4) == 0) { ui32Seed = ui32Tmp; break; } // Reset the "used" map f_memset( pucUsedMap, 0, uiPacketSize); f_memset( pucUsedMap, 0xFF, 64); } if( !ui32Seed) { rc = RC_SET( FERR_INVALID_CRC); goto Exit; } // Get the data size if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, ucTmp, 4))) { goto Exit; } uiDataSize = (FLMUINT)FB2UD( &ucTmp[ 0]); if( uiDataSize > uiPacketSize) { rc = RC_SET( FERR_INVALID_CRC); goto Exit; } // Allocate space for the data if( RC_BAD( rc = f_alloc( uiDataSize, &pucData))) { goto Exit; } // Get the data if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, pucData, uiDataSize))) { goto Exit; } // Un-obfuscate the data for( uiLoop = 0; uiLoop < uiDataSize; uiLoop++) { pucData[ uiLoop] = pucData[ uiLoop] ^ pucBinPacket[ uiLoop % 64]; } // Get the data CRC if( RC_BAD( rc = flmGetNextHexPacketBytes( pucUsedMap, uiPacketSize, pucBinPacket, pRandGen, ucTmp, 4))) { goto Exit; } // Verify the data CRC ui32Tmp = 0xFFFFFFFF; f_updateCRC( pucData, uiDataSize, &ui32Tmp); ui32Tmp = ~ui32Tmp; if( ui32Tmp != FB2UD( &ucTmp[ 0])) { rc = RC_SET( FERR_INVALID_CRC); goto Exit; } *ppucData = pucData; pucData = NULL; *puiDataSize = uiDataSize; Exit: if( pucUsedMap) { f_free( &pucUsedMap); } if( pucData) { f_free( &pucData); } if( pucBinPacket) { f_free( &pucBinPacket); } if( pRandGen) { pRandGen->Release(); } return( rc); } /**************************************************************************** Desc: Used by flmGenerateHexPacket to find an unused byte in the packet *****************************************************************************/ FLMBOOL flmGetNextHexPacketSlot( FLMBYTE * pucUsedMap, FLMUINT uiMapSize, IF_RandomGenerator * pRandGen, FLMUINT * puiSlot) { FLMUINT uiLoop; FLMUINT uiSlot = 0; FLMBOOL bFound = FALSE; for( uiLoop = 0; uiLoop < 100; uiLoop++) { uiSlot = ((FLMUINT)pRandGen->getUINT32()) % uiMapSize; if( !pucUsedMap[ uiSlot]) { bFound = TRUE; goto Exit; } } // Scan the table from the top to find an empty slot for( uiSlot = 0; uiSlot < uiMapSize; uiSlot++) { if( !pucUsedMap[ uiSlot]) { bFound = TRUE; goto Exit; } } Exit: if( bFound) { flmAssert( uiSlot < uiMapSize); *puiSlot = uiSlot; pucUsedMap[ uiSlot] = 0xFF; } return( bFound); } /**************************************************************************** Desc: Used by flmExtractHexPacket to get the next N bytes of data from the packet. *****************************************************************************/ RCODE flmGetNextHexPacketBytes( FLMBYTE * pucUsedMap, FLMUINT uiMapSize, FLMBYTE * pucPacket, IF_RandomGenerator * pRandGen, FLMBYTE * pucBuf, FLMUINT uiCount) { FLMUINT uiSlot; FLMUINT uiLoop; RCODE rc = FERR_OK; for( uiLoop = 0; uiLoop < uiCount; uiLoop++) { if( !flmGetNextHexPacketSlot( pucUsedMap, uiMapSize, pRandGen, &uiSlot)) { rc = RC_SET( FERR_INVALID_CRC); goto Exit; } pucBuf[ uiLoop] = pucPacket[ uiSlot]; } Exit: return( rc); } /**************************************************************************** Desc: Decodes a string containing %XX sequences and does it in place. Typically, this data comes from an HTML form. ****************************************************************************/ void fcsDecodeHttpString( char * pszSrc) { char * pszDest; pszDest = pszSrc; while( *pszSrc) { if( *pszSrc == '%') { pszSrc++; if( f_isHexChar( (FLMBYTE)pszSrc[ 0]) && f_isHexChar( (FLMBYTE)pszSrc[ 1])) { *pszDest = (f_getHexVal( (FLMBYTE)pszSrc[ 0]) << 4) | f_getHexVal( (FLMBYTE)pszSrc[ 1]); pszSrc += 2; pszDest++; continue; } } else if( *pszSrc == '+') { *pszDest = ' '; pszSrc++; pszDest++; continue; } if( pszSrc != pszDest) { *pszDest = *pszSrc; } pszSrc++; pszDest++; } *pszDest = 0; } /**************************************************************************** Desc: *****************************************************************************/ FCS_WIRE::FCS_WIRE( FCS_DIS * pDIStream, FCS_DOS * pDOStream) { m_pool.poolInit( 2048); m_pPool = &m_pool; m_pDIStream = pDIStream; m_pDOStream = pDOStream; m_pRecord = NULL; m_pFromKey = NULL; m_pUntilKey = NULL; m_bSendGedcom = FALSE; (void)resetCommon(); } /**************************************************************************** Desc: *****************************************************************************/ FCS_WIRE::~FCS_WIRE( void) { if( m_pRecord) { m_pRecord->Release(); m_pRecord = NULL; } if( m_pFromKey) { m_pFromKey->Release(); m_pFromKey = NULL; } if( m_pUntilKey) { m_pUntilKey->Release(); m_pUntilKey = NULL; } m_pool.poolFree(); } /**************************************************************************** Desc: Resets all member variables to their default / initial values. *****************************************************************************/ void FCS_WIRE::resetCommon( void) { if( m_pRecord) { m_pRecord->Release(); m_pRecord = NULL; } if( m_pFromKey) { m_pFromKey->Release(); m_pFromKey = NULL; } if( m_pUntilKey) { m_pUntilKey->Release(); m_pUntilKey = NULL; } m_uiClass = 0; m_uiOp = 0; m_uiRCode = 0; m_uiDrn = 0; m_uiTransType = FLM_READ_TRANS; m_ui64Count = 0; m_uiItemId = 0; m_uiIndexId = 0; m_puzItemName = NULL; m_pHTD = NULL; m_uiSessionId = FCS_INVALID_ID; m_uiSessionCookie = 0; m_uiContainer = FLM_DATA_CONTAINER; m_uiTransId = FCS_INVALID_ID; m_uiIteratorId = FCS_INVALID_ID; m_puzFilePath = NULL; m_puzFilePath2 = NULL; m_puzFilePath3 = NULL; m_pucBlock = NULL; m_pucSerialNum = NULL; m_uiBlockSize = 0; m_bIncludesAsync = FALSE; fcsInitCreateOpts( &m_CreateOpts); m_pPool->poolReset(); m_bFlag = FALSE; m_ui64Number1 = 0; m_ui64Number2 = 0; m_ui64Number3 = 0; m_uiAddress = 0; m_uiFlags = 0; m_uiFlaimVersion = 0; m_i64SignedValue = 0; m_pCSContext = NULL; m_pDb = NULL; } /**************************************************************************** Desc: Reads the class and opcode for a client request or server response. *****************************************************************************/ RCODE FCS_WIRE::readOpcode( void) { FLMBYTE ucClass; FLMBYTE ucOp; RCODE rc = FERR_OK; if( RC_BAD( rc = m_pDIStream->read( &ucClass, 1, NULL))) { goto Exit; } m_uiClass = ucClass; if( RC_BAD( rc = m_pDIStream->read( &ucOp, 1, NULL))) { goto Exit; } m_uiOp = ucOp; Exit: return( rc); } /**************************************************************************** Desc: Reads a client request or server response and sets the appropriate member variable values. *****************************************************************************/ RCODE FCS_WIRE::readCommon( FLMUINT * puiTagRV, FLMBOOL * pbEndRV) { FLMUINT16 ui16Tmp; FLMUINT uiTag = 0; RCODE rc = FERR_OK; *pbEndRV = FALSE; // Read the tag. if( RC_BAD( rc = m_pDIStream->readUShort( &ui16Tmp))) { goto Exit; } uiTag = ui16Tmp; // Read the request / response values. switch( (uiTag & WIRE_VALUE_TAG_MASK)) { case WIRE_VALUE_RCODE: { rc = readNumber( uiTag, &m_uiRCode); uiTag = 0; break; } case WIRE_VALUE_SESSION_ID: { rc = readNumber( uiTag, &m_uiSessionId); uiTag = 0; break; } case WIRE_VALUE_SESSION_COOKIE: { rc = readNumber( uiTag, &m_uiSessionCookie); uiTag = 0; break; } case WIRE_VALUE_CONTAINER_ID: { rc = readNumber( uiTag, &m_uiContainer); uiTag = 0; break; } case WIRE_VALUE_COUNT: { rc = readNumber( uiTag, NULL, NULL, &m_ui64Count); uiTag = 0; break; } case WIRE_VALUE_DRN: { rc = readNumber( uiTag, &m_uiDrn); uiTag = 0; break; } case WIRE_VALUE_INDEX_ID: { rc = readNumber( uiTag, &m_uiIndexId); uiTag = 0; break; } case WIRE_VALUE_HTD: { rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pHTD, NULL); uiTag = 0; break; } case WIRE_VALUE_RECORD: { FlmRecord * pRecord = m_pRecord; if( RC_OK( rc = receiveRecord( &pRecord))) { if( m_pRecord != pRecord) { if( m_pRecord) { m_pRecord->Release(); } m_pRecord = pRecord; } } uiTag = 0; break; } case WIRE_VALUE_FROM_KEY: { FlmRecord * pFromKey = m_pFromKey; if( RC_OK( rc = receiveRecord( &pFromKey))) { if( m_pFromKey != pFromKey) { if( m_pFromKey) { m_pFromKey->Release(); } m_pFromKey = pFromKey; } } uiTag = 0; break; } case WIRE_VALUE_UNTIL_KEY: { FlmRecord * pUntilKey = m_pUntilKey; if( RC_OK( rc = receiveRecord( &pUntilKey))) { if( m_pUntilKey != pUntilKey) { if( m_pUntilKey) { m_pUntilKey->Release(); } m_pUntilKey = pUntilKey; } } uiTag = 0; break; } case WIRE_VALUE_CREATE_OPTS: { rc = receiveCreateOpts(); uiTag = 0; break; } case WIRE_VALUE_ITERATOR_ID: { rc = readNumber( uiTag, &m_uiIteratorId); uiTag = 0; break; } case WIRE_VALUE_TRANSACTION_TYPE: { rc = readNumber( uiTag, &m_uiTransType); uiTag = 0; break; } case WIRE_VALUE_TRANSACTION_ID: { rc = readNumber( uiTag, &m_uiTransId); uiTag = 0; break; } case WIRE_VALUE_ITEM_NAME: { rc = m_pDIStream->readUTF( m_pPool, &m_puzItemName); uiTag = 0; break; } case WIRE_VALUE_ITEM_ID: { rc = readNumber( uiTag, &m_uiItemId); uiTag = 0; break; } case WIRE_VALUE_BOOLEAN: { FLMUINT uiTmp; if( RC_OK( rc = readNumber( uiTag, &uiTmp))) { m_bFlag = (FLMBOOL)((uiTmp) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); } uiTag = 0; break; } case WIRE_VALUE_NUMBER1: { rc = readNumber( uiTag, NULL, NULL, &m_ui64Number1); uiTag = 0; break; } case WIRE_VALUE_NUMBER2: { rc = readNumber( uiTag, NULL, NULL, &m_ui64Number2); uiTag = 0; break; } case WIRE_VALUE_NUMBER3: { rc = readNumber( uiTag, NULL, NULL, &m_ui64Number3); uiTag = 0; break; } case WIRE_VALUE_ADDRESS: { rc = readNumber( uiTag, &m_uiAddress); uiTag = 0; break; } case WIRE_VALUE_SIGNED_NUMBER: { rc = readNumber( uiTag, NULL, NULL, NULL, &m_i64SignedValue); uiTag = 0; break; } case WIRE_VALUE_FILE_PATH: { rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath); uiTag = 0; break; } case WIRE_VALUE_FILE_PATH_2: { rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath2); uiTag = 0; break; } case WIRE_VALUE_FILE_PATH_3: { rc = m_pDIStream->readUTF( m_pPool, &m_puzFilePath3); uiTag = 0; break; } case WIRE_VALUE_BLOCK: { rc = m_pDIStream->readLargeBinary( m_pPool, &m_pucBlock, &m_uiBlockSize); uiTag = 0; break; } case WIRE_VALUE_SERIAL_NUM: { FLMUINT uiSerialLen; if( RC_BAD( rc = m_pDIStream->readBinary( m_pPool, &m_pucSerialNum, &uiSerialLen))) { goto Exit; } if( uiSerialLen != F_SERIAL_NUM_SIZE) { rc = RC_SET( FERR_CONV_DEST_OVERFLOW); goto Exit; } uiTag = 0; break; } case WIRE_VALUE_START_ASYNC: { m_bIncludesAsync = TRUE; *pbEndRV = TRUE; uiTag = 0; break; } case WIRE_VALUE_FLAGS: { rc = readNumber( uiTag, &m_uiFlags); uiTag = 0; break; } case WIRE_VALUE_FLAIM_VERSION: { rc = readNumber( uiTag, &m_uiFlaimVersion); uiTag = 0; break; } case WIRE_VALUE_TERMINATE: { rc = m_pDIStream->endMessage(); *pbEndRV = TRUE; uiTag = 0; break; } default: { break; } } Exit: *puiTagRV = uiTag; return( rc); } /**************************************************************************** Desc: Copies the internal CREATE_OPTS structure into a user-supplied location *****************************************************************************/ void FCS_WIRE::copyCreateOpts( CREATE_OPTS * pCreateOptsRV) { f_memcpy( pCreateOptsRV, &m_CreateOpts, sizeof( CREATE_OPTS)); } /**************************************************************************** Desc: Reads a numeric value from the specified data input stream. *****************************************************************************/ RCODE FCS_WIRE::readNumber( FLMUINT uiTag, FLMUINT * puiNumber, FLMINT * piNumber, FLMUINT64 * pui64Number, FLMINT64 * pi64Number) { RCODE rc = FERR_OK; flmAssert( !(puiNumber && piNumber)); // Read the number of bytes specified by the // value's tag. switch( ((uiTag & WIRE_VALUE_TYPE_MASK) >> WIRE_VALUE_TYPE_START_BIT)) { case WIRE_VALUE_TYPE_GEN_0: { if( puiNumber) { *puiNumber = 0; } else if( piNumber) { *piNumber = 0; } else if( pui64Number) { *pui64Number = 0; } else if( pi64Number) { *pi64Number = 0; } break; } case WIRE_VALUE_TYPE_GEN_1: { FLMBYTE ucValue; if( RC_BAD( rc = m_pDIStream->read( &ucValue, 1, NULL))) { goto Exit; } if( puiNumber) { *puiNumber = (FLMUINT)ucValue; } else if( piNumber) { *piNumber = (FLMINT)*((FLMINT8 *)&ucValue); } else if( pui64Number) { *pui64Number = (FLMUINT64)ucValue; } else if( pi64Number) { *pi64Number = (FLMINT64)*((FLMINT8 *)&ucValue); } break; } case WIRE_VALUE_TYPE_GEN_2: { if( puiNumber || pui64Number) { FLMUINT16 ui16Value; if( RC_BAD( rc = m_pDIStream->readUShort( &ui16Value))) { goto Exit; } if( puiNumber) { *puiNumber = (FLMUINT)ui16Value; } else if( pui64Number) { *pui64Number = (FLMUINT64)ui16Value; } } else if( piNumber || pi64Number) { FLMINT16 i16Value; if( RC_BAD( rc = m_pDIStream->readShort( &i16Value))) { goto Exit; } if( piNumber) { *piNumber = (FLMINT)i16Value; } else if( pi64Number) { *pi64Number = (FLMINT)i16Value; } } break; } case WIRE_VALUE_TYPE_GEN_4: { if( puiNumber || pui64Number) { FLMUINT32 ui32Value; if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Value))) { goto Exit; } if( puiNumber) { *puiNumber = (FLMUINT)ui32Value; } else if( pui64Number) { *pui64Number = (FLMUINT64)ui32Value; } } else if( piNumber || pi64Number) { FLMINT32 i32Value; if( RC_BAD( rc = m_pDIStream->readInt( &i32Value))) { goto Exit; } if( piNumber) { *piNumber = (FLMINT)i32Value; } else if( pi64Number) { *pi64Number = (FLMINT64)i32Value; } } break; } case WIRE_VALUE_TYPE_GEN_8: { if( puiNumber || piNumber) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { if( pui64Number) { if( RC_BAD( rc = m_pDIStream->readUInt64( pui64Number))) { goto Exit; } } else if( pi64Number) { if( RC_BAD( rc = m_pDIStream->readInt64( pi64Number))) { goto Exit; } } else { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } } break; } } Exit: return( rc); } /**************************************************************************** Desc: Writes an unsigned number to the specified data output stream. *****************************************************************************/ RCODE FCS_WIRE::writeUnsignedNumber( FLMUINT uiTag, FLMUINT64 ui64Number) { RCODE rc = FERR_OK; if( ui64Number <= (FLMUINT64)0x000000FF) { uiTag |= WIRE_VALUE_TYPE_GEN_1 << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeByte( (FLMBYTE)ui64Number))) { goto Exit; } } else if( ui64Number <= (FLMUINT64)0x0000FFFF) { uiTag |= WIRE_VALUE_TYPE_GEN_2 << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)ui64Number))) { goto Exit; } } else if( ui64Number <= (FLMUINT64)0xFFFFFFFF) { uiTag |= WIRE_VALUE_TYPE_GEN_4 << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeUInt32( (FLMUINT32)ui64Number))) { goto Exit; } } else { uiTag |= WIRE_VALUE_TYPE_GEN_8 << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeUInt64( ui64Number))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Writes a signed number to the specified data output stream. *****************************************************************************/ RCODE FCS_WIRE::writeSignedNumber( FLMUINT uiTag, FLMINT64 i64Number) { RCODE rc = FERR_OK; if( RC_BAD( rc = writeUnsignedNumber( uiTag, (FLMUINT64)i64Number))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Skips a parameter or return value in the data stream *****************************************************************************/ RCODE FCS_WIRE::skipValue( FLMUINT uiTag) { RCODE rc = FERR_OK; switch( ((uiTag & WIRE_VALUE_TYPE_MASK) >> WIRE_VALUE_TYPE_START_BIT)) { case WIRE_VALUE_TYPE_GEN_0: { break; } case WIRE_VALUE_TYPE_GEN_1: { if( RC_BAD( rc = m_pDIStream->skip( 1))) { goto Exit; } break; } case WIRE_VALUE_TYPE_GEN_2: { if( RC_BAD( rc = m_pDIStream->skip( 2))) { goto Exit; } break; } case WIRE_VALUE_TYPE_GEN_4: { if( RC_BAD( rc = m_pDIStream->skip( 4))) { goto Exit; } break; } case WIRE_VALUE_TYPE_GEN_8: { if( RC_BAD( rc = m_pDIStream->skip( 8))) { goto Exit; } break; } case WIRE_VALUE_TYPE_BINARY: { if( RC_BAD( rc = m_pDIStream->readBinary( NULL, NULL, NULL))) { goto Exit; } break; } case WIRE_VALUE_TYPE_LARGE_BINARY: { if( RC_BAD( rc = m_pDIStream->readLargeBinary( NULL, NULL, NULL))) { goto Exit; } break; } case WIRE_VALUE_TYPE_HTD: { if( RC_BAD( rc = m_pDIStream->readHTD( NULL, 0, 0, NULL, NULL))) { goto Exit; } break; } case WIRE_VALUE_TYPE_RECORD: { if( RC_BAD( rc = receiveRecord( NULL))) { goto Exit; } } case WIRE_VALUE_TYPE_UTF: { if( RC_BAD( rc = m_pDIStream->readUTF( NULL, NULL))) { goto Exit; } break; } default: { rc = RC_SET( FERR_FAILURE); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Sends an opcode to the client *****************************************************************************/ RCODE FCS_WIRE::sendOpcode( FLMUINT uiClass, FLMUINT uiOp) { FLMBYTE ucClass = (FLMBYTE)uiClass; FLMBYTE ucOp = (FLMBYTE)uiOp; RCODE rc = FERR_OK; if( RC_BAD( rc = m_pDOStream->write( &ucClass, 1))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->write( &ucOp, 1))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendTerminate( void) { RCODE rc = FERR_OK; if( RC_BAD( rc = m_pDOStream->writeUShort( 0))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->endMessage())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendNumber( FLMUINT uiTag, FLMUINT64 ui64Value, FLMINT64 i64Value) { RCODE rc = FERR_OK; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_AREA_ID: case WIRE_VALUE_OP_SEQ_NUM: case WIRE_VALUE_FLAGS: case WIRE_VALUE_CLIENT_VERSION: case WIRE_VALUE_SESSION_ID: case WIRE_VALUE_SESSION_COOKIE: case WIRE_VALUE_CONTAINER_ID: case WIRE_VALUE_INDEX_ID: case WIRE_VALUE_ITEM_ID: case WIRE_VALUE_TRANSACTION_ID: case WIRE_VALUE_TRANSACTION_TYPE: case WIRE_VALUE_DRN: case WIRE_VALUE_COUNT: case WIRE_VALUE_AUTOTRANS: case WIRE_VALUE_MAX_LOCK_WAIT: case WIRE_VALUE_RECORD_COUNT: case WIRE_VALUE_BOOLEAN: case WIRE_VALUE_ITERATOR_ID: case WIRE_VALUE_SHARED_DICT_ID: case WIRE_VALUE_PARENT_DICT_ID: case WIRE_VALUE_TYPE: case WIRE_VALUE_NUMBER1: case WIRE_VALUE_NUMBER2: case WIRE_VALUE_NUMBER3: case WIRE_VALUE_ADDRESS: case WIRE_VALUE_FLAIM_VERSION: { if( RC_BAD( rc = writeUnsignedNumber( uiTag, ui64Value))) { goto Exit; } break; } case WIRE_VALUE_SIGNED_NUMBER: { if( RC_BAD( rc = writeSignedNumber( uiTag, i64Value))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendBinary( FLMUINT uiTag, FLMBYTE * pData, FLMUINT uiLength) { RCODE rc = FERR_OK; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_PASSWORD: case WIRE_VALUE_SERIAL_NUM: { uiTag |= WIRE_VALUE_TYPE_BINARY << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeBinary( pData, uiLength))) { goto Exit; } break; } case WIRE_VALUE_BLOCK: { uiTag |= WIRE_VALUE_TYPE_LARGE_BINARY << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeLargeBinary( pData, uiLength))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Sends a record *****************************************************************************/ RCODE FCS_WIRE::sendRecord( FLMUINT uiTag, FlmRecord * pRecord) { RCODE rc = FERR_OK; #define RECORD_OUTPUT_BUFFER_SIZE 64 FLMBYTE pucBuffer[ RECORD_OUTPUT_BUFFER_SIZE]; FLMBYTE * pucBufPos; FLMBYTE ucDescriptor; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_RECORD: case WIRE_VALUE_FROM_KEY: case WIRE_VALUE_UNTIL_KEY: { uiTag |= WIRE_VALUE_TYPE_RECORD << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } // The format of a record is (X = 1 bit): // // X X XXXXXX 0-64 bytes HTD // RESERVED HTD_FOLLOWS ID_LENGTH ID_VALUE TREE (optional) // // This sequence can repeat, terminating with a 0 byte. ucDescriptor = 0; pucBufPos = pucBuffer; ucDescriptor |= (FLMBYTE)RECORD_HAS_HTD_FLAG; // Output the descriptor. ucDescriptor |= (FLMBYTE)RECORD_ID_SIZE; *pucBufPos = ucDescriptor; pucBufPos++; // Output the ID. Current format of a record ID is: // // 4-byte container ID, 4-byte DRN f_UINT32ToBigEndian( (FLMUINT32)pRecord->getContainerID(), pucBufPos); pucBufPos += 4; f_UINT32ToBigEndian( (FLMUINT32)pRecord->getID(), pucBufPos); pucBufPos += 4; // Send the descriptor and record source. if( RC_BAD( rc = m_pDOStream->write( pucBuffer, pucBufPos - pucBuffer))) { goto Exit; } // Send the record. if( RC_BAD( rc = m_pDOStream->writeHTD( NULL, pRecord, FALSE, m_bSendGedcom))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendDrnList( FLMUINT uiTag, FLMUINT * puiList) { FLMUINT uiItemCount; FLMUINT uiLoop; FLMUINT uiBufSize = 0; FLMBYTE * pucItemBuf = NULL; FLMBYTE * pucItemPos; RCODE rc = FERR_OK; if( !puiList) { rc = RC_SET( FERR_MEM); goto Exit; } // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_DRN_LIST: { uiTag |= WIRE_VALUE_TYPE_BINARY << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } // Count the entries in the list. For now, support only a list of // 2048 elements. for( uiItemCount = 0; uiItemCount < 2048; uiItemCount++) { if( !puiList[ uiItemCount]) { break; } } // Allocate a buffer for the list. uiBufSize = (FLMUINT)(((FLMUINT)sizeof( FLMUINT) * uiItemCount) + (FLMUINT)4); if( RC_BAD( rc = f_calloc( uiBufSize, &pucItemBuf))) { goto Exit; } pucItemPos = pucItemBuf; // Set the item count. UD2FBA( (FLMUINT32)uiItemCount, pucItemPos); pucItemPos += 4; // Put the items into the buffer. for( uiLoop = 0; uiLoop < uiItemCount; uiLoop++) { UD2FBA( (FLMUINT32)puiList[ uiLoop], pucItemPos); pucItemPos += 4; } // Send the list. if( RC_BAD( rc = m_pDOStream->writeBinary( pucItemBuf, uiBufSize))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: if( pucItemBuf) { f_free( (void **)&pucItemBuf); } return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendString( FLMUINT uiTag, FLMUNICODE * puzString) { RCODE rc = FERR_OK; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_FILE_NAME: case WIRE_VALUE_FILE_PATH: case WIRE_VALUE_FILE_PATH_2: case WIRE_VALUE_FILE_PATH_3: case WIRE_VALUE_DICT_FILE_PATH: case WIRE_VALUE_ITEM_NAME: case WIRE_VALUE_DICT_BUFFER: { uiTag |= WIRE_VALUE_TYPE_UTF << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeUTF( puzString))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendHTD( FLMUINT uiTag, NODE * pHTD) { RCODE rc = FERR_OK; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_HTD: case WIRE_VALUE_ITERATOR_SELECT: case WIRE_VALUE_ITERATOR_FROM: case WIRE_VALUE_ITERATOR_WHERE: case WIRE_VALUE_ITERATOR_CONFIG: { uiTag |= WIRE_VALUE_TYPE_HTD << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeHTD( pHTD, NULL, TRUE, m_bSendGedcom))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendHTD( FLMUINT uiTag, FlmRecord * pRecord) { RCODE rc = FERR_OK; // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_HTD: { uiTag |= WIRE_VALUE_TYPE_HTD << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } if( RC_BAD( rc = m_pDOStream->writeHTD( NULL, pRecord, FALSE, m_bSendGedcom))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: return( rc); } /**************************************************************************** Desc: Copies the current HTD tree to the application's pool *****************************************************************************/ RCODE FCS_WIRE::getHTD( F_Pool * pPool, NODE ** ppTreeRV) { RCODE rc = FERR_OK; if( !m_pHTD) { *ppTreeRV = NULL; goto Exit; } if( (*ppTreeRV = GedCopy( pPool, GED_FOREST, m_pHTD)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendCreateOpts( FLMUINT uiTag, CREATE_OPTS * pCreateOpts) { NODE * pRootNd = NULL; void * pvMark = m_pPool->poolMark(); RCODE rc = FERR_OK; FLMUINT uiTmp; // If no create options, goto exit. if( !pCreateOpts) { rc = RC_SET( FERR_MEM); goto Exit; } // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_CREATE_OPTS: { uiTag |= WIRE_VALUE_TYPE_HTD << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } // Build the root node of the CreateOpts tree. if( (pRootNd = GedNodeMake( m_pPool, FCS_COPT_CONTEXT, &rc)) == NULL) { goto Exit; } // Add fields to the tree. if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_BLOCK_SIZE, (void *)&pCreateOpts->uiBlockSize, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_MIN_RFL_FILE_SIZE, (void *)&pCreateOpts->uiMinRflFileSize, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_MAX_RFL_FILE_SIZE, (void *)&pCreateOpts->uiMaxRflFileSize, 0, FLM_NUMBER_TYPE))) { goto Exit; } uiTmp = pCreateOpts->bKeepRflFiles ? 1 : 0; if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_KEEP_RFL_FILES, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } uiTmp = pCreateOpts->bLogAbortedTransToRfl ? 1 : 0; if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_LOG_ABORTED_TRANS, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_DEFAULT_LANG, (void *)&pCreateOpts->uiDefaultLanguage, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_VERSION, (void *)&pCreateOpts->uiVersionNum, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_APP_MAJOR_VER, (void *)&pCreateOpts->uiAppMajorVer, 0, FLM_NUMBER_TYPE))) { goto Exit; } if( RC_BAD( rc = gedAddField( m_pPool, pRootNd, FCS_COPT_APP_MINOR_VER, (void *)&pCreateOpts->uiAppMinorVer, 0, FLM_NUMBER_TYPE))) { goto Exit; } // Send the tree. if( RC_BAD( rc = m_pDOStream->writeHTD( pRootNd, NULL, TRUE, m_bSendGedcom))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: m_pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Sends a value to the client *****************************************************************************/ RCODE FCS_WIRE::sendNameTable( FLMUINT uiTag, F_NameTable * pNameTable) { void * pvMark = m_pPool->poolMark(); NODE * pRootNd; NODE * pNd; NODE * pItemIdNd; FLMUINT uiMaxNameChars = 1024; FLMUNICODE * puzItemName = NULL; FLMUINT uiId; FLMUINT uiType; FLMUINT uiSubType; FLMUINT uiNextPos; RCODE rc = FERR_OK; // If the name table pointer is invalid, goto exit. if( !pNameTable) { rc = RC_SET( FERR_MEM); goto Exit; } // Allocate a temporary name buffer if( RC_BAD( rc = m_pPool->poolAlloc( uiMaxNameChars * sizeof( FLMUNICODE), (void **)&puzItemName))) { goto Exit; } // Send the parameter tag and value. switch( uiTag) { case WIRE_VALUE_NAME_TABLE: { uiTag |= WIRE_VALUE_TYPE_HTD << WIRE_VALUE_TYPE_START_BIT; if( RC_BAD( rc = m_pDOStream->writeUShort( (FLMUINT16)uiTag))) { goto Exit; } // Build the root node of the name table tree. if( (pRootNd = GedNodeMake( m_pPool, FCS_NAME_TABLE_CONTEXT, &rc)) == NULL) { goto Exit; } uiNextPos = 0; while( pNameTable->getNextTagNumOrder( &uiNextPos, puzItemName, NULL, uiMaxNameChars * sizeof( FLMUNICODE), &uiId, &uiType, &uiSubType)) { if( (pItemIdNd = GedNodeMake( m_pPool, FCS_NAME_TABLE_ITEM_ID, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = GedPutUINT( m_pPool, pItemIdNd, uiId))) { goto Exit; } if( (pNd = GedNodeMake( m_pPool, FCS_NAME_TABLE_ITEM_NAME, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = GedPutUNICODE( m_pPool, pNd, puzItemName))) { goto Exit; } GedChildGraft( pItemIdNd, pNd, GED_LAST); if( (pNd = GedNodeMake( m_pPool, FCS_NAME_TABLE_ITEM_TYPE, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = GedPutUINT( m_pPool, pNd, uiType))) { goto Exit; } GedChildGraft( pItemIdNd, pNd, GED_LAST); if( (pNd = GedNodeMake( m_pPool, FCS_NAME_TABLE_ITEM_SUBTYPE, &rc)) == NULL) { goto Exit; } if( RC_BAD( rc = GedPutUINT( m_pPool, pNd, uiSubType))) { goto Exit; } GedChildGraft( pItemIdNd, pNd, GED_LAST); // Graft the item into the tree GedChildGraft( pRootNd, pItemIdNd, GED_LAST); // Release CPU to prevent CPU hog f_yieldCPU(); } // Send the tree. if( RC_BAD( rc = m_pDOStream->writeHTD( pRootNd, NULL, TRUE, m_bSendGedcom))) { goto Exit; } break; } default: { #ifdef FLM_DEBUG flmAssert( 0); #else rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; #endif } } Exit: m_pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Receives a record *****************************************************************************/ RCODE FCS_WIRE::receiveRecord( FlmRecord ** ppRecord) { FLMBYTE ucDescriptor = 0; FLMUINT uiIdLen = 0; FLMUINT32 ui32Container; FLMUINT32 ui32Drn; void * pvMark = m_pPool->poolMark(); FLMBOOL bHasId = FALSE; RCODE rc = FERR_OK; // Read the record. if( RC_BAD( rc = m_pDIStream->read( &ucDescriptor, 1, NULL))) { goto Exit; } uiIdLen = (FLMUINT)(ucDescriptor & RECORD_ID_SIZE_MASK); if( uiIdLen != RECORD_ID_SIZE) { rc = RC_SET( FERR_FAILURE); goto Exit; } else if( uiIdLen) { bHasId = TRUE; } // Read the record ID. if( bHasId) { if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Container))) { goto Exit; } if( RC_BAD( rc = m_pDIStream->readUInt( &ui32Drn))) { goto Exit; } } // Read the record. if( (ucDescriptor & RECORD_HAS_HTD_FLAG)) { if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool, ui32Container, ui32Drn, NULL, ppRecord))) { goto Exit; } } Exit: if( RC_BAD( rc) && ppRecord && *ppRecord) { (*ppRecord)->Release(); *ppRecord = NULL; } m_pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Receives a CREATE_OPTS structure as an HTD tree. *****************************************************************************/ RCODE FCS_WIRE::receiveCreateOpts( void) { NODE * pRootNd; NODE * pTmpNd; void * pPoolMark; FLMUINT fieldPath[ 8]; FLMUINT uiTmp; RCODE rc = FERR_OK; pPoolMark = m_pPool->poolMark(); // Initialize the CREATE_OPTS structure to its default values. fcsInitCreateOpts( &m_CreateOpts); // Receive the tree. if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &pRootNd, NULL))) { goto Exit; } // Parse the tree and extract the values. fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_BLOCK_SIZE; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiBlockSize); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_MIN_RFL_FILE_SIZE; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiMinRflFileSize); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_MAX_RFL_FILE_SIZE; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiMaxRflFileSize); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_KEEP_RFL_FILES; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); m_CreateOpts.bKeepRflFiles = (FLMBOOL)((uiTmp) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_LOG_ABORTED_TRANS; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &uiTmp); m_CreateOpts.bLogAbortedTransToRfl = (FLMBOOL)((uiTmp) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_DEFAULT_LANG; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiDefaultLanguage); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_VERSION; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiVersionNum); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_APP_MAJOR_VER; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiAppMajorVer); } fieldPath[ 0] = FCS_COPT_CONTEXT; fieldPath[ 1] = FCS_COPT_APP_MINOR_VER; fieldPath[ 2] = 0; if( (pTmpNd = GedPathFind( GED_TREE, pRootNd, fieldPath, 1)) != NULL) { (void) GedGetUINT( pTmpNd, &m_CreateOpts.uiAppMinorVer); } Exit: m_pPool->poolReset( pPoolMark); return( rc); } /**************************************************************************** Desc: Receives a name table. *****************************************************************************/ RCODE FCS_WIRE::receiveNameTable( F_NameTable ** ppNameTable) { NODE * pRootNd; NODE * pItemIdNd; NODE * pNd = NULL; void * pvMark = m_pPool->poolMark(); FLMUINT uiMaxNameChars = 1024; FLMUNICODE * puzItemName; FLMUINT uiItemId; FLMUINT uiItemType; FLMUINT uiItemSubType; F_NameTable * pNameTable = NULL; FLMBOOL bCreatedTable = FALSE; RCODE rc = FERR_OK; // Allocate a temporary name buffer if( RC_BAD( rc = m_pPool->poolAlloc( uiMaxNameChars * sizeof( FLMUNICODE), (void **)&puzItemName))) { goto Exit; } // Initialize the name table. if( (pNameTable = *ppNameTable) == NULL) { if( (pNameTable = f_new F_NameTable) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } bCreatedTable = TRUE; } else { pNameTable->clearTable(); } // Receive the tree. if( RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &pRootNd, NULL))) { goto Exit; } // Parse the tree and extract the values. pItemIdNd = GedChild( pRootNd); while( pItemIdNd) { if( GedTagNum( pItemIdNd) == FCS_NAME_TABLE_ITEM_ID) { if( RC_BAD( rc = GedGetUINT( pItemIdNd, &uiItemId))) { goto Exit; } uiItemType = 0; uiItemSubType = 0; pNd = GedChild( pItemIdNd); while( pNd) { switch( GedTagNum( pNd)) { case FCS_NAME_TABLE_ITEM_NAME: { FLMUINT uiStrLen = uiMaxNameChars * sizeof( FLMUNICODE); if( RC_BAD( rc = GedGetUNICODE( pNd, puzItemName, &uiStrLen))) { goto Exit; } break; } case FCS_NAME_TABLE_ITEM_TYPE: { if( RC_BAD( rc = GedGetUINT( pNd, &uiItemType))) { goto Exit; } break; } case FCS_NAME_TABLE_ITEM_SUBTYPE: { if( RC_BAD( rc = GedGetUINT( pNd, &uiItemSubType))) { goto Exit; } break; } } pNd = GedSibNext( pNd); } if( puzItemName[ 0]) { if( RC_BAD( rc = pNameTable->addTag( puzItemName, NULL, uiItemId, uiItemType, uiItemSubType, FALSE))) { goto Exit; } } } pItemIdNd = GedSibNext( pItemIdNd); // Release CPU to prevent CPU hog f_yieldCPU(); } pNameTable->sortTags(); *ppNameTable = pNameTable; pNameTable = NULL; Exit: if( pNameTable && bCreatedTable) { pNameTable->Release(); } m_pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ FCL_WIRE::FCL_WIRE( CS_CONTEXT * pCSContext, FDB * pDb) : FCS_WIRE( pCSContext != NULL ? pCSContext->pIDataStream : NULL, pCSContext != NULL ? pCSContext->pODataStream : NULL) { m_pCSContext = pCSContext; m_pDb = pDb; if( m_pCSContext) { m_bSendGedcom = m_pCSContext->bGedcomSupport; } } /**************************************************************************** Desc: Sets the CS CONTEXT in FCL_WIRE and the I/O streams in FCS_WIRE *****************************************************************************/ void FCL_WIRE::setContext( CS_CONTEXT * pCSContext) { m_pCSContext = pCSContext; m_bSendGedcom = pCSContext->bGedcomSupport; FCS_WIRE::setDIStream( pCSContext->pIDataStream); FCS_WIRE::setDOStream( pCSContext->pODataStream); } /**************************************************************************** Desc: Send a client/server opcode with session id, and optionally the database id ****************************************************************************/ RCODE FCL_WIRE::sendOp( FLMUINT uiClass, FLMUINT uiOp) { RCODE rc = FERR_OK; if (!m_pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Exit; } // Send the class and opcode if (RC_BAD( rc = sendOpcode( (FLMBYTE)uiClass, (FLMBYTE)uiOp))) { goto Transmission_Error; } // Send session ID if (RC_BAD( rc = sendNumber( WIRE_VALUE_SESSION_ID, m_pCSContext->uiSessionId))) { goto Transmission_Error; } // Send session cookie if (RC_BAD( rc = sendNumber( WIRE_VALUE_SESSION_COOKIE, m_pCSContext->uiSessionCookie))) { goto Transmission_Error; } // Send operation sequence number m_pCSContext->uiOpSeqNum++; if (RC_BAD( rc = sendNumber( WIRE_VALUE_OP_SEQ_NUM, m_pCSContext->uiOpSeqNum))) { goto Transmission_Error; } Exit: return( rc); Transmission_Error: m_pCSContext->bConnectionGood = FALSE; goto Exit; } /**************************************************************************** Desc: This routine instructs the server to start or end a transaction ****************************************************************************/ RCODE FCL_WIRE::doTransOp( FLMUINT uiOp, FLMUINT uiTransType, FLMUINT uiFlags, FLMUINT uiMaxLockWait, FLMBYTE * pszHeader, FLMBOOL bForceCheckpoint) { FLMUINT uiTransFlags = 0; RCODE rc = FERR_OK; // Send request to server if( RC_BAD( rc = sendOp( FCS_OPCLASS_TRANS, uiOp))) { goto Exit; } if( uiOp == FCS_OP_TRANSACTION_BEGIN) { if (RC_BAD( rc = sendNumber( WIRE_VALUE_TRANSACTION_TYPE, uiTransType))) { goto Transmission_Error; } if (RC_BAD( rc = sendNumber( WIRE_VALUE_MAX_LOCK_WAIT, uiMaxLockWait))) { goto Transmission_Error; } if( pszHeader) { uiTransFlags |= FCS_TRANS_FLAG_GET_HEADER; } if( uiFlags & FLM_DONT_KILL_TRANS) { uiTransFlags |= FCS_TRANS_FLAG_DONT_KILL; } if( uiFlags & FLM_DONT_POISON_CACHE) { uiTransFlags |= FCS_TRANS_FLAG_DONT_POISON; } } else if( uiOp == FCS_OP_TRANSACTION_COMMIT_EX) { if( pszHeader) { if( RC_BAD( rc = sendBinary( WIRE_VALUE_BLOCK, pszHeader, F_TRANS_HEADER_SIZE))) { goto Exit; } } if( bForceCheckpoint) { uiTransFlags |= FCS_TRANS_FORCE_CHECKPOINT; } } if( uiTransFlags) { if (RC_BAD( rc = sendNumber( WIRE_VALUE_FLAGS, uiTransFlags))) { goto Transmission_Error; } } if( RC_BAD( rc = sendTerminate())) { goto Transmission_Error; } // Read the response if( RC_BAD( rc = read())) { goto Transmission_Error; } if (RC_BAD( rc = getRCode())) { goto Exit; } if( pszHeader) { if( getBlockSize()) { f_memcpy( pszHeader, getBlock(), getBlockSize()); } else { f_memset( pszHeader, 0, 2048); } } if (!m_pDb) { m_pCSContext->bTransActive = (FLMBOOL)((uiOp == FCS_OP_TRANSACTION_BEGIN) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); } Exit: return( rc); Transmission_Error: m_pCSContext->bConnectionGood = FALSE; goto Exit; } /**************************************************************************** Desc: Reads a server response for the client. *****************************************************************************/ RCODE FCL_WIRE::read( void) { FLMUINT uiTag; FLMUINT uiCount = 0; FLMBOOL bDone = FALSE; RCODE rc = FERR_OK; // Read the opcode. if( RC_BAD( rc = readOpcode())) { goto Exit; } // Read the request / response values. for( ;;) { if (RC_BAD( rc = readCommon( &uiTag, &bDone))) { if( rc == FERR_EOF_HIT && !uiCount) { rc = FERR_OK; } goto Exit; } if( bDone) { goto Exit; } // uiTag will be non-zero if readCommon did not understand it. uiCount++; if( uiTag) { switch( (uiTag & WIRE_VALUE_TAG_MASK)) { case WIRE_VALUE_NAME_TABLE: { if( RC_BAD( rc = receiveNameTable( &m_pNameTable))) { goto Exit; } break; } default: { if( RC_BAD( rc = skipValue( uiTag))) { goto Exit; } break; } } } } Exit: if( rc == FERR_EOF_HIT) { rc = RC_SET( FERR_FAILURE); goto Exit; } return( rc); } libflaim-4.9.966/src/flerror.cpp0000644000175000017500000003416110510774540020040 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Error routines. // Tabs: 3 // // Copyright (c) 1997-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flerror.cpp 12262 2006-01-19 14:42:10 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" // WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE // REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaim.h const char * FlmCorruptStrings[ FLM_LAST_CORRUPT_ERROR] = { "OK", // 0 "BAD_CHAR", // 1 "BAD_ASIAN_CHAR", // 2 "BAD_CHAR_SET", // 3 "BAD_TEXT_FIELD", // 4 "BAD_NUMBER_FIELD", // 5 "BAD_CONTEXT_FIELD", // 6 "BAD_FIELD_TYPE", // 7 "BAD_IX_DEF", // 8 "MISSING_REQ_KEY_FIELD", // 9 "BAD_TEXT_KEY_COLL_CHAR", // 10 "BAD_TEXT_KEY_CASE_MARKER", // 11 "BAD_NUMBER_KEY", // 12 "BAD_CONTEXT_KEY", // 13 "BAD_BINARY_KEY", // 14 "BAD_DRN_KEY", // 15 "BAD_KEY_FIELD_TYPE", // 16 "BAD_KEY_COMPOUND_MARKER", // 17 "BAD_KEY_POST_MARKER", // 18 "BAD_KEY_POST_BYTE_COUNT", // 19 "BAD_KEY_LEN", // 20 "BAD_LFH_LIST_PTR", // 21 "BAD_LFH_LIST_END", // 22 "BAD_PCODE_LIST_END", // 23 "BAD_BLK_END", // 24 "KEY_COUNT_MISMATCH", // 25 "REF_COUNT_MISMATCH", // 26 "BAD_CONTAINER_IN_KEY", // 27 "BAD_BLK_HDR_ADDR", // 28 "BAD_BLK_HDR_LEVEL", // 29 "BAD_BLK_HDR_PREV", // 30 "BAD_BLK_HDR_NEXT", // 31 "BAD_BLK_HDR_TYPE", // 32 "BAD_BLK_HDR_ROOT_BIT", // 33 "BAD_BLK_HDR_BLK_END", // 34 "BAD_BLK_HDR_LF_NUM", // 35 "BAD_AVAIL_LIST_END", // 36 "BAD_PREV_BLK_NEXT", // 37 "BAD_FIRST_ELM_FLAG", // 38 "BAD_LAST_ELM_FLAG", // 39 "BAD_LEM", // 40 "BAD_ELM_LEN", // 41 "BAD_ELM_KEY_SIZE", // 42 "BAD_ELM_PKC_LEN", // 43 "BAD_ELM_KEY_ORDER", // 44 "BAD_ELM_KEY_COMPRESS", // 45 "BAD_CONT_ELM_KEY", // 46 "NON_UNIQUE_FIRST_ELM_KEY", // 47 "BAD_ELM_FLD_OVERHEAD", // 48 "BAD_ELM_FLD_LEVEL_JUMP", // 49 "BAD_ELM_FLD_NUM", // 50 "BAD_ELM_FLD_LEN", // 51 "BAD_ELM_FLD_TYPE", // 52 "BAD_ELM_END", // 53 "BAD_PARENT_KEY", // 54 "BAD_ELM_DOMAIN_SEN", // 55 "BAD_ELM_BASE_SEN", // 56 "BAD_ELM_IX_REF", // 57 "BAD_ELM_ONE_RUN_SEN", // 58 "BAD_ELM_DELTA_SEN", // 59 "BAD_ELM_DOMAIN", // 60 "BAD_LAST_BLK_NEXT", // 61 "BAD_FIELD_PTR", // 62 "REBUILD_REC_EXISTS", // 63 "REBUILD_KEY_NOT_UNIQUE", // 64 "NON_UNIQUE_ELM_KEY_REF", // 65 "OLD_VIEW", // 66 "COULD_NOT_SYNC_BLK", // 67 "IX_REF_REC_NOT_FOUND", // 68 "IX_KEY_NOT_FOUND_IN_REC", // 69 "DRN_NOT_IN_KEY_REFSET", // 70 "BAD_BLK_CHECKSUM", // 71 "BAD_LAST_DRN", // 72 "BAD_FILE_SIZE", // 73 "BAD_AVAIL_BLOCK_COUNT", // 74 "BAD_DATE_FIELD", // 75 "BAD_TIME_FIELD", // 76 "BAD_TMSTAMP_FIELD", // 77 "BAD_DATE_KEY", // 78 "BAD_TIME_KEY", // 79 "BAD_TMSTAMP_KEY", // 80 "BAD_BLOB_FIELD", // 81 "BAD_PCODE_IXD_TBL", // 82 "DICT_REC_ADD_ERR", // 83 "FLM_BAD_FIELD_FLAG", // 84 "FLM_BAD_FOP", // 85 }; /**************************************************************************** Desc: The primary purpose of this function is to provide a way to easily trap errors when they occur. Just put a breakpoint in this function to catch them. Note: Some of the most common errors will be coded so the use can set a break point. ****************************************************************************/ #ifdef FLM_DEBUG RCODE flmMakeErr( RCODE rc, const char * pszFile, int iLine, FLMBOOL bAssert) { if( rc == FERR_OK) { return FERR_OK; } // Switch on warning type return codes if( rc <= FERR_NOT_FOUND) { switch(rc) { case FERR_BOF_HIT: break; case FERR_EOF_HIT: break; case FERR_END: break; case FERR_EXISTS: break; case FERR_FAILURE: break; case FERR_NOT_FOUND: break; default: break; } return( rc); } switch(rc) { case FERR_DATA_ERROR: case FERR_BAD_RFL_PACKET: flmAssert( 0); flmLogError( rc, "", pszFile, iLine); break; case FERR_BTREE_ERROR: flmLogError( rc, "", pszFile, iLine); break; case FERR_MEM: break; case FERR_OLD_VIEW: break; case FERR_SYNTAX: break; case FERR_BLOCK_CHECKSUM: flmLogError( rc, "", pszFile, iLine); break; case FERR_CACHE_ERROR: flmLogError( rc, "", pszFile, iLine); break; case FERR_BLOB_MISSING_FILE: break; case FERR_CONV_BAD_DIGIT: break; case FERR_NOT_IMPLEMENTED: break; case FERR_BAD_REFERENCE: break; case FERR_IO_ACCESS_DENIED: break; case FERR_IO_PATH_NOT_FOUND: break; case FERR_UNSUPPORTED_FEATURE: break; case FERR_ENCRYPTION_UNAVAILABLE: break; default: rc = rc; break; } if( bAssert) { flmAssert( 0); } return( rc); } #endif /**************************************************************************** Desc : Returns a pointer to the string representation of a corruption error code. ****************************************************************************/ const char * FLMAPI FlmVerifyErrToStr( eCorruptionType eCorruption) { return( FlmCorruptStrings [eCorruption]); } /**************************************************************************** Desc: Returns a pointer to the ASCII string representation of a FLAIM return code. ****************************************************************************/ FLMEXP const char * FLMAPI FlmErrorString( RCODE rc) { const char * pszStr; if( (pszStr = flmErrorString( rc)) == NULL) { pszStr = "Unknown Error"; } return( pszStr); } /**************************************************************************** Desc: Returns a pointer to a static RCODE string or NULL if the RCODE cannot be mapped ****************************************************************************/ const char * flmErrorString( RCODE rc) { if( RC_OK( rc)) { return( "FERR_OK"); } #define CASE_RET( c) \ case c: return( #c ) switch( rc) { CASE_RET( FERR_BOF_HIT); CASE_RET( FERR_EOF_HIT); CASE_RET( FERR_END); CASE_RET( FERR_EXISTS); CASE_RET( FERR_FAILURE); CASE_RET( FERR_NOT_FOUND); CASE_RET( FERR_BAD_DICT_ID); CASE_RET( FERR_BAD_CONTAINER); CASE_RET( FERR_NO_ROOT_BLOCK); CASE_RET( FERR_BAD_DRN); CASE_RET( FERR_BAD_FIELD_NUM); CASE_RET( FERR_BAD_FIELD_TYPE); CASE_RET( FERR_BAD_HDL); CASE_RET( FERR_BAD_IX); CASE_RET( FERR_BACKUP_ACTIVE); CASE_RET( FERR_SERIAL_NUM_MISMATCH); CASE_RET( FERR_BAD_RFL_DB_SERIAL_NUM); CASE_RET( FERR_BTREE_ERROR); CASE_RET( FERR_BTREE_FULL); CASE_RET( FERR_BAD_RFL_FILE_NUMBER); CASE_RET( FERR_CANNOT_DEL_ITEM); CASE_RET( FERR_CANNOT_MOD_FIELD_TYPE); CASE_RET( FERR_CONV_BAD_DEST_TYPE); CASE_RET( FERR_CONV_BAD_DIGIT); CASE_RET( FERR_CONV_BAD_SRC_TYPE); CASE_RET( FERR_RFL_FILE_NOT_FOUND); CASE_RET( FERR_CONV_DEST_OVERFLOW); CASE_RET( FERR_CONV_ILLEGAL); CASE_RET( FERR_CONV_NULL_SRC); CASE_RET( FERR_CONV_NULL_DEST); CASE_RET( FERR_CONV_NUM_OVERFLOW); CASE_RET( FERR_CONV_NUM_UNDERFLOW); CASE_RET( FERR_DATA_ERROR); CASE_RET( FERR_DD_ERROR); CASE_RET( FERR_INVALID_FILE_SEQUENCE); CASE_RET( FERR_ILLEGAL_OP); CASE_RET( FERR_DUPLICATE_DICT_REC); CASE_RET( FERR_CANNOT_CONVERT); CASE_RET( FERR_UNSUPPORTED_VERSION); CASE_RET( FERR_FILE_ER); CASE_RET( FERR_BAD_FIELD_LEVEL); CASE_RET( FERR_GED_BAD_RECID); CASE_RET( FERR_GED_BAD_VALUE); CASE_RET( FERR_GED_MAXLVLNUM); CASE_RET( FERR_GED_SKIP_LEVEL); CASE_RET( FERR_ILLEGAL_TRANS); CASE_RET( FERR_ILLEGAL_TRANS_OP); CASE_RET( FERR_INCOMPLETE_LOG); CASE_RET( FERR_INVALID_BLOCK_LENGTH); CASE_RET( FERR_INVALID_TAG); CASE_RET( FERR_KEY_NOT_FOUND); CASE_RET( FERR_VALUE_TOO_LARGE); CASE_RET( FERR_MEM); CASE_RET( FERR_BAD_RFL_SERIAL_NUM); CASE_RET( FERR_NEWER_FLAIM); CASE_RET( FERR_CANNOT_MOD_FIELD_STATE); CASE_RET( FERR_NO_MORE_DRNS); CASE_RET( FERR_NO_TRANS_ACTIVE); CASE_RET( FERR_NOT_UNIQUE); CASE_RET( FERR_NOT_FLAIM); CASE_RET( FERR_NULL_RECORD); CASE_RET( FERR_NO_HTTP_STACK); CASE_RET( FERR_OLD_VIEW); CASE_RET( FERR_PCODE_ERROR); CASE_RET( FERR_PERMISSION); CASE_RET( FERR_SYNTAX); CASE_RET( FERR_CALLBACK_FAILURE); CASE_RET( FERR_TRANS_ACTIVE); CASE_RET( FERR_RFL_TRANS_GAP); CASE_RET( FERR_BAD_COLLATED_KEY); CASE_RET( FERR_UNSUPPORTED_FEATURE); CASE_RET( FERR_MUST_DELETE_INDEXES); CASE_RET( FERR_RFL_INCOMPLETE); CASE_RET( FERR_CANNOT_RESTORE_RFL_FILES); CASE_RET( FERR_INCONSISTENT_BACKUP); CASE_RET( FERR_BLOCK_CHECKSUM); CASE_RET( FERR_ABORT_TRANS); CASE_RET( FERR_NOT_RFL); CASE_RET( FERR_BAD_RFL_PACKET); CASE_RET( FERR_DATA_PATH_MISMATCH); CASE_RET( FERR_HTTP_REGISTER_FAILURE); CASE_RET( FERR_HTTP_DEREG_FAILURE); CASE_RET( FERR_IX_FAILURE); CASE_RET( FERR_HTTP_SYMS_EXIST); CASE_RET( FERR_FILE_EXISTS); CASE_RET( FERR_SYM_RESOLVE_FAIL); CASE_RET( FERR_BAD_SERVER_CONNECTION); CASE_RET( FERR_CLOSING_DATABASE); CASE_RET( FERR_INVALID_CRC); CASE_RET( FERR_KEY_OVERFLOW); CASE_RET( FERR_NOT_IMPLEMENTED); CASE_RET( FERR_MUTEX_OPERATION_FAILED); CASE_RET( FERR_MUTEX_UNABLE_TO_LOCK); CASE_RET( FERR_SEM_OPERATION_FAILED); CASE_RET( FERR_SEM_UNABLE_TO_LOCK); CASE_RET( FERR_BAD_REFERENCE); CASE_RET( FERR_UNALLOWED_UPGRADE); CASE_RET( FERR_ID_RESERVED); CASE_RET( FERR_CANNOT_RESERVE_ID); CASE_RET( FERR_DUPLICATE_DICT_NAME); CASE_RET( FERR_CANNOT_RESERVE_NAME); CASE_RET( FERR_BAD_DICT_DRN); CASE_RET( FERR_CANNOT_MOD_DICT_REC_TYPE); CASE_RET( FERR_PURGED_FLD_FOUND); CASE_RET( FERR_DUPLICATE_INDEX); CASE_RET( FERR_TOO_MANY_OPEN_DBS); CASE_RET( FERR_ACCESS_DENIED); CASE_RET( FERR_CACHE_ERROR); CASE_RET( FERR_BLOB_MISSING_FILE); CASE_RET( FERR_NO_REC_FOR_KEY); CASE_RET( FERR_DB_FULL); CASE_RET( FERR_TIMEOUT); CASE_RET( FERR_CURSOR_SYNTAX); CASE_RET( FERR_THREAD_ERR); CASE_RET( FERR_UNIMPORT_SYMBOL); CASE_RET( FERR_EMPTY_QUERY); CASE_RET( FERR_INDEX_OFFLINE); CASE_RET( FERR_TRUNCATED_KEY); CASE_RET( FERR_INVALID_PARM); CASE_RET( FERR_USER_ABORT); CASE_RET( FERR_RFL_DEVICE_FULL); CASE_RET( FERR_MUST_WAIT_CHECKPOINT); CASE_RET( FERR_NAMED_SEMAPHORE_ERR); CASE_RET( FERR_LOAD_LIBRARY); CASE_RET( FERR_UNLOAD_LIBRARY); CASE_RET( FERR_IMPORT_SYMBOL); CASE_RET( FERR_BLOCK_FULL); CASE_RET( FERR_BAD_BASE64_ENCODING); CASE_RET( FERR_MISSING_FIELD_TYPE); CASE_RET( FERR_BAD_DATA_LENGTH); CASE_RET( FERR_IO_ACCESS_DENIED); CASE_RET( FERR_IO_BAD_FILE_HANDLE); CASE_RET( FERR_IO_COPY_ERR); CASE_RET( FERR_IO_DISK_FULL); CASE_RET( FERR_IO_END_OF_FILE); CASE_RET( FERR_IO_OPEN_ERR); CASE_RET( FERR_IO_SEEK_ERR); CASE_RET( FERR_IO_DIRECTORY_ERR); CASE_RET( FERR_IO_PATH_NOT_FOUND); CASE_RET( FERR_IO_TOO_MANY_OPEN_FILES); CASE_RET( FERR_IO_PATH_TOO_LONG); CASE_RET( FERR_IO_NO_MORE_FILES); CASE_RET( FERR_DELETING_FILE); CASE_RET( FERR_IO_FILE_LOCK_ERR); CASE_RET( FERR_IO_FILE_UNLOCK_ERR); CASE_RET( FERR_IO_PATH_CREATE_FAILURE); CASE_RET( FERR_IO_RENAME_FAILURE); CASE_RET( FERR_IO_INVALID_PASSWORD); CASE_RET( FERR_SETTING_UP_FOR_READ); CASE_RET( FERR_SETTING_UP_FOR_WRITE); CASE_RET( FERR_IO_AT_PATH_ROOT); CASE_RET( FERR_INITIALIZING_IO_SYSTEM); CASE_RET( FERR_FLUSHING_FILE); CASE_RET( FERR_IO_INVALID_PATH); CASE_RET( FERR_IO_CONNECT_ERROR); CASE_RET( FERR_OPENING_FILE); CASE_RET( FERR_DIRECT_OPENING_FILE); CASE_RET( FERR_CREATING_FILE); CASE_RET( FERR_DIRECT_CREATING_FILE); CASE_RET( FERR_READING_FILE); CASE_RET( FERR_DIRECT_READING_FILE); CASE_RET( FERR_WRITING_FILE); CASE_RET( FERR_DIRECT_WRITING_FILE); CASE_RET( FERR_POSITIONING_IN_FILE); CASE_RET( FERR_GETTING_FILE_SIZE); CASE_RET( FERR_TRUNCATING_FILE); CASE_RET( FERR_PARSING_FILE_NAME); CASE_RET( FERR_CLOSING_FILE); CASE_RET( FERR_GETTING_FILE_INFO); CASE_RET( FERR_EXPANDING_FILE); CASE_RET( FERR_GETTING_FREE_BLOCKS); CASE_RET( FERR_CHECKING_FILE_EXISTENCE); CASE_RET( FERR_RENAMING_FILE); CASE_RET( FERR_SETTING_FILE_INFO); CASE_RET( FERR_NICI_CONTEXT); CASE_RET( FERR_NICI_FIND_INIT); CASE_RET( FERR_NICI_FIND_OBJECT); CASE_RET( FERR_NICI_WRAPKEY_NOT_FOUND); CASE_RET( FERR_NICI_ATTRIBUTE_VALUE); CASE_RET( FERR_NICI_BAD_ATTRIBUTE); CASE_RET( FERR_NICI_BAD_RANDOM); CASE_RET( FERR_NICI_WRAPKEY_FAILED); CASE_RET( FERR_NICI_GENKEY_FAILED); CASE_RET( FERR_REQUIRE_PASSWD); CASE_RET( FERR_NICI_SHROUDKEY_FAILED); CASE_RET( FERR_NICI_UNSHROUDKEY_FAILED); CASE_RET( FERR_NICI_UNWRAPKEY_FAILED); CASE_RET( FERR_NICI_ENC_INIT_FAILED); CASE_RET( FERR_NICI_ENCRYPT_FAILED); CASE_RET( FERR_NICI_DECRYPT_INIT_FAILED); CASE_RET( FERR_NICI_DECRYPT_FAILED); CASE_RET( FERR_NICI_INIT_FAILED); CASE_RET( FERR_NICI_KEY_NOT_FOUND); CASE_RET( FERR_NICI_INVALID_ALGORITHM); CASE_RET( FERR_FLD_NOT_ENCRYPTED); CASE_RET( FERR_BAD_ENCDEF_ID); CASE_RET( FERR_CANNOT_SET_KEY); CASE_RET( FERR_MISSING_ENC_TYPE); CASE_RET( FERR_CANNOT_MOD_ENC_TYPE); CASE_RET( FERR_MISSING_ENC_KEY); CASE_RET( FERR_CANNOT_CHANGE_KEY); CASE_RET( FERR_BAD_ENC_KEY); CASE_RET( FERR_CANNOT_MOD_ENC_STATE); CASE_RET( FERR_DATA_SIZE_MISMATCH); CASE_RET( FERR_ENCRYPTION_UNAVAILABLE); CASE_RET( FERR_PURGED_ENCDEF_FOUND); CASE_RET( FERR_FLD_NOT_DECRYPTED); CASE_RET( FERR_PBE_ENCRYPT_FAILED); CASE_RET( FERR_DIGEST_FAILED); CASE_RET( FERR_DIGEST_INIT_FAILED); CASE_RET( FERR_EXTRACT_KEY_FAILED); CASE_RET( FERR_INJECT_KEY_FAILED); CASE_RET( FERR_PBE_DECRYPT_FAILED); CASE_RET( FERR_PASSWD_INVALID); default: return( NULL); } } libflaim-4.9.966/src/imonerr.cpp0000644000175000017500000001236010510774540020035 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Error page for HTTP monitoring. // Tabs: 3 // // Copyright (c) 2001-2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonerr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Page that is displayed when a URL is requested that we don't know how to fill ****************************************************************************/ RCODE F_ErrorPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; // Can't use a call to stdHdr() because we want to send back a 404 error fnSetHdrValue( "Content-Type", "text/html"); fnSetNoCache( NULL); fnSendHeader( HTS_NOT_FOUND); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "Error Page\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "

That which you seek is not available.

\n"); fnPrintf( m_pHRequest, "

\n Number of Parameters: %ld
\n", uiNumParams); for (FLMUINT uiLoop = 0; uiLoop < uiNumParams; uiLoop++) { fnPrintf( m_pHRequest, "Parameter %ld:\t%s
\n", uiLoop, ppszParams[uiLoop]); } fnPrintf( m_pHRequest, "

\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /**************************************************************************** Desc: Page that is displayed when a URL is requested for a page requireing secure access but the Global security is not enabled or has expired. ****************************************************************************/ RCODE F_GblAccessPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "Global Access Error Page\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); // We're putting this script here to force the Nav bar to reload. The // reason for this because of the case where one user disables the secure // access stuff after a second user has successfully logged in. When the // second user attempts to load a page requiring secure access he or she // will get this page. If the nav bar is not reloaded then it will still // be indicating that the user is logged in and the user will have no idea // why this page is coming up. If the nav bar is reloaded, it will at // least give a clue (though not an obvious one) as to what has happened. fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "The page you are attempting to view requires " "secure access. The secure access either has not been enabled or " "it has expired. To activate secure access, you must select the " "\"Access Code\" link in the navigation bar and enter the " "enabling data provided to you by Novell Inc. You will then need " "to enter the secure access password.\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /**************************************************************************** Desc: Page that is displayed when a URL is requested for a page requireing secure access but the Session security is not enabled. A password is required. ****************************************************************************/ RCODE F_SessionAccessPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "Session Access Error Page\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "The page you are attempting to view requires " "secure access. The session level access has not been enabled." " To activate session level secure access, you must enter the " "secure access password.\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } libflaim-4.9.966/src/furl.h0000644000175000017500000000551610510774540017004 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: URL parsing - definitions. // Tabs: 3 // // Copyright (c) 1998-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: furl.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #ifndef FURL_H #define FURL_H #include "fpackon.h" // IMPORTANT NOTE: No other include files should follow this one except // for fpackoff.h #define NO_SUB_PROTOCOL -1 #define TCP_SUB_PROTOCOL 1 #define STREAM_SUB_PROTOCOL 2 class FUrl; typedef FUrl * FUrl_p; /**************************************************************************** Desc: The FUrl class is for dealing with URLs. ****************************************************************************/ class FUrl: public F_Object { private: FLMINT m_iSubProtocol; // Sub-Protocol to use, -1 if none. FLMBOOL m_bRelative; // Relative to server directory or area? FLMBOOL m_bLocal; // URL references a local file char * m_pucAlloc; // Memory allocation. char * m_pszHostName; // Host name string, NULL if none. char * m_pszFileName; // File name string. char * m_pszIPName; // IP name string, NULL if none. FLMUINT m_uiAddrType; // Address type: IP, IPX, etc. FLMUINT32 m_ui32IPAddr; // IP Address, 0 if not yet set. FLMINT m_iPort; // Port number, -1 if none. char m_pszAddr[ FLM_CS_MAX_ADDR_LEN]; public: FUrl(); virtual ~FUrl(); void Reset(); RCODE SetUrl( const char * pszUrlStr); FINLINE const char * GetFile( void) { return( (const char *)m_pszFileName); } FINLINE const char * GetIPHost( void) { return( (const char *)m_pszHostName); } FINLINE const char * GetAddress( void) { return( (const char *)m_pszAddr); } FINLINE FLMINT GetIPPort( void) { return m_iPort; } FINLINE FLMBOOL GetRelative( void) { return m_bRelative; } FINLINE FLMINT GetSubProtocol( void) { return m_iSubProtocol; } FINLINE FLMUINT GetAddrType( void) { return m_uiAddrType; } FINLINE FLMBOOL IsLocal( void) { return m_bLocal; } }; #include "fpackoff.h" #endif libflaim-4.9.966/src/fsrefupd.cpp0000644000175000017500000007214010510774540020202 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Index reference updating. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsrefupd.cpp 12321 2006-01-19 15:55:00 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" extern FLMBYTE SENLenArray[]; #define INSERT_REF 0 #define DELETE_REF 1 #define SPLIT_90_10 0 #define SPLIT_50_50 1 FSTATIC RCODE FSUpdateIxCounts( FDB * pDb, IXD * pIxd, FLMBYTE byFlags, FLMBOOL bSingleRef); FSTATIC RCODE FSOutputIxCounts( FDB * pDb, IX_STATS * pIxStats); FSTATIC RCODE FSRefCreateRec( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack); FSTATIC RCODE FSRefInsert( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack); FSTATIC RCODE FSRefDelete( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack, FLMBOOL * pbSingleRef); /**************************************************************************** Desc: Update (add or delete) a single reference ****************************************************************************/ RCODE FSRefUpdate( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry) { RCODE rc = FERR_OK; BTSK stackBuf[BH_MAX_LEVELS]; BTSK * pStack = stackBuf; FLMUINT uiDinDomain = DIN_DOMAIN( pKrefEntry->uiDrn) + 1; FLMBYTE byFlags = (FLMBYTE) pKrefEntry->uiFlags; FLMBOOL bSingleRef = FALSE; FLMBOOL bAddReference = (byFlags & KREF_DELETE_FLAG) ? FALSE : TRUE; FLMBYTE pKeyBuf[MAX_KEY_SIZ]; FSRU_try_again: if (pKrefEntry->uiFlags & KREF_ENCRYPTED_KEY) { // Can't allow updates whit these keys. flmAssert( pDb->pFile->bInLimitedMode); rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } FSInitStackCache( &stackBuf[0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, (FLMBYTE*) &pKrefEntry[1], pKrefEntry->ui16KeyLen, uiDinDomain))) { goto Exit; } // If pStack->bsStatus == REC_NOT_FOUND create a new element if found // then add the reference into the found element. if (pStack->uiCmpStatus == BT_EQ_KEY) { if ((byFlags & KREF_UNIQUE_KEY) && !(byFlags & KREF_DELETE_FLAG)) { rc = RC_SET( FERR_NOT_UNIQUE); goto Exit; } if (pLFile->pIxd->uiFlags & IXD_POSITIONING) { if (RC_BAD( rc = FSChangeCount( pDb, pStack, bAddReference))) { goto Exit; } } bSingleRef = FALSE; if (bAddReference) { if (RC_BAD( rc = FSRefInsert( pDb, pLFile, pKrefEntry, pStack))) { goto Exit; } } else { if (RC_BAD( rc = FSRefDelete( pDb, pLFile, pKrefEntry, pStack, &bSingleRef))) { goto Exit; } } } else { if (!bAddReference) { // Already been deleted, ignore the error condition and go on. flmAssert( 0); rc = FERR_OK; goto Exit; } // The B-Tree may be empty if (pLFile->uiRootBlk == BT_END) { if (RC_BAD( rc = flmLFileInit( pDb, pLFile))) { goto Exit; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); goto FSRU_try_again; } if (pLFile->pIxd->uiFlags & IXD_POSITIONING) { if (RC_BAD( rc = FSChangeCount( pDb, pStack, TRUE))) { goto Exit; } } // Add new key|reference element. bSingleRef = TRUE; if (RC_BAD( rc = FSRefCreateRec( pDb, pLFile, pKrefEntry, pStack))) { goto Exit; } } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); if (RC_OK( rc) && (pLFile->pIxd->uiFlags & IXD_COUNT)) { rc = FSUpdateIxCounts( pDb, pLFile->pIxd, byFlags, bSingleRef); } return (rc); } /**************************************************************************** Desc: Update the index reference and/or key counts in memory only. Counts are not written to the database until transaction commit time. ****************************************************************************/ FSTATIC RCODE FSUpdateIxCounts( FDB * pDb, IXD * pIxd, FLMBYTE byFlags, FLMBOOL bSingleRef) { RCODE rc = FERR_OK; IX_STATS * pIxStat; // See if we have already created an IX_STATS structure in memory pIxStat = pDb->pIxStats; while (pIxStat && pIxStat->uiIndexNum != pIxd->uiIndexNum) { pIxStat = pIxStat->pNext; } // Allocate an IX_STATS if we didn't find one. if (!pIxStat) { if (RC_BAD( rc = f_calloc( sizeof(IX_STATS), &pIxStat))) { goto Exit; } pIxStat->uiIndexNum = pIxd->uiIndexNum; pIxStat->pNext = pDb->pIxStats; pDb->pIxStats = pIxStat; } // Determine whether to increment or decrement the counts if (byFlags & KREF_DELETE_FLAG) { if (bSingleRef) { pIxStat->iDeltaKeys--; } pIxStat->iDeltaRefs--; } else { if (bSingleRef) { pIxStat->iDeltaKeys++; } pIxStat->iDeltaRefs++; } Exit: return (rc); } /**************************************************************************** Desc: Output the index reference and/or key counts by writing them to the tracker record. ****************************************************************************/ FSTATIC RCODE FSOutputIxCounts( FDB * pDb, IX_STATS * pIxStats) { RCODE rc = FERR_OK; FLMBOOL bCreateRec; FlmRecord * pRecord = NULL; FlmRecord * pTmpRec = NULL; void * pvField; FLMUINT uiCount; LFILE * pLFile; // If no counts changed, do nothing if (!pIxStats->iDeltaKeys && !pIxStats->iDeltaRefs) { goto Exit; } if (RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_TRACKER_CONTAINER, &pLFile))) { goto Exit; } // Retrieve the tracker record from record cache. if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER, pIxStats->uiIndexNum, TRUE, NULL, NULL, &pRecord))) { if (rc != FERR_NOT_FOUND) { goto Exit; } bCreateRec = TRUE; rc = FERR_OK; } else { bCreateRec = FALSE; } // If there was no record, create one. Otherwise, copy the record if (bCreateRec) { if ((pTmpRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Create at least the root node. if (RC_BAD( rc = pTmpRec->insertLast( 0, FLM_INDEX_TAG, FLM_CONTEXT_TYPE, &pvField))) { goto Exit; } } else { if ((pTmpRec = pRecord->copy()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } // Update the key count. if (pIxStats->iDeltaKeys) { if ((pvField = pTmpRec->find( pTmpRec->root(), FLM_KEY_TAG)) == NULL) { // Cannot make key count negative. if (pIxStats->iDeltaKeys < 0) { flmAssert( 0); pIxStats->iDeltaKeys = 0; } if (RC_BAD( rc = pTmpRec->insert( pTmpRec->root(), INSERT_LAST_CHILD, FLM_KEY_TAG, FLM_NUMBER_TYPE, &pvField))) { goto Exit; } uiCount = (FLMUINT) pIxStats->iDeltaKeys; } else { if (RC_BAD( rc = pTmpRec->getUINT( pvField, &uiCount))) { goto Exit; } if (pIxStats->iDeltaKeys < 0) { pIxStats->iDeltaKeys = -pIxStats->iDeltaKeys; if ((FLMUINT) pIxStats->iDeltaKeys <= uiCount) { uiCount -= (FLMUINT) pIxStats->iDeltaKeys; } else { // Key count cannot go negative. flmAssert( 0); uiCount = 0; } } else { uiCount += (FLMUINT) pIxStats->iDeltaKeys; } } if (RC_BAD( rc = pTmpRec->setUINT( pvField, uiCount))) { goto Exit; } } // Update the references count if (pIxStats->iDeltaRefs) { if ((pvField = pTmpRec->find( pTmpRec->root(), FLM_REFS_TAG)) == NULL) { // Cannot make reference count negative. if (pIxStats->iDeltaRefs < 0) { flmAssert( 0); pIxStats->iDeltaRefs = 0; } if (RC_BAD( rc = pTmpRec->insert( pTmpRec->root(), INSERT_LAST_CHILD, FLM_REFS_TAG, FLM_NUMBER_TYPE, &pvField))) { goto Exit; } uiCount = (FLMUINT) pIxStats->iDeltaRefs; } else { if (RC_BAD( rc = pTmpRec->getUINT( pvField, &uiCount))) { goto Exit; } if (pIxStats->iDeltaRefs < 0) { pIxStats->iDeltaRefs = -pIxStats->iDeltaRefs; if ((FLMUINT) pIxStats->iDeltaRefs <= uiCount) { uiCount -= (FLMUINT) pIxStats->iDeltaRefs; } else { // Reference count cannot go negative. flmAssert( 0); uiCount = 0; } } else { uiCount += (FLMUINT) pIxStats->iDeltaRefs; } } if (RC_BAD( rc = pTmpRec->setUINT( pvField, uiCount))) { goto Exit; } } // Update or add the record. pTmpRec->setID( pIxStats->uiIndexNum); pTmpRec->setContainerID( FLM_TRACKER_CONTAINER); if (bCreateRec) { if (RC_BAD( rc = FSRecUpdate( pDb, pLFile, pTmpRec, pIxStats->uiIndexNum, REC_UPD_ADD))) { goto Exit; } // Put the record into record cache. if (RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, pIxStats->uiIndexNum, pTmpRec))) { // Remove the record that was added. (void) FSRecUpdate( pDb, pLFile, NULL, pIxStats->uiIndexNum, REC_UPD_DELETE); goto Exit; } } else { // Modify the record. if (RC_BAD( rc = FSRecUpdate( pDb, pLFile, pTmpRec, pIxStats->uiIndexNum, REC_UPD_MODIFY))) { goto Exit; } // Put the modified record into record cache. if (RC_BAD( rc = flmRcaInsertRec( pDb, pLFile, pIxStats->uiIndexNum, pTmpRec))) { // Undo the record that was modified - replace with original // record. (void) FSRecUpdate( pDb, pLFile, pRecord, pIxStats->uiIndexNum, REC_UPD_MODIFY); goto Exit; } } Exit: if (pRecord) { pRecord->Release(); } if (pTmpRec) { pTmpRec->Release(); } return (rc); } /**************************************************************************** Desc: Free the index stats structures for an FDB. ****************************************************************************/ void FSFreeIxCounts( FDB * pDb) { IX_STATS * pNextIxStat; while (pDb->pIxStats) { pNextIxStat = pDb->pIxStats->pNext; f_free( &pDb->pIxStats); pDb->pIxStats = pNextIxStat; } } /**************************************************************************** Desc: Commit the index reference and/or key counts for a database. This routine is only called at commit time. ****************************************************************************/ RCODE FSCommitIxCounts( FDB * pDb) { RCODE rc = FERR_OK; IX_STATS * pNextIxStat; while (pDb->pIxStats) { pNextIxStat = pDb->pIxStats->pNext; if (RC_BAD( rc = FSOutputIxCounts( pDb, pDb->pIxStats))) { goto Exit; } f_free( &pDb->pIxStats); pDb->pIxStats = pNextIxStat; } Exit: if (RC_BAD( rc)) { FSFreeIxCounts( pDb); } return (rc); } /**************************************************************************** Desc: Create a new record and add the reference to it. Note: The record size is the size of the only SEN value. There is no overhead for references for the entry compression ****************************************************************************/ FSTATIC RCODE FSRefCreateRec( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack) { RCODE rc = FERR_OK; FLMUINT uiElmSize; FLMUINT uiKeyLen = pKrefEntry->ui16KeyLen; FLMUINT uiRecLen; FLMBYTE * pSen; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + SEN_MAX_SIZ]; // Create the element overhead byElmBuf[BBE_PKC] = BBE_FIRST_FLAG | BBE_LAST_FLAG; BBE_SET_KL( byElmBuf, uiKeyLen); // Copy in the key f_memcpy( &byElmBuf[BBE_KEY], (FLMBYTE*) &pKrefEntry[1], uiKeyLen); uiElmSize = (BBE_KEY + uiKeyLen); pSen = &byElmBuf[uiElmSize]; uiRecLen = SENPutNextVal( &pSen, pKrefEntry->uiDrn); uiElmSize += BBE_SET_RL( byElmBuf, uiRecLen); // Save the element if (RC_BAD( rc = FSBtInsert( pDb, pLFile, &pStack, byElmBuf, uiElmSize))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Insert a reference into a key|reference list Note: The algorithm positions for an insert and inserts the reference while altering the block. If a reference split is required refSplit() is called and FSRefInsert() could be recursively called. ****************************************************************************/ FSTATIC RCODE FSRefInsert( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack) { RCODE rc; FLMBYTE * pElement = CURRENT_ELM( pStack); FLMUINT uiElmLen = (FLMUINT) BBE_GET_RL( pElement); FLMUINT uiElmSize; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + REF_SET_MAX_SIZ]; // Build the element in byElmBuf[] FSSetElmOvhd( byElmBuf, BBE_KEY, 0, pStack->uiKeyLen, pElement); f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, pStack->uiKeyLen); if (uiElmLen > REF_SET_MAX_SIZ) { // May or may not split the reference set, but will always insert DRN rc = FSRefSplit( pDb, pLFile, &pStack, byElmBuf, pKrefEntry->uiDrn, INSERT_REF, (FLMBYTE) (BBE_IS_FIRST( pElement) ? SPLIT_50_50 : SPLIT_90_10)); } else { uiElmSize = BBE_KEY + pStack->uiKeyLen; if (FSSetInsertRef( &byElmBuf[uiElmSize], BBE_REC_PTR( pElement), pKrefEntry->uiDrn, &uiElmLen)) { rc = RC_SET( FERR_BTREE_ERROR); // Entry is already there goto Exit; } BBE_SET_RL( byElmBuf, uiElmLen); rc = FSBtReplace( pDb, pLFile, &pStack, byElmBuf, uiElmSize + uiElmLen); } Exit: return (rc); } /**************************************************************************** Desc: Insert a single reference into a reference set. This is the code that supports the new DIN (Dual Integer Numbers) format for reference set compression. ****************************************************************************/ RCODE FSSetInsertRef( FLMBYTE * pDestRef, FLMBYTE * pSrcRef, FLMUINT drn, FLMUINT * puiSetLength) { DIN_STATE destState; DIN_STATE srcState; FLMUINT uiSetLength = *puiSetLength; FLMUINT uiLastDrn; FLMUINT uiOldDelta; FLMUINT uiDelta; FLMUINT uiNewDelta; FLMUINT uiOneRun; FLMUINT uiPrevOneRun; FLMUINT uiLastSrcOfs; FLMUINT uiPrevLastSrcOfs = 0; FLMUINT uiMoveLen; FLMBYTE byValue; // Initialization Section uiPrevOneRun = uiOldDelta = 0; uiLastSrcOfs = 0; RESET_DINSTATE( destState); RESET_DINSTATE( srcState); // Take care of the domain value. if (*pSrcRef == SEN_DOMAIN) { srcState.uiOffset = 1; DINNextVal( pSrcRef, &srcState); uiLastSrcOfs = srcState.uiOffset; } uiLastDrn = DINNextVal( pSrcRef, &srcState); if (drn > uiLastDrn) { // ADD TO THE FRONT. Set uiDelta and uiNewDelta uiNewDelta = (uiDelta = drn) - uiLastDrn; goto FSSIR_add_delta; } uiOldDelta = uiLastDrn; uiOneRun = 0; // Search through the set finding where the "drn" fits in. while (drn < uiLastDrn) { // Save previous last source offset & previous one runs uiPrevLastSrcOfs = uiLastSrcOfs; uiLastSrcOfs = srcState.uiOffset; uiPrevOneRun = uiOneRun; uiOneRun = 0; if (srcState.uiOffset >= uiSetLength) { // APPEND TO THE END uiDelta = uiLastDrn - drn; uiNewDelta = 0; goto FSSIR_add_delta; } // Check for a run of ONE's byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_ONE_RUN( byValue)) { // Read the number of one runs uiOneRun = DINOneRunVal( pSrcRef, &srcState); uiLastDrn -= uiOneRun; if (drn > uiLastDrn) { uiLastDrn = drn; } } else { uiOldDelta = DINNextVal( pSrcRef, &srcState); uiLastDrn -= uiOldDelta; } } // Check for duplicates if (drn == uiLastDrn) { // Duplicate found or inside of one run return (RC_SET( FERR_BTREE_ERROR)); } uiDelta = uiLastDrn + uiOldDelta - drn; uiNewDelta = drn - uiLastDrn; FSSIR_add_delta: // COMPRESS MULTIPLE ONE RUNS Special case if delta is 1. Check the // previous destinaion DIN for a 1 or a run of ones and combine to the // left. Also check to combine a one run on the right by jumping within // add_new_delta. if (uiDelta == 1) { uiOneRun = 1; if (uiPrevOneRun) { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiPrevLastSrcOfs); uiOneRun += uiPrevOneRun; } else { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); } if (uiNewDelta == 1) { uiOneRun++; goto FSSIR_combine_right_one_runs; } // uiNewDelta is not 1 so write to destination and go on DINPutOneRunVal( pDestRef, &destState, uiOneRun); } else { f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); // No one runs found so write the delta value DINPutNextVal( pDestRef, &destState, uiDelta); } // Add new delta value (uiOldDelta - uiDelta). If the new delta value is // 1 then check next DIN for a 1 run and combine with the uiNewDelta // run. uiNewDelta only has a value when NOT appending to the end. if (uiNewDelta) { if (uiNewDelta != 1) { DINPutNextVal( pDestRef, &destState, uiNewDelta); } else { uiOneRun = 1; FSSIR_combine_right_one_runs: if (srcState.uiOffset < uiSetLength) { // CONNECT ONE RUNS Check on the right side of the source for // another 1 run. byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_ONE_RUN( byValue)) { uiOneRun += DINOneRunVal( pSrcRef, &srcState); } } DINPutOneRunVal( pDestRef, &destState, uiOneRun); } } // FSSIR_move_rest_of_dins: if ((uiMoveLen = (FLMUINT) (uiSetLength - srcState.uiOffset)) != 0) { f_memcpy( &pDestRef[destState.uiOffset], &pSrcRef[srcState.uiOffset], uiMoveLen); destState.uiOffset += uiMoveLen; } *puiSetLength = destState.uiOffset; return (FERR_OK); } /**************************************************************************** Desc: Delete a matching reference into a key|reference list. If this was the last continuation element then remove the SEN_DOMAIN value from the previous element. ****************************************************************************/ FSTATIC RCODE FSRefDelete( FDB * pDb, LFILE * pLFile, KREF_ENTRY * pKrefEntry, BTSK * pStack, FLMBOOL * pbSingleRef) { RCODE rc = FERR_OK; FLMBYTE * pElement = CURRENT_ELM( pStack); FLMBYTE * pReference; FLMBYTE * pDestRefPtr; FLMUINT uiElmLen = BBE_GET_RL( pElement); FLMUINT uiElmSize; FLMUINT uiSetLength; FLMUINT uiKeyLen; FLMUINT uiSenLen; FLMBYTE byElmBuf[MAX_KEY_SIZ + BBE_KEY + REF_SET_MAX_SIZ]; // Build the element in byElmBuf[] FSSetElmOvhd( byElmBuf, BBE_KEY, 0, pStack->uiKeyLen, pElement); f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, pStack->uiKeyLen); pDestRefPtr = &byElmBuf[BBE_KEY + pStack->uiKeyLen]; uiElmSize = (BBE_KEY + pStack->uiKeyLen); if (uiElmLen > REF_SET_MAX_SIZ) { // Straight deletions MAY overflow the record portion rc = FSRefSplit( pDb, pLFile, &pStack, byElmBuf, pKrefEntry->uiDrn, DELETE_REF, SPLIT_50_50); return (rc); } pReference = BBE_REC_PTR( pElement); uiSetLength = uiElmLen; if (FSSetDeleteRef( pDestRefPtr, pReference, pKrefEntry->uiDrn, &uiSetLength)) { goto Exit; } BBE_SET_RL( byElmBuf, uiSetLength); uiElmSize += uiSetLength; // Get the reference length and hasDomainFlag if (uiSetLength) { if (*pReference == SEN_DOMAIN) { uiSetLength--; uiSetLength -= SENValLen( pReference + 1); } } if (uiSetLength == 0) { FLMBOOL bLastElm = BBE_IS_LAST( pElement); FLMBOOL bFirstElm = BBE_IS_FIRST( pElement); // Delete the element if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack))) { return (rc); } // Remove the LAST (only) reference in an element. There are 4 cases // to consider that deal with continuation elements: // 1. ONLY element - no list - ALL DONE // 2. FIRST of many in a list - Set BBE_FIRST_FLAG in the // next element // 3. MIDDLE of a list - ALL DONE // 4. LAST in a list Remove the domain from the previous element and // set the BBE_LAST_FLAG in the new last element. if (bFirstElm && bLastElm) // Case 1 - ONLY element { *pbSingleRef = TRUE; } else if (bFirstElm && (!bLastElm)) // Case 2 - first of many { // Log the block before modifying it. if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { return (rc); } pElement = CURRENT_ELM( pStack); BBE_SET_FIRST( pElement); } else if (!bFirstElm && bLastElm) // Case 4 - Last in a list { // Element was either the ONLY or LAST in a list. FSBtPrevElm( pDb, pLFile, pStack); // Goto the previous element pElement = CURRENT_ELM( pStack); pReference = BBE_REC_PTR( pElement); uiSetLength = BBE_GET_RL( pElement); uiKeyLen = pStack->uiKeyLen; // Build the element in byElmBuf[] - sets BBE_FIRST_FLAG if set FSSetElmOvhd( byElmBuf, BBE_KEY, 0, uiKeyLen, pElement); BBE_SET_LAST( byElmBuf); // Set last element flag f_memcpy( &byElmBuf[BBE_KEY], pStack->pKeyBuf, uiKeyLen); pDestRefPtr = &byElmBuf[BBE_KEY + uiKeyLen]; // Parse past the element information and delete the domain data if (*pReference != SEN_DOMAIN) { return (RC_SET( FERR_BTREE_ERROR)); } uiSenLen = (SENValLen( pReference + 1) + 1); pReference += uiSenLen; uiSetLength -= uiSenLen; f_memcpy( pDestRefPtr, pReference, uiSetLength); BBE_SET_RL( byElmBuf, uiSetLength); uiElmSize = (BBE_KEY + uiKeyLen + uiSetLength); // We could call FSBtReplace() except that if the element is the // last element in the block, the parent element will still // contain the 3 byte non-leaf DOMAIN number which should no // longer exist. In addition, the BBE_LAST_FLAG flag should be // set in the current element and replace doesn't do that. if (RC_BAD( rc = FSBtDelete( pDb, pLFile, &pStack))) { return (rc); } // POSSIBLE CHECK YOU MAY FORGET... You could have deleted the // ONLY element in the tree. Check and if so init a new root // block and position for insert. if (pLFile->uiRootBlk == BT_END) { if (RC_BAD( rc = flmLFileInit( pDb, pLFile))) { return (rc); } // Call FSBtSearch to setup the pStack, would rather do a goto top; if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, (FLMBYTE*) &byElmBuf[BBE_KEY], uiKeyLen, 0))) { return (rc); } } else { if (RC_BAD( rc = FSBtScanTo( pStack, &byElmBuf[BBE_KEY], uiKeyLen, 0))) { goto Exit; } } rc = FSBtInsert( pDb, pLFile, &pStack, byElmBuf, uiElmSize); } } else { // Replace the current element - wElmSize has had uiSetLength added // to it rc = FSBtReplace( pDb, pLFile, &pStack, byElmBuf, uiElmSize); } if (RC_BAD( rc)) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Delete a single reference into a reference set. This is the code that supports the new DIN (Dual Integer Numbers) format for reference set compression. ****************************************************************************/ RCODE FSSetDeleteRef( FLMBYTE * pDestRef, FLMBYTE * pSrcRef, FLMUINT drn, FLMUINT * puiSetLength) { DIN_STATE destState; DIN_STATE srcState; FLMUINT uiSetLength = *puiSetLength; FLMUINT uiMoveLen; FLMUINT uiLastSrcOfs; FLMUINT uiLastDrn; FLMUINT uiOldDelta; FLMUINT uiOneRun; FLMUINT uiTemp; FLMBYTE byValue; RESET_DINSTATE( destState); RESET_DINSTATE( srcState); uiLastSrcOfs = 0; // Take care of the domain value if (*pSrcRef == SEN_DOMAIN) { srcState.uiOffset = 1; uiLastDrn = DINNextVal( pSrcRef, &srcState); uiLastSrcOfs = srcState.uiOffset; } uiLastDrn = DINNextVal( pSrcRef, &srcState); if (drn > uiLastDrn) { // Greater than the first reference return (RC_SET( FERR_KEY_NOT_FOUND)); } if (drn == uiLastDrn) { // MATCHED THE FIRST REFERENCE Write the replacement starting DRN // and fix 1 run if there. if (uiLastSrcOfs) { // If domain - copy it f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); } if (srcState.uiOffset >= uiSetLength) { // Only 1 reference goto FSSDR_done; } byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { // Run of 2 or above DINPutNextVal( pDestRef, &destState, uiLastDrn - 1); DINPutOneRunVal( pDestRef, &destState, DINOneRunVal( pSrcRef, &srcState) - 1); } else { uiLastDrn -= DINNextVal( pSrcRef, &srcState); DINPutNextVal( pDestRef, &destState, uiLastDrn); } goto FSSDR_move_rest_of_dins; } uiOldDelta = uiLastDrn; uiOneRun = 0; // Search through the set finding where the "din" fits in. Similar // while loop as the while loop in FSSetInsertRef() above. while (drn < uiLastDrn) { uiLastSrcOfs = srcState.uiOffset; uiOneRun = 0; if (srcState.uiOffset >= uiSetLength) { return (RC_SET( FERR_KEY_NOT_FOUND)); } // Check for a run of ONE's byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { // Read the number of one runs uiOneRun = DINOneRunVal( pSrcRef, &srcState); uiLastDrn -= uiOneRun; } else { uiOldDelta = DINNextVal( pSrcRef, &srcState); uiLastDrn -= uiOldDelta; } } f_memcpy( pDestRef, pSrcRef, destState.uiOffset = uiLastSrcOfs); if (uiOneRun) { // Divide out a one run. drn may match the first, middle or last // value of a one run. Copy up to the last source value to dest. uiLastDrn += uiOneRun; if (drn == uiLastDrn - 1) { // Remove the first value from the run - run is >= 2 DINPutNextVal( pDestRef, &destState, 2); if (uiOneRun > 2) { DINPutOneRunVal( pDestRef, &destState, uiOneRun - 2); } goto FSSDR_move_rest_of_dins; } else { DINPutOneRunVal( pDestRef, &destState, (uiLastDrn - drn) - 1); if (drn > uiLastDrn - uiOneRun) { FLMUINT oneRun2ndHalf = uiOneRun - (uiLastDrn - drn) - 1; DINPutNextVal( pDestRef, &destState, 2); if (oneRun2ndHalf) { DINPutOneRunVal( pDestRef, &destState, oneRun2ndHalf); } goto FSSDR_move_rest_of_dins; } else { // Delete from the END of a one run uiOldDelta = 1; goto FSSDR_combine_2_deltas; } } // Should never reach here! } // Check for duplicates else if (drn != uiLastDrn) { return (RC_SET( FERR_KEY_NOT_FOUND)); } // Hit a non-one run where drn == uiLastDrn Check the next DIN value // and add uiOldDelta to it UNLESS it is a one run, then write // uiOldDelta+1 and if uiOneRun-1 is non-zero then write uiOneRun-1 if (srcState.uiOffset < uiSetLength) { byValue = pSrcRef[srcState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { DINPutNextVal( pDestRef, &destState, uiOldDelta + 1); uiOneRun = DINOneRunVal( pSrcRef, &srcState) - 1; DINPutOneRunVal( pDestRef, &destState, uiOneRun); } else { FSSDR_combine_2_deltas: if (srcState.uiOffset >= uiSetLength) { goto FSSDR_done; } uiTemp = DINNextVal( pSrcRef, &srcState); DINPutNextVal( pDestRef, &destState, uiOldDelta + uiTemp); } } FSSDR_move_rest_of_dins: if ((uiMoveLen = (FLMUINT) (uiSetLength - srcState.uiOffset)) != 0) { f_memcpy( &pDestRef[destState.uiOffset], &pSrcRef[srcState.uiOffset], uiMoveLen); destState.uiOffset += uiMoveLen; } FSSDR_done: *puiSetLength = destState.uiOffset; return (FERR_OK); } /**************************************************************************** Desc: Put a SEN value into a buffer - return the length of storage used ****************************************************************************/ FLMUINT SENPutNextVal( FLMBYTE ** pSenRV, FLMUINT senValue) { FLMBYTE * pSen = *pSenRV; FLMUINT uiSenLen; if (senValue <= SEN_1B_VAL) { *pSen++ = (FLMBYTE) senValue; } else if (senValue <= SEN_2B_VAL) { *pSen++ = (FLMBYTE) (SEN_2B_CODE + (FLMBYTE) ((senValue >> 8) & SEN_2B_MASK)); *pSen++ = (FLMBYTE) senValue; } else if (senValue <= SEN_3B_VAL) { *pSen++ = (FLMBYTE) (SEN_3B_CODE + (FLMBYTE) ((senValue >> 16) & SEN_3B_MASK)); goto SENPV_2_bytes; } else if (senValue <= SEN_4B_VAL) { *pSen++ = (FLMBYTE) (SEN_4B_CODE + (FLMBYTE) ((senValue >> 24) & SEN_4B_MASK)); goto SENPV_3_bytes; } else { *pSen++ = SEN_5B_CODE; *pSen++ = (FLMBYTE) (senValue >> 24); SENPV_3_bytes: *pSen++ = (FLMBYTE) (senValue >> 16); SENPV_2_bytes: *pSen++ = (FLMBYTE) (senValue >> 8); *pSen++ = (FLMBYTE) senValue; } uiSenLen = (FLMUINT) (pSen -*pSenRV); *pSenRV = pSen; return (uiSenLen); } /**************************************************************************** Desc: Put the next one run value - high level ****************************************************************************/ FLMUINT DINPutOneRunVal( FLMBYTE * dinPtr, DIN_STATE * state, FLMUINT uiValue) { FLMUINT uiLength = 1; FLMUINT uiOffset = state->uiOffset; FLMBYTE * pOneRun; if (uiValue == 1) { dinPtr[uiOffset] = 1; } else if (uiValue <= DIN_MAX_1B_ONE_RUN) { dinPtr[uiOffset] = (FLMBYTE) (DIN_ONE_RUN_LV | (((FLMBYTE) uiValue) - 2)); } else { dinPtr[uiOffset] = DIN_ONE_RUN_HV; pOneRun = &dinPtr[uiOffset + 1]; uiLength += SENPutNextVal( &pOneRun, uiValue); } state->uiOffset += uiLength; return (uiLength); } libflaim-4.9.966/src/fquery.h0000644000175000017500000003760010510774540017346 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query structures and classes. // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fquery.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #ifndef FQUERY_H #define FQUERY_H #include "fpackon.h" // IMPORTANT NOTE: No other include files should follow this one except // for fpackoff.h class FSIndexCursor; class FSDataCursor; #define FLM_ALL_BOOL (FLM_TRUE | FLM_FALSE | FLM_UNK) #define FLM_MAX_POS_KEYS 1000 #define FLM_MIN_POS_KEYS 250 #define FLM_ADDR_GROW_SIZE 100 #define FLM_KEYS_GROW_SIZE 100 #define MAX_SIGNED_VAL 2147483647L // FQNODE status flags #define FLM_RESOLVE_UNK 0x01 #define FLM_NOTTED 0x02 #define FLM_FOR_EVERY 0x04 #define FLM_RESULT_MASK 0xF00 #define FLM_SET_RESULT(uiStatus,uiResult) \ ((uiStatus) = ((uiStatus) & (~(FLM_RESULT_MASK))) | (FLMUINT)((uiResult) << 8)) #define FLM_GET_RESULT(uiStatus) \ (FLMUINT)(((uiStatus) & (FLM_RESULT_MASK)) >> 8) /**************************************************************************** Macro: Desc: Macros used for determining the nature and precedence of OP codes. ****************************************************************************/ #define GET_QNODE_TYPE( x) ((x)->eOpType) #define IS_OP( e) (( (e) >= FIRST_OP) && ( (e) <= LAST_OP)) #define IS_COMM_ASSOC( e) (((e) == FLM_AND_OP) || \ ((e) == FLM_OR_OP) || \ ((e) == FLM_MULT_OP) || \ ((e) == FLM_PLUS_OP) || \ ((e) == FLM_BITAND_OP) || \ ((e) == FLM_BITOR_OP) || \ ((e) == FLM_BITXOR_OP)) #define IS_LOG_OP( e) (( (e) >= FIRST_LOG_OP) && \ ( (e) <= LAST_LOG_OP)) #define IS_COMPARE_OP( e) ((e) >= FIRST_COMPARE_OP && \ (e) <= LAST_COMPARE_OP) #define IS_ARITH_OP( e) ((e) >= FIRST_ARITH_OP && \ (e) <= LAST_ARITH_OP) #define IS_VAL( e) ((e) >= FIRST_VALUE && \ (e) <= LAST_VALUE) #define IS_FIELD( e) ((e) == FLM_FLD_PATH || \ (e) == FLM_CB_FLD) #define IS_FLD_CB(eOp,pQNode) \ (((eOp) == FLM_FLD_PATH) && \ ((pQNode)->pQAtom->val.QueryFld.fnGetField) && \ (!((pQNode)->pQAtom->val.QueryFld.bValidateOnly))) #define IS_BUF_TYPE( e) ((e) == FLM_TEXT_VAL || \ (e) == FLM_BINARY_VAL) #define DO_REVERSE( e) (((e) == FLM_GT_OP) ? FLM_LT_OP : \ ((e) == FLM_LT_OP) ? FLM_GT_OP : \ ((e) == FLM_GE_OP) ? FLM_LE_OP : \ ((e) == FLM_LE_OP) ? FLM_GE_OP : (e)) #define REVERSIBLE( e) ((((e) == FLM_CONTAINS_OP) || \ ((e) == FLM_MATCH_BEGIN_OP)) \ ? FALSE : TRUE) // NOTE: IS_DRN_FUNC does not include FLM_CURSOR_CURRENT_DRN because it is // used in places where that particular API is never being executed. #define IS_DRN_FUNC( x) (( (x) == FLM_CURSOR_FIRST_DRN) || \ ( (x) == FLM_CURSOR_NEXT_DRN) || \ ( (x) == FLM_CURSOR_LAST_DRN) || \ ( (x) == FLM_CURSOR_PREV_DRN)) #define FQ_COMPARE(left,right) ( \ ((left) == (right)) ? 0 : \ (((left) < (right)) ? -1 : 1 )) /**************************************************************************** Desc: ****************************************************************************/ typedef struct F_QUERY_FLD { FLMUINT * puiFldPath; // In child-to-parent order. FLMUINT * puiPToCPath; // In parent-to-child order. CURSOR_GET_FIELD_CB fnGetField; FLMBOOL bValidateOnly; void * pvUserData; FLMUINT uiUserDataLen; } F_QUERY_FLD; /**************************************************************************** Desc: ****************************************************************************/ typedef struct FQATOM { FQATOM * pNext; // Atoms are chained in some areas. FlmRecord * pFieldRec; QTYPES eType; // From enum qTypes in FLAIM.H. // Describes this atom. Value from 0 // to FLM_Q_MAX_TYPES. FLMUINT uiFlags; FLMUINT uiBufLen; // Length if the type is text or binary union { FLMUINT uiBool; FLMUINT32 ui32Val; FLMINT32 i32Val; FLMUINT64 ui64Val; FLMINT64 i64Val; F_TIME Time; F_DATE Date; F_TMSTAMP TimeStamp; F_QUERY_FLD QueryFld; FlmUserPredicate * pPredicate; FLMBYTE * pucBuf; } val; // Holds or points to the atom value. } FQATOM; /**************************************************************************** Desc: ****************************************************************************/ typedef struct FQNODE { QTYPES eOpType; // Type of QueryNode - see enum qTypes FLMUINT uiNestLvl; // Nesting level of query node. FLMUINT uiStatus; // Status of node - defs in FLAIM.h FQNODE * pParent; // Parent of this query node FQNODE * pPrevSib; // Previous sibling of this query node FQNODE * pNextSib; // Next sibling of this query node FQNODE * pChild; // Child of this query node FQATOM * pQAtom; // Atomic value(s) of this query node } FQNODE; /**************************************************************************** Desc: ****************************************************************************/ typedef struct QPREDICATE { FQNODE * pPredNode; // Root node of this predicate - unless // it is an exists operator or not exists, // it will point at the operator node. // Otherwise, it will point at the field // node. FLMUINT * puiFldPath; // Field path in child-to-parent order. // Points to field path in FQATOM. FLMBOOL bFldSingleValued; // Field is single valued. QTYPES eOperator; // Operator of the predicate - comes from // pPredNode. FLMBOOL bNotted; // Has operator been notted? FQATOM * pVal; // Points to FQATOM that has the value for // this predicate. Will be NULL for unary // operators such as exists, not exists, etc. FLMBOOL bEvaluatedNullRec;// Have we tested this against a NULL record // yet? FLMBOOL bReturnsTrueOnNullRec; // Does this predicate return NULL when // evaluated on an empty record? QPREDICATE * pNext; // Next predicate in the list. } QPREDICATE; /**************************************************************************** Desc: ****************************************************************************/ typedef struct QFIELD_PREDICATE { QPREDICATE * pPredicate; // Pointer to predicate IFD * pIfd; // IFD for this predicate's field. FLMUINT uiRank; // Ranking of this predicate with respect // to this IFD. QFIELD_PREDICATE * pNext; // Next predicate involving same field } QFIELD_PREDICATE; /**************************************************************************** Desc: ****************************************************************************/ typedef struct QINDEX { FLMUINT uiIndexNum; FLMUINT uiNumFields; FLMBOOL bDoRecMatch; FLMBOOL bPredicatesRequireMatch; // Either record match // or key match is required. // It doesn't matter which. FLMBOOL bMultiplePredsOnIfd; // Some IFD has multiple // predicates. IXD * pIxd; QFIELD_PREDICATE ** ppFieldPredicateList; // One for each IFD FLMUINT uiNumPredicatesCovered; FLMUINT uiRank; QINDEX * pNext; QINDEX * pPrev; } QINDEX; /**************************************************************************** Desc: ****************************************************************************/ typedef struct QTINFO { FQNODE * pTopNode; FQNODE * pCurOpNode; FQNODE * pCurAtomNode; FQNODE * pSaveQuery; FLMUINT uiNestLvl; // Number of unclosed left parens FLMUINT uiExpecting; // Next thing that should be pushed #define FLM_Q_PAREN 1 #define FLM_Q_OPERATOR 2 #define FLM_Q_OPERAND 4 FLMUINT uiFlags; // Text flags (case sensitivity, // wildcards), Granularity flags // (sentence, paragraph, etc), #define MAX_USER_PREDICATES 4 FlmUserPredicate * Predicates [MAX_USER_PREDICATES]; FlmUserPredicate ** ppPredicates; FLMUINT uiMaxPredicates; FLMUINT uiNumPredicates; } QTINFO; /**************************************************************************** Desc: ****************************************************************************/ typedef struct SET_DEL { FLMBYTE * pKeyBuf; FLMUINT uiKeyBufLen; FLMUINT uiDrn; FLMUINT uiCurrBufSize; FLMUINT uiAttr; } SET_DEL; /**************************************************************************** Desc: ****************************************************************************/ typedef struct FQKEY { SET_DEL FromKey; SET_DEL UntilKey; FQKEY * pNext; FQKEY * pPrev; } FQKEY; /**************************************************************************** Desc: ****************************************************************************/ typedef struct POS_KEY { FLMBYTE * pucKey; FLMUINT uiKeyLen; FLMUINT uiDrn; // May be domain if not leaf level key. } POS_KEY; /**************************************************************************** Desc: ****************************************************************************/ typedef struct SUBQUERY { SUBQUERY * pNext; SUBQUERY * pPrev; FQNODE * pTree; OPT_INFO OptInfo; FSIndexCursor * pFSIndexCursor; // Used of OptInfo.eOptType is // QOPT_USING_INDEX FlmUserPredicate * pPredicate; // Used if OptInfo.eOptType is // QOPT_USING_PREDICATE. FSDataCursor * pFSDataCursor; // Used if OptInfo.eOptType is // QOPT_PARTIAL_CONTAINER_SCAN or // QOPT_FULL_CONTAINER_SCAN. FLMBOOL bRecReturned; // Used if OptInfo.eOptType is // QOPT_SINGLE_RECORD_READ. FLMBOOL bSaveRecReturned; // Used when saving position for // single record reads. FLMUINT uiLowDrn; FLMUINT uiHighDrn; FLMUINT uiNotEqualDrn; FLMBOOL bHaveDrnFlds; // Part of query involved DRN==x FLMUINT uiLanguage; F_Pool OptPool; FCURSOR_SUBQUERY_STATUS SQStatus; FlmRecord * pRec; FLMBOOL bRecIsAKey; FLMUINT uiDrn; FLMBOOL bFirstReference; FLMUINT uiCurrKeyMatch; } SUBQUERY; /**************************************************************************** Desc: ****************************************************************************/ typedef struct CURSOR { FDB * pDb; FLMUINT uiContainer; FLMUINT uiRecType; FLMUINT uiIndexNum; // Query tree and subqueries. FQNODE * pTree; SUBQUERY * pSubQueryList; SUBQUERY * pCurrSubQuery; SUBQUERY * pSaveSubQuery; FLMBOOL bInvTrans; FLMUINT uiTransSeq; // Positioning keys. POS_KEY * pPosKeyArray; FLMUINT uiNumPosKeys; FLMBOOL bLeafLevel; FLMUINT uiLastPrcntPos; FLMUINT uiLastPrcntOffs; FLMBOOL bUsePrcntPos; F_DynSearchSet * pDRNSet; FLMBOOL bEliminateDups; QTINFO QTInfo; RCODE rc; F_Pool SQPool; F_Pool QueryPool; void * pOptMark; FLMBOOL bOptimized; FLMBOOL bEmpty; FLMBOOL bOkToReturnKeys; // OK To return index keys instead of // full records. REC_VALIDATOR_HOOK fnRecValidator; // Record Validator Hook void * RecValData; // Record Validator User Data STATUS_HOOK fnStatus; void * StatusData; FLMUINT uiLastCBTime; FLMUINT uiLastRecID; RCODE ReadRc; // Will only be SUCCESS, FRC_EOF_HIT, // or FRC_BOF_HIT FLMUINT uiTimeLimit; CS_CONTEXT * pCSContext; FLMUINT uiCursorId; } CURSOR; RCODE flmCurDbInit( CURSOR * pCursor); void flmSQFree( SUBQUERY * pSubQuery, FLMBOOL bFreeEverything); void flmCurFinishTrans( CURSOR * pCursor); RCODE flmCurSavePosition( CURSOR * pCursor); RCODE flmCurRestorePosition( CURSOR * pCursor); void flmCurFree( CURSOR * pCursor, FLMBOOL bFinishTrans); RCODE flmCurSearch( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, FLMUINT * puiCount, FLMUINT * puiSkipCount, FlmRecord ** ppUserRecord, FLMUINT * puiDrn); RCODE flmInitCurCS( CURSOR * pCursor); RCODE flmCurGetAtomVal( FlmRecord * pRecord, void * pField, F_Pool * pPool, QTYPES eFldType, FQATOM * pResult); RCODE flmCurEvalCriteria( CURSOR * pCursor, SUBQUERY * pSubQuery, FlmRecord * pRecord, FLMBOOL bHaveKey, FLMUINT * puiResult); void flmCompareOperands( FLMUINT uiLang, FQATOM * pLhs, FQATOM * pRhs, QTYPES eOp, FLMBOOL bResolveUnknown, FLMBOOL bForEvery, FLMBOOL bNotted, FLMBOOL bHaveKey, FLMUINT * puiTrueFalse); RCODE flmCurEvalCompareOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bHaveKey, FQATOM * pResult); FLMUINT flmCurDoMatchOp( FQATOM * lhs, FQATOM * rhs, FLMUINT uiLang, FLMBOOL bLeadingWildCard, FLMBOOL bTrailingWildCard); FLMINT flmCurDoRelationalOp( FQATOM * lhs, FQATOM * rhs, FLMUINT uiLang); FLMUINT flmCurDoContainsOp( FQATOM * lhs, FQATOM * rhs, FLMUINT uiLang); FLMINT flmTextCompare( FLMBYTE * pLeftBuf, FLMUINT uiLeftLen, FLMBYTE * pRightBuf, FLMUINT uiRightLen, FLMUINT uiFlags, FLMUINT uiLang); FLMUINT flmTextMatch( FLMBYTE * pLeftBuf, FLMUINT uiLeftLen, FLMBYTE * pRightBuf, FLMUINT uiRightLen, FLMUINT uiFlags, FLMBOOL bLeadingWildCard, FLMBOOL bTrailingWildCard, FLMUINT uiLang); RCODE flmCurMakeKeyFromRec( FDB * pDb, IXD * pIxd, F_Pool * pPool, FlmRecord * pRec, FLMBYTE ** ppucKeyBuffer, FLMUINT * puiKeyLen); RCODE flmCurSetPosFromDRN( CURSOR * pCursor, FLMUINT uiDRN); RCODE flmCurCopyQNode( FQNODE * pSrcNode, QTINFO * pDestQTInfo, FQNODE * * ppDestNode, F_Pool * pPool); RCODE flmCurPrep( CURSOR * pCursor); void flmCurFreePosKeys( CURSOR * pCursor); RCODE flmCurSetupPosKeyArray( CURSOR * pCursor); RCODE flmCurGetPercentPos( CURSOR * pCursor, FLMUINT * puiPrcntPos); RCODE flmCurSetPercentPos( CURSOR * pCursor, FLMUINT uiPrcntPos); RCODE flmSQSetupFullContainerScan( SUBQUERY * pSubQuery); RCODE flmCurOptimize( CURSOR * pCursor, FLMBOOL bStratified); RCODE flmCurPartitionTree( CURSOR * pCursor); RCODE flmCurAddRefPredicate( QTINFO * pQTInfo, FlmUserPredicate * pPredicate); void flmCurLinkFirstChild( FQNODE * pParent, FQNODE * pChild); void flmCurLinkLastChild( FQNODE * pParent, FQNODE * pChild); RCODE flmPutValInAtom( void * pAtom, QTYPES eValType, void * pvVal, FLMUINT uiValLen, FLMUINT uiFlags); RCODE flmCurMakeQNode( F_Pool * pPool, QTYPES eType, void * pVal, FLMUINT uiStrLen, FLMUINT uiFlags, FQNODE * * ppQNode); RCODE flmCurGraftNode( F_Pool * pPool, FQNODE * pQNode, QTYPES eGraftOp, FQNODE * * ppQTree); void flmLogQuery( IF_LogMessageClient * pLogMsg, FLMUINT uiIndent, CURSOR * pCursor); FINLINE void flmCurFinishTransactions( CURSOR * pCursor, FLMBOOL bSetToNull) { flmCurFinishTrans( pCursor); if (bSetToNull) { pCursor->pDb = NULL; } } void flmCurFreeSQList( CURSOR * pCursor, FLMBOOL bFreeEverything); FLMUINT flmGetPathLen( FLMUINT * pFldPath); #include "fpackoff.h" #endif libflaim-4.9.966/src/fpackoff.h0000644000175000017500000000237010510774540017606 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Unset packing for FLAIM structures. // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fpackoff.h 12267 2006-01-19 14:46:28 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- // IMPORTANT NOTE: DO NOT put #ifdef FPACKOFF_H in this file! We want // to be able to include it in many different places. #if !defined(FLM_UNIX) && !defined( FLM_64BIT) #pragma pack(pop) #endif libflaim-4.9.966/src/fblob.cpp0000644000175000017500000004224410510774540017452 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: BLOB read routines. // Tabs: 3 // // Copyright (c) 1995-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fblob.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" #define BLOB_H_VERSION_LEN_POS 0 // 1 - version is also where _H_ ends #define BLOB_CODE_VERSION 28 // BLOB Version 2.8 ends on offset 28 #define BLOB_H_STORAGE_TYPE_POS 1 // 1 - see byStorageType #define BLOB_H_FLAGS_POS 2 // 2 - owned, referenced, ... #define BLOB_H_TYPE_POS 4 // 2 - user defined type // Type of DATA or 0 if unknown #define BLOB_H_FUTURE2 6 // ZERO for now #define BLOB_H_RAW_SIZE_POS 8 // 4 - for large internals #define BLOB_H_STORAGE_SIZE_POS 12 // 4 - for large internals #define BLOB_H_MATCH_STAMP_POS 16 // 8 - match this with BLOB header #define BLOB_MATCH_STAMP_SIZE 8 #define BLOB_H_RIGHT_KEY_POS 24 // 4 - right part of encryption key /* Non-portable Reference BLOB Field Layout */ #define BLOB_R_CHARSET_POS 28 // 1=ANSI,2=UNICODE,... #define BLOB_R_STRLENGTH_POS 29 // Char Length of reference path #define BLOB_R_PATH_POS 30 // variable /**************************************************************************** Desc: Create a BLOB that references a file. Notes: The file will not be built by the FLAIM BLOB code, and thus the format of the file will not be known or controlled by FLAIM. ****************************************************************************/ RCODE FlmBlobImp::referenceFile( HFDB hDb, const char * pszFileName, FLMBOOL bOwned) { RCODE rc = FERR_OK; char szUnportablePath[ F_PATH_MAX_SIZE]; FLMUINT uiFlags; FDB * pDb = (FDB *)hDb; flmAssert( !m_pHeaderBuf); if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathToStorageString( pszFileName, szUnportablePath))) { goto Exit; } uiFlags = (bOwned) ? BLOB_OWNED_REFERENCE_FLAG : BLOB_UNOWNED_REFERENCE_FLAG; m_hDb = hDb; if( uiFlags & BLOB_OWNED_REFERENCE_FLAG ) { m_uiStorageType = BLOB_REFERENCE_TYPE | BLOB_OWNED_TYPE; } else if( uiFlags & BLOB_UNOWNED_REFERENCE_FLAG) { m_uiStorageType = BLOB_REFERENCE_TYPE; } else { flmAssert(0); rc = RC_SET( FERR_SYNTAX); goto Exit; } m_uiFlags = uiFlags; m_uiAction = BLOB_CREATE_ACTION; if( RC_BAD( rc = buildBlobHeader( szUnportablePath))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Builds a reference blob header that will be used as the data for the field. ****************************************************************************/ RCODE FlmBlobImp::buildBlobHeader( const char * pszUnportablePath) { RCODE rc = FERR_OK; FLMBYTE * ptr; FLMUINT uiFileNameLen; // Determine the number of bytes to allocate. uiFileNameLen = f_strlen( pszUnportablePath) + 1; m_uiHeaderLen = BLOB_R_PATH_POS + uiFileNameLen; if( RC_BAD( rc = f_alloc( m_uiHeaderLen, &m_pHeaderBuf))) { goto Exit; } ptr = m_pHeaderBuf; ptr[ BLOB_H_VERSION_LEN_POS] = BLOB_CODE_VERSION; // 28 ptr[ BLOB_H_STORAGE_TYPE_POS] = (FLMBYTE) m_uiStorageType; UW2FBA( (FLMUINT16)m_uiFlags, &ptr[ BLOB_H_FLAGS_POS ]); UW2FBA( BLOB_UNKNOWN_TYPE, &ptr[ BLOB_H_TYPE_POS ]); UW2FBA( 0, &ptr[ BLOB_H_FUTURE2 ]); UD2FBA( 0, &ptr[ BLOB_H_RAW_SIZE_POS ]); UD2FBA( 0, &ptr[ BLOB_H_STORAGE_SIZE_POS ]); f_memset( &ptr[ BLOB_H_MATCH_STAMP_POS ], 0, BLOB_MATCH_STAMP_SIZE ); UD2FBA( 0, &ptr[ BLOB_H_RIGHT_KEY_POS ]); ptr[ BLOB_R_CHARSET_POS ] = 1; ptr[ BLOB_R_STRLENGTH_POS ] = (FLMBYTE) uiFileNameLen; f_memcpy( &ptr[ BLOB_R_PATH_POS ], pszUnportablePath, uiFileNameLen ); // Watch out, the file name is NOT null terminated. Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FlmBlobImp::setupBlobFromField( FDB * pDb, const FLMBYTE * pBlobData, FLMUINT uiBlobDataLength) { RCODE rc = FERR_OK; // See if the database is being forced to close if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } if( getImportDataPtr( uiBlobDataLength) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memcpy( m_pHeaderBuf, pBlobData, uiBlobDataLength); // Check that the storage length is within reason to get information if( m_uiHeaderLen <= BLOB_R_PATH_POS) { rc = RC_SET( FERR_FAILURE); goto Exit; } m_hDb = (HFDB)pDb; // Read in the data that is in the header. m_uiAction = BLOB_OPEN_ACTION; m_uiStorageType = (FLMUINT) m_pHeaderBuf[ BLOB_H_STORAGE_TYPE_POS]; m_uiFlags = FB2UW( &m_pHeaderBuf[ BLOB_H_FLAGS_POS]); m_bReadWriteAccess = FALSE; Exit: if( RC_BAD(rc) && m_pHeaderBuf) { (void) close(); } return( rc); } /**************************************************************************** Desc: Closes a BLOB and frees all associated memory ****************************************************************************/ RCODE FlmBlobImp::close() // Return value is meaningless. { FlmBlobImp * pNextBlob; FlmBlobImp * pPrevBlob; FDB * pDb; if( m_pHeaderBuf) { f_free( &m_pHeaderBuf); m_pHeaderBuf = NULL; } // The case of a created referenced blob that is not attached // will have to be deleted by the caller. if (m_bInDbList) { if (m_hDb != HFDB_NULL) { // Pull out of the linked list pPrevBlob = m_pPrevBlob; pNextBlob = m_pNextBlob; if( pPrevBlob == NULL) /* New first blob element? */ { pDb = (FDB *) m_hDb; pDb->pBlobList = pNextBlob; } else { pPrevBlob->setNext( pNextBlob); /* Delete pBlob */ } if( pNextBlob != NULL) { pNextBlob->setPrev( pPrevBlob); } } m_bInDbList = FALSE; } if( m_pFileHdl) { (void) closeFile(); } return( FERR_OK); } /**************************************************************************** Desc: Builds a reference blob header that will be used as the data for the field. ****************************************************************************/ RCODE FlmBlobImp::closeFile() { if( m_pFileHdl) { m_pFileHdl->closeFile(); m_bFileAccessed = FALSE; m_pFileHdl->Release(); m_pFileHdl = NULL; } return FERR_OK; } /**************************************************************************** Desc: Opens a file given the open flags. ****************************************************************************/ RCODE FlmBlobImp::openFile() { RCODE rc = FERR_OK; char szFileName[ F_PATH_MAX_SIZE]; FDB * pDb = (FDB *) m_hDb; if( !m_pFileHdl && pDb) { buildFileName( szFileName); if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( szFileName, FLM_IO_SH_DENYNONE | (m_bReadWriteAccess ? FLM_IO_RDWR : FLM_IO_RDONLY), &m_pFileHdl))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Build a file name in the input buffer. ****************************************************************************/ RCODE FlmBlobImp::buildFileName( char * pszFileName) { RCODE rc = FERR_OK; FLMUINT uiNameLength; // Be carefull not to do a string copy - this is not null terminated. uiNameLength = m_uiHeaderLen - BLOB_R_PATH_POS; f_memcpy( pszFileName, &m_pHeaderBuf[ BLOB_R_PATH_POS], uiNameLength); pszFileName[ uiNameLength ] = '\0'; // Override the file extension set in the gv_FlmSysData structure. if( gv_FlmSysData.ucBlobExt [0]) { char szBlobPath[ F_PATH_MAX_SIZE]; char szBlobBaseName [F_FILENAME_SIZE]; char * pszFileExt; if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszFileName, szBlobPath, szBlobBaseName))) { goto Exit; } pszFileExt = szBlobBaseName; while( (*pszFileExt) && (*pszFileExt != '.')) { pszFileExt++; } // Add period if there was none. if( !(*pszFileExt)) { *pszFileExt = '.'; } // Get past period. pszFileExt++; f_strcpy( pszFileExt, (const char *)gv_FlmSysData.ucBlobExt); f_strcpy( pszFileName, szBlobPath); gv_FlmSysData.pFileSystem->pathAppend( pszFileName, szBlobBaseName); } Exit: return( rc); } /**************************************************************************** Desc: Compare a file name with the file name in the BLOB. Does not take into account one with a full path and one without. ****************************************************************************/ FLMINT FlmBlobImp::compareFileName( const char * pszFileName) { char szThisFileName[ F_PATH_MAX_SIZE]; if( RC_BAD( buildFileName( szThisFileName))) { return( 1); } #ifdef FLM_UNIX return( f_strcmp( szThisFileName, pszFileName)); #else return( f_stricmp( szThisFileName, pszFileName)); #endif } /**************************************************************************** Desc: Transition the action checking for multiple referenced blobs. ****************************************************************************/ void FlmBlobImp::transitionAction( FLMBOOL bDoTransition) { if( bDoTransition) { /* Transition table: Operation(s) Commit Action ADD Do nothing DELETE Delete blob ADD -> DELETE Delete blob DELETE -> ADD Do nothing ADD -> ADD Multi-referenced blob - ASSERT DELETE -> DELETE Multi-referenced blob - ASSERT */ // This is the time to look for multiple-referenced blobs. if( m_uiCurrentAction == BLOB_ADD_ACTION) { // Two ADDs mean multi-referenced blobs. if( m_uiAction == BLOB_ADD_ACTION) { flmAssert(0); } m_uiAction = m_uiCurrentAction; } else if( m_uiCurrentAction == BLOB_DELETE_ACTION) { // Two DELETEs mean multi-referenced blobs. if( m_uiAction == BLOB_DELETE_ACTION) { flmAssert(0); } m_uiAction = m_uiCurrentAction; } } m_uiCurrentAction = BLOB_NO_ACTION; return; } /**************************************************************************** Desc: ****************************************************************************/ void FlmBlobImp::setInDbList( FLMBOOL bInDbList) { m_bInDbList = bInDbList; } /**************************************************************************** Desc: Returns a pointer that the blob header (control) info can be copied to. ****************************************************************************/ FLMBYTE * FlmBlobImp::getImportDataPtr( FLMUINT uiLength) { if( m_pHeaderBuf && m_uiHeaderLen <= uiLength) { // This case handles a reuse of an existing BLOB object. m_uiHeaderLen = uiLength; return m_pHeaderBuf; } if( m_pHeaderBuf) { // Have a buffer, but it's not large enough f_free( &m_pHeaderBuf); m_pHeaderBuf = NULL; m_uiHeaderLen = 0; } m_uiHeaderLen = uiLength; if( RC_BAD( f_alloc( m_uiHeaderLen, &m_pHeaderBuf))) { m_pHeaderBuf = NULL; } return m_pHeaderBuf; } /**************************************************************************** Desc: This code only suports referenced blobs at this time. Look for this blob field within the current transaction list. If the FILENAME has a match then transition the action to the current action. If the FILENAME doesn't have a match in the list then create a new BLOB and add it to the list with either the BLOB_ADD_ACTION or the BLOB_DELETE_ACTION. At transaction commit time, all blobs that end with the BLOB_DELETE_ACTION will have their files removed. This code does not care about the records that the BLOB comes from. So, the user can do the following: Add BLOB( ABCD) in record 1 Delete BLOB( ABCD) in record 1 Add BLOB( ABCD) in record 2 The pre-ver41 code would have deleted BLOB( ABCD) because of the delete in record one. Then record 2 would be pointing to a non-existant blob. Be carefull, because this code still does not support multiply-referenced blobs; both record 3 and record 4 reference the same blob file. This is because the first delete will remove the blob file so the other reference will be corrupt. Ret: FERR_OK or FERR_MEM Called: Is only called from KyBuild(). ****************************************************************************/ RCODE flmBlobPlaceInTransactionList( FDB * pDb, FLMUINT uiAction, FlmRecord * pRecord, void * pvBlobField) { RCODE rc = FERR_OK; const FLMBYTE * pBlobData; FLMUINT uiBlobDataLength; FLMUINT uiStorageType; FlmBlobImp * pBlob; FlmBlobImp * pNewBlob = NULL; char szFileName[ F_PATH_MAX_SIZE]; // Nothing to work with? if( (pBlobData = pRecord->getDataPtr( pvBlobField)) == NULL) { goto Exit; } uiBlobDataLength = pRecord->getDataLength( pvBlobField); // Don't have to do anything for an unowned reference uiStorageType = pBlobData[ BLOB_H_STORAGE_TYPE_POS]; if( (uiStorageType & (BLOB_REFERENCE_TYPE|BLOB_OWNED_TYPE)) == BLOB_REFERENCE_TYPE) { goto Exit; } // Create a temporary new blob - may or may not keep it. // Need to create it so we can make a file name. if( (pNewBlob = f_new FlmBlobImp) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pNewBlob->setupBlobFromField( pDb, pBlobData, uiBlobDataLength))) { pNewBlob->Release(); pNewBlob = NULL; goto Exit; } pNewBlob->setCurrentAction( uiAction); pNewBlob->buildFileName( szFileName); for( pBlob = pDb->pBlobList; pBlob; pBlob = pBlob->getNext()) { if( pBlob->compareFileName( szFileName) == 0) { // Found a match! pBlob->transitionAction( FALSE); pNewBlob->Release(); pNewBlob = NULL; break; } } if( !pBlob) { // Link to the front of the list - doesn't matter where in the list. pBlob = pDb->pBlobList; pDb->pBlobList = pNewBlob; pNewBlob->setNext( pBlob); pNewBlob->setInDbList( TRUE); if( pBlob) { pBlob->setPrev( pNewBlob); } // Don't delete pNewBlob - in the linked list. } Exit: return( rc); } /**************************************************************************** Desc: Commit the record operation (add,modify,delete) ****************************************************************************/ RCODE FB_OperationEnd( FDB * pDb, RCODE rcOfOperation) { RCODE rc = FERR_OK; FlmBlobImp * pBlob; // The pDb may not be all initialized due to errors in // the fdbInit(). Check for null values. if( !pDb || pDb->uiTransType == FLM_NO_TRANS) { goto Exit; } for( pBlob = pDb->pBlobList; pBlob; pBlob = pBlob->getNext()) { pBlob->transitionAction( rcOfOperation == FERR_OK ? TRUE : FALSE); } Exit: rc = (rcOfOperation != FERR_OK) ? rcOfOperation : rc; return( rc ); } /**************************************************************************** Desc: Called after the commit phase. Go through the BLOB list and delete the file of all deleted blobs. No longer supports deleting unattached blobs. These were created blobs that were never attached to a data record field. ****************************************************************************/ void FBListAfterCommit( FDB * pDb) { FlmBlobImp * pBlob; FlmBlobImp * pNextBlob; char szFileName[ F_PATH_MAX_SIZE]; for( pBlob = pDb->pBlobList; pBlob; pBlob = pNextBlob ) { pNextBlob = pBlob->getNext(); if( pBlob->getAction() == BLOB_DELETE_ACTION) { // Better not be opened. Build the file name and delete. if( RC_OK( pBlob->buildFileName( szFileName))) { gv_FlmSysData.pFileSystem->deleteFile( szFileName); } } (void) pBlob->close(); pBlob->Release(); } return; } /**************************************************************************** Desc: Called after the abort command. Cleans up the FlmBlob actions according to the abort rules - doing nothing to the newly added blobs. Notes: Could be called before or during the commit call or as part of FlmTransAbort(). Must handle all of the cases. ****************************************************************************/ void FBListAfterAbort( FDB * pDb) { FlmBlobImp * pBlob; FlmBlobImp * pNextBlob; for( pBlob = pDb->pBlobList; pBlob; pBlob = pNextBlob ) { pNextBlob = pBlob->getNext(); (void) pBlob->close(); pBlob->Release(); } return; } /**************************************************************************** Desc: Allocate a new blob object ****************************************************************************/ FLMEXP RCODE FLMAPI FlmAllocBlob( FlmBlob ** ppBlob) { RCODE rc = FERR_OK; if( (*ppBlob = f_new FlmBlobImp) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } Exit: return( rc); } libflaim-4.9.966/src/fsrefspl.cpp0000644000175000017500000002065410510774540020213 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Index reference splitting routines. // Tabs: 3 // // Copyright (c) 1991-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsrefspl.cpp 12286 2006-01-19 14:55:18 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC FLMUINT FSSplitRefSet( FLMBYTE * leftBuf, FLMUINT * leftLenRV, FLMBYTE * rightBuf, FLMUINT * rightLenRV, FLMBYTE * refPtr, FLMUINT uiRefLen, FLMUINT uiSplitFactor); /*************************************************************************** Desc: Try to split a reference set. The size is over the first threshold. If you split this update the b-tree with the new element and position to the current element for insert of the din. ***************************************************************************/ RCODE FSRefSplit( FDB * pDb, LFILE * pLFile, BTSK ** pStackRV, FLMBYTE * pElmBuf, FLMUINT din, FLMUINT uiDeleteFlag, FLMUINT uiSplitFactor) { RCODE rc = FERR_OK; BTSK * pStack = *pStackRV; FLMBYTE * pCurElm = CURRENT_ELM( pStack); FLMINT iElmLen; FLMBYTE leftBuf[MAX_REC_ELM]; FLMUINT leftDomain; FLMUINT leftLen; FLMBYTE rightBuf[MAX_REC_ELM]; FLMUINT rightDomain; FLMUINT rightLen; FLMBYTE * refPtr; FLMBYTE * recPtr; FLMUINT uiRefLen; FLMUINT firstFlag = 0; refPtr = pCurElm; recPtr = BBE_REC_PTR( pCurElm); rightDomain = FSGetDomain( &refPtr, (FLMBYTE) pStack->uiElmOvhd); uiRefLen = (FLMUINT) (BBE_GET_RL( pCurElm) - (FLMUINT) (refPtr - recPtr)); FSRS_try_again: leftDomain = FSSplitRefSet( leftBuf, &leftLen, rightBuf, &rightLen, refPtr, uiRefLen, uiSplitFactor); if (leftDomain == 0) { // Split failed, setup to add // // Try again using a different split factor - OK to fail above; // In the future, should just handle no splitting and go on if (uiSplitFactor == SPLIT_50_50) { uiSplitFactor = SPLIT_90_10; goto FSRS_try_again; } // Setup for inserting the din into the right buffer and call // replace leftDomain = DIN_DOMAIN( din) + 1; f_memcpy( rightBuf, refPtr, rightLen = uiRefLen); leftLen = 0; } // Write the right element's references. Write the right domain if // non-zero and replace element iElmLen = (FLMINT) (BBE_REC_OFS( pElmBuf)); refPtr = recPtr = &pElmBuf[iElmLen]; if (rightDomain) { *refPtr++ = SEN_DOMAIN; SENPutNextVal( &refPtr, rightDomain); } if (DIN_DOMAIN( din) < leftDomain) { // Build element inserting the input din if (uiDeleteFlag) { if (FSSetDeleteRef( refPtr, rightBuf, din, &rightLen)) { // rightLen should not have changed if error found return (RC_SET( FERR_KEY_NOT_FOUND)); } } else if (FSSetInsertRef( refPtr, rightBuf, din, &rightLen)) { // Reference there so give up and return success goto Exit; } } else { f_memcpy( refPtr, rightBuf, rightLen); } // The other flags and lengths are been set by the caller iElmLen += BBE_SET_RL( pElmBuf, rightLen + (FLMUINT) (refPtr - recPtr)); if (BBE_IS_FIRST( pElmBuf) && leftLen) { firstFlag++; BBE_CLR_FIRST( pElmBuf); // Log the block before modifying it. if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } pCurElm = CURRENT_ELM( pStack); BBE_CLR_FIRST( pCurElm); // Call replace below because FIRST flag is now clear } // Can call replace because FIRST flag was NOT set if (RC_BAD( rc = FSBtReplace( pDb, pLFile, &pStack, pElmBuf, iElmLen))) { goto Exit; } // Write the left buffer Should be positioned to the right buffer if (leftLen) { // Adjust variables to build and point to the left buffers // references. Set the domain and insert into the b-tree. Then go to // the next element BBE_CLR_LAST( pElmBuf); if (firstFlag) { BBE_SET_FIRST( pElmBuf); } iElmLen = (FLMINT) (BBE_REC_OFS( pElmBuf)); refPtr = recPtr = &pElmBuf[iElmLen]; *refPtr++ = SEN_DOMAIN; SENPutNextVal( &refPtr, leftDomain); if (DIN_DOMAIN( din) >= leftDomain) { // Build element inserting the input din if (uiDeleteFlag) { if (FSSetDeleteRef( refPtr, leftBuf, din, &leftLen)) { return (RC_SET( FERR_KEY_NOT_FOUND)); } } else { if (FSSetInsertRef( refPtr, leftBuf, din, &leftLen)) { f_memcpy( refPtr, leftBuf, leftLen); } } } else { f_memcpy( refPtr, leftBuf, leftLen); } iElmLen += BBE_SET_RL( pElmBuf, leftLen + (FLMUINT) (refPtr - recPtr)); // Setup the pStack and bsKeyBuf[] for the insert if (RC_BAD( rc = FSBtScanTo( pStack, &pElmBuf[BBE_KEY], (FLMUINT) (BBE_GET_KL( pElmBuf)), 0))) { goto Exit; } rc = FSBtInsert( pDb, pLFile, &pStack, pElmBuf, iElmLen); } Exit: return (rc); } /*************************************************************************** Desc: Split a reference set within a domain value. If buffer cannot be split then will return a leftDomain value of ZERO. Must have a minimum of 2 references in left and right buffers. ***************************************************************************/ FSTATIC FLMUINT FSSplitRefSet( FLMBYTE * leftBuf, FLMUINT * leftLenRV, FLMBYTE * rightBuf, FLMUINT * rightLenRV, FLMBYTE * refPtr, FLMUINT uiRefLen, FLMUINT uiSplitFactor) { FLMUINT leftDomain = 0; FLMUINT din = 0; FLMUINT oneRuns = 0; FLMUINT delta; FLMUINT rightLen; FLMUINT offsetTarget; DIN_STATE leftState; DIN_STATE rightState; DIN_STATE refState; FLMBYTE byValue; FLMUINT uiLeftCnt; RESET_DINSTATE( leftState); RESET_DINSTATE( rightState); RESET_DINSTATE( refState); offsetTarget = (uiSplitFactor == SPLIT_90_10) ? REF_SPLIT_90_10 : REF_SPLIT_50_50; // Read the first din value din = DINNextVal( refPtr, &refState); DINPutNextVal( leftBuf, &leftState, din); uiLeftCnt = 1; // Must have at least 2 in the left buffer. while (refState.uiOffset < offsetTarget || uiLeftCnt < 2) { byValue = refPtr[refState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { oneRuns = DINOneRunVal( refPtr, &refState); DINPutOneRunVal( leftBuf, &leftState, oneRuns); din -= oneRuns; } else { delta = DINNextVal( refPtr, &refState); DINPutNextVal( leftBuf, &leftState, delta); din -= delta; } uiLeftCnt++; } // Made it past the target point - find where domain changes leftDomain = DIN_DOMAIN( din); // Don't parse past the end while (refState.uiOffset < uiRefLen) { byValue = refPtr[refState.uiOffset]; if (DIN_IS_REAL_ONE_RUN( byValue)) { oneRuns = DINOneRunVal( refPtr, &refState); if (DIN_DOMAIN( din - oneRuns) != leftDomain) { // This is tricky, write only correct number of one runs delta = din & 0xFF; if (delta) { DINPutOneRunVal( leftBuf, &leftState, delta); } // Increment delta because setting up for next element delta++; oneRuns -= delta; // Write din and one runs below din -= delta; break; } DINPutOneRunVal( leftBuf, &leftState, oneRuns); din -= oneRuns; } else { delta = DINNextVal( refPtr, &refState); din -= delta; if (DIN_DOMAIN( din) != leftDomain) { oneRuns = 0; break; } DINPutNextVal( leftBuf, &leftState, delta); } } if (refState.uiOffset == uiRefLen) { // Cannot split, caller take care of return (0); } // Start writing to the right side, compare /w uiRefLen proves > 2 refs DINPutNextVal( rightBuf, &rightState, din); if (oneRuns) { DINPutOneRunVal( rightBuf, &rightState, oneRuns); } *leftLenRV = leftState.uiOffset; rightLen = (FLMUINT) (uiRefLen - refState.uiOffset); f_memcpy( &rightBuf[rightState.uiOffset], &refPtr[refState.uiOffset], rightLen); *rightLenRV = rightLen + rightState.uiOffset; return (leftDomain); } libflaim-4.9.966/src/flverify.cpp0000644000175000017500000021262010510774540020211 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Verify database structure. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flverify.cpp 12266 2006-01-19 14:45:33 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC eCorruptionType flmVerifyBlobField( FLMBYTE * pBlobHdr, FLMUINT uiBlobHdrLen); FSTATIC eCorruptionType flmVerifyTextKey( IFD * pIfd, FLMBOOL bIxAsia, FLMBOOL bIxArab, FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT * puiOffsetRV, FLMUINT * puiCollateCountRV); FSTATIC eCorruptionType flmVerifyNumberKey( FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT * puiOffset); FSTATIC FLMBOOL flmGetSEN( FLMBYTE * pTmpElmRec, FLMUINT * puiDrnRV, FLMUINT * puiNumBytesRV); extern FLMBYTE gnDaysInMonth[]; extern FLMBYTE SENLenArray[]; #define ASC_N 95 #define ML1_N 242 #define ML2_N 145 #define BOX_N 88 #define TYP_N 103 #define ICN_N 255 #define MTH_N 238 #define MTX_N 229 #define GRK_N 219 #define HEB_N 123 #define CYR_N 250 #define KAN_N 63 #define USR_N 255 #define ARB_N 196 #define ARS_N 220 /**************************************************************************** Desc: Number of characters in each character set ****************************************************************************/ FLMBYTE flm_c60_max[] = { ASC_N, // ascii ML1_N, // multinational 1 ML2_N, // multinational 2 BOX_N, // line draw TYP_N, // typographic ICN_N, // icons MTH_N, // math MTX_N, // math extension GRK_N, // Greek HEB_N, // Hebrew CYR_N, // Cyrillic - Russian KAN_N, // Kana USR_N, // user ARB_N, // Arabic ARS_N, // Arabic Script }; /**************************************************************************** Desc: Verifies that a WordPerfect character is a legal character. ****************************************************************************/ eCorruptionType flmVerifyWPChar( FLMUINT uiCharSet, FLMUINT uiChar) { if (uiCharSet < F_NCHSETS) { if (uiChar >= (FLMUINT) flm_c60_max[uiCharSet]) { return (FLM_BAD_CHAR); } } else if ((uiCharSet >= F_ACHSMIN) && (uiCharSet < F_ACHSETS)) { if (uiChar > F_ACHCMAX) { return (FLM_BAD_ASIAN_CHAR); } } else { return (FLM_BAD_CHAR_SET); } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: This routine verifies a text field. It makes sure that all of the characters, formatting codes, etc. are valid. ****************************************************************************/ eCorruptionType flmVerifyTextField( FLMBYTE * pText, FLMUINT uiTextLen) { FLMUINT uiChar1; FLMUINT uiBytesProcessed; FLMUINT uiObjType; FLMUINT uiObjLen; FLMUINT uiLength; FLMUINT uiCharSet; FLMUINT uiChar; eCorruptionType eCorruptionCode; if (!uiTextLen) { return (FLM_NO_CORRUPTION); } // Parse through the data, verifying that it is consistent with the // internal TEXT format. uiBytesProcessed = 0; while (uiBytesProcessed < uiTextLen) { // Determine what we are pointing at uiChar1 = (FLMUINT) * pText; uiObjType = (FLMUINT) (flmTextObjType( uiChar1)); switch (uiObjType) { case ASCII_CHAR_CODE: { uiObjLen = 1; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // There should NEVER be a character less than 32. if (uiChar1 < 32) { return (FLM_BAD_CHAR); } break; } case CHAR_SET_CODE: { uiObjLen = 2; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // If the character set is zero, it had better be a valid // character. uiChar = *(pText + 1); if ((uiCharSet = (FLMUINT) (uiChar1 & (~uiObjType))) == 0) { if ((uiChar < 32) || (uiChar > 127)) { return (FLM_BAD_CHAR); } } else { // Make sure we have a valid WordPerfect character. if ((eCorruptionCode = flmVerifyWPChar( uiCharSet, uiChar)) != FLM_NO_CORRUPTION) { return (eCorruptionCode); } } break; } case WHITE_SPACE_CODE: { uiObjLen = 1; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } break; } case UNICODE_CODE: { uiObjLen = 3; if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // Anything is valid except 0xFFFF or 0xFFFE. break; } case UNK_GT_255_CODE: case UNK_LE_255_CODE: { if (uiObjType == UNK_GT_255_CODE) { uiObjLen = 1 + sizeof(FLMUINT16); // Before testing anything else, make sure that we are not // going to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } uiLength = (FLMUINT) FB2UW( pText + 1); } else { uiObjLen = 2; // Before testing anything else, make sure that we are not // going to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } uiLength = (FLMUINT) * (pText + 1); } if (uiLength > 65535 - uiObjLen) { return (FLM_BAD_TEXT_FIELD); } uiObjLen += uiLength; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // Make sure it is one of our valid types. if (((uiChar1 & (~uiObjType)) != WP60_TYPE) && ((uiChar1 & (~uiObjType)) != NATIVE_TYPE)) { return (FLM_BAD_TEXT_FIELD); } break; } case UNK_EQ_1_CODE: { uiObjLen = 2; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // Make sure it is one of our valid types. if (((uiChar1 & (~uiObjType)) != WP60_TYPE) && ((uiChar1 & (~uiObjType)) != NATIVE_TYPE)) { return (FLM_BAD_TEXT_FIELD); } break; } case EXT_CHAR_CODE: { uiObjLen = 3; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // If the character set is zero, the character had better be // between 32 and 127. uiChar = (FLMUINT) (*(pText + 2)); if ((uiCharSet = (FLMUINT) (*(pText + 1))) == 0) { if ((uiChar < 32) || (uiChar > 127)) { return (FLM_BAD_CHAR); } } else { // Make sure we have a valid WordPerfect character. if ((eCorruptionCode = flmVerifyWPChar( uiCharSet, uiChar)) != FLM_NO_CORRUPTION) { return (eCorruptionCode); } } break; } case OEM_CODE: { uiObjLen = 2; // Before testing anything else, make sure that we are not going // to access something beyond the end of the buffer. if (uiBytesProcessed + uiObjLen > uiTextLen) { return (FLM_BAD_TEXT_FIELD); } // OEM characters must be > 127. if (*(pText + 1) <= 127) { return (FLM_BAD_CHAR); } break; } default: { // These codes should NEVER HAPPEN. return (FLM_BAD_TEXT_FIELD); } } pText += uiObjLen; uiBytesProcessed += uiObjLen; } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: This routine verifies a number field. ****************************************************************************/ eCorruptionType flmVerifyNumberField( STATE_INFO * pStateInfo, FLMBYTE * pNumber, FLMUINT uiNumberLen) { FLMUINT uiChar; FLMBOOL bFirstNibble; FLMUINT uiNibbleCount; FLMBOOL bHitExponent = FALSE; FLMBOOL bRealNumberFlag = FALSE; if (!uiNumberLen) { return (FLM_NO_CORRUPTION); } bFirstNibble = TRUE; uiNibbleCount = 0; for (;;) { // Determine what we are pointing at uiChar = (FLMUINT) (*pNumber); if (bFirstNibble) { uiChar >>= 4; uiChar &= 0x0F; bFirstNibble = FALSE; } else { uiChar &= 0x0F; pNumber++; bFirstNibble = TRUE; } uiNibbleCount++; switch (uiChar) { case 0x0A: { // Periods are currently NOT supported. return (FLM_BAD_NUMBER_FIELD); } case 0x0B: { // The minus had better be the very FIRST character except E-xF. if (uiNibbleCount > 1 && !bHitExponent) { return (FLM_BAD_NUMBER_FIELD); } break; } case 0x0C: case 0x0D: { return (FLM_BAD_NUMBER_FIELD); } case 0x0E: { // 'E' should be in the first or second position, but the format // has the ability to change at a later time. if (bRealNumberFlag) { return (FLM_BAD_NUMBER_FIELD); } bRealNumberFlag = TRUE; bHitExponent = TRUE; break; } case 0x0F: { if (bHitExponent) { bHitExponent = FALSE; break; } // If we didn't end right on the last byte, we have a problem. if ((uiNibbleCount + 1) / 2 < uiNumberLen) { return (FLM_BAD_NUMBER_FIELD); } else { return (FLM_NO_CORRUPTION); } } default: { break; } } // If we are at the last byte, but have not encountered a 0x0F we // have a corrupted number. if (uiNibbleCount / 2 == uiNumberLen) { return (FLM_BAD_NUMBER_FIELD); } if (pStateInfo->uiVersionNum >= FLM_FILE_FORMAT_VER_4_62) { // Numbers greater than 21 digits not yet supported. if (!bRealNumberFlag && (uiNibbleCount > 21)) { return (FLM_BAD_NUMBER_FIELD); } } else { // Numbers greater than 11 digits not yet supported. if (!bRealNumberFlag && (uiNibbleCount > 11)) { return (FLM_BAD_NUMBER_FIELD); } } } } /**************************************************************************** Desc: ****************************************************************************/ FSTATIC eCorruptionType flmVerifyBlobField( FLMBYTE * pBlobHdr, FLMUINT uiBlobHdrLen) { FLMUINT uiSubType; FLMUINT uiPathLen; FLMUINT uiCharLen = 1; FLMBYTE * pPath; #define BLOB_ALIGNMENT_FUDGE 8 if (!uiBlobHdrLen) { return (FLM_NO_CORRUPTION); } // Definitions taken from fblob.cpp #define BLOB_H_VERSION_LEN_POS 0 #define BLOB_CODE_VERSION 28 #define BLOB_H_STORAGE_TYPE_POS 1 #define BLOB_H_FLAGS_POS 2 #define BLOB_H_TYPE_POS 4 // Type of DATA or 0 if unknown #define BLOB_H_FUTURE2 6 #define BLOB_H_RAW_SIZE_POS 8 #define BLOB_H_STORAGE_SIZE_POS 12 #define BLOB_H_MATCH_STAMP_POS 16 #define BLOB_MATCH_STAMP_SIZE 8 #define BLOB_H_RIGHT_KEY_POS 24 // Non-portable Reference BLOB Field Layout #define BLOB_R_CHARSET_POS 28 #define BLOB_R_STRLENGTH_POS 29 #define BLOB_R_PATH_POS 30 // Must be at least as big as the smallest header. if (pBlobHdr[BLOB_H_VERSION_LEN_POS] != BLOB_CODE_VERSION) { return (FLM_BAD_BLOB_FIELD); } uiSubType = (FLMUINT) (pBlobHdr[BLOB_H_STORAGE_TYPE_POS] & 0x0F); if (uiSubType == BLOB_REFERENCE_TYPE) { if (uiBlobHdrLen < BLOB_R_PATH_POS) { return (FLM_BAD_BLOB_FIELD); } if (pBlobHdr[BLOB_R_CHARSET_POS] == 1) // ANSI { uiPathLen = pBlobHdr[BLOB_R_STRLENGTH_POS]; } else if (pBlobHdr[BLOB_R_CHARSET_POS] == 2) // UNICODE { uiPathLen = (FLMUINT) (pBlobHdr[BLOB_R_STRLENGTH_POS] * 2); uiCharLen = 2; } else { return (FLM_BAD_BLOB_FIELD); } // uiPathLen will include the NULL byte(s). if (uiBlobHdrLen < BLOB_R_PATH_POS + uiPathLen) { return (FLM_BAD_BLOB_FIELD); } pPath = pBlobHdr + BLOB_R_PATH_POS; } else { return (FLM_BAD_BLOB_FIELD); } // Verify FILENAME that no characters are less than 0x20 and zero // terminates. uiPathLen includes the NULL byte/word so pre-decrement. // Zero or one path length is invalid. EXTERNAL and REFERENCE BLOBS // ONLY! if (uiPathLen <= 1) { return (FLM_BAD_BLOB_FIELD); } for (; --uiPathLen;) { if (uiCharLen == 1) { if (*pPath++ < 0x20) { return (FLM_BAD_BLOB_FIELD); } } else { if (FB2UW( pPath) < 0x20) { return (FLM_BAD_BLOB_FIELD); } pPath += 2; } } if (uiCharLen == 1) { if (*pPath++ != 0) { return (FLM_BAD_BLOB_FIELD); } } else { if (FB2UW( pPath) != 0) { return (FLM_BAD_BLOB_FIELD); } pPath += 2; } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: ****************************************************************************/ eCorruptionType flmVerifyField( STATE_INFO * pStateInfo, FLMBYTE* pField, FLMUINT uiFieldLen, FLMUINT uiFieldType) { if (((uiFieldLen) && (!pField)) || ((!uiFieldLen) && (pField))) { return (FLM_BAD_FIELD_PTR); } switch (uiFieldType) { case FLM_TEXT_TYPE: { return (flmVerifyTextField( pField, uiFieldLen)); } case FLM_NUMBER_TYPE: { return (flmVerifyNumberField( pStateInfo, pField, uiFieldLen)); } case FLM_BINARY_TYPE: { break; } case FLM_BLOB_TYPE: { return (flmVerifyBlobField( pField, uiFieldLen)); } case FLM_CONTEXT_TYPE: { // Length must be zero or four in context fields. if ((uiFieldLen != 0) && (uiFieldLen != 4)) { return (FLM_BAD_CONTEXT_FIELD); } break; } default: { // Unknown type. return (FLM_BAD_FIELD_TYPE); } } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: Verifies a text field within a key - compound or single. ****************************************************************************/ FSTATIC eCorruptionType flmVerifyTextKey( IFD * pIfd, // Pointer to index field definition structure. FLMBOOL bIxAsia, // Is this one of the ASIAN indexes? FLMBOOL bIxArab, // Is this one of the ARAB indexes? FLMBYTE * pKey, // Pointer to beginning of key. FLMUINT uiKeyLen, // Byte length of entire key. FLMUINT * puiOffsetRV, // Offset in key where text key begins. // Returns offset where text key ends. FLMUINT* puiCollateCountRV) // Returns the number of collation characters. { FLMUINT uiCntJ = *puiOffsetRV; FLMUINT uiTextCollateCount = 0; FLMUINT uiArabCollateCount = 0; FLMUINT uiCntK; FLMUINT uiBitsToSkip; FLMUINT uiBit; FLMUINT uiChar; FLMUINT uiNextChar = 0; FLMUINT uiCaseBits; // See how many collated values there are - go until we hit 0x07, 0x02, // 0x04, 0x05, 0x06, 0x01, or end of key. while (uiCntJ < uiKeyLen) { uiNextChar = (FLMUINT) pKey[uiCntJ]; if (bIxAsia) { if (uiCntJ + 1 >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiNextChar <<= 8; uiNextChar += pKey[uiCntJ + 1]; } if ((uiNextChar == END_COMPOUND_MARKER) || (uiNextChar == COMPOUND_MARKER) || (uiNextChar == COLL_FIRST_SUBSTRING) || (uiNextChar == (COLL_MARKER | SC_SUB_COL)) || (uiNextChar == (COLL_MARKER | SC_MIXED)) || ((uiNextChar == (COLL_MARKER | SC_LOWER)) && (!bIxAsia)) || ((uiNextChar == (COLL_MARKER | SC_UPPER)) && (!bIxAsia)) || (uiNextChar == COLL_TRUNCATED)) { // These checks must be in order if (uiCntJ > *puiOffsetRV && uiNextChar == COLL_FIRST_SUBSTRING) { uiCntJ++; if (bIxAsia) { uiCntJ++; } continue; } if (uiNextChar == COLL_TRUNCATED) { uiCntJ++; if (bIxAsia) { uiCntJ++; } // Get character after COLL_TRUNCATED, if any if (uiCntJ < uiKeyLen) { uiNextChar = (FLMUINT) pKey[uiCntJ]; if (bIxAsia) { if (uiCntJ + 1 >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiNextChar <<= 8; uiNextChar += pKey[uiCntJ + 1]; } } } break; } // It is impossible to have a collated value that is less than 0x20 // (space). if (uiNextChar < 0x20) { return (FLM_BAD_TEXT_KEY_COLL_CHAR); } uiTextCollateCount++; uiCntJ++; if (bIxAsia) { uiCntJ++; } } // See if we got sub-collation values. if ((uiCntJ < uiKeyLen) && (uiNextChar == 0x07)) { uiCntJ++; if (bIxAsia) { uiCntJ++; } if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiCntK = 0; uiBit = 0x80; uiChar = pKey[uiCntJ]; for (;;) { FLMUINT uiBitCode; // Determine what the code is. uiBitCode = 0; while (uiChar & uiBit) { uiBitCode |= 0x01; uiBitCode <<= 1; // Get the next bit. uiBit >>= 1; // See if we need to get the next byte. if (!uiBit) { uiCntJ++; if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiChar = (FLMUINT) pKey[uiCntJ]; uiBit = 0x80; } } // The uiBitCode value tells whether or not there is a collating // value and what type it is. switch (uiBitCode) { // Code of zero means there is no collating value. case 0: { if ((!uiTextCollateCount) && (bIxArab)) { uiBitsToSkip = 0; } else { uiBitsToSkip = 1; } uiCntK++; break; } // Code of 0x02 means that the sub-collation value is the next // five bits. case 0x02: { uiBitsToSkip = 6; uiCntK++; break; } // Code of 0x06 means that next two bytes contains sub-collation. // Code of 0x0E should only happen in Arabic, means that next two // bytes contains sub-collation, but should not be counted. case 0x0E: { if (!bIxArab) { return (FLM_BAD_KEY_LEN); } uiArabCollateCount++; // Fall through to 0x06 case. } case 0x06: { uiBitsToSkip = 0; uiCntJ += 3; if (uiCntJ > uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiBit = 0x80; uiChar = (FLMUINT) pKey[uiCntJ]; // If Arabic, the sub-collation should not be counted. if (uiBitCode != 0x0E) { uiCntK++; } break; } // Unicode character that did not convert to a WP char. The // actual unicode character follows the 1E. case 0x1E: { uiBitsToSkip = 0; uiCntJ += 3; if (uiCntJ > uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiBit = 0x80; uiChar = (FLMUINT) pKey[uiCntJ]; uiCntK++; // The spec for Unicode has an additional case bit. Added Oct // 98. Note, the extra case bit is only set if we are not an // asian index. if (!bIxAsia) { uiArabCollateCount++; } break; } default: { return (FLM_BAD_KEY_LEN); } } // Skip the required number of bits. while (uiBitsToSkip) { uiBit >>= 1; uiBitsToSkip--; if ((!uiBit) && ((uiBitsToSkip) || (uiCntK < uiTextCollateCount))) { uiCntJ++; if (uiCntJ == uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiChar = (FLMUINT) pKey[uiCntJ]; uiBit = 0x80; } } if (uiCntK >= uiTextCollateCount) { if (!bIxArab) { break; } // Arab languages have one more terminating bit. See if we // need to go to the next byte. if (!uiBit) { // Terminating bit is in next byte. uiCntJ++; if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiChar = (FLMUINT) pKey[uiCntJ]; uiBit = 0x80; } // If the next bit isn't set, we are done. However, we must // skip the current character if (!(uiChar & uiBit)) { uiCntJ++; if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiChar = (FLMUINT) pKey[uiCntJ]; uiBit = 0x80; break; } } } // If NOT at beginning of byte, increment uiCntJ to skip rest of // byte. if (uiBit != 0x80) { uiCntJ++; } } // Do lower/upper case bits -- unless a POST index. if (pIfd->uiFlags & IFD_POST) { *puiCollateCountRV = uiTextCollateCount + uiArabCollateCount; } else { // If there are no UPPER/LOWER indicators, we have a problem. if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiNextChar = (FLMUINT) pKey[uiCntJ]; uiCntJ++; if (bIxAsia) { if (uiCntJ >= uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiNextChar <<= 8; uiNextChar += (FLMUINT) pKey[uiCntJ]; uiCntJ++; } switch (uiNextChar) { case COLL_FIRST_SUBSTRING: { break; } case COLL_TRUNCATED: { break; } case (COLL_MARKER | SC_LOWER): case (COLL_MARKER | SC_UPPER): { if (bIxAsia) { return (FLM_BAD_TEXT_KEY_CASE_MARKER); } break; } case (COLL_MARKER | SC_MIXED): { uiCaseBits = uiTextCollateCount + uiArabCollateCount; if (bIxAsia) { uiCaseBits <<= 1; } uiCntJ += ((uiCaseBits + 7) >> 3); break; } default: { return (FLM_BAD_TEXT_KEY_CASE_MARKER); } } } if (uiCntJ > uiKeyLen) { return (FLM_BAD_KEY_LEN); } *puiOffsetRV = uiCntJ; return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: ****************************************************************************/ FSTATIC eCorruptionType flmVerifyNumberKey( FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT * puiOffsetRV) { FLMUINT uiCntJ = *puiOffsetRV; FLMUINT uiChar; FLMUINT uiNumDigits; FLMUINT uiNibble; // Determine the number of digits. uiChar = pKey[uiCntJ++]; uiNumDigits = (FLMUINT) (uiChar & 0x7F); // If negative, the number of digits must be NOTed. if (!(uiChar & 0x80)) { uiNumDigits = (FLMUINT) ((~uiNumDigits) & 0x7F); } // Adjust the number of digits by -64 + 1. uiNumDigits = (FLMUINT) (uiNumDigits - COLLATED_NUM_EXP_BIAS + 1); // Process until we run out of digits or key or until we hit the // compound marker or post marker. while( uiNumDigits && (uiCntJ < uiKeyLen) && (pKey[uiCntJ] != 0x02) && (pKey[uiCntJ] != 0x01)) { // Check the first nibble. uiNibble = (FLMUINT) ((pKey[uiCntJ] >> 4) & 0x0F); if ((uiNibble < 0x05) || (uiNibble > 0x0E)) { return (FLM_BAD_NUMBER_KEY); } uiNumDigits--; // Check the 2nd nibble. If we are out of digits it had better be // 0x0F. uiNibble = (FLMUINT) (pKey[uiCntJ] & 0x0F); if (!uiNumDigits) { if (uiNibble != 0x0F) { return (FLM_BAD_NUMBER_KEY); } } else { if ((uiNibble < 0x05) || (uiNibble > 0x0E)) { return (FLM_BAD_NUMBER_KEY); } uiNumDigits--; } uiCntJ++; } // If we ran out of key before we processed all of the digits, we have // an error. if ((uiNumDigits) && (uiCntJ > uiKeyLen)) { return (FLM_BAD_KEY_LEN); } *puiOffsetRV = uiCntJ; return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: This routine verifies that a collated key conforms to the index it belongs to. ****************************************************************************/ eCorruptionType flmVerifyKey( FLMBYTE* pKey, // Key which is to be verified. FLMUINT uiKeyLen, // Byte length of pKey. FLMUINT uiIxLang, // Language for index. IFD* pIfdArray, // List of fields in index. FLMUINT uiNumIxFields) // Number of fields in pIfdArray. { IFD* pIfd = pIfdArray; FLMUINT uiI; FLMUINT uiJ; #define MAX_IX_FIELDS 100 FLMUINT uiCollateCount[MAX_IX_FIELDS]; FLMUINT uiPostByteCount; eCorruptionType eCorruptionCode; FLMUINT uiTotalTextChars = 0; FLMBOOL bIxAsia; FLMBOOL bIxArab; FLMBOOL bIxIsPost = FALSE; FLMUINT uiTrueNumIxFields = uiNumIxFields; FLMUINT uiMarkerChar; FLMUINT uiCaseBits; FLMUINT uiFieldType; bIxAsia = (uiIxLang >= FLM_FIRST_DBCS_LANG && uiIxLang <= FLM_LAST_DBCS_LANG) ? TRUE : FALSE; bIxArab = (uiIxLang == FLM_AR_LANG || uiIxLang == FLM_FA_LANG || uiIxLang == FLM_HE_LANG || uiIxLang == FLM_UR_LANG) ? TRUE : FALSE; // If we weren't able to get the IX information from the dictionary // just return FLM_NO_CORRUPTION. if ((!pIfdArray) || (!uiNumIxFields)) { return (FLM_NO_CORRUPTION); } // See if we have a POST index. for (uiI = 0; uiI < uiNumIxFields; uiI++) { if (pIfdArray[uiI].uiFlags & IFD_POST) { bIxIsPost = TRUE; break; } } // If it is not a compound index, set the uiNumIxFields to one - we // only need to examine one field, because they are all the same type. if (!(pIfdArray->uiFlags & IFD_COMPOUND)) { if (bIxIsPost) { return (FLM_BAD_IX_DEF); } uiNumIxFields = 1; } uiJ = 0; uiI = 0; if (uiNumIxFields > MAX_IX_FIELDS) { return (FLM_BAD_IX_DEF); } while (uiJ < uiKeyLen) { uiCollateCount[uiI] = 0; // First see if the component has anything in it. If we hit the // compound marker right away, the component piece is empty. uiMarkerChar = (FLMUINT) pKey[uiJ]; if ((bIxAsia) && (IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) && (!(pIfd->uiFlags & IFD_CONTEXT))) { if (uiJ + 1 == uiKeyLen) { return (FLM_BAD_KEY_LEN); } uiMarkerChar <<= 8; uiMarkerChar += (FLMUINT) pKey[uiJ + 1]; } if (uiMarkerChar == 2) { ; } else if (uiMarkerChar == 1) { // If we hit a 1, it should be the beginning of upper/lower case // bits for a POST index. break; } else if (uiMarkerChar == NULL_KEY_MARKER) { uiJ++; if ((bIxAsia) && (IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) && (!(pIfd->uiFlags & IFD_CONTEXT))) { uiJ++; } } // See if indexing context. If not, use the field's type to // determine how the key is formatted. else if (pIfd->uiFlags & IFD_CONTEXT) { // If indexing context, the first byte of the key is a 0x1E // followed by the two byte tag number in high/low order. if (pKey[uiJ] != 0x1E) { return (FLM_BAD_CONTEXT_KEY); } // Verify that the tag portion of the key matches the field // number. if (pIfd->uiFlags & IFD_COMPOUND) { if (f_bigEndianToUINT16( &pKey[uiJ + 1]) != pIfd->uiFldNum) { return (FLM_BAD_CONTEXT_KEY); } } else { FLMUINT uiH; IFD * pTmpIfd; // If it is NOT a compound index, be sure to check each field // in the index to see if it matches that field number - it // could be a multi-field index. for (uiH = 0, pTmpIfd = pIfdArray; uiH < uiTrueNumIxFields; uiH++, pTmpIfd++) { if (f_bigEndianToUINT16( &pKey[uiJ + 1]) == pTmpIfd->uiFldNum) { break; } } // If it did not match any of the fields, return an error. if (uiH == uiTrueNumIxFields) { return (FLM_BAD_CONTEXT_KEY); } } uiJ += 3; } else { switch (uiFieldType = (FLMUINT) (IFD_GET_FIELD_TYPE( pIfd))) { case FLM_TEXT_TYPE: { if ((eCorruptionCode = flmVerifyTextKey( pIfd, bIxAsia, bIxArab, pKey, uiKeyLen, &uiJ, &uiCollateCount[uiI])) != FLM_NO_CORRUPTION) { return (eCorruptionCode); } uiTotalTextChars += uiCollateCount[uiI]; break; } case FLM_NUMBER_TYPE: { if ((eCorruptionCode = flmVerifyNumberKey( pKey, uiKeyLen, &uiJ)) != FLM_NO_CORRUPTION) { return (eCorruptionCode); } break; } case FLM_BINARY_TYPE: { while( (uiJ < uiKeyLen) && (pKey[uiJ] != 0x02) && (pKey[uiJ] != 0x01) && (pKey[uiJ] != COLL_TRUNCATED)) { if ((pKey[uiJ] < 0x20) || (pKey[uiJ] > 0x2F)) { return (FLM_BAD_BINARY_KEY); } uiJ++; } if (uiJ < uiKeyLen && pKey[uiJ] == COLL_TRUNCATED) { uiJ++; } break; } case FLM_CONTEXT_TYPE: { if (pKey[uiJ] != 0x1F) { return (FLM_BAD_DRN_KEY); } uiJ += 5; break; } default: { return (FLM_BAD_KEY_FIELD_TYPE); } } } // See if there is another field. while( ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd + 1)->uiCompoundPos)) { pIfd++; } // pIfd will point to the LAST ifd with the same compound position. // pIfd increments below AFTER the compound marker is added. uiI++; if (uiI == uiNumIxFields) { break; } // If this is not the last field, make sure we are pointing to a // compound marker. if (uiJ >= uiKeyLen) { return (FLM_BAD_KEY_COMPOUND_MARKER); } uiMarkerChar = (FLMUINT) pKey[uiJ]; uiJ++; if (bIxAsia && IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE && !(pIfd->uiFlags & IFD_CONTEXT)) { if (uiJ >= uiKeyLen) { return (FLM_BAD_KEY_COMPOUND_MARKER); } uiMarkerChar <<= 8; uiMarkerChar += (FLMUINT) pKey[uiJ]; uiJ++; } if (uiMarkerChar != 2) { return (FLM_BAD_KEY_COMPOUND_MARKER); } pIfd++; } // If we didn't get through all of the fields in the loop above make // sure the remaining fields are all optional. No need to check for // alternate keys. while (uiI < uiNumIxFields) { uiCollateCount[uiI] = 0; uiI++; pIfd++; } // If we have a POST index, get the lower/upper case bits for each text // field. if ((bIxIsPost) && (uiTotalTextChars)) { // If it is not a compound key, we have an error. if (uiNumIxFields == 1) { return (FLM_BAD_IX_DEF); } // If we did not hit a 0x01, we have an error. if (uiJ >= uiKeyLen) { return (FLM_BAD_KEY_POST_MARKER); } uiMarkerChar = (FLMUINT) pKey[uiJ]; uiJ++; // If the last field is text, and we are in an Asian index we need // two bytes for the post marker. if (bIxAsia) { pIfd = &pIfdArray[uiNumIxFields - 1]; if ((IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) && (!(pIfd->uiFlags & IFD_CONTEXT))) { if (uiJ >= uiKeyLen) { return (FLM_BAD_KEY_POST_MARKER); } uiMarkerChar <<= 8; uiMarkerChar += (FLMUINT) pKey[uiJ]; uiJ++; } } if (uiMarkerChar != 1) { return (FLM_BAD_KEY_POST_MARKER); } // Go through all of the fields looking for TEXT fields. uiPostByteCount = 0; uiI = 0; pIfd = pIfdArray; while ((uiI < uiNumIxFields) && (uiJ < uiKeyLen)) { if (uiCollateCount[uiI]) { FLMINT uiTempCnt; uiMarkerChar = pKey[uiJ]; uiJ++; uiPostByteCount++; if (bIxAsia) { if (uiJ >= uiKeyLen) { return (FLM_BAD_KEY_POST_BYTE_COUNT); } uiMarkerChar <<= 8; uiMarkerChar += (FLMUINT) pKey[uiJ]; uiJ++; uiPostByteCount++; } switch (uiMarkerChar) { case 4: case 6: { if (bIxAsia) { return (FLM_BAD_TEXT_KEY_CASE_MARKER); } break; } case 5: { uiCaseBits = uiCollateCount[uiI]; if (bIxAsia) { uiCaseBits <<= 1; } uiTempCnt = (FLMUINT) ((uiCaseBits + 7) >> 3); uiJ += uiTempCnt; uiPostByteCount += uiTempCnt; break; } default: { return (FLM_BAD_TEXT_KEY_CASE_MARKER); } } } while( ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd + 1)->uiCompoundPos)) { uiI++; // Compares against uiNumIxFields. pIfd++; } uiI++; pIfd++; } // Account for the post byte count if ((uiJ >= uiKeyLen) || (uiPostByteCount != (FLMUINT) pKey[uiJ])) { return (FLM_BAD_KEY_POST_BYTE_COUNT); } uiJ++; } // We must end exactly right on the end of the key. return (((uiJ != uiKeyLen) || (uiI != uiNumIxFields)) ? FLM_BAD_KEY_LEN : FLM_NO_CORRUPTION); } /**************************************************************************** Desc: Verifies a block's header and sets up the STATE_INFO structure to verify the rest of the block. ****************************************************************************/ eCorruptionType flmVerifyBlockHeader( STATE_INFO * pStateInfo, BLOCK_INFO * pBlockInfoRV, FLMUINT uiBlockSize, FLMUINT uiExpNextBlkAddr, FLMUINT uiExpPrevBlkAddr, FLMBOOL bCheckEOF, FLMBOOL bCheckFullBlkAddr) { FLMBYTE * pBlk = pStateInfo->pBlk; if (pBlockInfoRV) { pBlockInfoRV->uiBlockCount++; } pStateInfo->uiNextBlkAddr = (FLMUINT) FB2UD( &pBlk[BH_NEXT_BLK]); if ((pStateInfo->uiEndOfBlock = (FLMUINT) FB2UW( &pBlk[BH_ELM_END])) < BH_OVHD) { pStateInfo->uiEndOfBlock = BH_OVHD; return (FLM_BAD_BLK_HDR_BLK_END); } else if (pStateInfo->uiEndOfBlock > uiBlockSize) { pStateInfo->uiEndOfBlock = uiBlockSize; return (FLM_BAD_BLK_HDR_BLK_END); } else if (pBlockInfoRV) { pBlockInfoRV->ui64BytesUsed += (FLMUINT64) (pStateInfo->uiEndOfBlock - BH_OVHD); } pStateInfo->uiElmOffset = BH_OVHD; // Verify the block address. if (bCheckFullBlkAddr) { if (GET_BH_ADDR( pBlk) != pStateInfo->uiBlkAddress) { return (FLM_BAD_BLK_HDR_ADDR); } } else { if ((GET_BH_ADDR( pBlk) & 0xFFFFFF00) != (pStateInfo->uiBlkAddress & 0xFFFFFF00)) { return (FLM_BAD_BLK_HDR_ADDR); } } // Verify that block address is below the logical EOF if (bCheckEOF && pStateInfo->pDb) { if (!FSAddrIsBelow( pStateInfo->uiBlkAddress, pStateInfo->pDb->LogHdr.uiLogicalEOF)) { return (FLM_BAD_FILE_SIZE); } } // Verify the block type. if ((pStateInfo->uiBlkType != 0xFF) && (pStateInfo->uiBlkType != (FLMUINT) BH_GET_TYPE( pBlk))) { return (FLM_BAD_BLK_HDR_TYPE); } // Verify the block level. if ((pStateInfo->uiLevel != 0xFF) && (pStateInfo->uiLevel != pBlk[BH_LEVEL])) { return (FLM_BAD_BLK_HDR_LEVEL); } // Verify the previous block address. If uiExpPrevBlkAddr is zero, we // do not know what the previous address should be, so we don't verify. if ((uiExpPrevBlkAddr) && (uiExpPrevBlkAddr != (FLMUINT) FB2UD( &pBlk[BH_PREV_BLK]))) { return (FLM_BAD_BLK_HDR_PREV); } // Verify the next block address. If uiExpNextBlkAddr is zero, we do // not know what the next address should be, se we don't verify. if ((uiExpNextBlkAddr) && (uiExpNextBlkAddr != pStateInfo->uiNextBlkAddr)) { return (FLM_BAD_BLK_HDR_NEXT); } // Verify that if it is a root block, the root bit flags is set, or if // it is NOT a root block, that the root bit flag is NOT set. if (pStateInfo->pLogicalFile) { if (pStateInfo->uiLevel != 0xFF) { FLMBOOL bShouldBeRootBlk; bShouldBeRootBlk = (pStateInfo->uiLevel == pStateInfo->pLogicalFile->pLfStats->uiNumLevels - 1) ? TRUE : FALSE; if (((bShouldBeRootBlk) && (!BH_IS_ROOT_BLK( pBlk))) || ((!bShouldBeRootBlk) && (BH_IS_ROOT_BLK( pBlk)))) { return (FLM_BAD_BLK_HDR_ROOT_BIT); } } // Verify the logical file number - if any. if (pStateInfo->pLogicalFile->pLFile->uiLfNum != (FLMUINT) FB2UW( &pBlk[BH_LOG_FILE_NUM])) { return (FLM_BAD_BLK_HDR_LF_NUM); } } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: ****************************************************************************/ FLMINT flmCompareKeys( FLMBYTE * pBuf1, FLMUINT uiBuf1Len, FLMBYTE * pBuf2, FLMUINT uiBuf2Len) { FLMINT iStatus; if (!uiBuf1Len) { iStatus = (!uiBuf2Len) ? 0 : -1; } else if (!uiBuf2Len) { iStatus = 1; } else if (uiBuf1Len < uiBuf2Len) { if ((iStatus = f_memcmp( pBuf1, pBuf2, uiBuf1Len)) == 0) { iStatus = -1; } } else if (uiBuf1Len > uiBuf2Len) { if ((iStatus = f_memcmp( pBuf1, pBuf2, uiBuf2Len)) == 0) { iStatus = 1; } } else { iStatus = f_memcmp( pBuf1, pBuf2, uiBuf1Len); } return (iStatus); } /**************************************************************************** Desc: Verify a index or data element in a b-tree and set pStateInfo. ****************************************************************************/ eCorruptionType flmVerifyElement( STATE_INFO * pStateInfo, FLMUINT uiFlags) { FLMUINT uiEndOfBlock = pStateInfo->uiEndOfBlock; FLMUINT uiOffset = pStateInfo->uiElmOffset; FLMUINT uiBlkType = pStateInfo->uiBlkType; FLMBYTE * pElm; FLMUINT uiElmLen; FLMBYTE * pElmKey; FLMUINT uiElmKeyLen; FLMUINT uiElmPKCLen; FLMINT iCmpStatus = 0; FLMBYTE * pCurKey = pStateInfo->pCurKey; FLMUINT uiCurKeyLen = pStateInfo->uiCurKeyLen; FLMBOOL bLfIsContainer = FALSE; FLMBOOL bLfIsIndex = FALSE; FLMUINT uiLfType = LF_INVALID; IXD * pIxd = NULL; IFD * pIfd = NULL; if (pStateInfo->pLogicalFile) { uiLfType = pStateInfo->pLogicalFile->pLFile->uiLfType; bLfIsContainer = (uiLfType == LF_CONTAINER) ? TRUE : FALSE; bLfIsIndex = (uiLfType == LF_INDEX) ? TRUE : FALSE; pIxd = pStateInfo->pLogicalFile->pIxd; pIfd = pStateInfo->pLogicalFile->pIfd; } // Get a pointer to the element to work with. pElm = pStateInfo->pElm = &pStateInfo->pBlk[uiOffset]; // Get the element length. if (uiBlkType == BHT_LEAF) { if (uiOffset + BBE_KEY > uiEndOfBlock) { return (FLM_BAD_ELM_LEN); } uiElmLen = pStateInfo->uiElmLen = (FLMUINT) (BBE_LEN( pElm)); pElmKey = pStateInfo->pElmKey = &pElm[BBE_KEY]; pStateInfo->pElmRec = BBE_REC_PTR( pElm); pStateInfo->uiElmRecLen = BBE_GET_RL( pElm); pStateInfo->uiElmRecOffset = 0; // Get the element key length and previous key count (PKC). uiElmKeyLen = pStateInfo->uiElmKeyLen = (FLMUINT) (BBE_GET_KL( pElm)); uiElmPKCLen = pStateInfo->uiElmPKCLen = (FLMUINT) (BBE_GET_PKC( pElm)); } else if (uiBlkType == BHT_NON_LEAF_DATA) { if (uiOffset + pStateInfo->uiElmOvhd > uiEndOfBlock) { return (FLM_BAD_ELM_LEN); } uiElmLen = pStateInfo->uiElmLen = BNE_DATA_OVHD; pElmKey = pStateInfo->pElmKey = pElm; uiElmKeyLen = 4; uiElmPKCLen = 0; } else { if (uiBlkType == BHT_NON_LEAF_COUNTS) { pStateInfo->uiChildCount = FB2UD( pElm + BNE_CHILD_COUNT); } if (uiOffset + pStateInfo->uiElmOvhd > uiEndOfBlock) { return (FLM_BAD_ELM_LEN); } uiElmLen = pStateInfo->uiElmLen = (FLMUINT) BBE_GET_KL( pElm) + pStateInfo->uiElmOvhd + (BNE_IS_DOMAIN( pElm) ? BNE_DOMAIN_LEN : 0); pElmKey = pStateInfo->pElmKey = &pElm[pStateInfo->uiElmOvhd]; // Get the element key length and previous key count (PKC). uiElmKeyLen = pStateInfo->uiElmKeyLen = (FLMUINT) (BBE_GET_KL( pElm)); uiElmPKCLen = pStateInfo->uiElmPKCLen = (FLMUINT) (BBE_GET_PKC( pElm)); } // Make sure the element length is within the block boundary. if (uiOffset + uiElmLen > uiEndOfBlock) { return (FLM_BAD_ELM_LEN); } // Get the record number from the element, if any. pStateInfo->uiElmDrn = 0; if ((bLfIsContainer) && (uiElmKeyLen + uiElmPKCLen == 4)) { FLMBYTE ucRecBuff[4]; if (uiElmPKCLen) { if ((uiCurKeyLen >= uiElmPKCLen) && (pStateInfo->bValidKey)) { f_memcpy( ucRecBuff, pCurKey, uiElmPKCLen); } else { f_memset( ucRecBuff, 0, uiElmPKCLen); } } if (uiElmKeyLen) { f_memcpy( &ucRecBuff[uiElmPKCLen], pElmKey, uiElmKeyLen); } pStateInfo->uiElmDrn = f_bigEndianToUINT32( ucRecBuff); if (pStateInfo->uiElmDrn == DRN_LAST_MARKER && uiBlkType == BHT_LEAF) { FLMUINT uiTempDrn; // Verify that the marker value is > the last DRN value. uiTempDrn = (FLMUINT) FB2UD( &pElmKey[uiElmKeyLen]); if (uiTempDrn <= pStateInfo->uiLastElmDrn) { return (FLM_BAD_LAST_DRN); } pStateInfo->uiLastElmDrn = uiTempDrn; } else { pStateInfo->uiLastElmDrn = pStateInfo->uiElmDrn; } } // Verify the first/last flags if it is a leaf element. if (uiBlkType == BHT_LEAF) { FLMUINT uiFirstFlag = (FLMUINT) (BBE_IS_FIRST( pElm)); FLMUINT uiPrevLastFlag = pStateInfo->uiElmLastFlag; // Verify the first element flag pStateInfo->uiElmLastFlag = (FLMUINT) (BBE_IS_LAST( pElm)); if (uiPrevLastFlag != 0xFF) { if ((uiPrevLastFlag) && (!uiFirstFlag)) { return (FLM_BAD_FIRST_ELM_FLAG); } else if ((!uiPrevLastFlag) && (uiFirstFlag)) { return (FLM_BAD_LAST_ELM_FLAG); } } } // If we are on the last element, verify that we are indeed. If we are, // set the key length to zero. if ((uiElmLen == pStateInfo->uiElmOvhd) && (uiElmLen + uiOffset == uiEndOfBlock) && (pStateInfo->uiNextBlkAddr == BT_END)) { pStateInfo->bValidKey = TRUE; pStateInfo->uiCurKeyLen = uiCurKeyLen = 0; } // If the length in a leaf element is BBE_LEM_LEN and it is not the // last element, we have an error. else if ((uiBlkType == BHT_LEAF) && (uiElmLen == BBE_LEM_LEN)) { return (FLM_BAD_LEM); } // If this is the last element in the block, and this is the last block // in the chain, this had better be the LEM. else if ((uiOffset + uiElmLen == uiEndOfBlock) && (pStateInfo->uiNextBlkAddr == BT_END)) { return (FLM_BAD_LEM); } // Verify that the key is OK. The key must pass three tests in order // for it to be OK. First, the total key length must not exceed the // maximum key size. Second, if there is a previous key count, it must // not exceed the size of the previous key. Third, the new key must be // greater than or equal to the previous key -- all keys in the block // must be in ascending order. The third part is tested by comparing // only the part of the key which is going to change - the part pointed // to by pElmKey. We already know that the part of the key represented // in the previous key count is equal - by definition, so there is no // need to test it. else if ((uiElmKeyLen + uiElmPKCLen > MAX_KEY_SIZ) || (bLfIsContainer && (uiElmKeyLen + uiElmPKCLen != 4) && (uiBlkType != BHT_NON_LEAF_DATA))) { pStateInfo->bValidKey = FALSE; return (FLM_BAD_ELM_KEY_SIZE); } else if ((uiOffset == BH_OVHD && uiElmPKCLen) || (uiElmPKCLen > uiCurKeyLen && uiCurKeyLen && pStateInfo->bValidKey)) { pStateInfo->bValidKey = FALSE; return (FLM_BAD_ELM_PKC_LEN); } else { eCorruptionType eKeyCorruptionCode = FLM_NO_CORRUPTION; // NOTE: The reason we are saving the error code into // eKeyCorruptionCode instead of returning when we detect it is // because we want to save the new key into pStateInfo->pCurKey // before we return. However, there are some checks which we must // make before saving the new key. if ((pStateInfo->bValidKey) || (uiElmPKCLen == 0)) { if (pStateInfo->bValidKey) { if (uiBlkType == BHT_NON_LEAF_DATA) { iCmpStatus = flmCompareKeys( pElmKey, uiElmKeyLen, pCurKey, DIN_KEY_SIZ); } else { iCmpStatus = flmCompareKeys( pElmKey, uiElmKeyLen, &pCurKey[uiElmPKCLen], uiCurKeyLen - uiElmPKCLen); } if (iCmpStatus < 0) { eKeyCorruptionCode = FLM_BAD_ELM_KEY_ORDER; } // Check the key compression to see if it is good. else if ((uiBlkType != BHT_NON_LEAF_DATA) && (uiOffset > BH_OVHD) && (uiElmKeyLen > 0) && (uiElmPKCLen < BBE_PKC_MAX) && (uiElmPKCLen < uiCurKeyLen) && (pElmKey[0] == pCurKey[uiElmPKCLen])) { eKeyCorruptionCode = FLM_BAD_ELM_KEY_COMPRESS; } } // The keys had better be equal if it is a continuation element. // Otherwise, they had better be different. if (!pStateInfo->bValidKey) { pStateInfo->ui64KeyCount++; pStateInfo->ui64KeyRefs = 0; } else if (iCmpStatus != 0) { pStateInfo->ui64KeyCount++; pStateInfo->ui64KeyRefs = 0; // If this is a continuation element in a leaf block, the key // should be the same as the last key. if ((uiBlkType == BHT_LEAF) && (!BBE_IS_FIRST( pElm))) { eKeyCorruptionCode = FLM_BAD_CONT_ELM_KEY; } } else { if (pIxd) { if (((uiBlkType == BHT_LEAF) && (BBE_IS_FIRST( pElm))) || ((uiBlkType != BHT_LEAF) && (pIxd->uiFlags & IXD_UNIQUE))) { eKeyCorruptionCode = FLM_NON_UNIQUE_FIRST_ELM_KEY; } } } // Save the new key. if (uiBlkType != BHT_NON_LEAF_DATA) { pStateInfo->uiCurKeyLen = uiCurKeyLen = uiElmPKCLen + uiElmKeyLen; f_memcpy( &pCurKey[uiElmPKCLen], pElmKey, uiElmKeyLen); } else { pStateInfo->uiCurKeyLen = uiCurKeyLen = DIN_KEY_SIZ; *(FLMUINT32*) pCurKey = *(FLMUINT32*) pElmKey; } pStateInfo->bValidKey = TRUE; // Perform some additional checks on the key if an index key. if (eKeyCorruptionCode == FLM_NO_CORRUPTION && bLfIsIndex && pIxd && (uiFlags & FLM_CHK_FIELDS)) { if (!pIxd->uiContainerNum) { FLMUINT uiContainerPartLen = getIxContainerPartLen( pIxd); RCODE tmpRc; LFILE * pTmpLFile; if (uiCurKeyLen <= uiContainerPartLen) { eKeyCorruptionCode = FLM_BAD_KEY_LEN; goto Bad_Key; } if (pStateInfo->pDb) { if (RC_BAD( tmpRc = fdictGetContainer( pStateInfo->pDb->pDict, getContainerFromKey( pCurKey, uiCurKeyLen), &pTmpLFile))) { eKeyCorruptionCode = FLM_BAD_CONTAINER_IN_KEY; goto Bad_Key; } } uiCurKeyLen -= uiContainerPartLen; } eKeyCorruptionCode = flmVerifyKey( pCurKey, uiCurKeyLen, pIxd->uiLanguage, pIfd, pIxd->uiNumFlds); } } if (eKeyCorruptionCode != FLM_NO_CORRUPTION) { Bad_Key: pStateInfo->bValidKey = FALSE; return (eKeyCorruptionCode); } } return (FLM_NO_CORRUPTION); } /**************************************************************************** Desc: ****************************************************************************/ eCorruptionType flmVerifyElmFOP( STATE_INFO * pStateInfo) { eCorruptionType eCorruptionCode = FLM_NO_CORRUPTION; FLMBYTE * pElmRec = pStateInfo->pElmRec; FLMUINT uiElmRecOffset = pStateInfo->uiElmRecOffset; FLMUINT uiElmRecLen = pStateInfo->uiElmRecLen; FLMBYTE * pField; FLMBYTE * pTmpFld; FLMBOOL bDictField; FLMBOOL bFOPIsField; FLMBYTE * pElm = pStateInfo->pElm; FLMUINT uiFOPDataLen; FLMUINT uiFldOverhead; FLMUINT uiBaseFldFlags; FLMUINT uiLfNumber; FLMUINT uiMaxDictFieldNum = FLM_LAST_DICT_FIELD_NUM; uiLfNumber = (pStateInfo->pLogicalFile) ? pStateInfo->pLogicalFile->pLFile->uiLfNum : (FLMUINT) 0; if ((BBE_IS_FIRST( pElm)) && (uiElmRecOffset == 0)) { pStateInfo->uiFOPType = 0xFF; pStateInfo->uiFieldLen = 0; pStateInfo->uiFieldProcessedLen = 0; pStateInfo->uiFieldType = 0xFF; pStateInfo->uiFieldNum = 0; pStateInfo->uiFieldLevel = 0; pStateInfo->uiJumpLevel = 0; pStateInfo->pFOPData = NULL; pStateInfo->uiFOPDataLen = 0; pStateInfo->pValue = NULL; pStateInfo->uiEncId = 0; pStateInfo->uiEncFieldLen = 0; pStateInfo->pData = NULL; pStateInfo->pvField = NULL; if (pStateInfo->pRecord) { pStateInfo->pRecord->clear(); } pStateInfo->bElmRecOK = TRUE; } // If the state is goofed up, just give back the reset of the element. // Don't try to parse it. if (!pStateInfo->bElmRecOK) { pStateInfo->uiFOPType = FLM_FOP_CONT_DATA; pStateInfo->uiFieldLen = uiElmRecLen - uiElmRecOffset; pStateInfo->uiFieldProcessedLen = 0; } // If we are at the first of an element's record and we have a half // processed field, return that first. else if (uiElmRecOffset == 0 && (pStateInfo->uiFieldProcessedLen < (pStateInfo->uiEncId ? pStateInfo->uiEncFieldLen : pStateInfo->uiFieldLen))) { // If this is a FIRST element, we have a problem. if (BBE_IS_FIRST( pElm)) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_FIRST_ELM_FLAG; goto Exit; } else { pStateInfo->uiFOPType = FLM_FOP_CONT_DATA; } } else if (pStateInfo->uiElmDrn == DRN_LAST_MARKER) { pStateInfo->uiFOPType = FLM_FOP_NEXT_DRN; if (uiElmRecLen != 4) { eCorruptionCode = FLM_BAD_LAST_DRN; goto Exit; } } else { bDictField = FALSE; bFOPIsField = TRUE; pStateInfo->uiFieldType = 0xFF; pStateInfo->uiFieldNum = 0; pStateInfo->uiFieldLen = 0; pStateInfo->uiEncId = 0; pStateInfo->uiEncFieldLen = 0; pField = &pElmRec[uiElmRecOffset]; // Test for STANDARD field -- must be defined in dictionary. if (FOP_IS_STANDARD( pField)) { pStateInfo->uiFOPType = FLM_FOP_STANDARD; bDictField = TRUE; uiFldOverhead = 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = (FLMUINT) (FSTA_FLD_LEN( pField)); pStateInfo->uiFieldNum = FSTA_FLD_NUM( pField); // See if field is a child or sibling of previous field. if (FSTA_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } } // Test for OPEN type -- must also be defined in dictionary. else if (FOP_IS_OPEN( pField)) { pStateInfo->uiFOPType = FLM_FOP_OPEN; bDictField = TRUE; // See if the field is a child or sibling of the previous field. if (FOPE_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } // See if field number is one or two bytes. pTmpFld = pField; uiFldOverhead = 0; uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pTmpFld)); pTmpFld++; if (FOP_2BYTE_FLDNUM( uiBaseFldFlags)) { uiFldOverhead += 3; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld); pTmpFld += 2; } else { uiFldOverhead += 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld); pTmpFld++; } // Determine the field length if (FOP_2BYTE_FLDLEN( uiBaseFldFlags)) { uiFldOverhead += 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = (FLMUINT) FB2UW( pTmpFld); } else { uiFldOverhead++; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = (FLMUINT) (*pTmpFld); } } // Test for UNREGISTERED fields -- not in dictionary. else if (FOP_IS_TAGGED( pField)) { pStateInfo->uiFOPType = FLM_FOP_TAGGED; // See if the field is a child or sibling of the previous field. if (FTAG_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } pTmpFld = pField; uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pTmpFld)); pTmpFld++; uiFldOverhead = 1; // Get the field type. pStateInfo->uiFieldType = (FLMUINT) FTAG_GET_FLD_TYPE( *pTmpFld); pTmpFld++; uiFldOverhead++; // See if field number is one or two bytes. if (FOP_2BYTE_FLDNUM( uiBaseFldFlags)) { uiFldOverhead += 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld); pTmpFld += 2; } else { uiFldOverhead++; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld); pTmpFld++; } // Toggle high bit to get true field number. FOP_TAGGED is now // used for more than just UNREGISTERED fields. if (pStateInfo->uiFieldNum & 0x8000) { pStateInfo->uiFieldNum &= (FLMUINT) 0x7FFF; } else { pStateInfo->uiFieldNum |= (FLMUINT) 0x8000; } // See if the field length is one or two bytes if (FOP_2BYTE_FLDLEN( uiBaseFldFlags)) { uiFldOverhead += 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = (FLMUINT) FB2UW( pTmpFld); } else { uiFldOverhead++; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = (FLMUINT) (*pTmpFld); } } // Test for a field with NO value -- must be in dictionary else if (FOP_IS_NO_VALUE( pField)) { pStateInfo->uiFOPType = FLM_FOP_NO_VALUE; bDictField = TRUE; pStateInfo->uiFieldLen = 0; // See if the field is a child or sibling of previous field if (FNOV_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } // See if field number is one or two bytes pTmpFld = pField + 1; uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pField)); if (FOP_2BYTE_FLDNUM( uiBaseFldFlags)) { uiFldOverhead = 3; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld); pTmpFld += 2; } else { uiFldOverhead = 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld); pTmpFld++; } } // Test for the code which just resets the field level else if (FOP_IS_SET_LEVEL( pField)) { FLMUINT uiTempLevel; pStateInfo->uiFOPType = FLM_FOP_JUMP_LEVEL; uiFldOverhead = 1; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } bFOPIsField = FALSE; pStateInfo->uiFieldNum = 0; pStateInfo->uiFieldLen = 0; pStateInfo->uiFieldType = 0xFF; // Jumping back better not cause us to go below level one uiTempLevel = (FLMUINT) (FSLEV_GET( pField)); pStateInfo->uiJumpLevel = uiTempLevel; if (pStateInfo->uiFieldLevel <= uiTempLevel) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_LEVEL_JUMP; goto Exit; } pStateInfo->uiFieldLevel -= uiTempLevel; } else if (FOP_IS_RECORD_INFO( pField)) { bFOPIsField = FALSE; pStateInfo->uiFOPType = FLM_FOP_REC_INFO; uiFldOverhead = 1; pStateInfo->uiFieldNum = 0; pStateInfo->uiFieldType = 0xFF; pTmpFld = pField + 1; uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pField)); if (FOP_2BYTE_FLDLEN( uiBaseFldFlags)) { uiFldOverhead += 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = *pTmpFld++; pStateInfo->uiFieldLen += ((FLMUINT) * pTmpFld++) << 8; } else { uiFldOverhead++; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFieldLen = *pTmpFld++; } } else if (FOP_IS_ENCRYPTED( pField)) { FLMBOOL bTagSz; FLMBOOL bLenSz; FLMBOOL bENumSz; FLMBOOL bELenSz; pStateInfo->uiFOPType = FLM_FOP_ENCRYPTED; bFOPIsField = TRUE; uiFldOverhead = 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } if (FENC_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } pStateInfo->uiFieldType = (FLMUINT) (FENC_FLD_TYPE( pField)); bTagSz = FENC_TAG_SZ( pField); if (bTagSz) { uiFldOverhead += 2; } else { uiFldOverhead++; } bLenSz = FENC_LEN_SZ( pField); if (bLenSz) { uiFldOverhead += 2; } else { uiFldOverhead++; } bENumSz = FENC_ETAG_SZ( pField); if (bENumSz) { uiFldOverhead += 2; } else { uiFldOverhead++; } bELenSz = FENC_ELEN_SZ( pField); if (bELenSz) { uiFldOverhead += 2; } else { uiFldOverhead++; } if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pTmpFld = pField + 2; pStateInfo->uiFieldNum = (FLMUINT) * pTmpFld++; if (bTagSz) { pStateInfo->uiFieldNum += ((FLMUINT) * pTmpFld++) << 8; } pStateInfo->uiFieldLen = (FLMUINT) * pTmpFld++; if (bLenSz) { pStateInfo->uiFieldLen += ((FLMUINT) * pTmpFld++) << 8; } pStateInfo->uiEncId = (FLMUINT) * pTmpFld++; if (bENumSz) { pStateInfo->uiEncId += ((FLMUINT) * pTmpFld++) << 8; } pStateInfo->uiEncFieldLen = (FLMUINT) * pTmpFld++; if (bELenSz) { pStateInfo->uiEncFieldLen += ((FLMUINT) * pTmpFld++) << 8; } } else if (FOP_IS_LARGE( pField)) { if( pStateInfo->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) { pStateInfo->uiFOPType = FLM_FOP_BAD; pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } pStateInfo->uiFOPType = FLM_FOP_LARGE; bFOPIsField = TRUE; uiFldOverhead = 2; if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } if (FLARGE_LEVEL( pField)) { pStateInfo->uiFieldLevel++; } pStateInfo->uiFieldType = FLARGE_FLD_TYPE( pField); pStateInfo->uiFieldNum = FLARGE_TAG_NUM( pField); uiFldOverhead += 2; pStateInfo->uiFieldLen = FLARGE_DATA_LEN( pField); uiFldOverhead += 4; if (FLARGE_ENCRYPTED( pField)) { pStateInfo->uiEncId = FLARGE_ETAG_NUM( pField); uiFldOverhead += 2; pStateInfo->uiEncFieldLen = FLARGE_EDATA_LEN( pField); uiFldOverhead += 4; } if (uiElmRecOffset + uiFldOverhead > uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } } else { // Anything else is a code we don't understand pStateInfo->uiFOPType = FLM_FOP_BAD; pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD; goto Exit; } // If it is a field, get its type and make further checks if (bFOPIsField) { FLMUINT uiFldNum = pStateInfo->uiFieldNum; if (pStateInfo->pLogicalFile) { pStateInfo->pLogicalFile->pLfStats->ui64FldRefCount++; } // If it is a dictionary field, verify that it is indeed and get // the field type from the dictionary. if (bDictField) { // If the field number is a reserved dictionary tag, it must // be a TEXT field. These are always stored in the FOP_OPEN // format - hence, the bDictField flag will be TRUE. if ((uiFldNum >= FLM_DICT_FIELD_NUMS) && (uiFldNum <= uiMaxDictFieldNum)) { pStateInfo->uiFieldType = FLM_TEXT_TYPE; } // If we have no dictionary, set the field type to binary so // that the field will pass every test. else if (!pStateInfo->pDb) { pStateInfo->uiFieldType = FLM_BINARY_TYPE; } else { // If we can't find the field in the dictionary we have a // corruption. if (RC_BAD( fdictGetField( pStateInfo->pDb->pDict, uiFldNum, &pStateInfo->uiFieldType, NULL, NULL))) { pStateInfo->bElmRecOK = FALSE; pStateInfo->uiFieldType = FLM_BINARY_TYPE; eCorruptionCode = FLM_BAD_ELM_FLD_NUM; // Keep processing and fill out the rest of the state // information. Even though we have a bad field number // here, the caller may want to simply skip the field // instead of aborting the entire element. goto Keep_Processing_Field; } } } // Check the field type switch (pStateInfo->uiFieldType) { case FLM_TEXT_TYPE: case FLM_NUMBER_TYPE: case FLM_BINARY_TYPE: case FLM_BLOB_TYPE: { break; } case FLM_CONTEXT_TYPE: { if (pStateInfo->uiFieldLen != 0 && pStateInfo->uiFieldLen != 4) { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_LEN; goto Exit; } break; } default: { pStateInfo->bElmRecOK = FALSE; eCorruptionCode = FLM_BAD_ELM_FLD_TYPE; goto Exit; } } } Keep_Processing_Field: // At this point, it is possible for us to have a bad field number // error, but we still want to set up pStateInfo so the caller can // simply skip the field if desired. uiElmRecOffset += uiFldOverhead; pStateInfo->uiFieldProcessedLen = 0; } // Setup the state structure to point to the data if (pStateInfo->uiEncId) { uiFOPDataLen = pStateInfo->uiEncFieldLen - pStateInfo->uiFieldProcessedLen; } else { uiFOPDataLen = pStateInfo->uiFieldLen - pStateInfo->uiFieldProcessedLen; } if (uiFOPDataLen > uiElmRecLen - uiElmRecOffset) { uiFOPDataLen = uiElmRecLen - uiElmRecOffset; if (BBE_IS_LAST( pElm)) { eCorruptionCode = FLM_BAD_ELM_FLD_LEN; goto Exit; } } pStateInfo->uiFieldProcessedLen += uiFOPDataLen; pStateInfo->uiFOPDataLen = uiFOPDataLen; pStateInfo->pFOPData = &pElmRec[uiElmRecOffset]; uiElmRecOffset += uiFOPDataLen; pStateInfo->uiElmRecOffset = uiElmRecOffset; Exit: return (eCorruptionCode); } /**************************************************************************** Desc: ****************************************************************************/ RCODE flmVerifyIXRefs( STATE_INFO * pStateInfo, IX_CHK_INFO * pIxChkInfo, FLMUINT uiResetDrn, eCorruptionType * peElmCorruptionCode) { FLMUINT uiElmRecOffset = pStateInfo->uiElmRecOffset; FLMBYTE * pElm = pStateInfo->pElm; FLMBYTE * pElmRec = pStateInfo->pElmRec; FLMUINT uiElmRecLen = pStateInfo->uiElmRecLen; FLMUINT uiLowestDrn; FLMUINT uiDrn; FLMUINT uiTmpNum; FLMBYTE * pTmpElmRec; FLMUINT uiNumBytes; FLMUINT uiElmRefs = 0; FLMBOOL bOneRun; RCODE rc = FERR_OK; *peElmCorruptionCode = FLM_NO_CORRUPTION; if ((BBE_IS_FIRST( pElm)) && (uiElmRecOffset == 0)) { pStateInfo->uiCurrIxRefDrn = 0; pStateInfo->bElmRecOK = TRUE; } // Determine the element domain uiLowestDrn = 0; if (*pElmRec == 0xFC) { uiElmRecOffset++; if (!flmGetSEN( &pElmRec[uiElmRecOffset], &uiLowestDrn, &uiNumBytes)) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_DOMAIN_SEN; goto Exit; } uiElmRecOffset += uiNumBytes; uiLowestDrn <<= 8; } // Get the base DRN for the element if (!flmGetSEN( &pElmRec[uiElmRecOffset], &uiDrn, &uiNumBytes)) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_BASE_SEN; goto Exit; } uiElmRecOffset += uiNumBytes; // If this is the first element or the state info has not yet been // set, set the pStateInfo. if ((BBE_IS_FIRST( pElm)) || (!pStateInfo->uiCurrIxRefDrn)) { pStateInfo->uiCurrIxRefDrn = uiDrn; } else if (uiDrn >= pStateInfo->uiCurrIxRefDrn) { // If the DRN's are not descending, we have a problem pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_IX_REF; goto Exit; } uiElmRefs++; if (uiDrn <= uiResetDrn) { uiResetDrn = 0; } if (pIxChkInfo != NULL && !uiResetDrn) { pStateInfo->uiCurrIxRefDrn = uiDrn; if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo, uiDrn))) || (pIxChkInfo->pDbInfo->bReposition)) { goto Exit; } } while (uiElmRecOffset < uiElmRecLen) { pTmpElmRec = &pElmRec[uiElmRecOffset]; // See if we have a one run bOneRun = FALSE; if (*pTmpElmRec >= 0xF0 && *pTmpElmRec <= 0xF7) { uiTmpNum = (FLMUINT) ((*pTmpElmRec & 0x0F) + 2); uiElmRefs += uiTmpNum; uiNumBytes = 1; bOneRun = TRUE; } else if (*pTmpElmRec == 0xF8) { if (!flmGetSEN( pTmpElmRec + 1, &uiTmpNum, &uiNumBytes)) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_ONE_RUN_SEN; goto Exit; } uiNumBytes++; uiElmRefs += uiTmpNum; bOneRun = TRUE; } else { // We have a delta if (!flmGetSEN( pTmpElmRec, &uiTmpNum, &uiNumBytes)) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_DELTA_SEN; goto Exit; } uiElmRefs++; } // The new drn must not take us zero or negative if (uiDrn <= uiTmpNum) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_IX_REF; goto Exit; } if (bOneRun) { while (uiTmpNum > 0) { uiTmpNum--; uiDrn--; if (uiDrn <= uiResetDrn) { uiResetDrn = 0; } pStateInfo->uiCurrIxRefDrn = uiDrn; if (pIxChkInfo != NULL && !uiResetDrn) { // Verify that the key+ref is in the result set if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo, uiDrn))) || pIxChkInfo->pDbInfo->bReposition) { goto Exit; } } } } else { uiDrn -= uiTmpNum; if (uiDrn <= uiResetDrn) { uiResetDrn = 0; } pStateInfo->uiCurrIxRefDrn = uiDrn; if (pIxChkInfo != NULL && !uiResetDrn) { // Verify that the key+ref is in the result set if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo, uiDrn))) || pIxChkInfo->pDbInfo->bReposition) { goto Exit; } } } uiElmRecOffset += uiNumBytes; } // If we didn't end up right at the end of the element, we have // corruption. if (uiElmRecOffset != uiElmRecLen) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_END; goto Exit; } // The last drn must not be lower than the lowest Drn for the element if (uiDrn < uiLowestDrn) { pStateInfo->bElmRecOK = FALSE; *peElmCorruptionCode = FLM_BAD_ELM_DOMAIN; goto Exit; } pStateInfo->uiCurrIxRefDrn = uiDrn; pStateInfo->uiElmRecOffset = uiElmRecOffset; if (pStateInfo->pLogicalFile) { pStateInfo->pLogicalFile->pLfStats->ui64FldRefCount += (FLMUINT64) uiElmRefs; } pStateInfo->ui64KeyRefs += (FLMUINT64) uiElmRefs; // Check for a non-unique reference in a unique key if ((pStateInfo->pLogicalFile) && (pStateInfo->pLogicalFile->pIxd->uiFlags & IXD_UNIQUE) && (pStateInfo->ui64KeyRefs > (FLMUINT64) 1)) { pStateInfo->bElmRecOK = FALSE; if (pIxChkInfo != NULL) { // Give the application the option of deleting the record to // resolve the corruption. if (RC_BAD( rc = chkResolveNonUniqueKey( pStateInfo, pIxChkInfo, pStateInfo->pLogicalFile->pLFile->uiLfNum, pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, pStateInfo->uiCurrIxRefDrn))) { if (rc == FERR_OLD_VIEW) { // Set uiCurrIxRefDrn to zero so that the check will // re-position to the first reference of the current key. pStateInfo->uiCurrIxRefDrn = 0; } goto Exit; } } *peElmCorruptionCode = FLM_NON_UNIQUE_ELM_KEY_REF; } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL flmGetSEN( FLMBYTE * pTmpElmRec, FLMUINT * puiDrnRV, FLMUINT * puiNumBytesRV) { FLMUINT uiNumBytes; FLMUINT uiDrn; FLMUINT uiChar = (FLMUINT) *pTmpElmRec; FLMUINT uiTempDrn; pTmpElmRec++; if (!(uiChar & 0x80)) { uiNumBytes = 0; uiDrn = (FLMUINT) uiChar; } else if ((uiChar & 0xC0) == 0x80) { uiNumBytes = 1; uiDrn = (FLMUINT) (uiChar & 0x3F); } else if ((uiChar & 0xF0) == 0xC0) { uiNumBytes = 2; uiDrn = (FLMUINT) (uiChar & 0x0F); } else if ((uiChar & 0xF0) == 0xD0) { uiNumBytes = 3; uiDrn = (FLMUINT) (uiChar & 0x0F); } else if ((uiChar & 0xF0) == 0xE0) { uiNumBytes = 4; uiDrn = (FLMUINT) (uiChar & 0x0F); } else { return (FALSE); } *puiNumBytesRV = uiNumBytes + 1; while (uiNumBytes) { // Check for overflow uiTempDrn = (FLMUINT) (*pTmpElmRec); if (0xFFFFFFFF - uiDrn < (FLMUINT) 256 + uiTempDrn) { return (FALSE); } uiDrn <<= 8; uiDrn += uiTempDrn; pTmpElmRec++; uiNumBytes--; } *puiDrnRV = uiDrn; return (TRUE); } /**************************************************************************** Desc: ****************************************************************************/ void flmInitReadState( STATE_INFO * pStateInfo, FLMBOOL * pbStateInitialized, FLMUINT uiVersionNum, FDB * pDb, // May be NULL. LF_HDR * pLogicalFile, FLMUINT uiLevel, FLMUINT uiBlkType, FLMBYTE * pKeyBuffer) { if ((*pbStateInitialized) && (pStateInfo->pRecord)) { pStateInfo->pRecord->Release(); pStateInfo->pRecord = NULL; } f_memset( pStateInfo, 0, sizeof(STATE_INFO)); *pbStateInitialized = TRUE; pStateInfo->uiVersionNum = uiVersionNum; pStateInfo->pDb = pDb; pStateInfo->pLogicalFile = pLogicalFile; pStateInfo->uiLevel = uiLevel; // Special cases for leaf and non-leaf blocks if (uiBlkType == BHT_LEAF) { pStateInfo->uiElmOvhd = BBE_KEY; } else if (uiBlkType == BHT_NON_LEAF) { if (pLogicalFile) { if (pLogicalFile->pLFile->uiLfType == LF_INDEX) { if (pLogicalFile->pIxd && (pLogicalFile->pIxd->uiFlags & IXD_POSITIONING)) { uiBlkType = BHT_NON_LEAF_COUNTS; } else { uiBlkType = BHT_NON_LEAF; } } else { uiBlkType = BHT_NON_LEAF_DATA; } } } if (uiBlkType == BHT_NON_LEAF_DATA) { pStateInfo->uiElmOvhd = BNE_DATA_OVHD; } else if (uiBlkType == BHT_NON_LEAF) { pStateInfo->uiElmOvhd = BNE_KEY_START; } else if (uiBlkType == BHT_NON_LEAF_COUNTS) { pStateInfo->uiElmOvhd = BNE_KEY_COUNTS_START; } pStateInfo->uiBlkType = uiBlkType; pStateInfo->pCurKey = pKeyBuffer; pStateInfo->uiElmLastFlag = 0xFF; pStateInfo->uiFieldType = 0xFF; } libflaim-4.9.966/src/fdict.cpp0000644000175000017500000027354510510774540017471 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Dictionary access routiones. // Tabs: 3 // // Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdict.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE fdictAddDictIndex( TDICT * pTDict); FSTATIC RCODE DDFieldParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord, FLMUINT uiDictRecNum); FSTATIC RCODE DDGetReference( FlmRecord * pRecord, void * pvField, const char * pszBuffer, FLMUINT * puiIdRef); FSTATIC RCODE DDAllocEntry( TDICT * pTDict, FlmRecord * pRecord, FLMUINT uiDictRecNum, DDENTRY ** ppDDEntryRV); FSTATIC RCODE DDIxParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord, void * pvField); FSTATIC RCODE DDBuildFldPath( TDICT * pTDict, TIFD ** ppTIfd, FlmRecord * pRecord, void * pvField, FLMUINT uiBaseNum); FSTATIC FLMBOOL DDMoveWord( char * pucDest, char * pucSrc, FLMUINT uiMaxDestLen, FLMUINT * puiPos); FSTATIC RCODE DDContainerParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord); FSTATIC void DDTextToNative( FlmRecord * pRecord, void * pvField, char * pszBuffer, FLMUINT uiBufLen, FLMUINT * puiBufLen); FSTATIC RCODE DDParseStateOptions( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo); FSTATIC RCODE DDEncDefParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord); FSTATIC RCODE DDGetEncKey( TDICT * pTDict, FlmRecord * pRecord, void * pvField, TENCDEF * pTEncDef); FSTATIC RCODE DDMakeDictIxKey( FDB * pDb, FlmRecord * pRecord, FLMBYTE * pKeyBuf, FLMUINT * puiKeyLenRV); FSTATIC RCODE DDCheckNameConflict( FDB * pDb, LFILE * pDictIxLFile, FlmRecord * pNewRec, FLMUINT uiDrn, FlmRecord * pOldRec); FSTATIC RCODE DDCheckIDConflict( FDB * pDb, LFILE * pDictContLFile, FLMUINT uiDrn); FSTATIC RCODE DDIxDictRecord( FDB * pDb, LFILE * pDictIxLFile, FLMUINT uiDrn, FlmRecord * pRecord, FLMUINT uiFlags); FSTATIC void fdictFixupPointers( FDICT * pNewDict, FDICT * pOldDict); FSTATIC RCODE fdictReallocAllTables( TDICT * pTDict); FSTATIC RCODE fdictReallocTbl( FLMUINT uiElementSize, FLMUINT uiTblSize, FLMUINT uiAddElements, void ** ppvTblRV); FSTATIC void fdictAddItem( TDICT * pTDict, FLMUINT uiFieldNum, FLMUINT uiFieldType); FSTATIC RCODE fdictAddIndex( TDICT * pTDict, DDENTRY * pEntry); FSTATIC RCODE fdictFixupIfdPointers( FDICT * pDict, FLMUINT uiIfdStartOffset); FSTATIC RCODE fdictAddNewCCS( TDICT * pTDict, TENCDEF * pTEncDef, FLMUINT uiRecNum); #define FDD_MAX_VALUE_SIZE 64 #define MAX_ENC_TYPES 3 #define START_DD_INDEX_OPTS 0 #define DD_IX_FIELD_OPT 0 #define DD_IX_COMPOUND_OPT 1 #define DD_IX_UPPER_OPT 2 #define DD_IX_EACHWORD_OPT 3 #define DD_IX_MIXED_OPT 4 #define DD_IX_CONTEXT_OPT 5 #define DD_IX_POST_OPT 6 #define MAX_DD_INDEX_OPTS 7 // NOTE: If you change the arrangement of the values in this array, make sure // you search the entire codebase for references to DDEncOpts and DDGetEncType // and verify that the changes won't cause problems. This is particularly // important because these values DO NOT match up exactly with the values in // the SMEncryptionScheme enum that's used at the SMI level. const char * DDEncOpts[ MAX_ENC_TYPES] = { "aes", "des3", "des" }; FSTATIC POOL_STATS g_TDictPoolStats = {0,0}; /******************************************************************************* Desc: Retrieves a dictionary item name. Notes: Given an item ID, this routine will search a specified shared or local dictionary for the item. If it is found, the name of the item will be returned. This routine supports version 2.0 and higher databases only. *******************************************************************************/ FLMEXP RCODE FLMAPI FlmGetItemName( HFDB hDb, FLMUINT uiItemId, FLMUINT uiNameBufSize, char * pszNameBuf) { RCODE rc = FERR_OK; FlmRecord * pRecord = NULL; *pszNameBuf = 0; if( RC_BAD( rc = FlmRecordRetrieve( hDb, FLM_DICT_CONTAINER, uiItemId, FO_EXACT, &pRecord, NULL))) { goto Exit; } if( RC_BAD( rc = pRecord->getNative( pRecord->root(), pszNameBuf, &uiNameBufSize))) { goto Exit; } Exit: if( pRecord) { pRecord->Release(); } return (rc == FERR_EOF_HIT) ? RC_SET( FERR_NOT_FOUND) : rc; } /**************************************************************************** Desc: Read all data dictionary records parsing and sending to process. All temporary structures are off of pTDict. pTDict must be setup. ****************************************************************************/ RCODE fdictProcessAllDictRecs( FDB * pDb, TDICT * pTDict) { RCODE rc; LFILE * pLFile = pTDict->pLFile; BTSK stackBuf[ BH_MAX_LEVELS ]; // Stack to hold b-tree variables BTSK * stack = stackBuf; // Points to proper stack frame FLMBYTE btKeyBuf[ DRN_KEY_SIZ +8]; // Key buffer pointed to by stack FLMBYTE key[4]; // Used for dummy empty key FLMUINT uiDrn; FlmRecord * pRecord = NULL; // Add the dictionary index to the front of TDICT. if( RC_BAD( rc = fdictAddDictIndex( pTDict))) { goto Exit; } // Position to the first of the data dictionary data records & read. FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); stack->pKeyBuf = btKeyBuf; f_UINT32ToBigEndian( 0, key); if( RC_BAD(rc = FSBtSearch( pDb, pLFile, &stack, key, DRN_KEY_SIZ, 0 ))) { goto Exit; } // Special case of no records. if( stack->uiCmpStatus == BT_END_OF_DATA) goto Exit; stack->uiFlags = NO_STACK; // Fake out the stack for speed. do { uiDrn = f_bigEndianToUINT32( btKeyBuf); if( uiDrn == DRN_LAST_MARKER) { break; } // VERY IMPORTANT NOTE: // DO NOT READ FROM CACHE - THE RECORD MAY // NOT HAVE BEEN PUT INTO RECORD CACHE YET, AND WE NEED TO HAVE // THE CORRECT VERSION OF THE RECORD. if( RC_BAD( rc = FSReadElement( pDb, &pDb->TempPool, pLFile, uiDrn, stack, TRUE, &pRecord, NULL, NULL))) { break; } if( RC_BAD(rc = fdictProcessRec( pTDict, pRecord, uiDrn))) { pDb->Diag.uiDrn = uiDrn; pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN; if( pTDict->uiBadField != 0) { pDb->Diag.uiFieldNum = pTDict->uiBadField; pDb->Diag.uiInfoFlags |= FLM_DIAG_FIELD_NUM; } break; } // Position to the next record - SUCCESS or FERR_BT_END_OF_DATA rc = FSNextRecord( pDb, pLFile, stack); } while( RC_OK(rc)); rc = (rc == FERR_BT_END_OF_DATA) ? FERR_OK : rc; Exit: if( pRecord) { pRecord->Release(); } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return( rc ); } /**************************************************************************** Desc: Add the dictionary index to pTDict. ****************************************************************************/ RCODE fdictAddDictIndex( TDICT * pTDict) { RCODE rc; DDENTRY * pDDEntry; TIXD * pTIxd; TIFD * pTIfd; TIFP * pTIfp; if( RC_BAD( rc = DDAllocEntry( pTDict, NULL, FLM_DICT_INDEX, &pDDEntry))) { goto Exit; } pDDEntry->uiType = ITT_INDEX_TYPE; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIXD), (void **)&pTIxd))) { goto Exit; } pTDict->uiNewIxds++; pDDEntry->vpDef = (void *) pTIxd; pTIxd->uiFlags = IXD_UNIQUE; pTIxd->uiContainerNum = FLM_DICT_CONTAINER; pTIxd->uiNumFlds = 1; pTIxd->uiLanguage = pTDict->uiDefaultLanguage; pTIxd->uiEncId = 0; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFD), (void **)&pTIfd))) { goto Exit; } pTIxd->pNextTIfd = pTIfd; pTDict->uiNewIfds++; pTIfd->pTIfp = NULL; pTIfd->pNextTIfd = NULL; pTIfd->uiFlags = (FLMUINT)(IFD_FIELD | FLM_TEXT_TYPE); pTIfd->uiNextFixupPos = 0; pTIfd->uiLimit = IFD_DEFAULT_LIMIT; pTIfd->uiCompoundPos = 0; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp))) { goto Exit; } pTDict->uiNewFldPaths += 2; pTIfd->pTIfp = pTIfp; pTIfp->pNextTIfp = NULL; pTIfp->bFieldInThisDict = FALSE; pTIfp->uiFldNum = FLM_NAME_TAG; Exit: return( rc); } /**************************************************************************** Desc: Process a single data dictionary record. Parse the record for syntax errors depending on flag value. Only supports adding new stuff to pTDict. ****************************************************************************/ RCODE fdictProcessRec( TDICT * pTDict, FlmRecord * pRecord, FLMUINT uiDictRecNum) { RCODE rc = FERR_OK; DDENTRY * pDDEntry; void * pvField = pRecord->root(); // Ignore items with root nodes that are in the unregistered range. if( pRecord->getFieldID( pvField) >= FLM_UNREGISTERED_TAGS) { goto Exit; } // Parse only on modify or add. switch( pRecord->getFieldID( pvField)) { case FLM_FIELD_TAG: { if( RC_BAD( rc = DDAllocEntry( pTDict, pRecord, uiDictRecNum, &pDDEntry))) { goto Exit; } pDDEntry->uiType = 0; // Type of zero means field. if( RC_BAD( rc = DDFieldParse( pTDict, pDDEntry, pRecord, uiDictRecNum))) { goto Exit; } break; } case FLM_INDEX_TAG: { if( RC_BAD( rc = DDAllocEntry( pTDict, pRecord, uiDictRecNum, &pDDEntry))) { goto Exit; } pDDEntry->uiType = ITT_INDEX_TYPE; if( RC_BAD( rc = DDIxParse( pTDict, pDDEntry, pRecord, pvField))) { goto Exit; } pTDict->uiNewIxds++; break; } case FLM_CONTAINER_TAG: { if( RC_BAD( rc = DDAllocEntry( pTDict, pRecord, uiDictRecNum, &pDDEntry))) { goto Exit; } pDDEntry->uiType = ITT_CONTAINER_TYPE; if( RC_BAD( rc = DDContainerParse( pTDict, pDDEntry, pRecord))) { goto Exit; } pTDict->uiTotalLFiles++; break; } case FLM_ENCDEF_TAG: { if( RC_BAD( rc = DDAllocEntry( pTDict, pRecord, uiDictRecNum, &pDDEntry))) { goto Exit; } pDDEntry->uiType = ITT_ENCDEF_TYPE; if (RC_BAD( rc = DDEncDefParse( pTDict, pDDEntry, pRecord))) { goto Exit; } break; } case FLM_AREA_TAG: case FLM_RESERVED_TAG: { break; } default: { // Cannot allow anything else to pass through the dictionary. rc = RC_SET( FERR_SYNTAX); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Allocate, check and add a name to the DDEntry structure. ****************************************************************************/ FSTATIC RCODE DDAllocEntry( TDICT * pTDict, FlmRecord * pRecord, FLMUINT uiDictRecNum, DDENTRY ** ppDDEntryRV) { RCODE rc = FERR_OK; DDENTRY * pNewEntry; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( DDENTRY), (void **)&pNewEntry))) { goto Exit; } pNewEntry->pNextEntry = NULL; pNewEntry->vpDef = NULL; pNewEntry->uiEntryNum = uiDictRecNum; pNewEntry->uiType = 0; // Zero length name NOT allowed for dictionary items. if( pRecord) { if( pRecord->getDataLength( pRecord->root()) == 0) { rc = RC_SET( FERR_SYNTAX); goto Exit; } } if( pTDict->pLastEntry) { pTDict->pLastEntry->pNextEntry = pNewEntry; } else { pTDict->pFirstEntry = pNewEntry; } pTDict->pLastEntry = pNewEntry; *ppDDEntryRV = pNewEntry; Exit: return( rc ); } /**************************************************************************** Desc: Parse field definition ****************************************************************************/ FSTATIC RCODE DDFieldParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord, FLMUINT uiDictRecNum) { RCODE rc = FERR_OK; TFIELD * pTField; void * pvField = NULL; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TFIELD), (void **)&pTField))) { goto Exit; } pTField->uiFldNum = uiDictRecNum; pTField->uiFldInfo = FLM_CONTEXT_TYPE; pDDEntry->vpDef = (void *) pTField; if( (pvField = pRecord->firstChild( pRecord->root())) == NULL) { rc = RC_SET( FERR_SYNTAX); goto Exit; } for( ; pvField; pvField = pRecord->nextSibling( pvField)) { switch( pRecord->getFieldID( pvField)) { case FLM_TYPE_TAG: { rc = DDGetFieldType( pRecord, pvField, &pTField->uiFldInfo); break; } case FLM_STATE_TAG: { rc = DDParseStateOptions( pRecord, pvField, &pTField->uiFldInfo); break; } default: { if( pRecord->getFieldID( pvField) < FLM_UNREGISTERED_TAGS && pRecord->getFieldID( pvField) != FLM_COMMENT_TAG) { rc = RC_SET( FERR_SYNTAX); } break; } } } Exit: if( RC_BAD(rc) && pvField) { pTDict->uiBadField = pRecord->getFieldID( pvField); } return( rc ); } /**************************************************************************** Desc: Returns the fields data type. May be called outside of DDPREP.C ****************************************************************************/ RCODE DDGetFieldType( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo) { RCODE rc = FERR_OK; char szNativeBuf[ FDD_MAX_VALUE_SIZE]; DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL ); // Parse the type keyword - only one type allowed. if (f_strnicmp( szNativeBuf, "text", 4) == 0) { *puiFldInfo = FLM_TEXT_TYPE; } else if (f_strnicmp( szNativeBuf, "numb", 4) == 0) { *puiFldInfo = FLM_NUMBER_TYPE; } else if (f_strnicmp( szNativeBuf, "bina", 4) == 0) { *puiFldInfo = FLM_BINARY_TYPE; } else if (f_strnicmp( szNativeBuf, "cont", 4) == 0) { *puiFldInfo = FLM_CONTEXT_TYPE; } else if (f_strnicmp( szNativeBuf, "blob", 4) == 0) { *puiFldInfo = FLM_BLOB_TYPE; } else { rc = RC_SET( FERR_SYNTAX); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Parses the 'state' option that is found within the 'field' dictionary definition. Format: state [checking | unused | purge | active] ****************************************************************************/ FSTATIC RCODE DDParseStateOptions( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo) { RCODE rc = FERR_OK; char szNativeBuf[ FDD_MAX_VALUE_SIZE]; DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL); // Parse the 'state' keyword - only one type allowed if( f_strnicmp( szNativeBuf, "chec", 4) == 0) { // 0xFFCF is used to clear out any existing field 'state' value *puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_CHECKING); } else if( f_strnicmp( szNativeBuf, "unus", 4) == 0) { *puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_UNUSED); } else if( f_strnicmp( szNativeBuf, "purg", 4) == 0) { *puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_PURGE); } else if( f_strnicmp( szNativeBuf, "acti", 4) == 0) { *puiFldInfo = (FLMUINT)((*puiFldInfo & ~ITT_FLD_STATE_MASK) | ITT_FLD_STATE_ACTIVE); } else { rc = RC_SET( FERR_SYNTAX); } return( rc); } /**************************************************************************** Desc: Get a number reference and set in the (OUT) parameter. ****************************************************************************/ FSTATIC RCODE DDGetReference( FlmRecord * pRecord, void * pvField, const char * pszBuffer, FLMUINT * puiIdRef) { RCODE rc = FERR_OK; *puiIdRef = 0; if( pszBuffer) { if( !(*pszBuffer)) { rc = RC_SET( FERR_SYNTAX); goto Exit; } *puiIdRef = f_atoud( pszBuffer); } else { if( RC_BAD( rc = pRecord->getUINT( pvField, puiIdRef))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Returns the encryption type. May be called outside of DDPREP.C ****************************************************************************/ RCODE DDGetEncType( FlmRecord * pRecord, void * pvField, FLMUINT * puiFldInfo) { RCODE rc = FERR_OK; FLMUINT uiType; char szNativeBuf[ FDD_MAX_VALUE_SIZE]; DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, NULL ); // Parse the type keyword - only one type allowed. for( uiType = 0; uiType < MAX_ENC_TYPES ; uiType++) { if( f_strnicmp( szNativeBuf, DDEncOpts[ uiType], f_strlen(DDEncOpts[ uiType])) == 0) { *puiFldInfo = uiType; goto Exit; } } rc = RC_SET( FERR_SYNTAX); Exit: return( rc); } /**************************************************************************** Desc: Returns the binary key info. May be called outside of DDPREP.C ****************************************************************************/ FSTATIC RCODE DDGetEncKey( TDICT * pTDict, FlmRecord * pRecord, void * pvField, TENCDEF * pTEncDef) { RCODE rc = FERR_OK; char * pucBuffer = NULL; FLMUINT uiLength; pTEncDef->uiLength = 0; if (RC_BAD( rc = pRecord->getNativeLength( pvField, &uiLength))) { goto Exit; } uiLength++; if( RC_BAD( rc = pTDict->pool.poolAlloc( uiLength, (void **)&pucBuffer))) { goto Exit; } if (RC_BAD( rc = pRecord->getNative( pvField, pucBuffer, &uiLength))) { goto Exit; } pTEncDef->uiLength = uiLength; pTEncDef->pucKeyInfo = (FLMBYTE *)pucBuffer; Exit: return( rc); } /**************************************************************************** Desc: Parse an data dictionary index definition for correct syntax & assign the correct attributes. Build the pcode buffer for the index. Return: RCODE - SUCCESS or FERR_SYNTAX Format: 0 index # FLM_INDEX_TAG [ 1 area [ 0 | ]] # FLM_AREA_TAG - QF files area, 0 = "same as DB" [ 1 container {DEFAULT | }] # FLM_CONTAINER_TAG - indexes span only one container [ 1 count [ KEYS &| REFS]] # FLM_COUNT_TAG - key count of keys and/or refs [ 1 language {US | }] # FLM_LANGUAGE_TAG - for full-text parsing and/or sorting [ 1 positioning] # FLM_POSITIONING_TAG - full reference counts at all b-tree elements [ 1 encdef ] # FLM_ENCDEF_TAG - identify the encryption definition to use 1 key [EACHWORD] # FLM_KEY_TAG - 'use' defaults based on type [ 2 base ] # FLM_BASE_TAG - base rec/field for fields below [ 2 combinations # FLM_COMBINATIONS_TAG - how to handle repeating fields { ALL | NORMALIZED}] [ 2 post] # FLM_POST_TAG - case-flags post-pended to key [ 2 required*] # FLM_REQUIRED_TAG - key value is required [ 2 unique] # FLM_UNIQUE_TAG - key has only 1 reference { 2 }... # FLM_FIELD_TAG - compound key if 2 or more [ 3 case mixed | upper] # FLM_CASE_TAG - text-only, define chars case [ 3 ]... # FLM_FIELD_TAG - alternate field(s) [ 3 paired] # FLM_PAIRED_TAG - add field ID to key [ 3 optional* # FLM_OPTIONAL_TAG - component's value is optional | 3 required] # FLM_REQUIRED_TAG - component's value is required [ 3 use eachword|value|field|minspaces|nounderscore|nospace|nodash] # FLM_USE_TAG == n field # path identifies field -- maybe "based" [ m type ] # FLM_TYPE_TAG - only for ixing unregistered fields Please Note: This code only supports the minimal old 11 index format needed for skads databases. ****************************************************************************/ FSTATIC RCODE DDIxParse( TDICT * pTDict, DDENTRY * pDDEntry, // Points to defined entry. FlmRecord * pRecord, // Index definition record. void * pvField) { RCODE rc = FERR_OK; FLMUINT uiIfdFlags; FLMUINT uiTempIfdFlags; FLMUINT uiBaseNum; FLMUINT uiNLen; TIXD * pTIxd; TIFD * pLastTIfd; TIFD * pTIfd; void * pvTempField = NULL; void * pvIfdField = NULL; char szNativeBuf[ 64]; FLMUINT uiCompoundPos; FLMUINT uiTemp; FLMBOOL bHasRequiredTag = TRUE; FLMBOOL bOld11Mode = FALSE; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIXD), (void **)&pTIxd))) { goto Exit; } pTIxd->pNextTIfd = NULL; pTIxd->uiFlags = 0; pTIxd->uiContainerNum = FLM_DATA_CONTAINER; pTIxd->uiNumFlds = 0; pTIxd->uiLanguage = pTDict->uiDefaultLanguage; pTIxd->uiEncId = 0; if( (pvField = pRecord->firstChild( pRecord->root())) == NULL) { rc = RC_SET( FERR_SYNTAX); goto Exit; } pLastTIfd = NULL; for( ; pvField; pvField = pRecord->nextSibling( pvField)) { switch( pRecord->getFieldID( pvField)) { case FLM_CONTAINER_TAG: { char szTmpBuf [50]; FLMUINT uiLen = sizeof( szTmpBuf); // See if a special keyword is used - ALL or * if ((pRecord->getDataType( pvField) == FLM_TEXT_TYPE) && (RC_OK( pRecord->getNative( pvField, szTmpBuf, &uiLen))) && (f_stricmp( "ALL", szTmpBuf) == 0 || f_stricmp( "*", szTmpBuf) == 0)) { if (pTDict->pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_50) { rc = RC_SET( FERR_UNSUPPORTED_FEATURE); goto Exit; } // Zero will mean all containers pTIxd->uiContainerNum = 0; } else { if( RC_BAD( rc = DDGetReference( pRecord, pvField, NULL, &pTIxd->uiContainerNum))) { goto Exit; } if( pTIxd->uiContainerNum == 0) { pTIxd->uiContainerNum = FLM_DATA_CONTAINER; } } break; } case FLM_COUNT_TAG: { pTIxd->uiFlags |= IXD_COUNT; break; } case FLM_LANGUAGE_TAG: { uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvField, szNativeBuf, &uiNLen); pTIxd->uiLanguage = f_languageToNum( szNativeBuf); break; } case FLM_ENCDEF_TAG: { uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvField, szNativeBuf, &uiNLen); pTIxd->uiEncId = f_atoud( szNativeBuf); flmAssert( pTIxd->uiEncId); break; } case FLM_TYPE_TAG: { bOld11Mode = TRUE; break; } case FLM_POSITIONING_TAG: { if (pTDict->pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) { pTIxd->uiFlags |= IXD_POSITIONING; } else { rc = RC_SET( FERR_SYNTAX); goto Exit; } break; } case FLM_FIELD_TAG: { uiCompoundPos = 0; uiBaseNum = 0; uiIfdFlags = IFD_FIELD; bHasRequiredTag = TRUE; pvTempField = pvField; bOld11Mode = TRUE; goto Parse_Fields; } case FLM_KEY_TAG: { uiCompoundPos = 0; uiBaseNum = 0; uiIfdFlags = IFD_FIELD | IFD_OPTIONAL; bHasRequiredTag = FALSE; uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvField, szNativeBuf, &uiNLen); if( f_strnicmp( szNativeBuf, "EACH", 4) == 0) { pTIxd->uiFlags |= IXD_EACHWORD; uiIfdFlags = IFD_EACHWORD | IFD_OPTIONAL; } if( (pvTempField = pRecord->firstChild( pvField)) == NULL) { rc = RC_SET( FERR_SYNTAX); goto Exit; } Parse_Fields: for( ; pvTempField; pvTempField = pRecord->nextSibling( pvTempField)) { switch( pRecord->getFieldID( pvTempField)) { case FLM_BASE_TAG: { if( RC_BAD( rc = DDGetReference( pRecord, pvTempField, NULL, &uiBaseNum))) { goto Exit; } break; } case FLM_COMBINATIONS_TAG: { rc = RC_SET( FERR_SYNTAX); goto Exit; } case FLM_POST_TAG: { pTIxd->uiFlags |= IXD_HAS_POST; uiIfdFlags |= IFD_POST; break; } case FLM_REQUIRED_TAG: { break; } case FLM_OPTIONAL_TAG: { rc = RC_SET( FERR_SYNTAX); goto Exit; } case FLM_UNIQUE_TAG: { pTIxd->uiFlags |= IXD_UNIQUE; uiIfdFlags |= IFD_UNIQUE_PIECE; break; } case FLM_FIELD_TAG: { pTIxd->uiNumFlds++; if( bOld11Mode) { pvField = pvTempField; } // Need to set IFD_COMPOUND if there is more than one field. if( pTIxd->uiNumFlds == 1 && (pRecord->find( pvTempField, FLM_FIELD_TAG, 2) != NULL)) { uiIfdFlags |= IFD_COMPOUND; } pTIfd = pLastTIfd; if( RC_BAD(rc = DDBuildFldPath( pTDict, &pLastTIfd, pRecord, pvTempField, uiBaseNum))) { goto Exit; } pLastTIfd->uiCompoundPos = uiCompoundPos++; if( !pTIfd) { pTIxd->pNextTIfd = pLastTIfd; } else { pTIfd->pNextTIfd = pLastTIfd; } uiTempIfdFlags = uiIfdFlags; if( bOld11Mode) { uiTempIfdFlags &= ~IFD_OPTIONAL; uiTempIfdFlags |= (IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET); } for( pvIfdField = pRecord->firstChild( pvTempField); pvIfdField; pvIfdField = pRecord->nextSibling( pvIfdField)) { switch ( pRecord->getFieldID( pvIfdField)) { case FLM_CASE_TAG: { uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen); if( f_strnicmp( szNativeBuf, "UPPE", 4) == 0) { uiTempIfdFlags |= IFD_UPPER; } break; } case FLM_FIELD_TAG: { break; } case FLM_OPTIONAL_TAG: { if( bOld11Mode) { // Old 11 format - default for each field is required. uiTempIfdFlags |= IFD_OPTIONAL; uiTempIfdFlags &= ~(IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET); } // New format default is optional break; } case FLM_PAIRED_TAG: { uiTempIfdFlags |= IFD_FIELDID_PAIR; break; } case FLM_POST_TAG: { uiTempIfdFlags |= IFD_POST; break; } case FLM_REQUIRED_TAG: { bHasRequiredTag = TRUE; uiTempIfdFlags &= ~IFD_OPTIONAL; uiTempIfdFlags |= (IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET); break; } case FLM_LIMIT_TAG: { if( RC_BAD( pRecord->getUINT( pvIfdField, &uiTemp)) || uiTemp > IFD_DEFAULT_LIMIT) { pLastTIfd->uiLimit = IFD_DEFAULT_LIMIT; } else { pLastTIfd->uiLimit = uiTemp; } break; } case FLM_UNIQUE_TAG: { uiTempIfdFlags |= IFD_UNIQUE_PIECE; pTIxd->uiFlags |= IXD_UNIQUE; break; } case FLM_USE_TAG: { uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen); if( f_strnicmp( szNativeBuf, "EACH", 4) == 0) { uiTempIfdFlags |= IFD_EACHWORD; uiTempIfdFlags &= ~(IFD_VALUE|IFD_SUBSTRING); } else if( f_strnicmp( szNativeBuf, "SUBS", 4) == 0) { pTIxd->uiFlags |= IXD_HAS_SUBSTRING; uiTempIfdFlags |= IFD_SUBSTRING; uiTempIfdFlags &= ~(IFD_VALUE|IFD_EACHWORD); if( pLastTIfd->uiLimit == IFD_DEFAULT_LIMIT) { pLastTIfd->uiLimit = IFD_DEFAULT_SUBSTRING_LIMIT; } } else if( f_strnicmp( szNativeBuf, "VALU", 4) == 0) { uiTempIfdFlags |= IFD_VALUE; uiTempIfdFlags &= ~(IFD_EACHWORD|IFD_SUBSTRING); } else if( f_strnicmp( szNativeBuf, "FIEL", 4) == 0) { uiTempIfdFlags |= IFD_CONTEXT; uiTempIfdFlags &= ~(IFD_VALUE|IFD_EACHWORD|IFD_SUBSTRING); } break; } case FLM_FILTER_TAG: { uiNLen = sizeof( szNativeBuf); (void) pRecord->getNative( pvIfdField, szNativeBuf, &uiNLen); if( f_strnicmp( szNativeBuf, "MINS", 4) == 0) { uiTempIfdFlags |= IFD_MIN_SPACES; } else if( f_strnicmp( szNativeBuf, "NOUN", 4) == 0) { uiTempIfdFlags |= IFD_NO_UNDERSCORE; } else if( f_strnicmp( szNativeBuf, "NOSP", 4) == 0) { uiTempIfdFlags |= IFD_NO_SPACE; } else if( f_strnicmp( szNativeBuf, "NODA", 4) == 0) { uiTempIfdFlags |= IFD_NO_DASH; } else { rc = RC_SET( FERR_SYNTAX); goto Exit; } break; } default: { if( pRecord->getFieldID( pvIfdField) < FLM_UNREGISTERED_TAGS && pRecord->getFieldID( pvIfdField) != FLM_COMMENT_TAG) { rc = RC_SET( FERR_SYNTAX); goto Exit; } break; } } } // Parse again the level 3 field definitions. Now we // have the IFD uiFlags value to assign each piece that // will have the same compound position. pLastTIfd->uiFlags |= uiTempIfdFlags; for( pvIfdField = pRecord->firstChild( pvTempField); pvIfdField; pvIfdField = pRecord->nextSibling( pvIfdField)) { if( pRecord->getFieldID( pvIfdField) == FLM_FIELD_TAG ) { rc = RC_SET( FERR_SYNTAX); goto Exit; } } break; } default: { if( bOld11Mode) { break; } if( pRecord->getFieldID( pvTempField) < FLM_UNREGISTERED_TAGS && pRecord->getFieldID( pvTempField) != FLM_COMMENT_TAG) { rc = RC_SET( FERR_SYNTAX); goto Exit; } break; } } } // Special case for optional if( !bHasRequiredTag) { // Set all of the IFD flags to IFD_REQUIRED_IN_SET for( pTIfd = pTIxd->pNextTIfd; pTIfd; pTIfd = pTIfd->pNextTIfd) { pTIfd->uiFlags |= IFD_REQUIRED_IN_SET; } } break; } default: { if( pRecord->getFieldID( pvField) < FLM_UNREGISTERED_TAGS && pRecord->getFieldID( pvField) != FLM_COMMENT_TAG) { rc = RC_SET( FERR_SYNTAX); goto Exit; } break; } } } pDDEntry->vpDef = (void *) pTIxd; Exit: if( RC_BAD(rc)) { if( pvIfdField) { pTDict->uiBadField = pRecord->getFieldID( pvIfdField); } else if( pvTempField) { pTDict->uiBadField = pRecord->getFieldID( pvTempField); } else if( pvField) { pTDict->uiBadField = pRecord->getFieldID( pvField); } } else { pTDict->uiNewIxds++; pTDict->uiNewIfds += pTIxd->uiNumFlds; pTDict->uiNewLFiles++; } return( rc); } /**************************************************************************** Desc: Build field path for each index field. This function will also check for the existence of the 'batch' option for QF indexes. ****************************************************************************/ FSTATIC RCODE DDBuildFldPath( TDICT * pTDict, TIFD ** ppTIfd, FlmRecord * pRecord, void * pvField, FLMUINT uiBaseNum) { RCODE rc = FERR_OK; TIFD * pTIfd; TIFP * pLastFldPath; TIFP * pTIfp; FLMUINT uiNumInFldPath; char szNameBuf[ 32]; char * pszCurrent; char szNativeBuf[ FDD_MAX_VALUE_SIZE]; FLMUINT uiBufLen; FLMUINT uiPos; pTDict->uiTotalIfds++; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFD), (void **)&pTIfd))) { goto Exit; } pTIfd->pTIfp = NULL; pTIfd->pNextTIfd = NULL; pTIfd->uiFlags = 0; pTIfd->uiNextFixupPos = 0; pTIfd->uiLimit = IFD_DEFAULT_LIMIT; pTIfd->uiCompoundPos = 0; pLastFldPath = NULL; *ppTIfd = pTIfd; // Build the field paths DDTextToNative( pRecord, pvField, szNativeBuf, FDD_MAX_VALUE_SIZE, &uiBufLen); pszCurrent = szNativeBuf; uiNumInFldPath = uiPos = 0; if( uiBaseNum) { uiNumInFldPath++; if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp))) { goto Exit; } pTIfp->pNextTIfp = NULL; pTIfp->bFieldInThisDict = FALSE; pTIfp->uiFldNum = uiBaseNum; pTIfd->pTIfp = pTIfp; pLastFldPath = pTIfp; } while( uiPos < uiBufLen) { uiNumInFldPath++; if( DDMoveWord( szNameBuf, pszCurrent, sizeof( szNameBuf ), &uiPos ) == FALSE ) { break; } if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TIFP), (void **)&pTIfp))) { goto Exit; } pTIfp->pNextTIfp = NULL; pTIfp->bFieldInThisDict = FALSE; if( pTIfd->pTIfp == NULL) { pTIfd->pTIfp = pTIfp; } else { pLastFldPath->pNextTIfp = pTIfp; } pLastFldPath = pTIfp; // See if there is a wildcard in the path. if (f_stricmp( szNameBuf, "*") == 0) { if (pTDict->pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_50) { rc = RC_SET( FERR_UNSUPPORTED_FEATURE); goto Exit; } else { pTIfp->uiFldNum = FLM_ANY_FIELD; } } else { if( RC_BAD( rc = DDGetReference( NULL, NULL, szNameBuf, &pTIfp->uiFldNum))) { goto Exit; } } } if( uiNumInFldPath == 0) { rc = RC_SET( FERR_SYNTAX); goto Exit; } // Cannot have wildcard in last field of field path. if (pLastFldPath->uiFldNum == FLM_ANY_FIELD) { rc = RC_SET( FERR_SYNTAX); goto Exit; } // Single field has the field NULL terminated if( uiNumInFldPath == 1 ) { pTDict->uiNewFldPaths += 2; } else { // The field paths are stored child to parent and parent to child // each are zero terminated. pTDict->uiNewFldPaths += 2 * (uiNumInFldPath + 1); } Exit: return( rc); } /**************************************************************************** Desc: Parse a data dictionary domain definition for correct syntax & assign the correct attributes. ****************************************************************************/ FSTATIC RCODE DDContainerParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord) { RCODE rc = FERR_OK; void * pvField = NULL; if( pDDEntry) { if( (pvField = pRecord->firstChild( pRecord->root())) != NULL) { for( ; pvField; pvField = pRecord->nextSibling( pvField)) { // Only option is unregistered fields if( pRecord->getFieldID( pvField) < FLM_FREE_TAG_NUMS) { rc = RC_SET( FERR_SYNTAX); goto Exit; } } } } Exit: if( RC_BAD(rc) && pvField) { pTDict->uiBadField = pRecord->getFieldID( pvField); } return( rc ); } /**************************************************************************** Desc: Parse a data dictionary domain definition for correct syntax & assign the correct attributes. ****************************************************************************/ FSTATIC RCODE DDEncDefParse( TDICT * pTDict, DDENTRY * pDDEntry, FlmRecord * pRecord) { RCODE rc = FERR_OK; void * pvField = NULL; TENCDEF * pTEncDef; // Make sure the version of the database is correct for encryption. if (pTDict->pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) { rc = RC_SET( FERR_UNSUPPORTED_FEATURE); goto Exit; } if( RC_BAD( rc = pTDict->pool.poolAlloc( sizeof( TENCDEF), (void **)&pTEncDef))) { goto Exit; } pTEncDef->uiAlgType = 0; pTEncDef->uiState = 0; pTEncDef->pucKeyInfo = NULL; pTEncDef->uiLength = 0; if( pDDEntry) { if( (pvField = pRecord->firstChild( pRecord->root())) != NULL) { for( ; pvField; pvField = pRecord->nextSibling( pvField)) { switch ( pRecord->getFieldID( pvField) ) { case FLM_TYPE_TAG: { // Get the encryption type. if (RC_BAD( rc = DDGetEncType( pRecord, pvField, &pTEncDef->uiAlgType))) { goto Exit; } break; } case FLM_KEY_TAG: { // Get the key information. if (RC_BAD( rc = DDGetEncKey( pTDict, pRecord, pvField, pTEncDef))) { goto Exit; } break; } case FLM_STATE_TAG: { // Get the status information. if (RC_BAD( rc = DDParseStateOptions( pRecord, pvField, &pTEncDef->uiState))) { goto Exit; } break; } default: { rc = RC_SET( FERR_SYNTAX); goto Exit; } } } pDDEntry->vpDef = (void *)pTEncDef; } else { rc = RC_SET( FERR_SYNTAX); goto Exit; } } Exit: if (RC_BAD( rc) && pvField) { pTDict->uiBadField = pRecord->getFieldID( pvField); } return( rc ); } /**************************************************************************** Desc: Move word delimited by spaces from src to dest. Used to move a word at a time for field path lists. Notes: Isolated so changes can be made to delemeting NAMES. Visit: Still bugs when name > buffer size - won't happen because only #'s ****************************************************************************/ FSTATIC FLMBOOL DDMoveWord( char * pucDest, char * pucSrc, FLMUINT uiMaxDestLen, FLMUINT * puiPos) { FLMBOOL bFoundWord = TRUE; FLMUINT uiPos = *puiPos; char * pMatch; FLMUINT uiBytesToCopy; pucSrc += uiPos; while( *pucSrc == NATIVE_SPACE) { pucSrc++; } pMatch = pucSrc; while( *pMatch > NATIVE_SPACE) { pMatch++; } if( !*pMatch) { if( *pucSrc == '\0') { bFoundWord = FALSE; goto Exit; } uiBytesToCopy = f_strlen( pucSrc); if( uiBytesToCopy + 1 > uiMaxDestLen) { uiBytesToCopy = uiMaxDestLen - 1; } f_memcpy( pucDest, pucSrc, uiBytesToCopy + 1); *puiPos = uiPos + uiBytesToCopy + 1; } else { // Copy the bytes between pucSrc and pMatch minus one uiBytesToCopy = (FLMUINT) (pMatch - pucSrc); if( uiBytesToCopy + 1 > uiMaxDestLen) { uiBytesToCopy = uiMaxDestLen - 1; } f_memcpy( pucDest, pucSrc, uiBytesToCopy ); pucDest[ uiBytesToCopy ] = '\0'; // Go past consuctive spaces while( pucSrc[ ++uiBytesToCopy ] == NATIVE_SPACE) { uiBytesToCopy++; } *puiPos = uiPos + uiBytesToCopy; } Exit: return( bFoundWord); } /**************************************************************************** Desc: Normalizes an internal string with possible formatting codes into a NATIVE string. Drops all formatting codes and extended chars. ****************************************************************************/ FSTATIC void DDTextToNative( FlmRecord * pRecord, void * pvField, char * pszBuffer, FLMUINT uiBufLen, FLMUINT * puiBufLen) { RCODE rc = FERR_OK; pszBuffer[ 0] = 0; if( pRecord->getDataLength( pvField)) { if( RC_BAD( rc = pRecord->getNative( pvField, pszBuffer, &uiBufLen))) { if( rc != FERR_CONV_DEST_OVERFLOW) { pszBuffer[0] = 0; uiBufLen = 0; } } } else { uiBufLen = 0; } if( puiBufLen) { // Length needs to include the null byte *puiBufLen = uiBufLen + 1; } return; } /**************************************************************************** Desc: Allocate the LFILE and read in the LFile entries. The default data container and the dictionary container will be at hard coded slots at the first of the table. The LFiles do not need to be in any numeric order. ****************************************************************************/ RCODE fdictReadLFiles( FDB * pDb, FDICT * pDict) { RCODE rc = FERR_OK; LFILE * pLFiles = NULL; LFILE * pLFile; SCACHE * pSCache = NULL; FLMBOOL bReleaseCache = FALSE; FLMBYTE * pucBlk; FLMUINT uiBlkAddress; FLMUINT uiPos; FLMUINT uiEndPos; FLMUINT uiEstCount; FLMUINT uiLFileCnt; FLMUINT uiLFHCnt; FFILE * pFile = pDb->pFile; FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize; LFILE TmpLFile; f_memset( &TmpLFile, 0, sizeof( LFILE)); for( uiEstCount = 0, uiLFileCnt = 4, uiBlkAddress = pDb->pFile->FileHdr.uiFirstLFHBlkAddr; uiBlkAddress != BT_END; ) { if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LFH_BLK, uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; pucBlk = pSCache->pucBlk; uiPos = BH_OVHD; if( (uiEndPos = (FLMUINT)FB2UW( &pucBlk[ BH_ELM_END])) <= BH_OVHD) { uiEndPos = BH_OVHD; uiLFHCnt = 0; } else { if( uiEndPos > uiBlkSize) { uiEndPos = uiBlkSize; } uiLFHCnt = (FLMUINT)((uiEndPos - BH_OVHD) / LFH_SIZE); uiEndPos = (FLMUINT)(BH_OVHD + uiLFHCnt * LFH_SIZE); } // May allocate too many like the inactive ones but OK for now. // Allocate an additional 2 for the default data and dict containers. if( !uiEstCount) { uiEstCount = uiLFHCnt + uiLFileCnt; if( uiEstCount) { if( RC_BAD( rc = f_calloc( uiEstCount * sizeof( LFILE), &pLFiles))) { goto Exit; } } } else if( uiLFHCnt) { uiEstCount += uiLFHCnt; if( RC_BAD(rc = f_recalloc( uiEstCount * sizeof(LFILE), &pLFiles))) { goto Exit; } } // Read through all of the logical file definitions in the block for( ; uiPos < uiEndPos; uiPos += LFH_SIZE) { FLMUINT uiLfNum; // Have to fix up the offsets later when they are read in if( RC_BAD( rc = flmBufferToLFile( &pucBlk[ uiPos], &TmpLFile, uiBlkAddress, uiPos))) { goto Exit; } if( TmpLFile.uiLfType == LF_INVALID) { continue; } uiLfNum = TmpLFile.uiLfNum; if( uiLfNum == FLM_DATA_CONTAINER) { pLFile = pLFiles + LFILE_DATA_CONTAINER_OFFSET; } else if( uiLfNum == FLM_DICT_CONTAINER) { pLFile = pLFiles + LFILE_DICT_CONTAINER_OFFSET; } else if( uiLfNum == FLM_DICT_INDEX) { pLFile = pLFiles + LFILE_DICT_INDEX_OFFSET; } else if( uiLfNum == FLM_TRACKER_CONTAINER) { pLFile = pLFiles + LFILE_TRACKER_CONTAINER_OFFSET; } else { pLFile = pLFiles + uiLFileCnt++; } f_memcpy( pLFile, &TmpLFile, sizeof(LFILE)); } // Get the next block in the chain uiBlkAddress = (FLMUINT)FB2UD( &pucBlk[ BH_NEXT_BLK]); ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } // This routine could be called to re-read in the dictionary. if( pDict->pLFileTbl) { f_free( &pDict->pLFileTbl); } pDict->pLFileTbl = pLFiles; pDict->uiLFileCnt = uiLFileCnt; Exit: if( bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if( RC_BAD(rc) && pLFiles) { f_free( &pLFiles); } return( rc); } /**************************************************************************** Desc: Add data dictionary records to the data dictionary. ****************************************************************************/ RCODE fdictCreate( FDB * pDb, const char * pszDictPath, const char * pDictBuf) { RCODE rc = FERR_OK; IF_FileHdl * pDictFileHdl = NULL; FlmRecord * pDictRec = NULL; void * pvField; const char * pucGedBuf; LFILE * pDictContLFile; LFILE * pDictIxLFile; FLMUINT uiDrn = 0; FLMUINT uiCurrDictNum; FLMUINT uiLFileCount; LFILE DictContLFile; LFILE DictIxLFile; LFILE TempLFile; char ucTempBuf[ 256]; FLMUINT uiBufLen = sizeof( ucTempBuf); F_NameTable nameTable; // Initialize the name table if( RC_BAD( rc = nameTable.setupFromDb( HFDB_NULL))) { goto Exit; } // Create Dictionary and Default Data containers if( RC_BAD(rc = flmLFileCreate( pDb, &DictContLFile, FLM_DICT_CONTAINER, LF_CONTAINER))) { goto Exit; } uiCurrDictNum = FLM_DICT_CONTAINER; if( RC_BAD(rc = flmLFileCreate( pDb, &TempLFile, FLM_DATA_CONTAINER, LF_CONTAINER))) { goto Exit; } if( RC_BAD( rc = flmLFileCreate( pDb, &DictIxLFile, FLM_DICT_INDEX, LF_INDEX))) { goto Exit; } if( RC_BAD( rc = flmLFileCreate( pDb, &TempLFile, FLM_TRACKER_CONTAINER, LF_CONTAINER))) { goto Exit; } uiLFileCount = 4; // If we have a GEDCOM buffer, there is no need to open the file if( pDictBuf) { pucGedBuf = pDictBuf; uiBufLen = f_strlen( pDictBuf) + 1; } else if( pszDictPath) { pucGedBuf = ucTempBuf; if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszDictPath, FLM_IO_RDONLY, &pDictFileHdl))) { goto Exit; } } else { // Neither a dictionary buffer or file were specified. Create will // be done with an empty dictionary. goto Done_Getting_Dict; } // Create a new FDICT so we can write the dictionary records. // This FDICT is temporary and will be allocated again. if( RC_BAD( rc = fdictCreateNewDict( pDb))) { goto Exit; } if( (pDictRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, &pDictContLFile))) { goto Exit; } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, FLM_DICT_INDEX, &pDictIxLFile, NULL))) { goto Exit; } // Read through the dictionary records, adding them or creating dictionaries // as we go. for( ;;) { // Get records from buffer or file rc = ( pDictFileHdl) ? pDictRec->importRecord( pDictFileHdl, &nameTable) : pDictRec->importRecord( &pucGedBuf, uiBufLen, &nameTable); if( RC_BAD( rc)) { if( rc == FERR_END || rc == FERR_EOF_HIT) { rc = FERR_OK; break; } else if( uiDrn) { // If an error occur then at least set the DRN of the // previous record in the diagnostic information. pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN; pDb->Diag.uiDrn = uiDrn; } goto Exit; } // See if we are switching dictionaries. pvField = pDictRec->root(); if( pDictRec->getFieldID( pvField) == FLM_DICT_TAG) { rc = RC_SET( FERR_INVALID_TAG); goto Exit; } // Assign all fields a DRN value - parse for completeness. // If there is no DRN in the record (zero), one will be assigned // by FDDDictRecUpdate. uiDrn = pDictRec->getID(); // Add the data dictionary record. This also checks to see // if the record is already defined. if( RC_BAD( rc = fdictRecUpdate( pDb, pDictContLFile, pDictIxLFile, &uiDrn, pDictRec, NULL))) { goto Exit; } // Don't need to do the processing below if it is not a record // being put into the dictionary. if( uiCurrDictNum != FLM_DICT_CONTAINER) { continue; } // Create an LFILE for each index and container. if( pDictRec->getFieldID( pvField) == FLM_INDEX_TAG || pDictRec->getFieldID( pvField) == FLM_CONTAINER_TAG) { pvField = pDictRec->root(); if( RC_BAD( rc = flmLFileCreate( pDb, &TempLFile, uiDrn, ((pDictRec->getFieldID( pvField) == FLM_INDEX_TAG) ? (FLMUINT)LF_INDEX : (FLMUINT)LF_CONTAINER)))) { goto Exit; } uiLFileCount++; } } Done_Getting_Dict: // Create the FDICT again, this time with the dictionary pcode. if( RC_BAD( rc = fdictCreateNewDict( pDb))) { goto Exit; } Exit: if( pDictFileHdl) { pDictFileHdl->Release(); } if( pDictRec) { pDictRec->Release(); } return( rc); } /**************************************************************************** Desc: Creates a new dictionary for a database. This occurs when on database create and on a dictionary change. ****************************************************************************/ RCODE fdictCreateNewDict( FDB * pDb) { RCODE rc = FERR_OK; // Unlink the DB from the current FDICT, if any. if( pDb->pDict) { f_mutexLock( gv_FlmSysData.hShareMutex); flmUnlinkFdbFromDict( pDb); f_mutexUnlock( gv_FlmSysData.hShareMutex); } // Allocate a new FDICT structure for the new dictionary we // are going to create. if( RC_BAD( rc = fdictRebuild( pDb))) { goto Exit; } // Update the FDB structure to indicate that the dictionary // was updated. pDb->uiFlags |= FDB_UPDATED_DICTIONARY; Exit: // If we allocated an FDICT and there was an error, free the FDICT. if( (RC_BAD( rc)) && (pDb->pDict)) { flmFreeDict( pDb->pDict); pDb->pDict = NULL; } return( rc); } /**************************************************************************** Desc: Add a new field, container or index definition to the dictionary. ****************************************************************************/ RCODE flmAddRecordToDict( FDB * pDb, FlmRecord * pRecord, FLMUINT uiDictId, FLMBOOL bRereadLFiles) { RCODE rc = FERR_OK; TDICT tDict; FLMBOOL bTDictInitialized = FALSE; if( RC_BAD( rc = fdictCopySkeletonDict( pDb))) { goto Exit; } bTDictInitialized = TRUE; if( RC_BAD( rc = fdictInitTDict( pDb, &tDict))) { goto Exit; } if( RC_BAD( rc = fdictProcessRec( &tDict, pRecord, uiDictId))) { goto Exit; } if( RC_BAD( rc = fdictBuildTables( &tDict, bRereadLFiles, TRUE))) { goto Exit; } pDb->uiFlags |= FDB_UPDATED_DICTIONARY; Exit: if( bTDictInitialized) { tDict.pool.poolFree(); } // If we allocated an FDICT and there was an error, free the FDICT. if( (RC_BAD( rc)) && (pDb->pDict)) { flmFreeDict( pDb->pDict); pDb->pDict = NULL; } return( rc); } /**************************************************************************** Desc: Add an index a dictionary record to the container LFILE and the index LFILE. ****************************************************************************/ RCODE fdictRecUpdate( FDB * pDb, LFILE * pDictContLFile, LFILE * pDictIxLFile, FLMUINT * puiDrnRV, FlmRecord * pNewRec, FlmRecord * pOldRec, FLMBOOL bRebuildOp) { RCODE rc = FERR_OK; FLMUINT uiDrn = *puiDrnRV; FLMBOOL bAllocatedID; void * pvField; FLMBYTE * pucKeyField = NULL; FLMUINT32 ui32BufLen; FLMUINT uiEncType; bAllocatedID = FALSE; // Make sure we are using a valid DRN if( (uiDrn >= FLM_RESERVED_TAG_NUMS) && (uiDrn != 0xFFFFFFFF)) { pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN; pDb->Diag.uiDrn = uiDrn; rc = RC_SET( FERR_BAD_DICT_DRN); goto Exit; } // Allocate an unused DRN, if one has not been allocated. if( (pNewRec) && ((!uiDrn) || (uiDrn == 0xFFFFFFFF))) { FLMBOOL bAllocAtEnd = (!uiDrn) ? TRUE : FALSE; bAllocatedID = TRUE; if( bAllocAtEnd) { if( RC_BAD( rc = FSGetNextDrn( pDb, pDictContLFile, FALSE, &uiDrn))) { goto Exit; } } else { // Scott 12/99: This must not be called any more. // The code merged ITT values into the table. flmAssert(0); } // Verify that we are not at our highest possible dictionary DRN. if( uiDrn >= FLM_RESERVED_TAG_NUMS) { rc = RC_SET( FERR_NO_MORE_DRNS); goto Exit; } } // The following code makes sure that the DRN and name have not already been // used, if adding. It also makes sure that there is no conflict in // the type/name index. It checks the entire shared dictionary // hierarchy if necessary - child and parent - to ensure no // conflicts. if( pNewRec) { // Check for ID conflicts in the dictionary being added to if( (!pOldRec) && (!bAllocatedID)) { if( RC_BAD( rc = DDCheckIDConflict( pDb, pDictContLFile, uiDrn))) { if( (rc == FERR_ID_RESERVED) || (rc == FERR_DUPLICATE_DICT_REC)) { pvField = pNewRec->root(); if( (rc == FERR_DUPLICATE_DICT_REC) && (pNewRec->getFieldID( pvField) == FLM_RESERVED_TAG)) { rc = RC_SET( FERR_CANNOT_RESERVE_ID); } pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN; pDb->Diag.uiDrn = uiDrn; } goto Exit; } } // Check for name conflicts in the dictionary being added to if (pNewRec) { if (RC_BAD( rc = DDCheckNameConflict( pDb, pDictIxLFile, pNewRec, uiDrn, pOldRec))) goto Exit; } } if (!pOldRec && pNewRec) { // If this is an encryption definition record, we need to generate // a new key. if (pNewRec->getFieldID( pNewRec->root()) == FLM_ENCDEF_TAG && !bRebuildOp && !(pDb->uiFlags & FDB_REPLAYING_RFL)) { F_CCS Ccs; // If we are running in limited mode, we will not be able to complete // this operation. if( pDb->pFile->bInLimitedMode) { rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } // Should not have a key yet. if( pNewRec->find(pNewRec->root(), FLM_KEY_TAG) != NULL) { rc = RC_SET( FERR_CANNOT_SET_KEY); goto Exit; } if( (pvField = pNewRec->find( pNewRec->root(), FLM_TYPE_TAG)) == NULL) { rc = RC_SET( FERR_MISSING_ENC_TYPE); goto Exit; } if( RC_BAD( rc = DDGetEncType( pNewRec, pvField, &uiEncType))) { goto Exit; } if( RC_BAD( rc = Ccs.init( FALSE, uiEncType))) { goto Exit; } if( RC_BAD( rc = Ccs.generateEncryptionKey())) { goto Exit; } if( RC_BAD( rc = Ccs.getKeyToStore( &pucKeyField, &ui32BufLen, NULL, pDb->pFile->pDbWrappingKey))) { goto Exit; } // Create the key field if( RC_BAD( rc = pNewRec->insert( pNewRec->root(), INSERT_LAST_CHILD, FLM_KEY_TAG, FLM_TEXT_TYPE, &pvField))) { goto Exit; } // Set the value of the new field if( RC_BAD( rc = pNewRec->setNative( pvField, (const char *)pucKeyField))) { goto Exit; } } } // Delete the old record and its index entries, if any if( pOldRec) { // Delete the old record's index entries if( RC_BAD( rc = DDIxDictRecord( pDb, pDictIxLFile, uiDrn, pOldRec, KREF_DELETE_FLAG))) { goto Exit; } // Delete the old record - unless it is a modify if( !pNewRec) { if( RC_BAD( rc = FSRecUpdate( pDb, pDictContLFile, NULL, uiDrn, REC_UPD_DELETE))) { goto Exit; } } } // Add the new record, if any if( pNewRec) { // Add the record's index keys if( RC_BAD( rc = DDIxDictRecord( pDb, pDictIxLFile, uiDrn, pNewRec, 0))) { goto Exit; } // Add or modify the record itself if( RC_BAD( rc = FSRecUpdate( pDb, pDictContLFile, pNewRec, uiDrn, (FLMUINT)((pOldRec) ? (FLMUINT)REC_UPD_MODIFY : (FLMUINT)REC_UPD_ADD)))) { goto Exit; } } Exit: if( RC_OK( rc)) { *puiDrnRV = uiDrn; } if (pucKeyField) { f_free( &pucKeyField); } return( rc); } /**************************************************************************** Desc: Creates a collated type/name key for a dictionary record. ****************************************************************************/ FSTATIC RCODE DDMakeDictIxKey( FDB * pDb, FlmRecord * pRecord, FLMBYTE * pKeyBuf, FLMUINT * puiKeyLenRV) { RCODE rc = FERR_OK; FLMUINT uiElmLen; FLMUINT uiKeyLen = 0; void * pvField = pRecord->root(); const FLMBYTE * pExportPtr; // Collate the name pExportPtr = pRecord->getDataPtr( pvField), uiElmLen = MAX_KEY_SIZ - uiKeyLen; if( RC_BAD( rc = KYCollateValue( &pKeyBuf [uiKeyLen], &uiElmLen, pExportPtr, pRecord->getDataLength( pvField), FLM_TEXT_TYPE, uiElmLen, NULL, NULL, pDb->pFile->FileHdr.uiDefaultLanguage, FALSE, FALSE, FALSE, NULL))) { goto Exit; } uiKeyLen += uiElmLen; Exit: *puiKeyLenRV = uiKeyLen; return( rc); } /**************************************************************************** Desc: Checks to make sure a dictionary name has not already been used. ****************************************************************************/ FSTATIC RCODE DDCheckNameConflict( FDB * pDb, LFILE * pDictIxLFile, FlmRecord * pNewRec, FLMUINT uiDrn, FlmRecord * pOldRec) { RCODE rc = FERR_OK; BTSK StackArray[ BH_MAX_LEVELS]; BTSK * pStack; FLMBYTE BtKeyBuf[ MAX_KEY_SIZ]; FLMBYTE IxKeyBuf[ MAX_KEY_SIZ]; FLMUINT uiKeyLen; void * pvField; FSInitStackCache( &StackArray [0], BH_MAX_LEVELS); if (RC_BAD( rc = DDMakeDictIxKey( pDb, pNewRec, IxKeyBuf, &uiKeyLen))) { goto Exit; } StackArray[0].pKeyBuf = BtKeyBuf; pStack = StackArray; if (RC_BAD( rc = FSBtSearch( pDb, pDictIxLFile, &pStack, IxKeyBuf, uiKeyLen, 0L))) { goto Exit; } if (pStack->uiCmpStatus == BT_EQ_KEY) { FLMUINT uiElmDoman; DIN_STATE DinState; FLMUINT uiFoundDrn; // If this is an ADD (!pOldRec), or the record found is different than // the one being updated, we have a problem. uiFoundDrn = FSRefFirst( pStack, &DinState, &uiElmDoman); if ((!pOldRec) || (uiFoundDrn != uiDrn)) { pvField = pNewRec->root(); pDb->Diag.uiInfoFlags |= FLM_DIAG_DRN; pDb->Diag.uiDrn = uiDrn; rc = (pNewRec->getFieldID( pvField) == FLM_RESERVED_TAG) ? RC_SET( FERR_CANNOT_RESERVE_NAME) : RC_SET( FERR_DUPLICATE_DICT_NAME); goto Exit; } } Exit: FSReleaseStackCache( StackArray, BH_MAX_LEVELS, FALSE); return( rc); } /**************************************************************************** Desc: Checks to make sure a dictionary DRN has not already been used. ****************************************************************************/ FSTATIC RCODE DDCheckIDConflict( FDB * pDb, LFILE * pDictContLFile, FLMUINT uiDrn) { RCODE rc = FERR_OK; FlmRecord * pOldRec = NULL; // Read to see if there is an existing record. // NOTE: Deliberately not bringing into cache if not found. if( RC_BAD( rc = FSReadRecord( pDb, pDictContLFile, uiDrn, &pOldRec, NULL, NULL))) { if (rc == FERR_NOT_FOUND) { rc = FERR_OK; } else { goto Exit; } } if( pOldRec) { void * pvField = pOldRec->root(); rc = ( pOldRec->getFieldID( pvField) == FLM_RESERVED_TAG) ? RC_SET( FERR_ID_RESERVED) : RC_SET( FERR_DUPLICATE_DICT_REC); } Exit: if( pOldRec) { pOldRec->Release(); } return( rc); } /**************************************************************************** Desc: Generate a key for an index record and add or delete it from the index. ****************************************************************************/ FSTATIC RCODE DDIxDictRecord( FDB * pDb, LFILE * pDictIxLFile, FLMUINT uiDrn, FlmRecord * pRecord, FLMUINT uiFlags) { RCODE rc; union { FLMBYTE KeyBuf [sizeof( KREF_ENTRY) + MAX_KEY_SIZ]; KREF_ENTRY KrefEntry; }; FLMUINT uiKeyLen; flmAssert( pDictIxLFile->uiLfNum > 0 && pDictIxLFile->uiLfNum < FLM_UNREGISTERED_TAGS); // Sanity check KrefEntry.ui16IxNum = (FLMUINT16)pDictIxLFile->uiLfNum; KrefEntry.uiDrn = uiDrn; KrefEntry.uiFlags = uiFlags; KrefEntry.uiTrnsSeq = 1; // Add or delete the key/reference if (RC_BAD( rc = DDMakeDictIxKey( pDb, pRecord, &KeyBuf [sizeof( KREF_ENTRY)], &uiKeyLen))) { goto Exit; } KrefEntry.ui16KeyLen = (FLMUINT16)uiKeyLen; if( RC_BAD( rc = FSRefUpdate( pDb, pDictIxLFile, &KrefEntry))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the field information. Try shared first and then local. ****************************************************************************/ RCODE fdictGetField( FDICT * pDict, FLMUINT uiFieldNum, // [in] Field Number to look up FLMUINT * puiFieldType, // [out] Optional IFD ** ppFirstIfd, // [out] Optional FLMUINT * puiFieldState) // [out] Optional { RCODE rc = FERR_OK; ITT nonstandardItt; ITT * pItt; if( pDict && pDict->pIttTbl && uiFieldNum < pDict->uiIttCnt) { pItt = &pDict->pIttTbl[ uiFieldNum]; // Is it really a field? if( ! ITT_IS_FIELD( pItt)) { rc = RC_SET( FERR_BAD_FIELD_NUM); goto Exit; } } else { // Check if the field is a FLAIM dictionary field. // Most of these fields are TEXT fields. if( (uiFieldNum >= FLM_DICT_FIELD_NUMS) && (uiFieldNum <= FLM_LAST_DICT_FIELD_NUM)) { // Most of the dictionary fields are text type. // KYBUILD now doesn't verify unregistered or dictionary fields types. pItt = &nonstandardItt; nonstandardItt.uiType = FLM_TEXT_TYPE; nonstandardItt.pvItem = NULL; } else if( uiFieldNum >= FLM_UNREGISTERED_TAGS) { pItt = &nonstandardItt; nonstandardItt.uiType = FLM_TEXT_TYPE; nonstandardItt.pvItem = NULL; } else { rc = RC_SET( FERR_BAD_FIELD_NUM); goto Exit; } } if( puiFieldType) { *puiFieldType = ITT_FLD_GET_TYPE( pItt); } if( ppFirstIfd) { *ppFirstIfd = (IFD *)pItt->pvItem; } if( puiFieldState) { *puiFieldState = ITT_FLD_GET_STATE( pItt); } Exit: return( rc); } /*************************************************************************** Desc: Get the encryption information. ****************************************************************************/ RCODE fdictGetEncInfo( FDB * pDb, FLMUINT uiEncId, // [in] Encryption definition to look up FLMUINT * puiEncType, // [out] Optional FLMUINT * puiEncState // [out] Optional ) { RCODE rc = FERR_OK; ITT * pItt; FDICT * pDict = pDb->pDict; FlmRecord * pRecord = NULL; void * pvField = NULL; FLMUINT uiEncState; FLMUINT uiEncType; if ( pDb->pFile->bInLimitedMode) { flmAssert( 0); rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } if( pDict && pDict->pIttTbl && uiEncId < pDict->uiIttCnt) { pItt = &pDict->pIttTbl[ uiEncId]; // Is it really an encryption definition? if( ! ITT_IS_ENCDEF( pItt)) { rc = RC_SET( FERR_BAD_ENCDEF_ID); goto Exit; } uiEncType = ((F_CCS *)pItt->pvItem)->getEncType(); // Get the Encryption record and determine the state. if (RC_BAD( rc = FlmRecordRetrieve( (HFDB)pDb, FLM_DICT_CONTAINER, uiEncId, FO_EXACT, &pRecord, NULL))) { goto Exit; } pvField = pRecord->find( pRecord->root(), FLM_STATE_TAG); if (pvField) { const char * pDataPtr = (const char *)pRecord->getDataPtr( pvField); if (f_strnicmp( pDataPtr, "chec", 4) == 0) { uiEncState = ITT_ENC_STATE_CHECKING; } else if (f_strnicmp( pDataPtr, "purg", 4) == 0) { uiEncState = ITT_ENC_STATE_PURGE; } else if (f_strnicmp( pDataPtr, "acti", 4) == 0) { uiEncState = ITT_ENC_STATE_ACTIVE; } else { uiEncState = ITT_ENC_STATE_UNUSED; } } else { uiEncState = ITT_ENC_STATE_UNUSED; } } else { rc = RC_SET( FERR_BAD_ENCDEF_ID); goto Exit; } if( puiEncType) { *puiEncType = uiEncType; } if( puiEncState) { *puiEncState = uiEncState; } Exit: if (pRecord) { pRecord->Release(); } return( rc); } /*************************************************************************** Desc: Get the Container given a container number. ****************************************************************************/ RCODE fdictGetContainer( FDICT * pDict, FLMUINT uiContNum, LFILE ** ppLFile) { ITT * pItt; if( pDict && uiContNum < pDict->uiIttCnt && pDict->pIttTbl) { pItt = &pDict->pIttTbl[ uiContNum]; // Is it really a container? if( !ITT_IS_CONTAINER( pItt)) { return( RC_SET( FERR_BAD_CONTAINER)); } if( ppLFile) { *ppLFile = (LFILE *) pItt->pvItem; } } else { // Hard coded container - data is [0], dictionary is [1]. if( uiContNum == FLM_DATA_CONTAINER) { if( ppLFile) { *ppLFile = &pDict->pLFileTbl[ LFILE_DATA_CONTAINER_OFFSET]; } } else if( uiContNum == FLM_DICT_CONTAINER) { if( ppLFile) { *ppLFile = &pDict->pLFileTbl[ LFILE_DICT_CONTAINER_OFFSET]; } } else if( uiContNum == FLM_TRACKER_CONTAINER) { if( ppLFile) { *ppLFile = &pDict->pLFileTbl[ LFILE_TRACKER_CONTAINER_OFFSET]; } } else { return( RC_SET( FERR_BAD_CONTAINER)); } } return( FERR_OK); } /*************************************************************************** Desc: Get the IXD, LFILE and IFD information given an index number. ****************************************************************************/ RCODE fdictGetIndex( FDICT * pDict, FLMBOOL bInLimitedMode, FLMUINT uiIxNum, LFILE ** ppLFile, // [out] optional IXD ** ppIxd, // [out] optional FLMBOOL bOfflineOk) { RCODE rc = FERR_OK; ITT * pItt; LFILE * pLFile; IXD * pIxd; if( ppIxd) { *ppIxd = NULL; } if( ppLFile) { *ppLFile = NULL; } if( pDict && uiIxNum < pDict->uiIttCnt && pDict->pIttTbl) { pItt = &pDict->pIttTbl[ uiIxNum]; // Is it really a container? if( !ITT_IS_INDEX( pItt)) { rc = RC_SET( FERR_BAD_IX); goto Exit; } pLFile = (LFILE *) pItt->pvItem; pIxd = pLFile->pIxd; if( ppLFile) { *ppLFile = pLFile; } if( ppIxd) { *ppIxd = pIxd; } // If the index is suspended the IXD_OFFLINE flag // will be set, so it is sufficient to just test // the IXD_OFFLINE for both suspended and offline // conditions. if( (pIxd->uiFlags & IXD_OFFLINE) && !bOfflineOk) { rc = RC_SET( FERR_INDEX_OFFLINE); goto Exit; } // An encrypted index that cannot be decrypted is as good as // offline. if ( pIxd->uiEncId && bInLimitedMode && !bOfflineOk) { rc = RC_SET( FERR_INDEX_OFFLINE); goto Exit; } } else if (uiIxNum == FLM_DICT_INDEX) { pLFile = pDict->pLFileTbl + LFILE_DICT_INDEX_OFFSET; if( ppLFile) { *ppLFile = pLFile; } if( ppIxd) { *ppIxd = pLFile->pIxd; } } else { rc = RC_SET( FERR_BAD_IX); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Given a IXD ID (index drn), returns the next Index Def (IXD). ****************************************************************************/ RCODE fdictGetNextIXD( FDICT * pDict, FLMUINT uiIndexNum, IXD ** ppIxd) { RCODE rc = FERR_OK; IXD * pIxd = NULL; flmAssert( pDict && pDict->uiIxdCnt); for( uiIndexNum++; uiIndexNum < pDict->uiIttCnt; uiIndexNum++) { ITT * pItt = &pDict->pIttTbl[ uiIndexNum]; if( ITT_IS_INDEX( pItt)) { LFILE * pLFile = (LFILE *) pItt->pvItem; pIxd = pLFile->pIxd; break; } } // Special case -- return the dictionary index if( !pIxd && uiIndexNum < FLM_DICT_INDEX) { pIxd = pDict->pIxdTbl; } if( pIxd) { // Check to see if the index is offline. Still return *ppIxd. if( pIxd->uiFlags & IXD_OFFLINE) { rc = RC_SET( FERR_INDEX_OFFLINE); goto Exit; } } else { rc = RC_SET( FERR_EOF_HIT); goto Exit; } Exit: if( ppIxd) { *ppIxd = pIxd; } return( rc); } /**************************************************************************** Desc: Rebuild the dictionary tables reading in all dictionary records. ****************************************************************************/ RCODE fdictRebuild( FDB * pDb) { RCODE rc = FERR_OK; TDICT tDict; FLMUINT uiCount; IXD * pIxd; FLMBOOL bTDictInitialized = FALSE; FLMBOOL bSuspended; FLMUINT uiOnlineTransId; // Allocate a new FDICT structure for reading the local dictionary // into memory. // At this point, pDb better not be pointing to a dictionary. flmAssert( pDb->pDict == NULL); if( RC_BAD( rc = flmAllocDict( &pDb->pDict))) { goto Exit; } if( !pDb->pDict->pLFileTbl) { // Read the local dictionary into memory. if( RC_BAD(rc = fdictReadLFiles( pDb, pDb->pDict))) { goto Exit; } // For a database create the LFiles still are not created. if( pDb->pDict->pLFileTbl->uiLfNum == 0) { goto Exit; } } bTDictInitialized = TRUE; if( RC_BAD( rc = fdictInitTDict( pDb, &tDict))) { goto Exit; } if( RC_BAD( rc = fdictProcessAllDictRecs( pDb, &tDict))) { goto Exit; } if( RC_BAD( rc = fdictBuildTables( &tDict, FALSE, FALSE))) { goto Exit; } // Loop through the IXD and set the uiLastDrnIndexed value. uiCount = pDb->pDict->uiIxdCnt; for( pIxd = pDb->pDict->pIxdTbl; uiCount--; pIxd++) { // Ignore any errors in case we are rebuilding. if( RC_BAD( flmGetIxTrackerInfo( pDb, pIxd->uiIndexNum, &pIxd->uiLastContainerIndexed, &pIxd->uiLastDrnIndexed, &uiOnlineTransId, &bSuspended))) { goto Exit; } if( bSuspended) { pIxd->uiFlags |= (IXD_SUSPENDED | IXD_OFFLINE); } else if( uiOnlineTransId == TRANS_ID_OFFLINE) { pIxd->uiFlags |= IXD_OFFLINE; } } Exit: if( bTDictInitialized) { tDict.pool.poolFree(); } return( rc ); } /**************************************************************************** Desc: Initializes and sets up a TDICT structure. ****************************************************************************/ RCODE fdictInitTDict( FDB * pDb, TDICT * pTDict) { RCODE rc = FERR_OK; f_memset( pTDict, 0, sizeof( TDICT)); pTDict->pool.smartPoolInit( &g_TDictPoolStats); pTDict->pDb = pDb; pTDict->uiVersionNum = pDb->pFile->FileHdr.uiVersionNum; pTDict->uiDefaultLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; pTDict->pDict = pDb->pDict; if( RC_BAD(rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, &pTDict->pLFile ))) goto Exit; Exit: return( rc); } /**************************************************************************** Desc: Build all of the dictionary tables given the temporary dictionary (pTDict) that was built in ddprep. Note: There are two ways this will be called. The first is when we are building a dictionary from scratch. The second is ONLY when a new field definition or container is added, or an index's state is changed. ****************************************************************************/ RCODE fdictBuildTables( TDICT * pTDict, FLMBOOL bRereadLFiles, FLMBOOL bNewDict) { RCODE rc = FERR_OK; DDENTRY * pEntry; TFIELD * pTField; FLMUINT uiEntryNum; TENCDEF * pTEncDef; if( RC_BAD( rc = fdictReallocAllTables( pTDict))) { goto Exit; } // Go through and add each new item to the dictionary. for( pEntry = pTDict->pFirstEntry ; pEntry ; pEntry = pEntry->pNextEntry ) { uiEntryNum = pEntry->uiEntryNum; switch( pEntry->uiType) { case 0: // Field { pTField = (TFIELD *) pEntry->vpDef; fdictAddItem( pTDict, uiEntryNum, pTField->uiFldInfo); break; } case ITT_INDEX_TYPE: { fdictAddItem( pTDict, uiEntryNum, ITT_INDEX_TYPE); if( RC_BAD( rc = fdictAddIndex( pTDict, pEntry ))) { goto Exit; } break; } case ITT_CONTAINER_TYPE: { fdictAddItem( pTDict, uiEntryNum, ITT_CONTAINER_TYPE); // rc = fdictAddLFile( pTDict, pEntry ); Already done. break; } case ITT_ENCDEF_TYPE: { if (!pTDict->pDb->pFile) { flmAssert( 0); rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } else { fdictAddItem( pTDict, uiEntryNum, ITT_ENCDEF_TYPE); pTEncDef = (TENCDEF *) pEntry->vpDef; // Need to add a new CCS. if (RC_BAD( rc = fdictAddNewCCS(pTDict, pTEncDef, uiEntryNum))) { goto Exit; } } break; } default: { break; } } } if( pTDict->uiNewIfds || bNewDict) { if( RC_BAD( rc = fdictFixupIfdPointers( pTDict->pDict, bNewDict ? 0 : (pTDict->uiTotalIfds - pTDict->uiNewIfds)))) { goto Exit; } } if( bRereadLFiles) { if( RC_BAD( rc = fdictReadLFiles( pTDict->pDb, pTDict->pDict))) { goto Exit; } } if( RC_BAD( rc = fdictFixupLFileTbl( pTDict->pDict))) { goto Exit; } Exit: return( rc ); } /**************************************************************************** Desc: Fixup pointers in tables of copied dictionary. This is called after a copy of one dictionary to another, or after a dictionary's tables have been reallocated. ****************************************************************************/ FSTATIC void fdictFixupPointers( FDICT * pNewDict, FDICT * pOldDict ) { FLMUINT uiPos; FLMUINT uiOffset; LFILE * pOldLFile; LFILE * pNewLFile; IFD * pOldIfd; IFD * pNewIfd; IXD * pOldIxd; IXD * pNewIxd; ITT * pOldItt; ITT * pNewItt; // Fixup anything that points to LFILE entries. if (pNewDict->pLFileTbl && pNewDict->pLFileTbl != pOldDict->pLFileTbl) { // Fixup pItt->pvItem pointers for indexes and containers for (uiPos = 0, pOldItt = pOldDict->pIttTbl, pNewItt = pNewDict->pIttTbl; uiPos < pOldDict->uiIttCnt; uiPos++, pOldItt++, pNewItt++) { if (ITT_IS_CONTAINER( pOldItt) || ITT_IS_INDEX( pOldItt)) { if (pOldItt->pvItem) { LFILE * pTmpLFile; pTmpLFile = (LFILE *)(pOldItt->pvItem); uiOffset = (FLMUINT)(pTmpLFile - pOldDict->pLFileTbl); pTmpLFile = pNewDict->pLFileTbl + uiOffset; pNewItt->pvItem = (void *)pTmpLFile; } else { flmAssert( pNewItt->pvItem == NULL); } } else if (ITT_IS_ENCDEF( pOldItt)) { if (pOldItt->pvItem) { pNewItt->pvItem = pOldItt->pvItem; ((F_CCS *)pNewItt->pvItem)->AddRef(); } else { flmAssert( pNewItt->pvItem == NULL); } } } } // Fixup anything that points to IXD entries if (pNewDict->pIxdTbl && pNewDict->pIxdTbl != pOldDict->pIxdTbl) { // Fixup pLFile->pIxd pointers for (uiPos = 0, pOldLFile = pOldDict->pLFileTbl, pNewLFile = pNewDict->pLFileTbl; uiPos < pOldDict->uiLFileCnt; uiPos++, pOldLFile++, pNewLFile++) { if (pOldLFile->pIxd) { uiOffset = (FLMUINT)(pOldLFile->pIxd - pOldDict->pIxdTbl); pNewLFile->pIxd = pNewDict->pIxdTbl + uiOffset; } else { flmAssert( pNewLFile->pIxd == NULL); } } // Fixup pIfd->pIxd pointers for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl, pNewIfd = pNewDict->pIfdTbl; uiPos < pOldDict->uiIfdCnt; uiPos++, pOldIfd++, pNewIfd++) { if (pOldIfd->pIxd) { uiOffset = (FLMUINT)(pOldIfd->pIxd - pOldDict->pIxdTbl); pNewIfd->pIxd = pNewDict->pIxdTbl + uiOffset; } else { flmAssert( pNewIfd->pIxd == NULL); } } } // Fixup anything that points to IFD entries if (pNewDict->pIfdTbl && pNewDict->pIfdTbl != pOldDict->pIfdTbl) { // Fixup pIfd->pNextInChain pointers for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl, pNewIfd = pNewDict->pIfdTbl; uiPos < pOldDict->uiIfdCnt; uiPos++, pOldIfd++, pNewIfd++) { if (pOldIfd->pNextInChain) { uiOffset = (FLMUINT)(pOldIfd->pNextInChain - pOldDict->pIfdTbl); pNewIfd->pNextInChain = pNewDict->pIfdTbl + uiOffset; } else { flmAssert( pNewIfd->pNextInChain == NULL); } } // Fixup pIxd->pFirstIfd pointers for (uiPos = 0, pOldIxd = pOldDict->pIxdTbl, pNewIxd = pNewDict->pIxdTbl; uiPos < pOldDict->uiIxdCnt; uiPos++, pOldIxd++, pNewIxd++) { if (pOldIxd->pFirstIfd) { uiOffset = (FLMUINT)(pOldIxd->pFirstIfd - pOldDict->pIfdTbl); pNewIxd->pFirstIfd = pNewDict->pIfdTbl + uiOffset; } else { flmAssert( pNewIxd->pFirstIfd == NULL); } } // Fixup pItt->pvItem pointers for (uiPos = 0, pOldItt = pOldDict->pIttTbl, pNewItt = pNewDict->pIttTbl; uiPos < pOldDict->uiIttCnt; uiPos++, pOldItt++, pNewItt++) { if (ITT_IS_FIELD( pOldItt)) { if (pOldItt->pvItem) { IFD * pTmpIfd; pTmpIfd = (IFD *)(pOldItt->pvItem); uiOffset = (FLMUINT)(pTmpIfd - pOldDict->pIfdTbl); pTmpIfd = pNewDict->pIfdTbl + uiOffset; pNewItt->pvItem = (void *)pTmpIfd; } else { flmAssert( pNewItt->pvItem == NULL); } } } } // Fixup anything that points to field path entries if (pNewDict->pFldPathsTbl && pNewDict->pFldPathsTbl != pOldDict->pFldPathsTbl) { // Fixup pIfd->pFieldPathCToP and pIfd->pFieldPathPToC pointers for (uiPos = 0, pOldIfd = pOldDict->pIfdTbl, pNewIfd = pNewDict->pIfdTbl; uiPos < pOldDict->uiIfdCnt; uiPos++, pOldIfd++, pNewIfd++) { if (pOldIfd->pFieldPathCToP) { uiOffset = (FLMUINT)(pOldIfd->pFieldPathCToP - pOldDict->pFldPathsTbl); pNewIfd->pFieldPathCToP = pNewDict->pFldPathsTbl + uiOffset; } else { flmAssert( pNewIfd->pFieldPathCToP == NULL); } if (pOldIfd->pFieldPathPToC) { uiOffset = (FLMUINT)(pOldIfd->pFieldPathPToC - pOldDict->pFldPathsTbl); pNewIfd->pFieldPathPToC = pNewDict->pFldPathsTbl + uiOffset; } else { flmAssert( pNewIfd->pFieldPathPToC == NULL); } } } } /**************************************************************************** Desc: Allocate all of the dictionary tables based on the counts that were incremented in pTDict. Coded to add new fields, indexes or container, but not to modify or delete anything! ****************************************************************************/ FSTATIC RCODE fdictReallocAllTables( TDICT * pTDict) { RCODE rc = FERR_OK; FDICT OldDict; FDICT * pDict = pTDict->pDict; // Save a copy of the old dictionary's pointers and counters // Easiest way to do this is to simply copy the structure. f_memcpy( &OldDict, pDict, sizeof( FDICT)); if( pTDict->pLastEntry && pTDict->pLastEntry->uiEntryNum >= pDict->uiIttCnt && pTDict->pLastEntry->uiEntryNum < FLM_RESERVED_TAG_NUMS) { ITT * pItt; FLMUINT uiNewCount; uiNewCount = pTDict->pLastEntry->uiEntryNum + 1 - pDict->uiIttCnt; if (uiNewCount) { // Must fake out so that we don't lose the old table. pDict->pIttTbl = NULL; if( RC_BAD( rc = fdictReallocTbl( sizeof( ITT), pDict->uiIttCnt, uiNewCount, (void **) &pDict->pIttTbl))) { goto Exit; } pTDict->uiTotalItts = pDict->uiIttCnt + uiNewCount; // Copy the table to the new location (because of fake out above) if( OldDict.uiIttCnt) { f_memcpy( pDict->pIttTbl, OldDict.pIttTbl, sizeof( ITT) * OldDict.uiIttCnt); } // Initialize the new items to empty. pItt = pDict->pIttTbl + pDict->uiIttCnt; for( ;uiNewCount--; pItt++) { pItt->uiType = ITT_EMPTY_SLOT; pItt->pvItem = NULL; } } } if (pTDict->uiNewIxds) { // Must fake out so that we don't lose the old table. pDict->pIxdTbl = NULL; if( RC_BAD( rc = fdictReallocTbl( sizeof( IXD), pDict->uiIxdCnt, pTDict->uiNewIxds, (void **)&pDict->pIxdTbl))) { goto Exit; } pTDict->uiTotalIxds = pDict->uiIxdCnt + pTDict->uiNewIxds; // Copy the table to the new location (because of fake out above) if( OldDict.uiIxdCnt) { f_memcpy( pDict->pIxdTbl, OldDict.pIxdTbl, sizeof( IXD) * OldDict.uiIxdCnt); } } if (pTDict->uiNewIfds) { // Must fake out so that we don't lose the old table. pDict->pIfdTbl = NULL; if( RC_BAD( rc = fdictReallocTbl( sizeof( IFD), pDict->uiIfdCnt, pTDict->uiNewIfds, (void **)&pDict->pIfdTbl))) { goto Exit; } pTDict->uiTotalIfds = pDict->uiIfdCnt + pTDict->uiNewIfds; // Copy the table to the new location (because of fake out above) if( OldDict.uiIfdCnt) { f_memcpy( pDict->pIfdTbl, OldDict.pIfdTbl, sizeof( IFD) * OldDict.uiIfdCnt); } } if (pTDict->uiNewFldPaths) { // Must fake out so that we don't lose the old table. pDict->pFldPathsTbl = NULL; if( RC_BAD( rc = fdictReallocTbl( sizeof( FLMUINT), pDict->uiFldPathsCnt, pTDict->uiNewFldPaths, (void **)&pDict->pFldPathsTbl))) { goto Exit; } pTDict->uiTotalFldPaths = pDict->uiFldPathsCnt + pTDict->uiNewFldPaths; // Copy the table to the new location (because of fake out above) if( OldDict.uiFldPathsCnt) { f_memcpy( pDict->pFldPathsTbl, OldDict.pFldPathsTbl, sizeof( FLMUINT) * OldDict.uiFldPathsCnt); } } fdictFixupPointers( pDict, &OldDict); Exit: // Free any old tables where a new table was allocated. if (OldDict.pLFileTbl != pDict->pLFileTbl) { f_free( &OldDict.pLFileTbl); } if (OldDict.pIttTbl != pDict->pIttTbl) { f_free( &OldDict.pIttTbl); } if (OldDict.pIxdTbl != pDict->pIxdTbl) { f_free( &OldDict.pIxdTbl); } if (OldDict.pIfdTbl != pDict->pIfdTbl) { f_free( &OldDict.pIfdTbl); } if (OldDict.pFldPathsTbl != pDict->pFldPathsTbl) { f_free( &OldDict.pFldPathsTbl); } return( rc ); } /**************************************************************************** Desc: Allocate or reallocate a table. ****************************************************************************/ FSTATIC RCODE fdictReallocTbl( FLMUINT uiElementSize, FLMUINT uiTblSize, FLMUINT uiAddElements, void ** ppvTblRV) { RCODE rc = FERR_OK; // Does the table need to grow? if( uiAddElements) { if( *ppvTblRV) { if( RC_BAD( rc = f_recalloc( uiElementSize * (uiTblSize + uiAddElements), ppvTblRV))) { goto Exit; } } else { if( RC_BAD( rc = f_calloc( uiElementSize * (uiTblSize + uiAddElements), ppvTblRV))) { goto Exit; } } } Exit: return( rc ); } /**************************************************************************** Desc: Add a new item to the item type table. ****************************************************************************/ FSTATIC void fdictAddItem( TDICT * pTDict, FLMUINT uiFieldNum, FLMUINT uiFieldType) { FDICT * pDict = pTDict->pDict; ITT * pItt; if( uiFieldNum < FLM_RESERVED_TAG_NUMS) { pItt = pDict->pIttTbl + uiFieldNum; pItt->uiType = uiFieldType; pItt->pvItem = NULL; if( uiFieldNum >= pDict->uiIttCnt) { pDict->uiIttCnt = uiFieldNum + 1; } } } /**************************************************************************** Desc: Add the new IXD, IFD, field paths and LFILE for the index. ****************************************************************************/ FSTATIC RCODE fdictAddIndex( TDICT * pTDict, DDENTRY * pEntry) { RCODE rc = FERR_OK; FDICT * pDict = pTDict->pDict; FLMUINT uiIndexNum = pEntry->uiEntryNum; IXD * pIxd; IFD * pIfd; FLMUINT * pFirstPToCFld; FLMUINT * pFirstCToPFld; FLMUINT * pCurFld; FLMUINT * pTempFld; TIXD * pTIxd; TIFD * pTIfd; TIFP * pTIfp; // The index numbers in the IXD array do not need to be in any order. // Just add all of the index information to the end of the table. pIxd = pDict->pIxdTbl + pDict->uiIxdCnt++; pIxd->uiIndexNum = uiIndexNum; pTIxd = (TIXD *) pEntry->vpDef; pIxd->uiContainerNum = pTIxd->uiContainerNum; pIxd->uiNumFlds = pTIxd->uiNumFlds; pIxd->uiFlags = pTIxd->uiFlags; pIxd->uiLanguage = pTIxd->uiLanguage; pIxd->uiLastContainerIndexed = 0xFFFFFFFF; pIxd->uiLastDrnIndexed = DRN_LAST_MARKER; pIxd->uiEncId = pTIxd->uiEncId; // Setup the IFD elements and the field paths. pIxd->pFirstIfd = pIfd = pDict->pIfdTbl + pDict->uiIfdCnt; pDict->uiIfdCnt += pIxd->uiNumFlds; for( pTIfd = pTIxd->pNextTIfd; pTIfd; pIfd++, pTIfd = pTIfd->pNextTIfd) { // This is a good place to set the IFD_LAST flag. // Could/Should be done in ddprep.c if( pTIfd->pNextTIfd == NULL) pTIfd->uiFlags |= IFD_LAST; pIfd->uiIndexNum = uiIndexNum; pIfd->pIxd = pIxd; pIfd->uiFlags = pTIfd->uiFlags; pIfd->uiLimit = pTIfd->uiLimit; pIfd->uiCompoundPos = pTIfd->uiCompoundPos; // The pTIfp->pNextTIfp are linked from parent to child. pTIfp = pTIfd->pTIfp; pCurFld = pDict->pFldPathsTbl + pDict->uiFldPathsCnt; pFirstPToCFld = pFirstCToPFld = pCurFld; pIfd->pFieldPathPToC = pFirstPToCFld; do { *pCurFld++ = pTIfp->uiFldNum; pTIfp = pTIfp->pNextTIfp; } while( pTIfp); pIfd->uiFldNum = *(pCurFld-1); pTempFld = pCurFld - 1; // Null Terminate *pCurFld++ = 0; pTIfp = pTIfd->pTIfp; if( pTIfp->pNextTIfp) // If more than one field make the CToP path. { pFirstCToPFld = pCurFld; while( pTempFld != pFirstPToCFld) { *pCurFld++ = *pTempFld--; } *pCurFld++ = *pTempFld; *pCurFld++ = 0; } pIfd->pFieldPathCToP = pFirstCToPFld; pDict->uiFldPathsCnt += pCurFld - pFirstPToCFld; } return( rc ); } /**************************************************************************** Desc: Fixup the IFD chain and the pIfd->pIxd pointers. ****************************************************************************/ FSTATIC RCODE fdictFixupIfdPointers( FDICT * pDict, FLMUINT uiIfdStartOffset) { RCODE rc = FERR_OK; FLMUINT uiCount; IFD * pIfd; ITT * pItt; ITT * pIttTbl = pDict->pIttTbl; // Go through the IFD list and setup the pNextInChain pointers // making sure that the required fields are first. for( uiCount = pDict->uiIfdCnt - uiIfdStartOffset, pIfd = pDict->pIfdTbl + uiIfdStartOffset; uiCount; uiCount--, pIfd++) { IFD * pPrevInChain; IFD * pTempIfd; if( pIfd->uiFldNum >= pDict->uiIttCnt) { if( pIfd->uiFldNum < FLM_RESERVED_TAG_NUMS) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } continue; } else { pItt = pIttTbl + pIfd->uiFldNum; if( !ITT_IS_FIELD( pItt)) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } } // Move the field type to the pIfd->uiFlags IFD_SET_FIELD_TYPE( pIfd, ITT_FLD_GET_TYPE( pItt)); // Need to include 'any', 'use', 'parent' tags as valid tags. if( !pItt->pvItem) { pItt->pvItem = (void *) pIfd; } else { // Follow the chain and index at the front or rear depending on // if the field is required within the set. pTempIfd = (IFD *) pItt->pvItem; if( (pIfd->uiFlags & IFD_REQUIRED_IN_SET) || !(pTempIfd->uiFlags & IFD_REQUIRED_IN_SET)) { pIfd->pNextInChain = pTempIfd; pItt->pvItem = (void *) pIfd; } else { // Not required in set and first IFD is required in set. // Look for first not required IFD in the chain. pPrevInChain = pTempIfd; pTempIfd = pTempIfd->pNextInChain; for( ; pTempIfd; pTempIfd = pTempIfd->pNextInChain) { if( !(pTempIfd->uiFlags & IFD_REQUIRED_IN_SET)) break; pPrevInChain = pTempIfd; } pIfd->pNextInChain = pPrevInChain->pNextInChain; pPrevInChain->pNextInChain = pIfd; } } } Exit: return( rc); } /**************************************************************************** Desc: Fixup the ITT pointers into the LFILE elements and all of the IXD pointers in the LDICT. ****************************************************************************/ RCODE fdictFixupLFileTbl( FDICT * pDict) { RCODE rc = FERR_OK; FLMUINT uiCount; LFILE * pLFile; IXD * pIxd; ITT * pItt; ITT * pIttTbl = pDict->pIttTbl; FLMUINT uiIttCnt = pDict->uiIttCnt; for( uiCount = pDict->uiLFileCnt, pLFile = pDict->pLFileTbl ; uiCount; uiCount--, pLFile++) { if( pLFile->uiLfNum != FLM_DATA_CONTAINER && pLFile->uiLfNum != FLM_DICT_CONTAINER && pLFile->uiLfNum != FLM_DICT_INDEX && pLFile->uiLfNum != FLM_TRACKER_CONTAINER) { pItt = pIttTbl + pLFile->uiLfNum; if( uiIttCnt <= pLFile->uiLfNum || (pLFile->uiLfType == LF_CONTAINER && !ITT_IS_CONTAINER( pItt))) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } if( pLFile->uiLfType == LF_INDEX && !ITT_IS_INDEX( pItt)) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } pItt->pvItem = pLFile; } else if( pLFile->uiLfNum == FLM_DICT_INDEX) { // The first IXD should be the dictionary index. if( pDict->pIxdTbl && pDict->pIxdTbl->uiIndexNum == FLM_DICT_INDEX) { pLFile->pIxd = pDict->pIxdTbl; } } } // Now that all of the indexes/containers in the ITT table point // to the LFILE entries, fixup the LFILE to point to the IXD entries. for( uiCount = pDict->uiIxdCnt, pIxd = pDict->pIxdTbl; uiCount; uiCount--, pIxd++) { if( uiIttCnt <= pIxd->uiIndexNum) { if( pIxd->uiIndexNum != FLM_DICT_INDEX) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } } else { pItt = pIttTbl + pIxd->uiIndexNum; pLFile = (LFILE *) pItt->pvItem; if( !pLFile) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } pLFile->pIxd = pIxd; } // Verify that the pIxd->uiContainerNum is actually a container. // A value of 0 means that the index is on ALL containers. if (pIxd->uiContainerNum) { if( uiIttCnt <= pIxd->uiContainerNum) { if( pIxd->uiContainerNum != FLM_DATA_CONTAINER && pIxd->uiContainerNum != FLM_DICT_CONTAINER && pIxd->uiContainerNum != FLM_TRACKER_CONTAINER) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } } else { pItt = pIttTbl + pIxd->uiContainerNum; if( !ITT_IS_CONTAINER( pItt)) { rc = RC_SET( FERR_BAD_REFERENCE); goto Exit; } } } } Exit: return( rc); } /**************************************************************************** Desc: Add a new CCS reference to the item type table. If a key is included we can use it, otherwise we will have to generate one. ****************************************************************************/ FSTATIC RCODE fdictAddNewCCS( TDICT * pTDict, TENCDEF * pTEncDef, FLMUINT uiRecNum) { RCODE rc = FERR_OK; FDICT * pDict = pTDict->pDict; ITT * pItt; F_CCS * pCcs = NULL; FDB * pDb = pTDict->pDb; F_CCS * pDbWrappingKey; if( uiRecNum >= FLM_RESERVED_TAG_NUMS) { goto Exit; } if (!pDb->pFile->bInLimitedMode) { pDbWrappingKey = pDb->pFile->pDbWrappingKey; flmAssert( pTEncDef); if ((pCcs = f_new F_CCS) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Setup the F_CCS. if (RC_BAD( rc = pCcs->init( FALSE, pTEncDef->uiAlgType ))) { goto Exit; } if (!pTEncDef->uiLength) { flmAssert( 0); rc = RC_SET( FERR_MISSING_ENC_KEY); goto Exit; } // We need to set the key information. This also unwraps the key and stores the // handle. if( RC_BAD( rc = pCcs->setKeyFromStore( pTEncDef->pucKeyInfo, (FLMUINT32)pTEncDef->uiLength, NULL, pDbWrappingKey))) { goto Exit; } } // Save the CCS object in the ITT table. pItt = pDict->pIttTbl + uiRecNum; pItt->pvItem = (void *)pCcs; pCcs = NULL; if( uiRecNum >= pDict->uiIttCnt) { pDict->uiIttCnt = uiRecNum + 1; } Exit: if (pCcs) { delete pCcs; } return( rc); } /**************************************************************************** Desc: Copies an existing dictionary to a new dictionary. This does not fix up all of the ITT's pvItem pointers (including the pFirstIfd pointer of fields in the ITT table). To clone the dictionary, call fdictCloneDict. ****************************************************************************/ RCODE fdictCopySkeletonDict( FDB * pDb) { RCODE rc = FERR_OK; FDICT * pNewDict = NULL; FDICT * pOldDict = pDb->pDict; FLMUINT uiTblSize; FLMUINT uiPos; LFILE * pLFile; IXD * pIxd; ITT * pItt; ITT * pNewIttTbl = NULL; FLMUINT uiNewIttTblLen = 0; LFILE * pNewDictIndexLFile = NULL; FLMUINT * pOldFieldPathsTbl = NULL; FLMUINT * pNewFieldPathsTbl = NULL; if( RC_BAD( rc = f_calloc( (FLMUINT)sizeof( FDICT), &pNewDict))) { goto Exit; } pNewDict->pNext = pNewDict->pPrev = NULL; pNewDict->pFile = NULL; pNewDict->uiUseCount = 1; // Nothing to do is not a legal state. if( !pOldDict) { flmAssert( pOldDict != NULL); pDb->pDict = pNewDict; goto Exit; } // ITT Table if( (uiTblSize = pNewDict->uiIttCnt = pOldDict->uiIttCnt) != 0) { if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( ITT), &pNewDict->pIttTbl))) { goto Exit; } pNewIttTbl = pNewDict->pIttTbl; uiNewIttTblLen = uiTblSize; f_memcpy( pNewDict->pIttTbl, pOldDict->pIttTbl, uiTblSize * sizeof( ITT)); // Clear out all of the pointer values. pItt = pNewDict->pIttTbl; for( uiPos = 0; uiPos < uiTblSize; uiPos++, pItt++) { if ( pItt->uiType == ITT_ENCDEF_TYPE && !pDb->pFile->bInLimitedMode) { flmAssert( pItt->pvItem); ((F_CCS *)pItt->pvItem)->AddRef(); } else { pItt->pvItem = NULL; } } } // LFILE Table if( (uiTblSize = pNewDict->uiLFileCnt = pOldDict->uiLFileCnt) != 0) { if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( LFILE), &pNewDict->pLFileTbl))) { goto Exit; } f_memcpy( pNewDict->pLFileTbl, pOldDict->pLFileTbl, uiTblSize * sizeof( LFILE)); for( pLFile = pNewDict->pLFileTbl; uiTblSize--; pLFile++) { if( pLFile->uiLfNum < FLM_RESERVED_TAG_NUMS) { // WARNING: The code must make a new LFILE // before the dictionary is aware of it. if( pLFile->uiLfNum < uiNewIttTblLen) { pItt = pNewIttTbl + pLFile->uiLfNum; pItt->pvItem = (void *) pLFile; } } else if( pLFile->uiLfNum == FLM_DICT_INDEX) { pNewDictIndexLFile = pLFile; } } } // IXD Table if( (uiTblSize = pNewDict->uiIxdCnt = pOldDict->uiIxdCnt) != 0) { if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( IXD), &pNewDict->pIxdTbl))) { goto Exit; } f_memcpy( pNewDict->pIxdTbl, pOldDict->pIxdTbl, uiTblSize * sizeof( IXD)); // Fixup all of the pointers to the IXD. for( pIxd = pNewDict->pIxdTbl; uiTblSize--; pIxd++) { if( pIxd->uiIndexNum != FLM_DICT_INDEX) { pItt = pNewIttTbl + pIxd->uiIndexNum; pLFile = (LFILE *) pItt->pvItem; pLFile->pIxd = pIxd; } else if( pNewDictIndexLFile) { pNewDictIndexLFile->pIxd = pIxd; } } } // Field Paths Table if( (uiTblSize = pNewDict->uiFldPathsCnt = pOldDict->uiFldPathsCnt) != 0) { if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( FLMUINT), &pNewDict->pFldPathsTbl))) { goto Exit; } f_memcpy( pNewDict->pFldPathsTbl, pOldDict->pFldPathsTbl, uiTblSize * sizeof( FLMUINT)); pOldFieldPathsTbl = pOldDict->pFldPathsTbl; pNewFieldPathsTbl = pNewDict->pFldPathsTbl; } // IFD Table if( (uiTblSize = pNewDict->uiIfdCnt = pOldDict->uiIfdCnt) != 0) { IFD * pIfd; FLMUINT uiLastIndexNum; FLMUINT uiOffset; if( RC_BAD( rc = f_alloc( uiTblSize * sizeof( IFD), &pNewDict->pIfdTbl))) { goto Exit; } f_memcpy( pNewDict->pIfdTbl, pOldDict->pIfdTbl, uiTblSize * sizeof( IFD)); // Fixup all pFirstIfd pointers, backlinks to the pIxd and fldPathTbls. // Set all of the IfdChain values to NULL to be fixed up later. pIfd = pNewDict->pIfdTbl; uiLastIndexNum = 0; for( uiPos = 0; uiPos < uiTblSize; uiPos++, pIfd++) { pIfd->pNextInChain = NULL; if( pIfd->uiIndexNum != FLM_DICT_INDEX) { pItt = pNewIttTbl + pIfd->uiIndexNum; pLFile = (LFILE *) pItt->pvItem; pIxd = pLFile->pIxd; } else { pIxd = pNewDictIndexLFile->pIxd; } pIfd->pIxd = pIxd; if( uiLastIndexNum != pIfd->uiIndexNum) { pIxd->pFirstIfd = pIfd; uiLastIndexNum = pIfd->uiIndexNum; } // Fixup the field paths. flmAssert( pNewFieldPathsTbl != NULL); uiOffset = pIfd->pFieldPathCToP - pOldFieldPathsTbl; pIfd->pFieldPathCToP = pNewFieldPathsTbl + uiOffset; uiOffset = pIfd->pFieldPathPToC - pOldFieldPathsTbl; pIfd->pFieldPathPToC = pNewFieldPathsTbl + uiOffset; } } f_mutexLock( gv_FlmSysData.hShareMutex); flmUnlinkFdbFromDict( pDb); f_mutexUnlock( gv_FlmSysData.hShareMutex); pDb->pDict = pNewDict; pNewDict = NULL; Exit: if( RC_BAD( rc) && pNewDict) { // Undo all of the allocations on the new table. if( pNewDict->pLFileTbl) { f_free( &pNewDict->pLFileTbl); } if( pNewDict->pIttTbl) { f_free( &pNewDict->pIttTbl); } if( pNewDict->pIxdTbl) { f_free( &pNewDict->pIxdTbl); } if( pNewDict->pIfdTbl) { f_free( &pNewDict->pIfdTbl); } if( pNewDict->pFldPathsTbl) { f_free( &pNewDict->pFldPathsTbl); } f_free( &pNewDict); } return( rc); } /**************************************************************************** Desc: Creates a new version of the current dictionary and fixes up all pointers ****************************************************************************/ RCODE fdictCloneDict( FDB * pDb) { RCODE rc = FERR_OK; TDICT tDict; FLMBOOL bTDictInitialized = FALSE; if( RC_BAD( rc = fdictCopySkeletonDict( pDb))) { goto Exit; } bTDictInitialized = TRUE; if( RC_BAD( rc = fdictInitTDict( pDb, &tDict))) { goto Exit; } if( RC_BAD( rc = fdictBuildTables( &tDict, FALSE, TRUE))) { goto Exit; } pDb->uiFlags |= FDB_UPDATED_DICTIONARY; Exit: if( bTDictInitialized) { tDict.pool.poolFree(); } // If we allocated an FDICT and there was an error, free the FDICT. if( (RC_BAD( rc)) && (pDb->pDict)) { flmFreeDict( pDb->pDict); pDb->pDict = NULL; } return( rc); } libflaim-4.9.966/src/frecset.cpp0000644000175000017500000000615110510774540020016 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Record set class implementation // Tabs: 3 // // Copyright (c) 1999-2000,2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: frecset.cpp 12282 2006-01-19 14:52:56 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Destructor ****************************************************************************/ FlmRecordSet::~FlmRecordSet() { clear(); if( m_ppRecArray) { f_free( &m_ppRecArray); } } /**************************************************************************** Public: FlmRecordSet::insert Desc: Insert a FlmRecord into the set. ****************************************************************************/ RCODE FlmRecordSet::insert( FlmRecord * pRecord) { RCODE rc = FERR_OK; FlmRecord ** ppTmpArray; // See if we need to reallocate the array. if (m_iTotalRecs == m_iRecArraySize) { if( RC_BAD( rc = f_calloc( sizeof( FlmRecord *) * (m_iRecArraySize + 10), &ppTmpArray))) { goto Exit; } if (m_iTotalRecs) { f_memcpy( ppTmpArray, m_ppRecArray, sizeof( FlmRecord *) * m_iTotalRecs); } m_ppRecArray = ppTmpArray; m_iRecArraySize += 10; } // Add the new entry into the array. m_ppRecArray [m_iTotalRecs] = pRecord; pRecord->AddRef(); m_iTotalRecs++; Exit: return( rc); } /**************************************************************************** Public: FlmRecordSet::clear Desc: Clear all records from the FlmRecord set. ****************************************************************************/ void FlmRecordSet::clear( void) { FLMINT iCnt; for (iCnt = 0; iCnt < m_iTotalRecs; iCnt++) { m_ppRecArray [iCnt]->Release(); m_ppRecArray [iCnt] = NULL; } // Set is now empty. m_iTotalRecs = 0; // Reset the current position. m_iCurrRec = -1; } /**************************************************************************** Public: FlmRecordSet::next Desc: Return the next record in the set. ****************************************************************************/ FlmRecord * FlmRecordSet::next( void) { if (!m_iTotalRecs) { return( NULL); } if (m_iCurrRec + 1 >= m_iTotalRecs) { m_iCurrRec = m_iTotalRecs; return( NULL); } m_iCurrRec++; return( m_ppRecArray [m_iCurrRec]); } libflaim-4.9.966/src/fdb.cpp0000644000175000017500000003523110510774540017117 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for working with and FDB database handle structure. // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdb.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC void flmLogMustCloseReason( FFILE * pFile, const char * pszFileName, FLMINT iLineNumber); /**************************************************************************** Desc: This function will use the FDB for use by the current thread. If another thread already has the FDB used, it will go into the debugger. ****************************************************************************/ #if defined( FLM_DEBUG) void fdbUseCheck( FDB * pDb) { FLMUINT uiMyThreadId = (FLMUINT)f_threadId(); f_mutexLock( pDb->hMutex); if (!pDb->uiUseCount) { pDb->uiUseCount++; pDb->uiThreadId = uiMyThreadId; } else if (pDb->uiThreadId != uiMyThreadId) { flmAssert( 0); } else { pDb->uiUseCount++; } f_mutexUnlock( pDb->hMutex); } #endif /**************************************************************************** Desc: This function will unuse the FDB for use by the current thread. ****************************************************************************/ #if defined( FLM_DEBUG) void fdbUnuse( FDB * pDb) { FLMUINT uiMyThreadId = (FLMUINT)f_threadId(); f_mutexLock( pDb->hMutex); if ((!pDb->uiUseCount) || (uiMyThreadId != pDb->uiThreadId)) { flmAssert( 0); } else { pDb->uiUseCount--; } f_mutexUnlock( pDb->hMutex); } #endif /**************************************************************************** Desc: This function will init an FDB for a database that is being handled via a client/server connection. ****************************************************************************/ void fdbInitCS( FDB * pDb) { if (pDb) { fdbUseCheck( pDb); (void)flmResetDiag( pDb); } } /**************************************************************************** Desc: This function will init an FDB for use. It will also start the necessary type of transaction - if any. ****************************************************************************/ RCODE fdbInit( FDB * pDb, // Pointer to database. FLMUINT uiTransType, // Type of transaction to start. FLMUINT uiFlags, // Flags for function. FLMUINT uiAutoTrans, // Auto transaction OK? Used only if // uiTransType == FLM_UPDATE_TRANS. This // also has the max lock wait time. FLMBOOL * pbStartedTransRV // Returns flag indicating whether or not // we started a transaction inside this // routine. ) { RCODE rc = FERR_OK; FLMUINT uiTransFlags = FLM_GET_TRANS_FLAGS( uiTransType); uiTransType = FLM_GET_TRANS_TYPE( uiTransType); if( pbStartedTransRV) { *pbStartedTransRV = FALSE; } fdbUseCheck( pDb); if (!pDb->uiInitNestLevel) { if (!(uiFlags & FDB_DONT_RESET_DIAG) && !pDb->uiInitNestLevel) { (void)flmResetDiag( pDb); } if (!gv_FlmSysData.Stats.bCollectingStats) { pDb->pStats = NULL; pDb->pDbStats = NULL; } else { pDb->pStats = &pDb->Stats; /* Statistics are being collected for the system. Therefore, if we are not currently collecting statistics in the session, start. If we were collecting statistics, but the start time was earlier than the start time in the system statistics structure, reset the statistics in the session. */ if (!pDb->Stats.bCollectingStats) { flmStatStart( &pDb->Stats); } else if (pDb->Stats.uiStartTime < gv_FlmSysData.Stats.uiStartTime) { flmStatReset( &pDb->Stats, FALSE, FALSE); } (void)flmStatGetDb( &pDb->Stats, pDb->pFile, 0, &pDb->pDbStats, NULL, NULL); pDb->pLFileStats = NULL; } } pDb->uiInitNestLevel++; // Now that the nest level has been incremented, test // to see if the database is being forced to close. if( !(uiFlags & FDB_CLOSING_OK)) { if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } } /* If uiTransType == FLM_NO_TRANS, lock down the default dictionary if the FDB is not currently involved in a transaction. If the FDB is already involved in a transaction, there is no need to lock down anything, because the FDICT structure and tables will already be locked down. */ if (uiTransType == FLM_NO_TRANS) { if (pDb->uiTransType == FLM_NO_TRANS) { f_mutexLock( gv_FlmSysData.hShareMutex); if (pDb->pFile->pDictList && pDb->pDict != pDb->pFile->pDictList) { flmLinkFdbToDict( pDb, pDb->pFile->pDictList); } f_mutexUnlock( gv_FlmSysData.hShareMutex); } goto Exit; } /* If they are requesting a read transaction, set the FLM_AUTO_TRANS bit to TRUE. */ if (uiTransType == FLM_READ_TRANS) { uiAutoTrans |= FLM_AUTO_TRANS; } else { if (pDb->uiTransType == FLM_UPDATE_TRANS) { pDb->bHadUpdOper = TRUE; } /* uiTransType == FLM_UPDATE_TRANS, make sure updates are OK. */ if (pDb->uiFlags & FDB_FILE_LOCK_SHARED) { // There is a shared lock on the database. rc = RC_SET( FERR_PERMISSION); goto Exit; } } Test_Trans: /* See if we already have a transaction going on the DB. */ if (pDb->uiTransType != FLM_NO_TRANS) { /* If the transaction is an invisible transaction, we may need to abort it, depending on what is being asked for. */ if (pDb->uiFlags & FDB_INVISIBLE_TRANS) { /* Several conditions will cause us to abort an invisible transaction: 1. If it is NOT ok for a transaction to already be in progress. That is, the caller does NOT want to piggy-back on an already running transaction. 2. If it is a transaction that has been marked as being required to abort because of some error. 3. If the transaction needed is an update transaction, but the invisible transaction is a read transaction. 4. The flag requesting that we join invisible transactions is not set. */ if ((!(uiFlags & FDB_TRANS_GOING_OK)) || (!(uiFlags & FDB_INVISIBLE_TRANS_OK)) || (flmCheckBadTrans( pDb)) || ((uiTransType == FLM_UPDATE_TRANS) && (pDb->uiTransType != FLM_UPDATE_TRANS))) { if (RC_BAD( rc = flmAbortDbTrans( pDb))) { goto Exit; } goto Test_Trans; } } else { if( !(uiFlags & FDB_TRANS_GOING_OK)) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } /* See if the transaction should be aborted. */ if( flmCheckBadTrans( pDb)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } /* If we need an update transaction, make sure that is what we currently have going. Also, make sure we are not in read-only mode. */ if ((uiTransType == FLM_UPDATE_TRANS) && (pDb->uiTransType != FLM_UPDATE_TRANS)) { rc = RC_SET( FERR_ILLEGAL_TRANS_OP); goto Exit; } } } else if (!(uiAutoTrans & FLM_AUTO_TRANS)) { rc = RC_SET( FERR_NO_TRANS_ACTIVE); goto Exit; } else { // If we get to this point, we need to start a transaction on the // database. if( RC_BAD( rc = flmBeginDbTrans( pDb, uiTransType, (FLMUINT)(0x00FF & uiAutoTrans), uiTransFlags))) { goto Exit; } if( pbStartedTransRV) { *pbStartedTransRV = TRUE; } if (uiTransType == FLM_UPDATE_TRANS) { pDb->bHadUpdOper = TRUE; } } Exit: // WARNING: The calling routine must call fdbExit or flmExit to unlock // any resources that are locked by fdbInit (even if this fdbInit // returns an error). return( rc); } /**************************************************************************** Desc: This function will unlock an FDB. ****************************************************************************/ void fdbExit( FDB * pDb) { flmAssert( pDb); if( !pDb->pCSContext) { flmAssert( pDb->uiInitNestLevel); pDb->uiInitNestLevel--; if (!pDb->uiInitNestLevel) { if (pDb->pDict && pDb->uiTransType == FLM_NO_TRANS) { f_mutexLock( gv_FlmSysData.hShareMutex); flmUnlinkFdbFromDict( pDb); f_mutexUnlock( gv_FlmSysData.hShareMutex); } pDb->pStats = NULL; } } fdbUnuse( pDb); } /**************************************************************************** Desc: This function is used to determine if a function id corresponds to a cursor function. ****************************************************************************/ FINLINE FLMBOOL IsQueryFunc( FLMUINT uiFuncId) { return( (uiFuncId == FLM_CURSOR_CONFIG || uiFuncId == FLM_CURSOR_NEXT || uiFuncId == FLM_CURSOR_NEXT_DRN || uiFuncId == FLM_CURSOR_PREV || uiFuncId == FLM_CURSOR_PREV_DRN || uiFuncId == FLM_CURSOR_FIRST || uiFuncId == FLM_CURSOR_FIRST_DRN || uiFuncId == FLM_CURSOR_LAST || uiFuncId == FLM_CURSOR_LAST_DRN || uiFuncId == FLM_CURSOR_MOVE_RELATIVE || uiFuncId == FLM_CURSOR_REC_COUNT) ? TRUE : FALSE); } /**************************************************************************** Desc: This function is used to determine if an error should require an update transaction to be aborted. ****************************************************************************/ FINLINE FLMBOOL IsAbortError( FLMUINT uiFuncId, RCODE rc) { return( (rc != FERR_OK && rc != FERR_END && rc != FERR_BOF_HIT && rc != FERR_EOF_HIT && rc != FERR_EXISTS && rc != FERR_NOT_FOUND && rc != FERR_NOT_UNIQUE && rc != FERR_BAD_FIELD_NUM && rc != FERR_ABORT_TRANS && rc != FERR_IO_FILE_LOCK_ERR && rc != FERR_IO_ACCESS_DENIED && rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH && rc != FERR_OLD_VIEW && rc != FERR_PERMISSION && rc != FERR_ILLEGAL_OP && rc != FERR_ILLEGAL_TRANS_OP && rc != FERR_DUPLICATE_DICT_REC && rc != FERR_TIMEOUT && rc != FERR_INDEX_OFFLINE && (rc != FERR_BAD_IX || (!IsQueryFunc( uiFuncId) && uiFuncId != FLM_INDEX_STATUS)) && (rc != FERR_CURSOR_SYNTAX || !IsQueryFunc( uiFuncId))) ? TRUE : FALSE); } /**************************************************************************** Desc: This function checks to see if an update transaction should be forced to abort. It also resets the gedcom memory pool. ****************************************************************************/ void flmExit( eFlmFuncs eFlmFuncId, FDB * pDb, RCODE rc) { // There are a few functions that may not have an FDB if (pDb) { // If this is an update transaction, see if it should be aborted. if (pDb->uiTransType == FLM_UPDATE_TRANS && IsAbortError( eFlmFuncId, rc)) { // Set the abort flag pDb->eAbortFuncId = eFlmFuncId; pDb->AbortRc = rc; } // Don't reset or free the temporary pool if FLAIM func was called // within a user call-back that called another FLAIM functions. if (pDb->uiInFlmFunc == 0) { // Keep the main pool block around inbetween FLAIM calls. pDb->TempPool.poolReset(); } fdbExit( pDb); } } /**************************************************************************** Desc: Logs information about an error ****************************************************************************/ void flmLogError( RCODE rc, const char * pszDoing, const char * pszFileName, FLMINT iLineNumber) { flmLogMessage( F_DEBUG_MESSAGE, FLM_YELLOW, FLM_BLACK, pszFileName ? "Error %s: 0x%04X (%s), File=%s, Line=%d." : "Error %s: 0x%04X (%s).", pszDoing, (unsigned)rc, FlmErrorString( rc), pszFileName ? pszFileName : "", pszFileName ? (int)iLineNumber : 0); } /**************************************************************************** Desc: Logs messages ****************************************************************************/ void flmLogMessage( eLogMessageSeverity eMsgSeverity, eColorType foreground, eColorType background, const char * pszFormat, ...) { FLMINT iLen; f_va_list args; IF_LogMessageClient * pLogMsg = NULL; char * pszMsgBuf = NULL; if( !gv_FlmSysData.pLogger) { return; } if( (pLogMsg = gv_FlmSysData.pLogger->beginMessage( FLM_GENERAL_MESSAGE, eMsgSeverity)) != NULL) { if( RC_OK( f_alloc( 1024, &pszMsgBuf))) { f_va_start( args, pszFormat); iLen = f_vsprintf( pszMsgBuf, pszFormat, &args); f_va_end( args); pLogMsg->changeColor( foreground, background); pLogMsg->appendString( pszMsgBuf); } pLogMsg->endMessage(); pLogMsg->Release(); pLogMsg = NULL; if( pszMsgBuf) { f_free( &pszMsgBuf); } } } /**************************************************************************** Desc: Logs the reason for the "must close" flag being set ****************************************************************************/ FSTATIC void flmLogMustCloseReason( FFILE * pFile, const char * pszFileName, FLMINT iLineNumber) { // Log a message indicating why the "must close" flag was set flmLogMessage( F_DEBUG_MESSAGE, FLM_YELLOW, FLM_BLACK, "Database (%s) must be closed because of a 0x%04X error, File=%s, Line=%d.", (pFile->pszDbPath ? pFile->pszDbPath : ""), (unsigned)pFile->rcMustClose, pszFileName, (int)iLineNumber); } /**************************************************************************** Desc: Checks to see if the database should be closed ****************************************************************************/ RCODE flmCheckDatabaseStateImp( FDB * pDb, const char * pszFileName, FLMINT iLineNumber) { RCODE rc = FERR_OK; if( pDb && pDb->bMustClose) { flmLogMustCloseReason( pDb->pFile, pszFileName, iLineNumber); rc = RC_SET( FERR_CLOSING_DATABASE); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Checks the FFILE state ****************************************************************************/ RCODE flmCheckFFileStateImp( FFILE * pFile, const char * pszFileName, FLMINT iLineNumber) { RCODE rc = FERR_OK; if( pFile && pFile->bMustClose) { flmLogMustCloseReason( pFile, pszFileName, iLineNumber); rc = RC_SET( FERR_CLOSING_DATABASE); goto Exit; } Exit: return( rc); } libflaim-4.9.966/src/fqstack.cpp0000644000175000017500000006112110510774540020015 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query stack // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqstack.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" static FLMUINT PrecedenceTable [FLM_USER_PREDICATE - FLM_AND_OP + 1] = { 2, // FLM_AND_OP 1, // FLM_OR_OP 10, // FLM_NOT_OP 6, // FLM_EQ_OP 6, // FLM_MATCH_OP 6, // FLM_MATCH_BEGIN_OP 6, // FLM_MATCH_END_OP 6, // FLM_CONTAINS_OP 6, // FLM_NE_OP 7, // FLM_LT_OP 7, // FLM_LE_OP 7, // FLM_GT_OP 7, // FLM_GE_OP 5, // FLM_BITAND_OP 3, // FLM_BITOR_OP 4, // FLM_BITXOR_OP 9, // FLM_MULT_OP 9, // FLM_DIV_OP 9, // FLM_MOD_OP 8, // FLM_PLUS_OP 8, // FLM_MINUS_OP 10, // FLM_NEG_OP 0, // FLM_LPAREN_OP 0, // FLM_RPAREN_OP 0, // FLM_UNKNOWN 6 // FLM_USER_PREDICATE }; #define PRECEDENCE( e) (((e) >= FLM_AND_OP && (e) <= FLM_USER_PREDICATE) \ ? PrecedenceTable [(e) - FLM_AND_OP] \ : (FLMUINT)0) /**************************************************************************** Desc : Adds an operator to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddOp( HFCURSOR hCursor, QTYPES eOperator, FLMBOOL bResolveUnknown ) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FQNODE * pTmpQNode; FQNODE * pTmpGraftNode; FQNODE * pTmpChildNode; FLMBOOL bDecrementNestLvl = FALSE; FLMUINT uiFlags = bResolveUnknown ? FLM_RESOLVE_UNK : 0; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // If the operator is a left paren, link it as the last sibling in the // argument list of the current operator. if (eOperator == FLM_LPAREN_OP) { (pCursor->QTInfo.uiNestLvl)++; goto Exit; } // If it is a right paren, find the left paren and close it out if (eOperator == FLM_RPAREN_OP) { if (!pCursor->QTInfo.uiNestLvl) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } (pCursor->QTInfo.uiNestLvl)--; goto Exit; } // If it is not an operator, return an error if (!IS_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // If an operator is not expected, bail out if (!(pCursor->QTInfo.uiExpecting & FLM_Q_OPERATOR) && eOperator != FLM_NEG_OP && eOperator != FLM_NOT_OP) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Make a QNODE and find a place for it in the query tree if (RC_BAD( rc = flmCurMakeQNode( &pCursor->QueryPool, eOperator, NULL, 0, uiFlags, &pTmpQNode))) { goto Exit; } pTmpQNode->uiNestLvl = pCursor->QTInfo.uiNestLvl; // If this is the first operator in the query, set the current operator // to it and graft in the current operand as its child. NOTE: there // should always be a current operand at this point. if (!pCursor->QTInfo.pTopNode) { pCursor->QTInfo.pTopNode = pTmpQNode; pCursor->QTInfo.pCurOpNode = pTmpQNode; if (pCursor->QTInfo.pCurAtomNode) { // If the current operand node is a user predicate, the only // thing that can become its parent is a logical operator. if (GET_QNODE_TYPE( pCursor->QTInfo.pCurAtomNode) == FLM_USER_PREDICATE && !IS_LOG_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } flmCurLinkLastChild( pTmpQNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND; goto Exit; } // Go up the stack until an operator whose nest level or precedence is < // this one's is encountered, then link this one in as the last child for (pTmpChildNode = NULL, pTmpGraftNode = pCursor->QTInfo.pCurOpNode; ; pTmpChildNode = pTmpGraftNode, pTmpGraftNode = pTmpGraftNode->pParent) { if (pTmpGraftNode->uiNestLvl < pTmpQNode->uiNestLvl || (pTmpGraftNode->uiNestLvl == pTmpQNode->uiNestLvl && PRECEDENCE( pTmpGraftNode->eOpType) < PRECEDENCE( eOperator))) { // If the node under which this operator is to be grafted already // has two children, or if its child is at a greater nesting level, // link the child as the last child of this operator. Example: // ((A - B) == C) && (((D + E) * F) == G). // When the '*' operator in this expression is added, it will be // grafted as the last child of the '&&' operator. But the '+' // must first be unlinked from the '&&' and then linked as the child // of the '*'. Otherwise, they will be siblings, and the expression // will be evaluated incorrectly. if (pTmpChildNode && (pTmpChildNode->uiNestLvl > pTmpQNode->uiNestLvl || pTmpChildNode->pPrevSib != NULL || pTmpGraftNode->eOpType == FLM_NEG_OP || pTmpGraftNode->eOpType == FLM_NOT_OP)) { flmCurLinkLastChild( pTmpQNode, pTmpChildNode); } // If this operator is to be grafted into the query tree at the leaf // level, link the current operand as its last child. Examples: // in A * (B + C), we want B to be linked to +; // in A + B * C, we want B linked to *. if (pTmpGraftNode == pCursor->QTInfo.pCurOpNode && eOperator != FLM_NEG_OP && eOperator != FLM_NOT_OP) { // If the current operand node is a user predicate, the only // thing that can become its parent is a logical operator. if (pCursor->QTInfo.pCurAtomNode && GET_QNODE_TYPE( pCursor->QTInfo.pCurAtomNode) == FLM_USER_PREDICATE && !IS_LOG_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } flmCurLinkLastChild( pTmpQNode, pCursor->QTInfo.pCurAtomNode); } flmCurLinkLastChild( pTmpGraftNode, pTmpQNode); break; } if (!pTmpGraftNode->pParent) { pCursor->QTInfo.pTopNode = pTmpQNode; flmCurLinkLastChild( pTmpQNode, pTmpGraftNode); break; } } pCursor->QTInfo.pCurOpNode = pTmpQNode; pCursor->QTInfo.uiExpecting = FLM_Q_OPERAND; Exit: if (bDecrementNestLvl) { (pCursor->QTInfo.uiNestLvl)--; } if (pCursor) { pCursor->rc = rc; } return( rc); } /**************************************************************************** Desc: Add a reference to an embedded user predicate. ****************************************************************************/ RCODE flmCurAddRefPredicate( QTINFO * pQTInfo, FlmUserPredicate * pPredicate ) { RCODE rc = FERR_OK; if (pQTInfo->uiNumPredicates == pQTInfo->uiMaxPredicates) { // Are we still in the embedded array? or have we // done an allocation? if (pQTInfo->uiMaxPredicates == MAX_USER_PREDICATES) { if (RC_BAD( rc = f_calloc( sizeof( FlmUserPredicate *) * (MAX_USER_PREDICATES * 2), &pQTInfo->ppPredicates))) { goto Exit; } // Copy all old pointers from embedded array. f_memcpy( pQTInfo->ppPredicates, &pQTInfo->Predicates [0], MAX_USER_PREDICATES * sizeof( FlmUserPredicate *)); } else { // Reallocate the structure. if (RC_BAD( rc = f_recalloc( sizeof( FlmUserPredicate *) * (pQTInfo->uiNumPredicates * 2), &pQTInfo->ppPredicates))) { goto Exit; } } pQTInfo->uiMaxPredicates *= 2; } pQTInfo->ppPredicates [pQTInfo->uiNumPredicates] = pPredicate; pPredicate->AddRef(); pQTInfo->uiNumPredicates++; Exit: return( rc); } /**************************************************************************** Desc: Adds an embedded user predicate. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddUserPredicate( HFCURSOR hCursor, FlmUserPredicate * pPredicate ) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FQNODE * pQNode; QTYPES eOperator; if (!pCursor || !pPredicate) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Better be expecting an operand. if (!(pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Make the user predicate node. if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, FLM_USER_PREDICATE, NULL, 0, pCursor->QTInfo.uiFlags, &(pCursor->QTInfo.pCurAtomNode)))) { if (pCursor->QTInfo.pCurOpNode) { eOperator = GET_QNODE_TYPE( pCursor->QTInfo.pCurOpNode); // Operator above a user predicate must be a logical operator. if (!IS_LOG_OP( eOperator)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; pQNode = pCursor->QTInfo.pCurAtomNode; pQNode->pQAtom->eType = FLM_USER_PREDICATE; pQNode->pQAtom->val.pPredicate = pPredicate; if (RC_BAD( rc = flmCurAddRefPredicate( &pCursor->QTInfo, pPredicate))) { goto Exit; } // Don't do an addRef on pPredicate because there is really no place // to call a corresponding release. } Exit: if (pCursor) { pCursor->rc = rc; } return( rc); } /**************************************************************************** Desc: Links one FQNODE as the first child of another. ****************************************************************************/ void flmCurLinkFirstChild( FQNODE * pParent, FQNODE * pChild ) { FQNODE * pTmpQNode; // If necessary, unlink the child from a sibling list and link it back in // as the first child. if (pChild->pPrevSib) { pChild->pPrevSib->pNextSib = pChild->pNextSib; if (pChild->pNextSib) { pChild->pNextSib->pPrevSib = pChild->pPrevSib; } for (pTmpQNode = pChild; pTmpQNode->pPrevSib; pTmpQNode = pTmpQNode->pPrevSib) { ; } pChild->pNextSib = pTmpQNode; pTmpQNode->pPrevSib = pChild; } // If there is already a child node, link this one (and any siblings) // before it. if (pParent->pChild) { for (pTmpQNode = pChild; pTmpQNode->pNextSib; pTmpQNode = pTmpQNode->pNextSib) { pTmpQNode->pParent = pParent; } pParent->pChild->pPrevSib = pTmpQNode; pTmpQNode->pNextSib = pParent->pChild; } pParent->pChild = pChild; pChild->pParent = pParent; pChild->pPrevSib = NULL; } /**************************************************************************** Desc: Links one FQNODE as the last child of another. ****************************************************************************/ void flmCurLinkLastChild( FQNODE * pParent, FQNODE * pChild ) { FQNODE * pTmpQNode; // If necessary, unlink the child from any parent or siblings if (pChild->pParent) { if (!pChild->pPrevSib) { pChild->pParent->pChild = pChild->pNextSib; if (pChild->pNextSib) { pChild->pNextSib->pPrevSib = NULL; } } else { pChild->pPrevSib->pNextSib = pChild->pNextSib; if (pChild->pNextSib) { pChild->pNextSib->pPrevSib = pChild->pPrevSib; } } } // Link pChild as the next sibling to the last node in the sibling list // of pParent->pChild if (pParent->pChild) { for (pTmpQNode = pParent->pChild; pTmpQNode->pNextSib; pTmpQNode = pTmpQNode->pNextSib) { ; } pTmpQNode->pNextSib = pChild; pChild->pPrevSib = pTmpQNode; } else { pParent->pChild = pChild; pChild->pPrevSib = NULL; } pChild->pParent = pParent; pChild->pNextSib = NULL; } /**************************************************************************** Desc: Put a value in an FQATOM node - so we can call it from SMI. Can only be used for values, not field paths. Cannot be used for unknown values. ****************************************************************************/ RCODE flmPutValInAtom( void * pAtom, QTYPES eValType, void * pvVal, FLMUINT uiValLen, FLMUINT uiFlags ) { RCODE rc = FERR_OK; FQATOM * pQAtom = (FQATOM *)pAtom; pQAtom->uiFlags = uiFlags; pQAtom->eType = eValType; switch (eValType) { case FLM_BOOL_VAL: pQAtom->val.uiBool = *((FLMUINT *)pvVal); break; case FLM_UINT32_VAL: case FLM_REC_PTR_VAL: pQAtom->val.ui32Val = *((FLMUINT32 *)pvVal); break; case FLM_UINT64_VAL: pQAtom->val.ui64Val = *((FLMUINT64 *)pvVal); break; case FLM_INT32_VAL: pQAtom->val.i32Val = *((FLMINT32 *)pvVal); break; case FLM_INT64_VAL: pQAtom->val.i64Val = *((FLMINT64 *)pvVal); break; case FLM_BINARY_VAL: case FLM_TEXT_VAL: pQAtom->val.pucBuf = (FLMBYTE *)pvVal; pQAtom->uiBufLen = uiValLen; break; case FLM_UNKNOWN: break; default: flmAssert( 0); rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Makes an FQNODE of a given type, and puts a value in it if necessary. ****************************************************************************/ RCODE flmCurMakeQNode( F_Pool * pPool, QTYPES eType, void * pVal, FLMUINT uiValLen, FLMUINT uiFlags, FQNODE * * ppQNode) { FLMUINT * puiTmpPath; FLMUINT * puiPToCPath; FLMUINT * puiFldPath; FLMUINT uiTmpLen = uiValLen; FLMBYTE * pTmpBuf; FLMUINT uiPathCnt; FLMUINT uiCnt; RCODE rc = FERR_OK; FQNODE * pQNode; FQATOM * pQAtom; if( RC_BAD( rc = pPool->poolCalloc( sizeof( FQNODE), (void **)&pQNode))) { goto Exit; } *ppQNode = pQNode; // Always set eOpType to the eType pQNode->eOpType = eType; if (IS_OP( eType)) { pQNode->uiStatus = uiFlags; goto Exit; } if( RC_BAD( rc = pPool->poolCalloc( sizeof( FQATOM), (void **)&pQAtom))) { goto Exit; } pQNode->pQAtom = pQAtom; pQAtom->uiFlags = uiFlags; switch (eType) { case FLM_TEXT_VAL: { if( RC_BAD( rc = pPool->poolAlloc( uiTmpLen + 1, (void **)&pTmpBuf))) { goto Exit; } f_memcpy( pTmpBuf, pVal, uiTmpLen); pTmpBuf[ uiTmpLen ] = '\0'; pQAtom->val.pucBuf = pTmpBuf; pQAtom->uiBufLen = uiTmpLen; break; } case FLM_BOOL_VAL: { pQAtom->val.uiBool = *(FLMUINT *)pVal; break; } case FLM_INT32_VAL: { pQAtom->val.i32Val = *(FLMINT32 *)pVal; break; } case FLM_INT64_VAL: { pQAtom->val.i64Val = *(FLMINT64 *)pVal; break; } case FLM_REC_PTR_VAL: case FLM_UINT32_VAL: { pQAtom->val.ui32Val = *(FLMUINT32 *)pVal; break; } case FLM_UINT64_VAL: { pQAtom->val.ui64Val = *(FLMUINT64 *)pVal; break; } case FLM_FLD_PATH: { for (uiPathCnt = 0; ((FLMUINT *)pVal)[ uiPathCnt]; uiPathCnt++) { if (uiPathCnt > GED_MAXLVLNUM + 1) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } if( RC_BAD( rc = pPool->poolCalloc( ((FLMUINT)(uiPathCnt + 1) * 2 * sizeof( FLMUINT)), (void **)&puiTmpPath))) { goto Exit; } puiPToCPath = &puiTmpPath [uiPathCnt + 1]; puiFldPath = (FLMUINT *)pVal; for (uiCnt = 0; uiCnt < uiPathCnt; uiCnt++) { puiTmpPath[ uiPathCnt - uiCnt - 1] = puiFldPath[ uiCnt]; puiPToCPath [uiCnt] = puiFldPath [uiCnt]; } pQAtom->val.QueryFld.puiFldPath = puiTmpPath; pQAtom->val.QueryFld.puiPToCPath = puiPToCPath; break; } case FLM_BINARY_VAL: { if( RC_BAD( rc = pPool->poolAlloc( uiTmpLen, (void **)&pTmpBuf))) { goto Exit; } f_memcpy( pTmpBuf, pVal, uiTmpLen); pQAtom->val.pucBuf = pTmpBuf; pQAtom->uiBufLen = uiTmpLen; break; } case FLM_USER_PREDICATE: { break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } pQAtom->eType = eType; Exit: return( rc); } /**************************************************************************** Desc: Grafts FQNODE onto a passed-in query tree as the right branch of a new root node which contains the passed-in operator. Ret: ****************************************************************************/ RCODE flmCurGraftNode( F_Pool * pPool, FQNODE * pQNode, QTYPES eGraftOp, FQNODE * * ppQTree) { FQNODE * pTmpQNode; RCODE rc = FERR_OK; if (*ppQTree == NULL) { *ppQTree = pQNode; goto Exit; } if (RC_BAD( rc = flmCurMakeQNode( pPool, eGraftOp, NULL, 0, 0, &pTmpQNode))) { goto Exit; } flmCurLinkLastChild( pTmpQNode, *ppQTree); flmCurLinkLastChild( pTmpQNode, pQNode); *ppQTree = pTmpQNode; Exit: return( rc); } /**************************************************************************** Desc : Adds a value to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddValue( HFCURSOR hCursor, QTYPES eValType, void * pVal, FLMUINT uiValLen ) { RCODE rc = FERR_OK; FLMUINT uiVal; void * pTmpVal = pVal; CURSOR * pCursor = (CURSOR *)hCursor; F_Pool pool; FLMBOOL bPoolInitialized = FALSE; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } switch (eValType) { // Convert all string types to FLM_TEXT_VALUE // in order to handle pure unicode coming in. case FLM_UNICODE_VAL: case FLM_STRING_VAL: { NODE node; f_memset( &node, 0, sizeof(NODE)); pool.poolInit( 512); bPoolInitialized = TRUE; rc = (eValType == FLM_UNICODE_VAL) ? GedPutUNICODE( &pool, &node, (FLMUNICODE *) pVal) : GedPutNATIVE( &pool, &node, (const char *)pVal); if (RC_BAD( rc)) { goto Exit; } pTmpVal = GedValPtr( &node); uiValLen = GedValLen( &node); eValType = FLM_TEXT_VAL; break; } case FLM_BOOL_VAL: if (!pVal) { uiVal = FLM_UNK; } else { FLMBOOL bTrueFalse = (FLMBOOL)*(FLMBOOL *)pVal; uiVal = (bTrueFalse) ? FLM_TRUE : FLM_FALSE; } pTmpVal = &uiVal; eValType = FLM_BOOL_VAL; break; case FLM_INT32_VAL: case FLM_UINT32_VAL: case FLM_REC_PTR_VAL: case FLM_UINT64_VAL: case FLM_INT64_VAL: case FLM_TEXT_VAL: case FLM_BINARY_VAL: // pTmpVal is already pointing to pVal, and // eValType does not need to be changed. break; default: flmAssert( 0); rc = RC_SET( FERR_CURSOR_SYNTAX); break; } if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, eValType, pTmpVal, uiValLen, pCursor->QTInfo.uiFlags, &(pCursor->QTInfo.pCurAtomNode)))) { if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } if (bPoolInitialized) { pool.poolFree(); } return( rc); } /**************************************************************************** Desc: Adds a field ID to the selection criteria of a given cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddField( HFCURSOR hCursor, FLMUINT uiFldId, FLMUINT uiFlags) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FQNODE * pTmpQNode; FLMUINT uiPath [2]; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (uiFldId == FLM_RECID_FIELD) { uiFlags |= FLM_SINGLE_VALUED; } // Set up a field path with one element in it. uiPath [0] = uiFldId; uiPath [1] = 0; if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, FLM_FLD_PATH, &uiPath [0], 0, pCursor->QTInfo.uiFlags, &pTmpQNode))) { pTmpQNode->pQAtom->uiFlags |= uiFlags; pCursor->QTInfo.pCurAtomNode = pTmpQNode; if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } return( rc); } /**************************************************************************** Desc: Adds a field path to the selection criteria of a given cursor. A field path is the fully qualified context of a field within a record. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddFieldPath( HFCURSOR hCursor, FLMUINT * puiFldPath, FLMUINT uiFlags) { RCODE rc = FERR_OK; FQNODE * pTmpQNode; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, FLM_FLD_PATH, puiFldPath, 0, pCursor->QTInfo.uiFlags, &pTmpQNode))) { pTmpQNode->pQAtom->uiFlags |= uiFlags; pCursor->QTInfo.pCurAtomNode = pTmpQNode; if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } return( rc); } /**************************************************************************** Desc: Adds a field path to the selection criteria of a given cursor - with a callback to retrieve the field. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorAddFieldCB( HFCURSOR hCursor, FLMUINT * puiFldPath, FLMUINT uiFlags, FLMBOOL bValidateOnly, CURSOR_GET_FIELD_CB fnGetField, void * pvUserData, FLMUINT uiUserDataLen) { RCODE rc = FERR_OK; FQNODE * pTmpQNode; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (RC_BAD( rc = pCursor->rc)) { goto Exit; } // If a read operation has already been performed on this query, no // selection criteria may be added. if (pCursor->bOptimized) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!( pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (RC_OK( rc = flmCurMakeQNode( &pCursor->QueryPool, FLM_FLD_PATH, puiFldPath, 0, pCursor->QTInfo.uiFlags, &pTmpQNode))) { FQATOM * pQAtom = pTmpQNode->pQAtom; pQAtom->val.QueryFld.fnGetField = fnGetField; pQAtom->val.QueryFld.bValidateOnly = bValidateOnly; if (pvUserData && uiUserDataLen) { if( RC_BAD( rc = pCursor->QueryPool.poolAlloc( uiUserDataLen, (void **)&pQAtom->val.QueryFld.pvUserData))) { goto Exit; } f_memcpy( pQAtom->val.QueryFld.pvUserData, pvUserData, uiUserDataLen); pQAtom->val.QueryFld.uiUserDataLen = uiUserDataLen; } else { pQAtom->val.QueryFld.pvUserData = NULL; pQAtom->val.QueryFld.uiUserDataLen = 0; } pQAtom->uiFlags |= uiFlags; pCursor->QTInfo.pCurAtomNode = pTmpQNode; if (pCursor->QTInfo.pCurOpNode) { flmCurLinkLastChild( pCursor->QTInfo.pCurOpNode, pCursor->QTInfo.pCurAtomNode); } pCursor->QTInfo.uiExpecting &= ~FLM_Q_OPERAND; pCursor->QTInfo.uiExpecting |= FLM_Q_OPERATOR; } Exit: if (pCursor) { pCursor->rc = rc; } return( rc); } libflaim-4.9.966/src/imonrec.cpp0000644000175000017500000007251710510774540020030 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for editing database records via HTTP web pages. // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonrec.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /********************************************************* Desc: This function handles the ProcessRecord request. **********************************************************/ RCODE F_ProcessRecordPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_Session * pFlmSession = m_pFlmSession; HFDB hDb; FLMUINT uiContainer; FLMUINT uiDrn; char szTmp [128]; char * pTmp = &szTmp[ 0]; FLMBOOL bReadOnly; char szDbKey[ F_SESSION_DB_KEY_LEN]; // We need to check our session. if (!pFlmSession) { printErrorPage( m_uiSessionRC, TRUE, "No session available for this request"); goto Exit; } // There are several fields on the form that we require, regardless of // the action that is being taken. // The hDb (Database File Handle) if (RC_BAD( rc = getDatabaseHandleParam( uiNumParams, ppszParams, pFlmSession, &hDb, szDbKey))) { printErrorPage( rc, TRUE, "Invalid Database Handle"); goto Exit; } // ReadOnly flag szTmp[ 0] = '\0'; bReadOnly = TRUE; // The value may be in the URL or in a form. if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "ReadOnly", sizeof( szTmp), szTmp))) { getFormValueByName( "ReadOnly", &pTmp, sizeof( szTmp), NULL); } if (szTmp[ 0]) { if (f_stricmp( szTmp, "FALSE") == 0) { bReadOnly = FALSE; } } // The Record Drn szTmp[ 0] = '\0'; if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "DRN", sizeof( szTmp), szTmp))) { getFormValueByName( "DRN", &pTmp, sizeof( szTmp), NULL); } if (szTmp[ 0]) { uiDrn = f_atoud( szTmp); } else { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Record DRN is Missing"); goto Exit; } // The Container number szTmp[ 0] = '\0'; if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "container", sizeof( szTmp), szTmp))) { getFormValueByName( "container", &pTmp, sizeof( szTmp), NULL); } if (szTmp[ 0]) { uiContainer = f_atoud( szTmp); } else { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Record Container is missing"); goto Exit; } // The Action to perform szTmp[ 0] = '\0'; if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Action", sizeof( szTmp), szTmp))) { getFormValueByName( "Action", &pTmp, sizeof( szTmp), NULL); } if (szTmp[ 0]) { if (f_stricmp( szTmp, "Add") == 0) { addRecord( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "New") == 0) { newRecord( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "Delete") == 0) { deleteRecord( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "Modify") == 0) { modifyRecord( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "Retrieve") == 0) { retrieveRecord( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "InsertSibling") == 0) { insertField( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly, INSERT_NEXT_SIB); } else if (f_stricmp( szTmp, "InsertChild") == 0) { insertField( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly, INSERT_FIRST_CHILD); } else if (f_stricmp( szTmp, "Copy") == 0) { copyField( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else if (f_stricmp( szTmp, "Clip") == 0) { clipField( pFlmSession, hDb, szDbKey, uiDrn, uiContainer, bReadOnly); } else { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Invalid Action on Form"); goto Exit; } } Exit: fnEmit(); return( rc); } /********************************************************* Desc: Adds the record to the database. The fields in the current form identify the data to add. **********************************************************/ void F_ProcessRecordPage::addRecord( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; FLMUINT uiAutoTrans; // We first need to reconsitute the record. if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { goto Exit; } uiAutoTrans = FLM_AUTO_TRANS | FLM_NO_TIMEOUT; if (RC_BAD( rc = FlmRecordAdd( hDb, uiContainer, &uiDrn, pRec, uiAutoTrans))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly,rc); goto Exit; } // Retrieve the new record to display. retrieveRecord( pFlmSession, hDb, pszDbKey, uiDrn, uiContainer, bReadOnly, FO_EXACT); Exit: if( pRec) { pRec->Release(); } return; } /********************************************************* Desc: Creates a new record. This is a record that does not exist in the database. Do not store the record yet. **********************************************************/ void F_ProcessRecordPage::newRecord( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; char szTmp[ 128]; F_NameTable * pNameTable = NULL; FLMUINT uiType; FLMUINT uiTagNum; void * pvField = NULL; char * pTmp = &szTmp[0]; // Start with a new record. if( (pRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); printErrorPage( rc, TRUE, "Failed to create new record"); goto Exit; } pRec->setID( uiDrn); pRec->setContainerID( uiContainer); // Get the tag number for the root field. if (RC_BAD( rc = getFormValueByName( "fieldlist", &pTmp, sizeof( szTmp), NULL))) { printErrorPage( rc, TRUE, "Root field type could not be determined"); goto Exit; } uiTagNum = f_atoud( szTmp); if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) { printErrorPage( rc, TRUE, "Could not get a Name Table"); goto Exit; } // Get the field type from the name table. if (!pNameTable->getFromTagNum( uiTagNum, NULL, szTmp, sizeof( szTmp), NULL, &uiType)) { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Invalid field selected"); goto Exit; } // Create the root field. if (RC_BAD( rc = pRec->insertLast( 0, uiTagNum, uiType, &pvField))) { printErrorPage( rc, TRUE, "Error occurred inserting field into record"); goto Exit; } if (RC_BAD( rc)) { goto Exit; } // Retrieve the new record to display. displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); Exit: if( pRec) { pRec->Release(); } return; } /********************************************************* Desc: Modifies (updates) the record identified by the Drn & Container and presents it to the client. **********************************************************/ void F_ProcessRecordPage::modifyRecord( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; FLMUINT uiAutoTrans; // We first need to reconsitute the record. if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { goto Exit; } uiAutoTrans = FLM_AUTO_TRANS | FLM_NO_TIMEOUT; if (RC_BAD( rc = FlmRecordModify( hDb, uiContainer, uiDrn, pRec, uiAutoTrans))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } // Retrieve the new record to display. retrieveRecord( pFlmSession, hDb, pszDbKey, uiDrn, uiContainer, bReadOnly, FO_EXACT); Exit: if( pRec) { pRec->Release(); } return; } /********************************************************* Desc: Builds a new record from the data in the form **********************************************************/ RCODE F_ProcessRecordPage::constructRecord( FLMUINT uiDrn, FLMUINT uiContainer, FlmRecord ** ppRec, HFDB hDb) { RCODE rc = FERR_OK; FlmRecord * pRec; char szTmp[ 128]; char * pTmp; FLMUINT uiFieldCount; FLMUINT uiFieldCounter; char * pszFldValue = NULL; flmAssert( ppRec); // Start with a new record. if( (pRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } pRec->setID( uiDrn); pRec->setContainerID( uiContainer); pTmp = &szTmp[ 0]; getFormValueByName( "FieldCount", &pTmp, sizeof( szTmp), NULL); if (szTmp[ 0]) { uiFieldCount = f_atoud( szTmp); } else { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Field Count missing or invalid"); goto Exit; } // Now we need to get each of the fields, beginning at level 0 for ( uiFieldCounter = 0; uiFieldCounter < uiFieldCount; uiFieldCounter++) { // Retrieve the next field. FLMUINT uiLevel; FLMUINT uiType; FLMUINT uiTag; void * pvField = NULL; if (RC_BAD( rc = extractFieldInfo( uiFieldCounter, &pszFldValue, &uiLevel, &uiType, &uiTag))) { printErrorPage( rc, TRUE, "Error occurred retrieving field data from form"); goto Exit; } // Insert the field into the record. if (RC_BAD( rc = pRec->insertLast( uiLevel, uiTag, uiType, &pvField))) { printErrorPage( rc, TRUE, "Error occurred inserting field into record"); goto Exit; } // Set the data value switch( uiType) { case FLM_TEXT_TYPE: { // Store as unicode rc = storeUnicodeField( pRec, pvField, pszFldValue); break; } case FLM_NUMBER_TYPE: { // Store as UINT or INT rc = storeNumberField( pRec, pvField, pszFldValue); break; } case FLM_BINARY_TYPE: { // Store after converting from hex display rc = storeBinaryField( pRec, pvField, pszFldValue); break; } case FLM_CONTEXT_TYPE: { // Store as a RecPointer if (pszFldValue && *pszFldValue != '\0') { rc = pRec->setRecPointer( pvField, (unsigned long)f_atoud( pszFldValue)); } break; } case FLM_BLOB_TYPE: { // Store a Blob object... rc = storeBlobField( pRec, pvField, pszFldValue, hDb); break; } } if (RC_BAD( rc)) { goto Exit; } f_free( &pszFldValue); } Exit: if (RC_BAD(rc)) { if (pRec) { pRec->Release(); pRec = NULL; } } if (pszFldValue) { f_free( &pszFldValue); } *ppRec = pRec; return( rc); } /********************************************************* Desc: **********************************************************/ RCODE F_ProcessRecordPage::extractFieldInfo( FLMUINT uiFieldCounter, char ** ppucBuf, FLMUINT * puiLevel, FLMUINT * puiType, FLMUINT * puiTag) { RCODE rc = FERR_OK; char szField[50]; char szTmp[ 128]; char * pTmp; // field value first. This call will allocate a buffer for us that will // need to be freed later! If we get an FERR_NOT_FOUND error, // then ignore it. f_sprintf( (char *)szField, "field%u", (unsigned)uiFieldCounter); *ppucBuf = NULL; if (RC_OK( rc = getFormValueByName( szField, ppucBuf, 0, NULL))) { fcsDecodeHttpString( *ppucBuf); } else if (rc != FERR_NOT_FOUND) { goto Exit; } pTmp = &szTmp[ 0]; // fieldLevel next. f_sprintf( (char *)szField, "fieldLevel%u", (unsigned)uiFieldCounter); if (RC_BAD( rc = getFormValueByName( szField, &pTmp, sizeof( szTmp), NULL))) { goto Exit; } *puiLevel = f_atoud( szTmp); // fieldType next f_sprintf( (char *)szField, "fieldType%u", (unsigned)uiFieldCounter); if (RC_BAD( rc = getFormValueByName( szField, &pTmp, sizeof( szTmp), NULL))) { goto Exit; } *puiType = f_atoud( szTmp); // fieldTag next f_sprintf( (char *)szField, "fieldTag%u", (unsigned)uiFieldCounter); if (RC_BAD( rc = getFormValueByName( szField, &pTmp, sizeof( szTmp), NULL))) { goto Exit; } *puiTag = f_atoud( szTmp); Exit: return( rc); } /********************************************************* Desc: Deletes the record identified by the Drn & Container and presents it to the client. **********************************************************/ void F_ProcessRecordPage::deleteRecord( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; RCODE uiRc; FLMUINT uiAutoTrans; FlmRecord * pRec = NULL; if (RC_OK( rc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, FO_EXACT, (FlmRecord **)&pRec, &uiDrn))) { uiAutoTrans = FLM_AUTO_TRANS | FLM_NO_TIMEOUT; if (RC_BAD( rc = FlmRecordDelete( hDb, uiContainer, uiDrn, uiAutoTrans))) { uiRc = rc; if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { printErrorPage( rc, TRUE, "Failed to delete record"); goto Exit; } displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, uiRc); goto Exit; } } else { uiRc = rc; if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { printErrorPage( rc, TRUE, "Failed to delete record. Invalid Record"); goto Exit; } displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, uiRc); goto Exit; } // This will present an empty record page since the record is no longer there. retrieveRecord( pFlmSession, hDb, pszDbKey, 0, uiContainer, bReadOnly); Exit: if (pRec) { pRec->Release(); } return; } /********************************************************* Desc: Retrieves the record identified by the Drn & Container and presents it to the client. **********************************************************/ void F_ProcessRecordPage::retrieveRecord( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly, FLMUINT uiFlag) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; char szTmp[ 20]; char * pTmp = &szTmp[ 0]; FLMUINT uiFlags = FO_EXACT; if (uiFlag == 0xFFFFFFFF) { // Get the flags if (RC_OK( rc = getFormValueByName( "flags", &pTmp, sizeof( szTmp), NULL))) { uiFlags = f_atoud( szTmp); } } else { uiFlags = uiFlag; } rc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, uiFlags, (FlmRecord **)&pRec, &uiDrn); if ((rc == FERR_NOT_FOUND) && (uiDrn == 0)) { rc = FERR_OK; } displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); if (pRec) { pRec->Release(); } return; } /********************************************************* Desc: Insert a new field at the specified location. **********************************************************/ void F_ProcessRecordPage::insertField( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly, FLMUINT uiInsertAt) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; char szTmp[ 128]; F_NameTable * pNameTable = NULL; FLMUINT uiType; FLMUINT uiTagNum; void * pvField = NULL; char * pTmp = &szTmp[0]; FLMUINT uiFldCnt; FLMUINT uiSelectedField; FLMUINT uiLoop; // Reconstruct the record. if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { goto Exit; } // Get the field count so we know whether to look for the selected field. if (RC_BAD( rc = getFormValueByName( "FieldCount", &pTmp, sizeof( szTmp), NULL))) { printErrorPage( rc, TRUE, "Could not retrieve the record field count"); goto Exit; } uiFldCnt = f_atoud( szTmp); if (uiFldCnt == 1) { uiSelectedField = 0; } else { // We need to get the actual selected field if (RC_BAD( rc = getFormValueByName( "radioSel", &pTmp, sizeof( szTmp), NULL))) { printErrorPage( rc, TRUE, "Could not retrieve the selected field"); goto Exit; } uiSelectedField = f_atoud( szTmp); } // Get the tag number for the new field. if (RC_BAD( rc = getFormValueByName( "fieldlist", &pTmp, sizeof( szTmp), NULL))) { printErrorPage( rc, TRUE, "Selected field type could not be determined"); goto Exit; } uiTagNum = f_atoud( szTmp); if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) { printErrorPage( rc, TRUE, "Could not get a Name Table"); goto Exit; } // Get the new field type from the name table. if (!pNameTable->getFromTagNum( uiTagNum, NULL, szTmp, sizeof( szTmp), NULL, &uiType)) { rc = RC_SET( FERR_INVALID_PARM); printErrorPage( rc, TRUE, "Invalid field selected"); goto Exit; } // Start at the root pvField = pRec->root(); for (uiLoop = 0; uiLoop < uiSelectedField; uiLoop++) { pvField = pRec->next( pvField); } // Insert the child if (RC_BAD( rc = pRec->insert( pvField, uiInsertAt, uiTagNum, uiType, &pvField))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } // Display the new record to display. displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); Exit: if( pRec) { pRec->Release(); } return; } /********************************************************* Desc: **********************************************************/ void F_ProcessRecordPage::copyField( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; char szTmp[ 128]; void * pvOrigField = NULL; void * pvMarkerField = NULL; char * pTmp = &szTmp[0]; FLMUINT uiFldCnt; FLMUINT uiSelectedField; FLMUINT uiLoop; // Reconstruct the record. if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { goto Exit; } // Get the field count so we know whether to look for the selected field. if (RC_BAD( rc = getFormValueByName( "FieldCount", &pTmp, sizeof( szTmp), NULL))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } uiFldCnt = f_atoud( szTmp); if (uiFldCnt == 1) { uiSelectedField = 0; } else { // We need to get the actual selected field if (RC_BAD( rc = getFormValueByName( "radioSel", &pTmp, sizeof( szTmp), NULL))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } uiSelectedField = f_atoud( szTmp); } // Start at the root pvOrigField = pRec->root(); // Scan to the selected field. for ( uiLoop = 0; uiLoop < uiSelectedField; uiLoop++) { pvOrigField = pRec->next( pvOrigField); } // Now find where to begin inserting the copy of this field. pvMarkerField = pRec->nextSibling( pvOrigField); if (!pRec->isLast( pvOrigField) && pvMarkerField == NULL && !pRec->hasChild( pvOrigField)) { pvMarkerField = pRec->next( pvOrigField); } // Copy/Insert fields from the OrigField to the MarkerField. If the MarkerField is // NULL, then add to the end of the record (i.e. after the last field). if (RC_BAD( rc = copyFieldsFromTo( pRec, pvOrigField, pvMarkerField))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } // Retrieve the new record to display. displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); Exit: if( pRec) { pRec->Release(); } return; } /********************************************************* Desc: **********************************************************/ RCODE F_ProcessRecordPage::copyFieldsFromTo( FlmRecord * pRec, void * pvOrigField, void * pvMarkerField) { RCODE rc = FERR_OK; const FLMBYTE * pSrcData = NULL; FLMBYTE * pDestData = NULL; void * pvField = NULL; void * pvSrcField = NULL; FLMUINT uiFldCount; FLMUINT uiType; FLMUINT uiFldId; FLMUINT uiLength; FLMUINT uiSrcLevel; FLMUINT uiTargetLevel; for( uiFldCount = 0, pvField = pvOrigField; pvField != pvMarkerField; uiFldCount++) { pvField = pRec->next( pvField); } pvSrcField = pvOrigField; for (pvField = pvOrigField ; uiFldCount > 0; uiFldCount--) { uiFldId = pRec->getFieldID( pvSrcField); uiType = pRec->getDataType( pvSrcField); uiLength = pRec->getDataLength( pvSrcField); uiSrcLevel = pRec->getLevel( pvSrcField); uiTargetLevel = pRec->getLevel( pvField); if (uiSrcLevel == uiTargetLevel) { // Insert as the next sibling if (RC_BAD( rc = pRec->insert( pvField, INSERT_NEXT_SIB, uiFldId, uiType, &pvField))) { goto Exit; } } else if (uiSrcLevel > uiTargetLevel) { if (RC_BAD( rc = pRec->insert( pvField, INSERT_LAST_CHILD, uiFldId, uiType, &pvField))) { goto Exit; } } else // if (uiTargetLevel > uiSrcLevel) { pvField = pRec->parent( pvField); // Insert as the next sibling if( RC_BAD( rc = pRec->insert( pvField, INSERT_NEXT_SIB, uiFldId, uiType, &pvField))) { goto Exit; } } pSrcData = pRec->getDataPtr( pvSrcField); if( RC_BAD( rc = pRec->allocStorageSpace( pvField, uiType, uiLength, 0, 0, 0, &pDestData, NULL))) { goto Exit; } f_memcpy( pDestData, pSrcData, uiLength); pvSrcField = pRec->next( pvSrcField); } Exit: return( rc); } /********************************************************* Desc: **********************************************************/ void F_ProcessRecordPage::clipField( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FLMUINT uiDrn, FLMUINT uiContainer, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; char szTmp[ 128]; void * pvField = NULL; char * pTmp = &szTmp[0]; FLMUINT uiFldCnt; FLMUINT uiSelectedField; FLMUINT uiLoop; // Reconstruct the record. if (RC_BAD( rc = constructRecord( uiDrn, uiContainer, &pRec, hDb))) { goto Exit; } // Get the field count so we know whether to look for the selected field. if (RC_BAD( rc = getFormValueByName( "FieldCount", &pTmp, sizeof( szTmp), NULL))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } uiFldCnt = f_atoud( szTmp); if (uiFldCnt == 1) { uiSelectedField = 0; } else { // We need to get the actual selected field if (RC_BAD( rc = getFormValueByName( "radioSel", &pTmp, sizeof( szTmp), NULL))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } uiSelectedField = f_atoud( szTmp); } // Start at the root pvField = pRec->root(); // Scan to the selected field. for ( uiLoop = 0; uiLoop < uiSelectedField; uiLoop++) { pvField = pRec->next( pvField); } if (RC_BAD( rc = pRec->remove( pvField))) { displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); goto Exit; } // Display the new record to display. displayRecordPage( pFlmSession, hDb, pszDbKey, pRec, bReadOnly, rc); Exit: if (pRec) { pRec->Release(); } return; } /********************************************************* Desc: Converts a string in the form XX XX XX XX (a hex representation of a binary stream to a binary stream again, then stores it in the provided field. **********************************************************/ RCODE F_ProcessRecordPage::storeBinaryField( FlmRecord * pRec, void * pvField, const char * pszFldValue) { RCODE rc = FERR_OK; F_DynamicBuffer * pBuf = NULL; const char * pszTmp; FLMBOOL bHaveFirstNibble = FALSE; FLMBYTE ucVal = 0; FLMUINT uiNibble; if (pszFldValue == '\0' || *pszFldValue == '\0') { goto Exit; } if ((pBuf = f_new F_DynamicBuffer) == NULL) { rc = RC_SET( FERR_MEM); printErrorPage( rc, TRUE, "Failed to allocate dynamic buffer to store binary field"); goto Exit; } pszTmp = pszFldValue; while( *pszTmp) { if (*pszTmp >= '0' && *pszTmp <= '9') { uiNibble = (FLMUINT)(*pszTmp - '0'); } else if (*pszTmp >= 'a' && *pszTmp <= 'f') { uiNibble = (FLMUINT)(*pszTmp - 'a' + 10); } else if (*pszTmp >= 'A' && *pszTmp <= 'F') { uiNibble = (FLMUINT)(*pszTmp - 'A' + 10); } else { pszTmp++; continue; } if (bHaveFirstNibble) { ucVal += (FLMBYTE)uiNibble; if (RC_BAD( rc = pBuf->addChar( ucVal))) { printErrorPage( rc, TRUE, "Failed to convert binary hex stream"); goto Exit; } bHaveFirstNibble = FALSE; } else { ucVal = (FLMBYTE)(uiNibble << 4); bHaveFirstNibble = TRUE; } pszTmp++; } // See if we ended on an odd number of nibbles. if (bHaveFirstNibble) { if (RC_BAD( rc = pBuf->addChar( ucVal))) { printErrorPage( rc, TRUE, "Failed to convert binary hex stream"); goto Exit; } } if (pBuf->getBufferSize()) { if (RC_BAD( rc = pRec->setBinary( pvField, (void *)pBuf->printBuffer(), pBuf->getBufferSize()))) { printErrorPage( rc, TRUE, "Failed to set BINARY value"); goto Exit; } } Exit: if (pBuf) { pBuf->Release(); } return( rc); } /********************************************************* Desc: **********************************************************/ RCODE F_ProcessRecordPage::storeUnicodeField( FlmRecord * pRec, void * pvField, const char * pszFldValue) { RCODE rc = FERR_OK; FLMUNICODE * puzBuf = NULL; FLMUINT uiBufSize = 0; FLMUINT uiLen; // If there is no data, then just return. if (pszFldValue == '\0' || *pszFldValue == '\0') { goto Exit; } if (RC_BAD( rc = tokenGetUnicode( pszFldValue, (void **)&puzBuf, &uiLen, &uiBufSize))) { printErrorPage( rc, TRUE, "Failed to parse UNICODE from ASCII buffer"); goto Exit; } if (RC_BAD( rc = pRec->setUnicode( pvField, puzBuf))) { printErrorPage( rc, TRUE, "Failed to set UNICODE value"); goto Exit; } Exit: if (puzBuf) { f_free( &puzBuf); } return( rc); } /********************************************************* Desc: **********************************************************/ RCODE F_ProcessRecordPage::storeNumberField( FlmRecord * pRec, void * pvField, const char * pszFldValue) { RCODE rc = FERR_OK; FLMUINT uiVal; FLMINT iVal; if (pszFldValue == '\0' || *pszFldValue == '\0') { goto Exit; } // If this is a negative value, then we will store it as an INT if (*pszFldValue == '-') { iVal = f_atoi( pszFldValue); if (RC_BAD( rc = pRec->setINT( pvField, iVal))) { printErrorPage( rc, TRUE, "Failed to set INT field in record"); goto Exit; } } else { uiVal = f_atoud( pszFldValue); if (RC_BAD( rc = pRec->setUINT( pvField, uiVal))) { printErrorPage( rc, TRUE, "Failed to set UINT field in record"); goto Exit; } } Exit: return( rc); } /********************************************************* Desc: **********************************************************/ RCODE F_ProcessRecordPage::storeBlobField( FlmRecord * pRec, void * pvField, const char * pszFldValue, HFDB hDb) { RCODE rc = FERR_OK; FlmBlob * pBlob = NULL; if (pszFldValue == '\0' || *pszFldValue == '\0') { goto Exit; } if ((pBlob = f_new FlmBlobImp) == NULL) { rc = RC_SET( FERR_MEM); printErrorPage( rc, TRUE, "Failed to allocate new Blob object"); goto Exit; } if(RC_BAD( rc = pBlob->referenceFile( hDb, pszFldValue, TRUE))) { printErrorPage( rc, TRUE, "Failed to create new Blob object"); goto Exit; } if(RC_BAD( rc = pRec->setBlob( pvField, pBlob))) { printErrorPage( rc, TRUE, "Failed to store Blob object in Record"); goto Exit; } Exit: if (pBlob) { pBlob->Release(); pBlob = NULL; } return( rc); } /********************************************************* Desc: **********************************************************/ void F_ProcessRecordPage::displayRecordPage( F_Session * pFlmSession, HFDB hDb, const char * pszDbKey, FlmRecord * pRec, FLMBOOL bReadOnly, RCODE uiRc) { RCODE rc = FERR_OK; FLMUINT uiContext = 0; F_NameTable * pNameTable = NULL; char szTmp[128]; char * pTmp = &szTmp[0]; FLMUINT uiTagNum = 0; FLMUINT uiFlags = FO_EXACT; // Get the name table. if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) { printErrorPage( rc, TRUE, "Could not get a Name Table"); goto Exit; } // Get the tag number for the currently selected field. if (RC_OK( rc = getFormValueByName( "fieldlist", &pTmp, sizeof( szTmp), NULL))) { uiTagNum = f_atoud( szTmp); } // Get the flags if (RC_OK( rc = getFormValueByName( "flags", &pTmp, sizeof( szTmp), NULL))) { uiFlags = f_atoud( szTmp); } // Begin the document. stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "Database iMonitor - Record Display\n"); printRecordStyle(); printStyle(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printTableStart( "Record Manager (Traditional)", 1, 100); printTableEnd(); if (RC_BAD( uiRc)) { fnPrintf( m_pHRequest, "Return Code = 0x%04X, %s\n", (unsigned)uiRc, FlmErrorString( uiRc)); } printRecord( pszDbKey, pRec, pNameTable, &uiContext, bReadOnly, uiTagNum, uiFlags); fnPrintf( m_pHRequest, "\n"); Exit: return; } libflaim-4.9.966/src/imonbase.cpp0000644000175000017500000022313310510774540020161 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Base class for monitoring code. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonbase.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" #define RESP_WRITE_BUF_SIZE 1024 #define FLM_SESSION_ID_NAME "flmsessionid" #define MAX_FIELD_SIZE(uiSize) \ (uiSize > 100 ? 100 : (uiSize < 20 ? 20 : uiSize)) /**************************************************************************** Desc: Outputs a javascript function that, when called, causes a new window to open and a page to be displayed in it. ****************************************************************************/ void F_WebPage::popupFrame( void) { fnPrintf( m_pHRequest, "\n"); } /****************************************************************************** Desc: This method will extract the value of the parameter in the form of PARAMETER=VALUE *******************************************************************************/ RCODE F_WebPage::ExtractParameter( FLMUINT uiNumParams, const char ** ppszParams, const char * pszParamName, FLMUINT uiParamLen, char* pszParamValue) { RCODE rc = FERR_OK; FLMUINT uiLoop; FLMUINT uiParamNameLen; const char* pszTemp; FLMBOOL bFound = FALSE; uiParamNameLen = f_strlen( pszParamName); for (uiLoop = 0; uiLoop < uiNumParams; uiLoop++) { if( f_strncmp( (char*) ppszParams[uiLoop], pszParamName, uiParamNameLen) == 0 && (ppszParams[uiLoop][uiParamNameLen] == '\0' || ppszParams[uiLoop][uiParamNameLen] == '=')) { pszTemp = &ppszParams[uiLoop][uiParamNameLen]; if (*pszTemp == '=') { // Skip past the equal sign pszTemp++; f_strncpy( pszParamValue, pszTemp, uiParamLen - 1); // See if the param was too long to store if (f_strlen( pszTemp) >= uiParamLen) { pszParamValue[uiParamLen] = '\0'; rc = RC_SET( FERR_MEM); } } else { *pszParamValue = 0; } bFound = TRUE; break; } } return (bFound ? rc : RC_SET( FERR_NOT_FOUND)); } /**************************************************************************** Desc: This method will detect the presence of a parameter in the form PARAMETER - no value will be checked. A TRUE or FALSE value will be returned. *****************************************************************************/ FLMBOOL F_WebPage::DetectParameter( FLMUINT uiNumParams, const char ** ppszParams, const char * pszParamName) { for (FLMUINT uiLoop = 0; uiLoop < uiNumParams; uiLoop++) { if (f_strncmp( (char*) ppszParams[uiLoop], pszParamName, f_strlen( (char*) pszParamName)) == 0) { return (TRUE); } } return (FALSE); } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_WebPage::getDatabaseHandleParam( FLMUINT uiNumParams, const char ** ppszParams, F_Session * pFlmSession, HFDB* phDb, char* pszKey) { RCODE rc = FERR_OK; HFDB hDb; char szTmp[ 64]; char * pTmp; if (phDb) { *phDb = hDb = HFDB_NULL; } if (pszKey) { *pszKey = 0; } // Need to memset the first F_SESSION_DB_KEY_LEN bytes of szTmp // because the hash lookup algorithm expects the buffer to be padded // with zeros at the end of the key. f_memset( szTmp, 0, F_SESSION_DB_KEY_LEN); if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, "dbhandle", sizeof(szTmp), szTmp))) { pTmp = &szTmp[0]; if (RC_BAD( getFormValueByName( "dbhandle", &pTmp, sizeof(szTmp), NULL))) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } } if (szTmp[0]) { if (RC_BAD( rc = pFlmSession->getDbHandle( szTmp, &hDb))) { goto Exit; } if (pszKey) { f_memcpy( pszKey, szTmp, F_SESSION_DB_KEY_LEN); } } if (phDb) { *phDb = hDb; } Exit: return (rc); } /**************************************************************************** Desc: Function to display the formatted time value in the form dd hh:mm:ss.ccc *****************************************************************************/ void F_WebPage::FormatTime( FLMUINT uiTimerUnits, char * pszFormattedTime) { FLMUINT uiMilli; FLMUINT uiSec; FLMUINT uiMin; FLMUINT uiHr; FLMUINT uiDays; FLMUINT uiTemp; // Initialize to NULL; pszFormattedTime[0] = '\0'; // Convert the timer units to milliseconds uiMilli = FLM_TIMER_UNITS_TO_MILLI( uiTimerUnits); // Determine the number of days uiDays = uiMilli / 86400000; uiTemp = uiMilli % 86400000; // Now the hours uiHr = uiTemp / 3600000; uiTemp = uiTemp % 3600000; // Determine the minutes uiMin = uiTemp / 60000; uiTemp = uiTemp % 60000; // Determine seconds uiSec = uiTemp / 1000; // Determine the milliseconds uiMilli = uiTemp % 1000; // Put it all together - hh:mm:ss f_sprintf( (char *) pszFormattedTime, "%ld %2.2ld:%2.2ld:%2.2ld.%3.3ld", uiDays, uiHr, uiMin, uiSec, uiMilli); } /**************************************************************************** Desc: Procedure to generate the HTML page that displays the usage statistics structure. ****************************************************************************/ RCODE F_WebPage::writeUsage( FLM_CACHE_USAGE* pUsage, FLMBOOL bRefresh, const char* pszURL, const char* pszTitle) { RCODE rc = FERR_OK; FLMBOOL bHighlight = FALSE; char szTemp[100]; stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); // Setup the page header & refresh control... Assuming the the first // parameter ?Usage is already contained in the pszURL string. if (bRefresh) { fnPrintf( m_pHRequest, "" "" "%s\n", m_pszURLString, pszURL, pszTitle); printStyle(); fnPrintf( m_pHRequest, "\n\n"); f_sprintf( (char*) szTemp, "Stop Auto-refresh", m_pszURLString, pszURL); } else { fnPrintf( m_pHRequest, "%s\n", pszTitle); printStyle(); fnPrintf( m_pHRequest, "\n\n"); f_sprintf( (char*) szTemp, "Start Auto-refresh (5 sec.)", m_pszURLString, pszURL); } // Begin the table printTableStart( (char*) pszTitle, 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString, pszURL); fnPrintf( m_pHRequest, "%s\n", szTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings. printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Byte Offset"); printColumnHeading( "Value"); printTableRowEnd(); // uiMaxBytes printHTMLUint( (char*) "uiMaxBytes", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiMaxBytes, pUsage->uiMaxBytes, (bHighlight = ~bHighlight)); // uiTotalBytesAllocated printHTMLUint( (char*) "uiTotalBytesAllocated", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiTotalBytesAllocated, pUsage->uiTotalBytesAllocated, (bHighlight = ~bHighlight)); // uiCount printHTMLUint( (char*) "uiCount", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiCount, pUsage->uiCount, (bHighlight = ~bHighlight)); // uiOldVerCount printHTMLUint( (char*) "uiOldVerCount", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiOldVerCount, pUsage->uiOldVerCount, (bHighlight = ~bHighlight)); // uiOldVerBytes printHTMLUint( (char*) "uiOldVerBytes", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiOldVerBytes, pUsage->uiOldVerBytes, (bHighlight = ~bHighlight)); // uiCacheHits printHTMLUint( (char*) "uiCacheHits", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiCacheHits, pUsage->uiCacheHits, (bHighlight = ~bHighlight)); // uiCacheHitLooks printHTMLUint( (char*) "uiCacheHitLooks", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiCacheHitLooks, pUsage->uiCacheHitLooks, (bHighlight = ~bHighlight)); // uiCacheFaults printHTMLUint( (char*) "uiCacheFaults", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiCacheFaults, pUsage->uiCacheFaults, (bHighlight = ~bHighlight)); // uiCacheFaultLooks printHTMLUint( (char*) "uiCacheFaultLooks", (char*) "FLMUINT", (void*) pUsage, (void*) &pUsage->uiCacheFaultLooks, pUsage->uiCacheFaultLooks, (bHighlight = ~bHighlight)); printTableEnd(); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return (rc); } /********************************************************************* Desc: This function prints a linkable field in HTML *********************************************************************/ void F_WebPage::printHTMLLink( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, void * pvValue, const char * pszLink, FLMBOOL bHighlight) { char szAddress[20]; char szOffset[8]; printOffset( pvBase, pvAddress, szOffset); printTableRowStart( bHighlight); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset if (pvValue) { printAddress( pvValue, szAddress); fnPrintf( m_pHRequest, TD_a_s_s, pszLink, pszName); // Link & Name fnPrintf( m_pHRequest, TD_s, pszType); // Type fnPrintf( m_pHRequest, TD_a_s_s, pszLink, szAddress); // Link & Value } else { fnPrintf( m_pHRequest, TD_s, pszName); fnPrintf( m_pHRequest, TD_s, pszType); fnPrintf( m_pHRequest, TD_s, "Null"); } printTableRowEnd(); } /********************************************************************* Desc: This function prints a text field in HTML *********************************************************************/ void F_WebPage::printHTMLString( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, const char * pszValue, FLMBOOL bHighlight) { char szOffset[8]; printOffset( pvBase, pvAddress, szOffset); printTableRowStart( bHighlight); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset fnPrintf( m_pHRequest, TD_s, pszName); // Name fnPrintf( m_pHRequest, TD_s, pszType); // Type fnPrintf( m_pHRequest, TD_s, pszValue); // Value printTableRowEnd(); } /********************************************************************* Desc: This function prints a unsigned long (FLMUINT) field in HTML *********************************************************************/ void F_WebPage::printHTMLUint( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, FLMUINT uiValue, FLMBOOL bHighlight) { char szOffset[8]; printOffset( pvBase, pvAddress, szOffset); printTableRowStart( bHighlight); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset fnPrintf( m_pHRequest, TD_s, pszName); // Name fnPrintf( m_pHRequest, TD_s, pszType); // Type fnPrintf( m_pHRequest, TD_ui, uiValue); // Value printTableRowEnd(); } /********************************************************************* Desc: This function prints a signed long (FLMINT) field in HTML *********************************************************************/ void F_WebPage::printHTMLInt( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, FLMINT iValue, FLMBOOL bHighlight) { char szOffset[8]; printOffset( pvBase, pvAddress, szOffset); printTableRowStart( bHighlight); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset fnPrintf( m_pHRequest, TD_s, pszName); // Name fnPrintf( m_pHRequest, TD_s, pszType); // Type fnPrintf( m_pHRequest, TD_i, iValue); // Value printTableRowEnd(); } /********************************************************************* Desc: This function prints a unsigned long field in HTML *********************************************************************/ void F_WebPage::printHTMLUlong( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, unsigned long luValue, FLMBOOL bHighlight) { char szOffset[8]; printOffset( pvBase, pvAddress, szOffset); printTableRowStart( bHighlight); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset fnPrintf( m_pHRequest, TD_s, pszName); // Name fnPrintf( m_pHRequest, TD_s, pszType); // Type fnPrintf( m_pHRequest, TD_lu, luValue); // Value printTableRowEnd(); } /********************************************************************* Desc: This function takes the name of a form field, and returns a pointer to it. This will allocate the buffer returned. The calling function is responsible for freeing that buffer. *********************************************************************/ RCODE F_WebPage::getFormValueByName( const char * pszValueTag, char ** ppszBuf, FLMUINT uiBufLen, FLMUINT * puiDataLen) { RCODE rc = FERR_OK; char szTag[128]; char * pszValue; FLMUINT uiLen; FLMBOOL bFreeFormData = FALSE; FLMBOOL bFreeUserData = FALSE; if (puiDataLen) { *puiDataLen = 0; } #ifdef FLM_DEBUG if (!uiBufLen) { flmAssert( ppszBuf && *ppszBuf == NULL); } #endif if (f_strlen( pszValueTag) + 1 >= sizeof(szTag)) { flmAssert( 0); rc = RC_SET( FERR_MEM); goto Exit; } f_sprintf( (char*) szTag, "%s=", pszValueTag); if (!m_pszFormData) { char * pszContentLength; FLMUINT uiContentLength; // First we need to determine how much form data there is. if ((pszContentLength = (char*) fnReqHdrValue( "Content-Length")) == NULL) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } if ((uiContentLength = f_atoi( pszContentLength)) == 0) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } // Now allocate a buffer to hold the form data if (RC_BAD( rc = f_alloc( uiContentLength + 1, &m_pszFormData))) { goto Exit; } bFreeFormData = TRUE; if (fnRecvBuffer( m_pszFormData, (FLMSIZET *) &uiContentLength) != 0) { rc = RC_SET( FERR_FAILURE); goto Exit; } m_pszFormData[uiContentLength] = 0; bFreeFormData = FALSE; } // Now, parse through the buffer until we find the field we are // looking for. The data is in the form name=value:name=value... if ((pszValue = f_strstr( m_pszFormData, szTag)) != NULL) { pszValue += f_strlen( szTag); for (uiLen = 0; pszValue[uiLen] && pszValue[uiLen] != ':' && pszValue[uiLen] != '&'; uiLen++); if (ppszBuf) { if (!uiBufLen) { uiBufLen = uiLen + 1; bFreeUserData = TRUE; *ppszBuf = NULL; if (RC_BAD( rc = f_alloc( uiBufLen, ppszBuf))) { goto Exit; } } if (uiLen >= uiBufLen) { rc = RC_SET( FERR_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( *ppszBuf, pszValue, uiLen); (*ppszBuf)[uiLen] = 0; bFreeUserData = FALSE; } if (puiDataLen) { *puiDataLen = uiLen + 1; } goto Exit; } else { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: if (bFreeFormData) { f_free( &m_pszFormData); } if (bFreeUserData && *ppszBuf) { f_free( ppszBuf); } return (rc); } /**************************************************************************** Desc: Prints the standard style sheet ****************************************************************************/ void F_WebPage::printStyle(void) { fnPrintf( m_pHRequest, "\n", m_pszURLString); } /**************************************************************************** Desc: Outputs a column heading using elements from the standard style sheet ****************************************************************************/ void F_WebPage::printColumnHeading( const char * pszHeading, JustificationType eJustification, const char * pszBackground, FLMUINT uiColSpan, FLMUINT uiRowSpan, FLMBOOL bClose, FLMUINT uiWidth) { fnPrintf( m_pHRequest, "
\n"); } } /**************************************************************************** Desc: Closes a column heading ****************************************************************************/ void F_WebPage::printColumnHeadingClose(void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: Encodes a string for rendering in an HTML page or for inclusion in an URL ****************************************************************************/ void F_WebPage::printEncodedString( const char * pszString, FStringEncodeType eEncodeType, FLMBOOL bMapSlashes) { char ucChar; while ((ucChar = *pszString) != 0) { if ((ucChar >= '0' && ucChar <= '9') || (ucChar >= 'A' && ucChar <= 'Z') || (ucChar >= 'a' && ucChar <= 'z') || ucChar == '_' || ( eEncodeType == URL_PATH_ENCODING && (ucChar == '.' || (bMapSlashes && (ucChar == '/' || ucChar == '\\'))) )) { if (ucChar == '\\') { ucChar = '/'; } fnPrintf( m_pHRequest, "%c", ucChar); } else if (eEncodeType == URL_PATH_ENCODING) { fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); } else if (eEncodeType == URL_QUERY_ENCODING) { if (ucChar == ' ') { ucChar = '+'; } fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); } else // HTML encoding { fnPrintf( m_pHRequest, "&#%u;", (unsigned) ucChar); } pszString++; } } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printDocStart( const char * pszTitle, FLMBOOL bPrintTitle, FLMBOOL bStdHeader, const char * pszBackground) { if (bStdHeader) { stdHdr(); } fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printRecordStyle(); printStyle(); fnPrintf( m_pHRequest, "Database iMonitor - "); printEncodedString( pszTitle); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n", pszBackground ? pszBackground : "white"); if (bPrintTitle) { printTableStart( pszTitle, 1); printTableEnd(); fnPrintf( m_pHRequest, "
\n"); } } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printDocEnd(void) { fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printMenuReload(void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableStart( const char* pszTitle, FLMUINT uiColumns, FLMUINT uiWidthFactor) { fnPrintf( m_pHRequest, "
hMutex F_MUTEXuiUseCount FLMUINTpszURLString FLMBYTE *uiURLStringLen FLMUINTbRegistered FLMBOOLfnReg REG_URL_HANDLER_FNfnDereg DEREG_URL_HANDLER_FNfnReqPath REQ_PATH_FNfnReqQuery REQ_QUERY_FNfnReqHdrValue REQ_HDR_VALUE_FNfnSetHdrValue SET_HDR_VAL_FNfnPrintf PRINTF_FNfnEmit EMIT_FNfnSetNoCache SET_NO_CACHE_FNfnSendHeader SEND_HDR_FNfnSetIOMode SET_IO_MODE_FNfnSendBuffer SEND_BUFF_FNfnAcquireSession ACQUIRE_SESSION_FNfnReleaseSession RELEASE_SESSION_FNfnAcquireUser ACQUIRE_USER_FNfnReleaseUser RELEASE_USER_FNfnSetSessionValue SET_SESSION_VALUE_FNfnGetSessionValue GET_SESSION_VALUE_FNfnGetGblValue GET_GBL_VALUE_FNfnSetGblValue SET_GBL_VALUE_FNfnRecvBuffer RECV_BUFFER_FN \n"); if (pszHeading) { printEncodedString( pszHeading); } if (bClose) { fnPrintf( m_pHRequest, "
\n"); if (pszTitle) { printTableRowStart(); fnPrintf( m_pHRequest, ""); printTableRowEnd(); } } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableEnd(void) { fnPrintf( m_pHRequest, "
\n"); printEncodedString( pszTitle); fnPrintf( m_pHRequest, "
\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableRowStart( FLMBOOL bHighlight) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableRowEnd(void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableDataStart( FLMBOOL bNoWrap, JustificationType eJustification, FLMUINT uiWidth) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableDataEnd(void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printTableDataEmpty(void) { fnPrintf( m_pHRequest, " "); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printErrorPage( RCODE rc, FLMBOOL bStdHeader, const char * pszWhat) { printDocStart( "Error", TRUE, bStdHeader); fnPrintf( m_pHRequest, "

\n"); fnPrintf( m_pHRequest, "%s
%s (0x%04X).\n", pszWhat, FlmErrorString( rc), (unsigned) rc); fnPrintf( m_pHRequest, "

\n"); printDocEnd(); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printErrorPage( const char * pszErrStr, const char * pszErrStr2, FLMBOOL bStdHeader) { printDocStart( "Error", TRUE, bStdHeader); fnPrintf( m_pHRequest, "

\n"); fnPrintf( m_pHRequest, "%s\n", pszErrStr); if (pszErrStr2 && *pszErrStr2) { fnPrintf( m_pHRequest, "
%s\n", pszErrStr2); } fnPrintf( m_pHRequest, "

\n"); printDocEnd(); } /**************************************************************************** Desc: Start an input form ****************************************************************************/ void F_WebPage::printStartInputForm( const char * pszFormName, const char * pszPage, FLMUINT uiFormValue) { fnPrintf( m_pHRequest, "
\n" "\n", pszFormName, m_pszURLString, pszPage, (unsigned) uiFormValue); } /**************************************************************************** Desc: End an input form ****************************************************************************/ void F_WebPage::printEndInputForm(void) { fnPrintf( m_pHRequest, "
"); } /**************************************************************************** Desc: Generic function to output HTML that gererates a button ****************************************************************************/ void F_WebPage::printButton( const char * pszContents, ButtonTypes eBType, const char * pszName, const char * pszValue, const char * pszExtra, FLMBOOL bDisabled, FLMBYTE ucAccessKey, FLMUINT uiTabIndex) { fnPrintf( m_pHRequest, "\n", (char*) (pszContents ? pszContents : "")); } /**************************************************************************** Desc: Format and output date. ****************************************************************************/ void F_WebPage::printDate( FLMUINT uiGMTTime, char * pszBuffer) { F_TMSTAMP timeStamp; FLMUINT uiLocalTime; char * pszAmPm; const char * pszMonth; uiLocalTime = (FLMUINT) (uiGMTTime - f_timeGetLocalOffset()); f_timeSecondsToDate( uiLocalTime, &timeStamp); pszAmPm = (char*) ((timeStamp.hour >= 12) ? (char*) "pm" : (char*) "am"); if (timeStamp.hour > 12) { timeStamp.hour -= 12; } if (timeStamp.hour == 0) { timeStamp.hour = 12; } switch (timeStamp.month) { case 0: pszMonth = "Jan"; break; case 1: pszMonth = "Feb"; break; case 2: pszMonth = "Mar"; break; case 3: pszMonth = "Apr"; break; case 4: pszMonth = "May"; break; case 5: pszMonth = "Jun"; break; case 6: pszMonth = "Jul"; break; case 7: pszMonth = "Aug"; break; case 8: pszMonth = "Sep"; break; case 9: pszMonth = "Oct"; break; case 10: pszMonth = "Nov"; break; default: case 11: pszMonth = "Dec"; break; } if (pszBuffer != NULL) { f_sprintf( (char*) pszBuffer, "%s %u, %u %u:%02u:%02u %s", pszMonth, (unsigned) timeStamp.day, (unsigned) timeStamp.year, (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, (unsigned) timeStamp.second, pszAmPm); } else { fnPrintf( m_pHRequest, "%s %u, %u %u:%02u:%02u %s", pszMonth, (unsigned) timeStamp.day, (unsigned) timeStamp.year, (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, (unsigned) timeStamp.second, pszAmPm); } } /**************************************************************************** Desc: Outputs a Yes or No value based on the passed-in boolean ****************************************************************************/ void F_WebPage::printYesNo( FLMBOOL bYes) { fnPrintf( m_pHRequest, "%s", bYes ? "Yes" : "No"); } /**************************************************************************** Desc: Outputs a number with commas, for easier reading. ****************************************************************************/ void F_WebPage::printCommaNumText( FLMUINT64 ui64Num) { FLMUINT uiTerm; FLMUINT64 ui64Divisor = 1; FLMBOOL bFirstPass = TRUE; while ((FLMUINT64) (ui64Num / (ui64Divisor * (FLMUINT64) 1000))) { ui64Divisor *= 1000; } while (ui64Divisor) { uiTerm = (FLMUINT) (ui64Num / ui64Divisor); ui64Num -= ((FLMUINT64) uiTerm) * ui64Divisor; if (bFirstPass) { fnPrintf( m_pHRequest, "%u", (unsigned) uiTerm); bFirstPass = FALSE; } else { fnPrintf( m_pHRequest, "%03u", (unsigned) uiTerm); } if ((ui64Divisor /= (FLMUINT64) 1000) > (FLMUINT64) 0) { fnPrintf( m_pHRequest, ","); } } } /**************************************************************************** Desc: Outputs a number with commas, for easier reading. ****************************************************************************/ void F_WebPage::printCommaNum( FLMUINT64 ui64Num, JustificationType eJustify, FLMBOOL bChangedValue) { printTableDataStart( TRUE, eJustify); if (bChangedValue) { fnPrintf( m_pHRequest, ""); } printCommaNumText( ui64Num); if (bChangedValue) { fnPrintf( m_pHRequest, ""); } printTableDataEnd(); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_WebPage::acquireSession(void) { RCODE rc = FERR_OK; FLMBOOL bHttpSessionMutexLocked = FALSE; FLMUINT uiSize; void * pvHttpSession = NULL; char szSessionKey[F_SESSION_KEY_LEN]; m_pFlmSession = NULL; if (!gv_FlmSysData.HttpConfigParms.fnAcquireSession) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } if ((pvHttpSession = fnAcquireSession()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_mutexLock( gv_FlmSysData.hHttpSessionMutex); bHttpSessionMutexLocked = TRUE; uiSize = sizeof(szSessionKey); if (fnGetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, (void*) szSessionKey, (FLMSIZET *) &uiSize) != 0) { CreateSession: if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->createSession( &m_pFlmSession))) { goto Exit; } fnSetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, m_pFlmSession->getKey(), sizeof(szSessionKey)); } else { if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->getSession( szSessionKey, &m_pFlmSession))) { if (rc == FERR_NOT_FOUND) { goto CreateSession; } } } Exit: if (RC_BAD( rc)) { if (m_pFlmSession) { releaseSession(); } } if (pvHttpSession) { fnReleaseSession( pvHttpSession); } if (bHttpSessionMutexLocked) { f_mutexUnlock( gv_FlmSysData.hHttpSessionMutex); } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::releaseSession(void) { if (m_pFlmSession) { gv_FlmSysData.pSessionMgr->releaseSession( &m_pFlmSession); m_pFlmSession = NULL; } } /**************************************************************************** Desc: ****************************************************************************/ void F_WebPage::printSpaces( FLMUINT uiCount) { while (uiCount--) { fnPrintf( m_pHRequest, " "); } } /**************************************************************************** Desc: Outputs elapsed milliseconds as seconds.milli. The optional parameter pszBuffer will cause the time to be written to pszBuffer instead of the web page. That way it can be incorporated into more complex structures if desired. ****************************************************************************/ void F_WebPage::printElapTime( FLMUINT64 ui64ElapTime, char* pszBuffer, JustificationType eJustify, FLMBOOL bTimeIsMilli) { FLMUINT uiHours; FLMUINT uiMinutes; FLMUINT uiSeconds; FLMUINT uiMilli = 0; if (bTimeIsMilli) { uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) (1000 * 3600)); uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) (1000 * 60)) % (FLMUINT64) 60); uiSeconds = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 1000) % (FLMUINT64) 60); uiMilli = (FLMUINT) (ui64ElapTime % (FLMUINT64) 1000); } else { uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) 3600); uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 60) % (FLMUINT64) 60); uiSeconds = (FLMUINT) (ui64ElapTime % (FLMUINT64) 60); } if (!pszBuffer) { printTableDataStart( TRUE, eJustify); } if (pszBuffer) { f_sprintf( (char*) pszBuffer, "%02u:%02u:%02u", (unsigned) uiHours, (unsigned) uiMinutes, (unsigned) uiSeconds); } else { fnPrintf( m_pHRequest, "%02u:%02u:%02u", (unsigned) uiHours, (unsigned) uiMinutes, (unsigned) uiSeconds); } if (bTimeIsMilli) { if (pszBuffer) { char szTemp[5]; f_sprintf( szTemp, ".%03u", (unsigned) uiMilli); f_strncat( (char*) pszBuffer, szTemp, 4); } else { fnPrintf( m_pHRequest, ".%03u", (unsigned) uiMilli); } } if (!pszBuffer) { printTableDataEnd(); } } /**************************************************************************** Desc: This function will output a form in an already existing page that will present a formatted display of the record passed in. An optional parameter bReadOnly will determine if the form should allow editing of the record or not. The default value is TRUE (No editing capability). A null may be passed in for the FlmRecord pointer, in which case an empty display will be created. Normally, a bReadOnly value of false would accompany a null record so that a record can be created. The printRecordStyle() must be called in the header section of the page prior to calling this function. Multiple calls may be made to display multiple records per page. The puiContext parameter must be initialized to zero before the first (and possibly the only) call, otherwise the scripts need to run this page will not be loaded. ****************************************************************************/ void F_WebPage::printRecord( const char * pszDbKey, FlmRecord * pRec, F_NameTable * pNameTable, FLMUINT * puiContext, FLMBOOL bReadOnly, FLMUINT uiSelectedField, FLMUINT uiFlags) { #define SP " " FLMBOOL bEmpty = FALSE; FLMUINT uiContainer; FLMUINT uiDrn; void * pvField; FLMUINT uiFieldCounter; FLMUINT uiFldCnt; FLMUINT uiTagNum; FLMUINT uiLevel; FLMUINT uiType; FLMUINT uiContext = 0; char szNameBuf[128]; flmAssert( pNameTable); if (puiContext) { uiContext = *puiContext; (*puiContext)++; } // See if we need to write out the scripts if (uiContext == 0) { printRecordScripts(); } if (!pRec) { bEmpty = TRUE; uiDrn = 0; uiContainer = 0; } else { // Get the Drn & Container uiDrn = pRec->getID(); uiContainer = pRec->getContainerID(); } // Count the fields uiFldCnt = 0; if (pRec != NULL) { pvField = pRec->root(); while (pvField) { pvField = pRec->next( pvField); uiFldCnt++; } } // Begin the form that displays the record data (if any) fnPrintf( m_pHRequest, "
\n", uiContext, gv_FlmSysData.HttpConfigParms.pszURLString); printHiddenField( "ReadOnly", (char*) (bReadOnly ? "TRUE" : "FALSE")); if (pszDbKey) { printHiddenField( "dbhandle", (char*) (pszDbKey)); } printHiddenField( (char*) "Action", "none"); printHiddenField( (char*) "FieldLevel", (FLMUINT) 0); printHiddenField( (char*) "FieldNumber", (FLMUINT) 0); printHiddenField( (char*) "FieldCount", uiFldCnt); // Print out the block that displays the DRN and Container list fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n\n\n", uiDrn, pszDbKey == NULL ? "disabled" : ""); if (pszDbKey != NULL) { fnPrintf( m_pHRequest, "\n\n\n"); } fnPrintf( m_pHRequest, "\n\n\n", uiContainer); } fnPrintf( m_pHRequest, "\n\n"); if (pszDbKey != NULL) { // Print out the field list drop down box. fnPrintf( m_pHRequest, "\n\n\n"); // Print out the Add, Modify, Delete action buttons. fnPrintf( m_pHRequest, "\n"); } fnPrintf( m_pHRequest, "
DRN  
Flags \n"); printRetrievalFlagsPulldown( uiFlags); fnPrintf( m_pHRequest, "
Container \n"); if (pszDbKey != NULL) { printContainerPulldown( pNameTable, uiContainer); } else { fnPrintf( m_pHRequest, " 
Field list "); printFieldPulldown( pNameTable, uiSelectedField); fnPrintf( m_pHRequest, "
", uiContext); if (pRec != NULL) { if (!bReadOnly) { fnPrintf( m_pHRequest, "", uiContext); fnPrintf( m_pHRequest, "", uiContext); } fnPrintf( m_pHRequest, "", uiContext); } fnPrintf( m_pHRequest, "", uiContext); if (pRec != NULL && bReadOnly) { fnPrintf( m_pHRequest, "", uiContext); } fnPrintf( m_pHRequest, "
\n
\n"); // Print out the record fields (if there are any) if (pRec != NULL) { fnPrintf( m_pHRequest, "
\n"); if (!bReadOnly) { fnPrintf( m_pHRequest, "", uiContext); if (uiFldCnt > 1) { fnPrintf( m_pHRequest, "", uiContext); fnPrintf( m_pHRequest, "", uiContext); fnPrintf( m_pHRequest, "\n", uiContext); } } fnPrintf( m_pHRequest, "
\n");

		// Now for the actual data. Start with the root field.

		pvField = pRec->root();

		uiFieldCounter = 0;

		while (pvField)
		{
			uiTagNum = pRec->getFieldID( pvField);
			uiLevel = pRec->getLevel( pvField);
			uiType = pRec->getDataType( pvField);

			if (uiLevel != 0 && !bReadOnly)
			{
				fnPrintf( m_pHRequest,
							"",
						uiFieldCounter, uiContext, uiFieldCounter, uiLevel);
			}

			pNameTable->getFromTagNum( uiTagNum, NULL, szNameBuf, sizeof(szNameBuf));
			printSpaces( uiLevel + 5);

			fnPrintf( m_pHRequest, "%s%d%s%s%s", SP,
						uiLevel, SP, szNameBuf, SP);

			if (pRec->getDataLength( pvField))
			{
				switch (uiType)
				{
					case FLM_TEXT_TYPE:
						printTextField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
					case FLM_NUMBER_TYPE:
						printNumberField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
					case FLM_BINARY_TYPE:
						printBinaryField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
					case FLM_CONTEXT_TYPE:
						printContextField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
					case FLM_BLOB_TYPE:
						printBlobField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
					default:
						printDefaultField( pRec, pvField, uiFieldCounter, bReadOnly);
						break;
				}
			}
			else if (!bReadOnly)
			{
				fnPrintf( m_pHRequest,
							"",
						uiFieldCounter, MAX_FIELD_SIZE( 0));
			}

			// Print the hidden field Ids

			printFieldIds( uiFieldCounter, uiLevel, uiType, uiTagNum);
			fnPrintf( m_pHRequest, "\n");

			pvField = pRec->next( pvField);
			uiFieldCounter++;
		}

		fnPrintf( m_pHRequest, "
\n
\n
\n"); } fnPrintf( m_pHRequest, "
\n"); return; } /**************************************************************************** Desc: Prints out a style sheet specific to displaying records. ****************************************************************************/ void F_WebPage::printRecordStyle(void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: Prints out the required scripts for displaying and updating records. ****************************************************************************/ void F_WebPage::printRecordScripts( void) { fnPrintf( m_pHRequest, "\n"); } /**************************************************************************** Desc: Prints out a hidden field with a character string value ****************************************************************************/ void F_WebPage::printHiddenField( const char * pszName, const char * pszValue) { fnPrintf( m_pHRequest, "", pszName, pszValue); } // /**************************************************************************** Desc: Prints out a hidden with an unsigned long value ****************************************************************************/ void F_WebPage::printHiddenField( const char * pszName, FLMUINT uiValue) { fnPrintf( m_pHRequest, "", pszName, (unsigned) uiValue); } // /**************************************************************************** Desc: Prints out the value for the field, assuming the field is a text field. ****************************************************************************/ void F_WebPage::printTextField( FlmRecord * pRec, void * pvField, FLMUINT uiFieldCounter, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FLMUNICODE * puzBuf = NULL; FLMUNICODE * puzTmp = NULL; F_DynamicBuffer * pBuffer = NULL; FLMUINT uiLen; if (RC_BAD( rc = pRec->getUnicodeLength( pvField, &uiLen))) { fnPrintf( m_pHRequest, "** Error retrieving Unicode field length (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } // The length returned does not allow for 2 NULL terminators. We must // allow for them when allocating a buffer. uiLen += 2; if (RC_BAD( rc = f_alloc( uiLen, &puzBuf))) { fnPrintf( m_pHRequest, "** Error allocating memory buffer (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } if (RC_BAD( rc = pRec->getUnicode( pvField, puzBuf, &uiLen))) { fnPrintf( m_pHRequest, "** Error retrieving Unicode field (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } puzTmp = puzBuf; if ((pBuffer = f_new F_DynamicBuffer) == NULL) { fnPrintf( m_pHRequest, "** Error allocating memory **"); goto Exit; } // Start the text field if not read only mode. if (!bReadOnly) { fnPrintf( m_pHRequest, ""); } while (*puzTmp) { // Check for ASCII characters if ((*puzTmp >= 32) && (*puzTmp <= 126)) { if (RC_BAD( rc = pBuffer->addChar( (char) *puzTmp))) { fnPrintf( m_pHRequest, "** Error adding Unicode character to buffer (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } } else { // Treat as though these are NON-ASCII. They will be printed in // the form ~[0x ] char szTempBuff[20]; f_sprintf( szTempBuff, "~[0x%04X]", (unsigned) (*puzTmp)); if (RC_BAD( rc = pBuffer->addString( szTempBuff))) { fnPrintf( m_pHRequest, "** Error formatting Unicode string (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } } // We are attempting to not let our buffer get any larger than the // Http stack buffer size. We don't really know what the limit is, // but we are using what seems to reasonable to us... if ((pBuffer->getBufferSize() + 9) >= RESP_WRITE_BUF_SIZE) { fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); pBuffer->reset(); } puzTmp++; } if (bReadOnly) { fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); } else { fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pBuffer->printBuffer(), MAX_FIELD_SIZE( uiLen)); } Exit: if (puzBuf) { f_free( &puzBuf); } if (pBuffer) { pBuffer->Release(); } } // /**************************************************************************** Desc: Prints out the value for the field, assuming the field is a number field. ****************************************************************************/ void F_WebPage::printNumberField( FlmRecord * pRec, void * pvField, FLMUINT uiFieldCounter, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FLMINT iVal; FLMUINT uiVal; if (RC_BAD( rc = pRec->getUINT( pvField, &uiVal))) { if (RC_OK( rc = pRec->getINT( pvField, &iVal))) { if (bReadOnly) { fnPrintf( m_pHRequest, "%d", (int) iVal); } else { fnPrintf( m_pHRequest, "", uiFieldCounter, (int) iVal, MAX_FIELD_SIZE( 0)); } } else { fnPrintf( m_pHRequest, "** Error retrieving number field (Return Code = 0x%04X, %s)**\n", (unsigned) rc, FlmErrorString( rc)); } } else { if (bReadOnly) { fnPrintf( m_pHRequest, "%lu", (unsigned long) uiVal); } else { fnPrintf( m_pHRequest, "", uiFieldCounter, (unsigned long) uiVal); } } } /**************************************************************************** Desc: Prints out the value for the field, assuming the field is a binary field. ****************************************************************************/ void F_WebPage::printBinaryField( FlmRecord * pRec, void * pvField, FLMUINT uiFieldCounter, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FLMBYTE * pucBuf = NULL; FLMBYTE * pszTmpBuf = NULL; FLMBYTE * pszTmp = NULL; FLMUINT uiLoop; FLMUINT uiLen; FLMUINT uiBufLen; uiLen = pRec->getDataLength( pvField); if (RC_BAD( rc = f_alloc( uiLen, &pucBuf))) { fnPrintf( m_pHRequest, "** Error occured allocating memory to retrieve binary field (Return Code = 0x%04X, %s) **\n", (unsigned) rc, FlmErrorString( rc)); goto Exit; } if (RC_BAD( rc = pRec->getBinary( pvField, pucBuf, &uiLen))) { if (rc != FERR_NOT_FOUND) { fnPrintf( m_pHRequest, "** Error occured retrieving binary field (Return Code = 0x%04X, %s) **\n", (unsigned) rc, FlmErrorString( rc)); goto Exit; } } if (RC_BAD( rc = f_alloc( RESP_WRITE_BUF_SIZE + 1, &pszTmpBuf))) { fnPrintf( m_pHRequest, "** Error occured allocating memory to format binary field (Return Code = 0x%04X, %s) **\n", (unsigned) rc, FlmErrorString( rc)); goto Exit; } if (!bReadOnly) { fnPrintf( m_pHRequest, ""); } // Scan through the binary data, present all data as Hex. for (pszTmp = pszTmpBuf, uiLoop = 0, uiBufLen = 0; uiLoop < uiLen; uiLoop++) { if (uiLoop) { *pszTmp++ = ' '; uiBufLen++; } f_sprintf( (char*) pszTmp, "%2.2X", (unsigned) pucBuf[uiLoop]); pszTmp += 2; uiBufLen += 2; if ((uiBufLen + 3) >= RESP_WRITE_BUF_SIZE) { // Flush the current buffer *pszTmp = '\0'; fnPrintf( m_pHRequest, "%s", pszTmpBuf); pszTmp = pszTmpBuf; uiBufLen = 0; } } *pszTmp = '\0'; if (bReadOnly) { fnPrintf( m_pHRequest, "%s", pszTmpBuf); } else { fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pszTmpBuf, MAX_FIELD_SIZE( uiLen * 3)); } Exit: if (pucBuf) { f_free( &pucBuf); } if (pszTmpBuf) { f_free( &pszTmpBuf); } } /**************************************************************************** Desc: Prints out the value for the field, assuming the field is a context field. ****************************************************************************/ void F_WebPage::printContextField( FlmRecord * pRec, void * pvField, FLMUINT uiFieldCounter, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FLMUINT uiRecPointer; if (RC_OK( rc = pRec->getRecPointer( pvField, &uiRecPointer))) { if (bReadOnly) { fnPrintf( m_pHRequest, "%lu", (unsigned long) uiRecPointer); } else { fnPrintf( m_pHRequest, "", uiFieldCounter, (unsigned long) uiRecPointer, MAX_FIELD_SIZE( 0)); } } else { fnPrintf( m_pHRequest, "** Error retrieving context field (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); } } /**************************************************************************** Desc: Prints out the value for the field, assuming the field is a blob field. ****************************************************************************/ void F_WebPage::printBlobField( FlmRecord * pRec, void * pvField, FLMUINT uiFieldCounter, FLMBOOL bReadOnly) { RCODE rc = FERR_OK; FlmBlob * pBlob = NULL; char szPath[F_PATH_MAX_SIZE]; FLMUINT uiLen; if (RC_BAD( rc = pRec->getBlob( pvField, &pBlob))) { fnPrintf( m_pHRequest, "** Failed to retrieve Blob object (Return Code = 0x%04X, %s) **", (unsigned long) rc, FlmErrorString( rc)); goto Exit; } uiLen = ((FlmBlobImp *) pBlob)->getDataLength(); if (uiLen == 0) { if (!bReadOnly) { fnPrintf( m_pHRequest, "", uiFieldCounter, MAX_FIELD_SIZE( 0)); } goto Exit; } if (RC_BAD( rc = pBlob->buildFileName( szPath))) { fnPrintf( m_pHRequest, "** Failed to retrieve Blob filename (Return Code = 0x%04X, %s) **", (unsigned) rc, FlmErrorString( rc)); goto Exit; } if (bReadOnly) { fnPrintf( m_pHRequest, ""); printEncodedString( szPath, HTML_ENCODING); fnPrintf( m_pHRequest, ""); } else { fnPrintf( m_pHRequest, ""); } Exit: if (pBlob) { pBlob->Release(); } } /**************************************************************************** Desc: Prints out a string identifying this as a default field - error condition. ****************************************************************************/ void F_WebPage::printDefaultField( FlmRecord *, void *, FLMUINT, FLMBOOL) { fnPrintf( m_pHRequest, "**Default Field**"); } /**************************************************************************** Desc: Prints out the hidden field identifiers ****************************************************************************/ void F_WebPage::printFieldIds( FLMUINT uiFieldCounter, FLMUINT uiFieldLevel, FLMUINT uiType, FLMUINT uiTagNum) { char szTmp[20]; f_sprintf( szTmp, "fieldLevel%u", (unsigned) uiFieldCounter); printHiddenField( szTmp, uiFieldLevel); f_sprintf( szTmp, "fieldType%u", (unsigned) uiFieldCounter); printHiddenField( szTmp, uiType); f_sprintf( szTmp, "fieldTag%u", (unsigned) uiFieldCounter); printHiddenField( szTmp, uiTagNum); } /**************************************************************************** Desc: Prints a table listing the fields of the supplied Log Headers. Any of the log header pointers may be null, in which case a series of blank entries will be created in the table for that entry. ****************************************************************************/ void F_WebPage::printLogHeaders( FLMBYTE * pucLastCommitted, FLMBYTE * pucCheckpoint, FLMBYTE * pucUncommitted) { FLMBOOL bHighlight = FALSE; // Start the table and headings... printTableStart( NULL, 5, 100); printTableRowStart( FALSE); printColumnHeading( "Offset (hex)"); printColumnHeading( "Field"); printColumnHeading( "Last Committed"); printColumnHeading( "Checkpoint"); printColumnHeading( "Uncommitted"); printTableRowEnd(); // Fill in the table here. LOG_RFL_FILE_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_FILE_NUM); fnPrintf( m_pHRequest, "Current RFL file"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_FILE_NUM); printLogFileEntryUD( pucCheckpoint, LOG_RFL_FILE_NUM); printLogFileEntryUD( pucUncommitted, LOG_RFL_FILE_NUM); printTableRowEnd(); // LOG_RFL_LAST_TRANS_OFFSET printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_TRANS_OFFSET); fnPrintf( m_pHRequest, "Current RFL offset"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_TRANS_OFFSET); printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_TRANS_OFFSET); printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_TRANS_OFFSET); printTableRowEnd(); // LOG_RFL_LAST_CP_FILE_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_FILE_NUM); fnPrintf( m_pHRequest, "Last CP RFL file"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_FILE_NUM); printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_FILE_NUM); printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_FILE_NUM); printTableRowEnd(); // LOG_RFL_LAST_CP_OFFSET printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_OFFSET); fnPrintf( m_pHRequest, "Last CP RFL offset"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_OFFSET); printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_OFFSET); printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_OFFSET); printTableRowEnd(); // LOG_ROLLBACK_EOF printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_ROLLBACK_EOF); fnPrintf( m_pHRequest, "End of file"); printLogFileEntryUD( pucLastCommitted, LOG_ROLLBACK_EOF); printLogFileEntryUD( pucCheckpoint, LOG_ROLLBACK_EOF); printLogFileEntryUD( pucUncommitted, LOG_ROLLBACK_EOF); printTableRowEnd(); // LOG_INC_BACKUP_SEQ_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SEQ_NUM); fnPrintf( m_pHRequest, "Incremental backup sequence number"); printLogFileEntryUD( pucLastCommitted, LOG_INC_BACKUP_SEQ_NUM); printLogFileEntryUD( pucCheckpoint, LOG_INC_BACKUP_SEQ_NUM); printLogFileEntryUD( pucUncommitted, LOG_INC_BACKUP_SEQ_NUM); printTableRowEnd(); // LOG_CURR_TRANS_ID printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_CURR_TRANS_ID); fnPrintf( m_pHRequest, "Transaction ID"); printLogFileEntryUD( pucLastCommitted, LOG_CURR_TRANS_ID); printLogFileEntryUD( pucCheckpoint, LOG_CURR_TRANS_ID); printLogFileEntryUD( pucUncommitted, LOG_CURR_TRANS_ID); printTableRowEnd(); // LOG_COMMIT_COUNT printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_COMMIT_COUNT); fnPrintf( m_pHRequest, "Commit count"); printLogFileEntryUD( pucLastCommitted, LOG_COMMIT_COUNT); printLogFileEntryUD( pucCheckpoint, LOG_COMMIT_COUNT); printLogFileEntryUD( pucUncommitted, LOG_COMMIT_COUNT); printTableRowEnd(); // LOG_PL_FIRST_CP_BLOCK_ADDR printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_PL_FIRST_CP_BLOCK_ADDR); fnPrintf( m_pHRequest, "First CP block address"); printLogFileEntryUDX( pucLastCommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); printLogFileEntryUDX( pucCheckpoint, LOG_PL_FIRST_CP_BLOCK_ADDR); printLogFileEntryUDX( pucUncommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); printTableRowEnd(); // LOG_LAST_RFL_FILE_DELETED printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_FILE_DELETED); fnPrintf( m_pHRequest, "Last RFL file deleted"); printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_FILE_DELETED); printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_FILE_DELETED); printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_FILE_DELETED); printTableRowEnd(); // LOG_RFL_MIN_FILE_SIZE printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MIN_FILE_SIZE); fnPrintf( m_pHRequest, "Minimum RFL file size"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_MIN_FILE_SIZE); printLogFileEntryUD( pucCheckpoint, LOG_RFL_MIN_FILE_SIZE); printLogFileEntryUD( pucUncommitted, LOG_RFL_MIN_FILE_SIZE); printTableRowEnd(); // LOG_HDR_CHECKSUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_HDR_CHECKSUM); fnPrintf( m_pHRequest, "Header checksum"); printLogFileEntryUW( pucLastCommitted, LOG_HDR_CHECKSUM); printLogFileEntryUW( pucCheckpoint, LOG_HDR_CHECKSUM); printLogFileEntryUW( pucUncommitted, LOG_HDR_CHECKSUM); printTableRowEnd(); // LOG_FLAIM_VERSION printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_FLAIM_VERSION); fnPrintf( m_pHRequest, "Flaim version"); printLogFileEntryUW( pucLastCommitted, LOG_FLAIM_VERSION); printLogFileEntryUW( pucCheckpoint, LOG_FLAIM_VERSION); printLogFileEntryUW( pucUncommitted, LOG_FLAIM_VERSION); printTableRowEnd(); // LOG_LAST_BACKUP_TRANS_ID printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LAST_BACKUP_TRANS_ID); fnPrintf( m_pHRequest, "Last backup trans ID"); printLogFileEntryUD( pucLastCommitted, LOG_LAST_BACKUP_TRANS_ID); printLogFileEntryUD( pucCheckpoint, LOG_LAST_BACKUP_TRANS_ID); printLogFileEntryUD( pucUncommitted, LOG_LAST_BACKUP_TRANS_ID); printTableRowEnd(); // LOG_BLK_CHG_SINCE_BACKUP printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_BLK_CHG_SINCE_BACKUP); fnPrintf( m_pHRequest, "Blocks changed since backup"); printLogFileEntryUD( pucLastCommitted, LOG_BLK_CHG_SINCE_BACKUP); printLogFileEntryUD( pucCheckpoint, LOG_BLK_CHG_SINCE_BACKUP); printLogFileEntryUD( pucUncommitted, LOG_BLK_CHG_SINCE_BACKUP); printTableRowEnd(); // LOG_LAST_CP_TRANS_ID printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LAST_CP_TRANS_ID); fnPrintf( m_pHRequest, "Last CP trans ID"); printLogFileEntryUD( pucLastCommitted, LOG_LAST_CP_TRANS_ID); printLogFileEntryUD( pucCheckpoint, LOG_LAST_CP_TRANS_ID); printLogFileEntryUD( pucUncommitted, LOG_LAST_CP_TRANS_ID); printTableRowEnd(); // LOG_PF_FIRST_BACKCHAIN printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BACKCHAIN); fnPrintf( m_pHRequest, "Backchain block address"); if (pucLastCommitted && FB2UD( &pucLastCommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucLastCommitted, LOG_PF_FIRST_BACKCHAIN); } if (pucCheckpoint && FB2UD( &pucCheckpoint[LOG_PF_FIRST_BACKCHAIN]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucCheckpoint, LOG_PF_FIRST_BACKCHAIN); } if (pucUncommitted && FB2UD( &pucUncommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucUncommitted, LOG_PF_FIRST_BACKCHAIN); } printTableRowEnd(); // LOG_PF_AVAIL_BLKS printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_PF_AVAIL_BLKS); fnPrintf( m_pHRequest, "Available blocks"); if (pucLastCommitted && FB2UD( &pucLastCommitted[LOG_PF_AVAIL_BLKS]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucLastCommitted, LOG_PF_AVAIL_BLKS); } if (pucCheckpoint && FB2UD( &pucCheckpoint[LOG_PF_AVAIL_BLKS]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucCheckpoint, LOG_PF_AVAIL_BLKS); } if (pucUncommitted && FB2UD( &pucUncommitted[LOG_PF_AVAIL_BLKS]) == BT_END) { fnPrintf( m_pHRequest, "none"); } else { printLogFileEntryUDX( pucUncommitted, LOG_PF_AVAIL_BLKS); } printTableRowEnd(); // LOG_LOGICAL_EOF printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LOGICAL_EOF); fnPrintf( m_pHRequest, "Logical EOF"); printLogFileEntryUD_X( pucLastCommitted, LOG_LOGICAL_EOF); printLogFileEntryUD_X( pucCheckpoint, LOG_LOGICAL_EOF); printLogFileEntryUD_X( pucUncommitted, LOG_LOGICAL_EOF); printTableRowEnd(); // LOG_LAST_RFL_COMMIT_ID printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_COMMIT_ID); fnPrintf( m_pHRequest, "Last RFL commit ID"); printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_COMMIT_ID); printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_COMMIT_ID); printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_COMMIT_ID); printTableRowEnd(); // LOG_KEEP_ABORTED_TRANS_IN_RFL printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_ABORTED_TRANS_IN_RFL); fnPrintf( m_pHRequest, "Keep aborted trans in RFL"); printLogFileEntryBool( pucLastCommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); printLogFileEntryBool( pucCheckpoint, LOG_KEEP_ABORTED_TRANS_IN_RFL); printLogFileEntryBool( pucUncommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); printTableRowEnd(); // LOG_PF_FIRST_BC_CNT printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BC_CNT); fnPrintf( m_pHRequest, "First BC count"); printLogFileEntryUC( pucLastCommitted, LOG_PF_FIRST_BC_CNT); printLogFileEntryUC( pucCheckpoint, LOG_PF_FIRST_BC_CNT); printLogFileEntryUC( pucUncommitted, LOG_PF_FIRST_BC_CNT); printTableRowEnd(); // LOG_KEEP_RFL_FILES printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_RFL_FILES); fnPrintf( m_pHRequest, "Keep RFL files"); printLogFileEntryBool( pucLastCommitted, LOG_KEEP_RFL_FILES); printLogFileEntryBool( pucCheckpoint, LOG_KEEP_RFL_FILES); printLogFileEntryBool( pucUncommitted, LOG_KEEP_RFL_FILES); printTableRowEnd(); // LOG_AUTO_TURN_OFF_KEEP_RFL printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_AUTO_TURN_OFF_KEEP_RFL); fnPrintf( m_pHRequest, "Auto turn off keep RFL"); printLogFileEntryBool( pucLastCommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); printLogFileEntryBool( pucCheckpoint, LOG_AUTO_TURN_OFF_KEEP_RFL); printLogFileEntryBool( pucUncommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); printTableRowEnd(); // LOG_PF_NUM_AVAIL_BLKS printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_PF_NUM_AVAIL_BLKS); fnPrintf( m_pHRequest, "Avail Blocks"); printLogFileEntryUD( pucLastCommitted, LOG_PF_NUM_AVAIL_BLKS); printLogFileEntryUD( pucCheckpoint, LOG_PF_NUM_AVAIL_BLKS); printLogFileEntryUD( pucUncommitted, LOG_PF_NUM_AVAIL_BLKS); printTableRowEnd(); // LOG_RFL_MAX_FILE_SIZE printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MAX_FILE_SIZE); fnPrintf( m_pHRequest, "Max file size"); printLogFileEntryUD( pucLastCommitted, LOG_RFL_MAX_FILE_SIZE); printLogFileEntryUD( pucCheckpoint, LOG_RFL_MAX_FILE_SIZE); printLogFileEntryUD( pucUncommitted, LOG_RFL_MAX_FILE_SIZE); printTableRowEnd(); // LOG_DB_SERIAL_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_DB_SERIAL_NUM); fnPrintf( m_pHRequest, "DB serial number"); printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_DB_SERIAL_NUM] : NULL); printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_DB_SERIAL_NUM] : NULL); printSerialNum( pucUncommitted ? &pucUncommitted[LOG_DB_SERIAL_NUM] : NULL); printTableRowEnd(); // LOG_LAST_TRANS_RFL_SERIAL_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_LAST_TRANS_RFL_SERIAL_NUM); fnPrintf( m_pHRequest, "Last Trans RFL serial number"); printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); printSerialNum( pucUncommitted ? &pucUncommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); printTableRowEnd(); // LOG_RFL_NEXT_SERIAL_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_RFL_NEXT_SERIAL_NUM); fnPrintf( m_pHRequest, "Next RFL serial number"); printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_RFL_NEXT_SERIAL_NUM] : NULL); printSerialNum( pucUncommitted ? &pucUncommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); printTableRowEnd(); // LOG_INC_BACKUP_SERIAL_NUM printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SERIAL_NUM); fnPrintf( m_pHRequest, "Incremental backup serial number"); printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_INC_BACKUP_SERIAL_NUM] : NULL); printSerialNum( pucUncommitted ? &pucUncommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); printTableRowEnd(); // LOG_MAX_FILE_SIZE printTableRowStart( bHighlight = !bHighlight); fnPrintf( m_pHRequest, "0x%X", LOG_MAX_FILE_SIZE); fnPrintf( m_pHRequest, "Maximum file size (64K units)"); printLogFileEntryUW( pucLastCommitted, LOG_MAX_FILE_SIZE); printLogFileEntryUW( pucCheckpoint, LOG_MAX_FILE_SIZE); printLogFileEntryUW( pucUncommitted, LOG_MAX_FILE_SIZE); printTableRowEnd(); printTableEnd(); } /******************************************************************* Desc: ********************************************************************/ void F_WebPage::printSerialNum( FLMBYTE * pucSerialNum) { if (pucSerialNum) { printTableDataStart( FALSE, JUSTIFY_LEFT); // fnPrintf( m_pHRequest, "0x"); for (int iLoop = 0; iLoop < F_SERIAL_NUM_SIZE; iLoop++) { fnPrintf( m_pHRequest, "%02X ", pucSerialNum[iLoop]); } printTableDataEnd(); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as unsigned 4 digit hex minimum. ********************************************************************/ void F_WebPage::printLogFileEntryUDX( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { fnPrintf( m_pHRequest, "0x%04X", FB2UD( &pucLog[uiOffset])); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as unsigned decimal hex in parenthesis. ********************************************************************/ void F_WebPage::printLogFileEntryUD_X( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { printTableDataStart( TRUE, JUSTIFY_LEFT); printCommaNumText( (FLMUINT64) FB2UD( &pucLog[uiOffset])); fnPrintf( m_pHRequest, " (0x%X)", FB2UD( &pucLog[uiOffset])); printTableDataEnd(); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as unsigned decimal. ********************************************************************/ void F_WebPage::printLogFileEntryUD( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { printCommaNum( (FLMUINT64) FB2UD( &pucLog[uiOffset]), JUSTIFY_LEFT); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as unsigned word. ********************************************************************/ void F_WebPage::printLogFileEntryUW( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { printCommaNum( (FLMUINT64) FB2UW( &pucLog[uiOffset]), JUSTIFY_LEFT); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as unsigned char. ********************************************************************/ void F_WebPage::printLogFileEntryUC( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { fnPrintf( m_pHRequest, "%u", (unsigned char) pucLog[uiOffset]); } else { fnPrintf( m_pHRequest, "-"); } } /******************************************************************* Desc: Print a table entry as yes or no (FLMBOOL) ********************************************************************/ void F_WebPage::printLogFileEntryBool( FLMBYTE * pucLog, FLMUINT uiOffset) { if (pucLog) { printTableDataStart( TRUE, JUSTIFY_LEFT); printYesNo( (FLMBOOL) pucLog[uiOffset]); printTableDataEnd(); } else { fnPrintf( m_pHRequest, "-"); } } libflaim-4.9.966/src/fnumber.cpp0000644000175000017500000004566010510774540020031 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines to handle numbers. // Tabs: 3 // // Copyright (c) 1999-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Given an unsigned number create the matching FLAIM-specific BCD number. Note: If terminating byte is half-full, low-nibble value is undefined. Example: -125 creates B1-25-FX Method: Using a MOD algorithm, stack BCD values -- popping to destination reverses the order for correct final sequence ****************************************************************************/ FLMEXP RCODE FLMAPI FlmUINT2Storage( FLMUINT uiNum, FLMUINT * puiBufLength, FLMBYTE * pBuf) { FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1]; FLMBYTE * pucNibStk; #ifdef FLM_DEBUG FLMBYTE * pucNibStkEnd = &ucNibStk[ sizeof( ucNibStk)]; #endif flmAssert( *puiBufLength >= F_MAX_NUM_BUF); // push spare (undefined) nibble for possible half-used terminating byte pucNibStk = &ucNibStk[ 1]; // push terminator nibble -- popped last *pucNibStk++ = 0x0F; // push digits // do 32 bit division until we get down to last digit while( uiNum >= 10) { // push BCD nibbles in reverse order *pucNibStk++ = (FLMBYTE)(uiNum % 10); uiNum /= 10; } // push last nibble of number *pucNibStk++ = (FLMBYTE)uiNum; f_assert( pucNibStk <= pucNibStkEnd); // count: nibbleCount / 2 and truncate *puiBufLength = ((pucNibStk - ucNibStk) >> 1); // Pop stack and pack nibbles into byte stream a pair at a time do { *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); } while( (pucNibStk -= 2) > &ucNibStk[ 1]); return( FERR_OK); } /**************************************************************************** Desc: Given a 64 bit unsigned number create the matching FLAIM-specific BCD number. Note: If terminating byte is half-full, low-nibble value is undefined. Example: -125 creates B1-25-FX Method: Using a MOD algorithm, stack BCD values -- popping to destination reverses the order for correct final sequence ****************************************************************************/ FLMEXP RCODE FLMAPI FlmUINT64ToStorage( FLMUINT64 ui64Num, FLMUINT * puiBufLength, FLMBYTE * pBuf) { FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1]; FLMBYTE * pucNibStk; flmAssert( *puiBufLength >= F_MAX_NUM64_BUF); // push spare (undefined) nibble for possible half-used terminating byte pucNibStk = &ucNibStk[ 1]; // push terminator nibble -- popped last *pucNibStk++ = 0x0F; // push digits // do 64 bit division until we get down to last digit. while( ui64Num >= 10) { // push BCD nibbles in reverse order *pucNibStk++ = (FLMBYTE)(ui64Num % 10); ui64Num /= 10; } // push last nibble of number *pucNibStk++ = (FLMBYTE)ui64Num; // count: nibbleCount / 2 and truncate *puiBufLength = ((pucNibStk - ucNibStk) >> 1); // Pop stack and pack nibbles into byte stream a pair at a time do { *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); } while( (pucNibStk -= 2) > &ucNibStk[ 1]); return( FERR_OK); } /**************************************************************************** Desc: Given an signed number create the matching FLAIM-specific BCD number. Note: If terminating byte is half-full, low-nibble value is undefined. Example: -125 creates B1-25-FX Method: Using a MOD algorithm, stack BCD values -- popping to destination reverses the order for correct final sequence ****************************************************************************/ FLMEXP RCODE FLMAPI FlmINT2Storage( FLMINT iNum, FLMUINT * puiBufLength, FLMBYTE * pBuf) { FLMUINT uiNum; FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1]; FLMBYTE * pucNibStk; FLMBOOL bNegFlag; #ifdef FLM_DEBUG FLMBYTE * pucNibStkEnd = &ucNibStk[ sizeof( ucNibStk)]; #endif flmAssert( *puiBufLength >= F_MAX_NUM_BUF); pucNibStk = &ucNibStk[ 1]; *pucNibStk++ = 0x0F; if (iNum < 0) { bNegFlag = TRUE; if (iNum == FLM_MIN_INT) { uiNum = (FLMUINT)(FLM_MAX_INT) + 1; } else { uiNum = (FLMUINT)(-iNum); } } else { bNegFlag = FALSE; uiNum = (FLMUINT)iNum; } while( uiNum >= 10) { *pucNibStk++ = (FLMBYTE)(uiNum % 10); uiNum /= 10; } *pucNibStk++ = (FLMBYTE)uiNum; if( bNegFlag) { *pucNibStk++ = 0x0B; } f_assert( pucNibStk <= pucNibStkEnd); *puiBufLength = ((pucNibStk - ucNibStk) >> 1); do { *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); } while( (pucNibStk -= 2) > &ucNibStk[ 1]); return( FERR_OK); } /**************************************************************************** Desc: Given a 64 bit signed number create the matching FLAIM-specific BCD number. Note: If terminating byte is half-full, low-nibble value is undefined. Example: -125 creates B1-25-FX Method: Using a MOD algorithm, stack BCD values -- popping to destination reverses the order for correct final sequence ****************************************************************************/ FLMEXP RCODE FLMAPI FlmINT64ToStorage( FLMINT64 i64Num, FLMUINT * puiBufLength, FLMBYTE * pBuf) { FLMUINT64 ui64Num; FLMBYTE ucNibStk[ F_MAX_NUM64_BUF + 1]; FLMBYTE * pucNibStk; FLMBOOL bNegFlag; flmAssert( *puiBufLength >= F_MAX_NUM64_BUF); pucNibStk = &ucNibStk[ 1]; *pucNibStk++ = 0x0F; if (i64Num < 0) { bNegFlag = TRUE; if (i64Num == FLM_MIN_INT64) { ui64Num = (FLMUINT64)(FLM_MAX_INT64) + 1; } else { ui64Num = (FLMUINT64)(-i64Num); } } else { bNegFlag = FALSE; ui64Num = (FLMUINT64)i64Num; } while( ui64Num >= 10) { *pucNibStk++ = (FLMBYTE)(ui64Num % 10); ui64Num /= 10; } *pucNibStk++ = (FLMBYTE)ui64Num; if (bNegFlag) { *pucNibStk++ = 0x0B; } *puiBufLength = ((pucNibStk - ucNibStk) >> 1); do { *pBuf++ = (FLMBYTE)((pucNibStk[ -1] << 4) | pucNibStk[ -2]); } while( (pucNibStk -= 2) > &ucNibStk[ 1]); return( FERR_OK); } /**************************************************************************** Desc: Returns a signed value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2INT( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMINT * piNum) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &uiNum, &bNegFlag))) { if (bNegFlag) { // If bNegFlag is set, we will have already checked to make sure // the value is in range inside of flmBcd2Num. if (uiNum == (FLMUINT)(FLM_MAX_INT) + 1) { *piNum = FLM_MIN_INT; } else { *piNum = -((FLMINT)uiNum); } } // If the value is positive, we will have checked to make sure the // number did not overflow FLM_MAX_UINT, but not FLM_MAX_INT. else if (uiNum > (FLMUINT)(FLM_MAX_INT)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { *piNum = (FLMINT)uiNum; } } return( rc); } /**************************************************************************** Desc: Returns a signed value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2INT32( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMINT32 * pi32Num) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &uiNum, &bNegFlag))) { if (bNegFlag) { if (uiNum > (FLMUINT)(FLM_MAX_INT32) + 1) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else if (uiNum == (FLMUINT)(FLM_MAX_INT32) + 1) { *pi32Num = FLM_MIN_INT32; } else { *pi32Num = -((FLMINT32)uiNum); } } // If the value is positive, we will have checked to make sure the // number did not overflow FLM_MAX_UINT, but not FLM_MAX_INT32. else if (uiNum > (FLMUINT)(FLM_MAX_INT32)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { *pi32Num = (FLMINT32)uiNum; } } return( rc); } /**************************************************************************** Desc: Returns a 64 bit signed value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2INT64( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMINT64 * pi64Num) { RCODE rc = FERR_OK; FLMUINT64 ui64Num; FLMBOOL bNegFlag; if( RC_OK(rc = flmBcd2Num64( uiValueType, uiValueLength, pucValue, &ui64Num, &bNegFlag))) { if (bNegFlag) { // If bNegFlag is set, we will have already checked to make sure // the value is in range inside of flmBcd2Num64. if (ui64Num == (FLMUINT64)(FLM_MAX_INT64) + 1) { *pi64Num = FLM_MIN_INT64; } else { *pi64Num = -((FLMINT64)ui64Num); } } // If the value is positive, we will have checked to make sure the // number did not overflow FLM_MAX_UINT64, but not FLM_MAX_INT64. else if (ui64Num > (FLMUINT64)(FLM_MAX_INT64)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { *pi64Num = (FLMINT64)ui64Num; } } return( rc); } /**************************************************************************** Desc: Returns a unsigned value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2UINT( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMUINT * puiNum) { RCODE rc = FERR_OK; FLMBOOL bNegFlag; if( RC_OK( rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, puiNum, &bNegFlag))) { if (bNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } } return( rc); } /**************************************************************************** Desc: Returns a unsigned value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2UINT32( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMUINT32 * pui32Num) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if( RC_OK(rc = flmBcd2Num( uiValueType, uiValueLength, pucValue, &uiNum, &bNegFlag))) { if (bNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } // On 64 bit platforms FLM_MAX_UINT32 will be less than FLM_MAX_UINT // so we need to test against it. Otherwise, we have already tested // against FLM_MAX_UINT, and it is the same as FLM_MAX_UINT32, so there // is no need to test against it. #ifdef FLM_64BIT else if (uiNum > FLM_MAX_UINT32) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } #endif else { *pui32Num = (FLMUINT32)uiNum; } } return( rc); } /**************************************************************************** Desc: Returns a unsigned value from a BCD value. The data may be a number type, or context type. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStorage2UINT64( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMUINT64 * pui64Num) { RCODE rc = FERR_OK; FLMBOOL bNegFlag; if( RC_OK(rc = flmBcd2Num64( uiValueType, uiValueLength, pucValue, pui64Num, &bNegFlag))) { if (bNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } } return( rc); } /**************************************************************************** Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a number ****************************************************************************/ RCODE flmBcd2Num( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMUINT * puiNum, FLMBOOL * pbNegFlag) { RCODE rc = FERR_OK; FLMUINT uiTotalNum; FLMUINT uiByte; FLMUINT uiNibble; FLMUINT uiMaxBeforeMultValue; FLMUINT uiMaxValue; if (pucValue == NULL) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } switch (uiValueType) { case FLM_NUMBER_TYPE: { uiTotalNum = 0; if ((*pucValue & 0xF0) == 0xB0) { *pbNegFlag = TRUE; uiNibble = 1; uiMaxBeforeMultValue = ((FLMUINT)(FLM_MAX_INT) + 1) / 10; uiMaxValue = ((FLMUINT)(FLM_MAX_INT) + 1); } else { *pbNegFlag = FALSE; uiNibble = 0; uiMaxBeforeMultValue = (FLM_MAX_UINT) / 10; uiMaxValue = FLM_MAX_UINT; } // Get each nibble and use to create the number while (uiValueLength) { // An odd value for uiNibble means we are on the 2nd nibble of // the byte. if (uiNibble & 1) { uiByte = (FLMINT)(*pucValue & 0x0F); pucValue++; uiValueLength--; } else { uiByte = (FLMUINT)(*pucValue >> 4); } uiNibble++; if (uiByte == 0x0F) { break; } if (uiTotalNum > uiMaxBeforeMultValue) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1); if (uiTotalNum > uiMaxValue - uiByte) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } uiTotalNum += uiByte; } *puiNum = uiTotalNum; break; } case FLM_TEXT_TYPE : { uiTotalNum = 0; if (*pucValue == '-') { *pbNegFlag = TRUE; uiMaxBeforeMultValue = ((FLMUINT)(FLM_MAX_INT) + 1) / 10; uiMaxValue = (FLMUINT)(FLM_MAX_INT) + 1; } else { *pbNegFlag = FALSE; uiMaxBeforeMultValue = (FLM_MAX_UINT) / 10; uiMaxValue = FLM_MAX_UINT; } while (uiValueLength--) { if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE) { break; } uiByte = (FLMUINT)(*pucValue - ASCII_ZERO); if (uiTotalNum > uiMaxBeforeMultValue) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1); if (uiTotalNum > uiMaxValue - uiByte) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } uiTotalNum += uiByte; pucValue++; } *puiNum = uiTotalNum; break; } case FLM_CONTEXT_TYPE : { if (uiValueLength == sizeof( FLMUINT32)) { *puiNum = (FLMUINT)( FB2UD( pucValue)); *pbNegFlag = FALSE; } break; } default: { flmAssert( 0); return( RC_SET( FERR_CONV_ILLEGAL)); } } Exit: return( rc); } /**************************************************************************** Desc: Converts FT_NUMBER and FT_CONTEXT storage buffers to a 64 bit number ****************************************************************************/ RCODE flmBcd2Num64( FLMUINT uiValueType, FLMUINT uiValueLength, const FLMBYTE * pucValue, FLMUINT64 * pui64Num, FLMBOOL * pbNegFlag) { RCODE rc = FERR_OK; FLMUINT64 ui64TotalNum; FLMUINT uiByte; FLMUINT uiNibble; FLMUINT64 ui64MaxBeforeMultValue; FLMUINT64 ui64MaxValue; if (pucValue == NULL) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } switch (uiValueType) { case FLM_NUMBER_TYPE: { ui64TotalNum = 0; if ((*pucValue & 0xF0) == 0xB0) { *pbNegFlag = TRUE; uiNibble = 1; ui64MaxBeforeMultValue = ((FLMUINT64)(FLM_MAX_INT64) + 1) / 10; ui64MaxValue = (FLMUINT64)(FLM_MAX_INT64) + 1; } else { *pbNegFlag = FALSE; uiNibble = 0; ui64MaxBeforeMultValue = (FLM_MAX_UINT64) / 10; ui64MaxValue = FLM_MAX_UINT64; } // Get each nibble and use to create the number while (uiValueLength) { // An odd value for uiNibble means we are on the 2nd nibble of // the byte. if (uiNibble & 1) { uiByte = (FLMINT)(*pucValue & 0x0F); pucValue++; uiValueLength--; } else { uiByte = (FLMUINT)(*pucValue >> 4); } uiNibble++; if (uiByte == 0x0F) { break; } if (ui64TotalNum > ui64MaxBeforeMultValue) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } ui64TotalNum = (ui64TotalNum << 3) + (ui64TotalNum << 1); if (ui64TotalNum > ui64MaxValue - (FLMUINT64)uiByte) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } ui64TotalNum += (FLMUINT64)uiByte; } *pui64Num = ui64TotalNum; break; } case FLM_TEXT_TYPE : { ui64TotalNum = 0; if (*pucValue == '-') { *pbNegFlag = TRUE; ui64MaxBeforeMultValue = ((FLMUINT64)(FLM_MAX_INT64) + 1) / 10; ui64MaxValue = (FLMUINT64)(FLM_MAX_INT64) + 1; } else { *pbNegFlag = FALSE; ui64MaxBeforeMultValue = (FLM_MAX_UINT64) / 10; ui64MaxValue = FLM_MAX_UINT64; } while (uiValueLength--) { if( *pucValue < ASCII_ZERO || *pucValue > ASCII_NINE) { break; } uiByte = (FLMUINT)(*pucValue - ASCII_ZERO); if (ui64TotalNum > ui64MaxBeforeMultValue) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } ui64TotalNum = (ui64TotalNum << 3) + (ui64TotalNum << 1); if (ui64TotalNum > ui64MaxValue - (FLMUINT64)uiByte) { if (*pbNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } goto Exit; } ui64TotalNum += (FLMUINT64)uiByte; pucValue++; } *pui64Num = ui64TotalNum; break; } case FLM_CONTEXT_TYPE : { if (uiValueLength == sizeof( FLMUINT32)) { *pui64Num = (FLMUINT64)( FB2UD( pucValue)); *pbNegFlag = FALSE; } break; } default: { flmAssert( 0); return( RC_SET( FERR_CONV_ILLEGAL)); } } Exit: return( rc); } libflaim-4.9.966/src/fsv.cpp0000644000175000017500000026631710510774540017175 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: FLAIM server functions // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id$ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE fsvIteratorParse( FSV_WIRE * pWire, F_Pool * pPool); FSTATIC RCODE fsvIteratorWhereParse( FSV_WIRE * pWire, F_Pool * pPool); FSTATIC RCODE fsvIteratorFromParse( FSV_WIRE * pWire, F_Pool * pPool); FSTATIC RCODE fsvIteratorSelectParse( FSV_WIRE * pWire, F_Pool * pPool); FSTATIC RCODE fsvDbGetBlocks( HFDB hDb, FLMUINT uiAddress, FLMUINT uiMinTransId, FLMUINT * puiCount, FLMUINT * puiBlocksExamined, FLMUINT * puiNextBlkAddr, FLMUINT uiFlags, F_Pool * pPool, FLMBYTE ** ppBlocks, FLMUINT * puiBytes); FSTATIC RCODE fsvGetHandles( FSV_WIRE * pWire); FSV_SCTX * gv_pGlobalContext = NULL; /**************************************************************************** Desc: Initializes the server's global context. ****************************************************************************/ RCODE fsvInitGlobalContext( FLMUINT uiMaxSessions, const char * pszServerBasePath, FSV_LOG_FUNC pLogFunc) { RCODE rc = FERR_OK; FSV_SCTX * pTmpContext = NULL; if (gv_pGlobalContext) { // Context already initialized goto Exit; } if ((pTmpContext = f_new FSV_SCTX) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = pTmpContext->Setup( uiMaxSessions, pszServerBasePath, pLogFunc))) { goto Exit; } Exit: if (RC_BAD( rc)) { if (pTmpContext) { pTmpContext->Release(); } } else if (pTmpContext) { gv_pGlobalContext = pTmpContext; } return (rc); } /**************************************************************************** Desc: Frees any resources allocated to the global context. ****************************************************************************/ void fsvFreeGlobalContext(void) { if (gv_pGlobalContext) { gv_pGlobalContext->Release(); gv_pGlobalContext = NULL; } } /**************************************************************************** Desc: Sets the server's base (relative) path ****************************************************************************/ RCODE fsvSetBasePath( const char * pszServerBasePath) { RCODE rc = FERR_OK; if (!gv_pGlobalContext) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = gv_pGlobalContext->SetBasePath( pszServerBasePath))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Sets the server's temporary directory ****************************************************************************/ RCODE fsvSetTempDir( const char * pszTempDir) { RCODE rc = FERR_OK; if (!gv_pGlobalContext) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = gv_pGlobalContext->SetTempDir( pszTempDir))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Returns a pointer to the server's global context object. ****************************************************************************/ RCODE fsvGetGlobalContext( FSV_SCTX ** ppGlobalContext) { *ppGlobalContext = gv_pGlobalContext; return (FERR_OK); } /**************************************************************************** Desc: This is the function that processes FLAIM requests. ****************************************************************************/ RCODE fsvProcessRequest( FCS_DIS * pDataIStream, FCS_DOS * pDataOStream, F_Pool * pScratchPool, FLMUINT * puiSessionIdRV) { void * pvMark = NULL; FSV_WIRE Wire(pDataIStream, pDataOStream); RCODE rc = FERR_OK; // Set the temporary pool if (pScratchPool) { pvMark = pScratchPool->poolMark(); Wire.setPool( pScratchPool); } // Read the request if (RC_BAD( rc = Wire.read())) { goto Exit; } // Close the input stream. pDataIStream->close(); Wire.setDIStream( NULL); // Get any required handles. if (RC_BAD( rc = fsvGetHandles( &Wire))) { goto Exit; } // Call the appropriate handler function. switch (Wire.getClass()) { case FCS_OPCLASS_GLOBAL: { if (RC_BAD( rc = fsvOpClassGlobal( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_SESSION: { if (RC_BAD( rc = fsvOpClassSession( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_DATABASE: { if (RC_BAD( rc = fsvOpClassDatabase( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_TRANS: { if (RC_BAD( rc = fsvOpClassTransaction( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_RECORD: { if (RC_BAD( rc = fsvOpClassRecord( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_ITERATOR: { if (RC_BAD( rc = fsvOpClassIterator( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_BLOB: { rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } case FCS_OPCLASS_DIAG: { if (RC_BAD( rc = fsvOpClassDiag( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_FILE: { if (RC_BAD( rc = fsvOpClassFile( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_ADMIN: { if (RC_BAD( rc = fsvOpClassAdmin( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_INDEX: { if (RC_BAD( rc = fsvOpClassIndex( &Wire))) { goto Exit; } break; } case FCS_OPCLASS_MISC: { if (RC_BAD( rc = fsvOpClassMisc( &Wire))) { goto Exit; } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } if (puiSessionIdRV) { // Set the session ID so that the calling routine has the option of // performing cleanup on an error *puiSessionIdRV = Wire.getSessionId(); } Exit: if (RC_BAD( rc)) { // If the input stream is still open, the handler never send any // data to the client. Close the input stream and try to send the // error code to the client. if (pDataIStream->isOpen()) { (void) pDataIStream->close(); Wire.setDIStream( NULL); } if (RC_OK( Wire.sendOpcode( Wire.getClass(), Wire.getOp()))) { if (RC_OK( Wire.sendRc( rc))) { if (RC_OK( Wire.sendTerminate())) { pDataOStream->close(); } } } } else { pDataOStream->close(); } if (pScratchPool) { pScratchPool->poolReset( pvMark); } return (rc); } /**************************************************************************** Desc: Performs a diagnostic operation ****************************************************************************/ RCODE fsvOpClassDiag( FSV_WIRE * pWire) { RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Service the request. switch (pWire->getOp()) { case FCS_OP_DIAG_HTD_ECHO: { // Simply echo the record back to the client. This is done below // when the response is sent to the client. break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_DIAG, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { switch (pWire->getOp()) { case FCS_OP_DIAG_HTD_ECHO: { if (pWire->getRecord() != NULL) { if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_HTD, pWire->getRecord()))) { goto Exit; } } break; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a file system operation ****************************************************************************/ RCODE fsvOpClassFile( FSV_WIRE * pWire) { RCODE rc = FERR_OK; RCODE opRc = FERR_OK; FSV_SCTX * pServerContext = NULL; FLMUNICODE * puzSourcePath; char szSourcePath[F_PATH_MAX_SIZE]; // Set up local variables. if (RC_BAD( opRc = fsvGetGlobalContext( &pServerContext))) { goto OP_EXIT; } puzSourcePath = pWire->getFilePath(); if (puzSourcePath) { // Convert the UNICODE URL to a server path. if (RC_BAD( rc = pServerContext->BuildFilePath( puzSourcePath, szSourcePath))) { goto Exit; } } // Service the request. switch (pWire->getOp()) { case FCS_OP_FILE_EXISTS: { if (!puzSourcePath) { opRc = RC_SET( FERR_SYNTAX); goto OP_EXIT; } if (RC_BAD( opRc = gv_FlmSysData.pFileSystem->doesFileExist( szSourcePath))) { goto OP_EXIT; } break; } case FCS_OP_FILE_DELETE: { if (!puzSourcePath) { opRc = RC_SET( FERR_SYNTAX); goto OP_EXIT; } if (RC_BAD( opRc = gv_FlmSysData.pFileSystem->deleteFile( szSourcePath))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_FILE, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs an administrative operation ****************************************************************************/ RCODE fsvOpClassAdmin( FSV_WIRE * pWire) { RCODE opRc = FERR_OK; RCODE rc = FERR_OK; opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_ADMIN, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a global operation ****************************************************************************/ RCODE fsvOpClassGlobal( FSV_WIRE * pWire) { FSV_SCTX * pServerContext; NODE * pTree = NULL; RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Service the request. if (RC_BAD( rc = fsvGetGlobalContext( &pServerContext))) { goto Exit; } switch (pWire->getOp()) { case FCS_OP_GLOBAL_STATS_START: { if (RC_BAD( opRc = FlmConfig( FLM_START_STATS, 0, 0))) { goto OP_EXIT; } break; } case FCS_OP_GLOBAL_STATS_STOP: { if (RC_BAD( opRc = FlmConfig( FLM_STOP_STATS, 0, 0))) { goto OP_EXIT; } break; } case FCS_OP_GLOBAL_STATS_RESET: { if (RC_BAD( opRc = FlmConfig( FLM_RESET_STATS, 0, 0))) { goto OP_EXIT; } break; } case FCS_OP_GLOBAL_MEM_INFO_GET: { FLM_MEM_INFO memInfo; FlmGetMemoryInfo( &memInfo); if (RC_BAD( opRc = fcsBuildMemInfo( &memInfo, pWire->getPool(), &pTree))) { goto OP_EXIT; } break; } case FCS_OP_GLOBAL_GET_THREAD_INFO: { if (RC_BAD( opRc = fcsBuildThreadInfo( pWire->getPool(), &pTree))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_GLOBAL, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { if (pTree) { if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pTree))) { goto Exit; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a session operation ****************************************************************************/ RCODE fsvOpClassSession( FSV_WIRE * pWire) { FLMUINT uiSessionIdRV; FSV_SCTX * pServerContext; FSV_SESN * pSession = NULL; RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Service the request. if (RC_BAD( opRc = fsvGetGlobalContext( &pServerContext))) { goto OP_EXIT; } switch (pWire->getOp()) { case FCS_OP_SESSION_OPEN: { // Create a new session. if (RC_BAD( opRc = pServerContext->OpenSession( pWire->getClientVersion(), pWire->getFlags(), &uiSessionIdRV, &pSession))) { goto OP_EXIT; } break; } case FCS_OP_SESSION_CLOSE: { // Close the session. if (RC_BAD( opRc = pServerContext->CloseSession( pWire->getSessionId()))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_SESSION, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { if (pWire->getOp() == FCS_OP_SESSION_OPEN) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_SESSION_ID, uiSessionIdRV))) { goto Exit; } if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_SESSION_COOKIE, pSession->getCookie()))) { goto Exit; } if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_FLAGS, FCS_SESSION_GEDCOM_SUPPORT))) { goto Exit; } if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_FLAIM_VERSION, FLM_CUR_FILE_FORMAT_VER_NUM))) { goto Exit; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a record or DRN operation ****************************************************************************/ RCODE fsvOpClassRecord( FSV_WIRE * pWire) { FSV_SESN * pSession; HFDB hDb; FLMUINT uiContainer; FLMUINT uiIndex; FLMUINT uiAutoTrans; FLMUINT uiDrn; FLMUINT uiFlags; FlmRecord * pRecord = NULL; FlmRecord * pRecordRV = NULL; FLMUINT uiDrnRV = 0; RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Get a pointer to the session object. if ((pSession = pWire->getSession()) == NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } // Get the database handle. This is needed by all of the record // operations. if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } // Initialize local variables. uiContainer = pWire->getContainerId(); uiIndex = pWire->getIndexId(); uiDrn = pWire->getDrn(); uiAutoTrans = pWire->getAutoTrans(); uiFlags = pWire->getFlags(); pRecord = pWire->getRecord(); // Perform the operation. switch (pWire->getOp()) { case FCS_OP_RECORD_RETRIEVE: { if (!uiFlags) { uiFlags = FO_EXACT; } if (pWire->getBoolean()) { // Fetch the record if (RC_BAD( opRc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, uiFlags, &pRecordRV, &uiDrnRV))) { goto OP_EXIT; } } else { // Just get the DRN if (RC_BAD( opRc = FlmRecordRetrieve( hDb, uiContainer, uiDrn, uiFlags, NULL, &uiDrnRV))) { goto OP_EXIT; } } break; } case FCS_OP_RECORD_ADD: { uiDrnRV = uiDrn; if (RC_BAD( opRc = FlmRecordAdd( hDb, uiContainer, &uiDrnRV, pRecord, uiAutoTrans))) { goto OP_EXIT; } break; } case FCS_OP_RECORD_MODIFY: { if (RC_BAD( opRc = FlmRecordModify( hDb, uiContainer, uiDrn, pRecord, uiAutoTrans))) { goto OP_EXIT; } break; } case FCS_OP_RECORD_DELETE: { if (RC_BAD( opRc = FlmRecordDelete( hDb, uiContainer, uiDrn, uiAutoTrans))) { goto OP_EXIT; } break; } case FCS_OP_RESERVE_NEXT_DRN: { uiDrnRV = uiDrn; if (RC_BAD( opRc = FlmReserveNextDrn( hDb, uiContainer, &uiDrnRV))) { goto OP_EXIT; } break; } case FCS_OP_KEY_RETRIEVE: { if (pSession->getClientVersion() >= FCS_VERSION_1_1_1) { if (RC_BAD( opRc = FlmKeyRetrieve( hDb, uiIndex, uiContainer, pRecord, uiDrn, uiFlags, &pRecordRV, &uiDrnRV))) { goto OP_EXIT; } } else { FLMUINT uiKeyContainer = 0; if (pRecord) { uiKeyContainer = pRecord->getContainerID(); } // Older clients sent index # in the container tag. if (RC_BAD( opRc = FlmKeyRetrieve( hDb, uiContainer, uiKeyContainer, pRecord, uiDrn, uiFlags, &pRecordRV, &uiDrnRV))) { goto OP_EXIT; } } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_RECORD, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { if (pRecordRV) { if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_RECORD, pRecordRV))) { goto Exit; } } if (uiDrnRV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_DRN, uiDrnRV))) { goto Exit; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: if (pRecordRV) { pRecordRV->Release(); } return (rc); } /**************************************************************************** Desc: Performs a database operation. ****************************************************************************/ RCODE fsvOpClassDatabase( FSV_WIRE * pWire) { RCODE rc = FERR_OK; RCODE opRc = FERR_OK; FSV_SESN * pSession; HFDB hDb = HFDB_NULL; CREATE_OPTS CreateOptsRV; FLMUINT uiBlockCountRV = 0; FLMUINT uiBlocksExaminedRV = 0; FLMUINT uiBlockAddrRV = 0; FLMUINT uiTransIdRV; FLMUINT64 ui64NumValue1RV = 0; FLMUINT64 ui64NumValue2RV = 0; FLMUINT64 ui64NumValue3RV = 0; FLMBOOL bBoolValueRV = FALSE; FLMUINT uiItemIdRV = 0; char szItemName[64]; NODE * pHTDRV = NULL; char szPathRV[F_PATH_MAX_SIZE]; F_NameTable nameTable; FLMBOOL bHaveCreateOptsVal = FALSE; FLMBOOL bHavePathValue = FALSE; FLMBYTE * pBinary = NULL; FLMUINT uiBinSize = 0; szItemName[0] = 0; if ((pSession = pWire->getSession()) == NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } if (pWire->getOp() != FCS_OP_DATABASE_OPEN && pWire->getOp() != FCS_OP_DATABASE_CREATE) { // Get the database handle for all database operations other than // open and create. if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } } switch (pWire->getOp()) { case FCS_OP_DATABASE_OPEN: { if (RC_BAD( opRc = pSession->OpenDatabase( pWire->getFilePath(), pWire->getFilePath3(), pWire->getFilePath2(), pWire->getFlags()))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_CREATE: { CREATE_OPTS createOpts; pWire->copyCreateOpts( &createOpts); if (RC_BAD( opRc = pSession->CreateDatabase( pWire->getFilePath(), pWire->getFilePath3(), pWire->getFilePath2(), pWire->getDictPath(), pWire->getDictBuffer(), &createOpts))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_CLOSE: { if (RC_BAD( opRc = pSession->CloseDatabase())) { goto OP_EXIT; } break; } case FCS_OP_DB_REDUCE_SIZE: { if (RC_BAD( opRc = FlmDbReduceSize( hDb, (FLMUINT) pWire->getCount(), &uiBlockCountRV))) { goto OP_EXIT; } break; } case FCS_OP_GET_ITEM_NAME: { if (RC_BAD( opRc = FlmGetItemName( hDb, pWire->getItemId(), sizeof(szItemName), szItemName))) { goto OP_EXIT; } break; } case FCS_OP_GET_NAME_TABLE: { if (RC_BAD( rc = nameTable.setupFromDb( hDb))) { goto OP_EXIT; } break; } case FCS_OP_GET_COMMIT_CNT: { if (RC_BAD( opRc = FlmDbGetCommitCnt( hDb, &uiBlockCountRV))) { goto OP_EXIT; } break; } case FCS_OP_GET_TRANS_ID: { if (RC_BAD( opRc = FlmDbGetTransId( hDb, &uiTransIdRV))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_GET_CONFIG: { switch ((eDbGetConfigType) pWire->getType()) { case FDB_GET_VERSION: { // Doing via create opts to maintain backward compatibility. f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_VERSION, (void *) &CreateOptsRV.uiVersionNum))) { goto OP_EXIT; } bHaveCreateOptsVal = TRUE; break; } case FDB_GET_BLKSIZ: { // Doing via create opts to maintain backward compatibility. f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_BLKSIZ, (void *) &CreateOptsRV.uiBlockSize))) { goto OP_EXIT; } bHaveCreateOptsVal = TRUE; break; } case FDB_GET_DEFAULT_LANG: { // Doing via create opts to maintain backward compatibility. f_memset( &CreateOptsRV, 0, sizeof(CreateOptsRV)); if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_DEFAULT_LANG, (void *) &CreateOptsRV.uiDefaultLanguage))) { goto OP_EXIT; } bHaveCreateOptsVal = TRUE; break; } case FDB_GET_TRANS_ID: case FDB_GET_RFL_FILE_NUM: case FDB_GET_RFL_HIGHEST_NU: case FDB_GET_LAST_BACKUP_TRANS_ID: case FDB_GET_BLOCKS_CHANGED_SINCE_BACKUP: case FDB_GET_FILE_EXTEND_SIZE: case FDB_GET_APP_DATA: { FLMUINT uiTmpValue; if (RC_BAD( opRc = FlmDbGetConfig( hDb, (eDbGetConfigType) pWire->getType(), (void *) &uiTmpValue))) { goto OP_EXIT; } ui64NumValue1RV = (FLMUINT64) uiTmpValue; break; } case FDB_GET_RFL_FILE_SIZE_LIMITS: { FLMUINT uiTmpValue1; FLMUINT uiTmpValue2; if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_RFL_FILE_SIZE_LIMITS, (void *) &uiTmpValue1, (void *) &uiTmpValue2))) { goto OP_EXIT; } ui64NumValue1RV = (FLMUINT64) uiTmpValue1; ui64NumValue2RV = (FLMUINT64) uiTmpValue2; break; } case FDB_GET_RFL_KEEP_FLAG: case FDB_GET_AUTO_TURN_OFF_KEEP_RFL_FLAG: case FDB_GET_KEEP_ABORTED_TRANS_IN_RFL_FLAG: { if (RC_BAD( opRc = FlmDbGetConfig( hDb, (eDbGetConfigType) pWire->getType(), (void *) &bBoolValueRV))) { goto OP_EXIT; } break; } case FDB_GET_PATH: { if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_PATH, (void *) szPathRV))) { goto OP_EXIT; } bHavePathValue = TRUE; break; } case FDB_GET_CHECKPOINT_INFO: { CHECKPOINT_INFO checkpointInfo; if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_CHECKPOINT_INFO, (void *) &checkpointInfo))) { goto OP_EXIT; } if (RC_BAD( opRc = fcsBuildCheckpointInfo( &checkpointInfo, pWire->getPool(), &pHTDRV))) { goto OP_EXIT; } break; } case FDB_GET_LOCK_HOLDER: { F_LOCK_USER lockUser; if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_LOCK_HOLDER, (void *) &lockUser))) { goto OP_EXIT; } if (RC_BAD( opRc = fcsBuildLockUser( &lockUser, FALSE, pWire->getPool(), &pHTDRV))) { goto OP_EXIT; } break; } case FDB_GET_LOCK_WAITERS: { F_LOCK_USER * pLockUser = NULL; if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_LOCK_WAITERS, (void *) &pLockUser))) { if (pLockUser) { f_free( &pLockUser); } goto OP_EXIT; } if (RC_BAD( opRc = fcsBuildLockUser( pLockUser, TRUE, pWire->getPool(), &pHTDRV))) { if (pLockUser) { f_free( &pLockUser); } goto OP_EXIT; } if (pLockUser) { f_free( &pLockUser); } break; } case FDB_GET_RFL_DIR: { if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_RFL_DIR, (void *) szPathRV))) { goto OP_EXIT; } bHavePathValue = TRUE; break; } case FDB_GET_SERIAL_NUMBER: { uiBinSize = F_SERIAL_NUM_SIZE; if( RC_BAD( opRc = pWire->getPool()->poolAlloc( uiBinSize, (void **)&pBinary))) { goto OP_EXIT; } if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_SERIAL_NUMBER, (void *) pBinary))) { goto OP_EXIT; } break; } case FDB_GET_SIZES: { if (RC_BAD( opRc = FlmDbGetConfig( hDb, FDB_GET_SIZES, (void *) &ui64NumValue1RV, (void *) &ui64NumValue2RV, (void *) &ui64NumValue3RV))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } break; } case FCS_OP_DATABASE_CONFIG: { switch ((eDbConfigType) pWire->getType()) { case FDB_SET_APP_VERSION: case FDB_RFL_KEEP_FILES: case FDB_RFL_ROLL_TO_NEXT_FILE: case FDB_KEEP_ABORTED_TRANS_IN_RFL: case FDB_AUTO_TURN_OFF_KEEP_RFL: case FDB_SET_APP_DATA: { if (RC_BAD( opRc = FlmDbConfig( hDb, (eDbConfigType) pWire->getType(), (void *) ((FLMUINT) pWire->getNumber2()), (void *) ((FLMUINT) pWire->getNumber3())))) { goto OP_EXIT; } break; } case FDB_RFL_FILE_LIMITS: case FDB_FILE_EXTEND_SIZE: { if (RC_BAD( opRc = FlmDbConfig( hDb, (eDbConfigType) pWire->getType(), (void *) ((FLMUINT) pWire->getNumber1()), (void *) ((FLMUINT) pWire->getNumber2())))) { goto OP_EXIT; } break; } case FDB_RFL_DIR: { char * pszPath; F_Pool * pPool = pWire->getPool(); void * pvMark = pPool->poolMark(); if (RC_BAD( rc = fcsConvertUnicodeToNative( pPool, pWire->getFilePath(), &pszPath))) { goto Exit; } if (RC_BAD( opRc = FlmDbConfig( hDb, (eDbConfigType) pWire->getType(), (void *) pszPath, (void *) ((FLMUINT) pWire->getNumber3())))) { goto OP_EXIT; } pPool->poolReset( pvMark); break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } break; } case FCS_OP_DATABASE_LOCK: { if (RC_BAD( opRc = FlmDbLock( hDb, (eLockType) (FLMUINT) pWire->getNumber1(), (FLMINT) pWire->getSignedValue(), (FLMUINT) pWire->getFlags()))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_UNLOCK: { if (RC_BAD( opRc = FlmDbUnlock( hDb))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_GET_BLOCK: { uiBlockCountRV = (FLMUINT) pWire->getCount(); if (RC_BAD( opRc = fsvDbGetBlocks( hDb, pWire->getAddress(), pWire->getTransId(), &uiBlockCountRV, &uiBlocksExaminedRV, &uiBlockAddrRV, pWire->getFlags(), pWire->getPool(), &pBinary, &uiBinSize))) { goto OP_EXIT; } break; } case FCS_OP_DATABASE_CHECKPOINT: { if (RC_BAD( opRc = FlmDbCheckpoint( hDb, pWire->getFlags()))) { goto OP_EXIT; } break; } case FCS_OP_DB_SET_BACKUP_FLAG: { FLMBOOL bNewState = pWire->getBoolean(); if (!IsInCSMode( hDb)) { FDB * pDb = (FDB *) hDb; f_mutexLock( gv_FlmSysData.hShareMutex); if (pDb->pFile->bBackupActive && bNewState) { f_mutexUnlock( gv_FlmSysData.hShareMutex); opRc = RC_SET( FERR_BACKUP_ACTIVE); goto OP_EXIT; } pDb->pFile->bBackupActive = bNewState; f_mutexUnlock( gv_FlmSysData.hShareMutex); } else { if (RC_BAD( opRc = fcsSetBackupActiveFlag( hDb, bNewState))) { goto OP_EXIT; } } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_DATABASE, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } switch (pWire->getOp()) { case FCS_OP_DB_REDUCE_SIZE: case FCS_OP_GET_COMMIT_CNT: { // Return a count if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_COUNT, uiBlockCountRV))) { goto Exit; } break; } case FCS_OP_GET_NAME_TABLE: { // Return the name table. if (RC_OK( opRc)) { if (RC_BAD( rc = pWire->sendNameTable( WIRE_VALUE_NAME_TABLE, &nameTable))) { goto Exit; } } break; } case FCS_OP_GET_ITEM_NAME: { FLMUNICODE * puzItemNameRV; if (RC_OK( opRc)) { if (szItemName[0]) { if (RC_BAD( rc = fcsConvertNativeToUnicode( pWire->getPool(), szItemName, &puzItemNameRV))) { goto Exit; } if (RC_BAD( rc = pWire->sendString( WIRE_VALUE_ITEM_NAME, puzItemNameRV))) { goto Exit; } } } break; } case FCS_OP_GET_ITEM_ID: { if (uiItemIdRV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ITEM_ID, uiItemIdRV))) { goto Exit; } } break; } case FCS_OP_GET_TRANS_ID: { // Return the transaction id for the database. if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_TRANSACTION_ID, uiTransIdRV))) { goto Exit; } break; } case FCS_OP_DATABASE_GET_BLOCK: { // Return the requested block if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_COUNT, uiBlockCountRV))) { goto Exit; } if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER2, uiBlocksExaminedRV))) { goto Exit; } if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ADDRESS, uiBlockAddrRV))) { goto Exit; } if (uiBlockCountRV) { if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_BLOCK, pBinary, uiBinSize))) { goto Exit; } } break; } case FCS_OP_DATABASE_GET_CONFIG: { switch (pWire->getType()) { case FDB_GET_SERIAL_NUMBER: if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_SERIAL_NUM, pBinary, uiBinSize))) { goto Exit; } break; default: break; } break; } } if (bHaveCreateOptsVal) { if (RC_BAD( rc = pWire->sendCreateOpts( WIRE_VALUE_CREATE_OPTS, &CreateOptsRV))) { goto Exit; } } if (ui64NumValue1RV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER1, ui64NumValue1RV))) { goto Exit; } } if (ui64NumValue2RV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER2, ui64NumValue2RV))) { goto Exit; } } if (ui64NumValue3RV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_NUMBER3, ui64NumValue3RV))) { goto Exit; } } if (bBoolValueRV) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_BOOLEAN, bBoolValueRV))) { goto Exit; } } if (bHavePathValue) { FLMUNICODE * puzPath; if (RC_BAD( rc = fcsConvertNativeToUnicode( pWire->getPool(), szPathRV, &puzPath))) { goto Exit; } if (RC_BAD( rc = pWire->sendString( WIRE_VALUE_FILE_PATH, puzPath))) { goto Exit; } } if (pHTDRV) { if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pHTDRV))) { goto Exit; } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs an iterator (cursor) operation ****************************************************************************/ RCODE fsvOpClassIterator( FSV_WIRE * pWire) { RCODE rc = FERR_OK; RCODE opRc = FERR_OK; FSV_SESN * pSession = NULL; HFCURSOR hIterator = HFCURSOR_NULL; FLMBOOL bDoDrnOp = FALSE; FlmRecord * pRecordRV = NULL; FlmRecord * pTmpRecord = NULL; FLMUINT uiIteratorIdRV = FCS_INVALID_ID; FLMUINT uiCountRV = 0; FLMUINT uiDrnRV = 0; FLMBOOL bFlag = FALSE; // Get a pointer to the session object. if ((pSession = pWire->getSession()) == NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } // Get the iterator handle. if ((hIterator = pWire->getIteratorHandle()) == HFDB_NULL) { if (pWire->getOp() != FCS_OP_ITERATOR_INIT) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } } // Examine the wire flags for the operation. bDoDrnOp = (FLMBOOL) ( (pWire->getFlags() & FCS_ITERATOR_DRN_FLAG) ? (FLMBOOL) TRUE : (FLMBOOL) FALSE ); // Perform the requested operation. switch (pWire->getOp()) { case FCS_OP_ITERATOR_INIT: { // Build the query. if (RC_BAD( opRc = fsvIteratorParse( pWire, pWire->getPool()))) { goto OP_EXIT; } uiIteratorIdRV = pWire->getIteratorId(); break; } case FCS_OP_ITERATOR_FREE: { // Free the iterator. if (RC_BAD( opRc = pSession->FreeIterator( pWire->getIteratorId()))) { goto OP_EXIT; } break; } case FCS_OP_ITERATOR_FIRST: { // Retrieve the first record (or DRN) in the result set. if (bDoDrnOp) { if (RC_BAD( opRc = FlmCursorFirstDRN( hIterator, &uiDrnRV))) { goto OP_EXIT; } } else { if (RC_BAD( opRc = FlmCursorFirst( hIterator, &pRecordRV))) { goto OP_EXIT; } } break; } case FCS_OP_ITERATOR_LAST: { // Retrieve the last record (or DRN) in the result set. if (bDoDrnOp) { if (RC_BAD( opRc = FlmCursorLastDRN( hIterator, &uiDrnRV))) { goto OP_EXIT; } } else { if (RC_BAD( opRc = FlmCursorLast( hIterator, &pRecordRV))) { goto OP_EXIT; } } break; } case FCS_OP_ITERATOR_NEXT: { // Retrieve the next record (or DRN) in the result set. if (bDoDrnOp) { if (RC_BAD( opRc = FlmCursorNextDRN( hIterator, &uiDrnRV))) { goto OP_EXIT; } } else { if (RC_BAD( opRc = FlmCursorNext( hIterator, &pRecordRV))) { goto OP_EXIT; } } break; } case FCS_OP_ITERATOR_PREV: { // Retrieve the previous record (or DRN) in the result set. if (bDoDrnOp) { if (RC_BAD( opRc = FlmCursorPrevDRN( hIterator, &uiDrnRV))) { goto OP_EXIT; } } else { if (RC_BAD( opRc = FlmCursorPrev( hIterator, &pRecordRV))) { goto OP_EXIT; } } break; } case FCS_OP_ITERATOR_COUNT: { // Count the number of records in the result set. if (RC_BAD( opRc = FlmCursorRecCount( hIterator, &uiCountRV))) { goto OP_EXIT; } break; } case FCS_OP_ITERATOR_TEST_REC: { if ((pTmpRecord = pWire->getRecord()) != NULL) { pTmpRecord->AddRef(); if (RC_BAD( opRc = FlmCursorTestRec( hIterator, pTmpRecord, &bFlag))) { goto OP_EXIT; } pTmpRecord->Release(); pTmpRecord = NULL; } else { if (RC_BAD( opRc = FlmCursorTestDRN( hIterator, pWire->getDrn(), &bFlag))) { goto OP_EXIT; } } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_ITERATOR, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { if (pRecordRV) { // Send the retrieved record. if (RC_BAD( rc = pWire->sendRecord( WIRE_VALUE_RECORD, pRecordRV))) { goto Exit; } } if (uiDrnRV) { // Send the record's DRN. if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_DRN, uiDrnRV))) { goto Exit; } } if (uiCountRV) { // Send the record count. if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_RECORD_COUNT, uiCountRV))) { goto Exit; } } if (uiIteratorIdRV != FCS_INVALID_ID) { // Send the iterator's ID. if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_ITERATOR_ID, uiIteratorIdRV))) { goto Exit; } } if (bFlag) { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_BOOLEAN, bFlag))) { goto Exit; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: if (pRecordRV) { pRecordRV->Release(); } if (pTmpRecord) { pTmpRecord->Release(); pTmpRecord = NULL; } return (rc); } /**************************************************************************** Desc: Performs a transaction operation ****************************************************************************/ RCODE fsvOpClassTransaction( FSV_WIRE * pWire) { RCODE rc = FERR_OK; RCODE opRc = FERR_OK; FSV_SESN * pSession; HFDB hDb; FLMUINT uiTransTypeRV; FLMBYTE * pBlock = NULL; FLMUINT uiBlockSize = 0; FLMUINT uiFlmTransFlags = 0; // Get a pointer to the session object. if ((pSession = pWire->getSession()) == NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } // Get a handle to the database in case this is a database transaction // operation hDb = (HFDB) pWire->getFDB(); // Perform the requested operation. switch (pWire->getOp()) { case FCS_OP_TRANSACTION_BEGIN: { // Start a database transaction. if (pWire->getFlags() & FCS_TRANS_FLAG_GET_HEADER) { uiBlockSize = 2048; if( RC_BAD( rc = pWire->getPool()->poolAlloc( uiBlockSize, (void **)&pBlock))) { goto OP_EXIT; } } if (pWire->getFlags() & FCS_TRANS_FLAG_DONT_KILL) { uiFlmTransFlags |= FLM_DONT_KILL_TRANS; } if (pWire->getFlags() & FCS_TRANS_FLAG_DONT_POISON) { uiFlmTransFlags |= FLM_DONT_POISON_CACHE; } if (RC_BAD( opRc = FlmDbTransBegin( hDb, pWire->getTransType() | uiFlmTransFlags, pWire->getMaxLockWait(), pBlock))) { goto OP_EXIT; } break; } case FCS_OP_TRANSACTION_COMMIT: { // Commit a database transaction. if (RC_BAD( opRc = FlmDbTransCommit( hDb))) { goto OP_EXIT; } break; } case FCS_OP_TRANSACTION_COMMIT_EX: { // Commit a database transaction. if (RC_BAD( opRc = fsvDbTransCommitEx( hDb, pWire))) { goto OP_EXIT; } break; } case FCS_OP_TRANSACTION_ABORT: { // Abort a database transaction. if (RC_BAD( opRc = FlmDbTransAbort( hDb))) { goto OP_EXIT; } break; } case FCS_OP_TRANSACTION_GET_TYPE: { // Get the database transaction type. if (RC_BAD( opRc = FlmDbGetTransType( hDb, &uiTransTypeRV))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_TRANS, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (pBlock) { if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_BLOCK, pBlock, uiBlockSize))) { goto Exit; } } if (RC_OK( opRc)) { switch (pWire->getOp()) { case FCS_OP_TRANSACTION_GET_TYPE: { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_TRANSACTION_TYPE, uiTransTypeRV))) { goto Exit; } break; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a maintenance operation. ****************************************************************************/ RCODE fsvOpClassMaintenance( FSV_WIRE * pWire) { FSV_SESN * pSession; HFDB hDb; F_Pool pool; RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Initialize a temporary pool. pool.poolInit( 1024); // Service the request. if ((pSession = pWire->getSession()) == NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } switch (pWire->getOp()) { case FCS_OP_CHECK: { if (RC_BAD( opRc = FlmDbCheck( hDb, NULL, NULL, NULL, pWire->getFlags(), &pool, NULL, NULL, 0))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_MAINTENANCE, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { switch (pWire->getOp()) { case FCS_OP_CHECK: { break; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs an index operation ****************************************************************************/ RCODE fsvOpClassIndex( FSV_WIRE * pWire) { HFDB hDb = HFDB_NULL; FLMUINT uiIndex; FINDEX_STATUS indexStatus; F_Pool * pTmpPool = pWire->getPool(); RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Get the database handle. This is needed by all of the index // operations. if ((hDb = (HFDB) pWire->getFDB()) == HFDB_NULL) { opRc = RC_SET( FERR_BAD_HDL); goto OP_EXIT; } // Initialize local variables. uiIndex = pWire->getIndexId(); // Service the request. switch (pWire->getOp()) { case FCS_OP_INDEX_GET_STATUS: { if (RC_BAD( opRc = FlmIndexStatus( hDb, uiIndex, &indexStatus))) { goto OP_EXIT; } break; } case FCS_OP_INDEX_GET_NEXT: { if (RC_BAD( opRc = FlmIndexGetNext( hDb, &uiIndex))) { goto OP_EXIT; } break; } case FCS_OP_INDEX_SUSPEND: { if (RC_BAD( opRc = FlmIndexSuspend( hDb, uiIndex))) { goto OP_EXIT; } break; } case FCS_OP_INDEX_RESUME: { if (RC_BAD( opRc = FlmIndexResume( hDb, uiIndex))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_INDEX, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { switch (pWire->getOp()) { case FCS_OP_INDEX_GET_STATUS: { NODE * pStatusTree; if (RC_BAD( fcsBuildIndexStatus( &indexStatus, pTmpPool, &pStatusTree))) { goto Exit; } if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_HTD, pStatusTree))) { goto Exit; } break; } case FCS_OP_INDEX_GET_NEXT: { if (RC_BAD( rc = pWire->sendNumber( WIRE_VALUE_INDEX_ID, uiIndex))) { goto Exit; } break; } } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Performs a misc. operation ****************************************************************************/ RCODE fsvOpClassMisc( FSV_WIRE * pWire) { FLMBYTE ucSerialNum[F_SERIAL_NUM_SIZE]; RCODE opRc = FERR_OK; RCODE rc = FERR_OK; // Service the request. switch (pWire->getOp()) { case FCS_OP_CREATE_SERIAL_NUM: { if (RC_BAD( opRc = f_createSerialNumber( ucSerialNum))) { goto OP_EXIT; } break; } default: { opRc = RC_SET( FERR_NOT_IMPLEMENTED); goto OP_EXIT; } } OP_EXIT: // Send the server's response. if (RC_BAD( rc = pWire->sendOpcode( FCS_OPCLASS_MISC, pWire->getOp()))) { goto Exit; } if (RC_BAD( rc = pWire->sendRc( opRc))) { goto Exit; } if (RC_OK( opRc)) { if (pWire->getOp() == FCS_OP_CREATE_SERIAL_NUM) { if (RC_BAD( rc = pWire->sendBinary( WIRE_VALUE_SERIAL_NUM, ucSerialNum, F_SERIAL_NUM_SIZE))) { goto Exit; } } else { flmAssert( rc == FERR_NOT_IMPLEMENTED); } } if (RC_BAD( rc = pWire->sendTerminate())) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Configures an iterator based on from, where, select, and config clauses provided by the client. ****************************************************************************/ FSTATIC RCODE fsvIteratorParse( FSV_WIRE * pWire, F_Pool * pPool) { RCODE rc = FERR_OK; // Parse the "from" clause. This contains record source information. if (pWire->getIteratorFrom()) { if (RC_BAD( rc = fsvIteratorFromParse( pWire, pPool))) { goto Exit; } } if (pWire->getIteratorHandle() == HFCURSOR_NULL) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Parse the "where" clause. This contains the criteria. if (pWire->getIteratorWhere()) { if (RC_BAD( rc = fsvIteratorWhereParse( pWire, pPool))) { goto Exit; } } // Parse the "select" clause. This contains customized view // information. if (pWire->getIteratorSelect()) { if (RC_BAD( rc = fsvIteratorSelectParse( pWire, pPool))) { goto Exit; } } Exit: return (rc); } /**************************************************************************** Desc: Adds selection criteria to an iterator. ****************************************************************************/ FSTATIC RCODE fsvIteratorWhereParse( FSV_WIRE * pWire, F_Pool * pPool) { HFCURSOR hIterator = pWire->getIteratorHandle(); NODE * pWhere = pWire->getIteratorWhere(); NODE * pCurNode; NODE * pTmpNode; void * pPoolMark; FLMUINT uiTag; RCODE rc = FERR_OK; // If no "where" clause, jump to exit. if (!pWhere) { goto Exit; } // Process each component of the "where" clause. pCurNode = GedChild( pWhere); while (pCurNode) { uiTag = GedTagNum( pCurNode); switch (uiTag) { case FCS_ITERATOR_MODE: { FLMUINT uiFlags = 0; // Set the iterator's mode flags if (RC_BAD( rc = GedGetUINT( pCurNode, &uiFlags))) { goto Exit; } if (RC_BAD( rc = FlmCursorSetMode( hIterator, uiFlags))) { goto Exit; } break; } // Add an attribute to the criteria. case FCS_ITERATOR_ATTRIBUTE: { FLMUINT uiAttrId; // Get the attribute ID. if (RC_BAD( rc = GedGetUINT( pCurNode, &uiAttrId))) { goto Exit; } // Add the attribute. if (uiTag == FCS_ITERATOR_ATTRIBUTE) { if (RC_BAD( rc = FlmCursorAddField( hIterator, uiAttrId, 0))) { goto Exit; } } else { // Sanity check. flmAssert( 0); } break; } // Add an attribute path to the criteria. case FCS_ITERATOR_ATTRIBUTE_PATH: { FLMUINT puiPath[FCS_ITERATOR_MAX_PATH + 1]; FLMUINT uiAttrId; FLMUINT uiPathPos = 0; FLMUINT uiStartLevel; if ((pTmpNode = GedFind( GED_TREE, pCurNode, FCS_ITERATOR_ATTRIBUTE, 1)) != NULL) { // Build the attribute path. uiStartLevel = GedNodeLevel( pTmpNode); while (pTmpNode && GedNodeLevel( pTmpNode) >= uiStartLevel) { if (GedNodeLevel( pTmpNode) == uiStartLevel && GedTagNum( pTmpNode) == FCS_ITERATOR_ATTRIBUTE) { if (RC_BAD( rc = GedGetUINT( pTmpNode, &uiAttrId))) { goto Exit; } puiPath[uiPathPos++] = uiAttrId; if (uiPathPos > FCS_ITERATOR_MAX_PATH) { rc = RC_SET( FERR_SYNTAX); goto Exit; } } pTmpNode = pTmpNode->next; } puiPath[uiPathPos] = 0; } // Add the attribute path. if (RC_BAD( rc = FlmCursorAddFieldPath( hIterator, puiPath, 0))) { goto Exit; } break; } // Add a numeric value to the criteria. case FCS_ITERATOR_NUMBER_VALUE: case FCS_ITERATOR_REC_PTR_VALUE: { // To save conversion time, cheat to determine if the number // is negative. FLMBYTE * pucValue = (FLMBYTE *) GedValPtr( pCurNode); FLMBOOL bNegative = ((*pucValue & 0xF0) == 0xB0) ? TRUE : FALSE; if (bNegative) { FLMINT64 i64Value; if (uiTag == FCS_ITERATOR_REC_PTR_VALUE) { rc = RC_SET( FERR_SYNTAX); goto Exit; } if (RC_BAD( rc = GedGetINT64( pCurNode, &i64Value))) { goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_INT64_VAL, &i64Value, 0))) { goto Exit; } } else { FLMUINT64 ui64Value; FLMUINT uiValue; if (RC_BAD( rc = GedGetUINT64( pCurNode, &ui64Value))) { goto Exit; } if (uiTag == FCS_ITERATOR_NUMBER_VALUE) { if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_UINT64_VAL, &ui64Value, 0))) { goto Exit; } } else if (uiTag == FCS_ITERATOR_REC_PTR_VALUE) { uiValue = (FLMUINT)ui64Value; if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_REC_PTR_VAL, &uiValue, 0))) { goto Exit; } } else { // Sanity check. flmAssert( 0); } } break; } // Add a binary value to the criteria. case FCS_ITERATOR_BINARY_VALUE: { FLMBYTE * pucValue = (FLMBYTE *) GedValPtr( pCurNode); FLMUINT uiValLen = GedValLen( pCurNode); if (GedValType( pCurNode) != FLM_BINARY_TYPE) { rc = RC_SET( FERR_SYNTAX); goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_BINARY_VAL, pucValue, uiValLen))) { goto Exit; } break; } // Add a UNICODE string value to the criteria. case FCS_ITERATOR_UNICODE_VALUE: { FLMUINT uiLen; FLMUNICODE * puzBuf; // Mark the pool. pPoolMark = pPool->poolMark(); // Determine the length of the string. if (RC_BAD( rc = GedGetUNICODE( pCurNode, NULL, &uiLen))) { goto Exit; } // Allocate a temporary buffer. uiLen += 2; if( RC_BAD( rc = pPool->poolAlloc( uiLen, (void **)&puzBuf))) { goto Exit; } // Extract the string and add it to the criteria. if (RC_BAD( rc = GedGetUNICODE( pCurNode, puzBuf, &uiLen))) { goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_UNICODE_VAL, puzBuf, 0))) { goto Exit; } pPool->poolReset( pPoolMark); break; } // Add a NATIVE, WP60, or Word String value to the criteria. case FCS_ITERATOR_NATIVE_VALUE: case FCS_ITERATOR_WP60_VALUE: case FCS_ITERATOR_WDSTR_VALUE: { FLMUINT uiLen; FLMBYTE * pucBuf; // Mark the pool. pPoolMark = pPool->poolMark(); // Determine the length of the string. if (uiTag == FCS_ITERATOR_NATIVE_VALUE) { if (RC_BAD( rc = GedGetNATIVE( pCurNode, NULL, &uiLen))) { goto Exit; } } else { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } // Allocate a temporary buffer. uiLen += 2; if( RC_BAD( rc = pPool->poolAlloc( uiLen, (void **)&pucBuf))) { goto Exit; } // Extract the string and add it to the criteria. if (uiTag == FCS_ITERATOR_NATIVE_VALUE) { if (RC_BAD( rc = GedGetNATIVE( pCurNode, (char *) pucBuf, &uiLen))) { goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_STRING_VAL, pucBuf, 0))) { goto Exit; } } pPool->poolReset( pPoolMark); break; } // Add a native (internal) text value case FCS_ITERATOR_FLM_TEXT_VALUE: { if (RC_BAD( rc = FlmCursorAddValue( hIterator, FLM_TEXT_VAL, GedValPtr( pCurNode), GedValLen( pCurNode)))) { goto Exit; } break; } // Add an operator to the criteria. case FCS_ITERATOR_OPERATOR: { FLMUINT uiOp; QTYPES eTranslatedOp; // Get the C/S operator ID. if (RC_BAD( rc = GedGetUINT( pCurNode, &uiOp))) { goto Exit; } if (!uiOp || ((uiOp - FCS_ITERATOR_OP_START) >= FCS_ITERATOR_OP_END)) { rc = RC_SET( FERR_SYNTAX); goto Exit; } // Translate the C/S ID to a FLAIM operator ID. if (RC_BAD( rc = fcsTranslateQCSToQFlmOp( uiOp, &eTranslatedOp))) { goto Exit; } // Add the operator to the criteria. if (RC_BAD( rc = FlmCursorAddOp( hIterator, eTranslatedOp))) { goto Exit; } break; } default: { rc = RC_SET( FERR_SYNTAX); goto Exit; } } pCurNode = GedSibNext( pCurNode); } Exit: return (rc); } /**************************************************************************** Desc: Adds source information to an iterator. ****************************************************************************/ FSTATIC RCODE fsvIteratorFromParse( FSV_WIRE * pWire, F_Pool * pPool) { HFDB hDb = HFDB_NULL; HFCURSOR hIterator = pWire->getIteratorHandle(); FLMUINT uiIteratorId = FCS_INVALID_ID; NODE * pFrom = pWire->getIteratorFrom(); NODE * pCurNode; NODE * pCSAttrNode; NODE * pTmpNode; RCODE rc = FERR_OK; F_UNREFERENCED_PARM( pPool); // If no "from" clause, jump to exit. if (!pFrom) { goto Exit; } // Process each component of the "from" clause. if (hIterator == HFCURSOR_NULL) { FSV_SESN * pSession; FLMUINT uiContainerId = FLM_DATA_CONTAINER; FLMUINT uiPath[4]; uiPath[0] = FCS_ITERATOR_FROM; uiPath[1] = FCS_ITERATOR_CANDIDATE_SET; uiPath[2] = FCS_ITERATOR_RECORD_SOURCE; uiPath[3] = 0; if ((pCSAttrNode = GedPathFind( GED_TREE, pFrom, uiPath, 1)) == NULL) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Get the database handle. if ((pSession = pWire->getSession()) == NULL) { rc = RC_SET( FERR_BAD_HDL); goto Exit; } hDb = pSession->GetDatabase(); // Get the container ID. A default value of FLM_DATA_CONTAINER will // be used if a container ID is not found. if ((pTmpNode = GedFind( GED_TREE, pCSAttrNode, FCS_ITERATOR_CONTAINER_ID, 1)) != NULL) { if (RC_BAD( rc = GedGetUINT( pTmpNode, &uiContainerId))) { goto Exit; } } // Initialize the cursor when we get the source - only one source is // allowed. if (RC_BAD( rc = pSession->InitializeIterator( &uiIteratorId, hDb, uiContainerId, &hIterator))) { goto Exit; } // Set the iterator handle and ID so they will be available for the // parser to use. pWire->setIteratorId( uiIteratorId); pWire->setIteratorHandle( hIterator); } pCurNode = GedChild( pFrom); while (pCurNode) { switch (GedTagNum( pCurNode)) { case FCS_ITERATOR_CANDIDATE_SET: { // Process record sources and indexes. pCSAttrNode = GedChild( pCurNode); while (pCSAttrNode) { switch (GedTagNum( pCSAttrNode)) { // Define a record source. case FCS_ITERATOR_RECORD_SOURCE: { // Handled above. break; } // Specify a FLAIM index. case FCS_ITERATOR_FLAIM_INDEX: { FLMUINT uiIndexId; // Get the index ID. if (RC_BAD( rc = GedGetUINT( pCSAttrNode, &uiIndexId))) { goto Exit; } // Add the index. if (RC_BAD( rc = FlmCursorConfig( hIterator, FCURSOR_SET_FLM_IX, (void *) uiIndexId, (void *) 0))) { goto Exit; } break; } // Set the record type. case FCS_ITERATOR_RECORD_TYPE: { FLMUINT uiRecordType; // Get the record type. if (RC_BAD( rc = GedGetUINT( pCSAttrNode, &uiRecordType))) { goto Exit; } // Add the record type. if (RC_BAD( rc = FlmCursorConfig( hIterator, FCURSOR_SET_REC_TYPE, (void *) uiRecordType, (void *) 0))) { goto Exit; } break; } case FCS_ITERATOR_OK_TO_RETURN_KEYS: { FLMUINT uiOkToReturnKeys; if (RC_BAD( rc = GedGetUINT( pCSAttrNode, &uiOkToReturnKeys))) { goto Exit; } if (RC_BAD( rc = FlmCursorConfig( hIterator, FCURSOR_RETURN_KEYS_OK, (void *) (FLMUINT)(uiOkToReturnKeys ? TRUE : FALSE), NULL))) { goto Exit; } break; } } pCSAttrNode = GedSibNext( pCSAttrNode); } break; } case FCS_ITERATOR_MODE: { FLMUINT uiFlags; // Get the mode flags. if (RC_BAD( rc = GedGetUINT( pCurNode, &uiFlags))) { goto Exit; } if (RC_BAD( rc = FlmCursorSetMode( hIterator, uiFlags))) { goto Exit; } break; } } pCurNode = GedSibNext( pCurNode); } Exit: return (rc); } /**************************************************************************** Desc: Adds a view to an iterator ****************************************************************************/ FSTATIC RCODE fsvIteratorSelectParse( FSV_WIRE * pWire, F_Pool * pPool) { NODE * pSelect = pWire->getIteratorSelect(); NODE * pCurNode; NODE * pView = NULL; FLMBOOL bNullViewNotRec = FALSE; RCODE rc = FERR_OK; F_UNREFERENCED_PARM( pPool); // If no "select" clause, jump to exit. if (!pSelect) { goto Exit; } pCurNode = GedChild( pSelect); while (pCurNode) { switch (GedTagNum( pCurNode)) { case FCS_ITERATOR_VIEW_TREE: { pView = GedChild( pCurNode); break; } case FCS_ITERATOR_NULL_VIEW_NOT_REC: { bNullViewNotRec = TRUE; break; } } pCurNode = GedSibNext( pCurNode); } // Set the view record, if any (not supported). if (GedChild( pCurNode)) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: Reads blocks from the database ****************************************************************************/ FSTATIC RCODE fsvDbGetBlocks( HFDB hDb, FLMUINT uiAddress, FLMUINT uiMinTransId, FLMUINT * puiCount, FLMUINT * puiBlocksExamined, FLMUINT * puiNextBlkAddr, FLMUINT uiFlags, F_Pool * pPool, FLMBYTE ** ppBlocks, FLMUINT * puiBytes) { FDB * pDb = (FDB *) hDb; FLMBOOL bDbInitialized = FALSE; FLMBOOL bTransStarted = FALSE; FLMUINT uiLoop; FLMUINT uiCount = *puiCount; SCACHE * pSCache = NULL; FLMUINT uiBlockSize; FLMUINT uiMaxFileSize; RCODE rc = FERR_OK; *ppBlocks = NULL; *puiCount = 0; *puiBlocksExamined = 0; *puiBytes = 0; if (IsInCSMode( hDb)) { fdbInitCS( pDb); bDbInitialized = TRUE; CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire(pCSContext, pDb); if (!pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_GET_BLOCK))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_ADDRESS, uiAddress))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_TRANSACTION_ID, uiMinTransId))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_COUNT, uiCount))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiFlags))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (RC_BAD( rc = Wire.getRCode())) { if (rc != FERR_IO_END_OF_FILE) { goto Exit; } } *puiBlocksExamined = (FLMUINT) Wire.getNumber2(); *puiCount = (FLMUINT) Wire.getCount(); *puiNextBlkAddr = Wire.getAddress(); if (*puiCount) { *puiBytes = Wire.getBlockSize(); if( RC_BAD( rc = pPool->poolAlloc( *puiBytes, (void **)ppBlocks))) { goto Exit; } f_memcpy( *ppBlocks, Wire.getBlock(), *puiBytes); } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (!uiCount) { uiCount = 1; } uiBlockSize = pDb->pFile->FileHdr.uiBlockSize; uiMaxFileSize = pDb->pFile->uiMaxFileSize; bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, &bTransStarted))) { goto Exit; } if( RC_BAD( rc = pPool->poolAlloc( uiBlockSize * uiCount, (void **)ppBlocks))) { goto Exit; } // Read uiCount blocks from the database starting at uiAddress. If none // of the blocks meet the min trans ID criteria, we will not return any // blocks to the reader. *puiNextBlkAddr = BT_END; for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { if (!FSAddrIsBelow( FSBlkAddress( FSGetFileNumber( uiAddress), FSGetFileOffset( uiAddress)), pDb->LogHdr.uiLogicalEOF)) { rc = RC_SET( FERR_IO_END_OF_FILE); goto Exit; } if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiAddress, NULL, &pSCache))) { goto Exit; } if (FB2UD( &pSCache->pucBlk[BH_TRANS_ID]) >= uiMinTransId) { f_memcpy( (*ppBlocks + ((*puiCount) * uiBlockSize)), pSCache->pucBlk, uiBlockSize); (*puiCount)++; (*puiBytes) += uiBlockSize; } (*puiBlocksExamined)++; ScaReleaseCache( pSCache, FALSE); pSCache = NULL; uiAddress += uiBlockSize; if (FSGetFileOffset( uiAddress) >= uiMaxFileSize) { uiAddress = FSBlkAddress( FSGetFileNumber( uiAddress) + 1, 0); } *puiNextBlkAddr = uiAddress; } Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } if (bTransStarted) { RCODE rc2 = flmAbortDbTrans( pDb); if (RC_OK( rc)) { rc = rc2; } } if (bDbInitialized) { fdbExit( pDb); } return (rc); } /**************************************************************************** Desc: Commits a database transaction and updates the log header ****************************************************************************/ RCODE fsvDbTransCommitEx( HFDB hDb, FSV_WIRE * pWire) { RCODE rc = FERR_OK; FDB * pDb = (FDB *) hDb; FLMBOOL bIgnore; FLMBOOL bForceCheckpoint = FALSE; FLMBYTE * pucHeader = NULL; if (pWire->getFlags() & FCS_TRANS_FORCE_CHECKPOINT) { bForceCheckpoint = TRUE; } pucHeader = pWire->getBlock(); if (IsInCSMode( hDb)) { fdbInitCS( pDb); FCL_WIRE Wire(pDb->pCSContext, pDb); if (!pDb->pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); } else { rc = Wire.doTransOp( FCS_OP_TRANSACTION_COMMIT_EX, 0, 0, 0, pucHeader, bForceCheckpoint); } goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } // If there is an invisible transaction going, it should not be // commitable by an application. if ((pDb->uiTransType == FLM_NO_TRANS) || (pDb->uiFlags & FDB_INVISIBLE_TRANS)) { rc = RC_SET( FERR_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( pDb->AbortRc)) { rc = RC_SET( FERR_ABORT_TRANS); goto Exit; } // Fix up the log header. Currently, only fields directly related to a // backup operation are updated. if (pucHeader) { FLMBYTE * pLogHdr = &pucHeader[16]; FLMBYTE * pucUncommittedHdr = &pDb->pFile->ucUncommittedLogHdr[0]; f_memcpy( &pucUncommittedHdr[LOG_LAST_BACKUP_TRANS_ID], &pLogHdr[LOG_LAST_BACKUP_TRANS_ID], 4); f_memcpy( &pucUncommittedHdr[LOG_BLK_CHG_SINCE_BACKUP], &pLogHdr[LOG_BLK_CHG_SINCE_BACKUP], 4); f_memcpy( &pucUncommittedHdr[LOG_INC_BACKUP_SEQ_NUM], &pLogHdr[LOG_INC_BACKUP_SEQ_NUM], 4); f_memcpy( &pucUncommittedHdr[LOG_INC_BACKUP_SERIAL_NUM], &pLogHdr[LOG_INC_BACKUP_SERIAL_NUM], F_SERIAL_NUM_SIZE); } // Commit the transaction rc = flmCommitDbTrans( pDb, 0, bForceCheckpoint); Exit: flmExit( FLM_DB_TRANS_COMMIT, pDb, rc); return (rc); } /**************************************************************************** Desc: Looks up session, database, and iterator handles. ****************************************************************************/ FSTATIC RCODE fsvGetHandles( FSV_WIRE * pWire) { FSV_SCTX * pServerContext = NULL; FSV_SESN * pSession = NULL; HFCURSOR hIterator = HFCURSOR_NULL; RCODE rc = FERR_OK; if (RC_BAD( rc = fsvGetGlobalContext( &pServerContext))) { goto Exit; } if (pWire->getSessionId() != FCS_INVALID_ID) { if (RC_BAD( pServerContext->GetSession( pWire->getSessionId(), &pSession))) { rc = RC_SET( FERR_BAD_HDL); goto Exit; } if (pSession->getCookie() != pWire->getSessionCookie()) { rc = RC_SET( FERR_BAD_HDL); goto Exit; } pWire->setSession( pSession); } if (pSession) { pWire->setFDB( (FDB *) pSession->GetDatabase()); if (pWire->getIteratorId() != FCS_INVALID_ID) { if (RC_BAD( rc = pSession->GetIterator( pWire->getIteratorId(), &hIterator))) { goto Exit; } pWire->setIteratorHandle( hIterator); } } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE fsvPostStreamedRequest( FSV_SESN * pSession, FLMBYTE * pucPacket, FLMUINT uiPacketSize, FLMBOOL bLastPacket, FCS_BIOS * pSessionResponse) { RCODE rc = FERR_OK; FLMBOOL bReleaseSession = FALSE; F_Pool localPool; localPool.poolInit( 1024); if (!pSession && !bLastPacket) { // If this is a session open request, the request must be contained // in a single packet. rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if (!pSession) { FCS_BIOS biosInput; FCS_DIS dataIStream; FCS_DOS dataOStream; if (RC_BAD( rc = dataIStream.setup( &biosInput))) { goto Exit; } if (RC_BAD( rc = dataOStream.setup( pSessionResponse))) { goto Exit; } if (RC_BAD( rc = biosInput.write( pucPacket, uiPacketSize))) { goto Exit; } if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, &localPool, NULL))) { goto Exit; } } else { FCS_BIOS * pServerBIStream; FCS_BIOS * pServerBOStream; // Need to add a reference to the session object so that if the // request closes the session, the response stream will not be // destructed until the response has been returned to the client. pSession->AddRef(); bReleaseSession = TRUE; if (RC_BAD( rc = pSession->GetBIStream( &pServerBIStream))) { goto Exit; } if (RC_BAD( rc = pSession->GetBOStream( &pServerBOStream))) { goto Exit; } if (RC_BAD( rc = pServerBIStream->write( pucPacket, uiPacketSize))) { goto Exit; } if (bLastPacket) { FCS_DIS dataIStream; FCS_DOS dataOStream; if (RC_BAD( rc = dataIStream.setup( pServerBIStream))) { goto Exit; } if (RC_BAD( rc = dataOStream.setup( pServerBOStream))) { goto Exit; } pSession->getWireScratchPool()->poolReset(); if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, pSession->getWireScratchPool(), NULL))) { goto Exit; } } } Exit: if (bReleaseSession) { pSession->Release(); } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE fsvGetStreamedResponse( FSV_SESN * pSession, FLMBYTE * pucPacketBuffer, FLMUINT uiMaxPacketSize, FLMUINT * puiPacketSize, FLMBOOL * pbLastPacket) { FCS_BIOS * pServerBOStream = NULL; RCODE rc = FERR_OK; if (RC_BAD( rc = pSession->GetBOStream( &pServerBOStream))) { goto Exit; } if (RC_BAD( rc = pServerBOStream->read( pucPacketBuffer, uiMaxPacketSize, puiPacketSize))) { if (rc == FERR_EOF_HIT) { *pbLastPacket = TRUE; rc = FERR_OK; } goto Exit; } if (!pServerBOStream->isDataAvailable()) { *pbLastPacket = TRUE; } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ FSV_SCTX::FSV_SCTX(void) { m_uiSessionToken = 0; m_uiCacheSize = FSV_DEFAULT_CACHE_SIZE; m_bSetupCalled = FALSE; m_paSessions = NULL; m_hMutex = F_MUTEX_NULL; m_szServerBasePath[0] = '\0'; m_pLogFunc = NULL; } /**************************************************************************** Desc: ****************************************************************************/ FSV_SCTX::~FSV_SCTX(void) { FLMUINT uiSlot; if (m_bSetupCalled) { // Clean up and free the session table. for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) { if (m_paSessions[uiSlot] != NULL) { m_paSessions[uiSlot]->Release(); } } f_free( &m_paSessions); // Free the session semaphore. (void) f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::Setup( FLMUINT uiMaxSessions, const char * pszServerBasePath, FSV_LOG_FUNC pLogFunc) { RCODE rc = FERR_OK; FLMUINT uiSlot; // Make sure that setup has not been called. flmAssert( m_bSetupCalled == FALSE); // If zero was passed as the value of uiMaxSessions, use the default. if (!uiMaxSessions) { m_uiMaxSessions = FSV_DEFAULT_MAX_CONNECTIONS; } else { m_uiMaxSessions = uiMaxSessions; } // Initialize the session table. if (RC_BAD( rc = f_alloc( sizeof(FSV_SESN *) * m_uiMaxSessions, &m_paSessions ))) { goto Exit; } for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) { m_paSessions[uiSlot] = NULL; } // Initialize the context mutex if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } // Set the server's home path. if (pszServerBasePath) { f_strcpy( m_szServerBasePath, pszServerBasePath); } else { m_szServerBasePath[0] = '\0'; } // Set the logging function. m_pLogFunc = pLogFunc; // Set the setup flag. m_bSetupCalled = TRUE; Exit: // Clean up any allocations if an error was encountered. if (RC_BAD( rc)) { if (m_paSessions != NULL) { f_free( &m_paSessions); } if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::OpenSession( FLMUINT uiVersion, FLMUINT uiFlags, FLMUINT * puiIdRV, FSV_SESN ** ppSessionRV) { FLMUINT uiSlot; FLMUINT uiCurrTime; FLMBOOL bLocked = FALSE; FSV_SESN * pSession = NULL; RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Initialize the Id *puiIdRV = 0; // Create a new session object if ((pSession = f_new FSV_SESN) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Allocate the session object if (RC_BAD( rc = pSession->Setup( this, uiVersion, uiFlags))) { goto Exit; } // Lock the context mutex f_mutexLock( m_hMutex); bLocked = TRUE; // Find an empty slot in the table. for (uiSlot = 0; uiSlot < m_uiMaxSessions; uiSlot++) { if (!m_paSessions[uiSlot]) { break; } } if (uiSlot >= m_uiMaxSessions) { rc = RC_SET( FERR_MEM); goto Exit; } // Assign the session to the table slot. m_paSessions[uiSlot] = pSession; // Increment the session token. m_uiSessionToken++; // If the session token is 0xFFFF, reset it to 1. Because // FSV_INVALID_ID is 0xFFFFFFFF, it is important to reset the session // token so that a session will never be assigned an invalid ID. if (m_uiSessionToken == 0xFFFF) { m_uiSessionToken = 1; } // Set the session's ID. *puiIdRV = uiSlot | (m_uiSessionToken << 16); pSession->setId( *puiIdRV); // Set the session's cookie using the current time. f_timeGetSeconds( &uiCurrTime); pSession->setCookie( uiCurrTime); // Unlock the context mutex f_mutexUnlock( m_hMutex); bLocked = FALSE; Exit: if (RC_BAD( rc)) { if (pSession) { pSession->Release(); pSession = NULL; } } else { if (ppSessionRV) { *ppSessionRV = pSession; } } if (bLocked) { f_mutexUnlock( m_hMutex); } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::CloseSession( FLMUINT uiId) { FLMUINT uiSlot = (0x0000FFFF & uiId); FLMBOOL bLocked = FALSE; FSV_SESN * pSession = NULL; RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Lock the context mutex f_mutexLock( m_hMutex); bLocked = TRUE; // Make sure that the slot is valid. if (uiSlot >= m_uiMaxSessions) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Get a pointer to the table entry. if ((pSession = m_paSessions[uiSlot]) == NULL) { // Session already closed goto Exit; } // Verify the session ID. if (pSession->getId() != uiId) { rc = RC_SET( FERR_MEM); goto Exit; } // Free the session. pSession->Release(); // Reset the table entry. m_paSessions[uiSlot] = NULL; Exit: if (bLocked) { f_mutexUnlock( m_hMutex); } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::GetSession( FLMUINT uiId, FSV_SESN ** ppSession) { FLMUINT uiSlot = (0x0000FFFF & uiId); FLMBOOL bLocked = FALSE; RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Lock the context mutex f_mutexLock( m_hMutex); bLocked = TRUE; // Make sure that the slot is valid. if (uiSlot >= m_uiMaxSessions) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Get a pointer to the entry in the session table. if ((*ppSession = m_paSessions[uiSlot]) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Verify the session ID. if ((*ppSession)->getId() != uiId) { rc = RC_SET( FERR_MEM); goto Exit; } Exit: if (bLocked) { f_mutexUnlock( m_hMutex); } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::SetBasePath( const char * pszServerBasePath) { // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Lock the context mutex f_mutexLock( m_hMutex); // Set the server's base path. if (pszServerBasePath) { f_strcpy( m_szServerBasePath, pszServerBasePath); } else { m_szServerBasePath[0] = '\0'; } // Unlock the context mutex f_mutexUnlock( m_hMutex); return (FERR_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::GetBasePath( char * pszServerBasePath) { // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Lock the context mutex f_mutexLock( m_hMutex); // Copy the base path. f_strcpy( pszServerBasePath, m_szServerBasePath); // Unlock the context mutex f_mutexUnlock( m_hMutex); return (FERR_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::BuildFilePath( const FLMUNICODE * puzUrlString, char * pszFilePathRV) { RCODE rc = FERR_OK; char szBasePath[F_PATH_MAX_SIZE]; FUrl Url; char * pucAsciiUrl; const char * pszFile; F_Pool tmpPool; // Initialize a temporary pool. tmpPool.poolInit( 256); // Attempt to convert the UNICODE URL to a native string if (RC_BAD( rc = fcsConvertUnicodeToNative( &tmpPool, puzUrlString, &pucAsciiUrl))) { goto Exit; } // Parse the URL. if (RC_BAD( rc = Url.SetUrl( pucAsciiUrl))) { goto Exit; } pszFile = Url.GetFile(); if (Url.GetRelative()) { // Get the server's base path. GetBasePath( szBasePath); // Build the database path. f_strcpy( pszFilePathRV, szBasePath); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszFilePathRV, pszFile))) { goto Exit; } } else { // Absolute path. Use the path "as-is." f_strcpy( pszFilePathRV, pszFile); } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SCTX::SetTempDir( const char * pszTempDir) { RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Set the temporary directory. There is no need to lock the context // semaphore because the state of the context is not being changed. if (RC_BAD( rc = FlmConfig( FLM_TMPDIR, (void *) pszTempDir, 0))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ void FSV_SCTX::LogMessage( FSV_SESN * pSession, const char * pucMsg, RCODE rc, FLMUINT uiMsgSeverity) { if (m_pLogFunc) { f_mutexLock( m_hMutex); m_pLogFunc( pucMsg, rc, uiMsgSeverity, (void *) pSession); f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: ****************************************************************************/ FSV_SESN::FSV_SESN(void) { m_pServerContext = NULL; m_hDb = HFDB_NULL; m_uiSessionId = FCS_INVALID_ID; m_uiCookie = 0; m_uiFlags = 0; m_pBIStream = NULL; m_pBOStream = NULL; m_bSetupCalled = FALSE; m_uiClientProtocolVersion = 0; m_wireScratchPool.poolInit( 2048); } /**************************************************************************** Desc: ****************************************************************************/ FSV_SESN::~FSV_SESN(void) { FLMUINT uiLoop; if (m_bSetupCalled) { // Free iterator resources. for (uiLoop = 0; uiLoop < MAX_SESN_ITERATORS; uiLoop++) { if (m_IteratorList[uiLoop] != HFCURSOR_NULL) { (void) FlmCursorFree( &m_IteratorList[uiLoop]); } } // Close the database if (m_hDb != HFDB_NULL) { (void) FlmDbClose( &m_hDb); } // Free the buffer streams if (m_pBIStream) { m_pBIStream->Release(); } if (m_pBOStream) { m_pBOStream->Release(); } } } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::Setup( FSV_SCTX * pServerContext, FLMUINT uiVersion, FLMUINT uiFlags) { FLMUINT uiLoop; RCODE rc = FERR_OK; // Make sure that setup has not been called. flmAssert( m_bSetupCalled == FALSE); // Verify that the requested version is supported. if (uiVersion > FCS_VERSION_1_1_1) { rc = RC_SET( FERR_UNSUPPORTED_VERSION); goto Exit; } m_uiClientProtocolVersion = uiVersion; // Set the server context. m_pServerContext = pServerContext; // Initialize the iterator list for (uiLoop = 0; uiLoop < MAX_SESN_ITERATORS; uiLoop++) { m_IteratorList[uiLoop] = HFCURSOR_NULL; } m_bSetupCalled = TRUE; m_uiFlags = uiFlags; Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::OpenDatabase( FLMUNICODE * puzDbPath, FLMUNICODE * puzDataDir, FLMUNICODE * puzRflDir, FLMUINT uiOpenFlags) { RCODE rc = FERR_OK; char * pszDbPath = NULL; char * pszDataDir; char * pszRflDir; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); flmAssert( m_hDb == HFDB_NULL); if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 3, &pszDbPath))) { goto Exit; } pszDataDir = pszDbPath + F_PATH_MAX_SIZE; pszRflDir = pszDataDir + F_PATH_MAX_SIZE; // Perform some sanity checking. if (!puzDbPath) { rc = RC_SET( FERR_MEM); goto Exit; } // Convert the UNICODE URL to a server path. if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDbPath, pszDbPath))) { goto Exit; } // Convert the data directory if (puzDataDir) { if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDataDir, pszDataDir))) { goto Exit; } } else { pszDataDir = NULL; } // Convert the RFL path if (puzRflDir) { if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzRflDir, pszRflDir))) { goto Exit; } } else { *pszRflDir = 0; } // Open the database. if (RC_BAD( rc = FlmDbOpen( pszDbPath, pszDataDir, pszRflDir, uiOpenFlags, NULL, &m_hDb))) { goto Exit; } Exit: if (pszDbPath) { f_free( &pszDbPath); } // Free resources if (RC_BAD( rc)) { if (m_hDb != HFDB_NULL) { (void) FlmDbClose( &m_hDb); } } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::CreateDatabase( FLMUNICODE * puzDbPath, FLMUNICODE * puzDataDir, FLMUNICODE * puzRflDir, FLMUNICODE * puzDictPath, FLMUNICODE * puzDictBuf, CREATE_OPTS * pCreateOpts) { RCODE rc = FERR_OK; F_Pool tmpPool; char * pucDictBuf = NULL; char * pszDbPath = NULL; char * pszDataDir; char * pszRflDir; char * pszDictPath; // Initialize a temporary pool. tmpPool.poolInit( 1024); // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); flmAssert( m_hDb == HFDB_NULL); if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 4, &pszDbPath))) { goto Exit; } pszDataDir = pszDbPath + F_PATH_MAX_SIZE; pszRflDir = pszDataDir + F_PATH_MAX_SIZE; pszDictPath = pszRflDir + F_PATH_MAX_SIZE; // Perform some sanity checking. if (!puzDbPath) { rc = RC_SET( FERR_MEM); goto Exit; } // Convert the DB URL to a server path. if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDbPath, pszDbPath))) { goto Exit; } // Convert the dictionary URL to a server path. if (puzDictPath) { if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDictPath, pszDictPath ))) { goto Exit; } } else { pszDictPath = NULL; } // Convert the data directory if (puzDataDir) { if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzDataDir, pszDataDir))) { goto Exit; } } else { pszDataDir = NULL; } // Convert the RFL path if (puzRflDir) { if (RC_BAD( rc = m_pServerContext->BuildFilePath( puzRflDir, pszRflDir))) { goto Exit; } } else { *pszRflDir = 0; } // Attempt to convert the UNICODE dictionary buffer to a native string if (puzDictBuf) { if (RC_BAD( rc = fcsConvertUnicodeToNative( &tmpPool, puzDictBuf, &pucDictBuf))) { goto Exit; } } // Create the database. if (RC_BAD( rc = FlmDbCreate( pszDbPath, pszDataDir, pszRflDir, pszDictPath, pucDictBuf, pCreateOpts, &m_hDb))) { goto Exit; } Exit: if (pszDbPath) { f_free( &pszDbPath); } // Free resources if (RC_BAD( rc)) { if (m_hDb != HFDB_NULL) { (void) FlmDbClose( &m_hDb); } } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::CloseDatabase(void) { RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Close the database. if (m_hDb != HFDB_NULL) { if (RC_BAD( rc = FlmDbClose( &m_hDb))) { goto Exit; } } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::InitializeIterator( FLMUINT * puiIteratorIdRV, HFDB hDb, FLMUINT uiContainer, HFCURSOR * phIteratorRV) { HFCURSOR hIterator = HFCURSOR_NULL; FLMUINT uiSlot; RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Set the iterator id. *puiIteratorIdRV = FCS_INVALID_ID; // Find a slot in the session's iterator table for (uiSlot = 0; uiSlot < MAX_SESN_ITERATORS; uiSlot++) { if (m_IteratorList[uiSlot] == HFCURSOR_NULL) { break; } } // Too many open iterators if (uiSlot == MAX_SESN_ITERATORS) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Initialize a new iterator (cursor). if (RC_BAD( rc = FlmCursorInit( hDb, uiContainer, &hIterator))) { goto Exit; } // Add the iterator to the iterator list. m_IteratorList[uiSlot] = hIterator; *puiIteratorIdRV = uiSlot; Exit: // Free resources if (RC_BAD( rc)) { if (hIterator != HFCURSOR_NULL) { (void) FlmCursorFree( &hIterator); } } else { if (phIteratorRV) { *phIteratorRV = hIterator; } } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::FreeIterator( FLMUINT uiIteratorId) { HFCURSOR hIterator = HFCURSOR_NULL; RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); // Find the iterator in the resource bag and remove it. if (uiIteratorId >= MAX_SESN_ITERATORS || m_IteratorList[uiIteratorId] == HFCURSOR_NULL) { rc = RC_SET( FERR_FAILURE); goto Exit; } hIterator = m_IteratorList[uiIteratorId]; m_IteratorList[uiIteratorId] = HFCURSOR_NULL; // Free the iterator. if (RC_BAD( rc = FlmCursorFree( &hIterator))) { goto Exit; } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::GetIterator( FLMUINT uiIteratorId, HFCURSOR * phIteratorRV) { RCODE rc = FERR_OK; // Make sure that setup has been called. flmAssert( m_bSetupCalled == TRUE); if (uiIteratorId >= MAX_SESN_ITERATORS || m_IteratorList[uiIteratorId] == HFCURSOR_NULL) { rc = RC_SET( FERR_FAILURE); goto Exit; } *phIteratorRV = m_IteratorList[uiIteratorId]; Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::GetBIStream( FCS_BIOS ** ppBIStream) { RCODE rc = FERR_OK; *ppBIStream = NULL; if (!m_pBIStream) { m_pBIStream = f_new FCS_BIOS; if (!m_pBIStream) { rc = RC_SET( FERR_MEM); goto Exit; } } *ppBIStream = m_pBIStream; Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_SESN::GetBOStream( FCS_BIOS ** ppBOStream) { RCODE rc = FERR_OK; *ppBOStream = NULL; if (!m_pBOStream) { m_pBOStream = f_new FCS_BIOS; if (!m_pBOStream) { rc = RC_SET( FERR_MEM); goto Exit; } } *ppBOStream = m_pBOStream; Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE flmStreamEventDispatcher( FCS_BIOS * pStream, FLMUINT uiEvent, void * UserData) { CS_CONTEXT * pCSContext = (CS_CONTEXT *) UserData; FLMUINT uiStreamHandlerId = FSEV_HANDLER_UNKNOWN; RCODE rc = FERR_OK; // Determine the handler if (pCSContext->uiStreamHandlerId == FSEV_HANDLER_UNKNOWN) { if (f_stricmp( pCSContext->pucAddr, "DS") == 0) { uiStreamHandlerId = FSEV_HANDLER_DS; } else if (f_stricmp( pCSContext->pucAddr, "LOOPBACK") == 0) { uiStreamHandlerId = FSEV_HANDLER_LOOPBACK; } pCSContext->uiStreamHandlerId = uiStreamHandlerId; } else { uiStreamHandlerId = pCSContext->uiStreamHandlerId; } // Invoke the handler switch (uiStreamHandlerId) { case FSEV_HANDLER_LOOPBACK: { if (RC_BAD( rc = fsvStreamLoopback( pStream, uiEvent, UserData))) { goto Exit; } break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } // Release CPU to prevent CPU hog f_yieldCPU(); Exit: if (RC_BAD( rc)) { // Clear the saved handler ID in case a new handler is tried pCSContext->uiStreamHandlerId = FSEV_HANDLER_UNKNOWN; } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE fsvStreamLoopback( FCS_BIOS * pStream, FLMUINT uiEvent, void * UserData) { CS_CONTEXT * pCSContext = (CS_CONTEXT *) UserData; FCS_DIS dataIStream; FCS_DOS dataOStream; RCODE rc = FERR_OK; F_UNREFERENCED_PARM( pStream); if (uiEvent == FCS_BIOS_EOM_EVENT) { if (RC_BAD( rc = dataIStream.setup( (FCS_BIOS *) (pCSContext->pOStream)))) { goto Exit; } if (RC_BAD( rc = dataOStream.setup( (FCS_BIOS *) (pCSContext->pIStream)))) { goto Exit; } if (RC_BAD( rc = fsvProcessRequest( &dataIStream, &dataOStream, &(pCSContext->pool), NULL))) { goto Exit; } } Exit: return (rc); } /**************************************************************************** Desc: ****************************************************************************/ void FSV_WIRE::reset(void) { resetCommon(); m_uiOpSeqNum = 0; m_uiClientVersion = 0; m_uiAutoTrans = 0; m_uiMaxLockWait = 0; m_puzDictPath = NULL; m_puzDictBuf = NULL; m_puzFileName = NULL; m_pucPassword = NULL; m_pDrnList = NULL; m_uiAreaId = 0; m_pIteratorSelect = NULL; m_pIteratorFrom = NULL; m_pIteratorWhere = NULL; m_pIteratorConfig = NULL; m_pSession = NULL; m_hIterator = HFCURSOR_NULL; m_uiType = 0; m_bSendGedcom = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ void FSV_WIRE::setSession( FSV_SESN * pSession) { m_pSession = pSession; // See if GEDCOM is supported by the client if (m_pSession && (m_pSession->getFlags() & FCS_SESSION_GEDCOM_SUPPORT)) { m_bSendGedcom = TRUE; } } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSV_WIRE::read(void) { FLMUINT uiTag; FLMUINT uiCount = 0; FLMBOOL bDone = FALSE; RCODE rc = FERR_OK; // Read the opcode. if (RC_BAD( rc = readOpcode())) { goto Exit; } // Read the request / response values. for (;;) { if (RC_BAD( rc = readCommon( &uiTag, &bDone))) { goto Exit; } if (bDone) { goto Exit; } // uiTag will be non-zero if readCommon did not understand it. uiCount++; if (uiTag) { switch ((uiTag & WIRE_VALUE_TAG_MASK)) { case WIRE_VALUE_OP_SEQ_NUM: { if (RC_BAD( rc = readNumber( uiTag, &m_uiOpSeqNum, NULL))) { goto Exit; } break; } case WIRE_VALUE_CLIENT_VERSION: { if (RC_BAD( rc = readNumber( uiTag, &m_uiClientVersion, NULL))) { goto Exit; } break; } case WIRE_VALUE_DICT_FILE_PATH: { if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzDictPath))) { goto Exit; } break; } case WIRE_VALUE_DICT_BUFFER: { if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzDictBuf))) { goto Exit; } break; } case WIRE_VALUE_PASSWORD: { if (RC_BAD( rc = m_pDIStream->readBinary( m_pPool, &m_pucPassword, NULL))) { goto Exit; } break; } case WIRE_VALUE_TYPE: { if (RC_BAD( rc = readNumber( uiTag, &m_uiType, NULL))) { goto Exit; } break; } case WIRE_VALUE_AREA_ID: { if (RC_BAD( rc = readNumber( uiTag, &m_uiAreaId, NULL))) { goto Exit; } break; } case WIRE_VALUE_FILE_NAME: { if (RC_BAD( rc = m_pDIStream->readUTF( m_pPool, &m_puzFileName))) { goto Exit; } break; } case WIRE_VALUE_AUTOTRANS: { if (RC_BAD( rc = readNumber( uiTag, &m_uiAutoTrans, NULL))) { goto Exit; } break; } case WIRE_VALUE_ITERATOR_SELECT: { if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pIteratorSelect, NULL))) { goto Exit; } break; } case WIRE_VALUE_ITERATOR_FROM: { if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pIteratorFrom, NULL))) { goto Exit; } break; } case WIRE_VALUE_ITERATOR_WHERE: { if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pIteratorWhere, NULL))) { goto Exit; } break; } case WIRE_VALUE_ITERATOR_CONFIG: { if (RC_BAD( rc = m_pDIStream->readHTD( m_pPool, 0, 0, &m_pIteratorConfig, NULL))) { goto Exit; } break; } case WIRE_VALUE_MAX_LOCK_WAIT: { if (RC_BAD( rc = readNumber( uiTag, &m_uiMaxLockWait, NULL))) { goto Exit; } break; } default: { if (RC_BAD( rc = skipValue( uiTag))) { goto Exit; } break; } } } } Exit: return (rc); } libflaim-4.9.966/src/imonstat.cpp0000644000175000017500000016765710510774540020244 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying various statistics in HTML on a web page. // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonstat.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define STAT_DISPLAY_ORDER "StatDisplayOrder" #define CACHE_STAT_STR "Cache" #define OPERATIONS_STAT_STR "Operations" #define LOCKS_STAT_STR "Locks" #define DISK_STAT_STR "Disk" #define CHECK_POINT_STR "CPThread" #define DEFAULT_STAT_ORDER CACHE_STAT_STR ";" \ OPERATIONS_STAT_STR ";" \ LOCKS_STAT_STR ";" \ DISK_STAT_STR ";" \ CHECK_POINT_STR ";" #define SAVED_STATS "SavedStats" #define STAT_FOCUS "StatFocus" // Stat types #define CACHE_STATS 1 #define OPERATION_STATS 2 #define LOCK_STATS 3 #define DISK_STATS 4 #define CHECK_POINT_STATS 5 #define MAX_STAT_TYPES 5 /**************************************************************************** Desc: Gather statistics from a BLOCKIO_STATS structure. ****************************************************************************/ void F_StatsPage::gatherBlockIOStats( STAT_GATHER * pStatGather, DISKIO_STAT * pReadStat, DISKIO_STAT * pWriteStat, BLOCKIO_STATS * pBlockIOStats ) { // Gather the read statistics. flmUpdateDiskIOStats( &pStatGather->IOReads, &pBlockIOStats->BlockReads); flmUpdateDiskIOStats( &pStatGather->IOReads, &pBlockIOStats->OldViewBlockReads); flmUpdateDiskIOStats( &pStatGather->IORollbackBlockReads, &pBlockIOStats->OldViewBlockReads); flmUpdateDiskIOStats( pReadStat, &pBlockIOStats->BlockReads); // Gather the check errors pStatGather->uiCheckErrors += (pBlockIOStats->uiBlockChkErrs + pBlockIOStats->uiOldViewBlockChkErrs); // Gather the write statistics flmUpdateDiskIOStats( &pStatGather->IOWrites, &pBlockIOStats->BlockWrites); flmUpdateDiskIOStats( pWriteStat, &pBlockIOStats->BlockWrites); } /**************************************************************************** Desc: Gather statistics for an LFILE. ****************************************************************************/ void F_StatsPage::gatherLFileStats( STAT_GATHER * pStatGather, LFILE_STATS * pLFileStats ) { pStatGather->uiNumLFileStats++; pStatGather->ui64BlockSplits += pLFileStats->ui64BlockSplits; pStatGather->ui64BlockCombines += pLFileStats->ui64BlockCombines; // Gather root block statistics gatherBlockIOStats( pStatGather, &pStatGather->IORootBlockReads, &pStatGather->IORootBlockWrites, &pLFileStats->RootBlockStats); // Gather non-leaf block statistics gatherBlockIOStats( pStatGather, &pStatGather->IONonLeafBlockReads, &pStatGather->IONonLeafBlockWrites, &pLFileStats->MiddleBlockStats); // Gather leaf block statistics gatherBlockIOStats( pStatGather, &pStatGather->IOLeafBlockReads, &pStatGather->IOLeafBlockWrites, &pLFileStats->LeafBlockStats); } /**************************************************************************** Desc: Gather statistics for a particular database. ****************************************************************************/ void F_StatsPage::gatherDbStats( STAT_GATHER * pStatGather, DB_STATS * pDbStats ) { FLMUINT uiLoop; pStatGather->uiNumDbStats++; flmUpdateCountTimeStats( &pStatGather->CommittedUpdTrans, &pDbStats->UpdateTransStats.CommittedTrans); flmUpdateCountTimeStats( &pStatGather->GroupCompletes, &pDbStats->UpdateTransStats.GroupCompletes); pStatGather->ui64GroupFinished += pDbStats->UpdateTransStats.ui64GroupFinished; flmUpdateCountTimeStats( &pStatGather->AbortedUpdTrans, &pDbStats->UpdateTransStats.AbortedTrans); flmUpdateCountTimeStats( &pStatGather->CommittedReadTrans, &pDbStats->ReadTransStats.CommittedTrans); flmUpdateCountTimeStats( &pStatGather->AbortedReadTrans, &pDbStats->ReadTransStats.AbortedTrans); pStatGather->Reads.ui64Count += pDbStats->ui64NumRecordReads; flmUpdateCountTimeStats( &pStatGather->Adds, &pDbStats->RecordAdds); flmUpdateCountTimeStats( &pStatGather->Modifies, &pDbStats->RecordModifies); flmUpdateCountTimeStats( &pStatGather->Deletes, &pDbStats->RecordDeletes); pStatGather->Queries.ui64Count += pDbStats->ui64NumCursors; pStatGather->QueryReads.ui64Count += pDbStats->ui64NumCursorReads; if ((m_pFocusBlock == NULL) || (m_pFocusBlock->uiLFileNum == 0)) { // Gather the avail block statistics gatherBlockIOStats( pStatGather, &pStatGather->IOAvailBlockReads, &pStatGather->IOAvailBlockWrites, &pDbStats->AvailBlockStats); // Gather the LFH block statistics gatherBlockIOStats( pStatGather, &pStatGather->IOLFHBlockReads, &pStatGather->IOLFHBlockWrites, &pDbStats->LFHBlockStats); // Gather log block reads flmUpdateDiskIOStats( &pStatGather->IOReads, &pDbStats->LogBlockReads); flmUpdateDiskIOStats( &pStatGather->IORollbackBlockReads, &pDbStats->LogBlockReads); // Gather log header writes flmUpdateDiskIOStats( &pStatGather->IOWrites, &pDbStats->LogHdrWrites); flmUpdateDiskIOStats( &pStatGather->IOLogHdrWrites, &pDbStats->LogHdrWrites); // Gather roll-back log writes flmUpdateDiskIOStats( &pStatGather->IOWrites, &pDbStats->LogBlockWrites); flmUpdateDiskIOStats( &pStatGather->IORollBackLogWrites, &pDbStats->LogBlockWrites); // Gather rolled-back block writes flmUpdateDiskIOStats( &pStatGather->IOWrites, &pDbStats->LogBlockRestores); flmUpdateDiskIOStats( &pStatGather->IORolledbackBlockWrites, &pDbStats->LogBlockRestores); // Gather I/O error statistics pStatGather->uiReadErrors += pDbStats->uiReadErrors; pStatGather->uiWriteErrors += pDbStats->uiWriteErrors; pStatGather->uiCheckErrors += (pDbStats->AvailBlockStats.uiBlockChkErrs + pDbStats->AvailBlockStats.uiOldViewBlockChkErrs + pDbStats->LFHBlockStats.uiBlockChkErrs + pDbStats->LFHBlockStats.uiOldViewBlockChkErrs + pDbStats->uiLogBlockChkErrs); } // Gather lock statistics flmUpdateCountTimeStats( &pStatGather->LockStats.NoLocks, &pDbStats->LockStats.NoLocks); flmUpdateCountTimeStats( &pStatGather->LockStats.WaitingForLock, &pDbStats->LockStats.WaitingForLock); flmUpdateCountTimeStats( &pStatGather->LockStats.HeldLock, &pDbStats->LockStats.HeldLock); for (uiLoop = 0; uiLoop < pDbStats->uiNumLFileStats; uiLoop++) { if ((m_pFocusBlock == NULL) || (m_pFocusBlock->uiLFileNum == 0) || (m_pFocusBlock->uiLFileNum == pDbStats->pLFileStats [uiLoop].uiLFileNum)) { gatherLFileStats( pStatGather, &pDbStats->pLFileStats [uiLoop]); } } } /**************************************************************************** Desc: Gather statistics for all databases in the FLM_STATS structure. ****************************************************************************/ void F_StatsPage::gatherStats( STAT_GATHER * pStatGather ) { FLMUINT uiLoop; f_memset( pStatGather, 0, sizeof( STAT_GATHER)); f_mutexLock( gv_FlmSysData.Stats.hMutex); pStatGather->bCollectingStats = gv_FlmSysData.Stats.bCollectingStats; if (gv_FlmSysData.Stats.uiStartTime) { pStatGather->uiStartTime = gv_FlmSysData.Stats.uiStartTime; pStatGather->uiStopTime = gv_FlmSysData.Stats.uiStopTime; for (uiLoop = 0; uiLoop < gv_FlmSysData.Stats.uiNumDbStats; uiLoop++) { if ((m_pFocusBlock == NULL) || (f_strcmp(m_pFocusBlock->szFileName, gv_FlmSysData.Stats.pDbStats [uiLoop].pszDbName) == 0)) { gatherDbStats( pStatGather, &gv_FlmSysData.Stats.pDbStats [uiLoop]); } } } f_mutexUnlock( gv_FlmSysData.Stats.hMutex); // Get the cache statistics. f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); f_memcpy( &pStatGather->RecordCache, &gv_FlmSysData.RCacheMgr.Usage, sizeof( pStatGather->RecordCache)); f_memcpy( &pStatGather->BlockCache, &gv_FlmSysData.SCacheMgr.Usage, sizeof( pStatGather->BlockCache)); pStatGather->uiFreeCount = gv_FlmSysData.SCacheMgr.uiFreeCount; pStatGather->uiFreeBytes = gv_FlmSysData.SCacheMgr.uiFreeBytes; pStatGather->uiReplaceableCount = gv_FlmSysData.SCacheMgr.uiReplaceableCount; pStatGather->uiReplaceableBytes = gv_FlmSysData.SCacheMgr.uiReplaceableBytes; f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); for (uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) { FFILE * pFile = (FFILE *)gv_FlmSysData.pFileHashTbl [uiLoop].pFirstInBucket; while (pFile) { if (pFile->uiDirtyCacheCount) { pStatGather->uiDirtyBytes += pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; pStatGather->uiDirtyBlocks += pFile->uiDirtyCacheCount; } if (pFile->uiLogCacheCount) { pStatGather->uiLogBytes += pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize; pStatGather->uiLogBlocks += pFile->uiLogCacheCount; } gatherCPStats( pStatGather, pFile); gatherLockStats( pStatGather, pFile); pFile = pFile->pNext; } } f_mutexUnlock( gv_FlmSysData.hShareMutex); } /**************************************************************************** Desc: Prints the web page for system configuration parameters. ****************************************************************************/ RCODE F_StatsPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; void * pvSession = NULL; STAT_GATHER * pStatGather = NULL; STAT_GATHER * pOldStatGather = NULL; char szStatOrder [50]; char szAction [5]; char szStatNewOrder [50]; char * pszStat; char * ppszStatOrders [MAX_STAT_TYPES]; FLMUINT uiLoop; FLMUINT uiAction; FLMBOOL bRefresh; FLMBOOL bFocus; char * pszTemp = NULL; char * pszHeading = NULL; char szCfgAction [50]; eFlmConfigTypes eConfigType; FLMUINT uiStatOrders [MAX_STAT_TYPES]; // Are we attempting to change the focus? bFocus = DetectParameter( uiNumParams, ppszParams, "Focus"); if (bFocus) { // Prepare the page to request the user input on what to focus on. displayFocus( uiNumParams, ppszParams); goto Exit; } if( RC_BAD( rc = f_alloc( 100, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } if( RC_BAD( rc = f_alloc( 250, &pszHeading))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } if (RC_OK( ExtractParameter( uiNumParams, ppszParams, "CfgAction", sizeof( szCfgAction), szCfgAction))) { FLMUINT uiValue1 = 0; FLMUINT uiValue2 = 0; eConfigType = (eFlmConfigTypes)f_atoi( szCfgAction); // Call FlmConfig to perform this action. if (RC_BAD( rc = FlmConfig( eConfigType, (void *)uiValue1, (void *)uiValue2))) { printErrorPage( FERR_FAILURE, TRUE, (char *)"Failed to perform configuration update"); goto Exit; } } // Get the session, then try to retrieve the StatOrder. If it isn't there, then // we can set it to the default. szStatOrder[0] = 0; if (gv_FlmSysData.HttpConfigParms.fnAcquireSession) { char szFocus[100]; if ((pvSession = fnAcquireSession()) != NULL) { FLMUINT uiSize = sizeof( szStatOrder); if (fnGetSessionValue( pvSession, STAT_DISPLAY_ORDER, (void *)szStatOrder, (FLMSIZET *)&uiSize) != 0) { // If we could not get display order, then set the default. f_strcpy( szStatOrder, DEFAULT_STAT_ORDER); } // Find out if we are focusing our stats collection. uiSize = sizeof( szFocus) - 1; if (fnGetSessionValue( pvSession, STAT_FOCUS, (void *)szFocus, (FLMSIZET *)&uiSize) == 0) { szFocus[ uiSize] = '\0'; if (RC_BAD( setFocus( szFocus))) { printErrorPage( FERR_MEM, TRUE, "Failed to establish focus criteria"); goto Exit; } } } } // See if they changed the display order. uiAction = (FLMUINT)(-1); if (RC_OK( ExtractParameter( uiNumParams, ppszParams, "Action", sizeof( szAction), szAction))) { uiAction = (FLMUINT)f_atoi( szAction); } uiLoop = 0; pszStat = &szStatOrder [0]; while (*pszStat && uiLoop < MAX_STAT_TYPES) { ppszStatOrders [uiLoop] = pszStat; uiLoop++; while (*pszStat && *pszStat != ';') { pszStat++; } if (*pszStat) { *pszStat = 0; pszStat++; } } // See if they changed the order. if (uiAction != (FLMUINT)(-1) && uiAction < MAX_STAT_TYPES * 2) { // Odd numbers mean shift to top (move slot 0 to 0, 1 to 0, 2 to 0, 3 to 0) // Even number mean shift up (move slot 0 to 3, 1 to 0, 2 to 1, 3 to 2) if (uiAction & 1) { uiAction /= 2; // Trade places with the guy that is in the top position // No need to move the top one. if (uiAction) { pszStat = ppszStatOrders [0]; ppszStatOrders [0] = ppszStatOrders [uiAction]; ppszStatOrders [uiAction] = pszStat; } } else { uiAction /= 2; pszStat = ppszStatOrders [uiAction]; // Trade places with the guy that is lower. if (!uiAction) { ppszStatOrders [uiAction] = ppszStatOrders [MAX_STAT_TYPES - 1]; ppszStatOrders [MAX_STAT_TYPES - 1] = pszStat; } else { ppszStatOrders [uiAction] = ppszStatOrders [uiAction - 1]; ppszStatOrders [uiAction - 1] = pszStat; } } // Output the new order. pszStat = &szStatNewOrder [0]; for (uiLoop = 0; uiLoop < MAX_STAT_TYPES; uiLoop++) { f_strcpy( pszStat, ppszStatOrders [uiLoop]); while (*pszStat) { pszStat++; } *pszStat++ = ';'; } *pszStat = 0; (void)fnSetSessionValue( pvSession, STAT_DISPLAY_ORDER, szStatNewOrder, (FLMSIZET)(f_strlen( szStatNewOrder) + 1)); } if (RC_BAD( rc = f_calloc( sizeof( STAT_GATHER), &pStatGather))) { printErrorPage( rc, TRUE, "ERROR ALLOCATING MEMORY: "); goto Exit; } if (RC_BAD( rc = f_calloc( sizeof( STAT_GATHER), &pOldStatGather))) { printErrorPage( rc, TRUE, "ERROR ALLOCATING MEMORY: "); goto Exit; } // Collect the current statistics... gatherStats( pStatGather); // See if we have any stats from a previous run stored in the session. if (pvSession) { FLMUINT uiSize = sizeof( STAT_GATHER); if (fnGetSessionValue( pvSession, SAVED_STATS, (void *)pOldStatGather, (FLMSIZET *)&uiSize) != 0) { // If we could not get any former stats, then just copy the current one. f_memcpy( pOldStatGather, pStatGather, sizeof( STAT_GATHER)); } // Now save the new stats for the next go round... if (fnSetSessionValue( pvSession, SAVED_STATS, (void *)pStatGather, sizeof( STAT_GATHER)) != 0) { printErrorPage( rc, TRUE, "ERROR Saving Gathered Statistics in current Session: "); goto Exit; } } // Output the web page. stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); if (bRefresh) { // Send back the page with a refresh command in the header fnPrintf( m_pHRequest, "" "" "System Statistics\n", m_pszURLString); printStyle(); popupFrame(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString); } else { fnPrintf( m_pHRequest, "System Statistics\n"); printStyle(); popupFrame(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString); } // Format the main heading title formatStatsHeading( pStatGather, pszHeading); // Begin the table fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString, (bRefresh ? "?Refresh" : "")); fnPrintf( m_pHRequest, "%s, ", pszTemp); if (!pStatGather->uiStartTime || gv_FlmSysData.Stats.uiStopTime) { fnPrintf( m_pHRequest, "Begin Statistics, ", m_pszURLString, FLM_START_STATS, (bRefresh ? "&Refresh" : "")); } if (pStatGather->uiStartTime && !gv_FlmSysData.Stats.uiStopTime) { fnPrintf( m_pHRequest, "End Statistics, ", m_pszURLString, FLM_STOP_STATS, (bRefresh ? "&Refresh" : "")); } fnPrintf( m_pHRequest, "Reset Statistics, ", m_pszURLString, FLM_RESET_STATS, (bRefresh ? "&Refresh" : "")); fnPrintf( m_pHRequest, "Set Focus", m_pszURLString, (bRefresh ? "&Refresh" : "")); printColumnHeadingClose(); printTableRowEnd(); printTableRowStart( TRUE); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Stats Order:  "); for (uiLoop = 0; uiLoop < MAX_STAT_TYPES; uiLoop++) { FLMUINT uiStat; char * pszStatOrder = ppszStatOrders [uiLoop]; if (f_stricmp( pszStatOrder, CACHE_STAT_STR) == 0) { uiStat = CACHE_STATS; } else if (f_stricmp( pszStatOrder, OPERATIONS_STAT_STR) == 0) { uiStat = OPERATION_STATS; } else if (f_stricmp( pszStatOrder, LOCKS_STAT_STR) == 0) { uiStat = LOCK_STATS; } else if (f_stricmp( pszStatOrder, CHECK_POINT_STR) == 0) { uiStat = CHECK_POINT_STATS; } else { uiStat = DISK_STATS; } uiStatOrders [uiLoop] = uiStat; fnPrintf( m_pHRequest, "%s: ", pszStatOrder); fnPrintf( m_pHRequest, "Top, ", m_pszURLString, (uiLoop * 2) + 1, (bRefresh ? "&Refresh" : "")); fnPrintf( m_pHRequest, "Up  \n", m_pszURLString, uiLoop * 2, (bRefresh ? "&Refresh" : "")); } printColumnHeadingClose(); printTableRowEnd(); printTableEnd(); displayStats( pStatGather, pOldStatGather, (FLMUINT *)uiStatOrders); printDocEnd(); Exit: fnEmit(); if (pStatGather) { freeCPInfoHeaders( pStatGather); freeLockUsers( pStatGather); f_free( &pStatGather); } if (pOldStatGather) { f_free( &pOldStatGather); } if (pvSession) { fnReleaseSession( pvSession); } if (pszTemp) { f_free( &pszTemp); } if (pszHeading) { f_free( &pszHeading); } return( rc); } /**************************************************************************** Desc: Formats the main title heading string so that it displays the start/stop /elapsed times for the statistics. ****************************************************************************/ void F_StatsPage::formatStatsHeading( STAT_GATHER * pStatGather, const char * pszHeading) { char szBuffer[ 30]; FLMUINT64 ui64ElapTime; FLMUINT uiCurrTime; flmAssert( pStatGather); flmAssert( pszHeading); f_sprintf( (char *)pszHeading, "Statistics:      "); // Are we collecting stats? if (pStatGather->uiStartTime) { printDate( gv_FlmSysData.Stats.uiStartTime, szBuffer); f_strcat( (char *)pszHeading, (char *)szBuffer); f_strcat( (char *)pszHeading, "   to   "); // Check for a stop time. if (gv_FlmSysData.Stats.uiStopTime) { printDate( gv_FlmSysData.Stats.uiStopTime, szBuffer); f_strcat( (char *)pszHeading, (char *)szBuffer); ui64ElapTime = (FLMUINT64)(gv_FlmSysData.Stats.uiStopTime - gv_FlmSysData.Stats.uiStartTime); } else { f_strcat( (char *)pszHeading, "Present"); f_timeGetSeconds( &uiCurrTime); ui64ElapTime = (FLMUINT64)(uiCurrTime - gv_FlmSysData.Stats.uiStartTime); } f_strcat( (char *)pszHeading, "      Elapsed: "); printElapTime( ui64ElapTime, szBuffer, JUSTIFY_LEFT, FALSE); f_strcat( (char *)pszHeading, (char *)szBuffer); } else { f_strcat( (char *)pszHeading, "      Not collecting"); } } /**************************************************************************** Desc: Outputs statistics for a particular IO category. ****************************************************************************/ void F_StatsPage::printIORow( FLMBOOL bHighlight, const char * pszIOCategory, DISKIO_STAT * pIOStat, DISKIO_STAT * pOldIOStat) { char szTemp[30]; printTableRowStart( bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "%s", pszIOCategory); printTableDataEnd(); printCommaNum( pIOStat->ui64Count, JUSTIFY_RIGHT, (pIOStat->ui64Count != pOldIOStat->ui64Count ? TRUE : FALSE)); printCommaNum( pIOStat->ui64TotalBytes, JUSTIFY_RIGHT, (pIOStat->ui64TotalBytes != pOldIOStat->ui64TotalBytes ? TRUE : FALSE)); printElapTime( pIOStat->ui64ElapMilli, szTemp); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%s%s", (pIOStat->ui64ElapMilli != pOldIOStat->ui64ElapMilli ? "" : ""), szTemp, (pIOStat->ui64ElapMilli != pOldIOStat->ui64ElapMilli ? "" : "")); printTableDataEnd(); if (pIOStat->ui64Count) { printElapTime( pIOStat->ui64ElapMilli / pIOStat->ui64Count, szTemp); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%s%s", (pOldIOStat->ui64Count ? (pIOStat->ui64ElapMilli / pIOStat->ui64Count != pOldIOStat->ui64ElapMilli / pOldIOStat->ui64Count ? "" : "") : "") , szTemp, (pOldIOStat->ui64Count ? (pIOStat->ui64ElapMilli / pIOStat->ui64Count != pOldIOStat->ui64ElapMilli / pOldIOStat->ui64Count ? "" : "") : "")); printTableDataEnd(); } else { printElapTime( 0); } printTableRowEnd(); } /**************************************************************************** Desc: Outputs count/time statistics for a particular category. ****************************************************************************/ void F_StatsPage::printCountTimeRow( FLMBOOL bHighlight, const char * pszCategory, F_COUNT_TIME_STAT * pStat, F_COUNT_TIME_STAT * pOldStat, FLMBOOL bPrintCountOnly) { char szTemp[ 30]; printTableRowStart( bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "%s", pszCategory); printTableDataEnd(); printCommaNum( pStat->ui64Count, JUSTIFY_RIGHT, (pStat->ui64Count != pOldStat->ui64Count ? TRUE : FALSE)); if (bPrintCountOnly) { printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); } else { printElapTime( pStat->ui64ElapMilli, szTemp); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%s%s", (pStat->ui64ElapMilli != pOldStat->ui64ElapMilli ? "" : ""), szTemp, (pStat->ui64ElapMilli != pOldStat->ui64ElapMilli ? "" : "")); printTableDataEnd(); if (pStat->ui64Count) { printElapTime( pStat->ui64ElapMilli / pStat->ui64Count, szTemp); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%s%s", (pOldStat->ui64Count ? (pStat->ui64ElapMilli / pStat->ui64Count != pOldStat->ui64ElapMilli / pOldStat->ui64Count ? "" : "") : ""), szTemp, (pOldStat->ui64Count ? (pStat->ui64ElapMilli / pStat->ui64Count != pOldStat->ui64ElapMilli / pOldStat->ui64Count ? "" : "") : "")); printTableDataEnd(); } else { printElapTime( 0); } } printTableRowEnd(); } /**************************************************************************** Desc: Outputs statistics for a particular Lock category. ****************************************************************************/ void F_StatsPage::printCacheStatRow( FLMBOOL bHighlight, const char * pszCategory, FLMUINT uiBlockCacheValue, FLMUINT uiRecordCacheValue, FLMBOOL bRecordCacheValueApplicable, FLMBOOL bBChangedValue, FLMBOOL bRChangedValue) { printTableRowStart( bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "%s", pszCategory); printTableDataEnd(); printCommaNum( uiBlockCacheValue, JUSTIFY_RIGHT, bBChangedValue); if (bRecordCacheValueApplicable) { printCommaNum( uiRecordCacheValue, JUSTIFY_RIGHT, bRChangedValue); } else { printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); } printTableRowEnd(); } /**************************************************************************** Desc: Outputs statistics page ****************************************************************************/ void F_StatsPage::displayStats( STAT_GATHER * pStatGather, STAT_GATHER * pOldStatGather, FLMUINT * puiStatOrders ) { FLMUINT uiLoop; for (uiLoop = 0; uiLoop < MAX_STAT_TYPES; uiLoop++) { switch (puiStatOrders [uiLoop]) { case CACHE_STATS: printCacheStats( pStatGather, pOldStatGather); break; case OPERATION_STATS: printOperationStats( pStatGather, pOldStatGather); break; case LOCK_STATS: printLockStats( pStatGather, pOldStatGather); break; case DISK_STATS: printDiskStats( pStatGather, pOldStatGather); break; case CHECK_POINT_STATS: printCPStats( pStatGather); break; default: break; } } fnPrintf( m_pHRequest, "
\n"); } /**************************************************************************** Desc: Deletes all LOCK_USER_HEADER structures linked from the pStatGather structure. ****************************************************************************/ void F_StatsPage::freeLockUsers( STAT_GATHER * pStatGather ) { LOCK_USER_HEADER_p pTmp; while (pStatGather->pLockUsers) { pTmp = pStatGather->pLockUsers; pStatGather->pLockUsers = pStatGather->pLockUsers->pNext; if (pTmp->pDbLockUser) { f_free( &pTmp->pDbLockUser); } if (pTmp->pTxLockUser) { f_free( &pTmp->pTxLockUser); } f_free( &pTmp); } } /**************************************************************************** Desc: Gets the LOCK_USER information for the specified pFile. ****************************************************************************/ void F_StatsPage::gatherLockStats( STAT_GATHER * pStatGather, FFILE * pFile) { LOCK_USER_HEADER_p pTmp; RCODE rc; flmAssert( pStatGather); flmAssert( pFile); // Allocate a new LOCK_USER_HEADER and link it into the list. if( RC_BAD( rc = f_alloc( sizeof( LOCK_USER_HEADER), &pTmp))) { goto Exit; } pTmp->pNext = pStatGather->pLockUsers; pStatGather->pLockUsers = pTmp; // Save the file name. if (pFile->pszDbPath) { f_strcpy( (char *)pTmp->szFileName, pFile->pszDbPath); } else { f_sprintf( (char *)pTmp->szFileName, "Unknown Db Name"); } // Now let's see if we can get the Lock User Info for the // two locks - Write locks (tx) and Wait locks (db). if (pFile->pFileLockObj) { if (RC_BAD( rc = pFile->pFileLockObj->getLockQueue( &pTmp->pDbLockUser))) { pTmp->pDbLockUser = NULL; } } else { pTmp->pDbLockUser = NULL; } if (pFile->pWriteLockObj) { if (RC_BAD( rc = pFile->pWriteLockObj->getLockQueue( &pTmp->pTxLockUser))) { pTmp->pTxLockUser = NULL; } } else { pTmp->pTxLockUser = NULL; } Exit: return; } /**************************************************************************** Desc: Gets the CHECKPOINT_INFO information for the specified pFile. ****************************************************************************/ void F_StatsPage::gatherCPStats( STAT_GATHER * pStatGather, FFILE * pFile) { CP_INFO_HEADER_p pTmp; RCODE rc = FERR_OK; flmAssert( pStatGather); flmAssert( pFile); // Allocate a new CP_INFO_HEADER and link it into the list. if( RC_BAD( rc = f_alloc( sizeof( CP_INFO_HEADER), &pTmp))) { goto Exit; } if( RC_BAD( rc = f_alloc( sizeof( CHECKPOINT_INFO), &pTmp->pCheckpointInfo))) { goto Exit; } // Save the file name. if (pFile->pszDbPath) { f_strcpy( (char *)pTmp->szFileName, pFile->pszDbPath); } else { f_sprintf( (char *)pTmp->szFileName, "Unknown Db Name"); } pTmp->pNext = pStatGather->pCPHeader; pStatGather->pCPHeader = pTmp; flmGetCPInfo( pFile, pTmp->pCheckpointInfo); Exit: if (RC_BAD(rc) && pTmp) { f_free( &pTmp); } return; } /**************************************************************************** Desc: Deletes all CP_INFO_HEADER structures linked from the pStatGather structure. ****************************************************************************/ void F_StatsPage::freeCPInfoHeaders( STAT_GATHER * pStatGather ) { CP_INFO_HEADER_p pTmp; while (pStatGather->pCPHeader) { pTmp = pStatGather->pCPHeader; pStatGather->pCPHeader = pStatGather->pCPHeader->pNext; if (pTmp->pCheckpointInfo) { f_free( &pTmp->pCheckpointInfo); } f_free( &pTmp); } } /**************************************************************************** Desc: Prints out the Cache Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::printCacheStats( STAT_GATHER * pStatGather, STAT_GATHER * pOldStatGather) { fnPrintf( m_pHRequest, "
\n"); // Cache table. printTableStart( "Cache", 3, 50); // Cache table column headers printTableRowStart(); printColumnHeading( "Stat Type", JUSTIFY_LEFT); printColumnHeading( "Block Cache", JUSTIFY_RIGHT); printColumnHeading( "Record Cache", JUSTIFY_RIGHT); printTableRowEnd(); printCacheStatRow( TRUE, "Current Limit (Bytes)", pStatGather->BlockCache.uiMaxBytes, pStatGather->RecordCache.uiMaxBytes, TRUE, (pStatGather->BlockCache.uiMaxBytes != pOldStatGather->BlockCache.uiMaxBytes ? TRUE : FALSE), (pStatGather->RecordCache.uiMaxBytes != pOldStatGather->RecordCache.uiMaxBytes ? TRUE : FALSE)); printCacheStatRow( FALSE, "Total Items Cached", pStatGather->BlockCache.uiCount, pStatGather->RecordCache.uiCount, TRUE, (pStatGather->BlockCache.uiCount != pOldStatGather->BlockCache.uiCount ? TRUE : FALSE), (pStatGather->RecordCache.uiCount != pOldStatGather->RecordCache.uiCount ? TRUE : FALSE)); printCacheStatRow( TRUE, "Total Bytes Cached", pStatGather->BlockCache.uiTotalBytesAllocated, pStatGather->RecordCache.uiTotalBytesAllocated, TRUE, (pStatGather->BlockCache.uiTotalBytesAllocated != pOldStatGather->BlockCache.uiTotalBytesAllocated ? TRUE : FALSE), (pStatGather->RecordCache.uiTotalBytesAllocated != pOldStatGather->RecordCache.uiTotalBytesAllocated ? TRUE : FALSE)); printCacheStatRow( FALSE, "Old Items Cached", pStatGather->BlockCache.uiOldVerCount, pStatGather->RecordCache.uiOldVerCount, TRUE, (pStatGather->BlockCache.uiOldVerCount != pOldStatGather->BlockCache.uiOldVerCount ? TRUE : FALSE), (pStatGather->RecordCache.uiOldVerCount != pOldStatGather->RecordCache.uiOldVerCount ? TRUE : FALSE)); printCacheStatRow( TRUE, "Old Bytes Cached", pStatGather->BlockCache.uiOldVerBytes, pStatGather->RecordCache.uiOldVerBytes, TRUE, (pStatGather->BlockCache.uiOldVerBytes != pOldStatGather->BlockCache.uiOldVerBytes ? TRUE : FALSE), (pStatGather->RecordCache.uiOldVerBytes != pOldStatGather->RecordCache.uiOldVerBytes ? TRUE : FALSE)); printCacheStatRow( FALSE, "Hits", pStatGather->BlockCache.uiCacheHits, pStatGather->RecordCache.uiCacheHits, TRUE, (pStatGather->BlockCache.uiCacheHits != pOldStatGather->BlockCache.uiCacheHits ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheHits != pOldStatGather->RecordCache.uiCacheHits ? TRUE : FALSE)); printCacheStatRow( TRUE, "Hit Looks", pStatGather->BlockCache.uiCacheHitLooks, pStatGather->RecordCache.uiCacheHitLooks, TRUE, (pStatGather->BlockCache.uiCacheHitLooks != pOldStatGather->BlockCache.uiCacheHitLooks ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheHitLooks != pOldStatGather->RecordCache.uiCacheHitLooks ? TRUE : FALSE)); printCacheStatRow( FALSE, "Looks per Hit", (pStatGather->BlockCache.uiCacheHits ? pStatGather->BlockCache.uiCacheHitLooks / pStatGather->BlockCache.uiCacheHits : (FLMUINT)0), (pStatGather->RecordCache.uiCacheHits ? pStatGather->RecordCache.uiCacheHitLooks / pStatGather->RecordCache.uiCacheHits : (FLMUINT)0), TRUE, (pStatGather->BlockCache.uiCacheHits != pOldStatGather->BlockCache.uiCacheHits ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheHits != pOldStatGather->RecordCache.uiCacheHits ? TRUE : FALSE)); printCacheStatRow( TRUE, "Faults", pStatGather->BlockCache.uiCacheFaults, pStatGather->RecordCache.uiCacheFaults, TRUE, (pStatGather->BlockCache.uiCacheFaults != pOldStatGather->BlockCache.uiCacheFaults ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheFaults != pOldStatGather->RecordCache.uiCacheFaults ? TRUE : FALSE)); printCacheStatRow( FALSE, "Fault Looks", pStatGather->BlockCache.uiCacheFaultLooks, pStatGather->RecordCache.uiCacheFaultLooks, TRUE, (pStatGather->BlockCache.uiCacheFaultLooks != pOldStatGather->BlockCache.uiCacheFaultLooks ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheFaultLooks != pOldStatGather->RecordCache.uiCacheFaultLooks ? TRUE : FALSE)); printCacheStatRow( TRUE, "Looks Per Fault", (pStatGather->BlockCache.uiCacheFaults ? pStatGather->BlockCache.uiCacheFaultLooks / pStatGather->BlockCache.uiCacheFaults : (FLMUINT)0), (pStatGather->RecordCache.uiCacheFaults ? pStatGather->RecordCache.uiCacheFaultLooks / pStatGather->RecordCache.uiCacheFaults : (FLMUINT)0), TRUE, (pStatGather->BlockCache.uiCacheFaults != pOldStatGather->BlockCache.uiCacheFaults ? TRUE : FALSE), (pStatGather->RecordCache.uiCacheFaults != pOldStatGather->RecordCache.uiCacheFaults ? TRUE : FALSE)); printCacheStatRow( FALSE, "Dirty Blocks", pStatGather->uiDirtyBlocks, 0, FALSE, (pStatGather->uiDirtyBlocks != pOldStatGather->uiDirtyBlocks ? TRUE : FALSE)); printCacheStatRow( TRUE, "Dirty Bytes", pStatGather->uiDirtyBytes, 0, FALSE, (pStatGather->uiDirtyBytes != pOldStatGather->uiDirtyBytes ? TRUE : FALSE)); printCacheStatRow( FALSE, "Log Blocks", pStatGather->uiLogBlocks, 0, FALSE, (pStatGather->uiLogBlocks != pOldStatGather->uiLogBlocks ? TRUE : FALSE)); printCacheStatRow( TRUE, "Log Bytes", pStatGather->uiLogBytes, 0, FALSE, (pStatGather->uiLogBytes != pOldStatGather->uiLogBytes ? TRUE : FALSE)); printCacheStatRow( FALSE, "Free Blocks", pStatGather->uiFreeCount, 0, FALSE, (pStatGather->uiFreeCount != pOldStatGather->uiFreeCount ? TRUE : FALSE)); printCacheStatRow( TRUE, "Free Bytes", pStatGather->uiFreeBytes, 0, FALSE, (pStatGather->uiFreeBytes != pOldStatGather->uiFreeBytes ? TRUE : FALSE)); printCacheStatRow( FALSE, "Replaceable Blocks", pStatGather->uiReplaceableCount, 0, FALSE, (pStatGather->uiReplaceableCount != pOldStatGather->uiReplaceableCount ? TRUE : FALSE)); printCacheStatRow( TRUE, "Replaceable Bytes", pStatGather->uiReplaceableBytes, 0, FALSE, (pStatGather->uiReplaceableBytes != pOldStatGather->uiReplaceableBytes ? TRUE : FALSE)); printTableEnd(); } /**************************************************************************** Desc: Prints out the Operation Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::printOperationStats( STAT_GATHER * pStatGather, STAT_GATHER * pOldStatGather) { F_COUNT_TIME_STAT Stat; F_COUNT_TIME_STAT OldStat; FLMBOOL bHighlight; if (pStatGather->uiStartTime) { fnPrintf( m_pHRequest, "
\n"); // Database operations table printTableStart( "Database Operations", 4, 75); // Operations table column headers printTableRowStart(); printColumnHeading( "Operation", JUSTIFY_LEFT); printColumnHeading( "Count", JUSTIFY_RIGHT); printColumnHeading( "Total Seconds", JUSTIFY_RIGHT); printColumnHeading( "Avg Seconds", JUSTIFY_RIGHT); printTableRowEnd(); // Transaction rows bHighlight = FALSE; printCountTimeRow( bHighlight = ~bHighlight, "Committed Update Trans", &pStatGather->CommittedUpdTrans, &pOldStatGather->CommittedUpdTrans); printCountTimeRow( bHighlight = ~bHighlight, "Aborted Update Trans", &pStatGather->AbortedUpdTrans, &pOldStatGather->AbortedUpdTrans); printCountTimeRow( bHighlight = ~bHighlight, "Group Finishes", &pStatGather->GroupCompletes, &pOldStatGather->GroupCompletes); Stat.ui64Count = pStatGather->ui64GroupFinished; OldStat.ui64Count = pOldStatGather->ui64GroupFinished; printCountTimeRow( bHighlight = ~bHighlight, "Total Finished", &Stat, &OldStat, TRUE); if (pStatGather->GroupCompletes.ui64Count) { Stat.ui64Count = pStatGather->ui64GroupFinished / pStatGather->GroupCompletes.ui64Count; } else { Stat.ui64Count = 0; } if (pOldStatGather->GroupCompletes.ui64Count) { OldStat.ui64Count = pOldStatGather->ui64GroupFinished / pOldStatGather->GroupCompletes.ui64Count; } else { OldStat.ui64Count = 0; } printCountTimeRow( bHighlight = ~bHighlight, "Average Per Group", &Stat, &OldStat, TRUE); printCountTimeRow( bHighlight = ~bHighlight, "Committed Read Trans", &pStatGather->CommittedReadTrans, &pOldStatGather->CommittedReadTrans); printCountTimeRow( bHighlight = ~bHighlight, "Aborted Read Trans", &pStatGather->AbortedReadTrans, &pOldStatGather->AbortedReadTrans); printCountTimeRow( bHighlight = ~bHighlight, "Reads", &pStatGather->Reads, &pOldStatGather->Reads); printCountTimeRow( bHighlight = ~bHighlight, "Adds", &pStatGather->Adds, &pOldStatGather->Adds); printCountTimeRow( bHighlight = ~bHighlight, "Modifies", &pStatGather->Modifies, &pOldStatGather->Modifies); printCountTimeRow( bHighlight = ~bHighlight, "Deletes", &pStatGather->Deletes, &pOldStatGather->Deletes); printCountTimeRow( bHighlight = ~bHighlight, "Queries", &pStatGather->Queries, &pOldStatGather->Queries, TRUE); printCountTimeRow( bHighlight = ~bHighlight, "Query Reads", &pStatGather->QueryReads, &pOldStatGather->QueryReads, TRUE); Stat.ui64Count = pStatGather->ui64BlockSplits; OldStat.ui64Count = pOldStatGather->ui64BlockSplits; printCountTimeRow( bHighlight = ~bHighlight, "Block Splits", &Stat, &OldStat, TRUE); Stat.ui64Count = pStatGather->ui64BlockCombines; OldStat.ui64Count = pOldStatGather->ui64BlockCombines; printCountTimeRow( bHighlight = ~bHighlight, "Block Combines", &Stat, &OldStat, TRUE); printTableEnd(); } } /**************************************************************************** Desc: Prints out the Lock Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::printLockStats( STAT_GATHER * pStatGather, STAT_GATHER * pOldStatGather) { FLMUINT uiLen; if (pStatGather->uiStartTime) { LOCK_USER_HEADER_p pLckHdr; fnPrintf( m_pHRequest, "
\n"); // Lock table. printTableStart( "Locks", 4, 75); // Locks table column headers printTableRowStart(); printColumnHeading( "Stat Type", JUSTIFY_LEFT); printColumnHeading( "Count", JUSTIFY_RIGHT); printColumnHeading( "Total Seconds", JUSTIFY_RIGHT); printColumnHeading( "Avg Seconds", JUSTIFY_RIGHT); printTableRowEnd(); printCountTimeRow( TRUE, "Time No Locks Held", &pStatGather->LockStats.NoLocks, &pOldStatGather->LockStats.NoLocks); printCountTimeRow( FALSE, "Time Waiting for Locks", &pStatGather->LockStats.WaitingForLock, &pOldStatGather->LockStats.WaitingForLock); printCountTimeRow( TRUE, "Time Locks Held", &pStatGather->LockStats.HeldLock, &pOldStatGather->LockStats.HeldLock); printTableEnd(); // Display the Lock Queue for each of the files open... pLckHdr = pStatGather->pLockUsers; while (pLckHdr) { char szTitle[ 128]; FLMBOOL bHighlight = FALSE; FLMBOOL bLocked = TRUE; F_LOCK_USER * pLckUsr; FLMUINT uiTxWaiters; FLMUINT uiDbWaiters; uiTxWaiters = 0; pLckUsr = pLckHdr->pTxLockUser; while (pLckUsr && pLckUsr->uiThreadId) { uiTxWaiters++; pLckUsr++; } if( uiTxWaiters) { uiTxWaiters--; } uiDbWaiters = 0; pLckUsr = pLckHdr->pDbLockUser; while (pLckUsr && pLckUsr->uiThreadId) { uiDbWaiters++; pLckUsr++; } if( uiDbWaiters) { uiDbWaiters--; } fnPrintf( m_pHRequest, "
\n"); // Start the new table f_sprintf( (char *)szTitle, "Lock Queue - %s, TX Waiters: %u, DB Waiters: %u", pLckHdr->szFileName, (unsigned)uiTxWaiters, (unsigned)uiDbWaiters); printTableStart( (char *)szTitle, 4, 75); printTableRowStart( bHighlight = ~bHighlight); printColumnHeading( "Thread Id", JUSTIFY_LEFT); printColumnHeading( "Name", JUSTIFY_RIGHT); printColumnHeading( "Status", JUSTIFY_RIGHT); printColumnHeading( "Time", JUSTIFY_RIGHT); printTableRowEnd(); // Display the body... pLckUsr = pLckHdr->pTxLockUser; while (pLckUsr && pLckUsr->uiThreadId) { char szThreadName[ 50]; printTableRowStart( bHighlight = ~bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "%u", (unsigned)pLckUsr->uiThreadId); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); uiLen = sizeof( szThreadName); gv_FlmSysData.pThreadMgr->getThreadName( pLckUsr->uiThreadId, szThreadName, &uiLen); fnPrintf( m_pHRequest, "%s", szThreadName); printTableDataEnd(); // Status printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s (Tx)", bLocked ? "Locked" : "Waiting"); bLocked = FALSE; printTableDataEnd(); // Time in status printElapTime( (FLMUINT64)pLckUsr->uiTime, NULL, JUSTIFY_RIGHT, TRUE); printTableRowEnd(); // Next entry... pLckUsr++; } // Display the Db info. pLckUsr = pLckHdr->pDbLockUser; bLocked = TRUE; while (pLckUsr && pLckUsr->uiThreadId) { char szThreadName[ 50]; printTableRowStart( bHighlight = ~bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "%u", (unsigned)pLckUsr->uiThreadId); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); uiLen = sizeof( szThreadName); gv_FlmSysData.pThreadMgr->getThreadName( pLckUsr->uiThreadId, szThreadName, &uiLen); fnPrintf( m_pHRequest, "%s", szThreadName); printTableDataEnd(); // Status printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s (Db)", bLocked ? "Locked" : "Waiting"); bLocked = FALSE; printTableDataEnd(); // Time in status printElapTime( (FLMUINT64)pLckUsr->uiTime, NULL, JUSTIFY_RIGHT, TRUE); printTableRowEnd(); // Next entry... pLckUsr++; } printTableEnd(); pLckHdr = pLckHdr->pNext; } } } /**************************************************************************** Desc: Prints out the Disk Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::printDiskStats( STAT_GATHER * pStatGather, STAT_GATHER * pOldStatGather) { char szTemp[ 100]; FLMBOOL bHighlight = FALSE; if (pStatGather->uiStartTime) { fnPrintf( m_pHRequest, "
\n"); // Disk IO table. f_sprintf( (char *)szTemp, "Disk IO"); if (m_pFocusBlock) { f_strcat( (char *)szTemp, " - focus enabled on "); f_strcat( (char *)szTemp, (char *)m_pFocusBlock->szFileName); if (m_pFocusBlock->uiLFileNum > 0) { char szLFNum[ 20]; f_strcat( (char *)szTemp, " on logical file "); f_sprintf( (char *)szLFNum, "%lu", m_pFocusBlock->uiLFileNum); f_strcat( (char *)szTemp, (char *)szLFNum); } } printTableStart( (char *)szTemp, 5, 100); // Database operations table column headers printTableRowStart(); printColumnHeading( "IO CATEGORY", JUSTIFY_LEFT); printColumnHeading( "Count", JUSTIFY_RIGHT); printColumnHeading( "Total Bytes", JUSTIFY_RIGHT); printColumnHeading( "Total Seconds", JUSTIFY_RIGHT); printColumnHeading( "Avg Seconds", JUSTIFY_RIGHT); printTableRowEnd(); printIORow( bHighlight = !bHighlight, "READS", &pStatGather->IOReads, &pOldStatGather->IOReads); printIORow( bHighlight = !bHighlight, "Root Blocks", &pStatGather->IORootBlockReads, &pOldStatGather->IORootBlockReads); printIORow( bHighlight = !bHighlight, "Non-Leaf Blocks", &pStatGather->IONonLeafBlockReads, &pOldStatGather->IONonLeafBlockReads); printIORow( bHighlight = !bHighlight, "Leaf Blocks", &pStatGather->IOLeafBlockReads, &pOldStatGather->IOLeafBlockReads); if ((m_pFocusBlock == NULL) || (m_pFocusBlock->uiLFileNum == 0)) { printIORow( bHighlight = !bHighlight, "Avail Blocks", &pStatGather->IOAvailBlockReads, &pOldStatGather->IOAvailBlockReads); printIORow( bHighlight = !bHighlight, "LFH Blocks", &pStatGather->IOLFHBlockReads, &pOldStatGather->IOLFHBlockReads); printIORow( bHighlight = !bHighlight, "Prior Image Blocks", &pStatGather->IORollbackBlockReads, &pOldStatGather->IORollbackBlockReads); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Read Errors"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%u%s", (pStatGather->uiReadErrors != pOldStatGather->uiReadErrors ? "" : ""), (unsigned)pStatGather->uiReadErrors, (pStatGather->uiReadErrors != pOldStatGather->uiReadErrors ? "" : "")); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Check Errors"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%u%s", (pStatGather->uiCheckErrors != pOldStatGather->uiCheckErrors ? "" : ""), (unsigned)pStatGather->uiCheckErrors, (pStatGather->uiCheckErrors != pOldStatGather->uiCheckErrors ? "" : "")); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableRowEnd(); } printIORow( bHighlight = !bHighlight, "WRITES", &pStatGather->IOWrites, &pOldStatGather->IOWrites); printIORow( bHighlight = !bHighlight, "Root Blocks", &pStatGather->IORootBlockWrites, &pOldStatGather->IORootBlockWrites); printIORow( bHighlight = !bHighlight, "Non-Leaf Blocks", &pStatGather->IONonLeafBlockWrites, &pOldStatGather->IONonLeafBlockWrites); printIORow( bHighlight = !bHighlight, "Leaf Blocks", &pStatGather->IOLeafBlockWrites, &pOldStatGather->IOLeafBlockWrites); if ((m_pFocusBlock == NULL) || (m_pFocusBlock->uiLFileNum == 0)) { printIORow( bHighlight = !bHighlight, "Avail Blocks", &pStatGather->IOAvailBlockWrites, &pOldStatGather->IOAvailBlockWrites); printIORow( bHighlight = !bHighlight, "LFH Blocks", &pStatGather->IOLFHBlockWrites, &pOldStatGather->IOLFHBlockWrites); printIORow( bHighlight = !bHighlight, "Rollback Log Blocks", &pStatGather->IORollBackLogWrites, &pOldStatGather->IORollBackLogWrites); printIORow( bHighlight = !bHighlight, "Log Header", &pStatGather->IOLogHdrWrites, &pOldStatGather->IOLogHdrWrites); printIORow( bHighlight = !bHighlight, "Undo Blocks", &pStatGather->IORolledbackBlockWrites, &pOldStatGather->IORolledbackBlockWrites); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Write Errors"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s%u%s", (pStatGather->uiWriteErrors != pOldStatGather->uiWriteErrors ? "" : ""), (unsigned)pStatGather->uiWriteErrors, (pStatGather->uiWriteErrors != pOldStatGather->uiWriteErrors ? "" : "")); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableRowEnd(); } printTableEnd(); } } /**************************************************************************** Desc: Prints out the Checkpoint Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::printCPStats( STAT_GATHER * pStatGather) { CP_INFO_HEADER_p pCPHdr; fnPrintf( m_pHRequest, "
\n"); // Checkpoint Thread table. pCPHdr = pStatGather->pCPHeader; while (pCPHdr) { char szTitle[ 50]; CHECKPOINT_INFO * pCPInfo; FLMBOOL bHighlight = FALSE; f_sprintf( (char *)szTitle, "Checkpoint Thread - %s", pCPHdr->szFileName); printTableStart( (char *)szTitle, 2, 50); printTableRowStart(); printColumnHeading( "Stat Type", JUSTIFY_LEFT); printColumnHeading( "Value", JUSTIFY_RIGHT); printTableRowEnd(); pCPInfo = pCPHdr->pCheckpointInfo; printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "State"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s", pCPInfo->bRunning ? "Yes" : "No"); printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Running Time"); printTableDataEnd(); printElapTime( (FLMUINT64)pCPInfo->uiRunningTime, NULL, JUSTIFY_RIGHT, TRUE); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Forcing Checkpoint"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s", pCPInfo->bForcingCheckpoint ? "Yes" : "No"); printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Forced Checkpoint Running Time"); printTableDataEnd(); printElapTime( (FLMUINT64)pCPInfo->uiForceCheckpointRunningTime, NULL, JUSTIFY_RIGHT, TRUE); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Forced Checkpoint Reason"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); switch( pCPInfo->iForceCheckpointReason) { case CP_TIME_INTERVAL_REASON: fnPrintf( m_pHRequest, "Time interval"); break; case CP_SHUTTING_DOWN_REASON: fnPrintf( m_pHRequest, "Shutting down"); break; case CP_RFL_VOLUME_PROBLEM: fnPrintf( m_pHRequest, "RFL volume problem"); break; default: fnPrintf( m_pHRequest, "Unknown"); break; } printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Waiting for Read Trans Time"); printTableDataEnd(); printElapTime( (FLMUINT64)pCPInfo->uiWaitTruncateTime, NULL, JUSTIFY_RIGHT, TRUE); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Writing Data Blocks"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%s", pCPInfo->bWritingDataBlocks ? "Yes" : "No"); printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Log Blocks Written"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%u", pCPInfo->uiLogBlocksWritten); printTableDataEnd(); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Data Blocks Written"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%u", pCPInfo->uiDataBlocksWritten); printTableDataEnd(); printTableRowEnd(); if (pCPInfo->uiDirtyCacheBytes && pCPInfo->uiBlockSize) { printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Dirty Cache Blocks"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%u", pCPInfo->uiDirtyCacheBytes / pCPInfo->uiBlockSize); printTableDataEnd(); printTableRowEnd(); } printTableRowStart( bHighlight = !bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT); fnPrintf( m_pHRequest, "Block Size"); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_RIGHT); fnPrintf( m_pHRequest, "%u", pCPInfo->uiBlockSize); printTableDataEnd(); printTableRowEnd(); printTableEnd(); pCPHdr = pCPHdr->pNext; } } /**************************************************************************** Desc: Prints out the Checkpoint Stats stored in pStatGather. ****************************************************************************/ void F_StatsPage::displayFocus( FLMUINT uiNumParams, const char ** ppszParams) { FLMBOOL bFocusAll; FLMBOOL bFocusLFile; FLMBOOL bFocusDb; void * pvSession = NULL; char szTmpFocus[ 1] = { 0 }; FLMUINT uiLoop; bFocusAll = DetectParameter( uiNumParams, ppszParams, "All"); bFocusLFile = DetectParameter( uiNumParams, ppszParams, "LFile"); bFocusDb = DetectParameter( uiNumParams, ppszParams, "Db"); if (gv_FlmSysData.HttpConfigParms.fnAcquireSession) { if ((pvSession = fnAcquireSession()) == NULL) { printErrorPage( FERR_FAILURE, TRUE, "Could not obtain session handle"); goto Exit; } } if (!bFocusLFile & !bFocusDb & !bFocusAll) { printDocStart( "Focus"); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printTableStart( "All Databases", 1, 100); printTableEnd(); printButton( "Submit", BT_Submit); fnPrintf( m_pHRequest, "\n"); // We need to collect a list of databases to present. f_mutexLock( gv_FlmSysData.Stats.hMutex); for (uiLoop = 0; uiLoop < gv_FlmSysData.Stats.uiNumDbStats; uiLoop++) { FLMBOOL bHighlight = FALSE; fnPrintf( m_pHRequest, "\n", uiLoop, m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n", gv_FlmSysData.Stats.pDbStats[ uiLoop].pszDbName); // Start a new table... printTableStart( (char *)gv_FlmSysData.Stats.pDbStats[ uiLoop].pszDbName, 3, 100); printTableRowStart(); printColumnHeading( "Select",JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 1, 1); printColumnHeading( "Logical File Type", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 1, 1); printColumnHeading( "Logical File Number", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 1, 1); printTableRowEnd(); printTableRowStart( bHighlight = !bHighlight); printTableDataStart(); fnPrintf( m_pHRequest, "\n"); printTableDataEnd(); printTableDataStart( ); fnPrintf( m_pHRequest, "All Logical files\n"); printTableDataEnd(); printTableDataStart(); fnPrintf( m_pHRequest, "N/A"); printTableDataEnd(); printTableRowEnd(); // Now for each file, present the LFiles... for (int iLoop = 0; iLoop < (int)gv_FlmSysData.Stats.pDbStats[ uiLoop].uiNumLFileStats; iLoop++) { printTableRowStart( bHighlight = !bHighlight); printTableDataStart(); fnPrintf( m_pHRequest, "", gv_FlmSysData.Stats.pDbStats[ uiLoop].pLFileStats[iLoop].uiLFileNum); printTableDataEnd(); printTableDataStart(); fnPrintf( m_pHRequest, "%s", (gv_FlmSysData.Stats.pDbStats[ uiLoop].pLFileStats[iLoop].uiFlags & LFILE_IS_INDEX ? "Index" : (gv_FlmSysData.Stats.pDbStats[ uiLoop].pLFileStats[iLoop].uiFlags & LFILE_TYPE_UNKNOWN ? "Unknown" : "Container"))); printTableDataEnd(); printTableDataStart(); fnPrintf( m_pHRequest, "%u", gv_FlmSysData.Stats.pDbStats[ uiLoop].pLFileStats[iLoop].uiLFileNum); printTableDataEnd(); printTableRowEnd(); } printTableEnd(); printButton( "Submit", BT_Submit); fnPrintf( m_pHRequest, "\n"); } f_mutexUnlock( gv_FlmSysData.Stats.hMutex); printDocEnd(); goto Exit; } if (bFocusAll) { // A request to set the focus to all indicates that we are currently // focusing on something other than All, so we will need to delete // the current focus setting. Setting the value to NULL will delete // the existing entry. If we find an entry in m_pFocusBlock // (which we shouldn't) we will delete it. if (m_pFocusBlock) { flmAssert( 0); f_free( &m_pFocusBlock); } if (fnSetSessionValue( pvSession, STAT_FOCUS, (void *)szTmpFocus, 0) != 0) { flmAssert( 0); printErrorPage( FERR_MEM, TRUE, "Could not process request due to a memory allocation failure"); goto Exit; } } else { // We assume we have bDb set since bLFile cannot be set without it. // Retrieve the Db value. char szDb[ 101]; char szLFile[ 21]; char szTemp[ 123]; if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, "Db", sizeof( szDb), szDb))) { printErrorPage( FERR_INVALID_PARM, TRUE, "Parameter Db not present. Could not process this request."); goto Exit; } if (bFocusLFile) { if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, "LFile", sizeof( szLFile), szLFile))) { printErrorPage( FERR_INVALID_PARM, TRUE, "Parameter Db not present. Could not process this request."); goto Exit; } } fcsDecodeHttpString( szDb); f_sprintf( (char *)szTemp, "%.100s;%.20s", szDb, (char *)szLFile); // Now save this in the current session... if (fnSetSessionValue( pvSession, STAT_FOCUS, (void *)szTemp, (FLMSIZET)f_strlen(szTemp)) != 0) { flmAssert( 0); goto Exit; } } // Getting to this point indicates success. We will return a confirmation page. printDocStart( "Focus - Confirmation"); fnPrintf( m_pHRequest, "\n", m_pszURLString); printDocEnd(); Exit: if (pvSession) { fnReleaseSession( pvSession); } } /**************************************************************************** Desc: Prints out the Checkpoint Stats stored in pStatGather. ****************************************************************************/ RCODE F_StatsPage::setFocus( char * pszFocus) { RCODE rc = FERR_OK; char * pTmp; if (m_pFocusBlock) { flmAssert( 0); f_free( &m_pFocusBlock); } if (f_strlen( pszFocus) == 0) { goto Exit; } if( RC_BAD( rc = f_alloc( sizeof( FOCUS_BLOCK), &m_pFocusBlock))) { goto Exit; } pTmp = pszFocus; m_pFocusBlock->uiLFileNum = 0; while (*pTmp != ';' && *pTmp != '\0') { pTmp++; } *pTmp = 0; f_strcpy( m_pFocusBlock->szFileName, pszFocus); pTmp++; if( *pTmp != '\0') { m_pFocusBlock->uiLFileNum = f_atoud( pTmp); } Exit: return( rc); } libflaim-4.9.966/src/imonfact.cpp0000644000175000017500000003663410510774540020174 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Factory class for pages created to do HTTP monitoring. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonfact.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Procedure to instantiate a new WebPage object given a text string ****************************************************************************/ RCODE F_WebPageFactory::create( const char * pszName, F_WebPage ** ppPage, HRequest * pHRequest) { RCODE rc = FERR_OK; void * pvSession = NULL; void * pvUser = NULL; ACQUIRE_SESSION_FN fnAcquireSession = gv_FlmSysData.HttpConfigParms.fnAcquireSession; ACQUIRE_USER_FN fnAcquireUser = gv_FlmSysData.HttpConfigParms.fnAcquireUser; flmAssert( ppPage); // Get the session for this user. if (fnAcquireSession) { if ((pvSession = fnAcquireSession( pHRequest)) == NULL) { rc = RC_SET( FERR_FAILURE); // We should expect to succeed here. goto Exit; } } // Get the current user ... if (fnAcquireUser) { if ((pvUser = fnAcquireUser( pvSession, pHRequest)) == NULL) { rc = RC_SET( FERR_FAILURE); goto Exit; } } // Are we being asked for the 'home page'? if (*pszName == '\0') { if( (*ppPage= m_fnDefault()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else // search the registry { FLMINT iEntryNum = searchRegistry ( pszName); if (iEntryNum == -1) { if ( (*ppPage = m_fnError()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else { // Check for a secure page. Ignore it if we don't have any session info. if (pvSession && isSecurePage( iEntryNum)) { // Make sure: // 1) Security is enable and not expired. // 2) The user has entered the secure password in the Nav bar. if ( isSecureAccessEnabled()) { if ( isSecurePasswordEntered( pvSession)) { if( (*ppPage = m_Registry[iEntryNum].fnCreate()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else { // Return an error page if ( (*ppPage = m_fnSessionAccess()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } } else if ( (*ppPage = m_fnGblAccess()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else if( (*ppPage = m_Registry[iEntryNum].fnCreate()) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } } Exit: if (pvSession) { gv_FlmSysData.HttpConfigParms.fnReleaseSession( pvSession); } if (pvUser) { gv_FlmSysData.HttpConfigParms.fnReleaseUser( pvUser); } return( rc); } /**************************************************************************** Desc: Tells the factory that the WebPage object is no longer needed ****************************************************************************/ void F_WebPageFactory::Release( F_WebPage ** ppPage) { if (ppPage && *ppPage) { (*ppPage)->releaseSession(); (*ppPage)->Release(); *ppPage = NULL; } } /**************************************************************************** Desc: Takes the entries in the registry and sorts them on the name field (We do this because we don't trust programmers to spell or alphabetize..:) ****************************************************************************/ void F_WebPageFactory::sortRegistry() { // We're going to use an insertion-sort algorithm here because it's simple // and has good performance for stuff that is already sorted or 'mostly' // sorted. FLMUINT uiInsertionPoint; FLMUINT uiCurrent; // First - how many entries in tmp? m_uiNumEntries=0; while( m_Registry[m_uiNumEntries++].fnCreate != NULL) { ; } m_uiNumEntries--; // The last entry in the array is NULL... // Basic algorithm: As uiCurrent goes from 1 to m_uiNumEntries-1, examine // the entries from 0 to uiCurrent-1 and place Nth entry in it's proper place. // (We'll use m_Registry[uiNumEntries] as a temporary holding spot. // Clever, eh?) for (uiCurrent = 1; uiCurrent < m_uiNumEntries; uiCurrent++) { uiInsertionPoint = uiCurrent; while( (f_strcmp( m_Registry[uiCurrent].pszName, m_Registry[uiInsertionPoint-1].pszName) < 0) && (uiInsertionPoint > 0) ) { uiInsertionPoint--; } if (uiInsertionPoint < uiCurrent) { // Copy the entry at uiCurrent to temp space... f_memcpy( &m_Registry[m_uiNumEntries], &m_Registry[uiCurrent], sizeof( RegistryEntry)); // Move the appropriate entries up f_memmove( &m_Registry[uiInsertionPoint + 1], &m_Registry[uiInsertionPoint], (uiCurrent-uiInsertionPoint) * sizeof( RegistryEntry)); //Copy the stuff in tmp to its sorted position... f_memcpy( &m_Registry[uiInsertionPoint], &m_Registry[m_uiNumEntries], sizeof( RegistryEntry)); } } //Reset the entry that we've been using for tmp storage f_memset( &m_Registry[m_uiNumEntries], 0, sizeof( RegistryEntry)); } /**************************************************************************** Desc: Returns the index into m_pRegistry for pszName, -1 if pszName is not in m_pRegistry (Uses a binary search, so make sure the registry is in sorted order!) ****************************************************************************/ FLMINT F_WebPageFactory::searchRegistry ( const char * pszName) { FLMBOOL bFound = FALSE; #define MAX_LEN 100 char szPath[ MAX_LEN]; char * pszFirstSlash; FLMUINT uiLow; FLMUINT uiHigh; FLMUINT uiTblSize; FLMUINT uiMid; FLMINT iCmp; // We only want the part of the string up to the first '/'. Anything // after that will be handled by the web page itself... pszFirstSlash = f_strchr( pszName, '/'); if (pszFirstSlash) { flmAssert( (pszFirstSlash - pszName) < MAX_LEN); f_strncpy( szPath, pszName, (pszFirstSlash - pszName)); szPath[pszFirstSlash-pszName] = '\0'; } else { flmAssert( f_strlen( pszName) < MAX_LEN); f_strcpy( szPath, pszName); } uiLow = 0; uiHigh = uiTblSize = m_uiNumEntries-1; for (;;) { uiMid = (uiLow + uiHigh) / 2; iCmp = f_strcmp( szPath, m_Registry[uiMid].pszName); if (iCmp == 0) { // Found Match bFound = TRUE; break; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found break; } if (iCmp < 0) { if (uiMid == 0) { break; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { break; } uiLow = uiMid + 1; } } return( bFound ? (FLMINT)uiMid : -1); } /****************************************************************** Desc: Function to test the Secure password has been entered for this user. If it has, then it will be storted in the session under the name defined by the constant FLM_SECURE_PASSWORD. *******************************************************************/ FLMBOOL F_WebPageFactory::isSecurePasswordEntered( void * pvSession) { GET_SESSION_VALUE_FN fnGetSessionValue = gv_FlmSysData.HttpConfigParms.fnGetSessionValue; char szData[ 21]; FLMUINT uiSize = sizeof( szData) - 1; FLMBOOL bResult = FALSE; flmAssert( fnGetSessionValue); flmAssert( pvSession); if (fnGetSessionValue( pvSession, FLM_SECURE_PASSWORD, (void *)szData, (FLMSIZET *)&uiSize) == 0) { szData[ uiSize] = '\0'; bResult = isValidSecurePassword( szData); } return bResult; } /****************************************************************** Desc: Function to test if the secure password entered matches the password stored globally. *******************************************************************/ FLMBOOL F_WebPageFactory::isValidSecurePassword( const char * pszData) { GET_GBL_VALUE_FN fnGetGblValue = gv_FlmSysData.HttpConfigParms.fnGetGblValue; char szPassword[21]; FLMUINT uiSize = sizeof( szPassword) -1; FLMBOOL bResult = FALSE; flmAssert( fnGetGblValue); if (fnGetGblValue( FLM_SECURE_PASSWORD, szPassword, (FLMSIZET *)&uiSize) == 0) { szPassword[ uiSize] = '\0'; if (f_strcmp(pszData, szPassword) == 0) { bResult = TRUE; } } return bResult; } /****************************************************************** Desc: Function to test if the secure password entered matches the password stored globally. This function expects that the expiration time will be a string representation of the time (i.e. FLM_GET_TIMER + some duration). *******************************************************************/ FLMBOOL F_WebPageFactory::isSecureAccessEnabled() { GET_GBL_VALUE_FN fnGetGblValue = gv_FlmSysData.HttpConfigParms.fnGetGblValue; char szExpiration[ 20]; FLMUINT uiExpSize = sizeof( szExpiration); FLMUINT uiExpTime; FLMUINT uiCurrTime; FLMBOOL bResult = FALSE; flmAssert( fnGetGblValue); // Assuming that an error code will be returned if the global value // has not been set. if (fnGetGblValue( FLM_SECURE_EXPIRATION, szExpiration, (FLMSIZET *)&uiExpSize) == 0) { uiExpTime = f_atoud( szExpiration); f_timeGetSeconds( &uiCurrTime); if (uiCurrTime < uiExpTime) { bResult = TRUE; } } return bResult; } /**************************************************************************** Desc: Each of these functions, when called, will create a new object of a particular class. They are called by WebPageFactory::Create() ****************************************************************************/ static F_WebPage * createErrorPage() { return f_new F_ErrorPage; } static F_WebPage * createGblAccessPage() { return f_new F_GblAccessPage; } static F_WebPage * createSessionAccessPage() { return f_new F_SessionAccessPage; } static F_WebPage * createSCacheBlockPage() { return f_new F_SCacheBlockPage; } static F_WebPage * createSCacheHashTablePage() { return f_new F_SCacheHashTablePage; } static F_WebPage * createSCacheUseListPage() { return f_new F_SCacheUseListPage; } static F_WebPage * createSCacheNotifyListPage() { return f_new F_SCacheNotifyListPage; } static F_WebPage * createSCacheDataPage() { return f_new F_SCacheDataPage; } static F_WebPage * createSCacheMgrPage() { return f_new F_SCacheMgrPage; } static F_WebPage * createQueriesPage() { return f_new F_QueriesPage; } static F_WebPage * createQueryPage() { return f_new F_QueryPage; } static F_WebPage * createQueryStatsPage() { return f_new F_QueryStatsPage; } static F_WebPage * createSysConfigPage() { return f_new F_SysConfigPage; } static F_WebPage * createStatsPage() { return f_new F_StatsPage; } static F_WebPage * createFlmSysDataPage() { return f_new F_FlmSysDataPage; } static F_WebPage * createHttpConfigParmsPage() { return f_new F_HttpConfigParmsPage; } static F_WebPage * createFlmThreadsPage() { return f_new F_FlmThreadsPage; } static F_WebPage * createFlmIndexPage() { return f_new F_FlmIndexPage; } static F_WebPage * createIndexListPage() { return f_new F_IndexListPage; } static F_WebPage * createSelectPage() { return f_new F_SelectPage; } static F_WebPage * createCheckDbPage() { return f_new F_CheckDbPage; } static F_WebPage * serveFile() { return f_new F_HttpFile; } static F_WebPage * createDbBackupPage() { return f_new F_HttpDbBackup; } static F_WebPage * createFileHashTblPage() { return f_new F_FileHashTblPage; } static F_WebPage * createFFilePage() { return f_new F_FFilePage; } static F_WebPage * createFDBPage() { return f_new F_FDBPage; } static F_WebPage * createRCacheMgrPage() { return f_new F_RCacheMgrPage; } static F_WebPage * createRCachePage() { return f_new F_RCachePage; } static F_WebPage * createRecordMgrPage() { return f_new F_RecordMgrPage; } static F_WebPage * createRCHashBucketPage() { return f_new F_RCHashBucketPage; } // Frame pages static F_WebPage * createHeaderFrame() { return f_new F_FrameHeader; } static F_WebPage * createMainFrame() { return f_new F_FrameMain; } static F_WebPage * createNavFrame() { return f_new F_FrameNav; } static F_WebPage * createWelcomeFrame() { return f_new F_FrameWelcome; } static F_WebPage * createSecureDbAccessPage() { return f_new F_SecureDbAccess; } static F_WebPage * createSecureDbInfoPage() { return f_new F_SecureDbInfo; } static F_WebPage * createDatabaseConfigPage() { return f_new F_DatabaseConfigPage; } static F_WebPage * createDatabasePage() { return f_new F_DatabasePage; } static F_WebPage * createRecordPage() { return f_new F_RecordPage; } static F_WebPage * createProcessRecordPage() { return f_new F_ProcessRecordPage; } static F_WebPage * createLogHeaderPage() { return f_new F_LogHeaderPage; } // Initialize the static variables in the class... CREATE_FN F_WebPageFactory::m_fnDefault = createMainFrame; CREATE_FN F_WebPageFactory::m_fnError = createErrorPage; CREATE_FN F_WebPageFactory::m_fnGblAccess = createGblAccessPage; CREATE_FN F_WebPageFactory::m_fnSessionAccess = createSessionAccessPage; RegistryEntry F_WebPageFactory::m_Registry[] = { {"FDB", createFDBPage, FALSE}, {"FFile", createFFilePage, FALSE}, {"FileHashTbl", createFileHashTblPage, FALSE}, {"FlmSysData", createFlmSysDataPage, FALSE}, {"Header.htm", createHeaderFrame, FALSE}, {"HttpConfigParms", createHttpConfigParmsPage, FALSE}, {"LogHdr", createLogHeaderPage, FALSE}, {"Nav.htm", createNavFrame, FALSE}, {"ProcessRecord", createProcessRecordPage, TRUE}, {"Queries", createQueriesPage, FALSE}, {"Query", createQueryPage, FALSE}, {"QueryStats", createQueryStatsPage, FALSE}, {"RCHashBucket", createRCHashBucketPage, FALSE}, {"RCache", createRCachePage, FALSE}, {"RCacheMgr", createRCacheMgrPage, FALSE}, {"Record", createRecordPage, TRUE}, {"SCacheBlock", createSCacheBlockPage, FALSE}, {"SCacheData", createSCacheDataPage, TRUE}, {"SCacheHashTable", createSCacheHashTablePage, FALSE}, {"SCacheMgr", createSCacheMgrPage, FALSE}, {"SCacheNotifyList", createSCacheNotifyListPage, FALSE}, {"SCacheUseList", createSCacheUseListPage, FALSE}, {"SecureDbAccess", createSecureDbAccessPage, FALSE}, {"SecureDbInfo", createSecureDbInfoPage, FALSE}, {"Stats", createStatsPage, FALSE}, {"SysConfig", createSysConfigPage, TRUE}, {"Welcome.htm", createWelcomeFrame, FALSE}, {"checkdb", createCheckDbPage, TRUE}, {"database", createDatabasePage, TRUE}, {"dbconfig", createDatabaseConfigPage, TRUE}, {"dbbackup", createDbBackupPage, TRUE}, {"file", serveFile, TRUE}, {"index", createFlmIndexPage, TRUE}, {"indexlist", createIndexListPage, TRUE}, {"recordmgr", createRecordMgrPage, TRUE}, {"select", createSelectPage, TRUE}, {"staticfile", serveFile, FALSE}, {"threads", createFlmThreadsPage, TRUE}, {"", NULL, FALSE} }; // WARNING: Make sure that every different WebPage class that you want to // display is listed in the array above. libflaim-4.9.966/src/imonffil.cpp0000644000175000017500000007672010510774540020177 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying an FFILE structure in HTML on a web page. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonffil.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: This function handle the details of extracting the parameters needed to interpret the request and then generating the response HTML page ****************************************************************************/ RCODE F_FFilePage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; #define GENERIC_SIZE_B 20 char szFrom[ GENERIC_SIZE_B]; char szBucket[ 4]; FLMUINT uiBucket; FFILE localFFile; FFILE * pFile; FLMBOOL bRefresh; void * pvAddress; char szAddress[GENERIC_SIZE_B]; char szLink[GENERIC_SIZE_B]; FLMBOOL bFlmLocked = FALSE; DATASTRUCT DataStruct; FLMBYTE * pszTemp = NULL; FLMBYTE * pszTemp1 = NULL; if( RC_BAD( rc = f_alloc( 150, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } if( RC_BAD( rc = f_alloc( 150, &pszTemp1))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Initialize a few variables first... szFrom[0] = '\0'; szBucket[0] = '\0'; pFile = NULL; // Get the "From" parameter. We use this to determine everything else. if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "From", sizeof( szFrom), szFrom))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); bFlmLocked = TRUE; if (!f_stricmp( szFrom, "FileHashTbl")) { // Get the hash bucket index if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Bucket", sizeof( szBucket), szBucket))) { goto Exit; } uiBucket = f_atoud( szBucket); pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket; } else if ( (f_stricmp( szFrom, "SCacheBlock") == 0) || (f_stricmp( szFrom, "RCache") == 0) || (f_stricmp( szFrom, "FDB") == 0)) { // Get the FFile address and the Hash Bucket if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Bucket", sizeof( szBucket), szBucket))) { goto Exit; } uiBucket = f_atoud( szBucket); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Address", sizeof( szAddress), szAddress))) { goto Exit; } pvAddress = (void *)f_atoud( szAddress); pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket; while (pFile && (void *)pFile != pvAddress) { pFile = pFile->pNext; } } else if (f_stricmp( szFrom, "FlmSysData") == 0) { // Get the Link and the FFile address if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Link", sizeof( szLink), szLink))) { goto Exit; } if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Address", sizeof( szAddress), szAddress))) { goto Exit; } pvAddress = (void *)f_atoud( szAddress); if (f_stricmp( szLink, "pMrnuFile") == 0) { pFile = gv_FlmSysData.pMrnuFile; // Now let's make sure we are looking at the right FFile... while (pFile && (void *)pFile != pvAddress) { pFile = pFile->pNextNUFile; } } else if (f_stricmp( szLink, "pLrnuFile") == 0) { pFile = gv_FlmSysData.pLrnuFile; // Now let's make sure we are looking at the right FFile... while (pFile && (void *)pFile != pvAddress) { pFile = pFile->pPrevNUFile; } } } else if (f_stricmp( szFrom, "FFile") == 0) { // We need to get the Link, Bucket & Address if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "Link", sizeof( szLink), szLink))) { goto Exit; } if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "Address", sizeof( szAddress), szAddress))) { goto Exit; } pvAddress = (void *)f_atoud( szAddress); if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "Bucket", sizeof( szBucket), szBucket))) { goto Exit; } uiBucket = f_atoud( szBucket); // First, let's get a reference to an FFile from the specified bucket if (gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket) { pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[uiBucket].pFirstInBucket; } // Now let's make sure we are looking at the right FFile... while (pFile && (void *)pFile != pvAddress) { pFile = pFile->pNext; } // Now what link are we supposed to follow? if (f_stricmp( szLink, "pNext") == 0) { pFile = pFile->pNext; } else if (f_stricmp( szLink, "pPrev") == 0) { pFile = pFile->pPrev; } else if (f_stricmp( szLink, "pNextNUFile") == 0) { pFile = pFile->pNextNUFile; } else if (f_stricmp( szLink, "pPrevNUFile") == 0) { pFile = pFile->pPrevNUFile; } } // Gather additional data if present. Initialize the structure before // using it. f_memset( &DataStruct, 0, sizeof(DataStruct)); if (pFile) { f_memcpy( &localFFile, pFile, sizeof(localFFile)); if (pFile->pSCacheList) { DataStruct.SCacheBlkAddress = pFile->pSCacheList->uiBlkAddress; DataStruct.SCacheLowTransID = scaGetLowTransID( pFile->pSCacheList), DataStruct.SCacheHighTransID = pFile->pSCacheList->uiHighTransID; } if (pFile->pPendingWriteList) { DataStruct.PendingWriteBlkAddress = pFile->pPendingWriteList->uiBlkAddress; DataStruct.PendingWriteLowTransID = scaGetLowTransID( pFile->pPendingWriteList), DataStruct.PendingWriteHighTransID = pFile->pPendingWriteList->uiHighTransID; } if (pFile->pLastDirtyBlk) { DataStruct.LastDirtyBlkAddress = pFile->pLastDirtyBlk->uiBlkAddress; DataStruct.LastDirtyLowTransID = scaGetLowTransID( pFile->pLastDirtyBlk), DataStruct.LastDirtyHighTransID = pFile->pLastDirtyBlk->uiHighTransID; } if (pFile->pFirstRecord) { DataStruct.FirstRecordContainer = pFile->pFirstRecord->uiContainer; DataStruct.FirstRecordDrn = pFile->pFirstRecord->uiDrn; DataStruct.FirstRecordLowTransId = pFile->pFirstRecord->uiLowTransId; } if (pFile->pLastRecord) { DataStruct.LastRecordContainer = pFile->pLastRecord->uiContainer; DataStruct.LastRecordDrn = pFile->pLastRecord->uiDrn; DataStruct.LastRecordLowTransId = pFile->pLastRecord->uiLowTransId; } } f_mutexUnlock( gv_FlmSysData.hShareMutex); bFlmLocked = FALSE; stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); // Determine if we are being requested to refresh this page or not. if ((bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh")) == TRUE) { // Send back the page with a refresh command in the header f_sprintf( (char *)pszTemp, "%s/FFile?Refresh&From=%s&Bucket=%s", m_pszURLString, szFrom, szBucket); fnPrintf( m_pHRequest, "" "" "FFile Structure\n", pszTemp); } else { fnPrintf( m_pHRequest, "FFile Structure\n"); } printStyle(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); // If we are not to refresh this page, then don't include the // refresh meta command if (!bRefresh) { f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString, szFrom, szBucket); } else { f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString, szFrom, szBucket); } // Prepare the refresh link. f_sprintf( (char *)pszTemp1, "Refresh", m_pszURLString, szFrom, szBucket); // Show the table headings and the refresh option. if (pFile) { // Write out the table headings printTableStart( "FFile Structure", 4, 100); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "%s, ", pszTemp1); fnPrintf( m_pHRequest, "%s\n", pszTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings. printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); write_data( (pFile ? &localFFile: NULL), (void *)pFile, &DataStruct); } else { // Write out an error page... fnPrintf( m_pHRequest, "

Unable to find the FFile structure that you requested." " This is probably because the state of the cache changed between " "the time that you displayed the previous page and the time that you " "clicked on the link that brought you here.\n" "

Click on your browser's \"Back\" button, then click \"Reload\" " "and then try the link again.\n"); } fnPrintf( m_pHRequest, "\n"); fnEmit(); Exit: if (bFlmLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bFlmLocked = FALSE; } if (pszTemp) { f_free( &pszTemp); } if (pszTemp1) { f_free( &pszTemp1); } return( rc); } /**************************************************************************** Desc: This procedure generates the HTML page to display the contents of the FFile structure ****************************************************************************/ void F_FFilePage::write_data( FFILE * pFile, void * pvFFileAddress, DATASTRUCT * pDataStruct) { char szFormattedTime[13]; char szTemp[100]; char szAddress[20]; char szFFileAddress[20]; FLMBOOL bHighlight = FALSE; F_UNREFERENCED_PARM( fnPrintf); if (pFile == NULL) { flmAssert(0); return; } else { printAddress( pvFFileAddress, szFFileAddress); // pNext if ( pFile->pNext) { f_sprintf( (char *)szTemp, "%s/FFile?From=FFile?Link=pNext?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pNext", "FFILE *", (void *)pFile, (void *)&pFile->pNext, (void *)pFile->pNext, (char *)szTemp, (bHighlight = ~bHighlight)); // pPrev - previous file in hash bucket. if (pFile->pPrev) { f_sprintf( (char *)szTemp, "%s/FFile?From=FFile?Link=pPrev?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pPrev", "FFILE *", (void *)pFile, (void *)&pFile->pPrev, (void *)pFile->pPrev, (char *)szTemp, (bHighlight = ~bHighlight)); // uiZeroUseCountTime - Time Use Count went to zero FormatTime(pFile->uiZeroUseCountTime, szFormattedTime); printHTMLString( "uiZeroUseCountTime", "FLMUINT", (void *)pFile, (void *)&pFile->uiZeroUseCountTime, (char *)szFormattedTime, (bHighlight = ~bHighlight)); // uiInternalUseCount - Internal Use Count printHTMLUint( "uiInternalUseCount", "FLMUINT", (void *)pFile, (void *)&pFile->uiInternalUseCount, pFile->uiInternalUseCount, (bHighlight = ~bHighlight)); // uiUseCount - Current Use Count printHTMLUint( "uiUseCount", "FLMUINT", (void *)pFile, (void *)&pFile->uiUseCount, pFile->uiUseCount, (bHighlight = ~bHighlight)); // pFirstDb if (pFile->pFirstDb) { char szFDBAddr[20]; printAddress( pFile->pFirstDb, szAddress); f_sprintf( szFDBAddr, "%s", szAddress); f_sprintf( (char *)szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket, szFDBAddr); } printHTMLLink( "pFirstDb", "FDB *", (void *)pFile, (void *)&pFile->pFirstDb, (void *)pFile->pFirstDb, (char *)szTemp, (bHighlight = ~bHighlight)); // pszDbPath - Database File Name printHTMLString( "pszDbPath", "FLMBYTE *", (void *)pFile, (void *)&pFile->pszDbPath, (char *)(pFile->pszDbPath ? (char *)pFile->pszDbPath : "Null"), (bHighlight = ~bHighlight)); // pszDataDir printHTMLString( "pszDataDir", "FLMBYTE *", (void *)pFile, (void *)&pFile->pszDataDir, (char *)(pFile->pszDataDir ? (char *)pFile->pszDataDir : "Null"), (bHighlight = ~bHighlight)); // pNextNUFile - Next Not Used File if (pFile->pNextNUFile) { f_sprintf( (char *)szTemp, "%s/FFile?From=FFile?Link=pNextNUFile?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pNextNUFile", "FFILE *", (void *)pFile, (void *)&pFile->pNextNUFile, (void *)pFile->pNextNUFile, (char *)szTemp, (bHighlight = ~bHighlight)); // pPrevNUFile - Previous Not Used File if (pFile->pPrevNUFile) { f_sprintf( (char *)szTemp, "%s/FFile?From=FFile?Link=pPrevNUFile?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pPrevNUFile", "FFILE *", (void *)pFile, (void *)&pFile->pPrevNUFile, (void *)pFile->pPrevNUFile, (char *)szTemp, (bHighlight = ~bHighlight)); // pSCacheList - Shared Cache Blocks if (pFile->pSCacheList) { f_sprintf( (char *)szTemp, "%s/SCacheBlock?" "BlockAddress=%ld&File=%s&LowTransID=%ld&HighTransID=%ld", m_pszURLString, pDataStruct->SCacheBlkAddress, szFFileAddress, pDataStruct->SCacheLowTransID, pDataStruct->SCacheHighTransID); } printHTMLLink( "pSCacheList", "SCACHE *", (void *)pFile, (void *)&pFile->pSCacheList, (void *)pFile->pSCacheList, (char *)szTemp, (bHighlight = ~bHighlight)); // pPendingWriteList if (pFile->pPendingWriteList) { f_sprintf( (char *)szTemp, "%s/SCacheBlock?" "BlockAddress=%ld&File=%s&LowTransID=%ld&HighTransID=%ld", m_pszURLString, pDataStruct->PendingWriteBlkAddress, szFFileAddress, pDataStruct->PendingWriteLowTransID, pDataStruct->PendingWriteHighTransID); } printHTMLLink( "pPendingWriteList", "SCACHE *", (void *)pFile, (void *)&pFile->pPendingWriteList, (void *)pFile->pPendingWriteList, (char *)szTemp, (bHighlight = ~bHighlight)); // pLastDirtyBlk if (pFile->pLastDirtyBlk) { f_sprintf( (char *)szTemp, "%s/SCacheBlock?" "BlockAddress=%ld&File=%s&LowTransID=%ld&HighTransID=%ld", m_pszURLString, pDataStruct->LastDirtyBlkAddress, szFFileAddress, pDataStruct->LastDirtyLowTransID, pDataStruct->LastDirtyHighTransID); } printHTMLLink( "pLastDirtyBlk", "SCACHE *", (void *)pFile, (void *)&pFile->pLastDirtyBlk, (void *)pFile->pLastDirtyBlk, (char *)szTemp, (bHighlight = ~bHighlight)); // uiDirtyCacheCount printHTMLUint( "uiDirtyCacheCount", "FLMUINT", (void *)pFile, (void *)&pFile->uiDirtyCacheCount, pFile->uiDirtyCacheCount, (bHighlight = ~bHighlight)); // uiLogCacheCount printHTMLUint( "uiLogCacheCount", "FLMUINT", (void *)pFile, (void *)&pFile->uiLogCacheCount, pFile->uiLogCacheCount, (bHighlight = ~bHighlight)); // pFirstRecord - First Record Cache Block // **Do we need to rework this by passing in the Container, Drn, pFile & LowTransId? ** // if (pFile->pFirstRecord) { f_sprintf( (char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, pDataStruct->FirstRecordContainer, pDataStruct->FirstRecordDrn, szFFileAddress, pDataStruct->FirstRecordLowTransId); } printHTMLLink( "pFirstRecord", "RCACHE_p", (void *)pFile, (void *)&pFile->pFirstRecord, (void *)pFile->pFirstRecord, (char *)szTemp, (bHighlight = ~bHighlight)); // pLastRecord - Last Record Cache Block if (pFile->pLastRecord) { f_sprintf( (char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, pDataStruct->LastRecordContainer, pDataStruct->LastRecordDrn, szFFileAddress, pDataStruct->LastRecordLowTransId); } printHTMLLink( "pLastRecord", "RCACHE_p", (void *)pFile, (void *)&pFile->pLastRecord, (void *)pFile->pLastRecord, (char *)szTemp, (bHighlight = ~bHighlight)); // ppBlocksDone - List of blocks to be written to the Rollback if (pFile->ppBlocksDone) { f_sprintf( (char *)szTemp, "%s/SCache?From=FFile?Link=ppBlocksDone?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "ppBlocksDone", "SCACHE **", (void *)pFile, (void *)&pFile->ppBlocksDone, (void *)pFile->ppBlocksDone, (char *)szTemp, (bHighlight = ~bHighlight)); // uiBlocksDoneArraySize printHTMLUint( "uiBlocksDoneArraySize", "FLMUINT", (void *)pFile, (void *)&pFile->uiBlocksDoneArraySize, pFile->uiBlocksDoneArraySize, (bHighlight = ~bHighlight)); // uiBlocksDone - Number of Blocks in Blocks Done Array printHTMLUint( "uiBlocksDone", "FLMUINT", (void *)pFile, (void *)&pFile->uiBlocksDone, pFile->uiBlocksDone, (bHighlight = ~bHighlight)); // pTransLogList - Shared Cache Log List if (pFile->pTransLogList != NULL) { f_sprintf( (char *)szTemp, "%s/SCache?From=FFile?Link=pTransLogList?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pTransLogList", "SCACHE *", (void *)pFile, (void *)&pFile->pTransLogList, (void *)pFile->pTransLogList, (char *)szTemp, (bHighlight = ~bHighlight)); // pOpenNotifies - Open Notifies Threads if (pFile->pOpenNotifies != NULL) { f_sprintf( (char *)szTemp, "%s/FNOTIFY?From=FFile?Link=pOpenNotifies?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pOpenNotifies", "FNOTIFY *", (void *)pFile, (void *)&pFile->pOpenNotifies, (void *)pFile->pOpenNotifies, (char *)szTemp, (bHighlight = ~bHighlight)); // pCloseNotifies if (pFile->pCloseNotifies != NULL) { f_sprintf( (char *)szTemp, "%s/FNOTIFY?From=FFile?Link=pCloseNotifies?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pCloseNotifies", "FNOTIFY *", (void *)pFile, (void *)&pFile->pCloseNotifies, (void *)pFile->pCloseNotifies, (char *)szTemp, (bHighlight = ~bHighlight)); // pDictList - Dictionaries List if (pFile->pDictList != NULL) { f_sprintf( (char *)szTemp, "%s/FDICT?From=FFile?Link=pDictList?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pDictList", "FDICT *", (void *)pFile, (void *)&pFile->pDictList, (void *)pFile->pDictList, (char *)szTemp, (bHighlight = ~bHighlight)); // krefPool - Kref pool printAddress( &pFile->krefPool, szAddress); printHTMLString( "krefPool", "POOL", (void *)pFile, (void *)&pFile->krefPool, (char *)szAddress, (bHighlight = ~bHighlight)); // FileHdr - File Header f_sprintf( (char *)szTemp, "%s/FILE_HDR?From=FFile?Link=FileHdr?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); printHTMLLink( "FileHdr", "FILE_HDR", (void *)pFile, (void *)&pFile->FileHdr, (void *)&pFile->FileHdr, (char *)szTemp, (bHighlight = ~bHighlight)); // uiMaxFileSize - Maximum File Size printHTMLUint( "uiMaxFileSize", "FLMUINT", (void *)pFile, (void *)&pFile->uiMaxFileSize, pFile->uiMaxFileSize, (bHighlight = ~bHighlight)); // uiFileExtendSize - File Extend Size printHTMLUint( "uiFileExtendSize", "FLMUINT", (void *)pFile, (void *)&pFile->uiFileExtendSize, pFile->uiFileExtendSize, (bHighlight = ~bHighlight)); // uiUpdateTransID - Update Transaction Id printHTMLUint( "uiUpdateTransID", "FLMUINT", (void *)pFile, (void *)&pFile->uiUpdateTransID, pFile->uiUpdateTransID, (bHighlight = ~bHighlight)); // pRfl - Roll Forward Log Object if (pFile->pRfl) { f_sprintf( (char *)szTemp, "%s/Rfl?From=FFile?Link=pRfl?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pRfl", "F_Rfl *", (void *)pFile, (void *)&pFile->pRfl, (void *)pFile->pRfl, (char *)szTemp, (bHighlight = ~bHighlight)); // ucLastCommittedLogHdr - Last Committed Log Header f_sprintf( (char *)szTemp, "%s/LogHdr?From=FFile?" "Link=ucLastCommittedLogHdr?" "Address=%s?Bucket=%ld", m_pszURLString, szFFileAddress, pFile->uiBucket); printHTMLLink( "ucLastCommittedLogHdr", "FLMBYTE", (void *)pFile, (void *)&pFile->ucLastCommittedLogHdr[0], (void *)&pFile->ucLastCommittedLogHdr[0], (char *)szTemp, (bHighlight = ~bHighlight)); // ucCheckpointLogHdr - Checkpoint Log Header f_sprintf( (char *)szTemp, "%s/LogHdr?From=FFile?" "Link=ucCheckpointLogHdr?" "Address=%s?Bucket=%ld", m_pszURLString, szFFileAddress, pFile->uiBucket); printHTMLLink( "ucCheckpointLogHdr", "FLMBYTE", (void *)pFile, (void *)&pFile->ucCheckpointLogHdr[0], (void *)&pFile->ucCheckpointLogHdr[0], (char *)szTemp, (bHighlight = ~bHighlight)); // ucUncommittedLogHdr - Uncommitted Log Header f_sprintf( (char *)szTemp, "%s/LogHdr?From=FFile?Link=ucUncommittedLogHdr?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); printHTMLLink( "ucUncommittedLogHdr", "FLMBYTE", (void *)pFile, (void *)&pFile->ucUncommittedLogHdr[0], (void *)&pFile->ucUncommittedLogHdr[0], (char *)szTemp, (bHighlight = ~bHighlight)); // pBufferMgr f_sprintf( (char *)szTemp, "%s/F_IOBufferMgr?From=FFile?" "Link=pBufferMgr?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); printHTMLLink( "pBufferMgr", "F_IOBufferMgr *", (void *)pFile, (void *)&pFile->pBufferMgr, (void *)pFile->pBufferMgr, (char *)szTemp, (bHighlight = ~bHighlight)); // pCurrLogBuffer f_sprintf( (char *)szTemp, "%s/F_IOBuffer?From=FFile?" "Link=pCurrLogBuffer?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); printHTMLLink( "pCurrLogBuffer", "F_IOBuffer *", (void *)pFile, (void *)&pFile->pCurrLogBuffer, (void *)pFile->pCurrLogBuffer, (char *)szTemp, (bHighlight = ~bHighlight)); // uiCurrLogWriteOffset printHTMLUint( "uiCurrLogWriteOffset", "FLMUINT", (void *)pFile, (void *)&pFile->uiCurrLogWriteOffset, pFile->uiCurrLogWriteOffset, (bHighlight = ~bHighlight)); // uiCurrLogBlkAddr printHTMLUint( "uiCurrLogBlkAddr", "FLMUINT", (void *)pFile, (void *)&pFile->uiCurrLogBlkAddr, pFile->uiCurrLogBlkAddr, (bHighlight = ~bHighlight)); // pFileLockObj - File Locking Object if (pFile->pFileLockObj) { f_sprintf( (char *)szTemp, "%s/ServerLockObject?From=FFile?" "Link=pFileLockObj?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pFileLockObj", "ServerLockObject_p", (void *)pFile, (void *)&pFile->pFileLockObj, (void *)pFile->pFileLockObj, (char *)szTemp, (bHighlight = ~bHighlight)); // pWriteLockObj if (pFile->pWriteLockObj) { f_sprintf( (char *)szTemp, "%s/ServerLockObject?From=FFile?" "Link=pWriteLockObj?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pWriteLockObj", "ServerLockObject_p", (void *)pFile, (void *)&pFile->pWriteLockObj, (void *)pFile->pWriteLockObj, (char *)szTemp, (bHighlight = ~bHighlight)); // pLockFileHdl - File Lock Handle (3.x Db) if (pFile->pLockFileHdl) { f_sprintf( (char *)szTemp, "%s/F_FileHdl?From=FFile?" "Link=pLockFileHdl?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pLockFileHdl", "F_FileHdl_p", (void *)pFile, (void *)&pFile->pLockFileHdl, (void *)pFile->pLockFileHdl, (char *)szTemp, (bHighlight = ~bHighlight)); // pLockNotifies - Notifies List if (pFile->pLockNotifies) { f_sprintf( (char *)szTemp, "%s/FNOTIFY?From=FFile?" "Link=pLockNotifies?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pLockNotifies", "FNOTIFY *", (void *)pFile, (void *)&pFile->pLockNotifies, (void *)pFile->pLockNotifies, (char *)szTemp, (bHighlight = ~bHighlight)); // bBeingLocked - File being locked printHTMLString( "bBeingLocked", "FLMBOOL", (void *)pFile, (void *)&pFile->bBeingLocked, (char *)(pFile->bBeingLocked ? "Yes" : "No"), (bHighlight = ~bHighlight)); // pFirstReadTrans - First Read Transaction if (pFile->pFirstReadTrans) { char szFDBAddr[20]; printAddress( pFile->pFirstReadTrans, szAddress); f_sprintf( szFDBAddr, "%s", szAddress); f_sprintf( (char *)szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket, szFDBAddr); } printHTMLLink( "pFirstReadTrans", "FDB *", (void *)pFile, (void *)&pFile->pFirstReadTrans, (void *)pFile->pFirstReadTrans, (char *)szTemp, (bHighlight = ~bHighlight)); // pLastReadTrans - Last Read Transaction if (pFile->pLastReadTrans) { char szFDBAddr[20]; printAddress( pFile->pLastReadTrans, szAddress); f_sprintf( szFDBAddr, "%s", szAddress); f_sprintf( (char *)szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket, szFDBAddr); } printHTMLLink( "pLastReadTrans", "FDB *", (void *)pFile, (void *)&pFile->pLastReadTrans, (void *)pFile->pLastReadTrans, (char *)szTemp, (bHighlight = ~bHighlight)); // pFirstKilledTrans - First Killed Transaction if (pFile->pFirstKilledTrans) { char szFDBAddr[20]; printAddress( pFile->pFirstKilledTrans, szAddress); f_sprintf( szFDBAddr, "%s", szAddress); f_sprintf( (char *)szTemp, "%s/FDB?FFileAddress=%s?Bucket=%lu?FDBAddress=%s", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket, szFDBAddr); } printHTMLLink( "pFirstKilledTrans", "FDB *", (void *)pFile, (void *)&pFile->pFirstKilledTrans, (void *)pFile->pFirstKilledTrans, (char *)szTemp, (bHighlight = ~bHighlight)); // uiFirstLogBlkAddress printHTMLUint( "uiFirstLogBlkAddress", "FLMUINT", (void *)pFile, (void *)&pFile->uiFirstLogBlkAddress, pFile->uiFirstLogBlkAddress, (bHighlight = ~bHighlight)); // uiFirstLogCPBlkAddress - First Log Checkpoint Block Address printHTMLUint( "uiFirstLogCPBlkAddress", "FLMUINT", (void *)pFile, (void *)&pFile->uiFirstLogCPBlkAddress, pFile->uiFirstLogCPBlkAddress, (bHighlight = ~bHighlight)); // uiLastCheckpointTime - Last Checkpoint Time FormatTime( pFile->uiLastCheckpointTime, szFormattedTime); printHTMLString( "uiLastCheckpointTime", "FLMUINT", (void *)pFile, (void *)&pFile->uiLastCheckpointTime, (char *)szFormattedTime, (bHighlight = ~bHighlight)); // pCPThrd if (pFile->pCPThrd) { f_sprintf( (char *)szTemp, "%s/F_Thread?From=FFile?" "Link=pCPThrd?" "Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pCPThrd", "F_Thread *", (void *)pFile, (void *)&pFile->pCPThrd, (void *)pFile->pCPThrd, (char *)szTemp, (bHighlight = ~bHighlight)); // pCPInfo - Checkpoint Info Buffer if (pFile->pCPInfo) { f_sprintf( (char *)szTemp, "%s/CP_INFO?From=FFile?Link=pCPInfo?Address=%s?Bucket=%lu", m_pszURLString, szFFileAddress, (unsigned long)pFile->uiBucket); } printHTMLLink( "pCPInfo", "CP_INFO_p", (void *)pFile, (void *)&pFile->pCPInfo, (void *)pFile->pCPInfo, (char *)szTemp, (bHighlight = ~bHighlight)); // CheckpointRc - Last Checkpoint Return Code printHTMLUint( "CheckpointRc", "FLMUINT", (void *)pFile, (void *)&pFile->CheckpointRc, pFile->CheckpointRc, (bHighlight = ~bHighlight)); // uiBucket - Hash Table Bucket printHTMLUint( "uiBucket", "FLMUINT", (void *)pFile, (void *)&pFile->uiBucket, pFile->uiBucket, (bHighlight = ~bHighlight)); // uiFlags - Flags if (pFile->uiFlags) { FLMBOOL bTest = FALSE; char * pTemp = (char *)szTemp; f_sprintf( (char *)szTemp, "%08X
", (unsigned)pFile->uiFlags); pTemp += 12; if (pFile->uiFlags & DBF_BEING_OPENED) { f_sprintf(pTemp, "Being Opened"); pTemp += f_strlen("Being Opened"); bTest=TRUE; } if (pFile->uiFlags & DBF_IN_NU_LIST) { if (bTest) { f_sprintf(pTemp, "
"); pTemp += f_strlen("
"); } f_sprintf(pTemp, "In Not Used List"); pTemp += f_strlen("In Not Used List"); bTest = TRUE; } if (pFile->uiFlags & DBF_BEING_CLOSED) { if (bTest) { f_sprintf(pTemp, "
"); pTemp += f_strlen("
"); } f_sprintf(pTemp, "Being Closed"); pTemp += f_strlen("Being Closed"); } } else { f_sprintf( (char *)szTemp, "%08X
Normal", (unsigned)pFile->uiFlags); } printHTMLString( "uiFlags", "FLMUINT", (void *)pFile, (void *)&pFile->uiFlags, (char *)szTemp, (bHighlight = ~bHighlight)); // bBackupActive - Backup Active printHTMLString( "bBackupActive", "FLMBOOL", (void *)pFile, (void *)&pFile->bBackupActive, (char *)(pFile->bBackupActive ? "Yes" : "No"), (bHighlight = ~bHighlight)); printTableEnd(); } } libflaim-4.9.966/src/fscounts.cpp0000644000175000017500000006626610510774540020244 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for calculating estimated costs for query optimization. // Tabs: 3 // // Copyright (c) 2000-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscounts.cpp 12320 2006-01-19 15:53:51 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" extern FLMBYTE SENLenArray[]; /*************************************************************************** Desc: Compute the number of blocks between two stack positions. These values may be estimated or actual. *****************************************************************************/ RCODE FSComputeRecordBlocks( // Returns WERR_OK or FERR_BTREE_ERROR BTSK * pFromStack, // [in] - be carefull not to change // anything in this structure. BTSK * pUntilStack, // [in] FLMUINT * puiLeafBlocksBetween, // [out] blocks between the stacks FLMUINT * puiTotalRecords, // [out] FLMBOOL * pbTotalsEstimated)// [out] Set to TRUE when estimating. { RCODE rc = FERR_OK; FLMUINT uiTotalRecords, uiTempRecordCount, uiEstRecordCount; FLMUINT uiTotalBlocksBetween, uiEstBlocksBetween; FLMBYTE * pBlk; uiTotalBlocksBetween = 0; *pbTotalsEstimated = FALSE; // Are the from and until positions in the same block? if( pFromStack->uiBlkAddr == pUntilStack->uiBlkAddr) { rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pUntilStack->uiCurElm, &uiTotalRecords, NULL, NULL); goto Exit; } // Gather the counts in the from and until leaf blocks. if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pFromStack->uiBlkEnd, &uiTotalRecords, NULL, NULL))) goto Exit; if( RC_BAD( rc = FSBlockCounts( pUntilStack, BH_OVHD, pUntilStack->uiCurElm, &uiTempRecordCount, NULL, NULL))) goto Exit; uiTotalRecords += uiTempRecordCount; // Do the obvious check to see if the blocks are neighbors. pBlk = BLK_ELM_ADDR( pFromStack, BH_NEXT_BLK ); if( FB2UD( pBlk ) == pUntilStack->uiBlkAddr) { goto Exit; } // Get (or estimate) the number of elements in the parent block. *pbTotalsEstimated = TRUE; if( RC_BAD( rc = FSBlockCounts( pFromStack, BH_OVHD, pFromStack->uiBlkEnd, &uiEstRecordCount, NULL, NULL))) goto Exit; uiEstBlocksBetween = 1; for(;;) { FLMUINT uiElementCount; FLMUINT uiTempElementCount; FLMUINT uiEstElementCount; // Go up a b-tree level and check out how far apart the elements are. pFromStack--; pUntilStack--; // Share the same block? if( pFromStack->uiBlkAddr == pUntilStack->uiBlkAddr) { if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pUntilStack->uiCurElm, NULL, &uiElementCount, NULL))) goto Exit; // Don't count the pFromStack current element. uiElementCount--; uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalRecords += uiEstRecordCount * uiElementCount; goto Exit; } // Gather the counts in the from and until non-leaf blocks. if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pFromStack->uiBlkEnd, NULL, &uiElementCount, NULL))) goto Exit; // Don't count the first element. uiElementCount--; if( RC_BAD( rc = FSBlockCounts( pUntilStack, BH_OVHD, pUntilStack->uiCurElm, NULL, &uiTempElementCount, NULL))) goto Exit; uiElementCount += uiTempElementCount; uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalRecords += uiEstRecordCount * uiElementCount; // Do the obvious check to see if the blocks are neighbors. pBlk = BLK_ELM_ADDR( pFromStack, BH_NEXT_BLK ); if( FB2UD( pBlk ) == pUntilStack->uiBlkAddr) { goto Exit; } // Recompute the estimated element count on every b-tree level // because the compression is better the lower in the b-tree we go. if( RC_BAD( rc = FSBlockCounts( pFromStack, BH_OVHD, pFromStack->uiBlkEnd, NULL, &uiEstElementCount, NULL))) goto Exit; // Adjust the estimated key/ref count to be the counts from a complete // (not partial) block starting at this level going to the leaf. uiEstRecordCount *= uiEstElementCount; uiEstBlocksBetween *= uiEstElementCount; } Exit: if( puiTotalRecords) { // Always include the UNTIL record. *puiTotalRecords = uiTotalRecords + 1; } if( puiLeafBlocksBetween) { *puiLeafBlocksBetween = uiTotalBlocksBetween; } return( rc); } /*************************************************************************** Desc: Compute the key, element, reference and block counts between two stack positions. These values may be estimated or actual. Notes: There are two versions for this routine in the way of estimating how many keys and references there are in the unknown blocks between the from stack and the until stack. The first version is implemented. The first version will estimate using the average number of keys and references in the pFromStack blocks. The second version will estimate using the average number of keys and references using pre-parsed stats gathered for the index in the LFILE. *****************************************************************************/ RCODE FSComputeIndexCounts( // Returns WERR_OK or FERR_BTREE_ERROR BTSK * pFromStack, // [in] - be carefull not to change // anything in this structure. BTSK * pUntilStack, // [in] FLMUINT * puiLeafBlocksBetween, // [out] blocks between the stacks FLMUINT * puiTotalKeys, // [out] total number of keys inclusive FLMUINT * puiTotalRefs, // [out] total references inclusive FLMBOOL * pbTotalsEstimated) // [out] Set to TRUE when estimating. { RCODE rc = FERR_OK; FLMUINT uiTotalKeys; FLMUINT uiTempKeyCount; FLMUINT uiEstKeyCount; FLMUINT uiTotalRefs; FLMUINT uiTempRefCount; FLMUINT uiEstRefCount; FLMUINT uiTotalBlocksBetween; FLMUINT uiEstBlocksBetween; FLMBYTE * pBlk; uiTotalBlocksBetween = uiTotalKeys = uiTotalRefs = 0; *pbTotalsEstimated = FALSE; // Are the from and until positions in the same block? if( pFromStack->uiBlkAddr == pUntilStack->uiBlkAddr) { rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pUntilStack->uiCurElm, &uiTotalKeys, NULL, &uiTotalRefs); goto Exit; } // Gather the counts in the from and until leaf blocks. if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pFromStack->uiBlkEnd, &uiTotalKeys, NULL, &uiTotalRefs))) goto Exit; if( RC_BAD( rc = FSBlockCounts( pUntilStack, BH_OVHD, pUntilStack->uiCurElm, &uiTempKeyCount, NULL, &uiTempRefCount))) goto Exit; uiTotalKeys += uiTempKeyCount; uiTotalRefs += uiTempRefCount; // Do the obvious check to see if the blocks are neighbors. pBlk = BLK_ELM_ADDR( pFromStack, BH_NEXT_BLK ); if( FB2UD( pBlk ) == pUntilStack->uiBlkAddr) { goto Exit; } // Estimate number of keys/refs in the leaf block. // Estimate using just the left block. The right block may be a right-most // block so will skew the results. // // Code for non-leaf child counts is easy - no need to estimate. if( (pFromStack-1)->uiBlkType != BHT_NON_LEAF_COUNTS) { *pbTotalsEstimated = TRUE; if( RC_BAD( rc = FSBlockCounts( pFromStack, BH_OVHD, pFromStack->uiBlkEnd, &uiEstKeyCount, NULL, &uiEstRefCount))) { goto Exit; } } uiEstBlocksBetween = 1; for(;;) { FLMUINT uiElementCount; FLMUINT uiTempElementCount; FLMUINT uiEstElementCount; FLMUINT uiRefCount; FLMBYTE * pCounts; // Go up a b-tree level and check out how far apart the elements are. pFromStack--; pUntilStack--; // Share the same block? if( pFromStack->uiBlkAddr == pUntilStack->uiBlkAddr) { if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pUntilStack->uiCurElm, NULL, &uiElementCount, &uiRefCount))) { goto Exit; } // Don't count the current element nor the ref counts. uiElementCount--; if( pFromStack->uiBlkType == BHT_NON_LEAF_COUNTS) { pCounts = pFromStack->pBlk + pFromStack->uiCurElm + BNE_CHILD_COUNT; uiRefCount -= FB2UD( pCounts); uiTotalRefs += uiRefCount; uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalKeys += uiEstKeyCount * uiElementCount; if( ((uiEstBlocksBetween != 1) && puiLeafBlocksBetween) || puiTotalKeys) { *pbTotalsEstimated = TRUE; } } else { uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalKeys += uiEstKeyCount * uiElementCount; uiTotalRefs += uiEstRefCount * uiElementCount; } goto Exit; } // Gather the counts in the from and until non-leaf blocks. if( RC_BAD( rc = FSBlockCounts( pFromStack, pFromStack->uiCurElm, pFromStack->uiBlkEnd, NULL, &uiElementCount, &uiRefCount))) { goto Exit; } // Don't count the first element. uiElementCount--; if( pFromStack->uiBlkType == BHT_NON_LEAF_COUNTS) { pCounts = pFromStack->pBlk + pFromStack->uiCurElm + BNE_CHILD_COUNT; uiRefCount -= FB2UD( pCounts); uiTotalRefs += uiRefCount; } if( RC_BAD( rc = FSBlockCounts( pUntilStack, BH_OVHD, pUntilStack->uiCurElm, NULL, &uiTempElementCount, &uiRefCount))) { goto Exit; } uiElementCount += uiTempElementCount; uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalKeys += uiEstKeyCount * uiElementCount; if( pUntilStack->uiBlkType == BHT_NON_LEAF_COUNTS) { uiTotalRefs += uiRefCount; if( puiLeafBlocksBetween || puiTotalKeys) { *pbTotalsEstimated = TRUE; } } else { uiTotalRefs += uiEstRefCount * uiElementCount; } // Do the obvious check to see if the blocks are neighbors. pBlk = BLK_ELM_ADDR( pFromStack, BH_NEXT_BLK ); if( FB2UD( pBlk ) == pUntilStack->uiBlkAddr) { goto Exit; } // We recompute the estimated element count on every b-tree level // because the compression is better the lower in the b-tree we go. if( RC_BAD( rc = FSBlockCounts( pFromStack, BH_OVHD, pFromStack->uiBlkEnd, NULL, &uiEstElementCount, NULL))) { goto Exit; } // Adjust the estimated key/ref count to be the counts from a complete // (not partial) block starting at this level going to the leaf. uiEstKeyCount *= uiEstElementCount; uiEstRefCount *= uiEstElementCount; uiEstBlocksBetween *= uiEstElementCount; } Exit: if( puiLeafBlocksBetween) { *puiLeafBlocksBetween = uiTotalBlocksBetween; } if( puiTotalKeys) { *puiTotalKeys = uiTotalKeys; } if( puiTotalRefs) { *puiTotalRefs = uiTotalRefs; } return( rc); } /*************************************************************************** Desc: Returns the number of first keys (elements with the first flag), elements and references (for leaf blocks). *****************************************************************************/ RCODE FSBlockCounts( // Returns WERR_OK currently. BTSK * pStack, // [in] - be careful not to change // anything in this structure. FLMUINT uiFirstElement, // [in] start at this element FLMUINT uiLastElement, // [in] Do not include reference counts // from this element. FLMUINT * puiFirstKeyCount, // [out] first key count or NULL FLMUINT * puiElementCount, // [out] element count or NULL FLMUINT * puiRefCount) // [out] reference count or NULL { RCODE rc = FERR_OK; FLMUINT uiFirstKeyCount; FLMUINT uiElementCount; FLMUINT uiRefCount; FLMBYTE * pBlk; FLMBYTE * pCounts; FLMBOOL bHaveNonleafElementCounts; BTSK tempStack; flmAssert( uiFirstElement <= uiLastElement); flmAssert( uiLastElement <= pStack->uiBlkEnd); uiFirstKeyCount = uiElementCount = uiRefCount = 0; // Set up the temporary stack so that the input stack doesn't get changed. tempStack.pBlk = pBlk = pStack->pBlk; tempStack.pSCache = pStack->pSCache; tempStack.uiBlkAddr = pStack->uiBlkAddr; FSBlkToStack( &tempStack); bHaveNonleafElementCounts = (tempStack.uiBlkType == BHT_NON_LEAF_COUNTS) ? TRUE : FALSE; // Position to uiFirstElement (it could be bogus). tempStack.uiCurElm = uiFirstElement; // Loop gathering the statistics. while( tempStack.uiCurElm < uiLastElement) { uiElementCount++; if( puiFirstKeyCount) { if( pBlk[ tempStack.uiCurElm ] & BBE_FIRST_FLAG) { uiFirstKeyCount++; } } if( puiRefCount) { if( !bHaveNonleafElementCounts) { uiRefCount += FSElementRefCount( &tempStack); } else { pCounts = pBlk + tempStack.uiCurElm + BNE_CHILD_COUNT; uiRefCount += FB2UD( pCounts); } } // Next element. if( FSBlkNextElm( &tempStack) == FERR_BT_END_OF_DATA) { break; } } if( puiFirstKeyCount) { *puiFirstKeyCount = uiFirstKeyCount; } if( puiElementCount) { *puiElementCount = uiElementCount; } if( puiRefCount) { *puiRefCount = uiRefCount; } return( rc); } /*************************************************************************** Desc: Returns the number of references at the current b-tree element. Leaf level blocks must be passed in and the block must be usable. *****************************************************************************/ FLMUINT FSElementRefCount( // Returns the number of references BTSK * pStack) // [in] { FLMUINT uiRefCount; FLMBYTE * pCurRef; // Points to current reference FLMBYTE * pCurElm; // Points to current element FLMUINT uiRefSize; // Size of the reference set DIN_STATE tempState; // Check block type if( pStack->uiBlkType != BHT_LEAF) { uiRefCount = 0; goto Exit; } uiRefCount = 1; // Point to the start of the current reference skipping over domain info. pCurRef = pCurElm = CURRENT_ELM( pStack ); (void) FSGetDomain( &pCurRef, pStack->uiElmOvhd ); uiRefSize = (FLMUINT)(BBE_GET_RL(pCurElm) - (pCurRef - BBE_REC_PTR(pCurElm))); RESET_DINSTATE( tempState ); // Read the first reference - there must be at least one reference. (void) DINNextVal( pCurRef, &tempState ); while( tempState.uiOffset < uiRefSize ) { FLMUINT uiNextLength; // Get the current byte to see what kind of item it is if( (uiNextLength = SENValLen( pCurRef + tempState.uiOffset)) == 0) { uiRefCount += DINOneRunVal( pCurRef, &tempState ); } else { tempState.uiOffset += uiNextLength; uiRefCount++; } } Exit: return (uiRefCount); } /*************************************************************************** Desc: Read in the child block and set the counts in the input element. Does not go up the tree updating the counts. Caller must do this. *****************************************************************************/ RCODE FSUpdateAdjacentBlkCounts( FDB * pDb, LFILE * pLFile, BTSK * pStack, BTSK * pNextBlkStk) { RCODE rc = FERR_OK; FLMUINT uiNextBlkCount; BTSK * pBaseStack = pStack; // Get the count of the next block and update up the tree. if( RC_BAD( rc = FSBlockCounts( pNextBlkStk, BH_OVHD, pNextBlkStk->uiBlkEnd, NULL, NULL, &uiNextBlkCount))) { goto Exit; } pStack = pBaseStack; pStack--; if( RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } pStack = pBaseStack; if( RC_BAD( rc = FSUpdateBlkCounts( pDb, pStack, uiNextBlkCount))) { goto Exit; } pStack = pBaseStack; pStack--; if( RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Update the block counts for a block adjusting all of the parent entry counts. *****************************************************************************/ RCODE FSUpdateBlkCounts( FDB * pDb, BTSK * pStack, FLMUINT uiNewCount) { RCODE rc = FERR_OK; FLMBYTE * pCurElm; FLMUINT uiCount; FLMINT iDelta = 0; FLMBOOL bFirstTime; // Go up the stack and update all parent block counts for the current blk. bFirstTime = TRUE; while( !BH_IS_ROOT_BLK( pStack->pBlk)) { // Go to the parent and increment/decrement the counts. pStack--; pCurElm = pStack->pBlk + pStack->uiCurElm; uiCount = FB2UD( &pCurElm[ BNE_CHILD_COUNT]); if( bFirstTime) { iDelta = uiCount - uiNewCount; bFirstTime = FALSE; // If the delta is zero there is nothing to do. if( !iDelta) { break; } } // Log the block. if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } // The block should be able to be used. uiCount = uiCount - iDelta; UD2FBA( (FLMUINT32)uiCount, &pCurElm[ BNE_CHILD_COUNT]); } Exit: return( rc); } /*************************************************************************** Desc: For a positioning index update the count in all the parent elements. Splits and joins are very complex and will take care of redoing the counts. This is why the calling code will increment/decrement the counts before the key is added/deleted. *****************************************************************************/ RCODE FSChangeCount( FDB * pDb, BTSK * pStack, FLMBOOL bAddReference) // If FALSE, decrement the reference { RCODE rc = FERR_OK; FLMBYTE * pCurElm; FLMUINT uiCount; while( !BH_IS_ROOT_BLK( pStack->pBlk)) { // Go to the parent and increment/decrement the counts. pStack--; // Log the block. if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } // The block should be able to be used. pCurElm = pStack->pBlk + pStack->uiCurElm; uiCount = FB2UD( &pCurElm[ BNE_CHILD_COUNT]); if( bAddReference) { uiCount++; } else { // Don't allow value to be less than zero. if( uiCount) { uiCount--; } } UD2FBA( (FLMUINT32)uiCount, &pCurElm[ BNE_CHILD_COUNT]); } Exit: return( rc); } /*************************************************************************** Desc: Through an insert or delete, change the block counts. *****************************************************************************/ RCODE FSChangeBlkCounts( FDB * pDb, BTSK * pStack, FLMINT iDelta) { RCODE rc = FERR_OK; FLMBYTE * pCurElm; FLMUINT uiCount; // Go up the stack and update all parent block counts for the current blk. while( !BH_IS_ROOT_BLK( pStack->pBlk)) { // Go to the parent and increment/decrement the counts. pStack--; pCurElm = pStack->pBlk + pStack->uiCurElm; uiCount = FB2UD( &pCurElm[ BNE_CHILD_COUNT]); uiCount = (((FLMINT)(uiCount + iDelta)) < 0) ? 0 : (FLMUINT) (uiCount + iDelta); // Log the block. if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } // The block should be able to be used. UD2FBA( (FLMUINT32)uiCount, &pCurElm[ BNE_CHILD_COUNT]); } Exit: return( rc); } /*************************************************************************** Desc: Given a stack, current element and a DINSTATE coupute the absolute position of the reference. Must be called with a positioning index. *****************************************************************************/ RCODE FSGetBtreeRefPosition( FDB * pDb, BTSK * pStack, DIN_STATE * pDinState, FLMUINT * puiRefPosition) { RCODE rc = FERR_OK; FLMUINT uiTotalCount; FLMUINT uiRefCount; F_UNREFERENCED_PARM( pDb); // Compute the reference counts before all the current elements up the tree. if( RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiCurElm, NULL, NULL, &uiTotalCount))) { goto Exit; } // This must be a one-based number (first reference is 1). if( !pDinState->uiOffset) { uiTotalCount++; } else { FLMBYTE * pCurRef; // Points to current reference FLMBYTE * pCurElm; // Points to current element FLMUINT uiRefSize; // Size of the reference set DIN_STATE tempState; // Compute the absolute position of this reference. uiRefCount = 2; RESET_DINSTATE( tempState ); pCurRef = pCurElm = CURRENT_ELM( pStack ); (void) FSGetDomain( &pCurRef, pStack->uiElmOvhd ); uiRefSize = (FLMUINT)(BBE_GET_RL(pCurElm) - (pCurRef - BBE_REC_PTR(pCurElm))); // Read the first reference - there must be at least one reference. (void) DINNextVal( pCurRef, &tempState ); while( tempState.uiOffset < pDinState->uiOffset && tempState.uiOffset < uiRefSize) { FLMUINT uiNextLength; // Get the current byte to see what kind of item it is if( (uiNextLength = SENValLen( pCurRef + tempState.uiOffset)) == 0) { uiRefCount += DINOneRunVal( pCurRef, &tempState ); } else { tempState.uiOffset += uiNextLength; uiRefCount++; } } if( tempState.uiOffset == pDinState->uiOffset && pDinState->uiOnes) { uiRefCount += pDinState->uiOnes; } uiTotalCount += uiRefCount; } // Go up the stack and keep the count up. while( !BH_IS_ROOT_BLK( pStack->pBlk)) { // Go to the parent and increment/decrement the counts. pStack--; if( RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiCurElm, NULL, NULL, &uiRefCount))) { goto Exit; } uiTotalCount += uiRefCount; } Exit: *puiRefPosition = uiTotalCount; return( rc); } /*************************************************************************** Desc: Given a stack and a btree position, setup the b-tree to the current element and dinstate to the selected position. Must be called with a positioning index. *****************************************************************************/ RCODE FSPositionSearch( FDB * pDb, LFILE * pLFile, FLMUINT uiRefPosition, BTSK ** ppStack, FLMUINT * puiRecordId, FLMUINT * puiDomain, DIN_STATE * pDinState) { RCODE rc; BTSK * pStack = *ppStack; FLMBYTE * pKeyBuf = pStack->pKeyBuf; FLMUINT uiBlkAddr; LFILE TmpLFile; if( RC_BAD( rc = FSGetRootBlock( pDb, &pLFile, &TmpLFile, pStack))) { if (rc == FERR_NO_ROOT_BLOCK) { flmAssert( pLFile->uiRootBlk == BT_END); rc = FERR_OK; } goto Exit; } pStack->uiCurElm = BH_OVHD; pStack->uiBlkEnd = (FLMUINT)FB2UW( &pStack->pBlk[ BH_ELM_END ] ); for(;;) { pStack->uiFlags = FULL_STACK; pStack->uiKeyBufSize = MAX_KEY_SIZ; if( RC_BAD( rc = FSPositionScan( pStack, uiRefPosition, &uiRefPosition, puiRecordId, puiDomain, pDinState))) { goto Exit; } if( !pStack->uiLevel) { break; } uiBlkAddr = FSChildBlkAddr( pStack ); pStack++; pStack->pKeyBuf = pKeyBuf; if( RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlkAddr, pStack ))) { goto Exit; } } *ppStack = pStack; Exit: return( rc); } /*************************************************************************** Desc: Position to the element given a position value relative to the block. *****************************************************************************/ RCODE FSPositionScan( BTSK * pStack, FLMUINT uiRelativePosition, FLMUINT * puiRelativePosInElement, FLMUINT * puiRecordId, FLMUINT * puiDomain, DIN_STATE * pDinState) { RCODE rc = FERR_OK; FLMUINT uiRefCount; FLMBYTE * pCount; FLMBYTE * pBlk = pStack->pBlk; FLMBYTE * pKeyBuf = pStack->pKeyBuf; FLMBYTE * pPrevKey; FLMBYTE * pCurElm; FLMUINT uiBlkType = pStack->uiBlkType; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT uiElmKeyLen; FLMUINT uiPrevKeyCnt = 0; FLMUINT uiPrevPrevKeyCnt = 0; FLMUINT uiBytesToMove; FLMUINT uiTotalElmLen; pStack->uiCurElm = BH_OVHD; pStack->uiBlkEnd = (FLMUINT)FB2UW( &pBlk[ BH_ELM_END]); pPrevKey = NULL; for(;;) { pCurElm = &pBlk[ pStack->uiCurElm ]; if( uiBlkType != BHT_LEAF) { pCount = pCurElm + BNE_CHILD_COUNT; uiRefCount = FB2UD( pCount); } else { uiRefCount = FSElementRefCount( pStack); } uiElmKeyLen = BBE_GETR_KL( pCurElm ); if( ( uiPrevKeyCnt = (BBE_GETR_PKC( pCurElm ))) > BBE_PKC_MAX) { uiElmKeyLen += (uiPrevKeyCnt & BBE_KL_HBITS) << BBE_KL_SHIFT_BITS; uiPrevKeyCnt &= BBE_PKC_MAX; } uiTotalElmLen = uiElmOvhd + uiElmKeyLen; if( uiBlkType != BHT_LEAF) { if( BNE_IS_DOMAIN( pCurElm)) { uiTotalElmLen += BNE_DOMAIN_LEN; } } else { // Copy the key into the key buffer. if( uiPrevKeyCnt > uiPrevPrevKeyCnt) { uiBytesToMove = uiPrevKeyCnt - uiPrevPrevKeyCnt; f_memcpy( &pKeyBuf[ uiPrevPrevKeyCnt], pPrevKey, uiBytesToMove); } pPrevKey = pCurElm + uiElmOvhd; uiTotalElmLen += BBE_GET_RL( pCurElm); } if( uiRefCount >= uiRelativePosition) { pStack->uiKeyLen = uiElmKeyLen + uiPrevKeyCnt; pStack->uiPrevElmPKC = uiPrevPrevKeyCnt; pStack->uiPKC = uiPrevKeyCnt; if( uiBlkType == BHT_LEAF) { // Copy the remaining bytes of the key. pPrevKey is current key. if( uiElmKeyLen) { f_memcpy( &pKeyBuf[ uiPrevKeyCnt], pPrevKey, uiElmKeyLen); } if( RC_BAD( rc = FSPositionToRef( pStack, uiRelativePosition, puiRecordId, puiDomain, pDinState))) { goto Exit; } uiRelativePosition = 0; } break; } uiPrevPrevKeyCnt = uiPrevKeyCnt; uiRelativePosition -= uiRefCount; pStack->uiCurElm += uiTotalElmLen; if( pStack->uiCurElm >= pStack->uiBlkEnd) { uiRelativePosition = 0; rc = RC_SET( FERR_EOF_HIT); goto Exit; } } *puiRelativePosInElement = uiRelativePosition; Exit: return( rc); } /*************************************************************************** Desc: Position to the element given a position value relative to the block. *****************************************************************************/ RCODE FSPositionToRef( BTSK * pStack, FLMUINT uiRelativePosition, FLMUINT * puiRecordId, FLMUINT * puiDomain, DIN_STATE * pDinState) { RCODE rc = FERR_OK; FLMUINT uiRefCount; FLMUINT uiRecordId; RESET_DINSTATE_p( pDinState); uiRefCount = FSElementRefCount( pStack); if( uiRelativePosition <= 1) { uiRecordId = FSRefFirst( pStack, pDinState, puiDomain); } else { // Find the position within the element. FLMBYTE * pCurRef; // Points to current reference FLMBYTE * pCurElm; // Points to current element FLMUINT uiRefSize; // Size of the reference set DIN_STATE tempState; // Point to the start of the current reference skipping over domain info. pCurRef = pCurElm = CURRENT_ELM( pStack ); *puiDomain = FSGetDomain( &pCurRef, pStack->uiElmOvhd) + 1; uiRefSize = (FLMUINT)(BBE_GET_RL(pCurElm) - (pCurRef - BBE_REC_PTR(pCurElm))); uiRecordId = DINNextVal( pCurRef, pDinState); uiRelativePosition--; while( uiRelativePosition > 1 && pDinState->uiOffset < uiRefSize) { uiRecordId -= DINNextVal( pCurRef, pDinState); uiRelativePosition--; } flmAssert( pDinState->uiOffset < uiRefSize); // Get the last value without moving pDinState. tempState.uiOffset = pDinState->uiOffset; tempState.uiOnes = pDinState->uiOnes; uiRecordId -= DINNextVal( pCurRef, &tempState ); } *puiRecordId = uiRecordId; return( rc); } libflaim-4.9.966/src/flrddrct.cpp0000644000175000017500000002167610510774540020200 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Retrieve record from database. // Tabs: 3 // // Copyright (c) 1990,1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flrddrct.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmRecordRetrieveCS( FDB * pDb, FLMUINT uiContainer, FLMUINT uiDrn, FLMUINT uiFlag, FlmRecord ** ppRecord, FLMUINT * puiDrnRV); /**************************************************************************** Desc: Retrieves a single record from a container. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmRecordRetrieve( HFDB hDb, FLMUINT uiContainer, FLMUINT uiDrn, FLMUINT uiFlag, FlmRecord ** ppRecord, FLMUINT * puiDrnRV) { FLMUINT uiKeyRelPos; FLMBOOL bTransStarted; FLMUINT uiFoundDrn; LFILE * pLFile; BTSK stack[ BH_MAX_LEVELS]; FLMBOOL bStackInitialized = FALSE; BTSK * pStack; FLMBYTE pSearchBuf[ DRN_KEY_SIZ + 4]; FLMBYTE pKeyBuf[ DRN_KEY_SIZ + 4]; FDB * pDb = (FDB *)hDb; FLMUINT uiSaveInitNestLevel = 0; DB_STATS * pDbStats; RCODE rc = FERR_OK; #ifdef FLM_DEBUG flmAssert( hDb != NULL); flmAssert( uiContainer != 0); #endif bTransStarted = FALSE; uiFoundDrn = 0; if( !uiDrn && (uiFlag & (FO_EXACT | FO_INCL))) { if( uiFlag & FO_INCL) { uiDrn = 1; } else { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } } if (IsInCSMode( hDb)) { fdbInitCS( pDb); rc = flmRecordRetrieveCS( pDb, uiContainer, uiDrn, uiFlag, ppRecord, puiDrnRV); goto ExitCS; } // Remove the exclusive case and turn it into an inclusive search. if( uiFlag & FO_EXCL) { // For records, exclusive is same as inclusive drn+1 uiDrn++; uiFlag &= ~FO_EXCL; uiFlag = FO_INCL; } uiSaveInitNestLevel = pDb->uiInitNestLevel; // Test both FO_FIRST and FO_LAST at the same time to save time. if (uiFlag & (FO_FIRST | FO_LAST)) { if (uiFlag & FO_FIRST) { uiFlag = FO_INCL; uiDrn = 1; } else { uiFlag = FO_LAST; uiDrn = DRN_LAST_MARKER - 1; goto Search_Record; } } if( uiFlag & FO_EXACT) { if( RC_OK( rc = flmRcaRetrieveRec( pDb, &bTransStarted, uiContainer, uiDrn, TRUE, NULL, NULL, ppRecord))) { uiFoundDrn = uiDrn; } else if (rc != FERR_NOT_FOUND || ppRecord) { goto Exit; } // else do nothing - fall through, because even though we passed // TRUE in for the bOkToGetFromDisk parameter, flmRcaRetrieveRec // will NOT try to fetch from disk if ppRecord is NULL - so // we still need to try to fetch from disk. } else // only FO_INCL case can exist for the else case. { // Let's be optimistic and see if record is already in cache // before we search the b-tree. Don't try to retrieve the record from // disk if it is not in cache ... the next record may not have a DRN // of uiDrn and we don't want to search the b-tree twice to fetch // the next record. if( RC_OK( rc = flmRcaRetrieveRec( pDb, &bTransStarted, uiContainer, uiDrn, FALSE, NULL, NULL, ppRecord))) { uiFoundDrn = uiDrn; } else if( rc != FERR_NOT_FOUND) { goto Exit; } // else - rc == FERR_NOT_FOUND, so we need to see if we can find the // next record on disk. } if( !uiFoundDrn) { Search_Record: if( uiSaveInitNestLevel == pDb->uiInitNestLevel) { if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, &bTransStarted))) { goto Exit; } } if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) { goto Exit; } f_UINT32ToBigEndian( (FLMUINT32)uiDrn, pSearchBuf); FSInitStackCache( &stack [0], BH_MAX_LEVELS); pStack = &stack[0]; bStackInitialized = TRUE; pStack->pKeyBuf = pKeyBuf; // Search the B-Tree for the key. if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, pSearchBuf, 4, 0))) { goto Exit; } uiKeyRelPos = pStack->uiCmpStatus; if( uiFlag & FO_EXACT) { if( uiKeyRelPos != BT_EQ_KEY) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } } else // inclusive or FO_LAST { // Handle FO_LAST case - If FO_LAST bit was set, uiFlag will // have been changed above to simply be equal to FO_LAST. if (uiFlag == FO_LAST) { if (pStack->uiBlkAddr == BT_END) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if (uiKeyRelPos == BT_END_OF_DATA || f_bigEndianToUINT32( pKeyBuf) > uiDrn) { // Position to the last element in the block. if (RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack))) { if (rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } // Position to beginning of record. while (BBE_NOT_FIRST( CURRENT_ELM( pStack ))) { if (RC_BAD( rc = FSBtPrevElm( pDb, pLFile, pStack))) { if (rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } } } } else if( uiKeyRelPos == BT_END_OF_DATA) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } } uiFoundDrn = f_bigEndianToUINT32( pKeyBuf); if( uiFoundDrn == DRN_LAST_MARKER) { if( uiFlag & FO_EXACT) { rc = RC_SET( FERR_NOT_FOUND); } else { rc = RC_SET( FERR_EOF_HIT); } goto Exit; } // Need to return the record? if( ppRecord) { if( RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, uiContainer, uiFoundDrn, TRUE, pStack, pLFile, ppRecord))) { goto Exit; } } } if( puiDrnRV) { *puiDrnRV = uiFoundDrn; } if( (pDbStats = pDb->pDbStats) != NULL) { pDbStats->bHaveStats = TRUE; pDbStats->ui64NumRecordReads++; } ExitCS: // Call record validator callback if( pDb->fnRecValidator) { FLMBOOL bSavedInvisTrans; CB_ENTER( pDb, &bSavedInvisTrans); (void)(pDb->fnRecValidator)( FLM_RECORD_RETRIEVE, hDb, uiContainer, *ppRecord, NULL, pDb->RecValData, &rc); CB_EXIT( pDb, bSavedInvisTrans); } Exit: if (bStackInitialized) { FSReleaseStackCache( stack, BH_MAX_LEVELS, FALSE); } if (bTransStarted) { RCODE rc2 = flmAbortDbTrans( pDb); if (RC_OK( rc)) { rc = rc2; } } // Don't want it to call fdbExit if fdbInit wasn't called. if (pDb->uiInitNestLevel == uiSaveInitNestLevel) { pDb = NULL; } flmExit( FLM_RECORD_RETRIEVE, pDb, rc); return( rc); } /**************************************************************************** Desc: Retrieves a record based on uiDrn and uiFlag client-server. ****************************************************************************/ FSTATIC RCODE flmRecordRetrieveCS( FDB * pDb, FLMUINT uiContainer, FLMUINT uiDrn, FLMUINT uiFlag, FlmRecord ** ppRecord, FLMUINT * puiDrnRV) { RCODE rc; CS_CONTEXT * pCSContext = pDb->pCSContext; void * pvMark = pCSContext->pool.poolMark(); FCL_WIRE Wire( pCSContext, pDb); // Set the record object so that it can be re-used, // if possible if( ppRecord) { Wire.setRecord( *ppRecord); if( *ppRecord) { (*ppRecord)->Release(); *ppRecord = NULL; } } // Set the temporary pool Wire.setPool( &pCSContext->pool); // Send a request to retrieve the record if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_RECORD, FCS_OP_RECORD_RETRIEVE))) { goto Exit; } if (uiContainer) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_CONTAINER_ID, uiContainer))) { goto Transmission_Error; } } if (uiDrn) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_DRN, uiDrn))) { goto Transmission_Error; } } if (uiFlag) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiFlag))) { goto Transmission_Error; } } if (ppRecord) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_BOOLEAN, TRUE))) { goto Transmission_Error; } } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (RC_BAD( rc = Wire.getRCode())) { goto Exit; } if( puiDrnRV) { *puiDrnRV = Wire.getDrn(); } if( ppRecord) { if( (*ppRecord = Wire.getRecord()) != NULL) { (*ppRecord)->AddRef(); } } Exit: pCSContext->pool.poolReset( pvMark); return( rc); Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } libflaim-4.9.966/src/imonsche.cpp0000644000175000017500000012352110510774540020171 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying an SCACHE structure in HTML on a web page. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonsche.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC void flmPrintCacheLine( HRequest * pHRequest, const char * pszHREF, const char * pszName, void * pBaseAddr, SCACHE ** ppSCache); FSTATIC void flmBuildSCacheBlockString( char * pszString, SCACHE * pScache); /**************************************************************************** Desc: Prints the web page for an SCACHE struct ****************************************************************************/ RCODE F_SCacheBlockPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMUINT uiBlkAddress = 0; FLMUINT uiLowTransID = 0; FLMUINT uiHighTransID = 0; FFILE * pFile; FLMBOOL bHighlight = FALSE; char * pszTemp = NULL; char * pszTemp1 = NULL; FLMUINT uiLoop = 0; char szOffsetTable[10][6]; char szAddressTable[4][20]; SCACHE LocalSCacheBlock; FLMUINT uiPFileBucket = 0; char * pszSCacheRequestString[8] = {0, 0, 0, 0, 0, 0, 0, 0}; char * pszSCacheDataRequest = NULL; char * pszSCacheAutoRequest = NULL; char * pszSCacheUseListRequest = NULL; char * pszSCacheNotifyListRequest = NULL; char * pszFFileRequest = NULL; char * pszFlagNames = NULL; if( RC_BAD( rc = f_alloc( 200, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } if( RC_BAD( rc = f_alloc( 200, &pszTemp1))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Allocate memory for all those string pointers we declared above... for (uiLoop = 0; uiLoop < 8; uiLoop++) { if( RC_BAD( rc = f_alloc( 150, &pszSCacheRequestString[ uiLoop]))) { goto Exit; } } if( RC_BAD( rc = f_alloc( 150, &pszSCacheDataRequest))) { goto Exit; } if( RC_BAD( rc = f_alloc( 150, &pszSCacheAutoRequest))) { goto Exit; } if( RC_BAD( rc = f_alloc( 150, &pszSCacheUseListRequest))) { goto Exit; } if( RC_BAD( rc = f_alloc( 150, &pszSCacheNotifyListRequest))) { goto Exit; } if( RC_BAD( rc = f_alloc( 100, &pszFFileRequest))) { goto Exit; } if( RC_BAD( rc = f_alloc( 100, &pszFlagNames))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); rc = locateSCacheBlock( uiNumParams, ppszParams, &LocalSCacheBlock, &uiBlkAddress, &uiLowTransID, &uiHighTransID, &pFile); if (RC_OK(rc) && LocalSCacheBlock.pFile) { uiPFileBucket = LocalSCacheBlock.pFile->uiBucket; } if (RC_OK( rc)) { // Build the proper strings to request various other SCache blocks flmBuildSCacheBlockString( pszSCacheRequestString[0], LocalSCacheBlock.pPrevInFile); flmBuildSCacheBlockString( pszSCacheRequestString[1], LocalSCacheBlock.pNextInFile); flmBuildSCacheBlockString( pszSCacheRequestString[2], LocalSCacheBlock.pPrevInGlobalList); flmBuildSCacheBlockString( pszSCacheRequestString[3], LocalSCacheBlock.pNextInGlobalList); flmBuildSCacheBlockString( pszSCacheRequestString[4], LocalSCacheBlock.pPrevInHashBucket); flmBuildSCacheBlockString( pszSCacheRequestString[5], LocalSCacheBlock.pNextInHashBucket); flmBuildSCacheBlockString( pszSCacheRequestString[6], LocalSCacheBlock.pPrevInVersionList); flmBuildSCacheBlockString( pszSCacheRequestString[7], LocalSCacheBlock.pNextInVersionList); // Build the proper string to request the current Page flmBuildSCacheBlockString( pszSCacheAutoRequest, &LocalSCacheBlock); } f_mutexUnlock( gv_FlmSysData.hShareMutex); if (RC_BAD( rc)) { if (rc == FERR_NOT_FOUND) { // The block wasn't there, print an error message and exit notFoundErr(); rc = FERR_OK; } else if (rc == FERR_MEM) { // Parameters were too long to store in the space provided. // Probably means that the URL was malformed... malformedUrlErr(); rc = FERR_OK; } goto Exit; } //Build the proper string to request this block's data... printAddress( pFile, szAddressTable[0]); f_sprintf( (char *)pszSCacheDataRequest, "%s/SCacheData?BlockAddress=%lu&File=%s&LowTransID=%lu&HighTransID=%lu", m_pszURLString, LocalSCacheBlock.uiBlkAddress, szAddressTable[0], uiLowTransID, uiHighTransID); #ifdef FLM_DEBUG //Build the proper string to request this block's use list if( LocalSCacheBlock.pUseList) { f_sprintf( (char *)pszSCacheUseListRequest, "%s/SCacheUseList?BlockAddress=%lu&File=%s&LowTransID=%lu&HighTransID=%lu", m_pszURLString, LocalSCacheBlock.uiBlkAddress, szAddressTable[0], uiLowTransID, uiHighTransID); } else { pszSCacheUseListRequest[0] = '\0'; } #endif //Build the proper string to request the notify list data... if (LocalSCacheBlock.pNotifyList) { f_sprintf( (char *)pszSCacheNotifyListRequest, "%s/SCacheNotifyList?BlockAddress=%lu&File=%s&LowTransID=%lu&HighTransID=%lu", m_pszURLString, LocalSCacheBlock.uiBlkAddress, szAddressTable[0], uiLowTransID, uiHighTransID); } else { pszSCacheNotifyListRequest[0] = '\0'; } //Build the proper string to request the FFile printAddress( LocalSCacheBlock.pFile, szAddressTable[0]); f_sprintf( (char *)pszFFileRequest, "%s/FFile?From=SCacheBlock&Bucket=%lu&Address=%s", m_pszURLString, uiPFileBucket, szAddressTable[0]); // Build a string with the names of all the flags that have been set... pszFlagNames[0]='\0'; if (LocalSCacheBlock.ui16Flags & CA_DIRTY) { f_strcat( pszFlagNames, "
CA_DIRTY"); } if (LocalSCacheBlock.ui16Flags & CA_READ_PENDING) { f_strcat( pszFlagNames, "
CA_READ_PENDING"); } if (LocalSCacheBlock.ui16Flags & CA_WRITE_TO_LOG) { f_strcat( pszFlagNames, "
CA_WRITE_TO_LOG"); } if (LocalSCacheBlock.ui16Flags & CA_LOG_FOR_CP) { f_strcat( pszFlagNames, "
CA_LOG_FOR_CP"); } if (LocalSCacheBlock.ui16Flags & CA_WAS_DIRTY) { f_strcat( pszFlagNames, "
CA_WAS_DIRTY"); } if (LocalSCacheBlock.ui16Flags & CA_WRITE_PENDING) { f_strcat( pszFlagNames, "
CA_WRITE_PENDING"); } if (LocalSCacheBlock.ui16Flags & CA_IN_WRITE_PENDING_LIST) { f_strcat( pszFlagNames, "
CA_IN_WRITE_PENDING_LIST"); } // OK - Start outputting HTML... stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n"); // Determine if we are being requested to refresh this page or not. if (DetectParameter( uiNumParams, ppszParams, "Refresh")) { // Send back the page with a refresh command in the header fnPrintf( m_pHRequest, "\n" "" "SCache Block\n", pszSCacheAutoRequest); printStyle(); popupFrame(); //Spits out a Javascript function that will open a new window.. fnPrintf( m_pHRequest, "\n\n"); f_sprintf( (char*)pszTemp, "Stop Auto-refresh", pszSCacheAutoRequest); } else { // Send back a page without the refresh command fnPrintf( m_pHRequest, "\n"); printStyle(); popupFrame(); //Spits out a Javascript function that will open a new window.. fnPrintf( m_pHRequest, "\n\n"); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", pszSCacheAutoRequest); } // Write out the table headings printTableStart( "SCache Block Structure", 4, 100); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, %s\n", pszSCacheAutoRequest, pszTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings. printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); // Print the two rows for pPrevInFile and pNextInFile printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[0], "pPrevInFile", &LocalSCacheBlock, &LocalSCacheBlock.pPrevInFile); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[1], "pNextInFile", &LocalSCacheBlock, &LocalSCacheBlock.pNextInFile); // Format the strings that are displayed in the Offset and Address // columns of the table printOffset( &LocalSCacheBlock, &LocalSCacheBlock.pucBlk, szOffsetTable[0]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.pFile, szOffsetTable[1]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.uiBlkAddress, szOffsetTable[2]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.pNotifyList, szOffsetTable[3]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.uiHighTransID, szOffsetTable[4]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.uiUseCount, szOffsetTable[5]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.ui16Flags, szOffsetTable[6]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.ui16BlkSize, szOffsetTable[7]); #ifdef FLM_DEBUG printOffset( &LocalSCacheBlock, &LocalSCacheBlock.uiChecksum, szOffsetTable[8]); printOffset( &LocalSCacheBlock, &LocalSCacheBlock.pUseList, szOffsetTable[9]); #endif printAddress( LocalSCacheBlock.pucBlk, szAddressTable[0]); printAddress( LocalSCacheBlock.pFile, szAddressTable[1]); printAddress( LocalSCacheBlock.pNotifyList, szAddressTable[2]); #ifdef FLM_DEBUG printAddress( LocalSCacheBlock.pUseList, szAddressTable[3]); #endif printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "

\n" "\n\n", szOffsetTable[0], pszSCacheDataRequest, pszSCacheDataRequest, szAddressTable[0] ); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n\n", szOffsetTable[1], pszFFileRequest, pszFFileRequest, szAddressTable[1]); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n\n" "\n", szOffsetTable[2], LocalSCacheBlock.uiBlkAddress); printTableRowEnd(); //Print the rows for the remaining SCache * fields printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[2], "pPrevInGlobalList", &LocalSCacheBlock, &LocalSCacheBlock.pPrevInGlobalList); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[3], "pNextInGlobalList", &LocalSCacheBlock, &LocalSCacheBlock.pNextInGlobalList); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[4], "pPrevInHashBucket", &LocalSCacheBlock, &LocalSCacheBlock.pPrevInHashBucket); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[5], "pNextInHashBucket", &LocalSCacheBlock, &LocalSCacheBlock.pNextInHashBucket); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[6], "pPrevInVersionList", &LocalSCacheBlock, &LocalSCacheBlock.pPrevInVersionList); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[7], "pNextInVersionList", &LocalSCacheBlock, &LocalSCacheBlock.pNextInVersionList); //Notify list line printTableRowStart( bHighlight = ~bHighlight); if (LocalSCacheBlock.pNotifyList) { fnPrintf( m_pHRequest, TD_s " " "", szOffsetTable[3], pszSCacheNotifyListRequest, pszSCacheNotifyListRequest, szAddressTable[2]); } else { fnPrintf( m_pHRequest, TD_s " " "", szOffsetTable[3]); } printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_8x, szOffsetTable[4], LocalSCacheBlock.uiHighTransID); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n\n" TD_lu, szOffsetTable[5], LocalSCacheBlock.uiUseCount); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n\n" "\n", szOffsetTable[6], LocalSCacheBlock.ui16Flags, pszFlagNames); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n\n" TD_i, szOffsetTable[7], LocalSCacheBlock.ui16BlkSize); printTableRowEnd(); #ifdef FLM_DEBUG printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_8x, szOffsetTable[8], LocalSCacheBlock.uiChecksum); printTableRowEnd(); #endif #ifdef FLM_DEBUG //Last line - the use list... printTableRowStart( bHighlight = ~bHighlight); if (LocalSCacheBlock.pUseList) { fnPrintf( m_pHRequest, TD_s " ", szOffsetTable[9], pszSCacheUseListRequest, pszSCacheUseListRequest, szAddressTable[3]); } else { fnPrintf( m_pHRequest, TD_s " ", szOffsetTable[9]); } printTableRowEnd(); #endif fnPrintf( m_pHRequest, TABLE_END "\n"); fnEmit(); Exit: // Even though uiLoop2 is not in the same scope as uiLoop, VC6 still // complains if this is called uiLoop.... for (FLMUINT uiLoop2 = 0; uiLoop2 < 8; uiLoop2++) { if (pszSCacheRequestString[uiLoop2]) { f_free( &pszSCacheRequestString[uiLoop2]); } } if (pszSCacheDataRequest) { f_free( &pszSCacheDataRequest); } if (pszSCacheAutoRequest) { f_free( &pszSCacheAutoRequest); } if (pszSCacheUseListRequest) { f_free( &pszSCacheUseListRequest); } if (pszSCacheNotifyListRequest) { f_free( &pszSCacheNotifyListRequest); } if( pszFFileRequest) { f_free( &pszFFileRequest); } if( pszFlagNames) { f_free( &pszFlagNames); } if (pszTemp) { f_free( &pszTemp); } if (pszTemp1) { f_free( &pszTemp1); } return( rc); } /**************************************************************************** Desc: Prints the web page for an SCACHE use list This function is essentially unimplemented because I have yet to see an SCache page where the use_list value was non-null!! ****************************************************************************/ RCODE F_SCacheUseListPage::display( FLMUINT uiNumParams, const char ** ppszParams) { F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); RCODE rc = FERR_OK; stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n\n \n" "Congratulations! You've managed to find an SCache block with a valid " "use list! Too bad we haven't implemented a page to dislay use " "lists yet...\n "); fnEmit(); return( rc); } /**************************************************************************** Desc: Prints the web page for an SCACHE notify list This function is essentially unimplemented because I have yet to see an SCache page where the notify list value was non-null!! ****************************************************************************/ RCODE F_SCacheNotifyListPage::display( FLMUINT uiNumParams, const char ** ppszParams) { F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); RCODE rc = FERR_OK; stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n\n \n" "Congratulations! You've managed to find an SCache block with a valid " "notify list! Too bad we haven't implemented a page to dislay use " "lists yet...\n "); fnEmit(); return( rc); } /**************************************************************************** Desc: Prints the web page showing the binary data in an SCache block ****************************************************************************/ RCODE F_SCacheDataPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMUINT uiBlkAddress = 0; FLMUINT uiLowTransID = 0; FLMUINT uiHighTransID = 0; FFILE * pFile = NULL; FLMBOOL bFlaimLocked = FALSE; SCACHE LocalSCacheBlock; char * pucData = NULL; char * pucDataLine; char szData[97]; char szOneChar[7]; FLMUINT uiCurrentOffset = 0; FLMUINT uiLoop = 0; f_mutexLock( gv_FlmSysData.hShareMutex); bFlaimLocked = TRUE; rc = locateSCacheBlock( uiNumParams, ppszParams, &LocalSCacheBlock, &uiBlkAddress, &uiLowTransID, &uiHighTransID, &pFile); if (RC_BAD( rc)) { if(rc == FERR_NOT_FOUND) { notFoundErr(); rc = FERR_OK; } goto Exit; } else { // Store the data in a local variable... if( RC_BAD( rc = f_alloc( LocalSCacheBlock.ui16BlkSize, &pucData))) { goto Exit; } f_memcpy( pucData, LocalSCacheBlock.pucBlk, LocalSCacheBlock.ui16BlkSize); } f_mutexUnlock( gv_FlmSysData.hShareMutex); bFlaimLocked = FALSE; // Start the HTML... stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n
\n");

	while (uiCurrentOffset < LocalSCacheBlock.ui16BlkSize)
	{
		szData[0] = '\0';
		pucDataLine =  pucData + uiCurrentOffset;
		fnPrintf( m_pHRequest, "0x%04X    "
						"%02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X  %02X %02X %02X %02X    ",
						uiCurrentOffset,
						pucDataLine[ 0], pucDataLine[ 1], pucDataLine[ 2], pucDataLine[ 3],
						pucDataLine[ 4], pucDataLine[ 5], pucDataLine[ 6], pucDataLine[ 7],
						pucDataLine[ 8], pucDataLine[ 9], pucDataLine[10], pucDataLine[11],
						pucDataLine[12], pucDataLine[13], pucDataLine[14], pucDataLine[15]);
	
		for (uiLoop = 0; uiLoop < 16; uiLoop++)
		{
			if (	(pucDataLine[uiLoop] >= 32) &&  // 32 is a space
					(pucDataLine[uiLoop] <= 126)  ) // 126 is a ~
			{
				f_sprintf( szOneChar, "&#%d;", pucDataLine[uiLoop]);
			}
			else
			{
				f_strcpy( szOneChar, "."); // 46 is a .
			}
			f_strcat(szData, szOneChar);

			// The reason for all the &#xxx; nonsence is because if we just put
			// the characters into a string, when the brower comes across a <
			// character, it will try to interpret what follows as an HTML
			// tag...
		}

		fnPrintf( m_pHRequest, "%s\n", szData);
		
		uiCurrentOffset += 16;
	}

	fnPrintf( m_pHRequest, "
\n\n"); fnEmit(); Exit: if (bFlaimLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } if (pucData) { f_free( &pucData); } return( rc); } /**************************************************************************** Desc: Prints the web page for an SCACHEMGR struct (The URL for this page requires no parameters since there is only one SCACHE_MGR per copy of FLAIM.) ****************************************************************************/ RCODE F_SCacheMgrPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; SCACHE_MGR LocalSCacheMgr; FLMBOOL bAutoRefresh; #define NUM_CACHE_REQ_STRINGS 4 char * pszSCacheRequestString[ NUM_CACHE_REQ_STRINGS]; char szOffsetTable[12][6]; char szAddressTable[2][20]; FLMBOOL bHighlight = FALSE; char * pszTemp = NULL; FLMUINT uiLoop; // Note: The SCacheBlock requests need the following params: // "BlockAddress", "File", "LowTransID" and "HighTransID" // ex: pMRUCache if( RC_BAD( rc = f_alloc( 200, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // First thing that we need to do is grab a local copy of gv_FlmSysData.SCacheMgr, // and of the data for the three SCache blocks that it has pointers to... for (uiLoop = 0; uiLoop < NUM_CACHE_REQ_STRINGS; uiLoop++) { if( RC_BAD( rc = f_alloc( 150, &pszSCacheRequestString[ uiLoop]))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } } f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy (&LocalSCacheMgr, &gv_FlmSysData.SCacheMgr, sizeof (LocalSCacheMgr)); flmBuildSCacheBlockString( pszSCacheRequestString[0], LocalSCacheMgr.pMRUCache); flmBuildSCacheBlockString( pszSCacheRequestString[1], LocalSCacheMgr.pLRUCache); flmBuildSCacheBlockString( pszSCacheRequestString[2], LocalSCacheMgr.pFirstFree); flmBuildSCacheBlockString( pszSCacheRequestString[3], LocalSCacheMgr.pLastFree); f_mutexUnlock( gv_FlmSysData.hShareMutex); bAutoRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); // Now - are we being asked to display the usage stats? Or is this a regular page... if (DetectParameter( uiNumParams, ppszParams, "Usage")) { // There's a function to handle display the usage info (because both // RCacheMgr and SCacheMgr have usage stats). writeUsage( &LocalSCacheMgr.Usage, bAutoRefresh, "/SCacheMgr?Usage", "Usage Statistics for the SCache"); } else // This is a regular SCacheMgr page... { // Determine if we are being requested to refresh this page or not. stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n"); if (bAutoRefresh) { // Send back the page with a refresh command in the header fnPrintf( m_pHRequest, "" "" "gv_FlmSysData.SCacheMgr\n", m_pszURLString); printStyle(); popupFrame(); //Spits out a Javascript function that will open a new window.. fnPrintf( m_pHRequest, "\n\n\n"); f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString); } else // bAutoRefresh == FALSE { // Send back a page without the refresh command fnPrintf( m_pHRequest, "" "gv_FlmSysData.SCacheMgr\n"); printStyle(); popupFrame(); //Spits out a Javascript function that will open a new window.. fnPrintf( m_pHRequest, "\n\n\n"); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString); } // Write out the table headings printTableStart( "SCache Manager Structure", 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, %s\n", m_pszURLString, pszTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings. printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); //Now - we have three rows in the table that may or may not have hyperlinks in them. printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[0], "pMRUCache", &LocalSCacheMgr, &LocalSCacheMgr.pMRUCache); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[1], "pLRUCache", &LocalSCacheMgr, &LocalSCacheMgr.pLRUCache); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[2], "pFirstFree", &LocalSCacheMgr, &LocalSCacheMgr.pFirstFree); printTableRowStart( bHighlight = ~bHighlight); flmPrintCacheLine(m_pHRequest, pszSCacheRequestString[3], "pLastFree", &LocalSCacheMgr, &LocalSCacheMgr.pLastFree); //Format the strings that are displayed in the Offset column on of the table printOffset(&LocalSCacheMgr, &LocalSCacheMgr.ppHashTbl, szOffsetTable[0]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.Usage, szOffsetTable[1]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.bAutoCalcMaxDirty, szOffsetTable[2]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiMaxDirtyCache, szOffsetTable[3]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiLowDirtyCache, szOffsetTable[4]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiTotalUses, szOffsetTable[5]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiBlocksUsed, szOffsetTable[6]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiPendingReads, szOffsetTable[7]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiIoWaits, szOffsetTable[8]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiHashTblSize, szOffsetTable[9]); printOffset(&LocalSCacheMgr, &LocalSCacheMgr.uiHashTblBits, szOffsetTable[10]); #ifdef FLM_DEBUG printOffset(&LocalSCacheMgr, &LocalSCacheMgr.bDebug, szOffsetTable[11]); #endif printAddress( LocalSCacheMgr.ppHashTbl, szAddressTable[0]); printAddress( &LocalSCacheMgr.Usage, szAddressTable[1]); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" "\n", szOffsetTable[0], m_pszURLString, m_pszURLString, szAddressTable[0]); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" "\n", szOffsetTable[1], m_pszURLString, m_pszURLString, szAddressTable[1]); printTableRowEnd(); // uiFreeCount printHTMLUint( (char *)"uiFreeCount", (char *)"FLMUINT", (void *)&LocalSCacheMgr, (void *)&LocalSCacheMgr.uiFreeCount, LocalSCacheMgr.uiFreeCount, (bHighlight = ~bHighlight)); // uiFreeBytes printHTMLUint( (char *)"uiFreeBytes", (char *)"FLMUINT", (void *)&LocalSCacheMgr, (void *)&LocalSCacheMgr.uiFreeBytes, LocalSCacheMgr.uiFreeBytes, (bHighlight = ~bHighlight)); // uiReplaceableCount printHTMLUint( (char *)"uiReplaceableCount", (char *)"FLMUINT", (void *)&LocalSCacheMgr, (void *)&LocalSCacheMgr.uiReplaceableCount, LocalSCacheMgr.uiReplaceableCount, (bHighlight = ~bHighlight)); // uiReplaceableBytes printHTMLUint( (char *)"uiReplaceableBytes", (char *)"FLMUINT", (void *)&LocalSCacheMgr, (void *)&LocalSCacheMgr.uiReplaceableBytes, LocalSCacheMgr.uiReplaceableBytes, (bHighlight = ~bHighlight)); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_i, szOffsetTable[2], LocalSCacheMgr.bAutoCalcMaxDirty); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[3], LocalSCacheMgr.uiMaxDirtyCache); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[4], LocalSCacheMgr.uiLowDirtyCache); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[5], LocalSCacheMgr.uiTotalUses); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" TD_lu, szOffsetTable[6], LocalSCacheMgr.uiBlocksUsed); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[7], LocalSCacheMgr.uiPendingReads); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n \n" TD_lu, szOffsetTable[8], LocalSCacheMgr.uiIoWaits); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[9], LocalSCacheMgr.uiHashTblSize); printTableRowEnd(); printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_lu, szOffsetTable[10], LocalSCacheMgr.uiHashTblBits); printTableRowEnd(); #ifdef FLM_DEBUG printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s "\n" "\n" TD_i, szOffsetTable[11], LocalSCacheMgr.bDebug); printTableRowEnd(); #endif printTableEnd(); fnPrintf( m_pHRequest, "\n"); fnEmit(); } Exit: if (pszTemp) { f_free( &pszTemp); } for (uiLoop = 0; uiLoop < NUM_CACHE_REQ_STRINGS; uiLoop++) { if( pszSCacheRequestString[uiLoop]) { f_free( &pszSCacheRequestString[uiLoop]); } } return( rc); } /**************************************************************************** Desc: Prints the web page for the SCacheHashTable ****************************************************************************/ RCODE F_SCacheHashTablePage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMBOOL bRefresh; FLMBOOL bHighlight = TRUE; FLMUINT uiLoop; FLMUINT uiHashTableSize; FLMUINT uiUsedEntries = 0; char szStart[10]; char szRefresh[] = "&Refresh"; FLMUINT uiStart; FLMUINT uiNewStart; char * pszTemp; #define NUM_ENTRIES 20 char * pszHTLinks[NUM_ENTRIES]; F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); // Check for the refresh parameter bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); if (!bRefresh) { szRefresh[0]='\0'; // Effectively turns szRefresh into a null string } // Get the starting entry number... if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Start", sizeof( szStart), szStart))) { flmAssert( 0); goto Exit; } uiStart = f_atoud( szStart); // Allocate space for the hyperlink text for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { if( RC_BAD( rc = f_alloc( 250, &pszHTLinks[ uiLoop]))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } pszHTLinks[uiLoop][0] = '\0'; } if( RC_BAD( rc = f_alloc( 250, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Lock the database f_mutexLock( gv_FlmSysData.hShareMutex); // Get the number of entries in the hash table uiHashTableSize = gv_FlmSysData.SCacheMgr.uiHashTblSize; // May need to modify starting number if it's out of range... if ((uiStart + NUM_ENTRIES) >= uiHashTableSize) { uiStart = uiHashTableSize - NUM_ENTRIES; } // Loop through the entire table counting the number of entries in use // If the entry is one of the one's we're going to display, store the // appropriate text in pszHTLinks for (uiLoop = 0; uiLoop < uiHashTableSize; uiLoop++) { if (gv_FlmSysData.SCacheMgr.ppHashTbl[uiLoop]) { uiUsedEntries++; } if ( (uiLoop >= uiStart) && (uiLoop < (uiStart + NUM_ENTRIES)) ) { // This is one of the entries that we will display if (gv_FlmSysData.SCacheMgr.ppHashTbl[uiLoop]) { flmBuildSCacheBlockString( pszHTLinks[uiLoop - uiStart], gv_FlmSysData.SCacheMgr.ppHashTbl[uiLoop]); } } } // Unlock the database f_mutexUnlock( gv_FlmSysData.hShareMutex); // Begin rendering the page... stdHdr(); printStyle(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n"); // Determine if we are being requested to refresh this page or not. if (bRefresh) { fnPrintf( m_pHRequest, "" "" "Database iMonitor - SCache Hash Table\n", m_pszURLString, uiStart, szRefresh); } else { fnPrintf( m_pHRequest, "\n"); } // If we are not to refresh this page, then don't include the // refresh meta command if (!bRefresh) { f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString, uiStart); } else { f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString, uiStart); } // Print out a formal header and the refresh option. printTableStart("SCache Hash Table", 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, %s\n", m_pszURLString, uiStart, szRefresh, pszTemp); printColumnHeadingClose(); printTableRowEnd(); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, "\n", uiHashTableSize); printTableRowEnd(); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, "\n", uiUsedEntries, ((uiUsedEntries * 100) / uiHashTableSize) ); printTableRowEnd(); // The rest of the table is going to be a single row with two columns: // one for the list of hash buckets and the other for everything else printTableRowStart( FALSE); fnPrintf( m_pHRequest, " \n\n"); printTableRowEnd(); printTableEnd(); printDocEnd(); fnEmit(); Exit: // Free the space for the hyperlink text for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { f_free( &pszHTLinks[uiLoop]); } f_free( &pszTemp); return( rc); } /**************************************************************************** Desc: Searches the SCache for the block referenced by the parameters. If found, it copies the data into pLocalSCache. Assumes that the mutex has already been locked! ****************************************************************************/ RCODE F_SCacheBase::locateSCacheBlock( FLMUINT uiNumParams, const char ** ppszParams, SCACHE * pLocalSCache, FLMUINT * puiBlkAddress, FLMUINT * puiLowTransID, FLMUINT * puiHighTransID, FFILE * * ppFile) { RCODE rc = FERR_OK; FLMUINT uiSigBitsInBlkSize; SCACHE * pSCache; SCACHE ** ppSCache; #define MAXPARAMLEN 15 char szBlkAddress[MAXPARAMLEN]; char szLowTransID[MAXPARAMLEN]; char szHighTransID[MAXPARAMLEN]; char szFile[MAXPARAMLEN]; // Grab the block address, low and high trans id's and FFile pointer, which // we need to uniquely identify an scache block... if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "BlockAddress", sizeof( szBlkAddress), &szBlkAddress[0]))) { goto Exit; } *puiBlkAddress = f_atoi( szBlkAddress); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "LowTransID", sizeof( szLowTransID), &szLowTransID[0]))) { goto Exit; } *puiLowTransID = f_atoi( szLowTransID); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "HighTransID", sizeof( szHighTransID), &szHighTransID[0]))) { goto Exit; } *puiHighTransID = f_atoi( szHighTransID); if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "File", sizeof( szFile), &szFile[0]))) { goto Exit; } *ppFile = (FFILE *)f_atoud( szFile); flmAssert( *ppFile); uiSigBitsInBlkSize = (*ppFile)->FileHdr.uiSigBitsInBlkSize; // ScaHash actually returns a pointer to the first scache in the hash // bucket. It's up to us to traverse this list to find the proper block // address and FFile (and potentially high and low trans id) ppSCache = ScaHash( uiSigBitsInBlkSize, *puiBlkAddress); pSCache = *ppSCache; while ( pSCache && ( (pSCache->uiBlkAddress != *puiBlkAddress) || (pSCache->pFile != *ppFile) ) ) { pSCache = pSCache->pNextInHashBucket; } // Ok - we've found the right address and ffile. Do we need a different // version? while ( (pSCache) && (pSCache->uiHighTransID != *puiHighTransID) && (scaGetLowTransID( pSCache) != *puiLowTransID) ) { pSCache = pSCache->pNextInVersionList; } // Now, if we've found the right block, copy it's contents to local memory... if (pSCache) { f_memcpy( pLocalSCache, pSCache, sizeof( SCACHE)); } else { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Spits out a short message saying that the SCache block you were looking for wasn't found. Used when locateSCacheBlock() returns FERR_NOT_FOUND. ****************************************************************************/ void F_SCacheBase::notFoundErr() { stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n\n"); printStyle(); fnPrintf( m_pHRequest, "\n" "

SCache Block Not Found

" "

Unable to find the SCache Block that you requested." " This is probably because the state of the cache changed between the time" " that you displayed the previous page and the time that you clicked on the" " link that brought you here. (It's also possible that the link you selected" " was a NULL pointer.) \n" "

Your best bet is probably to click on the \"Database System Data\" link on" " the left.

\n\n"); fnEmit(); } /**************************************************************************** Desc: Spits out a short message saying that the URL for the SCache block that was requested was badly formed. Used when locateSCacheBlock() returns FERR_MEM. ****************************************************************************/ void F_SCacheBase::malformedUrlErr() { stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n\n"); printStyle(); fnPrintf( m_pHRequest, "\n" "

Bad SCache Block URL

" "

Couldn't process requested URL. Is" " the query string properly formed?.

\n\n"); fnEmit(); } /**************************************************************************** Desc: Generates the html to display a row in a table. If *ppSCache is a valid pointer, then that line will have hyperlinks in it. ****************************************************************************/ FSTATIC void flmPrintCacheLine( HRequest * pHRequest, const char * pszHREF, const char * pszName, void * pBaseAddr, SCACHE ** ppSCache) { char szAddress[20]; char szOffset[8]; PRINTF_FN fnPrintf = gv_FlmSysData.HttpConfigParms.fnPrintf; printAddress( *ppSCache, szAddress); printOffset( pBaseAddr, ppSCache, szOffset); if ((*ppSCache) && (*ppSCache)->pFile && pszHREF) { // We have a pointer to a valid SCache block and a valid HREF string, // so we need hyperlinks to appear in the browser... fnPrintf( pHRequest, TD_s TD_a_s_s " " TD_a_s_s TR_END, szOffset, pszHREF, pszName, pszHREF, szAddress); } else { fnPrintf( pHRequest, TD_s TD_s " " TD_s TR_END, szOffset, pszName, szAddress ); } } /**************************************************************************** Desc: Determines the values of the parameters needed to reference a specific SCache block. Must be called from within a mutex ****************************************************************************/ FSTATIC void flmBuildSCacheBlockString( char * pszString, SCACHE * pSCache) { char szAddress[ 20]; if ((pSCache == NULL) || (pSCache->pFile == NULL)) { pszString[0] = 0; } else { printAddress( pSCache->pFile, szAddress); f_sprintf( (char *)pszString, "%s/SCacheBlock?BlockAddress=%lu&File=%s&LowTransID=%lu&HighTransID=%lu", gv_FlmSysData.HttpConfigParms.pszURLString, pSCache->uiBlkAddress, szAddress, scaGetLowTransID( pSCache), pSCache->uiHighTransID); } return; } libflaim-4.9.966/src/fscursor.cpp0000644000175000017500000017512010510774540020234 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines used during query to traverse through index b-trees. // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscursor.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC FLMINT FSCompareKeys( FLMBOOL bKey1IsUntilKey, FLMBYTE * pKey1, FLMUINT uiKeyLen1, FLMBOOL bExclusiveKey1, FLMBOOL bKey2IsUntilKey, FLMBYTE * pKey2, FLMUINT uiKeyLen2, FLMBOOL bExclusiveKey2); #define FS_COMPARE_KEYS( bKey1IsUntilKey,pKeyPos1,bKey2IsUntilKey,pKeyPos2) \ FSCompareKeys( (bKey1IsUntilKey), \ (pKeyPos1)->pKey, (pKeyPos1)->uiKeyLen, (pKeyPos1)->bExclusiveKey, \ (bKey2IsUntilKey), \ (pKeyPos2)->pKey, (pKeyPos2)->uiKeyLen, (pKeyPos2)->bExclusiveKey) /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::FSIndexCursor() { m_pFirstSet = m_pCurSet = NULL; m_pSavedPos = NULL; m_curKeyPos.bStackInUse = FALSE; reset(); } /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::~FSIndexCursor() { releaseBlocks(); freeSets(); } /**************************************************************************** Desc: Resets any allocations, keys, state, etc. ****************************************************************************/ void FSIndexCursor::reset( void) { releaseBlocks(); freeSets(); m_uiIndexNum = m_uiCurrTransId = m_uiBlkChangeCnt = 0; m_pFirstSet = m_pCurSet = NULL; m_pSavedPos = NULL; m_curKeyPos.bExclusiveKey = FALSE; m_DefaultSet.pNext = m_DefaultSet.pPrev = NULL; m_DefaultSet.fromKey.bStackInUse = m_DefaultSet.untilKey.bStackInUse = FALSE; m_bAtBOF = TRUE; m_bAtEOF = FALSE; } /**************************************************************************** Desc: Resets to a new transaction that may change the read consistency of the query. This is usually an old view error internal or external of this class. ****************************************************************************/ RCODE FSIndexCursor::resetTransaction( FDB * pDb) { RCODE rc = FERR_OK; KEYSET * pTmpSet; if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, m_uiIndexNum, &m_pLFile, &m_pIxd))) { goto Exit; } m_uiCurrTransId = pDb->LogHdr.uiCurrTransID; m_uiBlkChangeCnt = pDb->uiBlkChangeCnt; m_bIsUpdateTrans = (pDb->uiTransType == FLM_UPDATE_TRANS) ? TRUE : FALSE; // Need to release all stacks that are currently in use. for( pTmpSet = m_pFirstSet; pTmpSet; pTmpSet = pTmpSet->pNext) { releaseKeyBlocks( &pTmpSet->fromKey); releaseKeyBlocks( &pTmpSet->untilKey); } releaseKeyBlocks( &m_DefaultSet.fromKey); releaseKeyBlocks( &m_DefaultSet.untilKey); if( m_pSavedPos) { releaseKeyBlocks( m_pSavedPos); } releaseKeyBlocks( &m_curKeyPos); Exit: return( rc); } /**************************************************************************** Desc: Free all of the allocated sets. ****************************************************************************/ void FSIndexCursor::freeSets( void) { KEYSET * pCurSet; // Current set. KEYSET * pNextSet; // Current set. for( pCurSet = m_pFirstSet; pCurSet; pCurSet = pNextSet) { pNextSet = pCurSet->pNext; if( pCurSet != &m_DefaultSet) { f_free( &pCurSet); } } m_pFirstSet = m_pCurSet = NULL; if( m_pSavedPos) { releaseKeyBlocks( m_pSavedPos); f_free( &m_pSavedPos); m_pSavedPos = NULL; } } /**************************************************************************** Desc: Releases the cache blocks back to cache. ****************************************************************************/ void FSIndexCursor::releaseBlocks( void) { KEYSET * pCurSet; // Unuse all of the cache blocks in the from and until keys. for( pCurSet = m_pFirstSet; pCurSet; pCurSet = pCurSet->pNext) { releaseKeyBlocks( &pCurSet->fromKey); releaseKeyBlocks( &pCurSet->untilKey); } releaseKeyBlocks( &m_curKeyPos); } /**************************************************************************** Desc: Setup the from and until keys in the cursor. Return counts after positioning to the from and until key in the index. This code does not work with multiple key sets of FROM/UNTIL keys. ****************************************************************************/ RCODE FSIndexCursor::setupKeys( FDB * pDb, IXD * pIxd, QPREDICATE ** ppQPredicateList, FLMBOOL * pbDoRecMatch, // [out] Leave alone or set to TRUE. FLMBOOL * pbDoKeyMatch, // [out] Set to TRUE or FALSE FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks FLMUINT * puiTotalKeys, // [out] total number of keys FLMUINT * puiTotalRefs, // [out] total references FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. { RCODE rc = FERR_OK; FLMUINT uiUntilKeyLen; FLMBYTE pUntilKey [MAX_KEY_SIZ + 4]; DIN_STATE dinState; RESET_DINSTATE( dinState); m_uiIndexNum = pIxd->uiIndexNum; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_DefaultSet.fromKey.uiRefPosition = 0; m_DefaultSet.untilKey.uiRefPosition = 0; m_DefaultSet.fromKey.bExclusiveKey = FALSE; m_DefaultSet.untilKey.bExclusiveKey = TRUE; if( RC_BAD( rc = flmBuildFromAndUntilKeys( pIxd, ppQPredicateList, m_DefaultSet.fromKey.pKey, &m_DefaultSet.fromKey.uiKeyLen, m_DefaultSet.untilKey.pKey, &m_DefaultSet.untilKey.uiKeyLen, pbDoRecMatch, pbDoKeyMatch, &m_DefaultSet.untilKey.bExclusiveKey))) { goto Exit; } // Here is a rundown of how the data is setup after this call. // Default.FROM key - block address and current element offset and // generated FROM key. // Default.UNTIL key - full stack setup and the generated UNTIL key // curKeyPos - pKey is the first key positioned the full stack is setup. f_memcpy( m_curKeyPos.pKey, m_DefaultSet.fromKey.pKey, m_curKeyPos.uiKeyLen = m_DefaultSet.fromKey.uiKeyLen); f_memcpy( pUntilKey, m_DefaultSet.untilKey.pKey, uiUntilKeyLen = m_DefaultSet.untilKey.uiKeyLen); m_pCurSet = m_pFirstSet = &m_DefaultSet; m_DefaultSet.fromKey.uiRecordId = m_curKeyPos.uiRecordId = 0; m_DefaultSet.fromKey.uiDomain = m_curKeyPos.uiDomain = MAX_DOMAIN; m_DefaultSet.untilKey.uiRecordId = 0; m_DefaultSet.untilKey.uiDomain = ZERO_DOMAIN; // Want any of the counts back? if( puiLeafBlocksBetween || puiTotalKeys || puiTotalRefs) { if( RC_OK( rc = setKeyPosition( pDb, TRUE, &m_DefaultSet.fromKey, &m_curKeyPos))) { // Copy the b-tree information to the from key. m_DefaultSet.fromKey.uiBlockAddr = m_curKeyPos.uiBlockAddr; m_DefaultSet.fromKey.uiDomain = m_curKeyPos.uiDomain; m_DefaultSet.fromKey.uiBlockTransId = m_curKeyPos.uiBlockTransId; m_DefaultSet.fromKey.uiCurElm = m_curKeyPos.uiCurElm; // All keys bewteen FROM and UNTIL may be gone. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_DefaultSet.untilKey) <= 0) { rc = setKeyPosition( pDb, TRUE, &m_DefaultSet.untilKey, &m_DefaultSet.untilKey); rc = (rc == FERR_EOF_HIT) ? FERR_OK: rc; // Restore the original UNTIL key - throws away what the last key is. f_memcpy( m_DefaultSet.untilKey.pKey, pUntilKey, m_DefaultSet.untilKey.uiKeyLen = uiUntilKeyLen); } else { rc = RC_SET( FERR_BOF_HIT); } } else { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } m_bAtBOF = FALSE; } if( RC_BAD( rc)) { // Empty tree or empty set case. if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { if( puiLeafBlocksBetween) *puiLeafBlocksBetween = 0; if( puiTotalKeys) *puiTotalKeys = 0; if( puiTotalRefs) *puiTotalRefs = 0; if( pbTotalsEstimated) *pbTotalsEstimated = FALSE; rc = FERR_OK; } goto Exit; } else { // If this is a positioning index, set the ref positions. if( pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &dinState, &m_DefaultSet.fromKey.uiRefPosition))) { goto Exit; } if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_DefaultSet.untilKey.pStack, &dinState, &m_DefaultSet.untilKey.uiRefPosition))) { goto Exit; } } if( RC_BAD( rc = FSComputeIndexCounts( m_curKeyPos.pStack, m_DefaultSet.untilKey.pStack, puiLeafBlocksBetween, puiTotalKeys, puiTotalRefs, pbTotalsEstimated))) { goto Exit; } } } m_bAtBOF = TRUE; Exit: return( rc); } /**************************************************************************** Public: setupKeys Desc: Setup a cursor with just a single FROM/UNTIL key. ****************************************************************************/ RCODE FSIndexCursor::setupKeys( FDB * pDb, IXD * pIxd, FLMBYTE * pFromKey, FLMUINT uiFromKeyLen, FLMUINT uiFromRecordId, FLMBYTE * pUntilKey, FLMUINT uiUntilKeyLen, FLMUINT uiUntilRecordId, FLMBOOL bExclusiveUntil) { RCODE rc; m_uiIndexNum = pIxd->uiIndexNum; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_DefaultSet.pNext = m_DefaultSet.pPrev = NULL; // FROM key m_DefaultSet.fromKey.uiRecordId = uiFromRecordId; m_DefaultSet.fromKey.uiDomain = uiFromRecordId ? DRN_DOMAIN( uiFromRecordId) + 1: MAX_DOMAIN; m_DefaultSet.fromKey.uiKeyLen = uiFromKeyLen; f_memcpy( m_DefaultSet.fromKey.pKey, pFromKey, uiFromKeyLen); m_DefaultSet.fromKey.bExclusiveKey = FALSE; // UNTIL key m_DefaultSet.untilKey.uiRecordId = uiUntilRecordId; m_DefaultSet.untilKey.uiDomain = uiUntilRecordId ? DRN_DOMAIN( uiUntilRecordId) + 1: ZERO_DOMAIN; m_DefaultSet.untilKey.uiKeyLen = uiUntilKeyLen; f_memcpy( m_DefaultSet.untilKey.pKey, pUntilKey, uiUntilKeyLen); m_DefaultSet.untilKey.bExclusiveKey = bExclusiveUntil; m_pFirstSet = &m_DefaultSet; m_bAtBOF = TRUE; m_pCurSet = NULL; // If this is a positioning index we need to setup the FROM/UNTIL // btrees to get the FROM/UNTIL reference positions. if( pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = setupForPositioning( pDb))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Read down all of the b-tree sets (FROM/UNTIL) so the all absolute positioning values can be set. ****************************************************************************/ RCODE FSIndexCursor::setupForPositioning( FDB * pDb) { RCODE rc = FERR_OK; DIN_STATE dinState; FLMUINT uiTempKeyLen; FLMBYTE pTempKey [MAX_KEY_SIZ + 4]; KEYSET * pSrcSet; // Current source set // Read all of the FROM/UNTIL keys going from the last set to the first. // This way m_curKeyPos will be positioned to the first key. for( pSrcSet = m_pFirstSet; pSrcSet->pNext; pSrcSet = pSrcSet->pNext) { ; } for( ; pSrcSet; pSrcSet = pSrcSet->pPrev) { f_memcpy( pTempKey, pSrcSet->untilKey.pKey, uiTempKeyLen = pSrcSet->untilKey.uiKeyLen); if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->untilKey, &pSrcSet->untilKey))) { goto Exit; } // Copy the key back. f_memcpy( pSrcSet->untilKey.pKey, pTempKey, pSrcSet->untilKey.uiKeyLen = uiTempKeyLen); if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, pSrcSet->untilKey.pStack, &dinState, &pSrcSet->untilKey.uiRefPosition))) { goto Exit; } } if( pSrcSet == m_pFirstSet) { if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->fromKey, &m_curKeyPos))) { goto Exit; } m_pCurSet = m_pFirstSet; // Copy the b-tree information to the from key. m_pCurSet->fromKey.uiBlockAddr = m_curKeyPos.uiBlockAddr; m_pCurSet->fromKey.uiDomain = m_curKeyPos.uiDomain; m_pCurSet->fromKey.uiBlockTransId = m_curKeyPos.uiBlockTransId; m_pCurSet->fromKey.uiCurElm = m_curKeyPos.uiCurElm; if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &dinState, &pSrcSet->fromKey.uiRefPosition))) { goto Exit; } } } else { f_memcpy( pTempKey, pSrcSet->fromKey.pKey, uiTempKeyLen = pSrcSet->fromKey.uiKeyLen); if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &pSrcSet->fromKey, &pSrcSet->fromKey))) { goto Exit; } // Copy the key back. f_memcpy( pSrcSet->fromKey.pKey, pTempKey, pSrcSet->fromKey.uiKeyLen = uiTempKeyLen); if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, pSrcSet->fromKey.pStack, &dinState, &pSrcSet->fromKey.uiRefPosition))) { goto Exit; } } } } Exit: return( rc); } /**************************************************************************** Desc: Merge the input cursors fromUntil sets as a result of a UNION. ****************************************************************************/ RCODE FSIndexCursor::unionKeys( FSIndexCursor * pFSCursor) { RCODE rc = FERR_OK; KEYSET * pInputSet; // Sets input via pFSCursor. KEYSET * pSrcSet; // Current set KEYSET * pDestSet = NULL; // Newly allocated set KEYSET * pCurDestSet = NULL; // New current set KEYSET * pPrevDestSet; // We need to release all of the blocks. Too complex pFSCursor->releaseBlocks(); releaseBlocks(); pInputSet = pFSCursor->getFromUntilSets(); pSrcSet = m_pFirstSet; while( pSrcSet || pInputSet) { FLMBOOL bFromKeyLessThan; FLMBOOL bUntilKeyGreaterThan; pPrevDestSet = pCurDestSet; if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pCurDestSet))) { goto Exit; } if( !pSrcSet) { f_memcpy( pCurDestSet, pInputSet, sizeof( KEYSET)); pInputSet = pInputSet->pNext; } else if( !pInputSet) { f_memcpy( pCurDestSet, pSrcSet, sizeof( KEYSET)); pSrcSet = pSrcSet->pNext; } else if( !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { if( bFromKeyLessThan) { f_memcpy( pCurDestSet, pInputSet, sizeof( KEYSET)); pInputSet = pInputSet->pNext; } else { f_memcpy( pCurDestSet, pSrcSet, sizeof( KEYSET)); pSrcSet = pSrcSet->pNext; } } else { f_memcpy( &pCurDestSet->fromKey, bFromKeyLessThan ? &pInputSet->fromKey : &pSrcSet->fromKey, sizeof( KEYPOS)); for(;;) { // Keys overlap - take the lowest FROM key and highest UNTIL key // walking through the lower UNTIL key set while the keys overlap. if( bUntilKeyGreaterThan) { if( ((pSrcSet = pSrcSet->pNext) == NULL) || !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { f_memcpy( &pCurDestSet->untilKey, &pInputSet->untilKey, sizeof( KEYPOS)); pInputSet = pInputSet->pNext; break; } } else { if( ((pInputSet = pInputSet->pNext) == NULL) || !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { f_memcpy( &pCurDestSet->untilKey, &pSrcSet->untilKey, sizeof( KEYPOS)); pSrcSet = pSrcSet->pNext; break; } } } } // Link in. pCurDestSet->pNext = NULL; if( !pDestSet) { pDestSet = pCurDestSet; pCurDestSet->pPrev = NULL; } else { pPrevDestSet->pNext = pCurDestSet; pCurDestSet->pPrev = pPrevDestSet; } } // We went to the trouble of having a default set allocated with this class. // Undo the last allocation. if( pDestSet) then pCurDestSet can be used. freeSets(); if( pDestSet) { f_memcpy( &m_DefaultSet, pCurDestSet, sizeof( KEYSET)); if( pCurDestSet->pPrev) { pCurDestSet->pPrev->pNext = &m_DefaultSet; m_pFirstSet = pDestSet; } else { m_pFirstSet = &m_DefaultSet; } f_free( &pCurDestSet); } m_bAtBOF = TRUE; m_pCurSet = NULL; Exit: return( rc); } /**************************************************************************** Desc: Intersect the from/until key sets of pFSCursor into 'this'. ****************************************************************************/ RCODE FSIndexCursor::intersectKeys( FDB * pDb, FSIndexCursor * pFSCursor) { RCODE rc = FERR_OK; KEYSET * pInputSet; // Sets input via pFSCursor. KEYSET * pSrcSet; // Current set KEYSET * pDestSet = NULL; // Newly allocated set KEYSET * pCurDestSet = NULL; // New current set KEYSET * pPrevDestSet; // Create a new destiniation set and throw away the current set. pFSCursor->releaseBlocks(); releaseBlocks(); pInputSet = pFSCursor->getFromUntilSets(); pSrcSet = m_pFirstSet; while( pSrcSet && pInputSet) { FLMBOOL bFromKeyLessThan; FLMBOOL bUntilKeyGreaterThan; if( !FSCompareKeyPos( pInputSet, pSrcSet, &bFromKeyLessThan, &bUntilKeyGreaterThan)) { // Keys do NOT overlap - go to the next set section. if( bFromKeyLessThan) { pInputSet = pInputSet->pNext; } else { pSrcSet = pSrcSet->pNext; } } else { // Values overlap - see the two boolean values on how they overlap. pPrevDestSet = pCurDestSet; if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pCurDestSet))) { goto Exit; } pCurDestSet->pNext = NULL; if( !pDestSet) { pDestSet = pCurDestSet; pCurDestSet->pPrev = NULL; } else { pCurDestSet->pPrev = pPrevDestSet; pPrevDestSet->pNext = pCurDestSet; } // Take the highest FROM key. f_memcpy( &pCurDestSet->fromKey, bFromKeyLessThan ? &pSrcSet->fromKey : &pInputSet->fromKey, sizeof( KEYPOS)); // Take the lowest until key and position to the next set. if( bUntilKeyGreaterThan) { f_memcpy( &pCurDestSet->untilKey, &pSrcSet->untilKey, sizeof( KEYPOS)); pSrcSet = pSrcSet->pNext; } else { f_memcpy( &pCurDestSet->untilKey, &pInputSet->untilKey, sizeof( KEYPOS)); pInputSet = pInputSet->pNext; } } } // We went to the trouble of having a default set allocated with this class. // Undo the last allocation. if( pDestSet) then pCurDestSet can be used. freeSets(); if( pDestSet) { f_memcpy( &m_DefaultSet, pCurDestSet, sizeof( KEYSET)); if( pCurDestSet->pPrev) { pCurDestSet->pPrev->pNext = &m_DefaultSet; m_pFirstSet = pDestSet; } else { m_pFirstSet = &m_DefaultSet; } f_free( &pCurDestSet); } m_bAtBOF = TRUE; m_pCurSet = NULL; // If this is a positioning index we need to setup the FROM/UNTIL // btrees to get the FROM/UNTIL reference positions. if( m_pIxd->uiFlags & IXD_POSITIONING) { if( RC_BAD( rc = setupForPositioning( pDb))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Specific compare for a key range against the key ranges in this cursor. ****************************************************************************/ FLMBOOL FSIndexCursor::compareKeyRange( // Returns TRUE if keys overlap. FLMBYTE * pFromKey, FLMUINT uiFromKeyLen, FLMBOOL bExclusiveFrom, FLMBYTE * pUntilKey, FLMUINT uiUntilKeyLen, FLMBOOL bExclusiveUntil, FLMBOOL * pbUntilKeyInSet, // Truth Table: T F F FLMBOOL * pbUntilGreaterThan) // F T F { FLMBOOL bKeysOverlap = FALSE; KEYSET * pSrcSet; // Current source set FLMINT iFromCmp; FLMINT iUntilCmp; FLMINT iFromUntilCmp; FLMINT iUntilFromCmp; for( pSrcSet = m_pFirstSet; pSrcSet; pSrcSet = pSrcSet->pNext) { iFromCmp = FSCompareKeys( FALSE, pFromKey, uiFromKeyLen, bExclusiveFrom, FALSE, pSrcSet->fromKey.pKey, pSrcSet->fromKey.uiKeyLen, pSrcSet->fromKey.bExclusiveKey); if( iFromCmp < 0) { // Move from left-most to right-most to see where the overlap is. iUntilFromCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveUntil, FALSE, pSrcSet->fromKey.pKey, pSrcSet->fromKey.uiKeyLen, pSrcSet->fromKey.bExclusiveKey); if( iUntilFromCmp < 0) { *pbUntilKeyInSet = FALSE; *pbUntilGreaterThan = FALSE; goto Exit; } if( iUntilFromCmp == 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } // UNTIL > pSrcSet->fromKey iUntilCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveUntil, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iUntilCmp <= 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } bKeysOverlap = TRUE; // Try the next source set to see if the UNTIL key is in a set. } else { // Move from left-most to right-most to see where the overlap is. if( iFromCmp == 0) { bKeysOverlap = TRUE; } else { iFromUntilCmp = FSCompareKeys( FALSE, pFromKey, uiFromKeyLen, bExclusiveFrom, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iFromUntilCmp <= 0) { bKeysOverlap = TRUE; } else { continue; } } iUntilCmp = FSCompareKeys( TRUE, pUntilKey, uiUntilKeyLen, bExclusiveFrom, TRUE, pSrcSet->untilKey.pKey, pSrcSet->untilKey.uiKeyLen, pSrcSet->untilKey.bExclusiveKey); if( iUntilCmp <= 0) { bKeysOverlap = TRUE; *pbUntilKeyInSet = TRUE; *pbUntilGreaterThan = FALSE; goto Exit; } } } *pbUntilKeyInSet = FALSE; *pbUntilGreaterThan = TRUE; Exit: return bKeysOverlap; } /**************************************************************************** Desc: Compare two From/Until key positions. ****************************************************************************/ FLMBOOL FSIndexCursor::FSCompareKeyPos( // TRUE if keys overlap KEYSET * pSet1, KEYSET * pSet2, FLMBOOL * pbFromKeyLessThan, // pSet1->from < pSet2->from FLMBOOL * pbUntilKeyGreaterThan) // pSet1->until > pSet2->until { if( FS_COMPARE_KEYS( TRUE, &pSet1->untilKey, FALSE, &pSet2->fromKey) < 0) { *pbFromKeyLessThan = TRUE; pbUntilKeyGreaterThan = FALSE; return FALSE; } if( FS_COMPARE_KEYS( FALSE, &pSet1->fromKey, TRUE, &pSet2->untilKey) > 0) { *pbFromKeyLessThan = FALSE; *pbUntilKeyGreaterThan = TRUE; return FALSE; } // Keys overlap. Two more compares needed *pbFromKeyLessThan = (FLMBOOL) ((FS_COMPARE_KEYS( FALSE, &pSet1->fromKey, FALSE, &pSet2->fromKey) < 0) ? TRUE : FALSE); *pbUntilKeyGreaterThan = (FLMBOOL) ((FS_COMPARE_KEYS( TRUE, &pSet1->untilKey, TRUE, &pSet2->untilKey) > 0) ? TRUE : FALSE); return (TRUE); } /**************************************************************************** Desc: Set information from the block into the KEYPOS structure. ****************************************************************************/ FINLINE void setKeyItemsFromBlock( KEYPOS * pKeyPos) { pKeyPos->uiBlockAddr = pKeyPos->pStack->uiBlkAddr; pKeyPos->uiCurElm = pKeyPos->pStack->uiCurElm; pKeyPos->uiKeyLen = pKeyPos->pStack->uiKeyLen; pKeyPos->uiBlockTransId = (pKeyPos->uiBlockAddr != BT_END) ? FB2UD( &pKeyPos->pStack->pBlk[ BH_TRANS_ID]) : 0; } /**************************************************************************** Desc: Set the key position given some KEYPOS structure. Please note that the blocks in the stack may or may not be used. ****************************************************************************/ RCODE FSIndexCursor::setKeyPosition( FDB * pDb, FLMBOOL bGoingForward, KEYPOS * pInKeyPos, // Input key position KEYPOS * pOutKeyPos) // Output to setup the stack/key buffer. // It is ok for Input==Output { RCODE rc; FLMBYTE * pSearchKey; FLMBYTE pTempKey[ MAX_KEY_SIZ + 4]; FLMUINT uiTargetRecordId = pInKeyPos->uiRecordId; // May have to unuse the b-tree blocks. Then setup the stack. if( !pOutKeyPos->bStackInUse) { FSInitStackCache( pOutKeyPos->Stack, BH_MAX_LEVELS); pOutKeyPos->bStackInUse = TRUE; } if( pInKeyPos == pOutKeyPos) { pSearchKey = pTempKey; f_memcpy( pTempKey, pInKeyPos->pKey, pInKeyPos->uiKeyLen); } else { pSearchKey = pInKeyPos->pKey; } // Setup the stack. pOutKeyPos->pStack = pOutKeyPos->Stack; pOutKeyPos->Stack[0].pKeyBuf = pOutKeyPos->pKey; // All of the variables should be setup for the search. if( RC_BAD( rc = FSBtSearch( pDb, m_pLFile, &pOutKeyPos->pStack, pSearchKey, pInKeyPos->uiKeyLen, uiTargetRecordId ? DRN_DOMAIN( uiTargetRecordId) + 1 : pInKeyPos->uiDomain))) { goto Exit; } pOutKeyPos->uiBlockAddr = pOutKeyPos->pStack->uiBlkAddr; pOutKeyPos->uiCurElm = pOutKeyPos->pStack->uiCurElm; if( pOutKeyPos->pStack->uiBlkAddr == BT_END) { pOutKeyPos->bStackInUse = FALSE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } pOutKeyPos->uiKeyLen = pOutKeyPos->pStack->uiKeyLen; if( bGoingForward) { if( pOutKeyPos->pStack->uiCmpStatus == BT_END_OF_DATA) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } } else { BTSK * pStack; // Going backwards or to the last. May have positioned too far. if( pOutKeyPos->pStack->uiCmpStatus == BT_END_OF_DATA || FS_COMPARE_KEYS( TRUE, pOutKeyPos, TRUE, pInKeyPos) > 0) { uiTargetRecordId = 0; pStack = pOutKeyPos->pStack; // The stack should be set up and is pointing to a valid block. if( pStack->uiCmpStatus != BT_END_OF_DATA) { while( BBE_NOT_FIRST( CURRENT_ELM( pStack))) { if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } } } // Position to the last continuation element of the previous element. if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } } // When using the positioning index, we must always have a complete stack. if( !(m_pIxd->uiFlags & IXD_POSITIONING)) { pOutKeyPos->pStack->uiFlags = NO_STACK; } // Get the record ID at the leaf level of the b-tree. if( uiTargetRecordId) { pOutKeyPos->uiRecordId = pInKeyPos->uiRecordId; rc = FSRefSearch( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiRecordId); if( rc == FERR_FAILURE) { rc = FERR_OK; } } else { if( bGoingForward) { pOutKeyPos->uiRecordId = FSRefFirst( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiDomain); pOutKeyPos->uiDomain++; } else { pOutKeyPos->uiRecordId = FSRefLast( pOutKeyPos->pStack, &pOutKeyPos->DinState, &pOutKeyPos->uiDomain); } } Exit: // Save state only on a good return value. if( RC_OK( rc) || ((rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) && pOutKeyPos->bStackInUse)) { setKeyItemsFromBlock( pOutKeyPos); } else { releaseKeyBlocks( pOutKeyPos); } return( rc); } /**************************************************************************** Desc: Return the current record and record id. VISIT: We may want to return BOF/EOF when positioned on an endpoint. ****************************************************************************/ RCODE FSIndexCursor::currentKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // The current key is gone. Returns FERR_NOT_FOUND. goto Exit; } } // If this assert happens we have to code for it. flmAssert( m_curKeyPos.uiRecordId != 0); if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Return the current record and record id. VISIT: We may want to return BOF/EOF when positioned on an endpoint. ****************************************************************************/ RCODE FSIndexCursor::currentKeyBuf( FDB * pDb, F_Pool * pPool, FLMBYTE ** ppKeyBuf, FLMUINT * puiKeyLen, FLMUINT * puiRecordId, FLMUINT * puiContainerId) { RCODE rc = FERR_OK; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // The current key is gone. Returns FERR_NOT_FOUND. goto Exit; } } // If this assert happens we have to code for it. flmAssert( m_curKeyPos.uiRecordId != 0); if( ppKeyBuf) { // If they passed in a non-null key buffer pointer, they also // need to pass in a non-null return length. flmAssert( puiKeyLen != NULL); if( (*puiKeyLen = m_curKeyPos.uiKeyLen) != 0) { if( RC_BAD( rc = pPool->poolAlloc( m_curKeyPos.uiKeyLen, (void **)ppKeyBuf))) { goto Exit; } f_memcpy( *ppKeyBuf, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); } else { *ppKeyBuf = NULL; } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if (puiContainerId) { if ((*puiContainerId = m_pIxd->uiContainerNum) == 0) { flmAssert( m_curKeyPos.uiKeyLen > getIxContainerPartLen( m_pIxd)); *puiContainerId = getContainerFromKey( m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); } } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the first record. This is hard because positioning using the first key may actually position past or into another FROM/UNTIL set in the list. ****************************************************************************/ RCODE FSIndexCursor::firstKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( !m_pFirstSet) { m_bAtBOF = FALSE; m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } // If at BOF and stack is in use, the key is the first key! // This should only happen when firstKey is called the first time. if( m_bAtBOF && m_curKeyPos.bStackInUse && m_pCurSet) { m_bAtBOF = FALSE; } else { m_pCurSet = m_pFirstSet; m_bAtBOF = m_bAtEOF = FALSE; if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } // Check to see if the key is within one of the FROM/UNTIL sets. for(;;) { // Check to see if the current key <= UNTIL key. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->untilKey) <= 0) { break; } // Nope - UntilKey < current key. if( !m_pCurSet->pNext) { rc = RC_SET( FERR_EOF_HIT); m_bAtEOF = TRUE; goto Exit; } m_pCurSet = m_pCurSet->pNext; // If (current key < FROM key of the next set) we need to reposition. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey) < 0) { if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( m_bAtEOF) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the next key and the first reference of that key. ****************************************************************************/ RCODE FSIndexCursor::nextKey( FDB * pDb, FlmRecord ** ppRecordKey, FLMUINT * puiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; BTSK * pStack; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtEOF) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } if( m_bAtBOF) { rc = firstKey( pDb, ppRecordKey, puiRecordId); goto Exit; } bKeyGone = bRefGone = FALSE; // Takes care of any re-read of a block if we changed transactions. if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, TRUE, FALSE, &bKeyGone, TRUE, FALSE, &bRefGone))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } // May return FERR_EOF_HIT if all remaining keys are deleted. goto Exit; } } pStack = m_curKeyPos.pStack; for(;;) { FLMINT iCmp; if( !bKeyGone) { FLMBYTE * pCurElm; pCurElm = CURRENT_ELM( pStack); while( BBE_NOT_LAST( pCurElm)) { if( RC_BAD( rc = FSBtNextElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } pCurElm = CURRENT_ELM( pStack); } // Now go to the next element. if( RC_BAD( rc = FSBtNextElm( pDb, m_pLFile, pStack))) { if( rc == FERR_BT_END_OF_DATA) { m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); } goto Exit; } bKeyGone = TRUE; m_curKeyPos.uiKeyLen = m_curKeyPos.pStack->uiKeyLen; } // Could have positioned after the current sets until key before // the FROM key of the next set. iCmp = FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->untilKey); if( iCmp <= 0) { setKeyItemsFromBlock( &m_curKeyPos); m_curKeyPos.uiRecordId = FSRefFirst( pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); break; } // Go to the next set if there is one. if( !m_pCurSet->pNext) { m_bAtEOF = TRUE; rc = RC_SET( FERR_EOF_HIT); goto Exit; } m_pCurSet = m_pCurSet->pNext; // The key may fit in the next set. iCmp = FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey); if( iCmp < 0) { // Reposition using the from key. if( RC_BAD( rc = setKeyPosition( pDb, TRUE, &m_pCurSet->fromKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( m_bAtEOF) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the next referece of the current key. ****************************************************************************/ RCODE FSIndexCursor::nextRef( // FERR_OK, FERR_EOF_HIT or error FDB * pDb, FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_pCurSet != NULL); bKeyGone = bRefGone = FALSE; if( !m_curKeyPos.bStackInUse) { // Take care of any re-read of a block if we changed transactions. if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, TRUE, FALSE, &bRefGone))) { // May return FERR_EOF_HIT if all remaining references are deleted. goto Exit; } flmAssert( !bKeyGone); } if( !bRefGone) { if( RC_BAD( rc = FSRefNext( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_EOF_HIT); } goto Exit; } else { setKeyItemsFromBlock( &m_curKeyPos); } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the last record. This is hard because positioning using the first key may actually position past or into another FROM/UNTIL set in the list. ****************************************************************************/ RCODE FSIndexCursor::lastKey( FDB * pDb, FlmRecord ** ppRecordKey, // Will replace what is there FLMUINT * puiRecordId) // Set the record ID { RCODE rc; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_pCurSet = m_pFirstSet; if( !m_pCurSet) { m_bAtBOF = TRUE; m_bAtEOF = FALSE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } // Position to the last set. while( m_pCurSet->pNext) { m_pCurSet = m_pCurSet->pNext; } m_bAtBOF = m_bAtEOF = FALSE; for(;;) { if( RC_BAD( rc = setKeyPosition( pDb, FALSE, &m_pCurSet->untilKey, &m_curKeyPos))) { // Returns an error or FERR_EOF_HIT. if( rc != FERR_EOF_HIT) { goto Exit; } // Position to the previous key if possible. if( m_curKeyPos.pStack->uiBlkAddr == BT_END) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } // Went past the until key. Go back one. if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, m_curKeyPos.pStack))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } // We may have positioned before the FROM key of this set. if( FS_COMPARE_KEYS( FALSE, &m_pCurSet->fromKey, FALSE, &m_curKeyPos) <= 0) { break; } if( !m_pCurSet->pPrev) { rc = RC_SET( FERR_BOF_HIT); m_bAtBOF = TRUE; goto Exit; } m_pCurSet = m_pCurSet->pPrev; } // Now we are positioned somewhere. Return the key and first record id. m_curKeyPos.uiRecordId = FSRefLast( m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); setKeyItemsFromBlock( &m_curKeyPos); if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( rc == FERR_BOF_HIT) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the PREVIOUS key and the LAST reference of that key. ****************************************************************************/ RCODE FSIndexCursor::prevKey( FDB * pDb, FlmRecord ** ppRecordKey, FLMUINT * puiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; BTSK * pStack = m_curKeyPos.pStack; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { rc = RC_SET( FERR_BOF_HIT); goto Exit; } if( !m_pCurSet || m_bAtEOF) { rc = lastKey( pDb, ppRecordKey, puiRecordId); goto Exit; } bKeyGone = bRefGone = FALSE; // Takes care of any re-read of a block if we changed transactions. if( !m_curKeyPos.bStackInUse) { if( RC_BAD( rc = reposition( pDb, FALSE, TRUE, &bKeyGone, FALSE, FALSE, &bRefGone))) { // May return FERR_EOF_HIT if all remaining keys are deleted. if( rc != FERR_BOF_HIT && rc != FERR_EOF_HIT) { goto Exit; } m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } } for(;;) { if( !bKeyGone) { FLMBYTE * pCurElm; pCurElm = CURRENT_ELM( pStack); while( BBE_NOT_FIRST( pCurElm)) { if( RC_BAD( rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BTREE_ERROR); } goto Exit; } pCurElm = CURRENT_ELM( pStack); } // Now go to the previous element. if( RC_BAD(rc = FSBtPrevElm( pDb, m_pLFile, pStack))) { // b-tree corrupt if FERR_BT_END_OF_DATA if( rc == FERR_BT_END_OF_DATA) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } goto Exit; } bKeyGone = TRUE; m_curKeyPos.uiKeyLen = m_curKeyPos.pStack->uiKeyLen; } // Could have positioned after the current sets until key before // the FROM key of the next set. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, TRUE, &m_pCurSet->fromKey) >= 0) { setKeyItemsFromBlock( &m_curKeyPos); m_curKeyPos.uiRecordId = FSRefLast( pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiDomain); break; } // Go to the previous set if there is one. if( !m_pCurSet->pPrev) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); goto Exit; } m_pCurSet = m_pCurSet->pPrev; // The key may fit in the previous set. if( FS_COMPARE_KEYS( FALSE, &m_curKeyPos, FALSE, &m_pCurSet->fromKey) > 0) { // Reposition using the from key. if( RC_BAD( rc = setKeyPosition( pDb, FALSE, &m_pCurSet->untilKey, &m_curKeyPos))) { if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { m_bAtBOF = TRUE; rc = RC_SET( FERR_BOF_HIT); } goto Exit; } } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } if( ppRecordKey) { if( RC_BAD( rc = flmIxKeyOutput( m_pIxd, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen, ppRecordKey, TRUE))) { goto Exit; } (*ppRecordKey)->setID( m_curKeyPos.uiRecordId); } Exit: if( rc == FERR_BOF_HIT) { // The saved state is not pointing anywhere specific in the B-tree. releaseKeyBlocks( &m_curKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the previous referece of the current key. ****************************************************************************/ RCODE FSIndexCursor::prevRef( FDB * pDb, FLMUINT * puiRecordId) // Set the record ID { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; flmAssert( m_pCurSet != NULL); if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } bKeyGone = bRefGone = FALSE; if( !m_curKeyPos.bStackInUse) { // Take care of any re-read of a block if we changed transactions. if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, TRUE, &bRefGone))) { // May return FERR_EOF_HIT if all preceeding references are deleted. if( rc == FERR_EOF_HIT) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } flmAssert( !bKeyGone); } if( !bRefGone) { if( RC_BAD( rc = FSRefPrev( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { if( rc == FERR_BT_END_OF_DATA) { rc = RC_SET( FERR_BOF_HIT); } goto Exit; } else { setKeyItemsFromBlock( &m_curKeyPos); } } if( puiRecordId) { *puiRecordId = m_curKeyPos.uiRecordId; } Exit: return( rc); } /**************************************************************************** Desc: Reposition to the current key + recordId. If the current key is gone we may reposition past the UNTIL key and should check. ****************************************************************************/ RCODE FSIndexCursor::reposition( FDB * pDb, FLMBOOL bCanPosToNextKey, // May be TRUE if bPosToPrevKey is FALSE FLMBOOL bCanPosToPrevKey, // May be TRUE if bPosToNextKey is FALSE FLMBOOL * pbKeyGone, // [out] cannot be NULL FLMBOOL bCanPosToNextRef, // May be TRUE if bPosToPrevRef is FALSE FLMBOOL bCanPosToPrevRef, // May be TRUE if bPosToNextRef is FALSE FLMBOOL * pbRefGone) // [out] cannot be NULL { RCODE rc = FERR_OK; FLMUINT uiBlkTransId = 0; FLMBOOL bReread = FALSE; FLMUINT uiTargetRecordId; uiTargetRecordId = m_curKeyPos.uiRecordId; // May have to unuse the b-tree blocks. Then setup the stack again. flmAssert( !m_curKeyPos.bStackInUse); *pbKeyGone = *pbRefGone = FALSE; // Re-read the block and see if it is the same block. if( m_curKeyPos.uiBlockAddr == BT_END) { bReread = TRUE; } else { if( RC_BAD( rc = FSGetBlock( pDb, m_pLFile, m_curKeyPos.uiBlockAddr, m_curKeyPos.pStack))) { if( rc != FERR_DATA_ERROR) { goto Exit; } rc = FERR_OK; bReread = TRUE; } else { uiBlkTransId = FB2UD( &m_curKeyPos.pStack->pBlk[ BH_TRANS_ID]); m_curKeyPos.bStackInUse = TRUE; } } if( m_curKeyPos.uiBlockTransId != uiBlkTransId) { bReread = TRUE; } else if( pDb->uiTransType == FLM_UPDATE_TRANS) { bReread = TRUE; } if( bReread) { FLMBYTE pKey [MAX_KEY_SIZ + 4]; FLMUINT uiKeyLen = m_curKeyPos.uiKeyLen; FLMUINT uiSaveRecId = m_curKeyPos.uiRecordId; f_memcpy( pKey, m_curKeyPos.pKey, uiKeyLen); // This may be a new read transaction. Call BTSearch. // The current reference or current key may go away on if( RC_BAD( rc = setKeyPosition( pDb, bCanPosToPrevKey ? FALSE : TRUE, &m_curKeyPos, &m_curKeyPos))) { if( rc != FERR_EOF_HIT && rc != FERR_BOF_HIT) { goto Exit; } } if( RC_BAD( rc) || uiKeyLen != m_curKeyPos.uiKeyLen || f_memcmp( pKey, m_curKeyPos.pKey, uiKeyLen)) { // It is OK that we may not be positioned inside of the current set. *pbKeyGone = TRUE; *pbRefGone = TRUE; if( !bCanPosToNextKey && !bCanPosToPrevKey) { if( uiKeyLen) { f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); } m_curKeyPos.uiKeyLen = uiKeyLen; m_curKeyPos.uiRecordId = uiSaveRecId; releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiBlockAddr = BT_END; if( bCanPosToNextKey || bCanPosToNextRef) { rc = RC_SET( FERR_EOF_HIT); } else if( bCanPosToPrevKey || bCanPosToPrevRef) { rc = RC_SET( FERR_BOF_HIT); } else { rc = RC_SET( FERR_NOT_FOUND); } } goto Exit; } } else { m_curKeyPos.bStackInUse = TRUE; } if (uiTargetRecordId && uiTargetRecordId != m_curKeyPos.uiRecordId) { *pbRefGone = TRUE; if( bCanPosToPrevRef && m_curKeyPos.uiRecordId < uiTargetRecordId) { if (RC_OK( rc = FSRefPrev( pDb, m_pLFile, m_curKeyPos.pStack, &m_curKeyPos.DinState, &m_curKeyPos.uiRecordId))) { setKeyItemsFromBlock( &m_curKeyPos); } } else if( !bCanPosToNextRef) { // We have an error positioning. Return NOT_FOUND rc = RC_SET( FERR_NOT_FOUND); } } Exit: return( rc); } /**************************************************************************** Desc: Save the current key position. ****************************************************************************/ void FSIndexCursor::saveCurrKeyPos( KEYPOS * pSaveKeyPos) { f_memcpy( pSaveKeyPos->pKey, m_curKeyPos.pKey, m_curKeyPos.uiKeyLen); pSaveKeyPos->uiKeyLen = m_curKeyPos.uiKeyLen; pSaveKeyPos->uiRecordId = m_curKeyPos.uiRecordId; pSaveKeyPos->uiDomain = m_curKeyPos.uiDomain; } /**************************************************************************** Desc: Restore the current key position. ****************************************************************************/ void FSIndexCursor::restoreCurrKeyPos( KEYPOS * pSaveKeyPos) { f_memcpy( m_curKeyPos.pKey, pSaveKeyPos->pKey, pSaveKeyPos->uiKeyLen); m_curKeyPos.uiKeyLen = pSaveKeyPos->uiKeyLen; m_curKeyPos.uiRecordId = pSaveKeyPos->uiRecordId; m_curKeyPos.uiDomain = pSaveKeyPos->uiDomain; } /**************************************************************************** Desc: Find the key set for the passed in key. ****************************************************************************/ RCODE FSIndexCursor::getKeySet( FLMBYTE * pKey, FLMUINT uiKeyLen, KEYSET ** ppKeySet) { RCODE rc = FERR_OK; KEYSET * pKeySet; pKeySet = m_pFirstSet; while (pKeySet) { // Compare this key against the from key. If it is less than the // from key, we are not inside one of the key ranges. if( FSCompareKeys( FALSE, pKey, uiKeyLen, FALSE, FALSE, pKeySet->fromKey.pKey, pKeySet->fromKey.uiKeyLen, pKeySet->fromKey.bExclusiveKey) < 0) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } // Key is >= from key, see how it compares to until key. if( FSCompareKeys( FALSE, pKey, uiKeyLen, FALSE, TRUE, pKeySet->untilKey.pKey, pKeySet->untilKey.uiKeyLen, pKeySet->untilKey.bExclusiveKey) <= 0) { break; } // Go to the next key range. pKeySet = pKeySet->pNext; } if( !pKeySet) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: *ppKeySet = pKeySet; return( rc); } /**************************************************************************** Desc: Position to the input key + recordId. ****************************************************************************/ RCODE FSIndexCursor::positionTo( FDB * pDb, FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT uiRecordId) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; KEYSET * pKeySet; KEYPOS * pSaveKeyPos = NULL; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // A key with a non-zero key length must be passed in. flmAssert( uiKeyLen != 0); // Make sure we fall inside one of the key ranges and // save the current key position so we can restore it // if the reposition call fails. if( RC_BAD( rc = f_alloc( sizeof( KEYPOS), &pSaveKeyPos))) { goto Exit; } if (RC_BAD( rc = getKeySet( pKey, uiKeyLen, &pKeySet))) { goto Exit; } saveCurrKeyPos( pSaveKeyPos); releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiKeyLen = uiKeyLen; f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); m_curKeyPos.uiRecordId = uiRecordId; m_curKeyPos.uiDomain = DRN_DOMAIN( uiRecordId) + 1; m_curKeyPos.uiBlockAddr = BT_END; if (RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { restoreCurrKeyPos( pSaveKeyPos); goto Exit; } m_bAtEOF = FALSE; m_bAtBOF = FALSE; m_pCurSet = pKeySet; Exit: if (pSaveKeyPos) { f_free( &pSaveKeyPos); } return( rc); } /**************************************************************************** Desc: Position to the input key + domain ****************************************************************************/ RCODE FSIndexCursor::positionToDomain( FDB * pDb, FLMBYTE * pKey, FLMUINT uiKeyLen, FLMUINT uiDomain) { RCODE rc; FLMBOOL bKeyGone; FLMBOOL bRefGone; KEYSET * pKeySet; KEYPOS * pSaveKeyPos = NULL; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // A key with a non-zero key length must be passed in. flmAssert( uiKeyLen != 0); // Make sure we fall inside one of the key ranges and // save the current key position so we can restore it // if the reposition call fails. if( RC_BAD( rc = f_alloc( sizeof( KEYPOS), &pSaveKeyPos))) { goto Exit; } if (RC_BAD( rc = getKeySet( pKey, uiKeyLen, &pKeySet))) { goto Exit; } saveCurrKeyPos( pSaveKeyPos); releaseKeyBlocks( &m_curKeyPos); m_curKeyPos.uiKeyLen = uiKeyLen; f_memcpy( m_curKeyPos.pKey, pKey, uiKeyLen); m_curKeyPos.uiRecordId = 0; m_curKeyPos.uiDomain = uiDomain; m_curKeyPos.uiBlockAddr = BT_END; if (RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { restoreCurrKeyPos( pSaveKeyPos); goto Exit; } m_bAtEOF = FALSE; m_bAtBOF = FALSE; m_pCurSet = pKeySet; Exit: if (pSaveKeyPos) { f_free( &pSaveKeyPos); } return( rc); } /**************************************************************************** Desc: Save the current position. ****************************************************************************/ RCODE FSIndexCursor::savePosition( void) { RCODE rc = FERR_OK; if( !m_pSavedPos) { if( RC_BAD( rc = f_calloc( sizeof( KEYPOS), &m_pSavedPos))) { goto Exit; } } f_memcpy( m_pSavedPos, &m_curKeyPos, sizeof( KEYPOS)); m_curKeyPos.bStackInUse = FALSE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FSIndexCursor::restorePosition( void) { if( m_pSavedPos) { releaseKeyBlocks( &m_curKeyPos); f_memcpy( &m_curKeyPos, m_pSavedPos, sizeof(KEYPOS)); } return (FERR_OK); } /**************************************************************************** Desc: Compare two key positions as they would be ordered in the index. ****************************************************************************/ FSTATIC FLMINT FSCompareKeys( // negative is '<', 0 is '=', position='>' FLMBOOL bKey1IsUntilKey, // TRUE (until key) or FALSE (from key) FLMBYTE * pKey1, FLMUINT uiKeyLen1, FLMBOOL bExclusiveKey1, FLMBOOL bKey2IsUntilKey, FLMBYTE * pKey2, FLMUINT uiKeyLen2, FLMBOOL bExclusiveKey2) { FLMINT iCmp; // Handle the first key and last key issues. if( !uiKeyLen1) // FROM or UNTIL key at end point? { if( !bKey1IsUntilKey) // FROM key { iCmp = !uiKeyLen2 && !bKey2IsUntilKey ? 0 : -1; } else // UNTIL key { iCmp = !bKey2IsUntilKey ? 1 : (!uiKeyLen2 ? 0 : 1); } } else if( !uiKeyLen2) { if( !bKey2IsUntilKey) // FROM key { iCmp = !uiKeyLen1 && !bKey1IsUntilKey ? 0 : 1; } else // UNTIL key { iCmp = !bKey1IsUntilKey ? -1 : (!uiKeyLen1 ? 0 : -1); } } else if( uiKeyLen1 > uiKeyLen2) { // Compare the key buffers. No FIRST or LAST key now. if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen2)) == 0) { iCmp = 1; } } else if( uiKeyLen1 < uiKeyLen2) { if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen1)) == 0) { iCmp = -1; } } else { if( (iCmp = f_memcmp( pKey1, pKey2, uiKeyLen1)) == 0) { // The keys are EQUAL. // bExclusiveKey ONLY applies to an UNTIL key. // Check the exclusive flag and THEN if needed the uiRecordId. if( !bKey1IsUntilKey) { if( bKey2IsUntilKey && bExclusiveKey2) { iCmp = 1; } } else if( !bKey2IsUntilKey) { if( bKey1IsUntilKey && bExclusiveKey1) { iCmp = -1; } } else // both are until keys. { if( bExclusiveKey1 != bExclusiveKey2) { iCmp = bExclusiveKey1 ? -1 : 1; } } } } return (iCmp); } /**************************************************************************** Desc: Allocate and return the first and last keys in an index. ****************************************************************************/ RCODE FSIndexCursor::getFirstLastKeys( FLMBYTE ** ppFirstKey, FLMUINT * puiFirstKeyLen, FLMBYTE ** ppLastKey, FLMUINT * puiLastKeyLen, FLMBOOL * pbLastKeyExclusive) { RCODE rc = FERR_OK; KEYSET * pCurSet = m_pFirstSet; if( !pCurSet) { *ppFirstKey = *ppLastKey = NULL; *puiFirstKeyLen = 0; *pbLastKeyExclusive = TRUE; goto Exit; } if( RC_BAD( rc = f_alloc( pCurSet->fromKey.uiKeyLen, ppFirstKey))) { goto Exit; } *puiFirstKeyLen = pCurSet->fromKey.uiKeyLen; f_memcpy( *ppFirstKey, pCurSet->fromKey.pKey, *puiFirstKeyLen); while( pCurSet->pNext) { pCurSet = pCurSet->pNext; } if( RC_BAD( rc = f_alloc( pCurSet->untilKey.uiKeyLen, ppLastKey))) { if( *ppFirstKey) { f_free( ppFirstKey); } goto Exit; } *puiLastKeyLen = pCurSet->untilKey.uiKeyLen; f_memcpy( *ppLastKey, pCurSet->untilKey.pKey, *puiLastKeyLen); *pbLastKeyExclusive = pCurSet->untilKey.bExclusiveKey; Exit: return( rc); } /**************************************************************************** Desc: Set the absolute position in a positioning index relative to the FROM/UNTIL keys in all of the key sets. Supports multiple key sets. ****************************************************************************/ RCODE FSIndexCursor::setAbsolutePosition( FDB * pDb, FLMUINT uiRefPosition) // 0 -> BOF, -1 -> EOF points, one based { RCODE rc = FERR_OK; FLMUINT uiBtreeRefPosition; KEYSET * pTempSet; if( !isAbsolutePositionable()) { flmAssert(0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } // Zero value position to BOF if( uiRefPosition == 0 || uiRefPosition == 1) { // Go to the first reference. if( RC_OK( rc = firstKey( pDb, NULL, NULL))) { if( uiRefPosition == 0) { // Should return FERR_BOF_HIT rc = prevKey( pDb, NULL, NULL); } } goto Exit; } // High-values value position to EOF if( uiRefPosition == (FLMUINT) -1) { // Position to EOF if( RC_OK( rc = lastKey( pDb, NULL, NULL))) { // Should return FERR_EOF_HIT rc = nextKey( pDb, NULL, NULL); } goto Exit; } // Find the set that contains this absolute position. uiBtreeRefPosition = 0; for( pTempSet = m_pFirstSet; pTempSet; pTempSet = pTempSet->pNext) { FLMUINT uiTotalInSet; uiTotalInSet = pTempSet->untilKey.uiRefPosition - pTempSet->fromKey.uiRefPosition; if( uiTotalInSet < uiRefPosition) { uiRefPosition -= uiTotalInSet; } else { m_pCurSet = pTempSet; uiBtreeRefPosition = pTempSet->fromKey.uiRefPosition + uiRefPosition - 1; break; } } if( !uiBtreeRefPosition) { // Position to EOF if( RC_OK( rc = lastKey( pDb, NULL, NULL))) { // Should return FERR_EOF_HIT rc = nextKey( pDb, NULL, NULL); } goto Exit; } m_curKeyPos.pStack = m_curKeyPos.Stack; RESET_DINSTATE( m_curKeyPos.DinState); m_curKeyPos.Stack[0].pKeyBuf = m_curKeyPos.pKey; if( RC_BAD( rc = FSPositionSearch( pDb, m_pLFile, uiBtreeRefPosition, &m_curKeyPos.pStack, &m_curKeyPos.uiRecordId, &m_curKeyPos.uiDomain, &m_curKeyPos.DinState))) { goto Exit; } m_curKeyPos.bStackInUse = TRUE; setKeyItemsFromBlock( &m_curKeyPos); m_bAtBOF = m_bAtEOF = FALSE; Exit: return( rc); } /**************************************************************************** Desc: Get the absolute position in a positioning index. Supports multiple sets. ****************************************************************************/ RCODE FSIndexCursor::getAbsolutePosition( FDB * pDb, FLMUINT * puiRefPosition) // 0 -> BOF, -1 -> EOF points, one based { RCODE rc = FERR_OK; FLMUINT uiRefPosition = 0; // Position relative to the FROM key sets. FLMUINT uiBtreeRefPosition; // True absolute position in the btree. KEYSET * pTempSet; if( !isAbsolutePositionable()) { flmAssert(0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if( m_bAtBOF) { *puiRefPosition = 0; goto Exit; } if( m_bAtEOF) { *puiRefPosition = (FLMUINT) -1; goto Exit; } if( !m_curKeyPos.bStackInUse) { FLMBOOL bKeyGone; FLMBOOL bRefGone; if( RC_BAD( rc = reposition( pDb, FALSE, FALSE, &bKeyGone, FALSE, FALSE, &bRefGone))) { if( rc != FERR_NOT_FOUND) goto Exit; rc = FERR_OK; } } // Compute where we are relative the the reference position of the current set. if( RC_BAD( rc = FSGetBtreeRefPosition( pDb, m_curKeyPos.pStack, &m_curKeyPos.DinState, &uiBtreeRefPosition))) { goto Exit; } uiRefPosition = (uiBtreeRefPosition - m_pCurSet->fromKey.uiRefPosition) + 1; for( pTempSet = m_pCurSet->pPrev; pTempSet; pTempSet = pTempSet->pPrev) { FLMUINT uiTemp; uiTemp = pTempSet->untilKey.uiRefPosition - pTempSet->fromKey.uiRefPosition; uiRefPosition += uiTemp; } Exit: if( RC_OK( rc)) { *puiRefPosition = uiRefPosition; } return( rc); } /**************************************************************************** Desc: Get the total number of reference for all of the sets. ****************************************************************************/ RCODE FSIndexCursor::getTotalReferences( FDB * pDb, FLMUINT * puiTotalRefs, FLMBOOL * pbTotalsEstimated) { RCODE rc = FERR_OK; KEYSET * pKeySet; // Current source set KEYSET * pTempSet = NULL; FLMUINT uiTotalRefs = 0; FLMUINT uiRefCount; FLMUINT uiStackPos; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } *pbTotalsEstimated = FALSE; // We have to be careful not to change the current position in any way. for( pKeySet = m_pFirstSet; pKeySet; pKeySet = pKeySet->pNext) { // If this is an positioning index, the ref positions MAY be set up. if( pKeySet->fromKey.uiRefPosition && pKeySet->untilKey.uiRefPosition) { uiTotalRefs += pKeySet->untilKey.uiRefPosition - pKeySet->fromKey.uiRefPosition; continue; } else if( !pTempSet) { if( RC_BAD( rc = f_calloc( sizeof( KEYSET), &pTempSet))) { goto Exit; } } // Compute the counts the old fashion way. f_memcpy( pTempSet, pKeySet, sizeof( KEYSET)); for( uiStackPos = 0; uiStackPos < BH_MAX_LEVELS; uiStackPos++) { pTempSet->fromKey.Stack[uiStackPos].pSCache = NULL; pTempSet->fromKey.Stack[uiStackPos].pBlk = NULL; pTempSet->untilKey.Stack[uiStackPos].pSCache = NULL; pTempSet->untilKey.Stack[uiStackPos].pBlk = NULL; } pTempSet->fromKey.bStackInUse = FALSE; pTempSet->untilKey.bStackInUse = FALSE; // Search down the tree and get the counts. if( RC_OK( rc = setKeyPosition( pDb, TRUE, &pTempSet->fromKey, &pTempSet->fromKey))) { // All keys bewteen low and high may be gone. if( FS_COMPARE_KEYS( FALSE, &pTempSet->fromKey, TRUE, &pKeySet->untilKey) > 0) { rc = RC_SET( FERR_BOF_HIT); } else { rc = setKeyPosition( pDb, FALSE, &pTempSet->untilKey, &pTempSet->untilKey); } } if( RC_BAD( rc)) { // Empty tree case. if( rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { rc = FERR_OK; } else { goto Exit; } } else { if( RC_BAD( rc = FSComputeIndexCounts( pTempSet->fromKey.pStack, pTempSet->untilKey.pStack, NULL, NULL, &uiRefCount, pbTotalsEstimated))) { goto Exit; } uiTotalRefs += uiRefCount; } releaseKeyBlocks( &pTempSet->fromKey); releaseKeyBlocks( &pTempSet->untilKey); } Exit: if( pTempSet) { releaseKeyBlocks( &pTempSet->fromKey); releaseKeyBlocks( &pTempSet->untilKey); f_free( &pTempSet); } if( RC_OK(rc)) { *puiTotalRefs = uiTotalRefs; } return( rc); } libflaim-4.9.966/src/imonfram.cpp0000644000175000017500000004720010510774540020173 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying the framesets used by the monitoring code // to display web pages. // Tabs: 3 // // Copyright (c) 2001-2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonfram.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /********************************************************* Desc: Return HTML code that defines the welcome page frames. There are two framesets. The first has one frame that references "Header.htm". The second frameset has two frames. The first frame references "Nav.htm" and "Welcome.htm". This class is invoked following a successful login. **********************************************************/ RCODE F_FrameMain::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM(uiNumParams); F_UNREFERENCED_PARM(ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printStyle(); fnPrintf( m_pHRequest, "Database iMonitor\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /********************************************************* Desc: Return HTML code that defines the Header.htm frame. **********************************************************/ RCODE F_FrameHeader::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM(uiNumParams); F_UNREFERENCED_PARM(ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); printStyle(); fnPrintf( m_pHRequest, "Header\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, (char *)pszHeading); fnPrintf( m_pHRequest, "
pucBlkFLMBYTE *%spFileFFILE *%suiBlkAddressFLMUINT0x%lX pNotifyList FNOTIFY * %s pNotifyList FNOTIFY * 0x0 uiHighTransIDFLMUINTuiUseCountFLMUINTui16FlagsFLMUINT160x%04X %sui16BlkSizeFLMUINT16uiChecksumFLMUINT pUseList SCACHE_USE_p %s pUseList SCACHE_USE_p 0x0 ppHashTblSCACHE **%sUsageFLM_CACHE_USAGE%sbAutoCalcMaxDirtyFLMBOOLuiMaxDirtyCacheFLMUINTuiLowDirtyCacheFLMUINTuiTotalUsesFLMUINTuiBlocksUsed FLMUINTuiPendingReadsFLMUINTuiIoWaitsFLMUINTuiHashTableSizeFLMUINTuiHashTableBitsFLMUINTbDebugFLMBOOLTable Size: %lu Entries Used: %lu (%lu%%) \n"); // Print out the hash buckets for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { if (pszHTLinks[uiLoop][0] != '\0') { fnPrintf( m_pHRequest, "%lu
\n", pszHTLinks[uiLoop], szRefresh, uiStart+uiLoop); } else { fnPrintf( m_pHRequest, "%lu
\n", uiStart+uiLoop); } } fnPrintf( m_pHRequest, "\n
\n"); // Print out the other stuff... uiNewStart = (uiStart > 100)?(uiStart - 100):0; fnPrintf( m_pHRequest, "Previous 100
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart > 10)?(uiStart - 10):0; fnPrintf( m_pHRequest, "Previous 10
\n", m_pszURLString, uiNewStart, szRefresh); fnPrintf( m_pHRequest, "
\n"); uiNewStart = (uiStart + 10); if (uiNewStart >= (uiHashTableSize - NUM_ENTRIES)) { uiNewStart = (uiHashTableSize - NUM_ENTRIES); } fnPrintf( m_pHRequest, "Next 10
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart + 100); if (uiNewStart >= (uiHashTableSize - NUM_ENTRIES)) { uiNewStart = (uiHashTableSize - NUM_ENTRIES); } fnPrintf( m_pHRequest, "Next 100
\n" "
\n" "
Jump to specific bucket:
\n" "
\n", m_pszURLString, uiNewStart, szRefresh); printButton( "Jump", BT_Submit); // We use a hidden field to pass the refresh parameter back the the server if (bRefresh) { fnPrintf( m_pHRequest, "\n"); } fnPrintf( m_pHRequest, "
\n
SCACHE * SCACHE *
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\"Novell\n", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "
Database iMonitor
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /********************************************************* Desc: Return HTML code that defines the Nav.htm frame. *********************************************************/ RCODE F_FrameNav::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; void * pvSession = NULL; char szValue[20]; char szGblPassword[20]; char * pszPassword = NULL; FLMUINT uiSize; F_Session * pFlmSession = m_pFlmSession; if (gv_FlmSysData.HttpConfigParms.fnAcquireSession) { pvSession = fnAcquireSession(); } printDocStart( "Navigator", FALSE, TRUE, FLM_IMON_COLOR_PUTTY_1); // Configuration fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Configuration"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "System", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); // Monitoring fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Monitoring"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Queries", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Threads", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Statistics", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); // Database if( pFlmSession) { fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Database", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Backup", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Check", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); } fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Index Manager", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); // Internal structures fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Internal Structures"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Database System Data", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); // Misc. fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Misc."); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "File Manager", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Return Code Lookup", m_pszURLString); fnPrintf( m_pHRequest, "
\n"); // The rest of this function is password related stuff... f_memset( szGblPassword, 0, sizeof( szGblPassword)); if (DetectParameter( uiNumParams, ppszParams, "StopSecureDbAccess")) { fnSetGblValue( FLM_SECURE_PASSWORD, "", (FLMSIZET)0); fnSetGblValue( FLM_SECURE_EXPIRATION, "", (FLMSIZET )0); uiSize = 0; } else { // Get the session global access password if it has been entered. // We are going to ignore any error, as the password may not have been entered. uiSize = sizeof( szGblPassword); (void)fnGetGblValue( FLM_SECURE_PASSWORD, (void *)szGblPassword, (FLMSIZET *)&uiSize); } fnPrintf( m_pHRequest, "
Secure Control
\n" "
"); if (f_strlen( szGblPassword) == 0) { fnPrintf( m_pHRequest, "Access Code", m_pszURLString); } else { fnPrintf( m_pHRequest, "" "Disallow Secure DB Access", m_pszURLString); } fnPrintf( m_pHRequest, "
\n"); // Check to see if we just entered the secure password. if (pvSession && DetectParameter( uiNumParams, ppszParams, "SecurePassword")) { // We need to get the password entered, but it is being passsed as form data. // pszPassword will be allocated within the getFormValueByName function and // will need to be released using f_free. if (RC_BAD( rc = getFormValueByName( FLM_SECURE_PASSWORD, &pszPassword, 0, &uiSize))) { goto Exit; } if (pszPassword && f_strlen( pszPassword) > 0) // Do we want to have a minimum password length? { if (f_strcmp( szGblPassword, pszPassword) == 0) { if (fnSetSessionValue( pvSession, FLM_SECURE_PASSWORD, pszPassword, uiSize)) { flmAssert( 0); } } else { // They don't match - need to tell the user. fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "Invalid access code"); fnPrintf( m_pHRequest, "
\n"); } } if (pszPassword) { f_free( &pszPassword); } } // Did the user ask to log off? else if (pvSession && DetectParameter( uiNumParams, ppszParams, "Logoff")) { // Pull the password out of the session data. if (fnSetSessionValue( pvSession, FLM_SECURE_PASSWORD, "", 0)) { flmAssert( 0); } // Close any database handles associated with our session... if (pFlmSession) { RCODE tmpRC = FERR_OK; F_SessionDb * pSessionDb = NULL; char * pDbKey; tmpRC = pFlmSession->getNextDb( &pSessionDb); while (RC_OK( tmpRC)) { pDbKey = (char *)pSessionDb->getKey(); tmpRC = pFlmSession->getNextDb( &pSessionDb); pFlmSession->closeDb( pDbKey); } if (tmpRC != FERR_EOF_HIT) { flmAssert( 0); } } // Reload the content window... fnPrintf( m_pHRequest, "", m_pszURLString); } // Do we display the Secure Password option? if (pvSession) { uiSize = sizeof( szValue); f_memset( szValue, 0, uiSize); (void)fnGetSessionValue( pvSession, FLM_SECURE_PASSWORD, (void *)szValue, (FLMSIZET *)&uiSize); if (f_strlen( szValue) != 0) { fnPrintf( m_pHRequest, "
" "Log Off
\n"); } else { // Only want to display the password entry box if the secure mode // has been enabled uiSize = sizeof( szGblPassword); (void)fnGetGblValue( FLM_SECURE_PASSWORD, (void *)szGblPassword, (FLMSIZET *)&uiSize); if (f_strlen( szGblPassword) != 0) { fnPrintf( m_pHRequest, "
"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
\n", FLM_SECURE_PASSWORD); printButton( "Login", BT_Submit); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "
\n"); } } } printDocEnd(); fnEmit(); Exit: if (pszPassword) { f_free( &pszPassword); } if (pvSession) { (void)fnReleaseSession( pvSession); } return( rc); } /********************************************************* Desc: Return HTML code that defines the Welcome.htm frame. **********************************************************/ RCODE F_FrameWelcome::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM(uiNumParams); F_UNREFERENCED_PARM(ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "Welcome\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "" "

Welcome to the Database iMonitor." "

\n"); fnPrintf( m_pHRequest, "


This is a tool for examining and, if necessary, " "altering various data elements of the database
" "internal structures as well as the database records " "themselves.\n"); fnPrintf( m_pHRequest, "

" "Please exercise caution when using this tool." "

\n"); fnPrintf( m_pHRequest, "
\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnEmit(); return( rc); } /********************************************************* Desc: Return HTML code that defines the SecureDbAccess popup window contents. **********************************************************/ RCODE F_SecureDbAccess::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM(uiNumParams); F_UNREFERENCED_PARM(ppszParams); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n\nSecure Database Access\n" "\n\n" "

Please paste the private access " "enabling data in the text area below, then click " "on the Submit button


\n" "
\n" "

\n
"); printButton( "Submit", BT_Submit); printButton( "Reset", BT_Reset); fnPrintf( m_pHRequest, "
\n" "\n" "\n\n"); fnEmit(); return( rc); } /********************************************************* Desc: Return HTML code that defines the SecureDbInfo popup window contents. **********************************************************/ RCODE F_SecureDbInfo::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMBYTE * pszBuffer = NULL; FLMUINT uiLen; char * pszPassword; char * pszExpiration; FLMUINT uiPwdLen = 0; FLMUINT uiExpLen = 0; FLMBYTE * pszData = NULL; FLMUINT uiDataSize; char * pTmp; FLMBOOL bDataOk = FALSE; void * pvSession = NULL; FLMUINT uiExpTime; FLMUINT uiCurrTime; F_UNREFERENCED_PARM( uiNumParams); F_UNREFERENCED_PARM( ppszParams); if (gv_FlmSysData.HttpConfigParms.fnAcquireSession) { pvSession = fnAcquireSession(); } if (RC_BAD( rc = getFormValueByName( "SecureData", (char **)&pszBuffer, 0, &uiLen))) { printErrorPage( FERR_INVALID_PARM, TRUE, "Could not retrieve required data."); goto Exit; } // Decode the data. fcsDecodeHttpString( (char *)pszBuffer); if ( RC_BAD( rc = flmExtractHexPacketData( pszBuffer, &pszData, &uiDataSize))) { goto SkipPwdExp; } // Extract the password field...The data should be in the format // password=Password,expire=Date if (( pszPassword = f_strstr( (char *)pszData, "password")) != NULL) { pszPassword += f_strlen("password") + 1; // Allow for '=' for ( pTmp = pszPassword, uiPwdLen = 0; *pTmp && *pTmp != ','; pTmp++, uiPwdLen++); } else { goto SkipPwdExp; } if (( pszExpiration = f_strstr( (char *)pszData, "expire")) != NULL) { pszExpiration += f_strlen("expire") + 1; // Allow for '=' for ( pTmp = pszExpiration, uiExpLen = 0; *pTmp && *pTmp != ','; pTmp++, uiExpLen++); } else { goto SkipPwdExp; } pszPassword[ uiPwdLen] = '\0'; pszExpiration[ uiExpLen] = '\0'; // Let's determine if the Expiration date is still valid. uiExpTime = f_atoud( pszExpiration); f_timeGetSeconds( &uiCurrTime); if (uiCurrTime > uiExpTime) { goto SkipPwdExp; } // Now store the data... if (gv_FlmSysData.HttpConfigParms.fnSetGblValue) { if (fnSetGblValue( FLM_SECURE_PASSWORD, pszPassword, (FLMSIZET)uiPwdLen)) { flmAssert( 0); } if (fnSetGblValue( FLM_SECURE_EXPIRATION, pszExpiration, (FLMSIZET)uiExpLen)) { flmAssert( 0); } // Now, reset the session password if it exists. pszPassword = '\0'; if (fnSetSessionValue( pvSession, FLM_SECURE_PASSWORD, pszPassword, 0)) { flmAssert( 0); } } bDataOk = TRUE; SkipPwdExp: if (bDataOk) { stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n", m_pszURLString); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); } else { printErrorPage( FERR_INVALID_PARM, TRUE, "The data you entered could not been accepted." "The information may be invalid or expired." " Please try again with new data."); } Exit: fnEmit(); if (pszBuffer) { f_free( &pszBuffer); } if (pszData) { f_free( &pszData); } if (pvSession) { (void)fnReleaseSession( pvSession); } return( rc); } libflaim-4.9.966/src/flmstat.cpp0000644000175000017500000011707510510774540020045 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for updating statistics - for monitoring. // Tabs: 3 // // Copyright (c) 1997-2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flmstat.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" #define INIT_DB_STAT_ARRAY_SIZE 5 #define DB_STAT_ARRAY_INCR_SIZE 5 #define INIT_LFILE_STAT_ARRAY_SIZE 5 #define LFILE_STAT_ARRAY_INCR_SIZE 5 FSTATIC RCODE flmStatGetDbByName( FLM_STATS * pFlmStats, const char * pszDbName, FLMUINT uiLowStart, DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV); FSTATIC void flmUpdateLFileStats( LFILE_STATS * pDest, LFILE_STATS * pSrc); FSTATIC RCODE flmUpdateDbStats( DB_STATS * pDest, DB_STATS * pSrc); FSTATIC RCODE flmStatCopy( FLM_STATS * pDestStats, FLM_STATS * pSrcStats); FSTATIC FLMUINT flmDaysInMonth( FLMUINT uiYear, FLMUINT uiMonth); FSTATIC void flmAdjustTime( F_TMSTAMP * pTime, FLMINT iStartPoint); /**************************************************************************** Desc: This routine returns a pointer to a particular database's statistics block. ****************************************************************************/ FSTATIC RCODE flmStatGetDbByName( FLM_STATS * pFlmStats, const char * pszDbName, FLMUINT uiLowStart, DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV) { RCODE rc = FERR_OK; FLMUINT uiTblSize; DB_STATS * pDbStatTbl; FLMUINT uiLow; FLMUINT uiMid = 0; FLMUINT uiHigh; FLMINT iCmp = 0; FLMUINT uiNewSize; FLMUINT uiElement; char * pszTmpDbName = NULL; // If there is a database array, search it first. if (((pDbStatTbl = pFlmStats->pDbStats) != NULL) && ((uiTblSize = pFlmStats->uiNumDbStats) != 0)) { for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize { uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 // See if we found a match. #ifdef FLM_UNIX if ((iCmp = f_strcmp( pszDbName, pDbStatTbl [uiMid].pszDbName)) == 0) #else if ((iCmp = f_stricmp( pszDbName, pDbStatTbl [uiMid].pszDbName)) == 0) #endif { // Found match. *ppDbStatsRV = &pDbStatTbl [uiMid]; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; } if (puiDbTblPosRV) { *puiDbTblPosRV = uiMid; } goto Exit; } // Check if we are done - where uiLow equals uiHigh. if (uiLow >= uiHigh) { // Item not found. break; } if (iCmp < 0) { if (uiMid == uiLowStart) { break; // Way too high? } uiHigh = uiMid - 1; // Too high } else { if (uiMid == uiTblSize) { break; // Done - Hit the top } uiLow = uiMid + 1; // Too low } } } // If the array is full, or was never allocated, allocate a new one. if (pFlmStats->uiDbStatArraySize <= pFlmStats->uiNumDbStats) { if (!pFlmStats->pDbStats) { uiNewSize = INIT_DB_STAT_ARRAY_SIZE; } else { uiNewSize = pFlmStats->uiDbStatArraySize + DB_STAT_ARRAY_INCR_SIZE; } if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( DB_STATS) * uiNewSize), &pDbStatTbl))) { goto Exit; } // Save whatever was in the old table, if any. if (pFlmStats->pDbStats && pFlmStats->uiNumDbStats) { f_memcpy( pDbStatTbl, pFlmStats->pDbStats, (FLMINT)(sizeof( DB_STATS) * pFlmStats->uiNumDbStats)); } if (pFlmStats->pDbStats) { f_free( &pFlmStats->pDbStats); } pFlmStats->uiDBAllocSeq++; pFlmStats->pDbStats = pDbStatTbl; pFlmStats->uiDbStatArraySize = uiNewSize; } // Allocate space for the database name if (RC_BAD( rc = f_alloc( f_strlen( pszDbName) + 1, &pszTmpDbName))) { goto Exit; } // Insert the item into the array. if (iCmp != 0) { uiElement = pFlmStats->uiNumDbStats; // If our new database number is greater than database number of the // element pointed to by uiMid, increment uiMid so that the new // database number will be inserted after it instead of before it. if (iCmp > 0) { uiMid++; } // Move everything up in the array, including the slot pointed to // by uiMid. while (uiElement > uiMid) { f_memcpy( &pDbStatTbl [uiElement], &pDbStatTbl [uiElement - 1], sizeof( DB_STATS)); uiElement--; } f_memset( &pDbStatTbl [uiMid], 0, sizeof( DB_STATS)); } f_strcpy( pszTmpDbName, pszDbName); pDbStatTbl[ uiMid].pszDbName = pszTmpDbName; pszTmpDbName = NULL; pFlmStats->uiNumDbStats++; *ppDbStatsRV = &pDbStatTbl [uiMid]; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; } if (puiDbTblPosRV) { *puiDbTblPosRV = uiMid; } Exit: if (pszTmpDbName) { f_free( &pszTmpDbName); } return( rc); } /**************************************************************************** Desc: This routine returns a pointer to a particular database's statistics block. ****************************************************************************/ RCODE flmStatGetDb( FLM_STATS * pFlmStats, void * pFile, FLMUINT uiLowStart, DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV) { if( !pFlmStats) { *ppDbStatsRV = NULL; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = 0; } if (puiDbTblPosRV) { *puiDbTblPosRV = 0; } return( FERR_OK); } return( flmStatGetDbByName( pFlmStats, ((FFILE *)pFile)->pszDbPath, uiLowStart, ppDbStatsRV, puiDBAllocSeqRV, puiDbTblPosRV)); } /**************************************************************************** Desc: This routine returns a pointer to a particular logical file in a particular database's statistics block. ****************************************************************************/ RCODE flmStatGetLFile( DB_STATS * pDbStats, FLMUINT uiLFileNum, FLMUINT uiLfType, FLMUINT uiLowStart, LFILE_STATS ** ppLFileStatsRV, FLMUINT * puiLFileAllocSeqRV, FLMUINT * puiLFileTblPosRV ) { RCODE rc = FERR_OK; FLMUINT uiTblSize; LFILE_STATS * pLFileStatTbl; LFILE_STATS * pLFileCurrStat; FLMUINT uiLow; FLMUINT uiMid = 0; FLMUINT uiHigh; FLMINT iCmp = 0; FLMUINT uiNewSize; FLMUINT uiElement; if (!pDbStats) { *ppLFileStatsRV = NULL; if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = 0; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = 0; } goto Exit; } // If there is a database array, search it first. if (((pLFileStatTbl = pDbStats->pLFileStats) != NULL) && ((uiTblSize = pDbStats->uiNumLFileStats) != 0)) { for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize { uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 // See if we found a match. pLFileCurrStat = &pLFileStatTbl [uiMid]; if (uiLFileNum < pLFileCurrStat->uiLFileNum) { iCmp = -1; } else if (uiLFileNum > pLFileCurrStat->uiLFileNum) { iCmp = 1; } else { // Found match. *ppLFileStatsRV = pLFileCurrStat; if (uiLfType != 0xFF) { pLFileCurrStat->uiFlags &= (~(LFILE_TYPE_UNKNOWN)); if (uiLfType == LF_INDEX) { pLFileCurrStat->uiFlags |= LFILE_IS_INDEX; } else { pLFileCurrStat->uiFlags &= (~(LFILE_IS_INDEX)); } } if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = uiMid; } goto Exit; } // Check if we are done - where uiLow equals uiHigh. if (uiLow >= uiHigh) { // Item not found. break; } if (iCmp < 0) { if (uiMid == uiLowStart) { break; // Way too high? } uiHigh = uiMid - 1; // Too high } else { if (uiMid == uiTblSize) { break; // Done - Hit the top } uiLow = uiMid + 1; // Too low } } } // If the array is full, or was never allocated, allocate a new one. if (pDbStats->uiLFileStatArraySize <= pDbStats->uiNumLFileStats) { if (!pDbStats->pLFileStats) { uiNewSize = INIT_LFILE_STAT_ARRAY_SIZE; } else { uiNewSize = pDbStats->uiLFileStatArraySize + LFILE_STAT_ARRAY_INCR_SIZE; } if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( LFILE_STATS) * uiNewSize), &pLFileStatTbl))) { goto Exit; } // Save whatever was in the old table, if any. if ((pDbStats->pLFileStats) && (pDbStats->uiNumLFileStats)) { f_memcpy( pLFileStatTbl, pDbStats->pLFileStats, (FLMUINT)(sizeof( LFILE_STATS) * pDbStats->uiNumLFileStats)); } if (pDbStats->pLFileStats) { f_free( &pDbStats->pLFileStats); } pDbStats->uiLFileAllocSeq++; pDbStats->pLFileStats = pLFileStatTbl; pDbStats->uiLFileStatArraySize = uiNewSize; } // Insert the item into the array. if (iCmp != 0) { uiElement = pDbStats->uiNumLFileStats; // If our new database number is greater than database number of the // element pointed to by uiMid, increment uiMid so that the new // database number will be inserted after it instead of before it. if (iCmp > 0) { uiMid++; } // Move everything up in the array, including the slot pointed to // by uiMid. while (uiElement > uiMid) { f_memcpy( &pLFileStatTbl [uiElement], &pLFileStatTbl [uiElement - 1], sizeof( LFILE_STATS)); uiElement--; } f_memset( &pLFileStatTbl [uiMid], 0, sizeof( LFILE_STATS)); } pLFileStatTbl [uiMid].uiLFileNum = uiLFileNum; if (uiLfType == LF_INDEX) { pLFileStatTbl [uiMid].uiFlags |= LFILE_IS_INDEX; pLFileStatTbl [uiMid].uiFlags &= (~(LFILE_TYPE_UNKNOWN)); } else if (uiLfType == 0xFF) { pLFileStatTbl [uiMid].uiFlags |= LFILE_TYPE_UNKNOWN; } else { pLFileStatTbl [uiMid].uiFlags &= (~(LFILE_IS_INDEX | LFILE_TYPE_UNKNOWN)); } pDbStats->uiNumLFileStats++; *ppLFileStatsRV = &pLFileStatTbl [uiMid]; if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = uiMid; } Exit: return( rc); } /**************************************************************************** Desc: This routine returns a pointer to a particular LFILE's statistics block. It uses the pointer in the OPC if it is up-to-date. ****************************************************************************/ LFILE_STATS * fdbGetLFileStatPtr( FDB * pDb, LFILE * pLFile ) { if (!pLFile) { return( (LFILE_STATS *)NULL); } if ((!pDb->pLFileStats) || (pDb->uiLFileAllocSeq != pDb->pDbStats->uiLFileAllocSeq) || (pDb->pLFileStats->uiLFileNum != pLFile->uiLfNum)) { if (RC_BAD( flmStatGetLFile( pDb->pDbStats, pLFile->uiLfNum, pLFile->uiLfType, 0, &pDb->pLFileStats, &pDb->uiLFileAllocSeq, NULL))) { pDb->pLFileStats = NULL; pDb->uiLFileAllocSeq = 0; } } return( pDb->pLFileStats); } /**************************************************************************** Desc: This routine resets the statistics in a FLM_STAT structure. ****************************************************************************/ void flmStatReset( FLM_STATS * pStats, FLMBOOL bMutexAlreadyLocked, FLMBOOL bFree ) { FLMUINT uiDb; DB_STATS * pDbStats; FLMUINT uiLFile; LFILE_STATS * pLFile; // If the structure has a mutex, lock it, if not already locked. if (!bMutexAlreadyLocked && pStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pStats->hMutex); } if ((pDbStats = pStats->pDbStats) != NULL) { for (uiDb = 0; uiDb < pStats->uiNumDbStats; uiDb++, pDbStats++) { if ((pLFile = pDbStats->pLFileStats) != NULL) { if (bFree) { f_free( &pDbStats->pLFileStats); } else { for (uiLFile = 0; uiLFile < pDbStats->uiNumLFileStats; uiLFile++, pLFile++) { FLMUINT uiSaveLFileNum = pLFile->uiLFileNum; FLMUINT uiSaveFlags = pLFile->uiFlags; f_memset( pLFile, 0, sizeof( LFILE_STATS)); pLFile->uiLFileNum = uiSaveLFileNum; pLFile->uiFlags = uiSaveFlags; } } } if (!bFree) { const char * pszSaveDbName; FLMUINT uiSaveLFileAllocSeq = pDbStats->uiLFileAllocSeq; LFILE_STATS * pSaveLFileStats = pDbStats->pLFileStats; FLMUINT uiSaveLFileStatArraySize = pDbStats->uiLFileStatArraySize; FLMUINT uiSaveNumLFileStats = pDbStats->uiNumLFileStats; pszSaveDbName = pDbStats->pszDbName; f_memset( pDbStats, 0, sizeof( DB_STATS)); pDbStats->pszDbName = pszSaveDbName; pDbStats->uiLFileAllocSeq = uiSaveLFileAllocSeq; pDbStats->pLFileStats = pSaveLFileStats; pDbStats->uiLFileStatArraySize = uiSaveLFileStatArraySize; pDbStats->uiNumLFileStats = uiSaveNumLFileStats; } else { f_free( &pDbStats->pszDbName); } } if (bFree) { f_free( &pStats->pDbStats); } } if ((bFree) || (!pDbStats)) { pStats->pDbStats = NULL; pStats->uiDbStatArraySize = 0; pStats->uiNumDbStats = 0; } pStats->uiStartTime = 0; pStats->uiStopTime = 0; if (pStats->bCollectingStats) { f_timeGetSeconds( &pStats->uiStartTime); } // Unlock the mutex, if we locked it in this routine. if (!bMutexAlreadyLocked && pStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pStats->hMutex); } } /**************************************************************************** Desc: This routine starts collecting statistics in a FLM_STAT structure. ****************************************************************************/ void flmStatStart( FLM_STATS * pStats ) { // If the structure has a mutex, lock it. if( pStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pStats->hMutex); } pStats->bCollectingStats = TRUE; flmStatReset( pStats, TRUE, TRUE); // If the structure has a mutex, unlock it. if( pStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pStats->hMutex); } } /**************************************************************************** Desc: This routine stops collecting statistics in a FLM_STAT structure. ****************************************************************************/ void flmStatStop( FLM_STATS * pStats ) { // If the structure has a mutex, lock it. if (pStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pStats->hMutex); } if (pStats->bCollectingStats) { pStats->bCollectingStats = FALSE; f_timeGetSeconds( &pStats->uiStopTime); } // If the structure has a mutex, unlock it. if (pStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pStats->hMutex); } } /**************************************************************************** Desc: This routine initializes a FLM_STAT structure. ****************************************************************************/ RCODE flmStatInit( FLM_STATS * pStats, FLMBOOL bEnableSharing ) { RCODE rc = FERR_OK; f_memset( pStats, 0, sizeof( FLM_STATS)); if (!bEnableSharing) { pStats->hMutex = F_MUTEX_NULL; } else { if( RC_BAD( rc = f_mutexCreate( &pStats->hMutex))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: This routine frees the memory associated with a FLM_STAT structure. ****************************************************************************/ FLMEXP void FLMAPI FlmFreeStats( FLM_STATS * pStats ) { // If the structure has a mutex, lock it. if (pStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pStats->hMutex); } pStats->bCollectingStats = FALSE; flmStatReset( pStats, TRUE, TRUE); // Unlock and free the mutex if (pStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pStats->hMutex); f_mutexDestroy( &pStats->hMutex); } } /**************************************************************************** Desc: This routine updates statistics from one RTRANS_STATS structure into another. ****************************************************************************/ FINLINE void flmUpdateRTransStats( RTRANS_STATS * pDest, RTRANS_STATS * pSrc) { flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); flmUpdateCountTimeStats( &pDest->InvisibleTrans, &pSrc->InvisibleTrans); } /**************************************************************************** Desc: This routine updates statistics from one UTRANS_STATS structure into another. ****************************************************************************/ FINLINE void flmUpdateUTransStats( UTRANS_STATS * pDest, UTRANS_STATS * pSrc) { flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); flmUpdateCountTimeStats( &pDest->GroupCompletes, &pSrc->GroupCompletes); pDest->ui64GroupFinished += pSrc->ui64GroupFinished; flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); } /**************************************************************************** Desc: This routine updates statistics from one BLOCKIO_STATS structure into another. ****************************************************************************/ void flmUpdateBlockIOStats( BLOCKIO_STATS * pDest, BLOCKIO_STATS * pSrc ) { flmUpdateDiskIOStats( &pDest->BlockReads, &pSrc->BlockReads); flmUpdateDiskIOStats( &pDest->OldViewBlockReads, &pSrc->OldViewBlockReads); pDest->uiBlockChkErrs += pSrc->uiBlockChkErrs; pDest->uiOldViewBlockChkErrs += pSrc->uiOldViewBlockChkErrs; pDest->uiOldViewErrors += pSrc->uiOldViewErrors; flmUpdateDiskIOStats( &pDest->BlockWrites, &pSrc->BlockWrites); } /**************************************************************************** Desc: This routine updates statistics from one LFILE_STATS structure into another. ****************************************************************************/ FSTATIC void flmUpdateLFileStats( LFILE_STATS * pDest, LFILE_STATS * pSrc ) { // Set uiFlags in case the number of levels has changed. pDest->uiFlags = pSrc->uiFlags; pDest->bHaveStats = TRUE; flmUpdateBlockIOStats( &pDest->RootBlockStats, &pSrc->RootBlockStats); flmUpdateBlockIOStats( &pDest->MiddleBlockStats, &pSrc->MiddleBlockStats); flmUpdateBlockIOStats( &pDest->LeafBlockStats, &pSrc->LeafBlockStats); pDest->ui64BlockSplits += pSrc->ui64BlockSplits; pDest->ui64BlockCombines += pSrc->ui64BlockCombines; } /**************************************************************************** Desc: This routine updates statistics from one DB_STATS structure into another. ****************************************************************************/ FSTATIC RCODE flmUpdateDbStats( DB_STATS * pDestDb, DB_STATS * pSrcDb ) { RCODE rc = FERR_OK; FLMUINT uiSrcLFile; FLMUINT uiDestLFile; LFILE_STATS * pDestLFile; LFILE_STATS * pSrcLFile; FLMUINT uiLowLFileStart; FLMUINT uiSaveNumLFiles; flmUpdateRTransStats( &pDestDb->ReadTransStats, &pSrcDb->ReadTransStats); flmUpdateUTransStats( &pDestDb->UpdateTransStats, &pSrcDb->UpdateTransStats); pDestDb->bHaveStats = TRUE; pDestDb->ui64NumCursors += pSrcDb->ui64NumCursors; pDestDb->ui64NumCursorReads += pSrcDb->ui64NumCursorReads; flmUpdateCountTimeStats( &pDestDb->RecordAdds, &pSrcDb->RecordAdds); flmUpdateCountTimeStats( &pDestDb->RecordDeletes, &pSrcDb->RecordDeletes); flmUpdateCountTimeStats( &pDestDb->RecordModifies, &pSrcDb->RecordModifies); pDestDb->ui64NumRecordReads += pSrcDb->ui64NumRecordReads; flmUpdateBlockIOStats( &pDestDb->LFHBlockStats, &pSrcDb->LFHBlockStats); flmUpdateBlockIOStats( &pDestDb->AvailBlockStats, &pSrcDb->AvailBlockStats); flmUpdateDiskIOStats( &pDestDb->LogHdrWrites, &pSrcDb->LogHdrWrites); flmUpdateDiskIOStats( &pDestDb->LogBlockWrites, &pSrcDb->LogBlockWrites); flmUpdateDiskIOStats( &pDestDb->LogBlockRestores, &pSrcDb->LogBlockRestores); flmUpdateDiskIOStats( &pDestDb->LogBlockReads, &pSrcDb->LogBlockReads); pDestDb->uiLogBlockChkErrs += pSrcDb->uiLogBlockChkErrs; pDestDb->uiReadErrors += pSrcDb->uiReadErrors; pDestDb->uiWriteErrors += pSrcDb->uiWriteErrors; flmUpdateCountTimeStats( &pDestDb->LockStats.NoLocks, &pSrcDb->LockStats.NoLocks); flmUpdateCountTimeStats( &pDestDb->LockStats.WaitingForLock, &pSrcDb->LockStats.WaitingForLock); flmUpdateCountTimeStats( &pDestDb->LockStats.HeldLock, &pSrcDb->LockStats.HeldLock); // Go through the LFILE statistics. for (uiDestLFile = 0, uiSrcLFile = 0, uiLowLFileStart = 0, pSrcLFile = pSrcDb->pLFileStats; uiSrcLFile < pSrcDb->uiNumLFileStats; uiSrcLFile++, pSrcLFile++) { if (!pSrcLFile->bHaveStats) continue; // Find or add the store in the destination store array. uiSaveNumLFiles = pDestDb->uiNumLFileStats; if (RC_BAD( rc = flmStatGetLFile( pDestDb, pSrcLFile->uiLFileNum, (FLMUINT)((pSrcLFile->uiFlags & LFILE_IS_INDEX) ? (FLMUINT)LF_INDEX : (FLMUINT)LF_CONTAINER), uiLowLFileStart, &pDestLFile, NULL, &uiLowLFileStart))) { goto Exit; } if (uiLowLFileStart < pDestDb->uiNumLFileStats - 1) { uiLowLFileStart++; } // If we created the LFILE, all we have to do is copy the // LFILE statistics. It will be quicker. if (uiSaveNumLFiles != pDestDb->uiNumLFileStats) { f_memcpy( pDestLFile, pSrcLFile, sizeof( LFILE_STATS)); } else { // LFILE was already present, need to go through and // update the statistics. flmUpdateLFileStats( pDestLFile, pSrcLFile); } } Exit: return( rc); } /**************************************************************************** Desc: This routine updates statistics from one FLM_STAT structure into another. The source statistics are reset after the update. ****************************************************************************/ RCODE flmStatUpdate( FLM_STATS * pDestStats, FLM_STATS * pSrcStats) { RCODE rc = FERR_OK; FLMUINT uiSrcDb; FLMUINT uiDestDb; DB_STATS * pDestDb; DB_STATS * pSrcDb; FLMUINT uiLowDbStart; // Do not update the statistics if the source statistics were started // at an earlier time that the destination statistics start time. if (!pDestStats->bCollectingStats || pSrcStats->uiStartTime < pDestStats->uiStartTime) { return( FERR_OK); } // If the destination structure has a mutex, lock it. if (pDestStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pDestStats->hMutex); } // Go through each of the source's databases for ( uiDestDb = 0, uiSrcDb = 0, uiLowDbStart = 0, pSrcDb = pSrcStats->pDbStats; uiSrcDb < pSrcStats->uiNumDbStats; uiSrcDb++, pSrcDb++) { if (!pSrcDb->bHaveStats) { continue; } // Find or add the store in the destination store array. if (RC_BAD( rc = flmStatGetDbByName( pDestStats, pSrcDb->pszDbName, uiLowDbStart, &pDestDb, NULL, &uiLowDbStart))) { goto Exit; } if (uiLowDbStart < pDestStats->uiNumDbStats - 1) { uiLowDbStart++; } if (RC_BAD( rc = flmUpdateDbStats( pDestDb, pSrcDb))) { goto Exit; } } Exit: // Unlock the destination's mutex, if there is one. if (pDestStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pDestStats->hMutex); } // Only clear the source statistics AFTER unlocking the destination // statistic's semaphore. This is just an attempt to be slightly more // efficient. It could be done before unlock the destination semaphore // and still be correct. But there is no sense in keeping the destination // semaphore locked. if (RC_OK( rc)) { flmStatReset( pSrcStats, TRUE, FALSE); } return( rc); } /**************************************************************************** Desc: This routine frees however many queries that are over the limit of the number we can save. Note: This routine will ALWAYS unlock the query mutex on leaving. It may be locked when entering. ****************************************************************************/ void flmFreeSavedQueries( FLMBOOL bMutexAlreadyLocked ) { QUERY_HDR * pQueriesToFree = NULL; // Must determine the queries to free inside the mutex lock and then free // them outside the mutex lock, because freeing a query may cause an // embedded query to be put into the list again, which will cause the // mutex to be locked again. if (!bMutexAlreadyLocked) { f_mutexLock( gv_FlmSysData.hQueryMutex); } while (gv_FlmSysData.uiQueryCnt > gv_FlmSysData.uiMaxQueries) { gv_FlmSysData.pOldestQuery = gv_FlmSysData.pOldestQuery->pPrev; gv_FlmSysData.uiQueryCnt--; } // Whatever is found after pOldestQuery should be freed. Unlink // those from the list and point to them with pQueriesToFree. if (!gv_FlmSysData.pOldestQuery) { pQueriesToFree = gv_FlmSysData.pNewestQuery; gv_FlmSysData.pNewestQuery = NULL; } else if (gv_FlmSysData.pOldestQuery->pNext) { pQueriesToFree = gv_FlmSysData.pOldestQuery->pNext; pQueriesToFree->pPrev = NULL; gv_FlmSysData.pOldestQuery->pNext = NULL; } f_mutexUnlock( gv_FlmSysData.hQueryMutex); // Now clean up each of the queries in the pQueriesToFree list. // This can be done outside the mutex lock because we are now // dealing with a completely local list that no other thread can // see. Also, the mutex must NOT be locked at this point because // flmCurFree may free an embedded query, which will want // to lock the mutex again. while (pQueriesToFree) { QUERY_HDR * pQueryHdrToFree = pQueriesToFree; pQueriesToFree = pQueriesToFree->pNext; flmCurFree( (CURSOR *)pQueryHdrToFree->hCursor, FALSE); f_free( &pQueryHdrToFree); } } /**************************************************************************** Desc: This routine saves a query so it can be analyzed later. ****************************************************************************/ void flmSaveQuery( HFCURSOR hCursor ) { QUERY_HDR * pQueryHdr = NULL; FLMBOOL bNeedToCleanup = TRUE; FLMBOOL bMutexLocked = FALSE; // Allocate memory for the new query if (RC_BAD( f_calloc( sizeof( QUERY_HDR), &pQueryHdr))) { goto Exit; } pQueryHdr->hCursor = hCursor; f_mutexLock( gv_FlmSysData.hQueryMutex); bMutexLocked = TRUE; // uiMaxQueries was originally checked outside of the mutex lock. // Make sure it is still non-zero. if (gv_FlmSysData.uiMaxQueries) { // Link query to head of list. bNeedToCleanup = FALSE; if ((pQueryHdr->pNext = gv_FlmSysData.pNewestQuery) != NULL) { pQueryHdr->pNext->pPrev = pQueryHdr; } else { gv_FlmSysData.pOldestQuery = pQueryHdr; } gv_FlmSysData.pNewestQuery = pQueryHdr; if (++gv_FlmSysData.uiQueryCnt > gv_FlmSysData.uiMaxQueries) { flmFreeSavedQueries( TRUE); // flmFreeSavedQueries will always unlock the mutex. bMutexLocked = FALSE; } } Exit: if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hQueryMutex); } // Must clean up the query if we didn't get it into the list for // some reason. if (bNeedToCleanup) { if (pQueryHdr) { f_free( &pQueryHdr); } flmCurFree( (CURSOR *)hCursor, FALSE); } } /**************************************************************************** Desc: This routine copies statistics from one FLM_STAT structure into another. This is used to retrieve statistics. ****************************************************************************/ FSTATIC RCODE flmStatCopy( FLM_STATS * pDestStats, FLM_STATS * pSrcStats ) { RCODE rc = FERR_OK; FLMUINT uiDb; DB_STATS * pDestDb; DB_STATS * pSrcDb; FLMUINT uiCount; FLMUINT uiLoop; DB_STATS * pDbStats; LFILE_STATS * pLFile; flmStatInit( pDestStats, FALSE); // If the source structure has a mutex, lock it. if (pSrcStats->hMutex != F_MUTEX_NULL) { f_mutexLock( pSrcStats->hMutex); } f_memcpy( pDestStats, pSrcStats, sizeof( FLM_STATS)); // Zero out the database array. We need to do this // so that if we get an error, we can correctly release memory for // the destination structure. pDestStats->hMutex = F_MUTEX_NULL; pDestStats->uiNumDbStats = 0; pDestStats->uiDbStatArraySize = 0; pDestStats->pDbStats = NULL; uiCount = 0; if (pSrcStats->uiNumDbStats) { for (uiLoop = 0, pDbStats = pSrcStats->pDbStats; uiLoop < pSrcStats->uiNumDbStats; uiLoop++, pDbStats++) { if (pDbStats->bHaveStats) { uiCount++; } } } if (uiCount) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( DB_STATS) * uiCount, &pDestStats->pDbStats))) { goto Exit; } for (uiLoop = 0, uiCount = 0, pDbStats = pSrcStats->pDbStats; uiLoop < pSrcStats->uiNumDbStats; uiLoop++, pDbStats++) { if (pDbStats->bHaveStats) { pDestDb = &pDestStats->pDbStats [uiCount]; f_memcpy( pDestDb, pDbStats, sizeof( DB_STATS)); // Zero out each store's LFILE statistics. We need to do this // so that if we get an error, we can correctly release memory for // the destination structure. pDestDb->uiNumLFileStats = 0; pDestDb->uiLFileStatArraySize = 0; pDestDb->pLFileStats = NULL; uiCount++; } } pDestStats->uiDbStatArraySize = pDestStats->uiNumDbStats = uiCount; } for (uiDb = pSrcStats->uiNumDbStats, pDestDb = pDestStats->pDbStats, pSrcDb = pSrcStats->pDbStats; uiDb; uiDb--, pSrcDb++) { if (!pSrcDb->bHaveStats) { continue; } pDestDb->uiNumLFileStats = 0; pDestDb->uiLFileStatArraySize = 0; pDestDb->pLFileStats = NULL; uiCount = 0; for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; uiLoop < pSrcDb->uiNumLFileStats; uiLoop++, pLFile++) { if (pLFile->bHaveStats) { uiCount++; } } if (uiCount) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( LFILE_STATS) * uiCount, &pDestDb->pLFileStats))) { goto Exit; } uiCount = 0; for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; uiLoop < pSrcDb->uiNumLFileStats; uiLoop++, pLFile++) { if (pLFile->bHaveStats) { f_memcpy( &pDestDb->pLFileStats [uiCount], pLFile, sizeof( LFILE_STATS)); uiCount++; } } pDestDb->uiLFileStatArraySize = pDestDb->uiNumLFileStats = uiCount; } pDestDb++; } Exit: // Unlock the source's mutex, if there is one. if (pSrcStats->hMutex != F_MUTEX_NULL) { f_mutexUnlock( pSrcStats->hMutex); } if (RC_BAD( rc)) { FlmFreeStats( pDestStats); } return( rc); } /**************************************************************************** Desc : Returns statistics that have been collected for a share. Notes: The statistics returned will be the statistics for ALL databases associated with the share structure. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmGetStats( FLM_STATS * pFlmStats) { RCODE rc = FERR_OK; // Get the statistics if( RC_BAD( rc = flmStatCopy( pFlmStats, &gv_FlmSysData.Stats))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine locates the appropriate BLOCKIO_STATS structure for a given block of data. NULL is returned if an appropriate one cannot be found. VISIT: uiBlkType passed in is a guess. Remove this parm and start using pBlk. ****************************************************************************/ BLOCKIO_STATS * flmGetBlockIOStatPtr( DB_STATS * pDbStats, LFILE_STATS * pLFileStats, FLMBYTE * pBlk, FLMUINT uiBlkType) { if (uiBlkType == BHT_FREE) { pDbStats->bHaveStats = TRUE; return( &pDbStats->AvailBlockStats); } else if (uiBlkType == BHT_LFH_BLK) { pDbStats->bHaveStats = TRUE; return( &pDbStats->LFHBlockStats); } else if (pLFileStats) { pLFileStats->bHaveStats = pDbStats->bHaveStats = TRUE; // Consider invalid type. if ((BH_GET_TYPE( pBlk) != BHT_LEAF) && (BH_GET_TYPE( pBlk) != BHT_NON_LEAF) && (BH_GET_TYPE( pBlk) != BHT_NON_LEAF_DATA) && (BH_GET_TYPE( pBlk) != BHT_NON_LEAF_COUNTS)) { return( &pLFileStats->LeafBlockStats); } if ((FB2UD( &(pBlk [BH_NEXT_BLK])) == BT_END) && (FB2UD( &(pBlk [BH_PREV_BLK])) == BT_END)) { return( &pLFileStats->RootBlockStats); } else if (BH_GET_TYPE( pBlk) != BHT_LEAF) { return( &pLFileStats->MiddleBlockStats); } else { return( &pLFileStats->LeafBlockStats); } } else { return( (BLOCKIO_STATS *)NULL); } } /******************************************************************** Desc: Determine if a given year is a leap year. *********************************************************************/ FINLINE FLMUINT flmLeapYear( FLMUINT uiYear) { if (uiYear % 4 != 0) { return( 0); } if (uiYear % 100 != 0) { return( 1); } if (uiYear % 400 != 0) { return( 0); } return( 1); } /******************************************************************** Desc: Calculate days in a given month of a given year. *********************************************************************/ FSTATIC FLMUINT flmDaysInMonth( FLMUINT uiYear, FLMUINT uiMonth ) { switch (uiMonth + 1) { case 4: case 6: case 9: case 11: return( 30); case 2: return( 28 + flmLeapYear( uiYear)); default: return( 31); } } /******************************************************************** Desc: Adjust the time. *********************************************************************/ FSTATIC void flmAdjustTime( F_TMSTAMP * pTime, FLMINT iStartPoint ) { switch (iStartPoint) { case 1: goto Adj_1; case 2: goto Adj_2; case 3: goto Adj_3; case 4: goto Adj_4; case 5: goto Adj_5; case 6: goto Adj_6; } Adj_1: if (pTime->hundredth >= 100) { pTime->second++; pTime->hundredth = 0; } Adj_2: if (pTime->second == 60) { pTime->minute++; pTime->second = 0; } Adj_3: if (pTime->minute == 60) { pTime->hour++; pTime->minute = 0; } Adj_4: if (pTime->hour == 24) { pTime->day++; pTime->hour = 0; } Adj_5: if ((FLMUINT)pTime->day > flmDaysInMonth( pTime->year, pTime->month)) { pTime->month++; pTime->day = 1; } Adj_6: if (pTime->month > 11) { pTime->year++; pTime->month = 1; } } /******************************************************************** Desc: Calculate the elapsed time, including milliseconds. *********************************************************************/ void flmAddElapTime( F_TMSTAMP * pStartTime, FLMUINT64 * pui64ElapMilli ) { F_TMSTAMP StartTime; F_TMSTAMP EndTime; FLMUINT uiSec = 0; FLMUINT uiHundredth = 0; f_timeGetTimeStamp( &EndTime); f_memcpy( &StartTime, pStartTime, sizeof( F_TMSTAMP)); if (StartTime.year < EndTime.year) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.day > 1) { uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - StartTime.day + 1) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } if (StartTime.month > 1) { while (StartTime.month <= 11) { uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, StartTime.month) * (FLMUINT)86400); StartTime.month++; } StartTime.year++; } while (StartTime.year < EndTime.year) { uiSec += (FLMUINT)((FLMUINT)(365 + flmLeapYear( StartTime.year)) * (FLMUINT)86400); StartTime.year++; } } if (StartTime.month < EndTime.month) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.day > 1) { uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - StartTime.day + 1) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } while (StartTime.month < EndTime.month) { uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, StartTime.month) * (FLMUINT)86400); StartTime.month++; } } if (StartTime.day < EndTime.day) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } uiSec += (FLMUINT)(EndTime.day - StartTime.day) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } if (StartTime.hour < EndTime.hour) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } uiSec += (FLMUINT)((EndTime.hour - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.minute < EndTime.minute) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } uiSec += (FLMUINT)((EndTime.minute - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.second < EndTime.second) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } uiSec += (FLMUINT)(EndTime.second - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.hundredth < EndTime.hundredth) { uiHundredth += (FLMUINT)(EndTime.hundredth - StartTime.hundredth); } if (uiSec) { *(pui64ElapMilli) += (FLMUINT64)(uiHundredth * 10 + uiSec * 1000); } else { *(pui64ElapMilli) += (FLMUINT64)(uiHundredth * 10); } } libflaim-4.9.966/src/fqeval.cpp0000644000175000017500000024343010510774540017644 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query evaluation // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqeval.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define IS_UNSIGNED( e) ((e) == FLM_UINT32_VAL || (e) == FLM_UINT64_VAL) #define IS_SIGNED( e) ((e) == FLM_INT32_VAL || (e) == FLM_INT64_VAL) FSTATIC FLMUINT flmCurEvalTrueFalse( FQATOM * pElm); FSTATIC RCODE flmCurGetAtomFromRec( FDB * pDb, F_Pool * pPool, FQATOM * pTreeAtom, FlmRecord * pRecord, QTYPES eFldType, FLMBOOL bGetAtomVals, FQATOM * pResult, FLMBOOL bHaveKey); FSTATIC RCODE flmFieldIterate( FDB * pDb, F_Pool * pPool, QTYPES eFldType, FQNODE * pOpCB, FlmRecord * pRecord, FLMBOOL bHaveKey, FLMBOOL bGetAtomVals, FLMUINT uiAction, FQATOM * pResult); FSTATIC RCODE flmCurEvalArithOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bGetNewField, FLMBOOL bHaveKey, FQATOM * pResult); FSTATIC RCODE flmCurEvalLogicalOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bHaveKey, FQATOM * pResult); #define IS_EXPORT_PTR(e) \ ((e) == FLM_TEXT_VAL || (e) == FLM_BINARY_VAL) FSTATIC void fqOpUUBitAND( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUBitOR( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUBitXOR( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUSMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSSMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSUMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUSDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSSDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSUDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUSMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSSMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSUMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUSPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSSPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSUPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUUMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpUSMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSSMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC void fqOpSUMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FSTATIC RCODE flmCurDoNeg( FQATOM * pResult); typedef void FQ_OPERATION( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult); FQ_OPERATION * FQ_ArithOpTable[ ((LAST_ARITH_OP - FIRST_ARITH_OP) + 1) * 4 ] = { /* U = Unsigned S = Signed U + U U + S S + U S + S */ /* BITAND */ fqOpUUBitAND, fqOpUUBitAND, fqOpUUBitAND, fqOpUUBitAND, /* BITOR */ fqOpUUBitOR, fqOpUUBitOR, fqOpUUBitOR, fqOpUUBitOR, /* BITXOR */ fqOpUUBitXOR, fqOpUUBitXOR, fqOpUUBitXOR, fqOpUUBitXOR, /* MULT */ fqOpUUMult, fqOpUSMult, fqOpSUMult, fqOpSSMult, /* DIV */ fqOpUUDiv, fqOpUSDiv, fqOpSUDiv, fqOpSSDiv, /* MOD */ fqOpUUMod, fqOpUSMod, fqOpSUMod, fqOpSSMod, /* PLUS */ fqOpUUPlus, fqOpUSPlus, fqOpSUPlus, fqOpSSPlus, /* MINUS */ fqOpUUMinus, fqOpUSMinus, fqOpSUMinus, fqOpSSMinus }; /*************************************************************************** Desc: Determines if number is the native type. ***************************************************************************/ FINLINE FLMBOOL isNativeNum( QTYPES eType) { return( eType == FLM_UINT32_VAL || eType == FLM_INT32_VAL ? TRUE : FALSE); } /*************************************************************************** Desc: Returns a 64-bit unsigned integer ***************************************************************************/ FINLINE FLMUINT64 fqGetUInt64( FQATOM * pValue) { if (pValue->eType == FLM_UINT32_VAL) { return( (FLMUINT64)pValue->val.ui32Val); } else if( pValue->eType == FLM_UINT64_VAL) { return( pValue->val.ui64Val); } else if( pValue->eType == FLM_INT64_VAL) { if( pValue->val.i64Val >= 0) { return( (FLMUINT64)pValue->val.i64Val); } } else if( pValue->eType == FLM_INT32_VAL) { if( pValue->val.i32Val >= 0) { return( (FLMUINT64)pValue->val.i32Val); } } flmAssert( 0); return( 0); } /*************************************************************************** Desc: Returns a 64-bit signed integer ***************************************************************************/ FINLINE FLMINT64 fqGetInt64( FQATOM * pValue) { if (pValue->eType == FLM_INT32_VAL) { return( (FLMINT64)pValue->val.i32Val); } else if( pValue->eType == FLM_INT64_VAL) { return( pValue->val.i64Val); } else if( pValue->eType == FLM_UINT32_VAL) { return( (FLMINT64)pValue->val.ui32Val); } else if( pValue->eType == FLM_UINT64_VAL) { if( pValue->val.ui64Val <= (FLMUINT64)FLM_MAX_INT64) { return( (FLMINT64)pValue->val.ui64Val); } } flmAssert( 0); return( 0); } /*************************************************************************** Desc: Performs the bit and operation ***************************************************************************/ FSTATIC void fqOpUUBitAND( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { if (isNativeNum( pLValue->eType) && isNativeNum( pRValue->eType)) { pResult->val.ui32Val = pLValue->val.ui32Val & pRValue->val.ui32Val; pResult->eType = FLM_UINT32_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) & fqGetUInt64( pRValue); pResult->eType = FLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the bit or operation ***************************************************************************/ FSTATIC void fqOpUUBitOR( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { if (isNativeNum( pLValue->eType) && isNativeNum( pRValue->eType)) { pResult->val.ui32Val = pLValue->val.ui32Val | pRValue->val.ui32Val; pResult->eType = FLM_UINT32_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) | fqGetUInt64( pRValue); pResult->eType = FLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the bit xor operation ***************************************************************************/ FSTATIC void fqOpUUBitXOR( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { if (isNativeNum( pLValue->eType) && isNativeNum( pRValue->eType)) { pResult->val.ui32Val = pLValue->val.ui32Val ^ pRValue->val.ui32Val; pResult->eType = FLM_UINT32_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) ^ fqGetUInt64( pRValue); pResult->eType = FLM_UINT64_VAL; } } /*************************************************************************** Desc: Put an unsigned result into a result atom. ***************************************************************************/ FINLINE void setUnsignedResult( FLMUINT64 ui64Result, FQATOM * pResult) { if (ui64Result <= (FLMUINT64)(FLM_MAX_UINT32)) { pResult->val.ui32Val = (FLMUINT32)ui64Result; pResult->eType = FLM_UINT32_VAL; } else { pResult->val.ui64Val = ui64Result; pResult->eType = FLM_UINT64_VAL; } } /*************************************************************************** Desc: Put a signed result into a result atom. ***************************************************************************/ FINLINE void setSignedResult( FLMINT64 i64Result, FQATOM * pResult) { if (i64Result >= (FLMINT64)(FLM_MIN_INT32) && i64Result <= (FLMINT64)(FLM_MAX_INT32)) { pResult->val.i32Val = (FLMINT32)i64Result; pResult->eType = FLM_INT32_VAL; } else { pResult->val.i64Val = i64Result; pResult->eType = FLM_INT64_VAL; } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpUUMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Result = fqGetUInt64( pLValue) * fqGetUInt64( pRValue); setUnsignedResult( ui64Result, pResult); } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpUSMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Right < 0) { i64Result = (FLMINT64)ui64Left * i64Right; setSignedResult( i64Result, pResult); } else { ui64Result = ui64Left * (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpSSMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Left < 0) { if (i64Right < 0) { if (i64Left == FLM_MIN_INT64) { if (i64Right == FLM_MIN_INT64) { // The result will actually overflow, but there is // nothing we can do about that. ui64Result = FLM_MAX_UINT64; } else { i64Right = -i64Right; ui64Result = ((FLMUINT64)(FLM_MAX_INT64) + 1) * (FLMUINT64)i64Right; } } else if (i64Right == FLM_MIN_INT64) { i64Left = -i64Left; ui64Result = (FLMUINT64)i64Left * ((FLMUINT64)(FLM_MAX_INT64) + 1); } else { i64Left = -i64Left; i64Right = -i64Right; ui64Result = (FLMUINT64)i64Left * (FLMUINT64)i64Right; } setUnsignedResult( ui64Result, pResult); } else { i64Result = i64Left * i64Right; setSignedResult( i64Result, pResult); } } else if (i64Right < 0) { i64Result = i64Left * i64Right; setSignedResult( i64Result, pResult); } else { ui64Result = (FLMUINT64)i64Left * (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpSUMult( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMINT64 i64Result; FLMUINT64 ui64Result; if (i64Left < 0) { i64Result = i64Left * (FLMINT64)ui64Right; setSignedResult( i64Result, pResult); } else { ui64Result = (FLMUINT64)i64Left * ui64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpUUDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; if (ui64Right) { ui64Result = ui64Left / ui64Right; setUnsignedResult( ui64Result, pResult); } else { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpUSDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Right < 0) { if (i64Right == FLM_MIN_INT64) { i64Result = -((FLMINT64)(ui64Left / ((FLMUINT64)(FLM_MAX_INT64) + 1))); } else { i64Right = -i64Right; i64Result = -((FLMINT64)(ui64Left / (FLMUINT64)i64Right)); } setSignedResult( i64Result, pResult); } else if (!i64Right) { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } else { ui64Result = ui64Left / (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpSSDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMINT64 i64Result; if (i64Right) { i64Result = i64Left / i64Right; setSignedResult( i64Result, pResult); } else { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpSUDiv( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (!ui64Right) { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } else if (i64Left < 0) { if (ui64Right >= (FLMUINT64)(FLM_MAX_INT64) + 1) { setUnsignedResult( 0, pResult); } else { i64Result = i64Left / (FLMINT64)ui64Right; setSignedResult( i64Result, pResult); } } else { ui64Result = (FLMUINT64)i64Left / ui64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpUUMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; if (ui64Right) { ui64Result = ui64Left % ui64Right; setUnsignedResult( ui64Result, pResult); } else { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpUSMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Right) { if (i64Right == FLM_MIN_INT64) { i64Result = -((FLMINT64)(ui64Left % ((FLMUINT64)(FLM_MAX_INT64) + 1))); } else { i64Right = -i64Right; i64Result = -((FLMINT64)(ui64Left % (FLMUINT64)i64Right)); } setSignedResult( i64Result, pResult); } else if (!i64Right) { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } else { ui64Result = ui64Left % (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpSSMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMINT64 i64Result; if (i64Right) { i64Result = i64Left % i64Right; setSignedResult( i64Result, pResult); } else { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpSUMod( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (!ui64Right) { pResult->val.ui32Val = 0; // Divide by ZERO case. pResult->eType = NO_TYPE; } else if (i64Left < 0) { if (ui64Right >= (FLMUINT64)(FLM_MAX_INT64) + 1) { setSignedResult( i64Left, pResult); } else { i64Result = i64Left % (FLMINT64)ui64Right; setSignedResult( i64Result, pResult); } } else { ui64Result = (FLMUINT64)i64Left % ui64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpUUPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Result = fqGetUInt64( pLValue) + fqGetUInt64( pRValue); setUnsignedResult( ui64Result, pResult); } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpUSPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Right < 0) { if (i64Right == FLM_MIN_INT64) { if (ui64Left < (FLMUINT64)(FLM_MAX_INT64) + 1) { if (!ui64Left) { i64Result = FLM_MIN_INT64; } else { i64Result = -((FLMINT64)((FLMUINT64)(FLM_MAX_INT64) + 1 - ui64Left)); } setSignedResult( i64Result, pResult); } else { ui64Result = ui64Left - (FLMUINT64)(FLM_MAX_INT64) - 1; setUnsignedResult( ui64Result, pResult); } } else { i64Right = -i64Right; if ((FLMUINT64)i64Right > ui64Left) { i64Result = -(i64Right - (FLMINT64)ui64Left); setSignedResult( i64Result, pResult); } else { ui64Result = ui64Left - (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } } else { ui64Result = ui64Left + (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpSSPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMINT64 i64Result; FLMUINT64 ui64Result; if (i64Left >= 0 && i64Right >= 0) { ui64Result = (FLMUINT64)i64Left + (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } else { i64Result = i64Left + i64Right; setSignedResult( i64Result, pResult); } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpSUPlus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Left < 0) { if (i64Left == FLM_MIN_INT64) { if (ui64Right < (FLMUINT64)(FLM_MAX_INT64) + 1) { if (!ui64Right) { i64Result = FLM_MIN_INT64; } else { i64Result = -((FLMINT64)((FLMUINT64)(FLM_MAX_INT64) + 1 - ui64Right)); } setSignedResult( i64Result, pResult); } else { ui64Result = ui64Right - (FLMUINT64)(FLM_MAX_INT64) - 1; setUnsignedResult( ui64Result, pResult); } } else { i64Left = -i64Left; if ((FLMUINT64)i64Left > ui64Right) { i64Result = -(i64Left - (FLMINT64)ui64Right); setSignedResult( i64Result, pResult); } else { ui64Result = ui64Right - (FLMUINT64)i64Left; setUnsignedResult( ui64Result, pResult); } } } else { ui64Result = ui64Right + (FLMUINT64)i64Left; setUnsignedResult( ui64Result, pResult); } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpUUMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if( ui64Left >= ui64Right) { ui64Result = ui64Left - ui64Right; setUnsignedResult( ui64Result, pResult); } else { i64Result = -((FLMINT64)(ui64Right - ui64Left)); setSignedResult( i64Result, pResult); } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpUSMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMUINT64 ui64Left = fqGetUInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Right < 0) { if (i64Right == FLM_MIN_INT64) { ui64Result = ui64Left + (FLMUINT64)FLM_MAX_INT64 + 1; } else { i64Right = -i64Right; ui64Result = ui64Left + (FLMUINT64)i64Right; } setUnsignedResult( ui64Result, pResult); } else { if( ui64Left >= (FLMUINT64)i64Right) { ui64Result = ui64Left - (FLMUINT64)i64Right; setUnsignedResult( ui64Result, pResult); } else { i64Result = -((FLMINT64)(i64Right - (FLMINT64)ui64Left)); setSignedResult( i64Result, pResult); } } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpSSMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMINT64 i64Right = fqGetInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Left < 0) { if (i64Right >= 0) { i64Result = i64Left - i64Right; setSignedResult( i64Result, pResult); } else if (i64Right == FLM_MIN_INT64) { if (i64Left == FLM_MIN_INT64) { ui64Result = 0; } else { i64Left = -i64Left; ui64Result = (FLMUINT64)FLM_MAX_INT64 + 1 - (FLMUINT64)i64Left; } setUnsignedResult( ui64Result, pResult); } else { i64Result = i64Left - i64Right; setSignedResult( i64Result, pResult); } } else if (i64Right < 0) { if (i64Right == FLM_MIN_INT64) { ui64Result = (FLMUINT64)i64Left + (FLMUINT64)(FLM_MAX_INT64) + 1; } else { i64Right = -i64Right; ui64Result = (FLMUINT64)i64Left + (FLMUINT64)i64Right; } setUnsignedResult( ui64Result, pResult); } else { i64Result = i64Left - i64Right; setSignedResult( i64Result, pResult); } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpSUMinus( FQATOM * pLValue, FQATOM * pRValue, FQATOM * pResult) { FLMINT64 i64Left = fqGetInt64( pLValue); FLMUINT64 ui64Right = fqGetUInt64( pRValue); FLMUINT64 ui64Result; FLMINT64 i64Result; if (i64Left < 0) { i64Result = i64Left - (FLMINT64)ui64Right; setSignedResult( i64Result, pResult); } else { if( (FLMUINT64)i64Left >= ui64Right) { ui64Result = (FLMUINT64)i64Left - ui64Right; setUnsignedResult( ui64Result, pResult); } else { i64Result = -((FLMINT64)(ui64Right - (FLMUINT64)i64Left)); setSignedResult( i64Result, pResult); } } } /**************************************************************************** Desc: Evaluates a list of QATOM elements, and returns a complex boolean based on their contents. Ret: FLM_TRUE if all elements have nonzero numerics or nonempty buffers. FLM_FALSE if all contents are zero or empty. FLM_UNK if any QATOM is of type FLM_UNKNOWN. Any combination of the preceeding values if their corresponding criteria are met. ****************************************************************************/ FSTATIC FLMUINT flmCurEvalTrueFalse( FQATOM * pQAtom) { FQATOM * pTmpQAtom; FLMUINT uiTrueFalse = 0; for (pTmpQAtom = pQAtom; pTmpQAtom; pTmpQAtom = pTmpQAtom->pNext) { if (IS_BUF_TYPE( pTmpQAtom->eType)) { if (pTmpQAtom->uiBufLen > 0) { uiTrueFalse |= FLM_TRUE; } else { uiTrueFalse |= FLM_FALSE; } } else { switch (pTmpQAtom->eType) { case FLM_BOOL_VAL: uiTrueFalse |= pTmpQAtom->val.uiBool; break; case FLM_UNKNOWN: uiTrueFalse |= FLM_UNK; break; case FLM_INT32_VAL: if (pTmpQAtom->val.i32Val) { uiTrueFalse |= FLM_TRUE; } else { uiTrueFalse |= FLM_FALSE; } break; case FLM_INT64_VAL: if (pTmpQAtom->val.i64Val) { uiTrueFalse |= FLM_TRUE; } else { uiTrueFalse |= FLM_FALSE; } break; case FLM_UINT32_VAL: if (pTmpQAtom->val.ui32Val) { uiTrueFalse |= FLM_TRUE; } else { uiTrueFalse |= FLM_FALSE; } break; case FLM_UINT64_VAL: if (pTmpQAtom->val.ui64Val) { uiTrueFalse |= FLM_TRUE; } else { uiTrueFalse |= FLM_FALSE; } break; default: goto Exit; } } if (uiTrueFalse == FLM_ALL_BOOL) { break; } } Exit: return (uiTrueFalse); } /**************************************************************************** Desc: Gets a value from the passed-in record field and stuffs it into the passed-in FQATOM. ****************************************************************************/ RCODE flmCurGetAtomVal( FlmRecord * pRecord, void * pField, F_Pool * pPool, QTYPES eFldType, FQATOM * pResult) { RCODE rc = FERR_OK; FLMUINT uiType = 0; if (pField) { uiType = pRecord->getDataType( pField); if (uiType == FLM_BLOB_TYPE) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } switch (eFldType) { case FLM_TEXT_VAL: { if (!pField) { // Default value pResult->uiBufLen = 0; pResult->val.pucBuf = NULL; } else { pResult->uiBufLen = pRecord->getDataLength( pField); if (pResult->uiBufLen) { pResult->val.pucBuf = (FLMBYTE *) pRecord->getDataPtr( pField); pResult->pFieldRec = pRecord; } else { if( RC_BAD( rc = pPool->poolAlloc( 1, (void **)&pResult->val.pucBuf))) { rc = RC_SET( FERR_MEM); break; } pResult->val.pucBuf[0] = 0; } } pResult->eType = FLM_TEXT_VAL; break; } case FLM_INT32_VAL: case FLM_INT64_VAL: case FLM_UINT32_VAL: case FLM_UINT64_VAL: case FLM_REC_PTR_VAL: { if (!pField || pRecord->getDataLength( pField) == 0) { // Default value pResult->val.ui32Val = 0; eFldType = FLM_UINT32_VAL; } else if (uiType == FLM_NUMBER_TYPE || uiType == FLM_TEXT_TYPE) { if (RC_OK( rc = pRecord->getUINT32( pField, &pResult->val.ui32Val))) { eFldType = FLM_UINT32_VAL; } else if (rc == FERR_CONV_NUM_OVERFLOW) { rc = pRecord->getUINT64( pField, &pResult->val.ui64Val); eFldType = FLM_UINT64_VAL; } else if (rc == FERR_CONV_NUM_UNDERFLOW) { if (RC_OK( rc= pRecord->getINT32( pField, &pResult->val.i32Val))) { eFldType = FLM_INT32_VAL; } else if (rc == FERR_CONV_NUM_UNDERFLOW) { rc = pRecord->getINT64( pField, &pResult->val.i64Val); eFldType = FLM_INT64_VAL; } } } else if (uiType == FLM_CONTEXT_TYPE) { rc = pRecord->getUINT32( pField, &pResult->val.ui32Val); eFldType = FLM_REC_PTR_VAL; } else { rc = RC_SET( FERR_CONV_BAD_SRC_TYPE); } if (RC_OK( rc)) { pResult->eType = eFldType; } break; } case FLM_BINARY_VAL: { if (pField) { pResult->uiBufLen = pRecord->getDataLength( pField); } else { pResult->uiBufLen = 0; } if (!pResult->uiBufLen) { pResult->val.pucBuf = NULL; } else { pResult->val.pucBuf = (FLMBYTE *) pRecord->getDataPtr( pField); pResult->pFieldRec = pRecord; } pResult->eType = FLM_BINARY_VAL; break; } // No type -- use the type in the passed-in node. case NO_TYPE: { // At this point, if we are attempting to get a default value, but // don't know the type, it is because both sides of the operand are // unknown, so we need to return no type. if (!pField) { pResult->eType = NO_TYPE; } else { switch (uiType) { case FLM_TEXT_TYPE: { pResult->uiBufLen = pRecord->getDataLength( pField); if (pResult->uiBufLen) { pResult->val.pucBuf = (FLMBYTE *) pRecord->getDataPtr( pField); pResult->pFieldRec = pRecord; } else { if( RC_BAD( rc = pPool->poolAlloc( 1, (void **)&pResult->val.pucBuf))) { break; } pResult->val.pucBuf[0] = 0; } pResult->eType = FLM_TEXT_VAL; break; } case FLM_BINARY_TYPE: { if (pField) { pResult->uiBufLen = pRecord->getDataLength( pField); } else { pResult->uiBufLen = 0; } if (!pResult->uiBufLen) { pResult->val.pucBuf = NULL; } else { pResult->val.pucBuf = (FLMBYTE *) pRecord->getDataPtr( pField); pResult->pFieldRec = pRecord; } pResult->eType = FLM_BINARY_VAL; break; } case FLM_NUMBER_TYPE: { if (RC_OK( rc = pRecord->getUINT32( pField, &pResult->val.ui32Val))) { pResult->eType = FLM_UINT32_VAL; } else if (rc == FERR_CONV_NUM_UNDERFLOW) { if (RC_OK( rc = pRecord->getINT32( pField, &pResult->val.i32Val))) { pResult->eType = FLM_INT32_VAL; } else if (rc == FERR_CONV_NUM_UNDERFLOW) { if (RC_OK( rc = pRecord->getINT64( pField, &pResult->val.i64Val))) { pResult->eType = FLM_INT64_VAL; } } } else if (rc == FERR_CONV_NUM_OVERFLOW) { if (RC_OK( rc = pRecord->getUINT64( pField, &pResult->val.ui64Val))) { pResult->eType = FLM_UINT64_VAL; } } break; } case FLM_CONTEXT_TYPE: { if (RC_OK( rc = pRecord->getUINT32( pField, &(pResult->val.ui32Val)))) { pResult->eType = FLM_UINT32_VAL; } break; } } } break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); break; } } Exit: pResult->uiFlags &= ~(FLM_IS_RIGHT_TRUNCATED_DATA | FLM_IS_LEFT_TRUNCATED_DATA); if (RC_OK( rc) && pField) { if (pRecord->isRightTruncated( pField)) { pResult->uiFlags |= FLM_IS_RIGHT_TRUNCATED_DATA; } if (pRecord->isLeftTruncated( pField)) { pResult->uiFlags |= FLM_IS_LEFT_TRUNCATED_DATA; } } return (rc); } /**************************************************************************** Desc: Given a list of FQATOMs containing alternate field paths, finds those field paths in a compound record and creates a list of FQATOMs from the contents of those paths. ****************************************************************************/ FSTATIC RCODE flmCurGetAtomFromRec( FDB * pDb, F_Pool * pPool, FQATOM * pTreeAtom, FlmRecord * pRecord, QTYPES eFldType, FLMBOOL bGetAtomVals, FQATOM * pResult, FLMBOOL bHaveKey) { RCODE rc = FERR_OK; FQATOM * pTmpResult = NULL; void * pvField = NULL; FLMUINT uiLastLevelOneFieldPos = 0; FLMUINT * puiFldPath; FLMUINT uiCurrFieldPath[ GED_MAXLVLNUM + 1]; FLMUINT uiFieldLevel; FLMUINT uiTmp; FLMUINT uiLeafFldNum; FLMUINT uiRecFldNum; FLMBOOL bFound; FLMBOOL bSavedInvisTrans; FLMUINT uiResult; FLMBOOL bPathFromRoot; FLMBOOL bUseFieldIdLookupTable; FLMUINT * puiPToCPath; FLMUINT uiHighestLevel = 0; FLMUINT uiLevelOneFieldId; pResult->eType = NO_TYPE; if (pTreeAtom->val.QueryFld.puiFldPath [0] == FLM_MISSING_FIELD_TAG) { goto Exit; } if (!pRecord) { goto Exit; } flmAssert( !pTreeAtom->pNext); puiFldPath = pTreeAtom->val.QueryFld.puiFldPath; puiPToCPath = pTreeAtom->val.QueryFld.puiPToCPath; uiLevelOneFieldId = puiPToCPath [1]; // We are only going to do the path to root optimation if // the field path is specified as having to be from the root (FLM_ROOTED_PATH) // and it goes down to at least level 1 in the tree, and our record // has a field id table in it. bPathFromRoot = (!bHaveKey && (pTreeAtom->uiFlags & FLM_ROOTED_PATH)) ? TRUE : FALSE; bUseFieldIdLookupTable = (bPathFromRoot && pRecord->fieldIdTableEnabled() && uiLevelOneFieldId) ? TRUE : FALSE; if (*puiFldPath == FLM_RECID_FIELD) { pResult->eType = FLM_UINT32_VAL; pResult->val.ui32Val = (FLMUINT32)pRecord->getID(); goto Exit; } pvField = pRecord->root(); uiFieldLevel = 0; if (bPathFromRoot) { // Determine the highest level we need to go down to in the record. uiHighestLevel = 1; while (puiPToCPath [uiHighestLevel + 1]) { uiHighestLevel++; } if (puiPToCPath [0] != pRecord->getFieldID( pvField)) { goto Exit; } if (bUseFieldIdLookupTable) { if ((pvField = pRecord->findLevelOneField( uiLevelOneFieldId, FALSE, &uiLastLevelOneFieldPos)) == NULL) { goto Exit; } uiCurrFieldPath [0] = puiPToCPath [0]; uiFieldLevel = 1; } } uiLeafFldNum = puiFldPath[ 0]; for (;;) { uiRecFldNum = pRecord->getFieldID( pvField); uiCurrFieldPath[ uiFieldLevel] = uiRecFldNum; // When we are doing path from root, we only need to traverse // back up when we are on a field that is exactly at the highest level // we can go down to in the tree - no need to check any others. // If we are not doing bPathFromRoot, we check all node paths. if (uiRecFldNum == uiLeafFldNum && (!bPathFromRoot || uiFieldLevel == uiHighestLevel)) { bFound = TRUE; // We already know that puiFldPath[0] matches - it is the same // as uiLeafFldNum. Traverse back up the tree and see if // the rest of the path matches. for (uiTmp = 1; puiFldPath[ uiTmp]; uiTmp++) { if (!uiFieldLevel) { bFound = FALSE; break; } uiFieldLevel--; if (puiFldPath[ uiTmp] != uiCurrFieldPath[ uiFieldLevel]) { bFound = FALSE; break; } } // Found field in proper path. Get the value if requested, // otherwise set the result to FLM_TRUE and exit. If a // callback is set, do that first to see if it is REALLY // found. if (bFound && pTreeAtom->val.QueryFld.fnGetField) { CB_ENTER( pDb, &bSavedInvisTrans); rc = pTreeAtom->val.QueryFld.fnGetField( pTreeAtom->val.QueryFld.pvUserData, pRecord, (HFDB)pDb, pTreeAtom->val.QueryFld.puiFldPath, FLM_FLD_VALIDATE, NULL, &pvField, &uiResult); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } if (uiResult == FLM_FALSE) { bFound = FALSE; } else if (uiResult == FLM_UNK) { if (bHaveKey) { // bHaveKey means we are evaluating a key. There // should only be one occurrence of the field in the // key in this case. If the callback does not know // if the field really exists, we must defer judgement // on this one until we can fetch the record. Hence, // we force the result to be UNKNOWN. Note that it // must be set to UNKNOWN, even if this is a field exists // predicate (!bGetAtomVals). If we set it to NO_TYPE // and fall through to exist, it would get converted to // a FLM_BOOL_VAL of FALSE, which is NOT what we // want. pResult->eType = FLM_UNKNOWN; pResult->uiFlags = pTreeAtom->uiFlags & ~(FLM_IS_RIGHT_TRUNCATED_DATA | FLM_IS_LEFT_TRUNCATED_DATA); if (pvField) { if (pRecord->isRightTruncated( pvField)) { pResult->uiFlags |= FLM_IS_RIGHT_TRUNCATED_DATA; } if (pRecord->isLeftTruncated( pvField)) { pResult->uiFlags |= FLM_IS_LEFT_TRUNCATED_DATA; } } // Better not be multiple results in this case because // we are evaluating a key. flmAssert( pResult->pNext == NULL); pResult->pNext = NULL; goto Exit; } else { bFound = FALSE; } } } if (bFound) { if (!bGetAtomVals) { pResult->eType = FLM_BOOL_VAL; pResult->val.uiBool = FLM_TRUE; goto Exit; } if (!pTmpResult) { pTmpResult = pResult; } else if (pTmpResult->eType) { if( RC_BAD( rc = pPool->poolCalloc( sizeof( FQATOM), (void **)&pTmpResult->pNext))) { goto Exit; } pTmpResult = pTmpResult->pNext; } pTmpResult->uiFlags = pTreeAtom->uiFlags; if ((rc = flmCurGetAtomVal( pRecord, pvField, pPool, eFldType, pTmpResult)) == FERR_CURSOR_SYNTAX) { goto Exit; } } } // Get the next field to process. If bPathFromRoot is set, we will skip // any fields that are at too high of levels in the record. // If bUseFieldIdLookupTable is set, it means // that when we get back up to level one fields, we should call the // API to get the next level one field. for (;;) { if ((pvField = pRecord->next( pvField)) == NULL) { break; } uiFieldLevel = pRecord->getLevel( pvField); if (!bPathFromRoot) { break; } if (uiFieldLevel > uiHighestLevel) { continue; } if (bUseFieldIdLookupTable && uiFieldLevel == 1) { pvField = pRecord->nextLevelOneField( &uiLastLevelOneFieldPos, TRUE); } break; } // If the end of the record has been reached, and the last field // value searched for was not found, unlink it from the result list. if (!pvField) { if (pTmpResult && pTmpResult != pResult && pTmpResult->eType == NO_TYPE) { FQATOM * pTmp; for (pTmp = pResult; pTmp && pTmp->pNext != pTmpResult; pTmp = pTmp->pNext) { ; } pTmp->pNext = NULL; } break; } } Exit: // If no match was found anywhere, set the result to FLM_UNKNOWN if field // content was requested, or FLM_FALSE if field existence was to be tested. if (pResult->eType == NO_TYPE) { if (bGetAtomVals && !bHaveKey && !pTreeAtom->val.QueryFld.fnGetField && (pTreeAtom->uiFlags & FLM_USE_DEFAULT_VALUE)) { rc = flmCurGetAtomVal( pRecord, NULL, pPool, eFldType, pResult); } else { if (bGetAtomVals || bHaveKey) { pResult->eType = FLM_UNKNOWN; } else { pResult->eType = FLM_BOOL_VAL; pResult->val.uiBool = FLM_FALSE; } pResult->uiFlags = pTreeAtom->uiFlags; } } return( rc); } /**************************************************************************** Desc: Iterate to the next occurrance of a field. ****************************************************************************/ FSTATIC RCODE flmFieldIterate( FDB * pDb, F_Pool * pPool, QTYPES eFldType, FQNODE * pOpCB, FlmRecord * pRecord, FLMBOOL bHaveKey, FLMBOOL bGetAtomVals, FLMUINT uiAction, FQATOM * pResult) { RCODE rc = FERR_OK; FlmRecord * pFieldRec = NULL; void * pField = NULL; FLMBOOL bSavedInvisTrans; if (bHaveKey) { // bHaveKey is TRUE when we are evaluating a key instead of the // full record. In this case, it will not be possible for the // callback function to get all of the values - so we simply return // unknown, which will be handled by the outside. If the entire // query evaluates to unknown, FLAIM will fetch the record and // evaluate the entire thing. This is the safe route to take in this // case. pResult->eType = FLM_UNKNOWN; } else { CB_ENTER( pDb, &bSavedInvisTrans); rc = pOpCB->pQAtom->val.QueryFld.fnGetField( pOpCB->pQAtom->val.QueryFld.pvUserData, pRecord, (HFDB) pDb, pOpCB->pQAtom->val.QueryFld.puiFldPath, uiAction, &pFieldRec, &pField, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } if (!pField) { if (!bGetAtomVals) { pResult->eType = FLM_BOOL_VAL; pResult->val.uiBool = FLM_FALSE; } else { if ((pOpCB->pQAtom->uiFlags & FLM_USE_DEFAULT_VALUE) && (uiAction == FLM_FLD_FIRST)) { if (RC_BAD( rc = flmCurGetAtomVal( pFieldRec, NULL, pPool, eFldType, pResult))) { goto Exit; } } else { pResult->eType = FLM_UNKNOWN; } } } else { if (!bGetAtomVals) { pResult->eType = FLM_BOOL_VAL; pResult->val.uiBool = FLM_TRUE; } else if (RC_BAD( rc = flmCurGetAtomVal( pFieldRec, pField, pPool, eFldType, pResult))) { goto Exit; } } } Exit: return (rc); } /**************************************************************************** Desc: Performs arithmetic operations on stack element lists. ****************************************************************************/ FSTATIC RCODE flmCurEvalArithOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bGetNewField, FLMBOOL bHaveKey, FQATOM * pResult) { RCODE rc = FERR_OK; FQNODE * pTmpQNode; FQATOM Lhs; FQATOM Rhs; FQATOM * pTmpQAtom; FQATOM * pRhs; FQATOM * pLhs; FQATOM * pFirstRhs; QTYPES eType; QTYPES eFldType = NO_TYPE; FLMBOOL bSecondOperand = FALSE; FQNODE * pRightOpCB = NULL; FQNODE * pLeftOpCB = NULL; FQNODE * pOpCB = NULL; F_Pool * pTmpPool = &pDb->TempPool; FLMBOOL bSavedInvisTrans; RCODE TempRc; if ((pTmpQNode = pQNode->pChild) == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); return (rc); } pLhs = &Lhs; pRhs = &Rhs; pLhs->pNext = NULL; pLhs->pFieldRec = NULL; pLhs->eType = NO_TYPE; pLhs->uiBufLen = 0; pLhs->val.ui32Val = 0; pRhs->pNext = NULL; pRhs->pFieldRec = NULL; pRhs->eType = NO_TYPE; pRhs->uiBufLen = 0; pRhs->val.ui32Val = 0; // Get the two operands (may be multiple values per operand) pTmpQAtom = pLhs; Get_Operand: eType = GET_QNODE_TYPE( pTmpQNode); if (IS_FLD_CB( eType, pTmpQNode)) { eType = FLM_CB_FLD; } if (IS_VAL( eType)) { if (bSecondOperand) { pRhs = pTmpQNode->pQAtom; } else { pLhs = pTmpQNode->pQAtom; } } else if (eType == FLM_FLD_PATH || eType == FLM_CB_FLD) { if (bSecondOperand) { eFldType = pLhs->eType; if (eType == FLM_CB_FLD) { pOpCB = pRightOpCB = pTmpQNode; } } else { if (pTmpQNode->pNextSib == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } eFldType = GET_QNODE_TYPE( pTmpQNode->pNextSib); if (eType == FLM_CB_FLD) { pOpCB = pLeftOpCB = pTmpQNode; } } if (!IS_VAL( eFldType)) { eFldType = NO_TYPE; } if (eType == FLM_CB_FLD) { // Get the first occurrence of the field. if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_FIRST, pTmpQAtom))) { goto Exit; } } else { if (RC_BAD( rc = flmCurGetAtomFromRec( pDb, pTmpPool, pTmpQNode->pQAtom, pRecord, eFldType, TRUE, pTmpQAtom, bHaveKey))) { goto Exit; } } } else if (IS_ARITH_OP( eType)) { // Recursive call if (RC_BAD( rc = flmCurEvalArithOp( pDb, pSubQuery, pRecord, pTmpQNode, eType, bGetNewField, bHaveKey, pTmpQAtom))) { goto Exit; } } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (!bSecondOperand) { if (eOp == FLM_NEG_OP) { pResult = pTmpQAtom; flmCurDoNeg( pResult); goto Exit; } else { if (pTmpQNode->pNextSib == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } pTmpQNode = pTmpQNode->pNextSib; pTmpQAtom = pRhs; bSecondOperand = TRUE; goto Get_Operand; } } // Now do the operation using our operators pFirstRhs = pRhs; pTmpQAtom = pResult; for (;;) { if (pLhs->eType == FLM_UNKNOWN || pRhs->eType == FLM_UNKNOWN) { pTmpQAtom->eType = FLM_UNKNOWN; } else { FQ_OPERATION * fnOp; FLMUINT uiOffset = 0; if( IS_UNSIGNED( pLhs->eType)) { if( IS_UNSIGNED( pRhs->eType)) { uiOffset = 0; } else if( IS_SIGNED( pRhs->eType)) { uiOffset = 1; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } else if( IS_SIGNED( pLhs->eType)) { if( IS_UNSIGNED( pRhs->eType)) { uiOffset = 2; } else if( IS_SIGNED( pRhs->eType)) { uiOffset = 3; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } fnOp = FQ_ArithOpTable[ ((((FLMUINT)eOp) - FIRST_ARITH_OP) * 4) + uiOffset]; fnOp( pLhs, pRhs, pTmpQAtom); } // Doing contextless, do them all - loop through right hand // operands, then left hand operands. // // Get the next right hand operand. if (!pRightOpCB) { pRhs = pRhs->pNext; } else if (pRhs->eType == FLM_UNKNOWN) { pRhs = NULL; } else { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pRightOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_NEXT, pRhs))) { goto Exit; } if (pRhs->eType == FLM_UNKNOWN) { pRhs = NULL; } } // If no more right hand side, get the next left hand side, and // reset the right hand side. if (!pRhs) { if (!pLeftOpCB) { pLhs = pLhs->pNext; } else if (pLhs->eType == FLM_UNKNOWN) { pLhs = NULL; } else { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pLeftOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_NEXT, pLhs))) { goto Exit; } if (pLhs->eType == FLM_UNKNOWN) { pLhs = NULL; } } if (!pLhs) { break; } // Reset the right hand side back to first. if (pRightOpCB) { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pRightOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_FIRST, pRhs))) { goto Exit; } } else { pRhs = pFirstRhs; } } // Set up for next result if( RC_BAD( rc = pTmpPool->poolCalloc( sizeof( FQATOM), (void **)&pTmpQAtom->pNext))) { goto Exit; } pTmpQAtom = pTmpQAtom->pNext; } Exit: // Clean up any field callbacks. if (pLeftOpCB) { CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pLeftOpCB->pQAtom->val.QueryFld.fnGetField( pLeftOpCB->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pLeftOpCB->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc)) { if (RC_OK( rc)) { rc = TempRc; } } } if (pRightOpCB) { CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pRightOpCB->pQAtom->val.QueryFld.fnGetField( pRightOpCB->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pRightOpCB->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc)) { if (RC_OK( rc)) { rc = TempRc; } } } return (rc); } /**************************************************************************** Desc: Performs a comparison operation on two operands, one or both of which can be FLM_UNKNOWN. ****************************************************************************/ void flmCompareOperands( FLMUINT uiLang, FQATOM * pLhs, FQATOM * pRhs, QTYPES eOp, FLMBOOL bResolveUnknown, FLMBOOL bForEvery, FLMBOOL bNotted, FLMBOOL bHaveKey, FLMUINT * puiTrueFalse) { if (pLhs->eType == FLM_UNKNOWN || pRhs->eType == FLM_UNKNOWN) { // If we are not resolving predicates with unknown operands, return // FLM_UNK. if (bHaveKey || !bResolveUnknown) { *puiTrueFalse = FLM_UNK; } else if (bNotted) { // If bNotted is TRUE, the result will be inverted on the // outside, so we need to set it to the opposite of what we want // it to ultimately be. *puiTrueFalse = (bForEvery ? FLM_FALSE : FLM_TRUE); } else { *puiTrueFalse = (bForEvery ? FLM_TRUE : FLM_FALSE); } } // At this point, both operands are known to be present. The // comparison will therefore be performed according to the operator // specified. else { switch (eOp) { case FLM_EQ_OP: { // OPTIMIZATION: for UINT32 compares avoid func call by doing // compare here! if (pLhs->eType == FLM_UINT32_VAL && pRhs->eType == FLM_UINT32_VAL) { *puiTrueFalse = (FQ_COMPARE( pLhs->val.ui32Val, pRhs->val.ui32Val) == 0) ? FLM_TRUE : FLM_FALSE; } else { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) == 0) ? FLM_TRUE : FLM_FALSE; } break; } case FLM_MATCH_OP: { if ((pLhs->uiFlags & FLM_COMP_WILD) || (pRhs->uiFlags & FLM_COMP_WILD)) { *puiTrueFalse = flmCurDoMatchOp( pLhs, pRhs, uiLang, FALSE, FALSE); } else { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) == 0) ? FLM_TRUE : FLM_FALSE; } break; } case FLM_MATCH_BEGIN_OP: { *puiTrueFalse = flmCurDoMatchOp( pLhs, pRhs, uiLang, FALSE, TRUE); break; } case FLM_MATCH_END_OP: { *puiTrueFalse = flmCurDoMatchOp( pLhs, pRhs, uiLang, TRUE, FALSE); break; } case FLM_NE_OP: { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) != 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_LT_OP: { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) < 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_LE_OP: { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) <= 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_GT_OP: { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) > 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_GE_OP: { *puiTrueFalse = (flmCurDoRelationalOp( pLhs, pRhs, uiLang) >= 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_CONTAINS_OP: { *puiTrueFalse = flmCurDoContainsOp( pLhs, pRhs, uiLang); break; } default: { // Syntax error. *puiTrueFalse = 0; flmAssert( 0); break; } } } } /**************************************************************************** Desc: Performs relational operations on stack elements. ****************************************************************************/ RCODE flmCurEvalCompareOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bHaveKey, FQATOM * pResult) { RCODE rc = FERR_OK; FQNODE * pTmpQNode; FQATOM * pTmpQAtom; FQATOM * pLhs; FQATOM * pRhs; FQATOM * pFirstRhs; FQATOM Lhs; FQATOM Rhs; QTYPES wTmpOp = eOp; QTYPES eType; QTYPES eFldType = NO_TYPE; FLMUINT uiTrueFalse = 0; FLMBOOL bSecondOperand; FLMBOOL bSwitchOperands = FALSE; FLMBOOL bGetNewField = FALSE; FLMBOOL bRightTruncated = FALSE; FLMBOOL bNotted = (pQNode->uiStatus & FLM_NOTTED) ? TRUE : FALSE; FLMBOOL bResolveUnknown = (pQNode->uiStatus & FLM_RESOLVE_UNK) ? TRUE : FALSE; FLMBOOL bForEvery = (pQNode->uiStatus & FLM_FOR_EVERY) ? TRUE : FALSE; FQNODE * pRightOpCB = NULL; FQNODE * pLeftOpCB = NULL; FQNODE * pOpCB = NULL; RCODE TempRc; FLMBOOL bSavedInvisTrans; F_Pool * pTmpPool = &pDb->TempPool; void * pvMark = pTmpPool->poolMark(); pResult->eType = FLM_BOOL_VAL; pResult->pNext = NULL; pResult->val.uiBool = 0; if (pQNode->pChild == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } pLhs = &Lhs; pRhs = &Rhs; pTmpQNode = pQNode->pChild; bSecondOperand = FALSE; f_memset( &Lhs, 0, sizeof(FQATOM)); f_memset( &Rhs, 0, sizeof(FQATOM)); pLhs->eType = pRhs->eType = NO_TYPE; // Get the two operands from the stack or passed-in record node pTmpQAtom = pLhs; Get_Operand: eType = GET_QNODE_TYPE( pTmpQNode); if (IS_FLD_CB( eType, pTmpQNode)) { eType = FLM_CB_FLD; } if (IS_VAL( eType)) { if (bSecondOperand) { pRhs = pTmpQNode->pQAtom; } else { pLhs = pTmpQNode->pQAtom; bSwitchOperands = TRUE; } } else if (eType == FLM_FLD_PATH || eType == FLM_CB_FLD) { if (bSecondOperand) { eFldType = pLhs->eType; if (eType == FLM_CB_FLD) { pOpCB = pRightOpCB = pTmpQNode; } } else { if (pTmpQNode->pNextSib == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } eFldType = GET_QNODE_TYPE( pTmpQNode->pNextSib); if (eType == FLM_CB_FLD) { pOpCB = pLeftOpCB = pTmpQNode; } } if (!IS_VAL( eFldType)) { eFldType = NO_TYPE; } if (eType == FLM_CB_FLD) { // Get the first occurrence of the field. if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_FIRST, pTmpQAtom))) { goto Exit; } if (pTmpQAtom->uiFlags & FLM_IS_RIGHT_TRUNCATED_DATA) { bRightTruncated = TRUE; } } else { if (RC_BAD( rc = flmCurGetAtomFromRec( pDb, pTmpPool, pTmpQNode->pQAtom, pRecord, eFldType, TRUE, pTmpQAtom, bHaveKey))) { goto Exit; } if (pTmpQAtom->uiFlags & FLM_IS_RIGHT_TRUNCATED_DATA) { bRightTruncated = TRUE; } } // Check to see if this field is a substring field in the index. If // it is, and it is not the first substring value in the field, and // we are doing a match begin or match operator, return FLM_FALSE - // we cannot evaluate anything except first substrings in these two // cases. NOTE: If we are evaluating a key and this is a callback // field, we don't need to worry about this condition, because the // CB field will have been set up to return unknown. if (bHaveKey && (pTmpQAtom->uiFlags & FLM_IS_LEFT_TRUNCATED_DATA) && (eOp == FLM_MATCH_OP || eOp == FLM_MATCH_BEGIN_OP)) { pResult->val.uiBool = FLM_FALSE; goto Exit; } } else if (IS_ARITH_OP( eType)) { if (RC_BAD( rc = flmCurEvalArithOp( pDb, pSubQuery, pRecord, pTmpQNode, eType, bGetNewField, bHaveKey, pTmpQAtom))) { goto Exit; } } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } if (!bSecondOperand) { if (pTmpQNode->pNextSib == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } pTmpQNode = pTmpQNode->pNextSib; pTmpQAtom = pRhs; bSecondOperand = TRUE; goto Get_Operand; } // If necessary, reverse the operator to render the expression in the // form . if (bSwitchOperands) { if (REVERSIBLE( eOp)) { wTmpOp = DO_REVERSE( eOp); pLhs = &Rhs; pRhs = &Lhs; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } // Now do the operation using our operators. pFirstRhs = pRhs; for (;;) { FLMBOOL bDoComp = TRUE; // If this key piece is truncated, and the selection criteria can't // be evaluated as a result, read the record and start again. NOTE: // this will only happen if the field type is text or binary. if (bHaveKey && bRightTruncated && pRhs->eType != FLM_UNKNOWN && pLhs->eType != FLM_UNKNOWN) { // VISIT: We should be optimized to flunk or pass text compares. // The problems come with comparing only up to the first // wildcard. if (pLhs->eType != FLM_BINARY_VAL) { uiTrueFalse = FLM_UNK; } else { FLMINT iCompVal; // We better only compare binary types here. flmAssert( pRhs->eType == FLM_BINARY_VAL); iCompVal = f_memcmp( pLhs->val.pucBuf, pRhs->val.pucBuf, f_min( pLhs->uiBufLen, pRhs->uiBufLen)); if (!iCompVal) { // Lhs is the truncated key. If its length is <= to the // length of the Rhs, comparison must continue by fetching // the record. So, we set uiTrueFalse to FLM_UNK. // Otherwise, we know that the Lhs length is greater than // the Rhs, so we are able to complete the comparison even // though the key is truncated. if (pLhs->uiBufLen <= pRhs->uiBufLen) { uiTrueFalse = FLM_UNK; } else { iCompVal = 1; } } // iCompVal == 0 has been handled above. This means that // uiTrueFalse has been set to FLM_UNK. if (iCompVal) { switch (eOp) { case FLM_NE_OP: { // We know that iCompVal != 0 uiTrueFalse = FLM_TRUE; break; } case FLM_GT_OP: case FLM_GE_OP: { uiTrueFalse = (iCompVal > 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_LT_OP: case FLM_LE_OP: { uiTrueFalse = (iCompVal < 0) ? FLM_TRUE : FLM_FALSE; break; } case FLM_EQ_OP: default: { // We know that iCompVal != 0 uiTrueFalse = FLM_FALSE; break; } } } bDoComp = FALSE; } } else { flmCompareOperands( pSubQuery->uiLanguage, pLhs, pRhs, eOp, bResolveUnknown, bForEvery, bNotted, bHaveKey, &uiTrueFalse); } if (bNotted) { uiTrueFalse = (uiTrueFalse == FLM_TRUE) ? FLM_FALSE : (uiTrueFalse == FLM_FALSE) ? FLM_TRUE : FLM_UNK; } // For index keys - validate that the field is correct if the // compare returned true. Otherwise, set the result to unknown. // VISIT: This will not work for index keys that have more than one // field that needs to be validated. if (bDoComp && eType == FLM_FLD_PATH && uiTrueFalse == FLM_TRUE && bHaveKey) { FQATOM * pTreeAtom = pTmpQNode->pQAtom; FLMUINT uiResult; void * pField = NULL; CB_ENTER( pDb, &bSavedInvisTrans); rc = pTreeAtom->val.QueryFld.fnGetField( pTreeAtom->val.QueryFld.pvUserData, pRecord, (HFDB) pDb, pTreeAtom->val.QueryFld.puiFldPath, FLM_FLD_VALIDATE, NULL, &pField, &uiResult); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } else if (uiResult == FLM_UNK) { uiTrueFalse = FLM_UNK; } else if (uiResult == FLM_FALSE) { uiTrueFalse = FLM_FALSE; } } pResult->val.uiBool = uiTrueFalse; // Doing contextless, see if we need to process any more. If the // FOR EVERY flag is TRUE (universal quantifier), we quit when we // see a FALSE. If the FOR EVERY flag is FALSE (existential // quantifier), we quit when we see a TRUE. if ((bForEvery && uiTrueFalse == FLM_FALSE) || (!bForEvery && uiTrueFalse == FLM_TRUE)) { break; } // Get the next right hand operand. if (!pRightOpCB) { pRhs = pRhs->pNext; } else if (pRhs->eType == FLM_UNKNOWN) { pRhs = NULL; } else { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pRightOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_NEXT, pRhs))) { goto Exit; } if (pRhs->uiFlags & FLM_IS_RIGHT_TRUNCATED_DATA) { bRightTruncated = TRUE; } if (pRhs->eType == FLM_UNKNOWN) { pRhs = NULL; } } // If no more right hand side, get the next left hand side, and // reset the right hand side. if (!pRhs) { if (!pLeftOpCB) { pLhs = pLhs->pNext; } else if (pLhs->eType == FLM_UNKNOWN) { pLhs = NULL; } else { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pLeftOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_NEXT, pLhs))) { goto Exit; } if (pLhs->uiFlags & FLM_IS_RIGHT_TRUNCATED_DATA) { bRightTruncated = TRUE; } if (pLhs->eType == FLM_UNKNOWN) { pLhs = NULL; } } if (!pLhs) { break; } // Reset the right hand side to the first. if (pRightOpCB) { if (RC_BAD( rc = flmFieldIterate( pDb, pTmpPool, eFldType, pRightOpCB, pRecord, bHaveKey, TRUE, FLM_FLD_FIRST, pRhs))) { goto Exit; } if (pRhs->uiFlags & FLM_IS_RIGHT_TRUNCATED_DATA) { bRightTruncated = TRUE; } } else { pRhs = pFirstRhs; } } } Exit: // Clean up any field callbacks. if (pLeftOpCB) { CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pLeftOpCB->pQAtom->val.QueryFld.fnGetField( pLeftOpCB->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pLeftOpCB->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc)) { if (RC_OK( rc)) { rc = TempRc; } } } if (pRightOpCB) { CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pRightOpCB->pQAtom->val.QueryFld.fnGetField( pRightOpCB->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pRightOpCB->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc)) { if (RC_OK( rc)) { rc = TempRc; } } } pTmpPool->poolReset( pvMark); return (rc); } /**************************************************************************** Desc: Performs logical AND or OR operations ****************************************************************************/ FSTATIC RCODE flmCurEvalLogicalOp( FDB * pDb, SUBQUERY * pSubQuery, FlmRecord * pRecord, FQNODE * pQNode, QTYPES eOp, FLMBOOL bHaveKey, FQATOM * pResult) { RCODE rc = FERR_OK; FQATOM TmpQAtom; FQNODE * pTmpQNode; FQATOM * pTmpQAtom; QTYPES eType; FLMBOOL bSavedInvisTrans; FLMUINT uiTrueFalse; RCODE TempRc; pResult->eType = FLM_BOOL_VAL; pResult->pNext = NULL; pResult->val.uiBool = 0; if (pQNode->pChild == NULL) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } FLM_SET_RESULT( pQNode->uiStatus, 0); pTmpQNode = pQNode->pChild; Get_Operand: // Get the operand to process pTmpQAtom = &TmpQAtom; pTmpQAtom->pNext = NULL; pTmpQAtom->pFieldRec = NULL; pTmpQAtom->eType = NO_TYPE; pTmpQAtom->uiBufLen = 0; pTmpQAtom->val.ui32Val = 0; eType = GET_QNODE_TYPE( pTmpQNode); if (IS_FLD_CB( eType, pTmpQNode)) { eType = FLM_CB_FLD; } if (IS_VAL( eType)) { pTmpQAtom = pTmpQNode->pQAtom; } else if (eType == FLM_CB_FLD) { // Get the first occurrence of the field. if (RC_OK( rc = flmFieldIterate( pDb, &pDb->TempPool, NO_TYPE, pTmpQNode, pRecord, bHaveKey, FALSE, FLM_FLD_FIRST, pTmpQAtom))) { if (pTmpQNode->uiStatus & FLM_NOTTED && pTmpQAtom->eType == FLM_BOOL_VAL) { pTmpQAtom->val.uiBool = (pTmpQAtom->val.uiBool == FLM_TRUE) ? FLM_FALSE : FLM_TRUE; } } CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pTmpQNode->pQAtom->val.QueryFld.fnGetField( pTmpQNode->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pTmpQNode->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc) && RC_OK( rc)) { rc = TempRc; } if (RC_BAD( rc)) { goto Exit; } } else if (eType == FLM_FLD_PATH) { if (RC_BAD( rc = flmCurGetAtomFromRec( pDb, &pDb->TempPool, pTmpQNode->pQAtom, pRecord, NO_TYPE, FALSE, pTmpQAtom, bHaveKey))) { goto Exit; } // NOTE: pTmpQAtom could come back from this as an UNKNOWN now, // even though we are testing for field existence. This could happen // when we are testing a key and we have a callback, but the // callback cannot tell if the field instance is actually present or // not. if ((pTmpQNode->uiStatus & FLM_NOTTED) && (pTmpQAtom->eType == FLM_BOOL_VAL)) { pTmpQAtom->val.uiBool = (pTmpQAtom->val.uiBool == FLM_TRUE) ? FLM_FALSE : FLM_TRUE; } } else if (IS_LOG_OP( eType)) { // Traverse down the tree. pQNode = pTmpQNode; eOp = eType; FLM_SET_RESULT( pQNode->uiStatus, 0); pTmpQNode = pTmpQNode->pChild; goto Get_Operand; } else if (IS_COMPARE_OP( eType)) { if (RC_BAD( rc = flmCurEvalCompareOp( pDb, pSubQuery, pRecord, pTmpQNode, eType, bHaveKey, pTmpQAtom))) { goto Exit; } } else if (eType == FLM_USER_PREDICATE) { if (bHaveKey) { // Don't want to do the callback if we only have a key - because // the callback won't have access to all of the values from here. // The safe thing is to just return unknown. pResult->eType = FLM_UNKNOWN; goto Exit; } else { CB_ENTER( pDb, &bSavedInvisTrans); rc = pTmpQNode->pQAtom->val.pPredicate->testRecord( (HFDB) pDb, pRecord, pRecord->getID(), &pTmpQAtom->val.uiBool); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } pTmpQAtom->eType = FLM_BOOL_VAL; } } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // See what our TRUE/FALSE result is. uiTrueFalse = flmCurEvalTrueFalse( pTmpQAtom); // Traverse back up the tree, ORing or ANDing or NOTing this result as // necessary. for (;;) { // If ANDing and we have a FALSE result or ORing and we have a TRUE // result, the result can simply be propagated up the tree. if ((eOp == FLM_AND_OP && uiTrueFalse == FLM_FALSE) || (eOp == FLM_OR_OP && uiTrueFalse == FLM_TRUE)) { // We are done if we can go no higher in the tree. pTmpQNode = pQNode; if ((pQNode = pQNode->pParent) == NULL) { break; } eOp = GET_QNODE_TYPE( pQNode); } else if (pTmpQNode->pNextSib) { // Can only be one operand of a NOT operator. flmAssert( eOp != FLM_NOT_OP); // Save the left-hand side result into pQNode->uiStatus FLM_SET_RESULT( pQNode->uiStatus, uiTrueFalse); pTmpQNode = pTmpQNode->pNextSib; goto Get_Operand; } else // Processing results of right hand operand { FLMUINT uiRhs; if (eOp == FLM_AND_OP) { // FALSE case for AND operator has already been handled up // above. flmAssert( uiTrueFalse != FLM_FALSE); // AND the results from the left-hand side. Get left-hand // side result from pQNode. uiRhs = uiTrueFalse; uiTrueFalse = FLM_GET_RESULT( pQNode->uiStatus); // Perform logical AND operation. if (uiRhs & FLM_FALSE) { uiTrueFalse |= FLM_FALSE; } if (uiRhs & FLM_UNK) { uiTrueFalse |= FLM_UNK; } // If both left hand side and right hand side do not have // FLM_TRUE set, we must turn it off. if ((uiTrueFalse & FLM_TRUE) && (!(uiRhs & FLM_TRUE))) { uiTrueFalse &= (~(FLM_TRUE)); } } else if (eOp == FLM_OR_OP) { // TRUE case for OR operator better have been handled up // above. flmAssert( uiTrueFalse != FLM_TRUE); // OR the results from the left hand side. Get left-hand side // result from pQNode. uiRhs = uiTrueFalse; uiTrueFalse = FLM_GET_RESULT( pQNode->uiStatus); // Perform logical OR operation. if (uiRhs & FLM_TRUE) { uiTrueFalse |= FLM_TRUE; } if (uiRhs & FLM_UNK) { uiTrueFalse |= FLM_UNK; } // If both left hand side and right hand side do not have // FLM_FALSE set, we must turn it off. if ((uiTrueFalse & FLM_FALSE) && (!(uiRhs & FLM_FALSE))) { uiTrueFalse &= (~(FLM_FALSE)); } } else // (eOp == FLM_NOT_OP) { flmAssert( eOp == FLM_NOT_OP); // NOT the result if (uiTrueFalse == FLM_TRUE) { uiTrueFalse = FLM_FALSE; } else if (uiTrueFalse == FLM_FALSE) { uiTrueFalse = FLM_TRUE; } else if (uiTrueFalse == (FLM_UNK | FLM_TRUE)) { uiTrueFalse = FLM_FALSE | FLM_UNK; } else if (uiTrueFalse == (FLM_UNK | FLM_FALSE)) { uiTrueFalse = FLM_TRUE | FLM_UNK; } } // Traverse back up to the parent with this result. pTmpQNode = pQNode; // We are done if we are at the top of the tree. if ((pQNode = pQNode->pParent) == NULL) { break; } eOp = GET_QNODE_TYPE( pQNode); } } // At this point, we are done, because there is no higher to traverse // back up in the tree. pResult->val.uiBool = uiTrueFalse; Exit: return (rc); } /**************************************************************************** Desc: Checks a record that has been retrieved from the database to see if it matches the criteria specified in the query stack. ****************************************************************************/ RCODE flmCurEvalCriteria( CURSOR * pCursor, SUBQUERY * pSubQuery, FlmRecord * pRecord, FLMBOOL bHaveKey, FLMUINT * puiResult) { RCODE rc = FERR_OK; FQATOM Result; QTYPES eType; FDB * pDb = pCursor->pDb; FQNODE * pQNode; void * pTmpMark = pDb->TempPool.poolMark(); FLMUINT uiResult = 0; FQNODE * pOpCB = NULL; RCODE TempRc; // By definition, a NULL record doesn't match selection criteria. if (!pRecord) { uiResult = FLM_FALSE; goto Exit; } // Record's container ID must match the cursor's if (pRecord->getContainerID() != pCursor->uiContainer) { uiResult = FLM_FALSE; goto Exit; } if (!pSubQuery->pTree) { uiResult = FLM_TRUE; goto Exit; } // First check the record type if necessary, then verify that there // are search criteria to match against. if (pCursor->uiRecType) { void * pField = pRecord->root(); if (!pField || pCursor->uiRecType != pRecord->getFieldID( pField)) { uiResult = FLM_FALSE; goto Exit; } } pQNode = pSubQuery->pTree; f_memset( &Result, 0, sizeof(FQATOM)); eType = GET_QNODE_TYPE( pQNode); if (IS_FLD_CB( eType, pQNode)) { eType = FLM_CB_FLD; } if (IS_VAL( eType)) { uiResult = flmCurEvalTrueFalse( pQNode->pQAtom); } else if (eType == FLM_USER_PREDICATE) { if (bHaveKey) { // Don't want to do the callback if we only have a key - because // the callback won't have access to all of the values from here. // The safe thing is to just return unknown. uiResult = FLM_UNK; rc = FERR_OK; } else { FLMBOOL bSavedInvisTrans; CB_ENTER( pDb, &bSavedInvisTrans); rc = pQNode->pQAtom->val.pPredicate->testRecord( (HFDB) pDb, pRecord, pRecord->getID(), &uiResult); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } } } else { if (eType == FLM_CB_FLD) { // Get the first occurrence of the field. pOpCB = pQNode; if (RC_BAD( rc = flmFieldIterate( pDb, &pDb->TempPool, NO_TYPE, pQNode, pRecord, bHaveKey, FALSE, FLM_FLD_FIRST, &Result))) { goto Exit; } if (pQNode->uiStatus & FLM_NOTTED && Result.eType == FLM_BOOL_VAL) { Result.val.uiBool = (Result.val.uiBool == FLM_TRUE) ? FLM_FALSE : FLM_TRUE; } } else if (eType == FLM_FLD_PATH) { if (RC_BAD( rc = flmCurGetAtomFromRec( pDb, &pDb->TempPool, pQNode->pQAtom, pRecord, NO_TYPE, FALSE, &Result, bHaveKey))) { goto Exit; } // NOTE: Result could come back from this as an UNKNOWN now, // even though we are testing for field existence. This could // happen when we are testing a key and we have a callback, but // the callback cannot tell if the field instance is actually // present or not. if ((pQNode->uiStatus & FLM_NOTTED) && (Result.eType == FLM_BOOL_VAL)) { Result.val.uiBool = (Result.val.uiBool == FLM_TRUE) ? FLM_FALSE : FLM_TRUE; } } else if (IS_LOG_OP( eType)) { if (RC_BAD( rc = flmCurEvalLogicalOp( pDb, pSubQuery, pRecord, pQNode, eType, bHaveKey, &Result))) { goto Exit; } } else if (IS_COMPARE_OP( eType)) { if (RC_BAD( rc = flmCurEvalCompareOp( pDb, pSubQuery, pRecord, pQNode, eType, bHaveKey, &Result))) { goto Exit; } } else { uiResult = FLM_FALSE; rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } uiResult = flmCurEvalTrueFalse( &Result); if (!bHaveKey && uiResult == FLM_UNK) { uiResult = FLM_FALSE; } } Exit: if (rc == FERR_EOF_HIT) { rc = FERR_OK; } // Clean up any field callbacks. if (pOpCB) { FLMBOOL bSavedInvisTrans; CB_ENTER( pDb, &bSavedInvisTrans); TempRc = pOpCB->pQAtom->val.QueryFld.fnGetField( pOpCB->pQAtom->val.QueryFld.pvUserData, NULL, (HFDB) pDb, pOpCB->pQAtom->val.QueryFld.puiFldPath, FLM_FLD_RESET, NULL, NULL, NULL); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TempRc)) { if (RC_OK( rc)) { rc = TempRc; } } } pDb->TempPool.poolReset( pTmpMark); *puiResult = uiResult; return (rc); } /**************************************************************************** Desc: ****************************************************************************/ FSTATIC RCODE flmCurDoNeg( FQATOM * pResult) { RCODE rc = FERR_OK; FQATOM * pTmpQAtom; // Perform operation on list according to operand types for (pTmpQAtom = pResult; pTmpQAtom; pTmpQAtom = pTmpQAtom->pNext) { if (IS_UNSIGNED( pTmpQAtom->eType)) { if (isNativeNum( pTmpQAtom->eType)) { if (pTmpQAtom->val.ui32Val >= (FLMUINT)(FLM_MAX_INT32) + 1) { pTmpQAtom->eType = NO_TYPE; } else { pTmpQAtom->val.i32Val = -((FLMINT32)(pTmpQAtom->val.ui32Val)); pTmpQAtom->eType = FLM_INT32_VAL; } } else { if (pTmpQAtom->val.ui64Val >= (FLMUINT64)(FLM_MAX_INT64) + 1) { pTmpQAtom->eType = NO_TYPE; } else { pTmpQAtom->val.i64Val = -((FLMINT64)(pTmpQAtom->val.ui64Val)); pTmpQAtom->eType = FLM_INT64_VAL; } } } else if (IS_SIGNED( pTmpQAtom->eType)) { if (isNativeNum( pTmpQAtom->eType)) { pTmpQAtom->val.i32Val *= -1; } else { pTmpQAtom->val.i64Val *= -1; } } else if (pTmpQAtom->eType != FLM_UNKNOWN) { rc = RC_SET( FERR_CURSOR_SYNTAX); break; } } return (rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT flmCurDoMatchOp( FQATOM * pLhs, FQATOM * pRhs, FLMUINT uiLang, FLMBOOL bLeadingWildCard, FLMBOOL bTrailingWildCard) { FLMUINT uiFlags = pLhs->uiFlags | pRhs->uiFlags; FLMUINT uiTrueFalse = 0; // Verify operand types - non-text and non-binary return false if (!IS_BUF_TYPE( pLhs->eType) || !IS_BUF_TYPE( pRhs->eType)) { goto Exit; } // If one of the operands is binary, simply do a byte comparison of the // two values without regard to case or wildcards. if ((pLhs->eType == FLM_BINARY_VAL) || (pRhs->eType == FLM_BINARY_VAL)) { FLMUINT uiLen1; FLMUINT uiLen2; uiLen1 = pLhs->uiBufLen; uiLen2 = pRhs->uiBufLen; flmAssert( !bLeadingWildCard); if ((bTrailingWildCard) && (uiLen2 > uiLen1)) { uiLen2 = uiLen1; } uiTrueFalse = (FLMUINT) ( ( (uiLen1 == uiLen2) && (f_memcmp( pLhs->val.pucBuf, pRhs->val.pucBuf, uiLen1) == 0) ) ? (FLMUINT) FLM_TRUE : (FLMUINT) FLM_FALSE ); goto Exit; } // If wildcards are set, do a string search, first making necessary // adjustments for case sensitivity. ; // // NOTE: THIS IS MATCH BEGIN CASE WITHOUT WILD CARD. The non-wild case // for bMatchEntire (DO_MATCH) does NOT come through this section of // code. Rather, flmCurDoEQ is called instead of this routine in that // case. if (pLhs->eType == FLM_TEXT_VAL && pRhs->eType == FLM_TEXT_VAL) { // Always true if there is a wild card. uiTrueFalse = flmTextMatch( pLhs->val.pucBuf, pLhs->uiBufLen, pRhs->val.pucBuf, pRhs->uiBufLen, uiFlags, bLeadingWildCard, bTrailingWildCard, uiLang); } else { uiTrueFalse = FLM_FALSE; } Exit: return (uiTrueFalse); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT flmCurDoContainsOp( FQATOM * pLhs, FQATOM * pRhs, FLMUINT uiLang) { FLMBYTE * pResult = NULL; FLMUINT uiFlags = pLhs->uiFlags | pRhs->uiFlags; FLMUINT uiTrueFalse = 0; // Verify operands -- both should be buffered types if (!IS_BUF_TYPE( pLhs->eType) || !IS_BUF_TYPE( pRhs->eType)) { goto Exit; } // If one of the operands is binary, simply do a byte comparison of the // two values without regard to case or wildcards. if ((pLhs->eType == FLM_BINARY_VAL) || (pRhs->eType == FLM_BINARY_VAL)) { uiTrueFalse = FLM_FALSE; for (pResult = pLhs->val.pucBuf; (FLMUINT) (pResult - pLhs->val.pucBuf) < pLhs->uiBufLen; pResult++) { if ((*pResult == pRhs->val.pucBuf[0]) && (f_memcmp( pLhs->val.pucBuf, pRhs->val.pucBuf, pRhs->uiBufLen) == 0)) { uiTrueFalse = FLM_TRUE; goto Exit; } } goto Exit; } uiTrueFalse = flmTextMatch( pLhs->val.pucBuf, pLhs->uiBufLen, pRhs->val.pucBuf, pRhs->uiBufLen, uiFlags, TRUE, TRUE, uiLang); Exit: return (uiTrueFalse); } /**************************************************************************** Desc: ****************************************************************************/ FLMINT flmCurDoRelationalOp( FQATOM * pLhs, FQATOM * pRhs, FLMUINT uiLang) { FLMUINT uiFlags = pLhs->uiFlags | pRhs->uiFlags; FLMINT iCompVal = 0; switch (pLhs->eType) { case FLM_TEXT_VAL: { flmAssert( pRhs->eType == FLM_TEXT_VAL); iCompVal = flmTextCompare( pLhs->val.pucBuf, pLhs->uiBufLen, pRhs->val.pucBuf, pRhs->uiBufLen, uiFlags, uiLang); break; } case FLM_UINT32_VAL: { switch (pRhs->eType) { case FLM_UINT32_VAL: { iCompVal = FQ_COMPARE( pLhs->val.ui32Val, pRhs->val.ui32Val); break; } case FLM_UINT64_VAL: { iCompVal = FQ_COMPARE( (FLMUINT64)(pLhs->val.ui32Val), pRhs->val.ui64Val); break; } case FLM_INT32_VAL: { if (pRhs->val.i32Val < 0) { iCompVal = 1; } else { iCompVal = FQ_COMPARE( pLhs->val.ui32Val, (FLMUINT32)pRhs->val.i32Val); } break; } case FLM_INT64_VAL: { if (pRhs->val.i64Val < 0) { iCompVal = 1; } else { iCompVal = FQ_COMPARE( (FLMINT64)(pLhs->val.ui32Val), pRhs->val.i64Val); } break; } default: { flmAssert( 0); break; } } break; } case FLM_UINT64_VAL: { switch (pRhs->eType) { case FLM_UINT32_VAL: { iCompVal = FQ_COMPARE( pLhs->val.ui64Val, (FLMUINT64)pRhs->val.ui32Val); break; } case FLM_UINT64_VAL: { iCompVal = FQ_COMPARE( pLhs->val.ui64Val, pRhs->val.ui64Val); break; } case FLM_INT32_VAL: { if (pRhs->val.i32Val < 0) { iCompVal = 1; } else { iCompVal = FQ_COMPARE( pLhs->val.ui64Val, (FLMUINT64)(pRhs->val.i32Val)); } break; } case FLM_INT64_VAL: { if (pRhs->val.i64Val < 0) { iCompVal = 1; } else { iCompVal = FQ_COMPARE( pLhs->val.ui64Val, (FLMUINT64)(pRhs->val.i64Val)); } break; } default: { flmAssert( 0); break; } } break; } case FLM_INT32_VAL: { switch (pRhs->eType) { case FLM_INT32_VAL: { iCompVal = FQ_COMPARE( pLhs->val.i32Val, pRhs->val.i32Val); break; } case FLM_INT64_VAL: { iCompVal = FQ_COMPARE( (FLMINT64)(pLhs->val.i32Val), pRhs->val.i64Val); break; } case FLM_UINT32_VAL: { if (pLhs->val.i32Val < 0) { iCompVal = -1; } else { iCompVal = FQ_COMPARE( (FLMUINT) pLhs->val.i32Val, pRhs->val.ui32Val); } break; } case FLM_UINT64_VAL: { if (pLhs->val.i32Val < 0) { iCompVal = -1; } else { iCompVal = FQ_COMPARE( (FLMUINT64)(pLhs->val.i32Val), pRhs->val.ui64Val); } break; } default: { flmAssert( 0); break; } } break; } case FLM_INT64_VAL: { switch (pRhs->eType) { case FLM_INT32_VAL: { iCompVal = FQ_COMPARE( pLhs->val.i64Val, (FLMINT64)(pRhs->val.i32Val)); break; } case FLM_INT64_VAL: { iCompVal = FQ_COMPARE( pLhs->val.i64Val, pRhs->val.i64Val); break; } case FLM_UINT32_VAL: { if (pLhs->val.i64Val < 0) { iCompVal = -1; } else { iCompVal = FQ_COMPARE( pLhs->val.i64Val, (FLMINT64)(pRhs->val.ui32Val)); } break; } case FLM_UINT64_VAL: { if (pLhs->val.i64Val < 0) { iCompVal = -1; } else { iCompVal = FQ_COMPARE( (FLMUINT64)(pLhs->val.i64Val), pRhs->val.ui64Val); } break; } default: { flmAssert( 0); break; } } break; } case FLM_REC_PTR_VAL: { if (pRhs->eType == FLM_REC_PTR_VAL || pRhs->eType == FLM_UINT32_VAL) { iCompVal = FQ_COMPARE( pLhs->val.ui32Val, pRhs->val.ui32Val); } else if (pRhs->eType == FLM_UINT64_VAL) { iCompVal = FQ_COMPARE( (FLMUINT64)(pLhs->val.ui32Val), pRhs->val.ui64Val); } else { flmAssert( 0); } break; } case FLM_BINARY_VAL: { flmAssert( (pRhs->eType == FLM_BINARY_VAL) || (pRhs->eType == FLM_TEXT_VAL)); if ((iCompVal = f_memcmp( pLhs->val.pucBuf, pRhs->val.pucBuf, ((pLhs->uiBufLen > pRhs->uiBufLen) ? pRhs->uiBufLen : pLhs->uiBufLen))) == 0) { if (pLhs->uiBufLen < pRhs->uiBufLen) { iCompVal = -1; } else if (pLhs->uiBufLen > pRhs->uiBufLen) { iCompVal = 1; } } break; } default: { flmAssert( 0); break; } } return (iCompVal); } libflaim-4.9.966/src/fdbrenam.cpp0000644000175000017500000002756110510774540020151 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Rename a database. // Tabs: 3 // // Copyright (c) 2001,2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbrenam.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" typedef struct DBRenameInfo { DB_RENAME_INFO Info; DBRenameInfo * pNext; } DBRenameInfo; FSTATIC RCODE flmRenameFile( const char * pszSrcFileName, const char * pszDstFileName, FLMBOOL bOverwriteDestOk, FLMBOOL bPathNotFoundOk, DBRenameInfo ** ppRenameList, FLMBOOL * pbFileFound, STATUS_HOOK fnStatusCallback, void * UserData); /**************************************************************************** Desc: Rename a database file and add to list of renamed files. ****************************************************************************/ FSTATIC RCODE flmRenameFile( const char * pszSrcFileName, const char * pszDstFileName, FLMBOOL bOverwriteDestOk, FLMBOOL bPathNotFoundOk, DBRenameInfo ** ppRenameList, FLMBOOL * pbFileFound, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; DBRenameInfo * pRenameFile = NULL; *pbFileFound = FALSE; // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcFileName, pszDstFileName) == 0) { if (RC_OK( gv_FlmSysData.pFileSystem->doesFileExist( pszSrcFileName))) { *pbFileFound = TRUE; } goto Exit; } if (RC_BAD( rc = f_alloc( sizeof( DBRenameInfo), &pRenameFile))) { goto Exit; } // If a destination file exists, and it is OK to overwrite // it, it must be deleted. if (bOverwriteDestOk) { if (gv_FlmSysData.pFileSystem->isDir( pszDstFileName)) { if (RC_BAD( rc = gv_FlmSysData.pFileSystem->removeDir( pszDstFileName, TRUE))) { goto Exit; } } else { if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszDstFileName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } } } // If names are the same, no need to actually do the // rename. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->renameFile( pszSrcFileName, pszDstFileName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { if (bPathNotFoundOk) { rc = FERR_OK; } else { goto Exit; } } else { goto Exit; } } else { *pbFileFound = TRUE; pRenameFile->pNext = *ppRenameList; *ppRenameList = pRenameFile; // Do user callback. User could choose to stop the rename // from continuing. f_strcpy( pRenameFile->Info.szSrcFileName, pszSrcFileName); f_strcpy( pRenameFile->Info.szDstFileName, pszDstFileName); if (fnStatusCallback) { if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_RENAME_STATUS, (void *)&pRenameFile->Info, (void *)0, UserData))) { goto Exit; } } // So it won't get deallocated at exit. pRenameFile = NULL; } Exit: if (pRenameFile) { f_free( &pRenameFile); } return( rc); } /******************************************************************************* Desc: Renames a database *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRename( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszNewDbName, FLMBOOL bOverwriteDestOk, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; IF_FileHdl * pFileHdl = NULL; FLMUINT uiFileNumber; FILE_HDR FileHdr; LOG_HDR LogHdr; DBRenameInfo * pRenameList = NULL; FLMBOOL bFileFound; FLMBYTE * pucBuffer = NULL; FLMBYTE * pucLogHdr; char * pszOldName; char * pszNewName; char * pszOldDataName; char * pszNewDataName; char * pszFullNewName; char szOldBase[ F_FILENAME_SIZE]; char szNewBase[ F_FILENAME_SIZE]; char * pszExtOld; char * pszExtNew; char * pszDataExtOld; char * pszDataExtNew; // Cannot handle empty database name. flmAssert( pszDbName && *pszDbName); flmAssert( pszNewDbName && *pszNewDbName); // Allocate memory for a read buffer, the log header, and various // file names. if( RC_BAD( rc = f_allocAlignedBuffer( 2048 + LOG_HEADER_SIZE + F_PATH_MAX_SIZE * 5, &pucBuffer))) { goto Exit; } pucLogHdr = pucBuffer + 2048; pszOldName = (char *)(pucLogHdr + LOG_HEADER_SIZE); pszNewName = pszOldName + F_PATH_MAX_SIZE; pszOldDataName = pszNewName + F_PATH_MAX_SIZE; pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; // There must be either no directory specified for the new name, or // it must be identical to the old directory. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) { goto Exit; } // Directories must be the same. if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } f_strcpy( pszNewName, pszOldName); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewName, szNewBase))) { goto Exit; } f_strcpy( pszFullNewName, pszNewName); f_strcpy( pszOldName, pszDbName); if( pszDataDir && *pszDataDir) { f_strcpy( pszOldDataName, pszDataDir); f_strcpy( pszNewDataName, pszDataDir); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszOldDataName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszNewDataName, szNewBase))) { goto Exit; } } else { f_strcpy( pszNewDataName, pszNewName); f_strcpy( pszOldDataName, pszOldName); } // First make sure we have closed the databases and gotten rid of // them from our internal memory tables - in case they had been open. if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszDbName, (void *)pszDataDir))) { goto Exit; } if( RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszFullNewName, (void *)pszDataDir))) { goto Exit; } gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Open the file so we can get the log header. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszDbName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } // Read the header to get the low and high RFL log // file numbers. if (RC_BAD( flmReadAndVerifyHdrInfo( NULL, pFileHdl, pucBuffer, &FileHdr, &LogHdr, pucLogHdr))) { goto Exit; } // Close the file. pFileHdl->Release(); pFileHdl = NULL; // Start renaming files, beginning with the main DB file. if( RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, bOverwriteDestOk, FALSE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Find where the extension of the old and new database names are pszExtOld = pszOldName + f_strlen( pszOldName) - 1; pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; while (pszExtOld != pszOldName && *pszExtOld != '.') { pszExtOld--; // Both the old db name and old data name have the same // base name, so we can decrement pszDataExtOld // at the same time we decrement pszExtOld. pszDataExtOld--; } if (*pszExtOld != '.') { pszExtOld = pszOldName + f_strlen( pszOldName); pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); } pszExtNew = pszNewName + f_strlen( pszNewName) - 1; pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; while (pszExtNew != pszOldName && *pszExtNew != '.') { pszExtNew--; // Both the new db name and new data name have the same // base name, so we can decrement pszDataExtNew // at the same time we decrement pszExtNew. pszDataExtNew--; } if (*pszExtNew != '.') { pszExtNew = pszNewName + f_strlen( pszNewName); pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); } // Rename the .lck file, if any. This is necessary for UNIX. f_strcpy( pszExtOld, ".lck"); f_strcpy( pszExtNew, ".lck"); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } // Rename block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszDataExtNew); if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER (FileHdr.uiVersionNum); for (;;) { F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtOld); F_SuperFileClient::bldSuperFileExtension( FileHdr.uiVersionNum, uiFileNumber, pszExtNew); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( FileHdr.uiVersionNum)) { break; } uiFileNumber++; } // Rename roll-forward log files. if (FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to rename one RFL file. if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszDbName, pszRflDir, 1, pszOldName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, 1, pszNewName))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } else { // For 4.3 and greater, rename the RFL directory. if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszDbName, pszRflDir, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = rflGetDirAndPrefix( FileHdr.uiVersionNum, pszFullNewName, pszRflDir, pszNewName, szNewBase))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, fnStatusCallback, UserData))) { goto Exit; } } Exit: if( pFileHdl) { pFileHdl->Release(); } if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } // Free the list of renamed files. while( pRenameList) { DBRenameInfo * pRenameFile; pRenameFile = pRenameList; pRenameList = pRenameList->pNext; // If we had an error of some sort, attempt to un-rename // the file that had been renamed. if (RC_BAD( rc)) { gv_FlmSysData.pFileSystem->renameFile( pRenameFile->Info.szDstFileName, pRenameFile->Info.szSrcFileName); } f_free( &pRenameFile); } return( rc); } libflaim-4.9.966/src/fsysdata.cpp0000644000175000017500000031712510510774540020207 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Initialization and shutdown and system data. // Tabs: 3 // // Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsysdata.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #define ALLOCATE_SYS_DATA 1 #include "flaimsys.h" #ifdef FLM_32BIT #if defined( FLM_LINUX) // With mmap'd memory on Linux, you're effectively limited to about ~2 GB. // Userspace only gets ~3GB of useable address space anyway, and then you // have all of the thread stacks too, which you can't have // overlapping the heap. #define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024) #else #define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024) #endif #else #define FLM_MAX_CACHE_SIZE (~((FLMUINT)0)) #endif #define DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache #define DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes FLMATOMIC gv_flmSysSpinLock = 0; FLMUINT gv_uiFlmSysStartupCount = 0; FSTATIC RCODE flmGetCacheBytes( FLMUINT uiPercent, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bCalcOnAvailMem, FLMUINT uiBytesCurrentlyInUse, FLMUINT * puiCacheBytes); FSTATIC void flmLockSysData( void); FSTATIC void flmUnlockSysData( void); FSTATIC RCODE flmSetCacheLimits( FLMUINT uiNewTotalCacheSize, FLMBOOL bForceLimit, FLMBOOL bPreallocateCache); FSTATIC void flmFreeEvent( FEVENT * pEvent, F_MUTEX hMutex, FEVENT ** ppEventListRV); FSTATIC RCODE flmCloseDbFile( const char * pszDbFileName, const char * pszDataDir); FSTATIC void flmShutdownDbThreads( FFILE * pFile); FSTATIC void flmCleanup( void); FSTATIC void flmUnlinkFileFromBucket( FFILE * pFile); RCODE FLMAPI flmSystemMonitor( IF_Thread * pThread); FSTATIC RCODE flmRegisterHttpCallback( FLM_MODULE_HANDLE hModule, const char * pszUrlString); FSTATIC RCODE flmDeregisterHttpCallback( void); /**************************************************************************** Desc: Sets the path for all temporary files that come into use within a FLAIM share structure. The share mutex should be locked when settting when called from FlmConfig(). ****************************************************************************/ FINLINE RCODE flmSetTmpDir( const char * pszTmpDir) { RCODE rc; if( RC_BAD( rc = gv_FlmSysData.pFileSystem->doesFileExist( pszTmpDir))) { goto Exit; } f_strcpy( gv_FlmSysData.szTempDir, pszTmpDir); gv_FlmSysData.bTempDirSet = TRUE; Exit: return( rc); } /**************************************************************************** Desc: This routine frees all of the local dictionaries in a list of local dictionaries. ****************************************************************************/ FINLINE void flmFreeDictList( FDICT ** ppDictRV) { FDICT * pTmp; FDICT * pDict = *ppDictRV; while (pDict) { pTmp = pDict; pDict = pDict->pNext; flmFreeDict( pTmp); } *ppDictRV = NULL; } /**************************************************************************** Desc: This routine determines the number of cache bytes to use for caching based on a percentage of available physical memory or a percentage of physical memory (depending on bCalcOnAvailMem flag). uiBytesCurrentlyInUse indicates how many bytes are currently allocated by FLAIM - so it can factor that in if the calculation is to be based on the available memory. Lower limit is 1 megabyte. ****************************************************************************/ FSTATIC RCODE flmGetCacheBytes( FLMUINT uiPercent, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bCalcOnAvailMem, FLMUINT uiBytesCurrentlyInUse, FLMUINT * puiCacheBytes) { RCODE rc = NE_FLM_OK; FLMUINT uiMem = 0; FLMUINT64 ui64TotalPhysMem; FLMUINT64 ui64AvailPhysMem; if( RC_BAD( rc = f_getMemoryInfo( &ui64TotalPhysMem, &ui64AvailPhysMem))) { goto Exit; } if( ui64TotalPhysMem > FLM_MAX_UINT) { ui64TotalPhysMem = FLM_MAX_UINT; } if( ui64AvailPhysMem > ui64TotalPhysMem) { ui64AvailPhysMem = ui64TotalPhysMem; } uiMem = (FLMUINT)((bCalcOnAvailMem) ? (FLMUINT)ui64AvailPhysMem : (FLMUINT)ui64TotalPhysMem); // If we are basing the calculation on available physical memory, // take in to account what has already been allocated. if (bCalcOnAvailMem) { if (uiMem > FLM_MAX_UINT - uiBytesCurrentlyInUse) { uiMem = FLM_MAX_UINT; } else { uiMem += uiBytesCurrentlyInUse; } } // If uiMax is zero, use uiMinToLeave to calculate the maximum. if (!uiMax) { if (!uiMinToLeave) { uiMax = uiMem; } else if (uiMinToLeave < uiMem) { uiMax = uiMem - uiMinToLeave; } else { uiMax = 0; } } // Calculate memory as a percentage of memory. uiMem = (FLMUINT)((uiMem > FLM_MAX_UINT / 100) ? (FLMUINT)(uiMem / 100) * uiPercent : (FLMUINT)(uiMem * uiPercent) / 100); // Don't go above the maximum. if (uiMem > uiMax) { uiMem = uiMax; } // Don't go below the minimum. if (uiMem < uiMin) { uiMem = uiMin; } Exit: *puiCacheBytes = uiMem; return( rc); } /*************************************************************************** Desc: Lock the system data structure for access - called only by startup and shutdown. NOTE: On platforms that do not support atomic exchange this is less than perfect - won't handle tight race conditions. ***************************************************************************/ FSTATIC void flmLockSysData( void) { while( f_atomicExchange( &gv_flmSysSpinLock, 1) == 1) { f_sleep( 10); } } /*************************************************************************** Desc: Unlock the system data structure for access - called only by startup and shutdown. ***************************************************************************/ FSTATIC void flmUnlockSysData( void) { f_atomicExchange( &gv_flmSysSpinLock, 0); } /**************************************************************************** Desc : Startup FLAIM. Notes: This routine may be called multiple times. However, if that is done FlmShutdown() should be called for each time this is called successfully. This routine does not handle race conditions on platforms that do not support atomic increment. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmStartup( void) { RCODE rc = FERR_OK; FLMUINT uiCacheBytes; #ifdef FLM_USE_NICI int iHandle; #endif flmLockSysData(); // See if FLAIM has already been started. If so, // we are done. if( ++gv_uiFlmSysStartupCount > 1) { goto Exit; } if( RC_BAD( rc = ftkStartup())) { goto Exit; } // The memset needs to be first. f_memset( &gv_FlmSysData, 0, sizeof( FLMSYSDATA)); gv_FlmSysData.uiMaxFileSize = f_getMaxFileSize(); flmAssert( gv_FlmSysData.uiMaxFileSize); // Initialize memory tracking variables - should be done before // call to f_memoryInit(). gv_FlmSysData.hShareMutex = F_MUTEX_NULL; gv_FlmSysData.uiMaxStratifyIterations = DEFAULT_MAX_STRATIFY_ITERATIONS; gv_FlmSysData.uiMaxStratifyTime = DEFAULT_MAX_STRATIFY_TIME; // Initialize the event categories to have no mutex. gv_FlmSysData.UpdateEvents.hMutex = F_MUTEX_NULL; gv_FlmSysData.LockEvents.hMutex = F_MUTEX_NULL; gv_FlmSysData.SizeEvents.hMutex = F_MUTEX_NULL; // Set the default file open flags gv_FlmSysData.uiFileOpenFlags = FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT; gv_FlmSysData.uiFileCreateFlags = gv_FlmSysData.uiFileOpenFlags | FLM_IO_EXCL | FLM_IO_CREATE_DIR; #ifdef FLM_DBG_LOG flmDbgLogInit(); #endif gv_FlmSysData.uiMaxUnusedTime = FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_UNUSED_TIME); gv_FlmSysData.uiMaxCPInterval = FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_CP_INTERVAL); gv_FlmSysData.uiMaxTransTime = FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_SECS); gv_FlmSysData.uiMaxTransInactiveTime = FLM_SECS_TO_TIMER_UNITS( DEFAULT_MAX_TRANS_INACTIVE_SECS); if( f_canGetMemoryInfo()) { gv_FlmSysData.bDynamicCacheAdjust = TRUE; gv_FlmSysData.uiCacheAdjustPercent = DEFAULT_CACHE_ADJUST_PERCENT; gv_FlmSysData.uiCacheAdjustMin = DEFAULT_CACHE_ADJUST_MIN; gv_FlmSysData.uiCacheAdjustMax = DEFAULT_CACHE_ADJUST_MAX; gv_FlmSysData.uiCacheAdjustMinToLeave = DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE; gv_FlmSysData.uiCacheAdjustInterval = FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_ADJUST_INTERVAL); if( RC_BAD( rc = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMax, gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, 0, &uiCacheBytes))) { goto Exit; } } else { gv_FlmSysData.bDynamicCacheAdjust = FALSE; gv_FlmSysData.uiCacheAdjustInterval = 0; uiCacheBytes = DEFAULT_CACHE_ADJUST_MIN; } if( uiCacheBytes > FLM_MAX_CACHE_SIZE) { uiCacheBytes = FLM_MAX_CACHE_SIZE; } gv_FlmSysData.uiBlockCachePercentage = DEFAULT_BLOCK_CACHE_PERCENTAGE; gv_FlmSysData.uiCacheCleanupInterval = FLM_SECS_TO_TIMER_UNITS( DEFAULT_CACHE_CLEANUP_INTERVAL); gv_FlmSysData.uiUnusedCleanupInterval = FLM_SECS_TO_TIMER_UNITS( DEFAULT_UNUSED_CLEANUP_INTERVAL); // Get a pointer to the thread manager if( RC_BAD( rc = FlmGetThreadMgr( &gv_FlmSysData.pThreadMgr))) { goto Exit; } // Allocate thread group IDs gv_uiBackIxThrdGroup = gv_FlmSysData.pThreadMgr->allocGroupId(); gv_uiCPThrdGrp = gv_FlmSysData.pThreadMgr->allocGroupId(); gv_uiDbThrdGrp = gv_FlmSysData.pThreadMgr->allocGroupId(); // Initialize the slab manager if( RC_BAD( rc = FlmAllocSlabManager( &gv_FlmSysData.pSlabManager))) { goto Exit; } if( !gv_FlmSysData.bDynamicCacheAdjust) { if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( uiCacheBytes))) { goto Exit; } } else { if( RC_BAD( rc = gv_FlmSysData.pSlabManager->setup( 0))) { goto Exit; } } // Divide cache bytes evenly between block and record cache. gv_FlmSysData.uiMaxCache = uiCacheBytes; uiCacheBytes >>= 1; if (RC_BAD( rc = ScaInit( uiCacheBytes))) { goto Exit; } if (RC_BAD( rc = flmRcaInit( uiCacheBytes))) { goto Exit; } // Create the mutex for controlling access to the structure if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hShareMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hQueryMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.HttpConfigParms.hMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.hHttpSessionMutex))) { goto Exit; } // Initialize a statistics structure if (RC_BAD( rc = flmStatInit( &gv_FlmSysData.Stats, TRUE))) { goto Exit; } gv_FlmSysData.bStatsInitialized = TRUE; // Allocate memory for the file name hash table if (RC_BAD(rc = f_allocHashTable( FILE_HASH_ENTRIES, &gv_FlmSysData.pFileHashTbl))) { goto Exit; } gv_FlmSysData.uiNextFFileId = 1; // Get the file system object if( RC_BAD( rc = FlmGetFileSystem( &gv_FlmSysData.pFileSystem))) { goto Exit; } // Allocate a file handle cache if( RC_BAD( rc = gv_FlmSysData.pFileSystem->allocFileHandleCache( DEFAULT_OPEN_THRESHOLD, DEFAULT_MAX_UNUSED_TIME, &gv_FlmSysData.pFileHdlCache))) { goto Exit; } // Set up the session manager if( (gv_FlmSysData.pSessionMgr = f_new F_SessionMgr) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pSessionMgr->setupSessionMgr())) { goto Exit; } // Set up mutexes for the event table. if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.UpdateEvents.hMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.LockEvents.hMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.SizeEvents.hMutex))) { goto Exit; } // Start the monitor thread - ALWAYS DO LAST. EVERYTHING MUST BE // SETUP PROPERLY BEFORE STARTING THIS THREAD. if (RC_BAD( rc = f_threadCreate( &gv_FlmSysData.pMonitorThrd, flmSystemMonitor, "FLAIM System Monitor"))) { goto Exit; } #ifdef FLM_USE_NICI iHandle = (int)f_getpid(); // Initialize NICI if (CCS_Init(&iHandle)) { // Failure. rc = RC_SET( FERR_NICI_INIT_FAILED); goto Exit; } #endif Exit: // If not successful, free up any resources that were allocated. if (RC_BAD( rc)) { flmCleanup(); } flmUnlockSysData(); return( rc); } /**************************************************************************** Desc: This routine sets the limits for record cache and block cache - dividing the total cache between the two caches. It uses the same ratio currently in force. ****************************************************************************/ FSTATIC RCODE flmSetCacheLimits( FLMUINT uiNewTotalCacheSize, FLMBOOL bForceLimit, FLMBOOL bPreallocateCache) { RCODE rc = FERR_OK; FLMUINT uiNewBlockCacheSize; FLMBOOL bResizeAfterConfig = FALSE; if( !bForceLimit && uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE) { uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE; } if( bPreallocateCache) { if( gv_FlmSysData.bDynamicCacheAdjust) { // Can't pre-allocate and dynamically adjust. goto DONT_PREALLOCATE; } if( RC_BAD( rc = gv_FlmSysData.pSlabManager->resize( uiNewTotalCacheSize, TRUE, &uiNewTotalCacheSize))) { // Log a message indicating that we couldn't pre-allocate // the cache flmLogMessage( F_DEBUG_MESSAGE, FLM_YELLOW, FLM_BLACK, "WARNING: Couldn't pre-allocate cache."); goto DONT_PREALLOCATE; } gv_FlmSysData.bCachePreallocated = TRUE; } else { DONT_PREALLOCATE: bResizeAfterConfig = TRUE; gv_FlmSysData.bCachePreallocated = FALSE; } if( gv_FlmSysData.uiBlockCachePercentage == 100) { uiNewBlockCacheSize = uiNewTotalCacheSize; } else { uiNewBlockCacheSize = (FLMUINT)((uiNewTotalCacheSize / 100) * gv_FlmSysData.uiBlockCachePercentage); } if (RC_OK( rc = ScaConfig( FLM_CACHE_LIMIT, (void *)uiNewBlockCacheSize, (void *)0))) { rc = flmRcaConfig( FLM_CACHE_LIMIT, (void *)(uiNewTotalCacheSize - uiNewBlockCacheSize), (void *)0); } if( bResizeAfterConfig) { (void)gv_FlmSysData.pSlabManager->resize( uiNewTotalCacheSize, FALSE); } gv_FlmSysData.uiMaxCache = uiNewTotalCacheSize; return( rc); } /**************************************************************************** Desc : Configures how memory will be dynamically regulated. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmSetDynamicMemoryLimit( FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave) { if( !f_canGetMemoryInfo()) { return( RC_SET( FERR_NOT_IMPLEMENTED)); } else { RCODE rc = FERR_OK; FLMUINT uiCacheBytes; f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); gv_FlmSysData.bDynamicCacheAdjust = TRUE; flmAssert( uiCacheAdjustPercent > 0 && uiCacheAdjustPercent <= 100); gv_FlmSysData.uiCacheAdjustPercent = uiCacheAdjustPercent; gv_FlmSysData.uiCacheAdjustMin = uiCacheAdjustMin; gv_FlmSysData.uiCacheAdjustMax = uiCacheAdjustMax; gv_FlmSysData.uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave; if( RC_OK( rc = flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMax, gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated, &uiCacheBytes))) { rc = flmSetCacheLimits( uiCacheBytes, FALSE, FALSE); } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); return( rc); } } /**************************************************************************** Desc : Sets a hard memory limit for cache. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmSetHardMemoryLimit( FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate) { RCODE rc = FERR_OK; f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); gv_FlmSysData.bDynamicCacheAdjust = FALSE; if (uiPercent) { if( !f_canGetMemoryInfo()) { rc = RC_SET( FERR_NOT_IMPLEMENTED); } else { FLMUINT uiCacheBytes; if( RC_OK( rc = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave, bPercentOfAvail, gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated, &uiCacheBytes))) { rc = flmSetCacheLimits( uiCacheBytes, FALSE, bPreallocate); } } } else { rc = flmSetCacheLimits( uiMax, TRUE, bPreallocate); } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); return( rc); } /**************************************************************************** Desc : Returns information about memory usage. ****************************************************************************/ FLMEXP void FLMAPI FlmGetMemoryInfo( FLM_MEM_INFO * pMemInfo) { f_memset( pMemInfo, 0, sizeof( FLM_MEM_INFO)); f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); pMemInfo->bDynamicCacheAdjust = gv_FlmSysData.bDynamicCacheAdjust; pMemInfo->uiCacheAdjustPercent = gv_FlmSysData.uiCacheAdjustPercent; pMemInfo->uiCacheAdjustMin = gv_FlmSysData.uiCacheAdjustMin; pMemInfo->uiCacheAdjustMax = gv_FlmSysData.uiCacheAdjustMax; pMemInfo->uiCacheAdjustMinToLeave = gv_FlmSysData.uiCacheAdjustMinToLeave; // Return record cache information. f_memcpy( &pMemInfo->RecordCache, &gv_FlmSysData.RCacheMgr.Usage, sizeof( FLM_CACHE_USAGE)); // Return block cache information. f_memcpy( &pMemInfo->BlockCache, &gv_FlmSysData.SCacheMgr.Usage, sizeof( FLM_CACHE_USAGE)); pMemInfo->uiFreeBytes = gv_FlmSysData.SCacheMgr.uiFreeBytes; pMemInfo->uiFreeCount = gv_FlmSysData.SCacheMgr.uiFreeCount; pMemInfo->uiReplaceableCount = gv_FlmSysData.SCacheMgr.uiReplaceableCount; pMemInfo->uiReplaceableBytes = gv_FlmSysData.SCacheMgr.uiReplaceableBytes; if( gv_FlmSysData.pFileHashTbl) { FLMUINT uiLoop; FFILE * pFile; for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) { if( (pFile = (FFILE *)gv_FlmSysData.pFileHashTbl[ uiLoop].pFirstInBucket) != NULL) { while( pFile) { if( pFile->uiDirtyCacheCount) { pMemInfo->uiDirtyBytes += pFile->uiDirtyCacheCount * pFile->FileHdr.uiBlockSize; pMemInfo->uiDirtyCount += pFile->uiDirtyCacheCount; } if( pFile->uiNewCount) { pMemInfo->uiNewBytes += pFile->uiNewCount * pFile->FileHdr.uiBlockSize; pMemInfo->uiNewCount += pFile->uiNewCount; } if( pFile->uiLogCacheCount) { pMemInfo->uiLogBytes += pFile->uiLogCacheCount * pFile->FileHdr.uiBlockSize; pMemInfo->uiLogCount += pFile->uiLogCacheCount; } pFile = pFile->pNext; } } } } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); } /**************************************************************************** Desc : Close a database file - all background threads are closed too. ****************************************************************************/ FSTATIC RCODE flmCloseDbFile( const char * pszDbFileName, const char * pszDataDir) { RCODE rc = FERR_OK; FLMBOOL bMutexLocked = FALSE; FFILE * pFile; f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; Retry: // Look up the file using flmFindFile to see if we have the // file open. May unlock and re-lock the global mutex. if (RC_BAD( rc = flmFindFile( pszDbFileName, pszDataDir, &pFile))) { goto Exit; } // If we did not find the file, we are OK. if (!pFile) { goto Exit; } // If the file is in the process of being opened by another // thread, wait for the open to complete. if( pFile->uiFlags & DBF_BEING_OPENED) { if( RC_BAD( rc = f_notifyWait( gv_FlmSysData.hShareMutex, F_SEM_NULL, NULL, &pFile->pOpenNotifies))) { // If f_notifyWait returns a bad RC, assume that the other thread // will unlock and free the pFile structure. This routine should // only unlock the pFile if an error occurs at some other point. // See flmVerifyFileUse. goto Exit; } goto Retry; } if( pFile->uiUseCount) { // Increment the use count temporarily so that the FFILE won't be // moved to the NU list because of db close calls made by // releaseFileResources. pFile->uiUseCount++; // Must unlock the mutex prior to calling releaseFileResources because // it may call API-level routines (such as FlmDbClose) that do not // expect the mutex to be locked. f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // First, all session resources are released that are // associated with the specified database if( gv_FlmSysData.pSessionMgr) { gv_FlmSysData.pSessionMgr->releaseFileResources( pFile); } f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; // Decrement the temporary use count that was added above. // By now, the FFILE may no longer be in use. If so, we need to // link it to the NU list. if (!(--pFile->uiUseCount)) { // If the "must close" flag is set, it indicates that // the FFILE is being forced to close. Put the FFILE in // the NU list, but specify that it should be quickly // timed-out. We link the file to the NU list even // though we are going to unlink it below because // flmLinkFileToNUList closes the RFL file(s). flmLinkFileToNUList( pFile, pFile->bMustClose); } } // If the FFILE is in the NU list, unlink it flmUnlinkFileFromNUList( pFile); // If we have non-background threads accessing the database, // we cannot close the file. if (pFile->uiUseCount > pFile->uiInternalUseCount) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } // Close the file. flmFreeFile( pFile); // Unlock the mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; Exit: if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } return( rc); } /**************************************************************************** Desc : Register our http callback function ****************************************************************************/ FSTATIC RCODE flmRegisterHttpCallback( FLM_MODULE_HANDLE hModule, const char * pszUrlString) { RCODE rc = FERR_OK; char * pszTemp; FLMUINT uiRegFlags; if (gv_FlmSysData.HttpConfigParms.bRegistered) { rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); goto Exit; } // Need to save the Url string for later use... if( RC_BAD( rc = f_alloc( f_strlen( pszUrlString) + 1, &pszTemp))) { goto Exit; } f_strcpy( pszTemp, pszUrlString); // Set the flags that tell the server what kind of authentication // we want: // HR_STK_BOTH = Allow both http and https // HR_AUTH_USERSA = Allow any user in the NDS tree or the SAdmin user // HR_REALM_NDS = Use the NDS realm for authentication uiRegFlags = HR_STK_BOTH | HR_AUTH_USERSA | HR_REALM_NDS; if (gv_FlmSysData.HttpConfigParms.fnReg) { if( gv_FlmSysData.HttpConfigParms.fnReg( hModule, pszUrlString, (FLMUINT32)uiRegFlags, flmHttpCallback, NULL, NULL) != 0) { rc = RC_SET( FERR_HTTP_REGISTER_FAILURE); goto Exit; } } else { flmAssert( 0); rc = RC_SET( FERR_NO_HTTP_STACK); goto Exit; } // Save the URL string in gv_FlmSysData gv_FlmSysData.HttpConfigParms.pszURLString = pszTemp; gv_FlmSysData.HttpConfigParms.uiURLStringLen = f_strlen( pszTemp); pszTemp = NULL; gv_FlmSysData.HttpConfigParms.bRegistered = TRUE; Exit: if (RC_BAD( rc)) { if (pszTemp) { f_free( &pszTemp); pszTemp = NULL; } } return( rc); } /**************************************************************************** Desc : Deregister our http callback function ****************************************************************************/ FSTATIC RCODE flmDeregisterHttpCallback( void) { RCODE rc = FERR_OK; if (!gv_FlmSysData.HttpConfigParms.bRegistered) { rc = RC_SET( FERR_HTTP_DEREG_FAILURE); goto Exit; } if ( !gv_FlmSysData.HttpConfigParms.fnDereg || !gv_FlmSysData.HttpConfigParms.pszURLString ) { flmAssert( 0); rc = RC_SET( FERR_NO_HTTP_STACK); goto Exit; } if ( gv_FlmSysData.HttpConfigParms.fnDereg( gv_FlmSysData.HttpConfigParms.pszURLString, flmHttpCallback) != 0) { rc = RC_SET( FERR_HTTP_DEREG_FAILURE); goto Exit; } if( gv_FlmSysData.HttpConfigParms.pszURLString) { f_free( &gv_FlmSysData.HttpConfigParms.pszURLString); gv_FlmSysData.HttpConfigParms.pszURLString = NULL; } // Now, tell the callback function to delete the webpage factory and cleanup // the allocated memory etc... f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); while (gv_FlmSysData.HttpConfigParms.uiUseCount) { f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); f_sleep( 10); f_mutexLock( gv_FlmSysData.HttpConfigParms.hMutex); } flmHttpCallback( NULL, NULL); f_mutexUnlock( gv_FlmSysData.HttpConfigParms.hMutex); gv_FlmSysData.HttpConfigParms.bRegistered = FALSE; Exit: return( rc); } /**************************************************************************** Desc: Configures share attributes. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmConfig( eFlmConfigTypes eConfigType, void * Value1, void * Value2) { RCODE rc = FERR_OK; FLMUINT uiValue; FLMUINT uiSave; FLMUINT uiCurrTime; FLMUINT uiSaveMax; switch( eConfigType) { case FLM_OPEN_THRESHOLD: { rc = gv_FlmSysData.pFileHdlCache->setOpenThreshold( (FLMUINT)Value1); break; } case FLM_DIRECT_IO_STATE: { // Direct I/O state can only be changed when there are no open // databases. We also only allow DIO to be disabled on Unix // platforms. #ifdef FLM_UNIX f_mutexLock( gv_FlmSysData.hShareMutex); if( !gv_FlmSysData.uiOpenFFiles) { if( (FLMBOOL)Value1) { gv_FlmSysData.uiFileOpenFlags = FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT; } else { gv_FlmSysData.uiFileOpenFlags = FLM_IO_RDWR | FLM_IO_SH_DENYNONE; } gv_FlmSysData.uiFileCreateFlags = gv_FlmSysData.uiFileOpenFlags | FLM_IO_EXCL | FLM_IO_CREATE_DIR; } f_mutexUnlock( gv_FlmSysData.hShareMutex); #endif break; } case FLM_CACHE_LIMIT: { f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); gv_FlmSysData.bDynamicCacheAdjust = FALSE; rc = flmSetCacheLimits( (FLMUINT)Value1, TRUE, (FLMBOOL)(FLMUINT)Value2); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_BLOCK_CACHE_PERCENTAGE: { f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); if( (gv_FlmSysData.uiBlockCachePercentage = (FLMUINT)Value1) > 100) { gv_FlmSysData.uiBlockCachePercentage = 100; } rc = flmSetCacheLimits( gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes, gv_FlmSysData.bDynamicCacheAdjust ? FALSE : TRUE, gv_FlmSysData.bCachePreallocated); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_SCACHE_DEBUG: { f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); if (RC_OK( rc = ScaConfig( FLM_SCACHE_DEBUG, Value1, Value2))) { rc = flmRcaConfig( FLM_SCACHE_DEBUG, Value1, Value2); } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_CLOSE_UNUSED_FILES: { // Timeout inactive sessions if( gv_FlmSysData.pSessionMgr) { gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( (FLMUINT)Value1, TRUE); } // Convert seconds to timer units uiValue = FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1); // Free any other unused structures that have not been used for the // specified amount of time. uiCurrTime = (FLMUINT)FLM_GET_TIMER(); f_mutexLock( gv_FlmSysData.hShareMutex); // Temporarily set the maximum unused seconds in the FLMSYSDATA structure // to the value that was passed in to Value1. Restore it after // calling flmCheckNUStructs. uiSave = gv_FlmSysData.uiMaxUnusedTime; gv_FlmSysData.uiMaxUnusedTime = uiValue; // May unlock and re-lock the global mutex. flmCheckNUStructs( uiCurrTime); gv_FlmSysData.uiMaxUnusedTime = uiSave; f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_CLOSE_ALL_FILES: { break; } case FLM_START_STATS: { (void)flmStatStart( &gv_FlmSysData.Stats); // Start query statistics, if they have not // already been started. f_mutexLock( gv_FlmSysData.hQueryMutex); if (!gv_FlmSysData.uiMaxQueries) { gv_FlmSysData.uiMaxQueries = 20; gv_FlmSysData.bNeedToUnsetMaxQueries = TRUE; } f_mutexUnlock( gv_FlmSysData.hQueryMutex); break; } case FLM_STOP_STATS: { (void)flmStatStop( &gv_FlmSysData.Stats); // Stop query statistics, if they were // started by a call to FLM_START_STATS. f_mutexLock( gv_FlmSysData.hQueryMutex); if (gv_FlmSysData.bNeedToUnsetMaxQueries) { gv_FlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( TRUE); // NOTE: flmFreeSavedQueries unlocks the mutex. } else { f_mutexUnlock( gv_FlmSysData.hQueryMutex); } break; } case FLM_RESET_STATS: { // Lock the record cache manager's mutex f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); // Reset the record cache statistics gv_FlmSysData.RCacheMgr.uiIoWaits = 0; gv_FlmSysData.RCacheMgr.Usage.uiCacheHits = 0; gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks = 0; gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults = 0; gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks = 0; // Reset the block cache statistics. gv_FlmSysData.SCacheMgr.uiIoWaits = 0; gv_FlmSysData.SCacheMgr.Usage.uiCacheHits = 0; gv_FlmSysData.SCacheMgr.Usage.uiCacheHitLooks = 0; gv_FlmSysData.SCacheMgr.Usage.uiCacheFaults = 0; gv_FlmSysData.SCacheMgr.Usage.uiCacheFaultLooks = 0; // Unlock the cache manager's mutex f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); (void)flmStatReset( &gv_FlmSysData.Stats, FALSE, TRUE); f_mutexLock( gv_FlmSysData.hQueryMutex); uiSaveMax = gv_FlmSysData.uiMaxQueries; gv_FlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( TRUE); // NOTE: flmFreeSavedQueries unlocks the mutex. // Restore the old maximum if (uiSaveMax) { // flmFreeSavedQueries unlocks the mutex, so we // must relock it to restore the old maximum. f_mutexLock( gv_FlmSysData.hQueryMutex); gv_FlmSysData.uiMaxQueries = uiSaveMax; f_mutexUnlock( gv_FlmSysData.hQueryMutex); } break; } case FLM_TMPDIR: { f_mutexLock( gv_FlmSysData.hShareMutex); rc = flmSetTmpDir( (const char *)Value1); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_MAX_CP_INTERVAL: { gv_FlmSysData.uiMaxCPInterval = FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1); break; } case FLM_BLOB_EXT: { const char * pszTmp = (const char *)Value1; if (!pszTmp) { gv_FlmSysData.ucBlobExt [0] = 0; } else { int iCnt; // Don't save any more than 63 characters. for (iCnt = 0; ((iCnt < 63) && (*pszTmp)); iCnt++, pszTmp++) { gv_FlmSysData.ucBlobExt [iCnt] = *pszTmp; } gv_FlmSysData.ucBlobExt [iCnt] = 0; } break; } case FLM_MAX_TRANS_SECS: { gv_FlmSysData.uiMaxTransTime = FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1); break; } case FLM_MAX_TRANS_INACTIVE_SECS: { gv_FlmSysData.uiMaxTransInactiveTime = FLM_SECS_TO_TIMER_UNITS( (FLMUINT) Value1); break; } case FLM_CACHE_ADJUST_INTERVAL: { gv_FlmSysData.uiCacheAdjustInterval = FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1); break; } case FLM_CACHE_CLEANUP_INTERVAL: { gv_FlmSysData.uiCacheCleanupInterval = FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1); break; } case FLM_UNUSED_CLEANUP_INTERVAL: { gv_FlmSysData.uiUnusedCleanupInterval = FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1); break; } case FLM_MAX_UNUSED_TIME: { f_mutexLock( gv_FlmSysData.hShareMutex); gv_FlmSysData.uiMaxUnusedTime = FLM_SECS_TO_TIMER_UNITS( (FLMUINT)Value1); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; } case FLM_CACHE_CHECK: gv_FlmSysData.bCheckCache = (FLMBOOL)((Value1 != 0) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); break; case FLM_CLOSE_FILE: rc = flmCloseDbFile( (const char *)Value1, (const char *)Value2); break; case FLM_LOGGER: f_mutexLock( gv_FlmSysData.hShareMutex); if( !gv_FlmSysData.pLogger && Value1) { gv_FlmSysData.pLogger = (IF_LoggerClient *)Value1; gv_FlmSysData.pLogger->AddRef(); f_setLoggerClient( gv_FlmSysData.pLogger); } f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_ASSIGN_HTTP_SYMS: if( gv_FlmSysData.HttpConfigParms.fnReg || gv_FlmSysData.HttpConfigParms.fnDereg || gv_FlmSysData.HttpConfigParms.fnReqPath || gv_FlmSysData.HttpConfigParms.fnReqQuery || gv_FlmSysData.HttpConfigParms.fnReqHdrValue || gv_FlmSysData.HttpConfigParms.fnSetHdrValue || gv_FlmSysData.HttpConfigParms.fnPrintf || gv_FlmSysData.HttpConfigParms.fnEmit || gv_FlmSysData.HttpConfigParms.fnSetNoCache || gv_FlmSysData.HttpConfigParms.fnSendHeader || gv_FlmSysData.HttpConfigParms.fnSetIOMode || gv_FlmSysData.HttpConfigParms.fnSendBuffer || gv_FlmSysData.HttpConfigParms.fnAcquireSession || gv_FlmSysData.HttpConfigParms.fnReleaseSession || gv_FlmSysData.HttpConfigParms.fnAcquireUser || gv_FlmSysData.HttpConfigParms.fnReleaseUser || gv_FlmSysData.HttpConfigParms.fnSetSessionValue || gv_FlmSysData.HttpConfigParms.fnGetSessionValue || gv_FlmSysData.HttpConfigParms.fnGetGblValue || gv_FlmSysData.HttpConfigParms.fnSetGblValue || gv_FlmSysData.HttpConfigParms.fnRecvBuffer) { rc = RC_SET( FERR_HTTP_SYMS_EXIST); goto Exit; } else { gv_FlmSysData.HttpConfigParms.fnReg = ((HTTPCONFIGPARAMS *)Value1)->fnReg; gv_FlmSysData.HttpConfigParms.fnDereg = ((HTTPCONFIGPARAMS *)Value1)->fnDereg; gv_FlmSysData.HttpConfigParms.fnReqPath = ((HTTPCONFIGPARAMS *)Value1)->fnReqPath; gv_FlmSysData.HttpConfigParms.fnReqQuery = ((HTTPCONFIGPARAMS *)Value1)->fnReqQuery; gv_FlmSysData.HttpConfigParms.fnReqHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnReqHdrValue; gv_FlmSysData.HttpConfigParms.fnSetHdrValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetHdrValue; gv_FlmSysData.HttpConfigParms.fnPrintf = ((HTTPCONFIGPARAMS *)Value1)->fnPrintf; gv_FlmSysData.HttpConfigParms.fnEmit = ((HTTPCONFIGPARAMS *)Value1)->fnEmit; gv_FlmSysData.HttpConfigParms.fnSetNoCache = ((HTTPCONFIGPARAMS *)Value1)->fnSetNoCache; gv_FlmSysData.HttpConfigParms.fnSendHeader = ((HTTPCONFIGPARAMS *)Value1)->fnSendHeader; gv_FlmSysData.HttpConfigParms.fnSetIOMode = ((HTTPCONFIGPARAMS *)Value1)->fnSetIOMode; gv_FlmSysData.HttpConfigParms.fnSendBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnSendBuffer; gv_FlmSysData.HttpConfigParms.fnAcquireSession = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireSession; gv_FlmSysData.HttpConfigParms.fnReleaseSession = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseSession; gv_FlmSysData.HttpConfigParms.fnAcquireUser = ((HTTPCONFIGPARAMS *)Value1)->fnAcquireUser; gv_FlmSysData.HttpConfigParms.fnReleaseUser = ((HTTPCONFIGPARAMS *)Value1)->fnReleaseUser; gv_FlmSysData.HttpConfigParms.fnSetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetSessionValue; gv_FlmSysData.HttpConfigParms.fnGetSessionValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetSessionValue; gv_FlmSysData.HttpConfigParms.fnGetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnGetGblValue; gv_FlmSysData.HttpConfigParms.fnSetGblValue = ((HTTPCONFIGPARAMS *)Value1)->fnSetGblValue; gv_FlmSysData.HttpConfigParms.fnRecvBuffer = ((HTTPCONFIGPARAMS *)Value1)->fnRecvBuffer; } break; case FLM_REGISTER_HTTP_URL: // Value1: FLM_MODULE_HANDLE // Value2: Url string if ((Value1 == NULL) || (Value2 == NULL)) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } rc = flmRegisterHttpCallback((FLM_MODULE_HANDLE)Value1, (char *)Value2); break; case FLM_DEREGISTER_HTTP_URL: rc = flmDeregisterHttpCallback(); break; case FLM_UNASSIGN_HTTP_SYMS: gv_FlmSysData.HttpConfigParms.fnReg = NULL; gv_FlmSysData.HttpConfigParms.fnDereg = NULL; gv_FlmSysData.HttpConfigParms.fnReqPath = NULL; gv_FlmSysData.HttpConfigParms.fnReqQuery = NULL; gv_FlmSysData.HttpConfigParms.fnReqHdrValue = NULL; gv_FlmSysData.HttpConfigParms.fnSetHdrValue = NULL; gv_FlmSysData.HttpConfigParms.fnPrintf = NULL; gv_FlmSysData.HttpConfigParms.fnEmit = NULL; gv_FlmSysData.HttpConfigParms.fnSetNoCache = NULL; gv_FlmSysData.HttpConfigParms.fnSendHeader = NULL; gv_FlmSysData.HttpConfigParms.fnSetIOMode = NULL; gv_FlmSysData.HttpConfigParms.fnSendBuffer = NULL; gv_FlmSysData.HttpConfigParms.fnAcquireSession = NULL; gv_FlmSysData.HttpConfigParms.fnReleaseSession = NULL; gv_FlmSysData.HttpConfigParms.fnAcquireUser = NULL; gv_FlmSysData.HttpConfigParms.fnReleaseUser = NULL; gv_FlmSysData.HttpConfigParms.fnSetSessionValue = NULL; gv_FlmSysData.HttpConfigParms.fnGetSessionValue = NULL; gv_FlmSysData.HttpConfigParms.fnGetGblValue = NULL; gv_FlmSysData.HttpConfigParms.fnSetGblValue = NULL; gv_FlmSysData.HttpConfigParms.fnRecvBuffer = NULL; break; case FLM_KILL_DB_HANDLES: { FFILE * pTmpFile; f_mutexLock( gv_FlmSysData.hShareMutex); if( Value1) { // Look up the file using flmFindFile to see if we have the // file open. May unlock and re-lock the global mutex. if( RC_OK( flmFindFile( (const char *)Value1, (const char *)Value2, &pTmpFile)) && pTmpFile) { flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); } } else { if( gv_FlmSysData.pFileHashTbl) { FLMUINT uiLoop; for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) { pTmpFile = (FFILE *)gv_FlmSysData.pFileHashTbl[ uiLoop].pFirstInBucket; while( pTmpFile) { flmSetMustCloseFlags( pTmpFile, FERR_OK, TRUE); pTmpFile = pTmpFile->pNext; } } } } f_mutexUnlock( gv_FlmSysData.hShareMutex); // Kill all sessions if( gv_FlmSysData.pSessionMgr) { gv_FlmSysData.pSessionMgr->shutdownSessions(); } break; } case FLM_QUERY_MAX: f_mutexLock( gv_FlmSysData.hQueryMutex); gv_FlmSysData.uiMaxQueries = (FLMUINT)Value1; gv_FlmSysData.bNeedToUnsetMaxQueries = FALSE; flmFreeSavedQueries( TRUE); // flmFreeSavedQueries unlocks the mutex. break; case FLM_MAX_DIRTY_CACHE: f_mutexLock( gv_FlmSysData.hShareMutex); if (!Value1) { gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = TRUE; gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = 0; gv_FlmSysData.SCacheMgr.uiLowDirtyCache = 0; } else { gv_FlmSysData.SCacheMgr.bAutoCalcMaxDirty = FALSE; gv_FlmSysData.SCacheMgr.uiMaxDirtyCache = (FLMUINT)Value1; // Low threshhold must be no higher than maximum! if ((gv_FlmSysData.SCacheMgr.uiLowDirtyCache = (FLMUINT)Value2) > gv_FlmSysData.SCacheMgr.uiMaxDirtyCache) { gv_FlmSysData.SCacheMgr.uiLowDirtyCache = gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; } } f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_QUERY_STRATIFY_LIMITS: f_mutexLock( gv_FlmSysData.hShareMutex); gv_FlmSysData.uiMaxStratifyIterations = (FLMUINT)Value1; gv_FlmSysData.uiMaxStratifyTime = (FLMUINT)Value2; f_mutexUnlock( gv_FlmSysData.hShareMutex); break; default: rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } Exit: return( rc); } /**************************************************************************** Desc : Gets configured shared attributes. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmGetConfig( eFlmConfigTypes eConfigType, void * Value1) { RCODE rc = FERR_OK; switch( eConfigType) { case FLM_CACHE_LIMIT: f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.Usage.uiMaxBytes + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes; f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_BLOCK_CACHE_PERCENTAGE: f_mutexLock( gv_FlmSysData.hShareMutex); *((FLMUINT *)Value1) = gv_FlmSysData.uiBlockCachePercentage; f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_SCACHE_DEBUG: #ifdef FLM_DEBUG *((FLMBOOL *)Value1) = gv_FlmSysData.SCacheMgr.bDebug; #else *((FLMBOOL *)Value1) = FALSE; #endif break; case FLM_OPEN_FILES: { *((FLMUINT *)Value1) = f_getOpenFileCount(); break; } case FLM_OPEN_THRESHOLD: *((FLMUINT *)Value1) = gv_FlmSysData.pFileHdlCache->getOpenThreshold(); break; case FLM_DIRECT_IO_STATE: { f_mutexLock( gv_FlmSysData.hShareMutex); if( gv_FlmSysData.uiFileOpenFlags & FLM_IO_DIRECT) { *((FLMBOOL *)Value1) = TRUE; } else { *((FLMBOOL *)Value1) = FALSE; } f_mutexUnlock( gv_FlmSysData.hShareMutex); } case FLM_TMPDIR: f_mutexLock( gv_FlmSysData.hShareMutex); if( !gv_FlmSysData.bTempDirSet ) { rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); // Set the output to nulls on failure. *((char *)Value1) = 0; } else { f_strcpy( (char *)Value1, gv_FlmSysData.szTempDir); } f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_MAX_CP_INTERVAL: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxCPInterval); break; case FLM_BLOB_EXT: f_strcpy( (char *)Value1, (const char *)gv_FlmSysData.ucBlobExt); break; case FLM_MAX_TRANS_SECS: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransTime); break; case FLM_MAX_TRANS_INACTIVE_SECS: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxTransInactiveTime); break; case FLM_CACHE_ADJUST_INTERVAL: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheAdjustInterval); break; case FLM_CACHE_CLEANUP_INTERVAL: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiCacheCleanupInterval); break; case FLM_UNUSED_CLEANUP_INTERVAL: *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiUnusedCleanupInterval); break; case FLM_MAX_UNUSED_TIME: f_mutexLock( gv_FlmSysData.hShareMutex); *((FLMUINT *)Value1) = FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime); f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_CACHE_CHECK: *((FLMBOOL *)Value1) = gv_FlmSysData.bCheckCache; break; case FLM_QUERY_MAX: f_mutexLock( gv_FlmSysData.hQueryMutex); *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxQueries; f_mutexUnlock( gv_FlmSysData.hQueryMutex); break; case FLM_MAX_DIRTY_CACHE: f_mutexLock( gv_FlmSysData.hShareMutex); *((FLMUINT *)Value1) = gv_FlmSysData.SCacheMgr.uiMaxDirtyCache; f_mutexUnlock( gv_FlmSysData.hShareMutex); break; case FLM_DYNA_CACHE_SUPPORTED: if( f_canGetMemoryInfo()) { *((FLMBOOL *)Value1) = TRUE; } else { *((FLMBOOL *)Value1) = FALSE; } break; case FLM_QUERY_STRATIFY_LIMITS: f_mutexLock( gv_FlmSysData.hShareMutex); if (Value1) { *((FLMUINT *)Value1) = gv_FlmSysData.uiMaxStratifyIterations; } f_mutexUnlock( gv_FlmSysData.hShareMutex); break; default: rc = RC_SET( FERR_NOT_IMPLEMENTED); break; } //Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMEXP RCODE FLMAPI FlmGetThreadInfo( F_Pool * pPool, F_THREAD_INFO ** ppThreadInfo, FLMUINT * puiNumThreads, const char * pszUrl) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = NULL; if( pszUrl) { // flmGetCSConnection may return a NULL CS_CONTEXT if the // URL references a local resource. if( RC_BAD( rc = flmGetCSConnection( pszUrl, &pCSContext))) { goto Exit; } } if( pCSContext) { NODE * pTree; FCL_WIRE Wire; Wire.setContext( pCSContext); // Send a request get statistics if (RC_BAD( Wire.sendOp( FCS_OPCLASS_GLOBAL, FCS_OP_GLOBAL_GET_THREAD_INFO))) { goto Exit; } if (RC_BAD( Wire.sendTerminate())) { goto Exit; } // Read the response. if (RC_BAD( Wire.read())) { goto Exit; } if (RC_BAD( Wire.getRCode())) { goto Exit; } if( RC_BAD( Wire.getHTD( pPool, &pTree))) { goto Exit; } if( RC_BAD( rc = fcsExtractThreadInfo( pTree, pPool, ppThreadInfo, puiNumThreads))) { goto Exit; } } else { if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getThreadInfo( pPool, ppThreadInfo, puiNumThreads))) { goto Exit; } } Exit: if( pCSContext) { flmCloseCSConnection( &pCSContext); } return( rc); } /**************************************************************************** Desc: Returns the temporary directory (path) that is part of the share structure. We have to pass in the FDB structure in order to know if we should lock a mutex in order to access the path structure. ****************************************************************************/ RCODE flmGetTmpDir( char * pszOutputTmpDir) { RCODE rc = FERR_OK; f_mutexLock( gv_FlmSysData.hShareMutex); // Return an error if the temp directory has not been set. if( !gv_FlmSysData.bTempDirSet) { rc = RC_SET( FERR_IO_PATH_NOT_FOUND ); // Set the output to nulls on failure. *pszOutputTmpDir = 0; } else { f_strcpy( pszOutputTmpDir, gv_FlmSysData.szTempDir); } f_mutexUnlock( gv_FlmSysData.hShareMutex); return( rc); } /**************************************************************************** Desc: This shuts down the background threads Note: This routine assumes that the global mutex is locked. The mutex will be unlocked internally, but will always be locked on exit. ****************************************************************************/ FSTATIC void flmShutdownDbThreads( FFILE * pFile) { RCODE rc = FERR_OK; F_BKGND_IX * pBackgroundIx; FDB * pDb; IF_Thread * pThread; FLMUINT uiThreadId; FLMUINT uiThreadCount; FLMBOOL bMutexLocked = TRUE; // Shut down the tracker thread if( pFile->pMaintThrd) { pFile->pMaintThrd->setShutdownFlag(); f_semSignal( pFile->hMaintSem); // Unlock the global mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; pFile->pMaintThrd->stopThread(); // Re-lock the mutex f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; pFile->pMaintThrd->Release(); pFile->pMaintThrd = NULL; f_semDestroy( &pFile->hMaintSem); } // Signal all background threads to shut down that are // associated with this FFILE for( ;;) { uiThreadCount = 0; // Shut down all background indexing threads. uiThreadId = 0; for( ;;) { if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_uiBackIxThrdGroup, &uiThreadId))) { if( rc == FERR_NOT_FOUND) { rc = FERR_OK; break; } else { flmAssert( 0); } } else { pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); if( pBackgroundIx && pBackgroundIx->pFile == pFile) { // Set the thread's terminate flag. uiThreadCount++; pThread->setShutdownFlag(); } pThread->Release(); pThread = NULL; } } // Shut down all threads in the database thread group uiThreadId = 0; for( ;;) { if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_uiDbThrdGrp, &uiThreadId))) { if( rc == FERR_NOT_FOUND) { rc = FERR_OK; break; } else { flmAssert( 0); } } else { pDb = (FDB *)pThread->getParm2(); if (pDb && pDb->pFile == pFile) { // Set the thread's terminate flag. uiThreadCount++; pThread->setShutdownFlag(); } pThread->Release(); pThread = NULL; } } if( !uiThreadCount) { break; } // Unlock the global mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Give the threads a chance to terminate f_sleep( 50); // Re-lock the mutex and see if any threads are still active f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } // Re-lock the mutex if( !bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); } } /**************************************************************************** Desc: This routine frees all of the structures associated with a file. Whoever called this routine has already determined that it is safe to do so. Notes: The global mutex is assumed to be locked when entering the routine. It may be unlocked and re-locked before the routine exits, however. ****************************************************************************/ void flmFreeFile( FFILE * pFile) { F_NOTIFY_LIST_ITEM * pCloseNotifies; // See if another thread is in the process of closing // this FFILE. It is possible for this to happen, since // the monitor thread may have selected this FFILE to be // closed because it has been unused for a period of time. // At the same time, a foreground thread could have called // FlmConfig to close all unused FFILEs. Since flmFreeFile // may unlock and re-lock the mutex, there is a small window // of opportunity for both threads to try to free the same // FFILE. if( pFile->uiFlags & DBF_BEING_CLOSED) { return; } // Set the DBF_BEING_CLOSED flag pFile->uiFlags |= DBF_BEING_CLOSED; // Shut down all background threads before shutting down the CP thread. flmShutdownDbThreads( pFile); // At this point, the use count better be zero flmAssert( pFile->uiUseCount == 0); // Unlock the mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); // Shutdown the checkpoint thread if( pFile->pCPThrd) { pFile->pCPThrd->stopThread(); pFile->pCPThrd->Release(); pFile->pCPThrd = NULL; } // Shutdown the monitor thread if( pFile->pMonitorThrd) { pFile->pMonitorThrd->stopThread(); pFile->pMonitorThrd->Release(); pFile->pMonitorThrd = NULL; } f_mutexLock( gv_FlmSysData.hShareMutex); // Unlink all of the DICTs that are connected to the file. while( pFile->pDictList) { flmUnlinkDict( pFile->pDictList); } // Take the file out of its name hash bucket, if any. if (pFile->uiBucket != 0xFFFF) { flmUnlinkFileFromBucket( pFile); } // Unlink the file from the not-used list flmUnlinkFileFromNUList( pFile); // Free the RFL data, if any. if (pFile->pRfl) { pFile->pRfl->Release(); pFile->pRfl = NULL; } // We shouldn't have any open notifies at this point flmAssert( pFile->pOpenNotifies == NULL); // Save pCloseNotifies -- we will notify any waiters once the // FFILE has been freed. pCloseNotifies = pFile->pCloseNotifies; // Free any dictionary usage structures associated with the file. flmFreeDictList( &pFile->pDictList); // Free any shared cache associated with the file. ScaFreeFileCache( pFile); // Free any record cache associated with the file. flmRcaFreeFileRecs( pFile); // Release the lock objects. if( pFile->pWriteLockObj) { pFile->pWriteLockObj->Release(); pFile->pWriteLockObj = NULL; } if( pFile->pFileLockObj) { pFile->pFileLockObj->Release(); pFile->pFileLockObj = NULL; } // Close and delete the lock file. if( pFile->pLockFileHdl) { pFile->pLockFileHdl->Release(); pFile->pLockFileHdl = NULL; } // Free the write buffer managers. if( pFile->pBufferMgr) { pFile->pBufferMgr->Release(); pFile->pBufferMgr = NULL; } // Free the log header write buffer if( pFile->pucLogHdrIOBuf) { f_freeAlignedBuffer( (void **)&pFile->pucLogHdrIOBuf); } pFile->krefPool.poolFree(); if( pFile->ppBlocksDone) { f_free( &pFile->ppBlocksDone); pFile->uiBlocksDoneArraySize = 0; } // Free the maintenance thread's semaphore if( pFile->hMaintSem != F_SEM_NULL) { f_semDestroy( &pFile->hMaintSem); } // Free the database wrapping key if( pFile->pDbWrappingKey) { pFile->pDbWrappingKey->Release(); pFile->pDbWrappingKey = NULL; } // Free the password if( pFile->pszDbPassword) { f_free( &pFile->pszDbPassword); } // Free the FFILE f_free( &pFile); // Notify waiters that the FFILE is gone while( pCloseNotifies) { F_SEM hSem; *(pCloseNotifies->pRc) = FERR_OK; hSem = pCloseNotifies->hSem; pCloseNotifies = pCloseNotifies->pNext; f_semSignal( hSem); } // Global mutex is still locked at this point } /**************************************************************************** Desc: This routine frees a registered event. ****************************************************************************/ FSTATIC void flmFreeEvent( FEVENT * pEvent, F_MUTEX hMutex, FEVENT ** ppEventListRV) { f_mutexLock( hMutex); if (pEvent->pPrev) { pEvent->pPrev->pNext = pEvent->pNext; } else { *ppEventListRV = pEvent->pNext; } if (pEvent->pNext) { pEvent->pNext->pPrev = pEvent->pPrev; } f_mutexUnlock( hMutex); f_free( &pEvent); } /************************************************************************ Desc : Cleans up - assumes that the spin lock has already been obtained. This allows it to be called directly from FlmStartup on error conditions. ************************************************************************/ FSTATIC void flmCleanup( void) { FLMUINT uiCnt; // NOTE: We are checking and decrementing a global variable here. // However, on platforms that properly support atomic exchange, // we are OK, because the caller has obtained a spin lock before // calling this routine, so we are guaranteed to be the only thread // executing this code at this point. On platforms that don't // support atomic exchange, our spin lock will be less reliable for // really tight race conditions. But in reality, nobody should be // calling FlmStartup and FlmShutdown in race conditions like that // anyway. We are only doing the spin lock stuff to try and be // nice about it if they are. // This check allows FlmShutdown to be called before calling // FlmStartup, or even if FlmStartup fails. if (!gv_uiFlmSysStartupCount) { return; } // If we decrement the count and it doesn't go to zero, we are not // ready to do cleanup yet. if (--gv_uiFlmSysStartupCount > 0) { return; } // Deregister the Http Callback function if it has been registered. // Note: Usually, this is all handled at the SMI level (in // SMDIBHandle::exit). This code is here for the cases where we're // part of Flint or some other utility that doesn't necessarily use // SMI... if (gv_FlmSysData.HttpConfigParms.fnDereg) { FlmConfig( FLM_DEREGISTER_HTTP_URL, NULL, NULL); } // Free any queries that have been saved in the query list. if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) { // Setting uiMaxQueries to zero will cause flmFreeSavedQueries // to free the entire list. Also, embedded queries will not be // added back into the list when uiMaxQueries is zero. gv_FlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( FALSE); } // Shut down the monitor thread, if there is one. f_threadDestroy( &gv_FlmSysData.pMonitorThrd); // Shut down the session manager if( gv_FlmSysData.pSessionMgr) { gv_FlmSysData.pSessionMgr->Release(); gv_FlmSysData.pSessionMgr = NULL; } // Destroy the session mutex if( gv_FlmSysData.hHttpSessionMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_FlmSysData.hHttpSessionMutex); } gv_FlmSysData.pMrnuFile = NULL; gv_FlmSysData.pLrnuFile = NULL; // Free all of the files and associated structures if (gv_FlmSysData.pFileHashTbl) { F_BUCKET * pFileHashTbl; // flmFreeFile expects the global mutex to be locked // IMPORTANT NOTE: pFileHashTbl is ALWAYS allocated // AFTER the mutex is allocated, so we are guaranteed // to have a mutex if pFileHashTbl is non-NULL. f_mutexLock( gv_FlmSysData.hShareMutex); for (uiCnt = 0, pFileHashTbl = gv_FlmSysData.pFileHashTbl; uiCnt < FILE_HASH_ENTRIES; uiCnt++, pFileHashTbl++) { FFILE * pFile = (FFILE *)pFileHashTbl->pFirstInBucket; FFILE * pTmpFile; while( pFile) { pTmpFile = pFile; pFile = pFile->pNext; flmFreeFile( pTmpFile); } pFileHashTbl->pFirstInBucket = NULL; } f_mutexUnlock( gv_FlmSysData.hShareMutex); f_free( &gv_FlmSysData.pFileHashTbl); } // Free the statistics. if (gv_FlmSysData.bStatsInitialized) { FlmFreeStats( &gv_FlmSysData.Stats); gv_FlmSysData.bStatsInitialized = FALSE; } // Free the resources of the shared cache manager. ScaExit(); // Free the resources of the record cache manager. flmRcaExit(); // Free the mutexes last of all. if (gv_FlmSysData.hQueryMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_FlmSysData.hQueryMutex); } if (gv_FlmSysData.hShareMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_FlmSysData.hShareMutex); } if (gv_FlmSysData.HttpConfigParms.hMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_FlmSysData.HttpConfigParms.hMutex); } // Free up callbacks that have been registered for events. if (gv_FlmSysData.UpdateEvents.hMutex != F_MUTEX_NULL) { while (gv_FlmSysData.UpdateEvents.pEventCBList) { flmFreeEvent( gv_FlmSysData.UpdateEvents.pEventCBList, gv_FlmSysData.UpdateEvents.hMutex, &gv_FlmSysData.UpdateEvents.pEventCBList); } f_mutexDestroy( &gv_FlmSysData.UpdateEvents.hMutex); } if (gv_FlmSysData.LockEvents.hMutex != F_MUTEX_NULL) { while (gv_FlmSysData.LockEvents.pEventCBList) { flmFreeEvent( gv_FlmSysData.LockEvents.pEventCBList, gv_FlmSysData.LockEvents.hMutex, &gv_FlmSysData.LockEvents.pEventCBList); } f_mutexDestroy( &gv_FlmSysData.LockEvents.hMutex); } if (gv_FlmSysData.SizeEvents.hMutex != F_MUTEX_NULL) { while (gv_FlmSysData.SizeEvents.pEventCBList) { flmFreeEvent( gv_FlmSysData.SizeEvents.pEventCBList, gv_FlmSysData.SizeEvents.hMutex, &gv_FlmSysData.SizeEvents.pEventCBList); } f_mutexDestroy( &gv_FlmSysData.SizeEvents.hMutex); } // Free (release) FLAIM's File Shared File System Object. if( gv_FlmSysData.pFileSystem) { gv_FlmSysData.pFileSystem->Release(); gv_FlmSysData.pFileSystem = NULL; } #ifdef FLM_DBG_LOG flmDbgLogExit(); #endif // Release the logger (if any) if( gv_FlmSysData.pLogger) { gv_FlmSysData.pLogger->Release(); gv_FlmSysData.pLogger = NULL; } // Release the thread manager if( gv_FlmSysData.pThreadMgr) { gv_FlmSysData.pThreadMgr->Release(); gv_FlmSysData.pThreadMgr = NULL; } // Release the file handle cache if( gv_FlmSysData.pFileHdlCache) { gv_FlmSysData.pFileHdlCache->Release(); gv_FlmSysData.pFileHdlCache = NULL; } // Release the slab manager if( gv_FlmSysData.pSlabManager) { gv_FlmSysData.pSlabManager->Release(); gv_FlmSysData.pSlabManager = NULL; } // Shutdown NICI #ifdef FLM_USE_NICI CCS_Shutdown(); #endif // Shut down the toolkit ftkShutdown(); } /**************************************************************************** Desc: Shuts down FLAIM. Notes: Allows itself to be called multiple times and even before FlmStartup is called, or even if FlmStartup fails. Warning: May not handle race conditions very well on platforms that do not support atomic exchange. ****************************************************************************/ FLMEXP void FLMAPI FlmShutdown( void) { flmLockSysData(); flmCleanup(); flmUnlockSysData(); } /**************************************************************************** Desc: This routine links an FFILE structure to its name hash bucket. NOTE: This function assumes that the global mutex has been locked. ****************************************************************************/ RCODE flmLinkFileToBucket( FFILE * pFile) { RCODE rc = FERR_OK; FFILE * pTmpFile; F_BUCKET * pBucket; FLMUINT uiBucket; char szDbPathStr[ F_PATH_MAX_SIZE]; pBucket = gv_FlmSysData.pFileHashTbl; // Normalize the path to a string before hashing on it. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathToStorageString( pFile->pszDbPath, szDbPathStr))) { goto Exit; } uiBucket = f_strHashBucket( szDbPathStr, pBucket, FILE_HASH_ENTRIES); pBucket = &pBucket [uiBucket]; if (pBucket->pFirstInBucket) { pTmpFile = (FFILE *)pBucket->pFirstInBucket; pTmpFile->pPrev = pFile; } pFile->uiBucket = uiBucket; pFile->pPrev = (FFILE *)NULL; pFile->pNext = (FFILE *)pBucket->pFirstInBucket; pBucket->pFirstInBucket = pFile; gv_FlmSysData.uiOpenFFiles++; Exit: return( rc); } /**************************************************************************** Desc: This routine unlinks an FFILE structure from its name hash bucket. NOTE: This function assumes that the global mutex has been locked. ****************************************************************************/ FSTATIC void flmUnlinkFileFromBucket( FFILE * pFile) { if (pFile->uiBucket != 0xFFFF) { if (pFile->pPrev) { pFile->pPrev->pNext = pFile->pNext; } else { gv_FlmSysData.pFileHashTbl [pFile->uiBucket].pFirstInBucket = pFile->pNext; } if (pFile->pNext) { pFile->pNext->pPrev = pFile->pPrev; } pFile->uiBucket = 0xFFFF; } flmAssert( gv_FlmSysData.uiOpenFFiles); gv_FlmSysData.uiOpenFFiles--; } /**************************************************************************** Desc: This routine links an FFILE structure to the unused list. NOTE: This function assumes that the global mutex has been locked. ****************************************************************************/ void flmLinkFileToNUList( FFILE * pFile, FLMBOOL bQuickTimeout) { if( !bQuickTimeout) { pFile->pPrevNUFile = NULL; if ((pFile->pNextNUFile = gv_FlmSysData.pMrnuFile) == NULL) { gv_FlmSysData.pLrnuFile = pFile; } else { pFile->pNextNUFile->pPrevNUFile = pFile; } gv_FlmSysData.pMrnuFile = pFile; pFile->uiZeroUseCountTime = (FLMUINT)FLM_GET_TIMER(); } else { pFile->pNextNUFile = NULL; if ((pFile->pPrevNUFile = gv_FlmSysData.pLrnuFile) == NULL) { gv_FlmSysData.pMrnuFile = pFile; } else { pFile->pPrevNUFile->pNextNUFile = pFile; } gv_FlmSysData.pLrnuFile = pFile; pFile->uiZeroUseCountTime = 0; } pFile->uiFlags |= DBF_IN_NU_LIST; if (pFile->pRfl) { pFile->pRfl->closeFile(); } } /**************************************************************************** Desc: This routine unlinks an FFILE structure from the unused list. NOTE: This function assumes that the global mutex has been locked. ****************************************************************************/ void flmUnlinkFileFromNUList( FFILE * pFile /* File to be unlinked from unused list. */ ) { if (pFile->uiFlags & DBF_IN_NU_LIST) { if (!pFile->pPrevNUFile) { gv_FlmSysData.pMrnuFile = pFile->pNextNUFile; } else { pFile->pPrevNUFile->pNextNUFile = pFile->pNextNUFile; } if (!pFile->pNextNUFile) { gv_FlmSysData.pLrnuFile = pFile->pPrevNUFile; } else { pFile->pNextNUFile->pPrevNUFile = pFile->pPrevNUFile; } pFile->pPrevNUFile = pFile->pNextNUFile = (FFILE *)NULL; pFile->uiFlags &= ~(DBF_IN_NU_LIST); } } /**************************************************************************** Desc: This routine checks unused structures to see if any have been unused longer than the maximum unused time. If so, it frees them up. Note: This routine assumes that the calling routine has locked the global mutex prior to calling this routine. The mutex may be unlocked and re-locked by one of the called routines. ****************************************************************************/ void flmCheckNUStructs( FLMUINT uiCurrTime) { FFILE * pFile; if (!uiCurrTime) { uiCurrTime = FLM_GET_TIMER(); } // Look for unused FFILEs pFile = gv_FlmSysData.pLrnuFile; for (;;) { // Break out of the loop as soon as we discover an unused FFILE // structure which has not been unused the maximum number of seconds. if (pFile && (FLM_ELAPSED_TIME( uiCurrTime, pFile->uiZeroUseCountTime) >= gv_FlmSysData.uiMaxUnusedTime || !pFile->uiZeroUseCountTime)) { // Remove the FFILE from memory. flmFreeFile( pFile); // flmFreeFile may have unlocked (and re-locked the global mutex, // so we need to start at the beginning of the list again. // Need to unlock the mutex here in case another thread is in // the process of closing the last FFILE. If we hang on to // the mutex, it will never be able to get back in and finish // the job. f_mutexUnlock( gv_FlmSysData.hShareMutex); f_yieldCPU(); f_mutexLock( gv_FlmSysData.hShareMutex); pFile = gv_FlmSysData.pLrnuFile; } else { break; } } gv_FlmSysData.pFileHdlCache->closeUnusedFiles( FLM_TIMER_UNITS_TO_SECS( gv_FlmSysData.uiMaxUnusedTime)); } /**************************************************************************** Desc: This routine unlinks an FDICT structure from its FFILE structure and then frees the FDICT structure. NOTE: This routine assumes that the global mutex is locked. ****************************************************************************/ void flmUnlinkDict( FDICT * pDict) { // Now unlink the local dictionary from its file - if it is connected // to one. if (pDict->pFile) { if (pDict->pPrev) { pDict->pPrev->pNext = pDict->pNext; } else { pDict->pFile->pDictList = pDict->pNext; } if (pDict->pNext) { pDict->pNext->pPrev = pDict->pPrev; } } /* Finally, free the local dictionary and its associated tables. */ flmFreeDict( pDict); } /**************************************************************************** Desc: This routine links an FDB structure to an FFILE structure. NOTE: This routine assumes that the global mutex has been locked. ****************************************************************************/ RCODE flmLinkFdbToFile( FDB * pDb, FFILE * pFile) { RCODE rc = FERR_OK; // If the use count on the file used to be zero, unlink it from the // unused list. flmAssert( !pDb->pFile); pDb->pPrevForFile = NULL; if ((pDb->pNextForFile = pFile->pFirstDb) != NULL) { pFile->pFirstDb->pPrevForFile = pDb; } pFile->pFirstDb = pDb; pDb->pFile = pFile; if (++pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } if (pDb->uiFlags & FDB_INTERNAL_OPEN) { pFile->uiInternalUseCount++; } return( rc); } /**************************************************************************** Desc: This routine unlinks an FDB structure from its FFILE structure. NOTE: This routine assumes that the global mutex has been locked. ****************************************************************************/ void flmUnlinkFdbFromFile( FDB * pDb) { FFILE * pFile; if ((pFile = pDb->pFile) != NULL) { // Unlink the FDB from the FFILE. if (pDb->pNextForFile) { pDb->pNextForFile->pPrevForFile = pDb->pPrevForFile; } if (pDb->pPrevForFile) { pDb->pPrevForFile->pNextForFile = pDb->pNextForFile; } else { pFile->pFirstDb = pDb->pNextForFile; } pDb->pNextForFile = pDb->pPrevForFile = NULL; pDb->pFile = NULL; // Decrement use counts in the FFILE. if (pDb->uiFlags & FDB_INTERNAL_OPEN) { flmAssert( pFile->uiInternalUseCount); pFile->uiInternalUseCount--; } // If the use count goes to zero on the file, put the file // into the unused list. flmAssert( pFile->uiUseCount); if (!(--pFile->uiUseCount)) { // If the "must close" flag is set, it indicates that // the FFILE is being forced to close. Put the FFILE in // the NU list, but specify that it should be quickly // timed-out. flmLinkFileToNUList( pFile, pFile->bMustClose); } } } /**************************************************************************** Desc: This routine functions as a thread. It monitors open files and frees up files which have been closed longer than the maximum close time. ****************************************************************************/ RCODE FLMAPI flmSystemMonitor( IF_Thread * pThread) { FLMUINT uiLastUnusedCleanupTime = 0; FLMUINT uiLastRCacheCleanupTime = 0; FLMUINT uiLastSCacheCleanupTime = 0; FLMUINT uiCurrTime; FLMUINT uiMaxLockTime; FLMUINT uiLastCacheAdjustTime = 0; uiMaxLockTime = FLM_MILLI_TO_TIMER_UNITS( 100); for (;;) { // See if we should shut down if( pThread->getShutdownFlag()) { break; } uiCurrTime = FLM_GET_TIMER(); // Check the not used stuff and lock timeouts. if ( FLM_ELAPSED_TIME( uiCurrTime, uiLastUnusedCleanupTime) >= gv_FlmSysData.uiUnusedCleanupInterval || (gv_FlmSysData.pLrnuFile && !gv_FlmSysData.pLrnuFile->uiZeroUseCountTime)) { // See if any unused structures have bee unused longer than the // maximum unused time. Free them if they have. // May unlock and re-lock the global mutex. f_mutexLock( gv_FlmSysData.hShareMutex); flmCheckNUStructs( 0); f_mutexUnlock( gv_FlmSysData.hShareMutex); // Reset the timer uiCurrTime = uiLastUnusedCleanupTime = FLM_GET_TIMER(); } // Check the adjusting cache limit if( f_canGetMemoryInfo()) { if ((gv_FlmSysData.bDynamicCacheAdjust) && (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= gv_FlmSysData.uiCacheAdjustInterval)) { FLMUINT uiCacheBytes; f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); // Make sure the dynamic adjust flag is still set. if ((gv_FlmSysData.bDynamicCacheAdjust) && (FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= gv_FlmSysData.uiCacheAdjustInterval)) { if( RC_OK( flmGetCacheBytes( gv_FlmSysData.uiCacheAdjustPercent, gv_FlmSysData.uiCacheAdjustMin, gv_FlmSysData.uiCacheAdjustMax, gv_FlmSysData.uiCacheAdjustMinToLeave, TRUE, gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated + gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated, &uiCacheBytes))) { flmSetCacheLimits( uiCacheBytes, FALSE, FALSE); } } f_mutexUnlock( gv_FlmSysData.hShareMutex); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); uiCurrTime = uiLastCacheAdjustTime = FLM_GET_TIMER(); } } // See if block cache should be cleaned up if ((gv_FlmSysData.uiCacheCleanupInterval) && (FLM_ELAPSED_TIME( uiCurrTime, uiLastSCacheCleanupTime) >= gv_FlmSysData.uiCacheCleanupInterval)) { ScaCleanupCache( uiMaxLockTime); uiCurrTime = uiLastSCacheCleanupTime = FLM_GET_TIMER(); } // See if record cache should be cleaned up if( (gv_FlmSysData.uiCacheCleanupInterval) && (FLM_ELAPSED_TIME( uiCurrTime, uiLastRCacheCleanupTime) >= gv_FlmSysData.uiCacheCleanupInterval)) { flmRcaCleanupCache( uiMaxLockTime, FALSE); uiCurrTime = uiLastRCacheCleanupTime = FLM_GET_TIMER(); } // Cleanup old sessions if( gv_FlmSysData.pSessionMgr) { gv_FlmSysData.pSessionMgr->timeoutInactiveSessions( MAX_SESSION_INACTIVE_SECS, FALSE); } pThread->sleep( 1000); } return( FERR_OK); } /**************************************************************************** Desc : Registers a callback function to receive events. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmRegisterForEvent( FEventCategory eCategory, FEVENT_CB fnEventCB, void * pvAppData, HFEVENT * phEventRV) { RCODE rc = FERR_OK; FEVENT * pEvent; *phEventRV = HFEVENT_NULL; // Allocate an event structure if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( FEVENT)), &pEvent))) { goto Exit; } *phEventRV = (HFEVENT)pEvent; // Initialize the structure members and linkt to the // list of events off of the event category. pEvent->eCategory = eCategory; pEvent->fnEventCB = fnEventCB; pEvent->pvAppData = pvAppData; // Mutex should be locked to link into list. switch( eCategory) { case F_EVENT_UPDATES: { f_mutexLock( gv_FlmSysData.UpdateEvents.hMutex); if ((pEvent->pNext = gv_FlmSysData.UpdateEvents.pEventCBList) != NULL) { pEvent->pNext->pPrev = pEvent; } gv_FlmSysData.UpdateEvents.pEventCBList = pEvent; f_mutexUnlock( gv_FlmSysData.UpdateEvents.hMutex); break; } case F_EVENT_LOCKS: { f_mutexLock( gv_FlmSysData.LockEvents.hMutex); if ((pEvent->pNext = gv_FlmSysData.LockEvents.pEventCBList) != NULL) { pEvent->pNext->pPrev = pEvent; } gv_FlmSysData.LockEvents.pEventCBList = pEvent; f_mutexUnlock( gv_FlmSysData.LockEvents.hMutex); break; } case F_EVENT_SIZE: { f_mutexLock( gv_FlmSysData.SizeEvents.hMutex); if ((pEvent->pNext = gv_FlmSysData.SizeEvents.pEventCBList) != NULL) { pEvent->pNext->pPrev = pEvent; } gv_FlmSysData.SizeEvents.pEventCBList = pEvent; f_mutexUnlock( gv_FlmSysData.SizeEvents.hMutex); break; } default: { rc = RC_SET_AND_ASSERT( FERR_NOT_IMPLEMENTED); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc : Deregisters a callback function that was registered to receive events. ****************************************************************************/ FLMEXP void FLMAPI FlmDeregisterForEvent( HFEVENT * phEventRV) { if (phEventRV && *phEventRV != HFEVENT_NULL) { FEVENT * pEvent = (FEVENT *)(*phEventRV); switch( pEvent->eCategory) { case F_EVENT_UPDATES: { flmFreeEvent( pEvent, gv_FlmSysData.UpdateEvents.hMutex, &gv_FlmSysData.UpdateEvents.pEventCBList); break; } case F_EVENT_LOCKS: { flmFreeEvent( pEvent, gv_FlmSysData.LockEvents.hMutex, &gv_FlmSysData.LockEvents.pEventCBList); break; } case F_EVENT_SIZE: { flmFreeEvent( pEvent, gv_FlmSysData.SizeEvents.hMutex, &gv_FlmSysData.SizeEvents.pEventCBList); break; } default: { flmAssert( 0); } } *phEventRV = HFEVENT_NULL; } } /**************************************************************************** Desc: This routine does an event callback. Note that the mutex is locked during the callback. ****************************************************************************/ void flmDoEventCallback( FEventCategory eCategory, FEventType eEventType, void * pvEventData1, void * pvEventData2) { FEVENT * pEvent; switch( eCategory) { case F_EVENT_UPDATES: { f_mutexLock( gv_FlmSysData.UpdateEvents.hMutex); pEvent = gv_FlmSysData.UpdateEvents.pEventCBList; while (pEvent) { (*pEvent->fnEventCB)( eEventType, pEvent->pvAppData, pvEventData1, pvEventData2); pEvent = pEvent->pNext; } f_mutexUnlock( gv_FlmSysData.UpdateEvents.hMutex); break; } case F_EVENT_LOCKS: { f_mutexLock( gv_FlmSysData.LockEvents.hMutex); pEvent = gv_FlmSysData.LockEvents.pEventCBList; while (pEvent) { (*pEvent->fnEventCB)( eEventType, pEvent->pvAppData, pvEventData1, pvEventData2); pEvent = pEvent->pNext; } f_mutexUnlock( gv_FlmSysData.LockEvents.hMutex); break; } case F_EVENT_SIZE: { f_mutexLock( gv_FlmSysData.SizeEvents.hMutex); pEvent = gv_FlmSysData.SizeEvents.pEventCBList; while (pEvent) { (*pEvent->fnEventCB)( eEventType, pEvent->pvAppData, pvEventData1, pvEventData2); pEvent = pEvent->pNext; } f_mutexUnlock( gv_FlmSysData.SizeEvents.hMutex); break; } default: { flmAssert( 0); } } } /**************************************************************************** Desc: This routine sets the "must close" flags on the FFILE and its FDBs ****************************************************************************/ void flmSetMustCloseFlags( FFILE * pFile, RCODE rcMustClose, FLMBOOL bMutexLocked) { FDB * pTmpDb; if( !bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); } if( !pFile->bMustClose) { pFile->bMustClose = TRUE; pFile->rcMustClose = rcMustClose; pTmpDb = pFile->pFirstDb; while( pTmpDb) { pTmpDb->bMustClose = TRUE; pTmpDb = pTmpDb->pNextForFile; } // Log a message indicating why the "must close" flag has been // set. Calling flmCheckFFileState with the bMustClose flag // already set to TRUE will cause a message to be logged. (void)flmCheckFFileState( pFile); } if( !bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } } /**************************************************************************** Desc: Constructor ****************************************************************************/ F_Session::F_Session() { m_pSessionMgr = NULL; m_uiThreadId = 0; m_uiThreadLockCount = 0; m_hMutex = F_MUTEX_NULL; m_pNotifyList = NULL; m_pPrev = NULL; m_pNext = NULL; m_pNameTable = NULL; m_uiNameTableFFileId = 0; m_uiDictSeqNum = 0; m_uiLastUsed = FLM_GET_TIMER(); m_uiNextToken = FLM_GET_TIMER(); m_pDbTable = NULL; f_memset( m_ucKey, 0, sizeof( m_ucKey)); } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_Session::~F_Session() { flmAssert( !m_pPrev); flmAssert( !m_pNext); flmAssert( !getRefCount()); flmAssert( !m_uiThreadLockCount); // Wake up any waiters signalLockWaiters( FERR_FAILURE, FALSE); // Free the session mutex if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } // Clean up any database objects if( m_pDbTable) { m_pDbTable->Release(); } // Free the name table if( m_pNameTable) { m_pNameTable->Release(); } } /**************************************************************************** Desc: Signals the next thread waiting to acquire the lock on the session if rc == FERR_OK. Otherwise, signals all waiting threads. ****************************************************************************/ void F_Session::signalLockWaiters( RCODE rc, FLMBOOL bMutexLocked) { F_SEM hSem; if( m_pNotifyList) { if( !bMutexLocked) { f_mutexLock( m_hMutex); } while( m_pNotifyList) { *(m_pNotifyList->pRc) = rc; hSem = m_pNotifyList->hSem; m_pNotifyList = m_pNotifyList->pNext; f_semSignal( hSem); if( RC_OK( rc)) { break; } } if( !bMutexLocked) { f_mutexUnlock( m_hMutex); } } } /**************************************************************************** Desc: Configures a session for use. ****************************************************************************/ RCODE F_Session::setupSession( F_SessionMgr * pSessionMgr) { RCODE rc = FERR_OK; flmAssert( m_hMutex == F_MUTEX_NULL); flmAssert( pSessionMgr); if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } if( (m_pDbTable = f_new F_HashTable) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = m_pDbTable->setupHashTable( FALSE, 16, 0))) { goto Exit; } m_pSessionMgr = pSessionMgr; Exit: return( rc); } /**************************************************************************** Desc: Adds a database handle to the session's list of handles. Once added to the session, the calling code should not close the handle directly. ****************************************************************************/ RCODE F_Session::addDbHandle( HFDB hDb, char * pucKey) { RCODE rc = FERR_OK; F_SessionDb * pSessionDb = NULL; const void * pvKey; FLMUINT uiKeyLen; if( (pSessionDb = f_new F_SessionDb) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSessionDb->setupSessionDb( this, hDb))) { goto Exit; } if( RC_BAD( rc = m_pDbTable->addObject( pSessionDb))) { goto Exit; } if( pucKey) { pvKey = pSessionDb->getKey(); uiKeyLen = pSessionDb->getKeyLength(); flmAssert( uiKeyLen == F_SESSION_DB_KEY_LEN); f_memcpy( pucKey, (FLMBYTE *)pvKey, uiKeyLen); } pSessionDb->Release(); pSessionDb = NULL; Exit: if( pSessionDb) { pSessionDb->m_hDb = HFDB_NULL; pSessionDb->Release(); } return( rc); } /**************************************************************************** Desc: Closes the specific database identified by the passed-in key ****************************************************************************/ void F_Session::closeDb( const char * pucKey) { (void)m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, NULL, TRUE); } /**************************************************************************** Desc: Gets a specific database handle from the session given the passed-in key ****************************************************************************/ RCODE F_Session::getDbHandle( const char * pucKey, HFDB * phDb) { RCODE rc = FERR_OK; F_SessionDb * pSessionDb = NULL; F_HashObject * pObject; *phDb = HFDB_NULL; if( RC_BAD( rc = m_pDbTable->getObject( (void *)pucKey, F_SESSION_DB_KEY_LEN, &pObject, FALSE))) { if( rc == FERR_NOT_FOUND) { rc = RC_SET( FERR_BAD_HDL); } goto Exit; } pSessionDb = (F_SessionDb *)pObject; *phDb = pSessionDb->m_hDb; Exit: if( pSessionDb) { pSessionDb->Release(); } return( rc); } /**************************************************************************** Desc: Returns the next database handle in the global list of handles managed by the session. ****************************************************************************/ RCODE F_Session::getNextDb( F_SessionDb ** ppSessionDb) { F_HashObject * pObject = *ppSessionDb; RCODE rc = FERR_OK; if( RC_BAD( rc = m_pDbTable->getNextObjectInGlobal( &pObject))) { goto Exit; } *ppSessionDb = (F_SessionDb *)pObject; Exit: return( rc); } /**************************************************************************** Desc: Releases any session resources associated with the specified FFILE ****************************************************************************/ void F_Session::releaseFileResources( FFILE * pFile) { F_HashObject * pObject; F_HashObject * pNextObject; F_SessionDb * pSessionDb; // Close all database handles with the specified FFILE pObject = NULL; if( RC_BAD( m_pDbTable->getNextObjectInGlobal( &pObject))) { goto Exit; } while( pObject) { if( (pNextObject = pObject->getNextInGlobal()) != NULL) { pNextObject->AddRef(); } if( pObject->getObjectType() == HASH_DB_OBJ) { pSessionDb = (F_SessionDb *)pObject; if( ((FDB *)pSessionDb->getDbHandle())->pFile == pFile) { closeDb( (const char *)pSessionDb->getKey()); } } pObject->Release(); pObject = pNextObject; } Exit: return; } /**************************************************************************** Desc: Returns a pointer to a name table generated based on the supplied database handle ****************************************************************************/ RCODE F_Session::getNameTable( HFDB hDb, F_NameTable ** ppNameTable) { FLMUINT uiSeq; FLMUINT uiFFileId; RCODE rc = FERR_OK; if( !m_pNameTable) { if( (m_pNameTable = f_new F_NameTable) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_DICT_SEQ_NUM, (void *)&uiSeq))) { goto Exit; } if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_FFILE_ID, (void *)&uiFFileId))) { goto Exit; } // If the database handle does not reference the same // database or dictionary as the last time the name table // was refreshed, we need to re-populate the table. if( uiSeq != m_uiDictSeqNum || m_uiNameTableFFileId != uiFFileId) { if( RC_BAD( rc = m_pNameTable->setupFromDb( hDb))) { goto Exit; } m_uiDictSeqNum = uiSeq; m_uiNameTableFFileId = uiFFileId; } *ppNameTable = m_pNameTable; Exit: return( rc); } /**************************************************************************** Desc: Returns a pointer to a name table generated based on the supplied FFILE. ****************************************************************************/ RCODE F_Session::getNameTable( FFILE * pFile, F_NameTable ** ppNameTable) { FDB * pDb = NULL; RCODE rc = FERR_OK; // Temporarily open the database if( RC_BAD( rc = flmOpenFile( pFile, NULL, NULL, NULL, 0, TRUE, NULL, NULL, pFile->pszDbPassword, &pDb))) { goto Exit; } // Get the name table if( RC_BAD( rc = getNameTable( (HFDB)pDb, ppNameTable))) { goto Exit; } Exit: if( pDb) { (void) FlmDbClose( (HFDB *)&pDb); } return( rc); } /**************************************************************************** Desc: Returns a unique token generated by the session manager. Tokens are used to help make handles passed to clients unique across server executions ****************************************************************************/ FLMUINT F_Session::getNextToken( void) { return( m_pSessionMgr->getNextToken()); } /**************************************************************************** Desc: Locks a session for use by a thread. If bWait is TRUE, the thread will be put into a queue until the lock can be granted. This routine can be called multiple times by the same thread, as long as unlockSession is called a corresponding number of times. ****************************************************************************/ RCODE F_Session::lockSession( FLMBOOL bWait) { RCODE rc = FERR_OK; flmAssert( getRefCount()); f_mutexLock( m_hMutex); if( m_uiThreadId && m_uiThreadId != f_threadId()) { if( !bWait) { rc = RC_SET( FERR_TIMEOUT); goto Exit; } if( RC_BAD( rc = f_notifyWait( m_hMutex, F_SEM_NULL, NULL, &m_pNotifyList))) { goto Exit; } } m_uiThreadId = f_threadId(); m_uiThreadLockCount++; Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: Releases a thread's lock on a session. ****************************************************************************/ void F_Session::unlockSession( void) { F_SEM hSem; flmAssert( getRefCount()); f_mutexLock( m_hMutex); if( m_uiThreadId != f_threadId()) { flmAssert( 0); } else { if( --m_uiThreadLockCount == 0) { m_uiThreadId = 0; if( m_pNotifyList) { *(m_pNotifyList->pRc) = FERR_OK; hSem = m_pNotifyList->hSem; m_pNotifyList = m_pNotifyList->pNext; f_semSignal( hSem); } } m_uiLastUsed = FLM_GET_TIMER(); } f_mutexUnlock( m_hMutex); } /**************************************************************************** Desc: Gets the session object's hash key ****************************************************************************/ const void * FLMAPI F_Session::getKey( void) { return( m_ucKey); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_Session::getKeyLength( void) { return( sizeof( m_ucKey)); } /**************************************************************************** Desc: Adds a reference to the session object. The mutex is locked prior to incrementing the count since multiple threads are allowed to acquire a pointer to the object. However, they shouldn't use the object w/o first locking it via a call to lockSession. ****************************************************************************/ FLMINT F_Session::AddRef( void) { FLMINT iRefCnt; f_mutexLock( m_hMutex); flmAssert( getRefCount()); iRefCnt = ++m_refCnt; f_mutexUnlock( m_hMutex); return( iRefCnt); } /**************************************************************************** Desc: Decrements the objects use count ****************************************************************************/ FLMINT F_Session::Release( void) { FLMINT iRefCnt; flmAssert( getRefCount()); f_mutexLock( m_hMutex); if( (iRefCnt = --m_refCnt) == 0) { f_mutexUnlock( m_hMutex); delete this; return( iRefCnt); } f_mutexUnlock( m_hMutex); return( iRefCnt); } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_SessionMgr::~F_SessionMgr() { if( m_pSessionTable) { shutdownSessions(); m_pSessionTable->Release(); } if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: Releases all resources (database handles, etc.) in all sessions if they are tied to the specified FFILE ****************************************************************************/ void F_SessionMgr::releaseFileResources( FFILE * pFile) { F_Session * pSession; F_HashObject * pObject; FLMBOOL bMutexLocked = FALSE; if( m_hMutex == F_MUTEX_NULL) { goto Exit; } f_mutexLock( m_hMutex); bMutexLocked = TRUE; pObject = NULL; if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) { goto Exit; } while( pObject) { flmAssert( pObject->getObjectType() == HASH_SESSION_OBJ); pSession = (F_Session *)pObject; if( (pObject = pObject->getNextInGlobal()) != NULL) { pObject->AddRef(); } if( RC_OK( pSession->lockSession())) { pSession->releaseFileResources( pFile); pSession->unlockSession(); } pSession->Release(); } Exit: if (bMutexLocked) { f_mutexUnlock( m_hMutex); } return; } /**************************************************************************** Desc: Shuts down all sessions being managed ****************************************************************************/ void F_SessionMgr::shutdownSessions() { F_Session * pSession; F_HashObject * pObject; FLMBOOL bMutexLocked = FALSE; if( m_hMutex == F_MUTEX_NULL) { goto Exit; } f_mutexLock( m_hMutex); bMutexLocked = TRUE; pObject = NULL; if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) { goto Exit; } while( pObject) { flmAssert( pObject->getObjectType() == HASH_SESSION_OBJ); pSession = (F_Session *)pObject; if( (pObject = pObject->getNextInGlobal()) != NULL) { pObject->AddRef(); } if( RC_OK( pSession->lockSession())) { m_pSessionTable->removeObject( pSession); pSession->signalLockWaiters( FERR_FAILURE, FALSE); pSession->unlockSession(); } pSession->Release(); } Exit: if (bMutexLocked) { f_mutexUnlock( m_hMutex); } return; } /**************************************************************************** Desc: Configures the session manager prior to use ****************************************************************************/ RCODE F_SessionMgr::setupSessionMgr( void) { RCODE rc = FERR_OK; flmAssert( m_hMutex == F_MUTEX_NULL); // Create the mutex if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } // Create the session object table if( (m_pSessionTable = f_new F_HashTable) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = m_pSessionTable->setupHashTable( FALSE, 128, 0))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Gets a session given a session key ****************************************************************************/ RCODE F_SessionMgr::getSession( const char * pszKey, F_Session ** ppSession) { RCODE rc = FERR_OK; F_Session * pSession = NULL; F_HashObject * pObject; FLMBOOL bMutexLocked = FALSE; *ppSession = NULL; f_mutexLock( m_hMutex); bMutexLocked = TRUE; if( RC_BAD( rc = m_pSessionTable->getObject( (void *)pszKey, F_SESSION_KEY_LEN, &pObject, FALSE))) { goto Exit; } // NOTE: getObject() does an addRef for the caller. pSession = (F_Session *)pObject; f_mutexUnlock( m_hMutex); bMutexLocked = FALSE; if( RC_BAD( rc = pSession->lockSession())) { pSession->Release(); goto Exit; } *ppSession = pSession; Exit: if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Unlocks and releases a session being used by a thread ****************************************************************************/ void F_SessionMgr::releaseSession( F_Session ** ppSession) { (*ppSession)->unlockSession(); (*ppSession)->Release(); *ppSession = NULL; } /**************************************************************************** Desc: Creates a new session ****************************************************************************/ RCODE F_SessionMgr::createSession( F_Session ** ppSession) { F_Session * pNewSession = NULL; FLMBOOL bMutexLocked = FALSE; RCODE rc = FERR_OK; if( (pNewSession = f_new F_Session) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pNewSession->setupSession( this))) { goto Exit; } f_mutexLock( m_hMutex); bMutexLocked = TRUE; // Session ID f_sprintf( (char *)&pNewSession->m_ucKey[ 0], "%0*X", (int)(sizeof( FLMUINT) * 2), (unsigned)m_uiNextId++); // Token f_sprintf( (char *)&pNewSession->m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", (int)(sizeof( FLMUINT) * 2), (unsigned)m_uiNextToken++); pNewSession->m_ucKey[ sizeof( pNewSession->m_ucKey) - 1] = 0; // Add the session to the table if( RC_BAD( rc = m_pSessionTable->addObject( pNewSession))) { goto Exit; } f_mutexUnlock( m_hMutex); bMutexLocked = FALSE; if( RC_BAD( rc = pNewSession->lockSession())) { pNewSession->Release(); goto Exit; } *ppSession = pNewSession; pNewSession = NULL; Exit: if( pNewSession) { pNewSession->Release(); } if( bMutexLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: Kills any unused sessions that have been inactive for the specified number of seconds ****************************************************************************/ void F_SessionMgr::timeoutInactiveSessions( FLMUINT uiInactiveSecs, FLMBOOL bWaitForLocks) { F_Session * pSession; F_HashObject * pObject; FLMUINT uiCurrTime; FLMUINT uiElapTime; FLMUINT uiElapSecs; FLMBOOL bMutexLocked = FALSE; f_mutexLock( m_hMutex); bMutexLocked = TRUE; pObject = NULL; if( RC_BAD( m_pSessionTable->getNextObjectInGlobal( &pObject))) { goto Exit; } while( pObject) { flmAssert( pObject->getObjectType() == HASH_SESSION_OBJ); pSession = (F_Session *)pObject; if( (pObject = pObject->getNextInGlobal()) != NULL) { pObject->AddRef(); } if( RC_OK( pSession->lockSession( bWaitForLocks))) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pSession->m_uiLastUsed); uiElapSecs = FLM_TIMER_UNITS_TO_SECS( uiElapTime); if( !uiInactiveSecs || uiElapSecs >= uiInactiveSecs) { m_pSessionTable->removeObject( pSession); pSession->signalLockWaiters( FERR_FAILURE, FALSE); } pSession->unlockSession(); } pSession->Release(); } Exit: if( bMutexLocked) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: Constructor ****************************************************************************/ F_SessionDb::F_SessionDb() { m_hDb = HFDB_NULL; f_memset( m_ucKey, 0, sizeof( m_ucKey)); } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_SessionDb::~F_SessionDb() { if( m_hDb != HFDB_NULL) { FlmDbClose( &m_hDb); } } /**************************************************************************** Desc: Configures a database object prior to being used ****************************************************************************/ RCODE F_SessionDb::setupSessionDb( F_Session * pSession, HFDB hDb) { flmAssert( hDb != HFDB_NULL); flmAssert( m_hDb == HFDB_NULL); m_pSession = pSession; m_hDb = hDb; // Handle f_sprintf( (char *)&m_ucKey[ 0], "%0*X", (int)(sizeof( FLMUINT) * 2), (unsigned)((FLMUINT)hDb)); // Token f_sprintf( (char *)&m_ucKey[ sizeof( FLMUINT) * 2], "%0*X", (int)(sizeof( FLMUINT) * 2), (unsigned)m_pSession->getNextToken()); m_ucKey[ sizeof( m_ucKey) - 1] = 0; return( FERR_OK); } /**************************************************************************** Desc: Returns the key and key length of a database object ****************************************************************************/ const void * FLMAPI F_SessionDb::getKey( void) { return( m_ucKey); } /**************************************************************************** Desc: Returns the key and key length of a database object ****************************************************************************/ FLMUINT FLMAPI F_SessionDb::getKeyLength( void) { return( sizeof( m_ucKey)); } /**************************************************************************** Desc: Deletes (releases) and F_CCS objected referenced in the ITT table. *****************************************************************************/ void flmDeleteCCSRefs( FDICT * pDict) { FLMUINT uiLoop; F_CCS * pCcs = NULL; ITT * pItt; if (pDict && pDict->pIttTbl) { for ( pItt = pDict->pIttTbl, uiLoop = 0; uiLoop < pDict->uiIttCnt; pItt++, uiLoop++) { if (ITT_IS_ENCDEF(pItt)) { pCcs = (F_CCS *)pItt->pvItem; pItt->pvItem = NULL; if (pCcs) { pCcs->Release(); pCcs = NULL; } } } } } /**************************************************************************** Desc: ****************************************************************************/ F_SuperFileClient::F_SuperFileClient() { m_pszCFileName = NULL; m_pszDataFileBaseName = NULL; m_uiExtOffset = 0; m_uiDataExtOffset = 0; m_uiDbVersion = 0; } /**************************************************************************** Desc: ****************************************************************************/ F_SuperFileClient::~F_SuperFileClient() { if( m_pszCFileName) { f_free( &m_pszCFileName); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_SuperFileClient::setup( const char * pszCFileName, const char * pszDataDir, FLMUINT uiDbVersion) { RCODE rc = NE_FLM_OK; FLMUINT uiNameLen; FLMUINT uiDataNameLen; char szDir[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; if( !pszCFileName && *pszCFileName == 0) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } uiNameLen = f_strlen( pszCFileName); if (pszDataDir && *pszDataDir) { if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszCFileName, szDir, szBaseName))) { goto Exit; } f_strcpy( szDir, pszDataDir); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( szDir, szBaseName))) { goto Exit; } uiDataNameLen = f_strlen( szDir); if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1), &m_pszCFileName))) { goto Exit; } f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1); m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1; flmGetDbBasePath( m_pszDataFileBaseName, szDir, &m_uiDataExtOffset); m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset); } else { if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszCFileName))) { goto Exit; } f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1); m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1; flmGetDbBasePath( m_pszDataFileBaseName, m_pszCFileName, &m_uiDataExtOffset); m_uiExtOffset = m_uiDataExtOffset; } m_uiDbVersion = uiDbVersion; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getFileNumber( FLMUINT uiBlockAddr) { return( FSGetFileNumber( uiBlockAddr)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getFileOffset( FLMUINT uiBlockAddr) { return( FSGetFileOffset( uiBlockAddr)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getBlockAddress( FLMUINT uiFileNumber, FLMUINT uiFileOffset) { return( FSBlkAddress( uiFileNumber, uiFileOffset)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT64 FLMAPI F_SuperFileClient::getMaxFileSize( void) { if( m_uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { return( gv_FlmSysData.uiMaxFileSize); } return( MAX_FILE_SIZE_VER40); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_SuperFileClient::getFilePath( FLMUINT uiFileNumber, char * pszPath) { RCODE rc = NE_FLM_OK; FLMUINT uiExtOffset; if (!uiFileNumber) { f_strcpy( pszPath, m_pszCFileName); goto Exit; } if ((m_uiDbVersion >= FLM_FILE_FORMAT_VER_4_3 && uiFileNumber <= MAX_DATA_FILE_NUM_VER43) || uiFileNumber <= MAX_DATA_FILE_NUM_VER40) { f_memcpy( pszPath, m_pszDataFileBaseName, m_uiDataExtOffset); uiExtOffset = m_uiDataExtOffset; } else { f_memcpy( pszPath, m_pszCFileName, m_uiExtOffset); uiExtOffset = m_uiExtOffset; } // Modify the file's extension. bldSuperFileExtension( m_uiDbVersion, uiFileNumber, &pszPath[ uiExtOffset]); Exit: return( rc); } /**************************************************************************** Desc: Generates a file name given a super file number. Adds ".xx" to pFileExtension. Use lower case characters. Notes: This is a base 24 alphanumeric value where { a, b, c, d, e, f, i, l, o, r, u, v } values are removed. ****************************************************************************/ void F_SuperFileClient::bldSuperFileExtension( FLMUINT uiDbVersion, FLMUINT uiFileNum, char * pszFileExtension) { FLMBYTE ucLetter; flmAssert( uiDbVersion); if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1536) { // No additional letter - File numbers 1 to 511 // This is just like pre-4.3 numbering. ucLetter = 0; } else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1024) { // File numbers 512 to 1023 ucLetter = 'r'; } else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 512) { // File numbers 1024 to 1535 ucLetter = 's'; } else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43) { // File numbers 1536 to 2047 ucLetter = 't'; } else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1536) { // File numbers 2048 to 2559 ucLetter = 'v'; } else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1024) { // File numbers 2560 to 3071 ucLetter = 'w'; } else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 512) { // File numbers 3072 to 3583 ucLetter = 'x'; } else { flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER43); // File numbers 3584 to 4095 ucLetter = 'z'; } } else { if (uiFileNum <= MAX_DATA_FILE_NUM_VER40) { // No additional letter - File numbers 1 to 511 // This is just like pre-4.3 numbering. ucLetter = 0; } else { flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER40); // File numbers 512 to 1023 ucLetter = 'x'; } } *pszFileExtension++ = '.'; *pszFileExtension++ = f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) / 24)); *pszFileExtension++ = f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) % 24)); *pszFileExtension++ = ucLetter; *pszFileExtension = 0; } libflaim-4.9.966/src/fssplblk.cpp0000644000175000017500000005324310510774540020207 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: B-tree block splitting. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fssplblk.cpp 12289 2006-01-19 14:56:21 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FSBtResetStack( FDB * pDb, LFILE * pLFile, BTSK ** ppStack, FLMBYTE * pElement, FLMUINT uiElmLen); FSTATIC RCODE FSMoveToNextBlk( FDB * pDb, LFILE * pLFile, BTSK ** ppStack, FLMUINT nextBlkNum, FLMBYTE * pElement, FLMUINT elmLen, FLMUINT uiBlockSize, FLMUINT * blkNumRV, FLMUINT * curElmRV); /**************************************************************************** Desc: Split a block using different algorithms depending on context ****************************************************************************/ RCODE FSBlkSplit( FDB * pDb, LFILE * pLFile, BTSK ** ppStack, FLMBYTE * pElement, FLMUINT elmLen) { RCODE rc = FERR_OK; FLMUINT uiBlockSize = pDb->pFile->FileHdr.uiBlockSize; BTSK * pStack = *ppStack; FLMUINT oldCurElm = pStack->uiCurElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMUINT tempWord; FLMUINT elmKeyLen; FLMUINT prevKeyCnt; FLMUINT curElm; FLMBYTE * curElmPtr; FLMBYTE * pBlk; FLMUINT blkNum = pStack->uiBlkAddr; FLMUINT uiBlkEnd; BTSK newBlkStk; BTSK nextBlkStk; FLMBYTE * newBlkPtr; FLMBYTE * nextBlkPtr; FLMUINT newBlkNum; FLMUINT nextBlkNum; FLMUINT blkNumRestore = 0; FLMUINT curElmRestore = 0; FLMBOOL bNewRootFlag; FLMBOOL bDoubleSplit; DB_STATS * pDbStats; bNewRootFlag = FALSE; bDoubleSplit = FALSE; FSInitStackCache( &newBlkStk, 1); FSInitStackCache( &nextBlkStk, 1); if ((pDbStats = pDb->pDbStats) != NULL) { LFILE_STATS* pLFileStats; if ((pLFileStats = fdbGetLFileStatPtr( pDb, pLFile)) != NULL) { pLFileStats->bHaveStats = pDbStats->bHaveStats = TRUE; pLFileStats->ui64BlockSplits++; } } // If there is room to move data to the next block then do it // and update the parent block with the new last element. Otherwise... // Divide data from current block and next block into the new block // (2/3 split). Delete parent element and update parent /w 2 elements. if (pStack->uiFlags & NO_STACK) { if (RC_BAD( rc = FSBtResetStack( pDb, pLFile, &pStack, pElement, elmLen))) { goto Exit; } } // Log the block before modifying it if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } pBlk = pStack->pBlk; bNewRootFlag = BH_IS_ROOT_BLK( pBlk); if ((nextBlkNum = FB2UD( &pBlk[BH_NEXT_BLK])) != BT_END) { // Try to move the elements from the current block to the next block // while inserting the element. If this succeeds than we are better // off than spliting blocks. If succeeds then all block operations // have been taken care of. if (RC_OK( rc = FSMoveToNextBlk( pDb, pLFile, &pStack, nextBlkNum, pElement, elmLen, uiBlockSize, &blkNumRestore, &curElmRestore))) { goto FSBlkSplit_position; } if (rc != FERR_BLOCK_FULL) { goto Exit; } } // Initialize variables, create a new block and setup header. This is // common stuff that will be done if you are splitting a RIGHT-MOST // block or a middle block. Remember we could be working with leaf or // non-leaf blks. if (RC_BAD( rc = ScaCreateBlock( pDb, pLFile, &newBlkStk.pSCache))) { goto Exit; } newBlkStk.pBlk = newBlkStk.pSCache->pucBlk; newBlkPtr = GET_CABLKPTR( &newBlkStk); newBlkNum = GET_BH_ADDR( newBlkPtr); pBlk = pStack->pBlk; uiBlkEnd = pStack->uiBlkEnd; UD2FBA( nextBlkNum, &newBlkPtr[BH_NEXT_BLK]); UD2FBA( blkNum, &newBlkPtr[BH_PREV_BLK]); UD2FBA( newBlkNum, &pBlk[BH_NEXT_BLK]); // Write over the root bit if present as set type. newBlkPtr[BH_TYPE] = pBlk[BH_TYPE] = (FLMBYTE) (BH_GET_TYPE( pBlk)); newBlkPtr[BH_LEVEL] = pBlk[BH_LEVEL]; tempWord = FB2UW( &pBlk[BH_LOG_FILE_NUM]); UW2FBA( tempWord, &newBlkPtr[BH_LOG_FILE_NUM]); UW2FBA( BH_OVHD, &newBlkPtr[BH_BLK_END]); // In both cases (middle or end split) if you split at the wrong place // you will still not be able to fit the element[] in the block. The // while loop will insure at least a split into two blocks where there // may not be room to move any elements from the next block. pStack->uiCurElm = (nextBlkNum == BT_END) ? (FFILE_MAX_FILL * pDb->pFile->FileHdr.uiBlockSize / 100) : ((uiBlockSize / 20) * 13); // Leave at least 65% full if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } if (pStack->uiCmpStatus == BT_GT_KEY) { // Save the key in the pKeyBuf curElmPtr = &pBlk[pStack->uiCurElm]; if (pStack->uiBlkType == BHT_NON_LEAF_DATA) { flmCopyDrnKey( pStack->pKeyBuf, curElmPtr); } else { elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr)); if (elmKeyLen) { // Copy key into pKeyBuf prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr)); f_memcpy( &pStack->pKeyBuf[prevKeyCnt], &curElmPtr[uiElmOvhd], elmKeyLen); } } } curElm = pStack->uiCurElm; // Check to see if the new element will fit whereever it goes. Don't // try to optimally place it because the next block may move stuff // over. while ((curElm > oldCurElm) && (curElm + elmLen + uiElmOvhd + uiElmOvhd > uiBlockSize)) { FSBtPrevElm( pDb, pLFile, pStack); curElm = pStack->uiCurElm; } newBlkStk.uiBlkAddr = newBlkNum; newBlkStk.pKeyBuf = pStack->pKeyBuf; FSBlkToStack( &newBlkStk); newBlkStk.uiKeyBufSize = pStack->uiKeyBufSize; curElmPtr = &pBlk[curElm]; if (curElm == oldCurElm) { // Decide whether to place the new element in the current or new // block. Give preference to placing with the current block. if ((curElm + elmLen + uiElmOvhd < uiBlockSize) && ((uiBlkEnd - curElm) > uiElmOvhd)) { // Place with the current block goto Addto_Current_Blk; } if (uiBlkEnd > curElm) { // Move if not at end of block FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm), pStack->pKeyBuf); } // Set the block end in the current blocks header & restore values pStack->uiBlkEnd = curElm; UW2FBA( curElm, &pBlk[BH_BLK_END]); blkNumRestore = newBlkNum; // Move if not at end of block curElmRestore = BH_OVHD; newBlkStk.uiCurElm = curElmRestore; if (newBlkStk.uiBlkEnd + elmLen + uiElmOvhd > uiBlockSize) { // Double split - move element later bDoubleSplit = 1; } else { FSBlkMoveElms( &newBlkStk, pElement, elmLen, NULL); } } else if (curElm > oldCurElm) { Addto_Current_Blk: // First move stuff over to the new blk FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm), pStack->pKeyBuf); // Set the block end in the current blocks header pStack->uiBlkEnd = curElm; UW2FBA( curElm, &pBlk[BH_BLK_END]); blkNumRestore = blkNum; curElmRestore = oldCurElm; // Setup to insert element pStack->uiCurElm = curElmRestore; FSBlkMoveElms( pStack, pElement, elmLen, NULL); } else if (curElm < oldCurElm) { blkNumRestore = newBlkNum; FSBlkMoveElms( &newBlkStk, curElmPtr, (FLMUINT) (oldCurElm - curElm), pStack->pKeyBuf); newBlkStk.uiCurElm = newBlkStk.uiBlkEnd; curElmRestore = newBlkStk.uiCurElm; // May not fit with 1K blocks - check for double split if (curElmRestore + elmLen + (uiBlkEnd - oldCurElm) + uiElmOvhd > uiBlockSize) { bDoubleSplit = 1; } else { FSBlkMoveElms( &newBlkStk, pElement, elmLen, NULL); } newBlkStk.uiCurElm = newBlkStk.uiBlkEnd; pStack->uiCurElm = oldCurElm; if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } if (oldCurElm < uiBlkEnd) { FSBlkMoveElms( &newBlkStk, &pBlk[oldCurElm], (FLMUINT) (uiBlkEnd - oldCurElm), pStack->pKeyBuf); } // Set the block end in the current blocks header pStack->uiBlkEnd = curElm; UW2FBA( curElm, &pBlk[BH_BLK_END]); } // All done with the current block - unpin and set to dirty. // // All done moving data from current block to new block. If created new // right most block check if new to create new root block and init new // root (easy) else try to move stuff from the next block into the new // block. if (nextBlkNum == BT_END) { FLMUINT uiLfNum = pLFile->uiLfNum; // We are done with block FSReleaseBlock( &newBlkStk, FALSE); // At the root? if (bNewRootFlag) { FLMBYTE byType; // Create a new root block if (pStack->uiLevel + 1 >= BH_MAX_LEVELS) { rc = RC_SET( FERR_BTREE_FULL); goto Exit; } // Set the block type if (pLFile->uiLfType == LF_INDEX) { if (pLFile->pIxd->uiFlags & IXD_POSITIONING) { byType = BHT_NON_LEAF_COUNTS + BHT_ROOT_BLK; } else { byType = BHT_NON_LEAF + BHT_ROOT_BLK; } } else { byType = BHT_NON_LEAF_DATA + BHT_ROOT_BLK; } // Move all pStack elements down by doing a shift shiftN( (FLMBYTE*) pStack, (FLMUINT) (sizeof(BTSK) * (pStack->uiLevel + 1)), (FLMINT) sizeof(BTSK)); // Create a new block if (RC_BAD( rc = ScaCreateBlock( pDb, pLFile, &pStack->pSCache))) { goto Exit; } pBlk = pStack->pBlk = pStack->pSCache->pucBlk; // Set prev/next block addresses to BT_END UD2FBA( BT_END, &pBlk[BH_PREV_BLK]); UD2FBA( BT_END, &pBlk[BH_NEXT_BLK]); UW2FBA( BH_OVHD, &pBlk[BH_BLK_END]); // Set logical file number in the block header and block type UW2FBA( uiLfNum, &pBlk[BH_LOG_FILE_NUM]); pBlk[BH_TYPE] = byType; pBlk[BH_LEVEL] = (FLMBYTE) (++(pStack->uiLevel)); pStack->uiBlkAddr = GET_BH_ADDR( pBlk); FSBlkToStack( pStack); pLFile->uiRootBlk = pStack->uiBlkAddr; // Always update the pLFile because level is incremented rc = flmLFileWrite( pDb, pLFile); pStack++; *ppStack = pStack; if (RC_BAD( rc)) { goto Exit; } } } else { FLMINT iBytesToMove; // Move stuff from the right block // Remember that newBlk is still pinned nextBlkStk.pKeyBuf = pStack->pKeyBuf; if (RC_BAD( rc = FSGetBlock( pDb, pLFile, nextBlkNum, &nextBlkStk))) { goto Exit; } if (RC_BAD( rc = FSLogPhysBlk( pDb, &nextBlkStk))) { goto Exit; } nextBlkStk.uiKeyBufSize = pStack->uiKeyBufSize; nextBlkPtr = nextBlkStk.pBlk; UD2FBA( newBlkNum, &nextBlkPtr[BH_PREV_BLK]); // Try to move so that the two blocks have about the same free space iBytesToMove = (FLMINT) ((nextBlkStk.uiBlkEnd - newBlkStk.uiBlkEnd) / 2); if (iBytesToMove > 100) { // Log the block before modifying it. nextBlkStk.uiCurElm = iBytesToMove + BH_OVHD; if (RC_BAD( rc = FSBtScanTo( &nextBlkStk, NULL, 0, 0))) { goto Exit; } while ((nextBlkStk.uiCurElm > BH_OVHD) && (nextBlkStk.uiCurElm + newBlkStk.uiBlkEnd + uiElmOvhd - BH_OVHD >= uiBlockSize)) { (void) FSBtPrevElm( pDb, pLFile, &nextBlkStk); } // Never try to move elements from the next block to the new // block if we are positioned on the first element. Never try // to move if on LEM or at the end if ((nextBlkStk.uiCurElm > BH_OVHD) && (nextBlkStk.uiCurElm + uiElmOvhd < nextBlkStk.uiBlkEnd)) { FLMUINT tempEnd; // Save the key in the pKeyBuf curElmPtr = &nextBlkPtr[nextBlkStk.uiCurElm]; if (pStack->uiBlkType != BHT_NON_LEAF_DATA) { elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr)); if (elmKeyLen) { // Copy key into pKeyBuf prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr)); f_memcpy( &(nextBlkStk.pKeyBuf)[prevKeyCnt], &curElmPtr[uiElmOvhd], elmKeyLen); } } tempWord = nextBlkStk.uiCurElm; newBlkStk.uiCurElm = newBlkStk.uiBlkEnd; FSBlkMoveElms( &newBlkStk, &nextBlkPtr[BH_OVHD], (FLMUINT) (tempWord - BH_OVHD), NULL); // Move the elements in the next block DOWN expanding PKC tempEnd = nextBlkStk.uiBlkEnd; // Make sure uiBlkEnd is reality or move will not work! nextBlkStk.uiBlkEnd = nextBlkStk.uiCurElm = BH_OVHD; UW2FBA( BH_OVHD, &nextBlkPtr[BH_BLK_END]); FSBlkMoveElms( &nextBlkStk, &nextBlkPtr[tempWord], (FLMUINT) (tempEnd - tempWord), nextBlkStk.pKeyBuf); if ((pStack - 1)->uiBlkType == BHT_NON_LEAF_COUNTS) { if (RC_BAD( rc = FSUpdateAdjacentBlkCounts( pDb, pLFile, pStack, &nextBlkStk))) { goto Exit; } } } } FSReleaseBlock( &newBlkStk, FALSE); } // Insert the new last element in the current block replacing what is // there. Insert the last element from the new block. All blocks should // be dirty and unpined! This means we have to read them in again. if (RC_BAD( rc = FSGetBlock( pDb, pLFile, blkNum, pStack))) { goto Exit; } if (pStack->uiCurElm >= pStack->uiBlkEnd) { pStack->uiCurElm = curElm; } // Passing 0 means insert only. if (RC_BAD( rc = FSNewLastBlkElm( pDb, pLFile, &pStack, (nextBlkNum == BT_END) ? 0 : FSNLBE_LESS))) { *ppStack = pStack; goto Exit; } if (RC_BAD( rc = FSAdjustStack( pDb, pLFile, pStack, TRUE))) { if (rc != FERR_BT_END_OF_DATA) { goto Exit; } } // Parent is positioned to the nextBlk element. if (RC_BAD( rc = FSGetBlock( pDb, pLFile, newBlkNum, pStack))) { goto Exit; } if ((nextBlkNum == BT_END) && !bNewRootFlag) { FLMUINT uiNewRefCount; FLMUINT uiOldRefCount; FLMBYTE* pTmpElement; // Only update the counts if the inserting a key and not replacing. if ((pStack - 1)->uiBlkType == BHT_NON_LEAF_COUNTS) { if (RC_BAD( rc = FSBlockCounts( pStack, BH_OVHD, pStack->uiBlkEnd, NULL, NULL, &uiNewRefCount))) { goto Exit; } } pStack--; // Modify the parent last element marker (LEM) to point to the new // last block. THEN delete the previous element that also pointer to // the new last block. // // Read the parent block if (RC_BAD( rc = FSGetBlock( pDb, pLFile, pStack->uiBlkAddr, pStack))) { return (rc); } if (RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { return (rc); } // Change where element points to in the last element marker (LEM) pTmpElement = pStack->pBlk + pStack->uiCurElm; FSSetChildBlkAddr( pTmpElement, newBlkNum, pStack->uiElmOvhd); if (pStack->uiBlkType == BHT_NON_LEAF_COUNTS) { uiOldRefCount = FB2UD( &pTmpElement[BNE_CHILD_COUNT]); if (RC_BAD( rc = FSChangeBlkCounts( pDb, pStack, (FLMINT) (uiNewRefCount - uiOldRefCount)))) { goto Exit; } UD2FBA( uiNewRefCount, &pTmpElement[BNE_CHILD_COUNT]); } pStack++; } else { // Inserts the new element into the tree. rc = FSNewLastBlkElm( pDb, pLFile, &pStack, 0); } FSBlkSplit_position: *ppStack = pStack; // Read in block - should be positioned to the current element inserted if (RC_OK( rc)) { // Parent element is on the newBlock - see if you need to back up if (blkNumRestore == blkNum) { if (RC_BAD( rc = FSAdjustStack( pDb, pLFile, pStack, FALSE))) { if (rc != FERR_BT_END_OF_DATA) { goto Exit; } } } if (RC_OK( rc = FSGetBlock( pDb, pLFile, blkNumRestore, pStack))) { // Set up the key buffer (pKeyBuf) to be correct for future // inserts pStack->uiCurElm = curElmRestore; if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } if (bDoubleSplit) { // Now insert the element if flag set This will cause another // split. RECURSIVE CALL rc = FSBlkSplit( pDb, pLFile, ppStack, pElement, elmLen); } } } Exit: FSReleaseBlock( &newBlkStk, FALSE); FSReleaseBlock( &nextBlkStk, FALSE); return (rc); } /**************************************************************************** Desc: Reset the pStack to set up for a block split ****************************************************************************/ FSTATIC RCODE FSBtResetStack( FDB * pDb, // Pointer to database DBC structure. LFILE * pLFile, // Logical file definition BTSK ** ppStack, // Stack of variables for each level FLMBYTE * pElement, // The input element to insert FLMUINT elmLen) // Length of the element { RCODE rc; BTSK * pStack = *ppStack; // Stack holding all state info FLMUINT oldPKC = pStack->uiPKC; // Save old PKC value FLMUINT oldPvElmPKC = pStack->uiPrevElmPKC; // Save old prev elm PKC value FLMUINT oldBlock = pStack->uiBlkAddr; // Save old block number FLMUINT oldCurElm = pStack->uiCurElm; // Save old current element value FLMUINT uiElmOvhd = pStack->uiElmOvhd; if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, &pElement[uiElmOvhd], (elmLen - uiElmOvhd), 0))) { return (rc); } // In case of continuation elements, parse to matching curElm while ((oldBlock != pStack->uiBlkAddr) && (oldCurElm != pStack->uiCurElm)) { if ((rc = FSBtNextElm( pDb, pLFile, pStack)) == FERR_BT_END_OF_DATA) { return (RC_SET( FERR_BTREE_ERROR)); } else if (RC_BAD( rc)) { return (rc); } } // Reset original PKC values pStack->uiPKC = oldPKC; pStack->uiPrevElmPKC = oldPvElmPKC; *ppStack = pStack; pStack->uiFlags = FULL_STACK; return (FERR_OK); } /**************************************************************************** Desc: Try to move elements between two blocks while inserting an element ****************************************************************************/ FSTATIC RCODE FSMoveToNextBlk( FDB * pDb, LFILE * pLFile, BTSK ** ppStack, FLMUINT nextBlkNum, FLMBYTE * pElement, FLMUINT elmLen, FLMUINT uiBlockSize, FLMUINT * blkNumRV, FLMUINT * curElmRV) { RCODE rc = FERR_OK; BTSK * pStack = *ppStack; FLMUINT uiBlkEnd = pStack->uiBlkEnd; FLMUINT oldCurElm = pStack->uiCurElm; FLMBYTE * curElmPtr; BTSK nextBlkStk; FLMUINT nextBlkFreeBytes; FLMUINT elmKeyLen; FLMUINT prevKeyCnt; FLMUINT curElm; FLMUINT uiElmOvhd = pStack->uiElmOvhd; FLMBOOL bInsertInCurrentBlock; FLMBYTE * pBlk = pStack->pBlk; FSInitStackCache( &nextBlkStk, 1); nextBlkStk.pKeyBuf = pStack->pKeyBuf; if (RC_BAD( rc = FSGetBlock( pDb, pLFile, nextBlkNum, &nextBlkStk))) { goto Exit; } nextBlkFreeBytes = (FLMUINT) (uiBlockSize - nextBlkStk.uiBlkEnd - uiElmOvhd); pStack->uiCurElm = (uiBlkEnd - (nextBlkFreeBytes / 2)); if (RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) { goto Exit; } rc = (pStack->uiCmpStatus == BT_END_OF_DATA) ? FERR_BT_END_OF_DATA : FERR_OK; for (; RC_OK( rc); rc = FSBlkNextElm( pStack)) { // The current element is positioned to mininum split point. Keep // testing till at end of block or split will fit within both blocks // while still adding the element to insert. // // Save the key in the pKeyBuf so can move entire element curElmPtr = CURRENT_ELM( pStack); if (pStack->uiBlkType == BHT_NON_LEAF_DATA) { prevKeyCnt = elmKeyLen = 0; } else { prevKeyCnt = (FLMUINT) (BBE_GET_PKC( curElmPtr)); elmKeyLen = (FLMUINT) (BBE_GET_KL( curElmPtr)); } if (elmKeyLen) { // Copy key into pKeyBuf f_memcpy( &pStack->pKeyBuf[prevKeyCnt], &curElmPtr[uiElmOvhd], elmKeyLen); } // Will element be inserted into the current block? if (oldCurElm <= (curElm = pStack->uiCurElm)) { // Insert the element in the current block - could be at the end if (curElm + elmLen + uiElmOvhd > uiBlockSize) { rc = FERR_BT_END_OF_DATA; break; } // Left fits - try right block - could fail because of pkc value/ if (prevKeyCnt + (uiBlkEnd - curElm) >= nextBlkFreeBytes) { // Doesn't fit - try again continue; } bInsertInCurrentBlock = TRUE; } else { // Moving elements from current block so no need to check it // Cannot remember why I put BBE_PKC_MAX in the line below if (elmLen + prevKeyCnt + BBE_PKC_MAX + (uiBlkEnd - curElm) >= nextBlkFreeBytes) { continue; } bInsertInCurrentBlock = FALSE; } if (RC_BAD( rc = FSLogPhysBlk( pDb, &nextBlkStk))) { goto Exit; } if (bInsertInCurrentBlock) { FSBlkMoveElms( &nextBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm), pStack->pKeyBuf); pStack->uiBlkEnd = curElm; UW2FBA( curElm, &pBlk[BH_BLK_END]); *curElmRV = oldCurElm; pStack->uiCurElm = (*curElmRV); FSBlkMoveElms( pStack, pElement, elmLen, NULL); *blkNumRV = pStack->uiBlkAddr; } else { FSBlkMoveElms( &nextBlkStk, curElmPtr, (FLMUINT) (uiBlkEnd - curElm), pStack->pKeyBuf); pStack->uiBlkEnd = curElm; UW2FBA( curElm, &pBlk[BH_BLK_END]); *curElmRV = (FLMUINT) (BH_OVHD + prevKeyCnt + (oldCurElm - curElm)); nextBlkStk.uiCurElm = (*curElmRV); FSBlkMoveElms( &nextBlkStk, pElement, elmLen, NULL); *blkNumRV = nextBlkNum; } if (pLFile->pIxd && (pLFile->pIxd->uiFlags & IXD_POSITIONING)) { if (RC_BAD( rc = FSUpdateAdjacentBlkCounts( pDb, pLFile, pStack, &nextBlkStk))) { goto Exit; } } break; } // If cannot move then return if (RC_BAD( rc)) { if (rc == FERR_BT_END_OF_DATA) { // Restore old current element pStack->uiCurElm = oldCurElm; rc = RC_SET( FERR_BLOCK_FULL); } goto Exit; } // Fix up the elements in the parent block pointing to the new last // element in the current block. REMEMBER - pStack may change on you! rc = FSNewLastBlkElm( pDb, pLFile, ppStack, FSNLBE_LESS | FSNLBE_POSITION); Exit: FSReleaseBlock( &nextBlkStk, FALSE); return (rc); } libflaim-4.9.966/src/kycollat.cpp0000644000175000017500000051014010510774540020203 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: FLAIM collation routines and tables // Tabs: 3 // // Copyright (c) 1990-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kycollat.cpp 12313 2006-01-19 15:14:44 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" // Collating sequence defines #define COLLS 32 // first collating number (space/end of line) #define COLS1 (COLLS+9) // quotes #define COLS2 (COLS1+5) // parens #define COLS3 (COLS2+6) // money #define COLS4 (COLS3+6) // math ops #define COLS5 (COLS4+8) // math others #define COLS6 (COLS5+14) // others: %#&@\_|~ #define COLS7 (COLS6+13) // greek #define COLS8 (COLS7+25) // numbers #define COLS9 (COLS8+10) // alphabet #define COLS10 (COLS9+60) // cyrillic #define COLS10h (COLS9+42) // hebrew - writes over european and cyrilic #define COLS10a (COLS10h+28) // arabic - inclusive from 198(C6)- 252(FC) #define COLS11 253 // End of list - arabic goes to the end #define COLS0_ARABIC COLS11 // Set if arabic accent marking #define COLS0_HEBREW COLS11 // Set if hebrew accent marking #define COLSOEM 254 // OEM character in upper range - non-collatable #define COLS0_UNICODE 254 // Use this for UNICODE #define COLS0 255 // graphics/misc - chars without a collate value // State table information for double character sorting #define STATE1 1 #define STATE2 2 #define STATE3 3 #define STATE4 4 #define STATE5 5 #define STATE6 6 #define STATE7 7 #define STATE8 8 #define STATE9 9 #define STATE10 10 #define STATE11 11 #define AFTERC 12 #define AFTERH 13 #define AFTERL 14 #define INSTAE 15 #define INSTOE 16 #define INSTSG 17 #define INSTIJ 18 #define WITHAA 19 #define START_COL 12 #define START_ALL (START_COL + 1) // all US and European #define START_DK (START_COL + 2) // Danish #define START_IS (START_COL + 3) // Icelandic #define START_NO (START_COL + 4) // Norwegian #define START_SU (START_COL + 5) // Finnish #define START_SV (START_COL + 5) // Swedish #define START_YK (START_COL + 6) // Ukrain #define START_TK (START_COL + 7) // Turkish #define START_CZ (START_COL + 8) // Czech #define START_SL (START_COL + 8) // Slovak #define FIXUP_AREA_SIZE 24 #define COMPARE_COLLATION 1 #define COMPARE_COL_AND_SUBCOL 2 #define COMPARE_VALUE 3 #define NULL_SUB_COL_CHECK NULL #define NULL_CASE_CHECK NULL #define NULL_WILD_CARD_CHECK NULL #define MAX_SUBCOL_BUF 500 // (((MAX_KEY_SIZ / 4) * 3 + fluff #define MAX_LOWUP_BUF 150 // ((MAX_KEY_SIZ - (MAX_KEY_SIZ / 8)) / 8) * 2 #define SET_CASE_BIT 0x01 #define SET_KATAKANA_BIT 0x01 #define SET_WIDTH_BIT 0x02 #define COLS_ASIAN_MARK_VAL 0x40 #define COLS_ASIAN_MARKS 0x140 FSTATIC RCODE KYCmpKeyElmBld( FDB * pDb, IXD * pIxd, FLMUINT uiContainerNum, IFD * pIfd, FLMUINT uiAction, FLMUINT uiDrn, FLMBOOL * pbHadUniqueKeys, FLMUINT uiCdlEntry, FLMUINT uiCompoundPos, FLMBYTE * pKeyBuf, FLMUINT uiKeyLen, FLMBYTE * pLowUpBuf, FLMUINT uiLuLen, FlmRecord * pRecord, FLD_CONTEXT * pFldContext); FSTATIC RCODE KYFormatText( const FLMBYTE * psVal, FLMUINT uiSrcLen, FLMBOOL bMinSpaces, FLMBOOL bNoUnderscore, FLMBOOL bNoSpace, FLMBOOL bNoDash, FLMBOOL bEscChar, FLMBOOL bInputTruncated, FLMBYTE * psDestBuf, FLMUINT * puiDestLen); FSTATIC RCODE AsiaFlmTextToColStr( const FLMBYTE * Str, FLMUINT uiStrLen, FLMBYTE * ColStr, FLMUINT * puiColStrLenRV, FLMUINT uiUppercaseFlag, FLMUINT * puiCollationLen, FLMUINT * puiCaseLenRV, FLMUINT uiCharLimit, FLMBOOL bFirstSubstring, FLMBOOL * pbDataTruncated); FSTATIC FLMUINT16 flmTextGetSubCol( FLMUINT16 ui16WPValue, FLMUINT16 ui16ColValue, FLMUINT uiLangId); FSTATIC FLMINT flmTextCompareSingleChar( FLMBYTE ** ppLeftText, FLMUINT * puiLeftLen, FLMUINT * puiLeftWpChar2, FLMBYTE ** ppRightText, FLMUINT * puiRightLen, FLMUINT * puiRightWpChar2, FLMINT * piSubColCompare, FLMINT * piCaseCompare, FLMBOOL * pbHitWildCard, FLMINT iCompareType, FLMUINT16 * pui16ColVal, FLMUINT uiFlags, FLMUINT uiLangId); FSTATIC FLMUINT FWWSGetColStr( FLMBYTE * fColStr, FLMUINT * fcStrLenRV, FLMBYTE * wordStr, FLMUINT fWPLang, FLMBOOL * pbDataTruncated, FLMBOOL * pbFirstSubstring); FSTATIC FLMUINT FWWSCmbSubColBuf( FLMBYTE * wordStr, FLMUINT * wdStrLenRV, FLMBYTE * subColBuf, FLMBOOL hebrewArabicFlag); FSTATIC FLMUINT AsiaParseCase( FLMBYTE * WordStr, FLMUINT * uiWordStrLenRV, FLMBYTE * pCaseBits); FSTATIC RCODE FTextToColStr( const FLMBYTE * pucStr, FLMUINT uiStrLen, FLMBYTE * pucCollatedStr, FLMUINT * puiCollatedStrLen, FLMUINT uiUppercaseFlag, FLMUINT * puiCollationLen, FLMUINT * puiCaseLen, FLMUINT uiLanguage, FLMUINT uiCharLimit, FLMBOOL bFirstSubstring, FLMBOOL * pbOriginalCharsLost, FLMBOOL * pbDataTruncated); FSTATIC FLMUINT16 flmAsiaGetCollation( FLMUINT16 ui16WpChar, FLMUINT16 ui16NextWpChar, FLMUINT16 ui16PrevColValue, FLMUINT16 * pui16ColValue, FLMUINT16 * pui16SubColVal, FLMBYTE * pucCaseBits, FLMUINT16 uiUppercaseFlag); FSTATIC FLMUINT AsiaParseSubCol( FLMBYTE * WordStr, FLMUINT * puiWordStrLen, FLMBYTE * SubColBuf); FSTATIC FLMUINT FColStrToText( FLMBYTE * fColStr, FLMUINT * fcStrLenRV, FLMBYTE * textStr, FLMUINT fWPLang, FLMBYTE * postBuf, FLMUINT * postBytesRV, FLMBOOL * pbDataTruncated, FLMBOOL * pbFirstSubstring); /**************************************************************************** Desc: ****************************************************************************/ typedef struct BYTE_WORD_TBL { FLMBYTE ByteValue; FLMUINT16 WordValue; } BYTE_WORD_TBL; /**************************************************************************** Desc: ****************************************************************************/ #define BYTES_IN_BITS( bits) \ ((bits + 7) >> 3) /**************************************************************************** Desc: ****************************************************************************/ #define TEST1BIT( buf, bPos) \ ((((buf)[ (bPos) >> 3]) >> (7 - ((bPos) & 7))) & 1) /**************************************************************************** Desc: ****************************************************************************/ #define GET1BIT( buf, bPos) \ ((((buf)[ (bPos) >> 3]) >> (7 - ((bPos) & 7))) & 1) /**************************************************************************** Desc: ****************************************************************************/ #define GETnBITS( n, bf, bit) \ (((unsigned int)( \ ((unsigned char)bf[ (bit) >> 3] << 8) \ | \ (unsigned char)bf[ ((bit) >> 3) + 1] \ ) >> (16 - (n) - ((bit) & 7)) \ ) & ((1 << (n)) - 1) \ ) /**************************************************************************** Desc: ****************************************************************************/ #define SET_BIT( buf, bPos) \ ((buf)[(bPos) >> 3] |= (FLMBYTE)((1 << (7 - ((bPos) & 7))))) /**************************************************************************** Desc: ****************************************************************************/ #define RESET_BIT( buf, bPos) \ ((buf)[(bPos) >> 3] &= (FLMBYTE)(~(1 << (7 - ((bPos) & 7))))) /**************************************************************************** Desc: ****************************************************************************/ #define SETnBITS( n, bf, bit, v) \ { (bf)[ (bit) >> 3] |= \ (FLMBYTE)(((v) << (8 - (n))) \ >> \ ((bit) & 7)); \ (bf)[ ((bit) >> 3) + 1] = \ (FLMBYTE)((v) \ << \ (16 - (n) - ((bit) & 7))); \ } /**************************************************************************** Desc: Map special chars in CharSet (x24) to collation values ****************************************************************************/ BYTE_WORD_TBL flmCh24ColTbl[] = { {1, COLLS + 2}, // comma {2, COLLS + 1}, // maru {5, COLS_ASIAN_MARKS + 2}, // chuuten {10, COLS_ASIAN_MARKS}, // dakuten {11, COLS_ASIAN_MARKS + 1}, // handakuten {43, COLS2 + 2}, // angled brackets {44, COLS2 + 3}, {49, COLS2 + 2}, // pointy brackets {50, COLS2 + 3}, {51, COLS2 + 2}, // double pointy brackets {52, COLS2 + 3}, {53, COLS1}, // Japanese quotes {54, COLS1}, {55, COLS1}, // hollow Japanese quotes {56, COLS1}, {57, COLS2 + 2}, // filled rounded brackets {58, COLS2 + 3} }; /**************************************************************************** Desc: ****************************************************************************/ FLMUINT16 colToWPChr[ COLS11 - COLLS] = { 0x20, // colls - 0x2e, // colls+1 - . 0x2c, // colls+2 - , 0x3a, // colls+3 - : 0x3b, // colls+4 - ; 0x21, // colls+5 - ! 0, // colls+6 - NO VALUE 0x3f, // colls+7 - ? 0, // colls+8 - NO VALUE 0x22, // cols1 - " 0x27, // cols1+1 - ' 0x60, // cols1+2 - ` 0, // cols1+3 - NO VALUE 0, // cols1+4 - NO VALUE 0x28, // cols2 - ( 0x29, // cols2+1 - ) 0x5b, // cols2+2 - japanese angle brackets 0x5d, // cols2+3 - japanese angle brackets 0x7b, // cols2+4 - { 0x7d, // cols2+5 - } 0x24, // cols3 - $ 0x413, // cols3+1 - cent 0x40b, // cols3+2 - pound 0x40c, // cols3+3 - yen 0x40d, // cols3+4 - pacetes 0x40e, // cols3+5 - floren 0x2b, // cols4 - + 0x2d, // cols4+1 - - 0x2a, // cols4+2 - * 0x2f, // cols4+3 - / 0x5e, // cols4+4 - ^ 0, // cols4+5 - NO VALUE 0, // cols4+6 - NO VALUE 0, // cols4+7 - NO VALUE 0x3c, // cols5 - < 0, // cols5+1 - NO VALUE 0x3d, // cols5+2 - = 0, // cols5+3 - NO VALUE 0x3e, // cols5+4 - > 0, // cols5+5 - NO VALUE 0, // cols5+6 - NO VALUE 0, // cols5+7 - NO VALUE 0, // cols5+8 - NO VALUE 0, // cols5+9 - NO VALUE 0, // cols5+10 - NO VALUE 0, // cols5+11 - NO VALUE 0, // cols5+12 - NO VALUE 0, // cols5+13 - NO VALUE 0x25, // cols6 - % 0x23, // cols6+1 - # 0x26, // cols6+2 - & 0x40, // cols6+3 - @ 0x5c, // cols6+4 - backslash 0x5f, // cols6+5 - _ 0x7c, // cols6+6 - | 0x7e, // cols6+7 - ~ 0, // cols6+8 - NO VALUE 0, // cols6+9 - NO VALUE 0, // cols6+10 - NO VALUE 0, // cols6+11 - NO VALUE 0, // cols6+12 - NO VALUE 0x800, // cols7 - Uppercase Alpha 0x802, // cols7+1 - Uppercase Beta 0x806, // cols7+2 - Uppercase Gamma 0x808, // cols7+3 - Uppercase Delta 0x80a, // cols7+4 - Uppercase Epsilon 0x80c, // cols7+5 - Uppercase Zeta 0x80e, // cols7+6 - Uppercase Eta 0x810, // cols7+7 - Uppercase Theta 0x812, // cols7+8 - Uppercase Iota 0x814, // cols7+9 - Uppercase Kappa 0x816, // cols7+10 - Uppercase Lambda 0x818, // cols7+11 - Uppercase Mu 0x81a, // cols7+12 - Uppercase Nu 0x81c, // cols7+13 - Uppercase Xi 0x81e, // cols7+14 - Uppercase Omicron 0x820, // cols7+15 - Uppercase Pi 0x822, // cols7+16 - Uppercase Rho 0x824, // cols7+17 - Uppercase Sigma 0x828, // cols7+18 - Uppercase Tau 0x82a, // cols7+19 - Uppercase Upsilon 0x82c, // cols7+20 - Uppercase Phi 0x82e, // cols7+21 - Uppercase Chi 0x830, // cols7+22 - Uppercase Psi 0x832, // cols7+23 - Uppercase Omega 0, // cols7+24 - NO VALUE 0x30, // cols8 - 0 0x31, // cols8+1 - 1 0x32, // cols8+2 - 2 0x33, // cols8+3 - 3 0x34, // cols8+4 - 4 0x35, // cols8+5 - 5 0x36, // cols8+6 - 6 0x37, // cols8+7 - 7 0x38, // cols8+8 - 8 0x39, // cols8+9 - 9 0x41, // cols9 - A 0x124, // cols9+1 - AE digraph 0x42, // cols9+2 - B 0x43, // cols9+3 - C 0xffff, // cols9+4 - CH in spanish 0x162, // cols9+5 - Holder for C caron in Czech 0x44, // cols9+6 - D 0x45, // cols9+7 - E 0x46, // cols9+8 - F 0x47, // cols9+9 - G 0x48, // cols9+10 - H 0xffff, // cols9+11 - CH in czech or dotless i in turkish 0x49, // cols9+12 - I 0x18a, // cols9+13 - IJ Digraph 0x4a, // cols9+14 - J 0x4b, // cols9+15 - K 0x4c, // cols9+16 - L 0xffff, // cols9+17 - LL in spanish 0x4d, // cols9+18 - M 0x4e, // cols9+19 - N 0x138, // cols9+20 - N Tilde 0x4f, // cols9+21 - O 0x1a6, // cols9+22 - OE digraph 0x50, // cols9+23 - P 0x51, // cols9+24 - Q 0x52, // cols9+25 - R 0x1aa, // cols9+26 - Holder for R caron in Czech 0x53, // cols9+27 - S 0x1b0, // cols9+28 - Holder for S caron in Czech 0x54, // cols9+29 - T 0x55, // cols9+30 - U 0x56, // cols9+31 - V 0x57, // cols9+32 - W 0x58, // cols9+33 - X 0x59, // cols9+34 - Y 0x5a, // cols9+35 - Z 0x1ce, // cols9+36 - Holder for Z caron in Czech 0x158, // cols9+37 - Uppercase Thorn 0, // cols9+38 - ??? 0, // cols9+39 - ??? 0x5b, // cols9+40 - [ (note: alphabetic - end of list) 0x5d, // cols9+41 - ] (note: alphabetic - end of list) 0x124, // cols9+42 - AE diagraph - DK 0x124, // cols9+43 - AE diagraph - NO 0x122, // cols9+44 - A ring - SW 0x11E, // cols9+45 - A diaeresis - DK 0x124, // cols9+46 - AE diagraph - IC 0x150, // cols9+47 - O slash - NO 0x11e, // cols9+48 - A diaeresis - SW 0x150, // cols9+49 - O slash - DK 0x13E, // cols9+50 - O Diaeresis - IC 0x122, // cols9+51 - A ring - NO 0x13E, // cols9+52 - O Diaeresis - SW 0x13E, // cols9+53 - O Diaeresis - DK 0x150, // cols9+54 - O slash - IC 0x122, // cols9+55 - A ring - DK 0x124, // cols9+56 - AE diagraph future 0x13E, // cols9+57 - O Diaeresis future 0x150, // cols9+58 - O slash future 0, // cols9+59 - NOT USED future 0xA00, // cols10 - Russian A 0xA02, // cols10+1 - Russian BE 0xA04, // cols10+2 - Russian VE 0xA06, // cols10+3 - Russian GHE 0xA46, // cols10+4 - Ukrainian HARD G 0xA08, // cols10+5 - Russian DE 0xA4a, // cols10+6 - Serbian SOFT DJ 0xA44, // cols10+7 - Macedonian SOFT DJ 0xA0a, // cols10+8 - Russian E 0xA0c, // cols10+9 - Russian YO 0xA4e, // cols10+10 - Ukrainian YE 0xA0e, // cols10+11 - Russian ZHE 0xA10, // cols10+12 - Russian ZE 0xA52, // cols10+13 - Macedonian ZELO 0xA12, // cols10+14 - Russian I 0xA58, // cols10+15 - Ukrainian I 0xA5a, // cols10+16 - Ukrainian I with Two dots 0xA14, // cols10+17 - Russian SHORT I 0xA5e, // cols10+18 - Serbian--Macedonian JE 0xA16, // cols10+19 - Russian KA 0xA18, // cols10+20 - Russian EL 0xA68, // cols10+21 - Serbian--Macedonian SOFT L 0xA1a, // cols10+22 - Russian EM 0xA1c, // cols10+23 - Russian EN 0xA6c, // cols10+24 - Serbian--Macedonian SOFT N 0xA1e, // cols10+25 - Russian O 0xA20, // cols10+26 - Russian PE 0xA22, // cols10+27 - Russian ER 0xA24, // cols10+28 - Russian ES 0xA26, // cols10+29 - Russian TE 0xA72, // cols10+30 - Serbian SOFT T 0xA60, // cols10+31 - Macedonian SOFT K 0xA28, // cols10+32 - Russian U 0xA74, // cols10+33 - Byelorussian SHORT U 0xA2a, // cols10+34 - Russian EF 0xA2c, // cols10+35 - Russian HA 0xA2e, // cols10+36 - Russian TSE 0xA30, // cols10+37 - Russian CHE 0xA86, // cols10+38 - Serbian HARD DJ 0xA32, // cols10+39 - Russian SHA 0xA34, // cols10+40 - Russian SHCHA 0xA36, // cols10+41 - Russian ER 0xA38, // cols10+42 - Russian ERY 0xA3a, // cols10+43 - Russian SOFT SIGN 0xA8e, // cols10+44 - Old Russian YAT 0xA3c, // cols10+45 - Russian uppercase REVERSE E 0xA3e, // cols10+46 - Russian YU 0xA40, // cols10+47 - Russian YA 0xA3a, // cols10+48 - Russian SOFT SIGN - UKRAIN ONLY 0 // cols10+49 - future }; /**************************************************************************** Desc: ****************************************************************************/ FLMUINT16 HebArabColToWPChr[ ] = { 0x0D00 +164, // hamzah 0x0D00 + 58, // [13,177] alef maddah 0x0D00 + 60, // baa 0x0E00 + 48, // Sindhi bb 0x0E00 + 52, // Sindhi bh 0x0E00 + 56, // Misc p = peh 0x0D00 +152, // taa marbuuTah 0x0E00 + 60, // Urdu T 0x0D00 + 68, // thaa 0x0E00 + 68, // Sindhi th 0x0E00 + 72, // Sindhi tr 0x0E00 + 76, // Sindhi Th 0x0D00 + 72, // jiim - jeem 0x0E00 + 80, // Sindhi jj 0x0E00 + 84, // Sindhi ny 0x0E00 + 88, // Misc ch 0x0D00 + 76, // Haa 0x0D00 + 80, // khaa 0x0E00 + 96, // Pashto ts 0x0E00 +100, // Pashto dz 0x0D00 + 84, // dal 0x0E00 +104, // Urdu D 0x0D00 + 86, // thal 0x0E00 +108, // Sindhi dh 0x0E00 +110, // Sindhi D 0x0E00 +112, // Sindhi Dr 0x0E00 +114, // Sindhi Dh 0x0D00 + 88, // ra 0x0E00 +116, // Pashto r 0x0D00 + 90, // zain 0x0E00 +126, // Mizc Z 0x0D00 + 92, // seen 0x0D00 + 96, // sheen 0x0E00 +132, // Pashto x 0x0D00 +100, // Sad 0x0D00 +104, // Dad 0x0D00 +108, // Tah 0x0D00 +112, // Za (dhah) 0x0D00 +116, // 'ain 0x0D00 +120, // ghain 0x0D00 +124, // fa 0x0E00 +140, // Malay p, kurdish v = veh 0x0D00 +128, // Qaf 0x0D00 +132, // kaf (caf) 0x0E00 +160, // Persian/Urdu gaf 0x0E00 +176, // Singhi gg 0x0D00 +136, // lam - all ligature variants 0x0D00 +140, // meem 0x0D00 +144, // noon 0x0D00 +148, // ha - arabic language only 0x0D00 +154, // waw 0x0D00 +148, // ha - non-arabic language 0x0D00 +160, // alef maqsurah 0x0D00 +156, // ya 0x0E00 +212 // Urdu ya barree }; /**************************************************************************** Desc: ****************************************************************************/ FLMUINT16 ArabSubColToWPChr[] = { 0x0D00 +177, // Alef maddah 0x0D00 +165, // Alef Hamzah 0x0D00 +169, // Waw hamzah 0x0D00 +167, // Hamzah under alef 0x0D00 +171, // ya hamzah 0x0D00 +175, // alef fathattan 0x0D00 +179, // alef waslah 0x0D00 + 58, // alef 0x0D00 + 64 // taa - after taa marbuuTah }; /**************************************************************************** Desc: Turns a collated diacritic value into the original diacritic value ****************************************************************************/ FLMBYTE ml1_COLtoD[ 27] = { 23, // dbls sort value = 0 sorts as 'ss' 6, // acute sort value = 1 0, // grave sort value = 2 22, // breve sort value = 3 3, // circum sort value = 4 19, // caron sort value = 5 7, // umlaut sort value = 6 2, // tilde sort value = 7 14, // ring sort value = 8 7, // umlaut in SU, SV and CZ after ring = 9 5, // slash sort value = 10 17, // cedilla sort value = 11 4, // crossb sort value = 12 15, // dota sort value = 13 18, // ogonek sort value = 14 20, // stroke sort value = 15 1, // centerd sort value = 16 8, // macron sort value = 17 9, // aposab sort value = 18 10, // aposbes sort value = 19 11, // aposba sort value = 20 12, // aposbc sort value = 21 13, // abosbl sort value = 22 16, // dacute sort value = 23 21, // bara sort value = 24 24, // dotlesi sort value = 25 25 // dotlesj sort value = 26 }; /**************************************************************************** Desc: Kana subcollation values BIT 0: set if large char BIT 1: set if voiced BIT 2: set if half voiced Notes: To save space should be nibbles IMPORTANT: The '1' entries that do not have a matching '0' entry have been changed to zero to save space in the subcollation area. ****************************************************************************/ FLMBYTE flmKanaSubColTbl[] = { 0,1,0,1,0,1,0,1,0,1, // a A i I u U e E o O 1,3,0,3,0,3,1,3,0,3, // KA GA KI GI KU GU KE GE KO GO 0,3,0,3,0,3,0,3,0,3, // SA ZA SHI JI SU ZU SE ZE SO ZO 0,3,0,3,0,1,3,0,3,0,3, // TA DA CHI JI tsu TSU ZU TE DE TO DO 0,0,0,0,0, // NA NI NU NE NO 0,3,5,0,3,5,0,3,5, // HA BA PA HI BI PI FU BU PU 0,3,5,0,3,5, // HE BE PE HO BO PO 0,0,0,0,0, // MA MI MU ME MO 0,1,0,1,0,1, // ya YA yu YU yo YO 0,0,0,0,0, // RA RI RU RE RO 0,1,0,0,0, // wa WA WI WE WO 0,3,0,0 // N VU ka ke }; /**************************************************************************** Desc: Map KataKana (CharSet x26) to collation values. Kana collating values are two byte values where the high byte is 0x01. ****************************************************************************/ FLMBYTE KanaColTbl[] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, // a A i I u U e E o O 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, // KA GA KI GI KU GU KE GE KO GO 10,10,11,11,12,12,13,13,14,14, // SA ZA SHI JI SU ZU SE ZE SO ZO 15,15,16,16,17,17,17,18,18,19,19,// TA DA CHI JI tsu TSU ZU TE DE TO DO 20,21,22,23,24, // NA NI NU NE NO 25,25,25,26,26,26,27,27,27, // HA BA PA HI BI PI FU BU PU 28,28,28,29,29,29, // HE BE PE HO BO PO 30,31,32,33,34, // MA MI MU ME MO 35,35,36,36,37,37, // ya YA yu YU yo YO 38,39,40,41,42, // RA RI RU RE RO 43,43,44,45,46, // wa WA WI WE WO 47, 2, 5, 8 // N VU ka ke }; /**************************************************************************** Desc: Map KataKana collated value to vowel value for use for the previous char. ****************************************************************************/ FLMBYTE KanaColToVowel[] = { 0,1,2,3,4, // a i u e o 0,1,2,3,4, // ka ki ku ke ko 0,1,2,3,4, // sa shi su se so 0,1,2,3,4, // ta chi tsu te to 0,1,2,3,4, // na ni nu ne no 0,1,2,3,4, // ha hi hu he ho 0,1,2,3,4, // ma mi mu me mo 0,2,4, // ya yu yo 0,1,2,3,4, // ra ri ru re ro 0,1,3,4, // wa wi we wo }; /**************************************************************************** Desc: ****************************************************************************/ static FLMBYTE ColToKanaTbl[ 48] = { 0, // a=0, A=1 2, // i=2, I=3 4, // u=4, U=5, VU=83 6, // e=6, E=7 8, // o=8, O=9 84, // KA=10, GA=11, ka=84 12, // KI=12, GI=13 14, // KU=14, GU=15 85, // KE=16, GE=17, ke=85 18, // KO=18, GO=19 20, // SA=20, ZA=21 22, // SHI=22, JI=23 24, // SU=24, ZU=25 26, // SE=26, ZE=27 28, // SO=28, ZO=29 30, // TA=30, DA=31 32, // CHI=32, JI=33 34, // tsu=34, TSU=35, ZU=36 37, // TE=37, DE=38 39, // TO=39, DO=40 41, // NA 42, // NI 43, // NU 44, // NE 45, // NO 46, // HA, BA, PA 49, // HI, BI, PI 52, // FU, BU, PU 55, // HE, BE, PE 58, // HO, BO, PO 61, // MA 62, // MI 63, // MU 64, // ME 65, // MO 66, // ya, YA 68, // yu, YU 70, // yo, YO 72, // RA 73, // RI 74, // RU 75, // RE 76, // RO 77, // wa, WA 79, // WI 80, // WE 81, // WO 82 // N }; /**************************************************************************** Desc: The diacritical to collated table translates the first 26 characters of character set #1 into a 5 bit value for "correct" sorting sequence for that diacritical (DCV) - diacritic collated value. The attempt here is to convert the collated character value along with the DCV to form the original character. The diacriticals are in an order to fit the most languages. Czech, Swedish, and Finnish will have to manual reposition the ring above (assign it a value greater then the umlaut) This table is index by the diacritical value. ****************************************************************************/ FLMBYTE flmDia60Tbl[] = { 2, // grave offset = 0 16, // centerd offset = 1 7, // tilde offset = 2 4, // circum offset = 3 12, // crossb offset = 4 10, // slash offset = 5 1, // acute offset = 6 6, // umlaut offset = 7 // In SU, SV and CZ will = 9 17, // macron offset = 8 18, // aposab offset = 9 19, // aposbes offset = 10 20, // aposba offset = 11 21, // aposbc offset = 12 22, // abosbl offset = 13 8, // ring offset = 14 13, // dota offset = 15 23, // dacute offset = 16 11, // cedilla offset = 17 14, // ogonek offset = 18 5, // caron offset = 19 15, // stroke offset = 20 24, // bara offset = 21 3, // breve offset = 22 0, // dbls offset = 23 sorts as 'ss' 25, // dotlesi offset = 24 26 // dotlesj offset = 25 }; /**************************************************************************** Desc: ****************************************************************************/ FLMBYTE flmAlefSubColTbl[] = { 1, // ÚÙ alif hamzah 1, // ÚÄ alif hamzah 3, // ÚÙ hamzah-under-alif 3, // ÚÄ hamzah-under-alif 2, // ÚÙ waw hamzah 2, // ÚÄ waw hamzah 4, // ÚÙ ya hamzah 4, // ÄÙ ya hamzah 4, // ÄÄ ya hamzah 4, // ÚÄ ya hamzah 5, // ÚÙ alif fatHataan 5, // ÚÄ alif fatHataan 0, // ÚÙ alif maddah 0, // ÚÄ alif maddah 6, // ÚÙ alif waSlah 6 // ÚÄ alif waSlah (final) }; /**************************************************************************** Desc: ****************************************************************************/ FLMBYTE flmAr2BitTbl[] = { 0xF0, // 64..71 0x00, // 72..79 0x00, // 80..87 0x0F, // 88..95 - 92..95 0x00, // 96..103 0x00, // 104..111 0x03, // 112..119 0xFC, // 120..127 0xFF, // 128..135 0xF0, // 136..143 - 136..139 0xFF, // 144..151 - 144..147, 148..159 0xFF, // 152..159 0x0F, // 160..167 - 164..175 0xFF, // 168..175 0x0F, // 176..183 - 180..185 0xFF, // 184..191 - 186..197 0xFF, // 192..199 - 198..203 0xFF, // 200..207 - 204..207 0xF3, // 208..215 - 208..211 , 214..217 0xF0 // 216..219 - 218..219 }; /**************************************************************************** Desc: ****************************************************************************/ FINLINE FLMBOOL charIsUpper( FLMUINT16 ui16Char) { return( (FLMBOOL)((ui16Char < 0x7F) ? (FLMBOOL)((ui16Char >= ASCII_LOWER_A && ui16Char <= ASCII_LOWER_Z) ? (FLMBOOL)FALSE : (FLMBOOL)TRUE) : f_wpIsUpper( ui16Char))); } /**************************************************************************** Desc: Add an field into the CDL (Compound Data List) for this ISK. ****************************************************************************/ RCODE KYCmpKeyAdd2Lst( FDB * pDb, IXD * pIxd, // Index definition IFD * pIfd, // Index field definition void * pvField, // Field whose value is part of the key void * pRootContext) // Points to root context of field path { RCODE rc = FERR_OK; CDL * pCdl; KREF_CNTRL * pKrefCntrl; CDL ** ppCdlTbl; FLMUINT uiCdlEntry; FLMUINT uiIxEntry; pKrefCntrl = &pDb->KrefCntrl; ppCdlTbl = pKrefCntrl->ppCdlTbl; flmAssert( ppCdlTbl != NULL); // Figure out which CDL and index entry to use uiIxEntry = (FLMUINT) (pIxd - pDb->pDict->pIxdTbl); uiCdlEntry = (FLMUINT) (pIfd - pDb->pDict->pIfdTbl); if( RC_BAD( rc = pDb->TempPool.poolAlloc( sizeof( CDL), (void **)&pCdl))) { goto Exit; } flmAssert( pKrefCntrl->pIxHasCmpKeys != NULL); pKrefCntrl->pIxHasCmpKeys [uiIxEntry] = TRUE; pCdl->pField = pvField; pCdl->pRootContext = pRootContext; // Insert at first of CDL list pCdl->pNext = ppCdlTbl [uiCdlEntry]; ppCdlTbl [uiCdlEntry] = pCdl; pKrefCntrl->bHaveCompoundKey = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Called when an entire record has been processed by the key building functions. Builds and add all compound keys to the table. ****************************************************************************/ RCODE KYBuildCmpKeys( FDB * pDb, FLMUINT uiAction, FLMUINT uiContainerNum, FLMUINT uiDrn, FLMBOOL * pbHadUniqueKeys, FlmRecord * pRecord) { RCODE rc = FERR_OK; KREF_CNTRL * pKrefCntrl = &pDb->KrefCntrl; CDL ** ppCdlTbl = pKrefCntrl->ppCdlTbl; FLMBYTE * pKeyBuf = pKrefCntrl->pKrefKeyBuf; FLMBYTE * pIxHasCmpKeys = pKrefCntrl->pIxHasCmpKeys; IXD * pIxd; IFD * pIfd; IFD * pFirstIfd; FLMUINT uiFirstCdlEntry; FLMUINT uiCdlEntry; FLMBOOL bBuildCmpKeys; FLMUINT uiIxEntry; FLMUINT uiTotalIndexes; FLMUINT uiIfdCnt; FLMUINT uiKeyLen; FLMBYTE LowUpBuf [MAX_LOWUP_BUF]; FLD_CONTEXT fldContext; FDICT * pDict = pDb->pDict; LowUpBuf[0] = '\0'; if( pKrefCntrl->bHaveCompoundKey == FALSE) { goto Exit; } flmAssert( pKeyBuf != NULL && pIxHasCmpKeys != NULL); pKrefCntrl->bHaveCompoundKey = FALSE; // Loop through all of the indexes looking for a CDL entry. // VISIT: We need to find the indexes faster than looping! uiTotalIndexes = pDict->uiIxdCnt; for (uiIxEntry = 0; uiIxEntry < uiTotalIndexes; uiIxEntry++) { // See if the index has compound keys to build. if( !pIxHasCmpKeys [uiIxEntry]) { continue; } pIxd = pDict->pIxdTbl + uiIxEntry; pIxHasCmpKeys [uiIxEntry] = FALSE; bBuildCmpKeys = TRUE; // Make sure that all required fields are present. pFirstIfd = pIfd = pIxd->pFirstIfd; uiCdlEntry = uiFirstCdlEntry = (FLMUINT) (pFirstIfd - pDict->pIfdTbl); for (uiIfdCnt = 0; uiIfdCnt < pIxd->uiNumFlds; pIfd++, uiCdlEntry++, uiIfdCnt++) { FLMUINT uiCompoundPos; FLMBOOL bHitFound; // Loop on each compound field piece looking for REQUIRED field // without any data - then we don't have to build a key. bHitFound = (pIfd->uiFlags & IFD_REQUIRED_PIECE) ? FALSE : TRUE; uiCompoundPos = pIfd->uiCompoundPos; for(;;) { if( !bHitFound) { if( ppCdlTbl [uiCdlEntry]) { bHitFound = TRUE; } } if( (pIfd->uiFlags & IFD_LAST) || ((pIfd+1)->uiCompoundPos != uiCompoundPos)) { break; } pIfd++; uiCdlEntry++; uiIfdCnt++; } if( !bHitFound) { bBuildCmpKeys = FALSE; break; } } // Build the individual compound keys. if( bBuildCmpKeys) { uiKeyLen = 0; f_memset( &fldContext, 0, sizeof(FLD_CONTEXT)); if( RC_BAD(rc = KYCmpKeyElmBld( pDb, pIxd, uiContainerNum, pFirstIfd, uiAction, uiDrn, pbHadUniqueKeys, uiFirstCdlEntry, 0, pKeyBuf, uiKeyLen, LowUpBuf, 0, pRecord, &fldContext))) { goto Exit; } } // Reset the CDL pointers to NULL f_memset( (void *) (&ppCdlTbl[ uiFirstCdlEntry]), 0, sizeof( CDL *) * pIxd->uiNumFlds); } Exit: return( rc); } /**************************************************************************** Desc: Build all compound keys for a record. ****************************************************************************/ RCODE KYCmpKeyElmBld( FDB * pDb, IXD * pIxd, // Index definition. FLMUINT uiContainerNum, IFD * pIfd, // Index field definition. FLMUINT uiAction, FLMUINT uiDrn, FLMBOOL * pbHadUniqueKeys, FLMUINT uiCdlEntry, // CDL entry for the IFD. FLMUINT uiCompoundPos, // Compound Piece number - zero based FLMBYTE * pKeyBuf, // Key buffer to build the key in FLMUINT uiKeyLen, // Total length left in the key buffer FLMBYTE * pLowUpBuf, // For POST compound keys place bits here. FLMUINT uiLuLen, // Length used in pLowUpBuf. FlmRecord * pRecord, // Record being indexed. FLD_CONTEXT * pFldContext) // State to verify all fields are siblings. { RCODE rc = FERR_OK; CDL ** pCdlTbl = pDb->KrefCntrl.ppCdlTbl; CDL * pCdl = pCdlTbl [uiCdlEntry]; FLMBYTE * pTmpBuf = NULL; void * pvMark = NULL; IFD * pNextIfdPiece; void * pvField; void * pSaveParentAnchor; FLMUINT uiNextCdlEntry; FLMBOOL bBuiltKeyPiece; FLMUINT uiElmLen; FLMUINT uiPostFlag; FLMUINT uiPostLen; FLMUINT uiTempLuLen; FLMUINT uiPieceLuLen; FLMUINT uiNextPiecePos; FLMUINT uiLanguage; FLMUINT uiMaxKeySize = (pIxd->uiContainerNum) ? MAX_KEY_SIZ : MAX_KEY_SIZ - getIxContainerPartLen( pIxd); FLMBOOL bFldIsEncrypted = FALSE; if ((uiLanguage = pIxd->uiLanguage) == 0xFFFF) { uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; } // Test for compound key being tons of levels flmAssert( uiCompoundPos < MAX_COMPOUND_PIECES); // Set if this piece is part of post uiPostFlag = IFD_IS_POST_TEXT( pIfd); // Add the DELIMITER, except on the first key element if( uiCompoundPos != 0) { IFD * pPrevIfd = pIfd - 1; if( (uiLanguage >= FLM_FIRST_DBCS_LANG) && (uiLanguage <= FLM_LAST_DBCS_LANG) && (IFD_GET_FIELD_TYPE( pPrevIfd) == FLM_TEXT_TYPE) && (!(pPrevIfd->uiFlags & IFD_CONTEXT))) { pKeyBuf [uiKeyLen++] = 0; } pKeyBuf [uiKeyLen++] = COMPOUND_MARKER; } // Determine the next IFD compound piece. for( pNextIfdPiece = (IFD *)NULL, uiNextCdlEntry = uiCdlEntry + 1, uiNextPiecePos = 0; ((pIfd+uiNextPiecePos)->uiFlags & IFD_LAST) == 0; ) { if( (pIfd+uiNextPiecePos)->uiCompoundPos != (pIfd+uiNextPiecePos+1)->uiCompoundPos) { pNextIfdPiece = pIfd + uiNextPiecePos + 1; uiNextCdlEntry = uiCdlEntry + uiNextPiecePos + 1; break; } if( !pCdl) { pIfd++; pCdl = pCdlTbl [ ++uiCdlEntry]; uiNextCdlEntry = uiCdlEntry + 1; } else { uiNextPiecePos++; } } pSaveParentAnchor = pFldContext->pParentAnchor; bBuiltKeyPiece = FALSE; // Loop on each CDL, but do at least once while( pCdl || !bBuiltKeyPiece) { // Restore context values for each iteration pFldContext->pParentAnchor = pSaveParentAnchor; // If there is a field to process, verify that its path is // relative to the previous non-null compound pieces if( pCdl) { pvField = pCdl->pField; // Validate the current and previous root contexts if( KYValidatePathRelation( pRecord, pCdl->pRootContext, pvField, pFldContext, uiCompoundPos) == FERR_FAILURE) { // This field didn't pass the test, get the next field. goto Next_CDL_Field; } } else { pvField = NULL; } bBuiltKeyPiece = TRUE; uiPostLen = uiElmLen = 0; uiTempLuLen = uiLuLen; if( pCdl && (pIfd->uiFlags & (IFD_EACHWORD | IFD_SUBSTRING)) && (pRecord->getDataType( pvField) == FLM_TEXT_TYPE) && pRecord->getDataLength( pvField) && ((!pRecord->isEncryptedField( pvField) || (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode)))) { const FLMBYTE * pText = pRecord->getDataPtr( pvField); FLMUINT uiTextLen = pRecord->getDataLength( pvField); FLMUINT uiWordLen; FLMBOOL bReturn; FLMBOOL bFirstSubstring = (pIfd->uiFlags & IFD_SUBSTRING) ? TRUE : FALSE; if( !pTmpBuf) { pvMark = pDb->TempPool.poolMark(); if( RC_BAD( rc = pDb->TempPool.poolAlloc( MAX_KEY_SIZ + 8, (void **)&pTmpBuf))) { goto Exit; } } // Loop on each WORD in the value for(;;) { bReturn = (pIfd->uiFlags & IFD_EACHWORD) ? (FLMBOOL) KYEachWordParse( &pText, &uiTextLen, pIfd->uiLimit, pTmpBuf, &uiWordLen) : (FLMBOOL) KYSubstringParse( &pText, &uiTextLen, pIfd->uiFlags, pIfd->uiLimit, pTmpBuf, &uiWordLen); if( !bReturn) { break; } uiTempLuLen = uiLuLen; // Compute number of bytes left uiElmLen = uiMaxKeySize - uiKeyLen - uiTempLuLen; if( RC_BAD( rc = KYCollateValue( &pKeyBuf [uiKeyLen], &uiElmLen, pTmpBuf, uiWordLen, pIfd->uiFlags, pIfd->uiLimit, NULL, &uiPieceLuLen, uiLanguage, TRUE, bFirstSubstring, FALSE, NULL))) { goto Exit; } bFirstSubstring = FALSE; if( uiPostFlag) { uiElmLen -= uiPieceLuLen; f_memcpy( &pLowUpBuf [uiTempLuLen], &pKeyBuf[ uiKeyLen + uiElmLen ], uiPieceLuLen); uiTempLuLen += uiPieceLuLen; } if( !pNextIfdPiece) { // All ISKs have been added so now output the key if( uiTempLuLen ) { uiPostLen = KYCombPostParts( pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen), pLowUpBuf, uiTempLuLen, uiLanguage, (FLMUINT)(pIfd->uiFlags) ); } if( RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd, uiAction, uiDrn, pbHadUniqueKeys, pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen + uiPostLen), TRUE, FALSE, FALSE))) { goto Cleanup1; } } else if( RC_BAD( rc)) { goto Cleanup1; } else { // RECURSIVE CALL to the Next ISK provided no overflow if( RC_BAD( rc = KYCmpKeyElmBld( pDb, pIxd, uiContainerNum, pNextIfdPiece, uiAction, uiDrn, pbHadUniqueKeys, uiNextCdlEntry, uiCompoundPos + 1, pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen), pLowUpBuf, uiTempLuLen, pRecord, pFldContext))) { goto Cleanup1; } } if( (pIfd->uiFlags & IFD_SUBSTRING) && (uiTextLen == 1 && !(uiLanguage >= FLM_FIRST_DBCS_LANG && uiLanguage <= FLM_LAST_DBCS_LANG))) { break; } } Cleanup1: if (RC_BAD( rc)) { goto Exit; } } else { if( pvField) { if( pIfd->uiFlags & IFD_CONTEXT) { pKeyBuf [uiKeyLen] = KY_CONTEXT_PREFIX; f_UINT16ToBigEndian( (FLMUINT16)pRecord->getFieldID( pvField), &pKeyBuf [uiKeyLen + 1]); uiKeyLen += KY_CONTEXT_LEN; } else if( pRecord->getDataLength( pvField)) { const FLMBYTE * pExportValue = pRecord->getDataPtr( pvField); FLMUINT uiDataLength = pRecord->getDataLength( pvField); if (pRecord->isEncryptedField( pvField) && pDb->pFile->bInLimitedMode) { pExportValue = pRecord->getEncryptionDataPtr( pvField); uiDataLength = pRecord->getEncryptedDataLength( pvField); bFldIsEncrypted = TRUE; } // Compute number of bytes left uiElmLen = uiMaxKeySize - uiKeyLen - uiLuLen; if( RC_BAD( rc = KYCollateValue( &pKeyBuf [uiKeyLen], &uiElmLen, pExportValue, uiDataLength, pIfd->uiFlags, pIfd->uiLimit, NULL, &uiPieceLuLen, uiLanguage, TRUE, FALSE, FALSE, NULL, NULL, bFldIsEncrypted))) { goto Exit; } if( uiPostFlag ) { uiElmLen -= uiPieceLuLen; f_memcpy( &pLowUpBuf [uiTempLuLen], &pKeyBuf [uiKeyLen + uiElmLen], uiPieceLuLen); uiTempLuLen += uiPieceLuLen; } } } if( !pNextIfdPiece) { // All IFDs have been added so now output the key if( uiTempLuLen) { uiPostLen = KYCombPostParts( pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen), pLowUpBuf, uiTempLuLen, uiLanguage, (FLMUINT)(pIfd->uiFlags)); } if( RC_BAD( rc = KYAddToKrefTbl( pDb, pIxd, uiContainerNum, pIfd, uiAction, uiDrn, pbHadUniqueKeys, pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen + uiPostLen), TRUE, FALSE, bFldIsEncrypted))) { goto Exit; } } else if( RC_BAD( rc)) { goto Exit; } else { if( RC_BAD( rc = KYCmpKeyElmBld( pDb, pIxd, uiContainerNum, pNextIfdPiece, uiAction, uiDrn, pbHadUniqueKeys, uiNextCdlEntry, uiCompoundPos + 1, pKeyBuf, (FLMUINT)(uiKeyLen + uiElmLen), pLowUpBuf, uiTempLuLen, pRecord, pFldContext))) { goto Exit; } } } Next_CDL_Field: if( pCdl) { pCdl = pCdl->pNext; } // If the CDL list is empty, goto the next IFD if same uiCompoundPos. while ((!pCdl) && ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd+1)->uiCompoundPos)) { pIfd++; pCdl = pCdlTbl [++uiCdlEntry]; } // If all fields failed the validate field path test and this piece of // the compound key is required, then goto exit NOW which will not // build any key with the previous built key pieces. if( !pCdl && !bBuiltKeyPiece && ((pIfd->uiFlags & IFD_OPTIONAL) == 0)) { goto Exit; } } Exit: if( pvMark) { pDb->TempPool.poolReset( pvMark); } return( rc); } /**************************************************************************** Desc: Validate that the current field is related to the other fields in the compound key index. The context (left-most) fields of the field paths must all be siblings of each other in order to be related. ****************************************************************************/ RCODE KYValidatePathRelation( FlmRecord * pRecord, void * pCurContext, void * pCurFld, FLD_CONTEXT * pFldContext, FLMUINT uiCompoundPos) { RCODE rc = FERR_OK; void * pCurParent; FLMUINT uiPrevCompoundPos; FLMBOOL bMatchedContext; // If too many compound levels, just exit and don't check. if( uiCompoundPos >= MAX_COMPOUND_PIECES) { goto Exit; } pCurParent = pRecord->parent( pCurContext); // First time in is the easy case - just set the parent anchor. // A value of NULL is OK. if( uiCompoundPos == 0) { pFldContext->pParentAnchor = pCurParent; goto Exit; } bMatchedContext = FALSE; uiPrevCompoundPos = uiCompoundPos; while( uiPrevCompoundPos--) { if( pFldContext->rootContexts[ uiPrevCompoundPos] == pCurContext) { // Check this field against the current field values. rc = KYVerifyMatchingPaths( pRecord, pCurContext, pCurFld, pFldContext->leafFlds[ uiPrevCompoundPos]); // Return failure on any failure. Otherwise continue. if( rc == FERR_FAILURE) { goto Exit; } bMatchedContext = TRUE; } } if( bMatchedContext) { // If we had some base relation match, there is no need to // verify that the parents are the same. goto Exit; } // Verify that the parent anchor equals the parent of pCurContext. if( pFldContext->pParentAnchor != pCurParent) { rc = RC_SET( FERR_FAILURE); goto Exit; } Exit: // Set the state variables for this compound position. if( RC_OK(rc)) { pFldContext->rootContexts[ uiCompoundPos ] = pCurContext; pFldContext->leafFlds[ uiCompoundPos] = pCurFld; } return( rc); } /**************************************************************************** Desc: Verify that two paths with a common context match paths. If the tag of pCurContext has a previous match in the compound key, the field should also match (more of a relational validation). This means that for keys (A.B.C.D AND A.B.C.E) the 'A.B.C' fields should be the same field. ALL previous field pieces must be checked for this. This could be (but isn't being) done by finding the best match" and only comparing the current with the best match. Hard Example: Do these fields match - A.B.D.E.F and A.C.D.E.G? We don't want to keep the field path of the two fields around because this is more state than we need right now. These match only if the 'A's are the same field. A A B C D D E E F G ****************************************************************************/ RCODE KYVerifyMatchingPaths( FlmRecord * pRecord, void * pCurContext, // Same value as pMatchFld's context. void * pCurFld, // Current field void * pMatchFld) // Some field from a previous piece. { RCODE rc = FERR_OK; FLMUINT uiCurLevel; FLMUINT uiMatchLevel; FLMBOOL bMismatchFound = FALSE; // If a field equals a context then don't bother to check. if( (pCurContext == pCurFld) || (pCurContext == pMatchFld)) { goto Exit; } // Go up the parent line until levels match. uiCurLevel = pRecord->getLevel( pCurFld); uiMatchLevel = pRecord->getLevel( pMatchFld); flmAssert( pRecord->getLevel( pCurContext) < uiCurLevel); while( uiCurLevel != uiMatchLevel) { if( uiCurLevel > uiMatchLevel) { pCurFld = pRecord->parent( pCurFld); uiCurLevel--; } else { pMatchFld = pRecord->parent( pMatchFld); uiMatchLevel--; } } // Go up until you hit the matching context. while( pCurFld != pCurContext) { if( pRecord->getFieldID( pCurFld) == pRecord->getFieldID( pMatchFld)) { // If the fields are NOT the same we MAY have a mismatch. if( pCurFld != pMatchFld) { bMismatchFound = TRUE; } } else { // Tags are different - start over checking bMismatchFound = FALSE; } // Go to the next parent. pCurFld = pRecord->parent( pCurFld); pMatchFld = pRecord->parent( pMatchFld); } if( bMismatchFound) { rc = RC_SET( FERR_FAILURE); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Combine the bits from all POST text keys. ****************************************************************************/ FLMUINT KYCombPostParts( FLMBYTE * pKeyBuf, FLMUINT uiKeyLen, FLMBYTE * pLowUpBuf, FLMUINT uiLuLen, FLMUINT uiLanguage, FLMUINT uiIfdAttr) { FLMUINT wReturnLen; if( !uiLuLen) { return( 0); } wReturnLen = (FLMUINT)(uiLuLen + 2); if( (uiLanguage >= FLM_FIRST_DBCS_LANG) && (uiLanguage <= FLM_LAST_DBCS_LANG) && ((uiIfdAttr & 0x0F) == FLM_TEXT_TYPE) && (!(uiIfdAttr & IFD_CONTEXT ))) { pKeyBuf [uiKeyLen++] = 0; wReturnLen++; } pKeyBuf [uiKeyLen++] = END_COMPOUND_MARKER; f_memcpy( &pKeyBuf [uiKeyLen], pLowUpBuf, uiLuLen); pKeyBuf [uiKeyLen + uiLuLen] = (FLMBYTE) uiLuLen; return( wReturnLen ); } /**************************************************************************** Desc: Create an index key given a keyTree and index definition. This routine works on a normal data tree - used in FlmKeyBuild. where a data record is traversed with field paths being checked. ****************************************************************************/ RCODE KYTreeToKey( FDB * pDb, IXD * pIxd, FlmRecord * pRecord, FLMUINT uiContainerNum, FLMBYTE * pKeyBuf, FLMUINT * puiKeyLenRV, FLMUINT uiFlags) { RCODE rc = FERR_OK; IFD * pIfd; void * pvMatchField; FLMBYTE * pToKey = pKeyBuf; const FLMBYTE * pExportPtr; FLMUINT uiToKeyLen; FLMUINT uiTotalLen; FLMINT nth; FLMINT iMissingFlds; FLMUINT uiIskPostFlag; FLMUINT uiLuLen; FLMUINT uiPieceLuLen; FLMUINT uiLanguage; FLMUINT uiIsPost = 0; FLMBOOL bIsAsianCompound; FLMBOOL bIsCompound; FLMBYTE LowUpBuf [MAX_LOWUP_BUF]; FLMUINT uiMaxKeySize = (pIxd->uiContainerNum) ? MAX_KEY_SIZ : MAX_KEY_SIZ - getIxContainerPartLen( pIxd); if ((uiLanguage = pIxd->uiLanguage) == 0xFFFF) { uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; } uiLuLen = 0; iMissingFlds = 0; uiTotalLen = 0; pIfd = pIxd->pFirstIfd; bIsCompound = (pIfd->uiFlags & IFD_COMPOUND) ? TRUE : FALSE; for (;;pIfd++) { uiIsPost |= (FLMUINT) (uiIskPostFlag = (FLMUINT)IFD_IS_POST_TEXT( pIfd)); bIsAsianCompound =((uiLanguage >= FLM_FIRST_DBCS_LANG) && (uiLanguage <= FLM_LAST_DBCS_LANG) && (IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) && (!(pIfd->uiFlags & IFD_CONTEXT))); nth = 1; uiToKeyLen = 0; // Find matching node in the tree - if not found skip and continue FIND_NXT: if( (pvMatchField = pRecord->find( pRecord->root(), pIfd->uiFldNum, nth)) != NULL) { // Match was found, now if flagged, validate its parent path if( uiFlags & KY_PATH_CHK_FLAG) { FLMUINT * puiFieldPath; void * pTempField = pvMatchField; FLMUINT uiCurrentFld; puiFieldPath = pIfd->pFieldPathCToP; for( uiCurrentFld = 1; puiFieldPath [uiCurrentFld]; uiCurrentFld++) { if( ((pTempField = pRecord->parent( pTempField)) == NULL) || (pRecord->getFieldID( pTempField) != puiFieldPath [uiCurrentFld])) { nth++; goto FIND_NXT; } } } // Convert the node's key value to the index type. // Compute maximum bytes remaining. uiToKeyLen = uiMaxKeySize - uiTotalLen; // Take the tag and make it the key if( pIfd->uiFlags & IFD_CONTEXT) { // Output the tag number. *pToKey = KY_CONTEXT_PREFIX; f_UINT16ToBigEndian( (FLMUINT16) pRecord->getFieldID( pvMatchField), &pToKey [1]); uiToKeyLen = KY_CONTEXT_LEN; } else { pExportPtr = pRecord->getDataPtr( pvMatchField); if( RC_BAD( rc = KYCollateValue( pToKey, &uiToKeyLen, pExportPtr, pRecord->getDataLength( pvMatchField), pIfd->uiFlags, pIfd->uiLimit, NULL, &uiPieceLuLen, uiLanguage, bIsCompound, (FLMBOOL) ((pIfd->uiFlags & IFD_SUBSTRING) ? (pRecord->isLeftTruncated( pvMatchField) ? FALSE : TRUE) : FALSE), pRecord->isRightTruncated( pvMatchField), NULL))) { goto Exit; } if( pRecord->isRightTruncated( pvMatchField)) { // If the string is EXACTLY the length of the truncation // length then it should, but doesn't, set the truncation flag. // The code didn't match the design intent. f_memmove( &pToKey[ uiToKeyLen - uiPieceLuLen + 1], &pToKey[ uiToKeyLen - uiPieceLuLen], uiPieceLuLen); pToKey[ uiToKeyLen - uiPieceLuLen] = COLL_TRUNCATED; uiToKeyLen++; } if( uiIskPostFlag) { uiToKeyLen -= uiPieceLuLen; f_memcpy( &LowUpBuf [uiLuLen], &pToKey [uiToKeyLen], uiPieceLuLen ); uiLuLen += uiPieceLuLen; } } } // Check here if key found else the fields are missing. if( uiToKeyLen) { iMissingFlds = 0; pToKey += uiToKeyLen; uiTotalLen += uiToKeyLen; // Go to the last IFD with the same compound position. while( ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd+1)->uiCompoundPos)) { pIfd++; } } else { // Continue if there are still fields with same compound position. if( ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd+1)->uiCompoundPos)) { continue; } iMissingFlds++; if( bIsAsianCompound) { iMissingFlds++; } } // Check if done. if( pIfd->uiFlags & IFD_LAST) { break; } if( bIsCompound) { if( bIsAsianCompound) { *pToKey++ = 0; uiTotalLen++; } *pToKey++ = COMPOUND_MARKER; uiTotalLen++; } else if( uiToKeyLen) { break; } } // Back up iMissingFlds-1 because last // field does not have compound marker. // Add 4 bytes of foxes for high values. if( iMissingFlds && (uiFlags & KY_HIGH_FLAG) && bIsCompound) { // Ignore the last one or two iMissingFlds values because a compound // marker was not added to the end of the key. if( bIsAsianCompound) { iMissingFlds--; } uiTotalLen -= --iMissingFlds; pToKey -= iMissingFlds; // Fill with high values to the end of the buffer. // It is easy for double byte ASIAN collation values to all be 0xFF. if( uiTotalLen < uiMaxKeySize) { f_memset( pToKey, 0xFF, uiMaxKeySize - uiTotalLen ); pToKey += (uiMaxKeySize - uiTotalLen); uiTotalLen += (uiMaxKeySize - uiTotalLen); } } else if( uiIsPost) { uiTotalLen += KYCombPostParts( pKeyBuf, uiTotalLen, LowUpBuf, uiLuLen, uiLanguage, (FLMUINT)(pIfd->uiFlags)); } // Add container number to the key if the index is on all containers. if (!pIxd->uiContainerNum) { appendContainerToKey( pIxd, uiContainerNum, pKeyBuf, &uiTotalLen); } *puiKeyLenRV = uiTotalLen; Exit: return( rc); } /**************************************************************************** Desc: Build a collated key value piece. ****************************************************************************/ RCODE KYCollateValue( FLMBYTE * pDest, FLMUINT * puiDestLenRV, const FLMBYTE * pSrc, FLMUINT uiSrcLen, FLMUINT uiFlags, FLMUINT uiLimit, FLMUINT * puiCollationLen, FLMUINT * puiLuLenRV, FLMUINT uiLanguage, FLMBOOL bCompoundPiece, FLMBOOL bFirstSubstring, FLMBOOL bInputTruncated, FLMBOOL * pbDataTruncated, FLMBOOL * pbOriginalCharsLost, FLMBOOL bFldIsEncrypted) { RCODE rc = FERR_OK; FLMUINT uiDestLen; FLMUINT uiDataType = uiFlags & 0x0F; // Treat an encrypted field as binary for collation purposes. if (bFldIsEncrypted) { uiDataType = FLM_BINARY_TYPE; } if( puiLuLenRV) { *puiLuLenRV = 0; } if( (uiDestLen = *puiDestLenRV) == 0) { return( RC_SET( FERR_KEY_OVERFLOW)); } if( uiDataType == FLM_TEXT_TYPE) { FLMUINT uiCharLimit; FLMBYTE byTmpBuf[ MAX_KEY_SIZ + 8]; if( uiFlags & (IFD_MIN_SPACES | IFD_NO_UNDERSCORE | IFD_NO_SPACE | IFD_NO_DASH | IFD_ESC_CHAR)) { if( RC_BAD( rc = KYFormatText( pSrc, uiSrcLen, (uiFlags & IFD_MIN_SPACES) ? TRUE : FALSE, (uiFlags & IFD_NO_UNDERSCORE) ? TRUE : FALSE, (uiFlags & IFD_NO_SPACE) ? TRUE : FALSE, (uiFlags & IFD_NO_DASH) ? TRUE : FALSE, (uiFlags & IFD_ESC_CHAR) ? TRUE : FALSE, bInputTruncated, byTmpBuf, &uiSrcLen))) { goto Exit; } pSrc = (FLMBYTE *) byTmpBuf; } uiCharLimit = uiLimit ? uiLimit : IFD_DEFAULT_LIMIT; if( (uiLanguage >= FLM_FIRST_DBCS_LANG ) && (uiLanguage <= FLM_LAST_DBCS_LANG)) { rc = AsiaFlmTextToColStr( pSrc, uiSrcLen, pDest, &uiDestLen, (uiFlags & IFD_UPPER), puiCollationLen, puiLuLenRV, uiCharLimit, bFirstSubstring, pbDataTruncated); } else { rc = FTextToColStr( pSrc, uiSrcLen, pDest, &uiDestLen, (uiFlags & IFD_UPPER), puiCollationLen, puiLuLenRV, uiLanguage, uiCharLimit, bFirstSubstring, pbOriginalCharsLost, pbDataTruncated); } } // uiDestLen could be set to zero if text and no value. if( !uiSrcLen || !uiDestLen) { if( !bCompoundPiece) { // Zero length key. Any value under 0x1F would work. if( (uiLanguage >= FLM_FIRST_DBCS_LANG ) && (uiLanguage <= FLM_LAST_DBCS_LANG)) { pDest [0] = 0; pDest [1] = NULL_KEY_MARKER; uiDestLen = 2; } else { pDest [0] = NULL_KEY_MARKER; uiDestLen = 1; } } else { uiDestLen = 0; } goto Exit; } switch (uiDataType) { case FLM_TEXT_TYPE: { break; } case FLM_NUMBER_TYPE: { FLMBYTE * pOutput = pDest + 1; const FLMBYTE * pTempSrc = pSrc; FLMUINT uiBytesOutput = 1; FLMUINT uiMaxOutLen = uiDestLen; FLMINT iHiInNibble = 1; FLMINT iHiOutNibble = 1; FLMUINT uiSigSign = SIG_POS; FLMUINT uiMagnitude = COLLATED_NUM_EXP_BIAS - 1; FLMBYTE byValue; for (rc = FERR_OK;;) { switch( byValue = (iHiInNibble++ & 1) ? (FLMBYTE)(*pTempSrc >> 4) : (FLMBYTE)(*pTempSrc++ & 0x0F)) { case 0x0B: // Negative Sign code { uiSigSign = 0; continue; } case 0x0A: // Ignore for now - not implemented case 0x0C: case 0x0D: case 0x0E: { continue; } case 0x0F: // Terminator { *pDest = (FLMBYTE)(uiSigSign | ((uiSigSign ? uiMagnitude : ~uiMagnitude) & 0x7F)); goto NumDone; } default: { uiMagnitude++; if( uiSigSign) { byValue += COLLATED_DIGIT_OFFSET; } else { // Invert for key collation byValue = (FLMBYTE)((COLLATED_DIGIT_OFFSET + 9) - byValue); } if( iHiOutNibble++ & 1) { if( uiBytesOutput++ == uiMaxOutLen) { uiBytesOutput = 0; rc = RC_SET( FERR_KEY_OVERFLOW); goto NumDone; } *pOutput = (FLMBYTE)((byValue << 4) | 0x0F); } else { *pOutput++ &= (FLMBYTE)(byValue | 0xF0); } continue; } } } NumDone: uiDestLen = uiBytesOutput; break; } case FLM_BINARY_TYPE: { FLMUINT uiLength = uiSrcLen; const FLMBYTE * tmpSrc = pSrc; FLMBYTE * tmpDest = pDest; FLMBOOL bTruncated = FALSE; if( uiLength >= uiLimit) { uiLength = uiLimit; bTruncated = TRUE; } if( uiDestLen < (uiLength << 1)) { // Compute length so will not overflow uiLength = (FLMUINT)(uiDestLen >> 1); } else { uiDestLen = (FLMUINT)(uiLength << 1); } // Convert each byte to two bytes while( uiLength--) { *tmpDest++ = (FLMBYTE)(COLLS + ((*tmpSrc) >> 4)); *tmpDest++ = (FLMBYTE)(COLLS + ((*tmpSrc++) & 0x0F)); } if( bTruncated) { *tmpDest++ = COLL_TRUNCATED; } break; } case FLM_CONTEXT_TYPE: { if( uiDestLen < 5) { uiDestLen = 0; rc = RC_SET( FERR_KEY_OVERFLOW); } else { *pDest = 0x1F; f_UINT32ToBigEndian( FB2UD( pSrc), pDest + 1); uiDestLen = 5; rc = FERR_OK; } break; } default: { rc = RC_SET( FERR_CONV_BAD_DEST_TYPE); break; } } Exit: *puiDestLenRV = uiDestLen; return( rc); } /**************************************************************************** Desc: Format text removing leading and trailing spaces. Treat underscores as spaces. As options, remove all spaces and dashes. Ret: FERR_OK always. WIll truncate so text will fill MAX_KEY_SIZ. Allocate 8 more than MAX_KEY_SIZ for psDestBuf. Visit: Pass in uiLimit and pass back a truncated flag when the string is truncated. This was not done because we will have to get the exact truncated count that is done in f_tocoll.cpp and that could introduce some bugs. ****************************************************************************/ RCODE KYFormatText( const FLMBYTE * psVal, // Points to value source FLMUINT uiSrcLen, // Length of the key-NOT NULL TERMINATED // Booleans below are zero or NON-zero FLMBOOL bMinSpaces, // Remove leading/trailing/multiple spaces FLMBOOL bNoUnderscore, // Convert underscore to space FLMBOOL bNoSpace, // Remove all spaces FLMBOOL bNoDash, // Remove all dashes (hyphens) FLMBOOL bEscChar, // Literal '*' or '\\' char after '\\' esc char FLMBOOL bInputTruncated,// TRUE if input key data is truncated. FLMBYTE * psDestBuf, // (out) Destination buffer FLMUINT * puuiDestLen) // (out) Length of key in destination buffer. { RCODE rc = FERR_OK; FLMBYTE * psDestPtr = psDestBuf; FLMBYTE ucValue; FLMBYTE objType; FLMUINT uiCurPos = 0; FLMUINT uiDestPos = 0; FLMUINT uiOldDestPos = 0; FLMUINT objLength; FLMBOOL bLastCharWasSpace = bMinSpaces; for( ; uiCurPos < uiSrcLen && uiDestPos < MAX_KEY_SIZ - 1; uiCurPos += objLength) { ucValue = psVal [uiCurPos]; objLength = 1; uiOldDestPos = uiDestPos; objType = (FLMBYTE)(flmTextObjType( ucValue)); switch( objType) { case ASCII_CHAR_CODE: // 0nnnnnnn { if( (ucValue == ASCII_SPACE) || ((ucValue == ASCII_UNDERSCORE) && bNoUnderscore)) { if( bLastCharWasSpace || bNoSpace) { break; } // Sets to true if we want to minimize spaces. bLastCharWasSpace = bMinSpaces; ucValue = ASCII_SPACE; } else if( (ucValue == ASCII_DASH) && bNoDash) { break; } else { if( (ucValue == ASCII_BACKSLASH) && bEscChar && (psVal [uiCurPos+1] == ASCII_WILDCARD || psVal [uiCurPos+1] == ASCII_BACKSLASH)) { ucValue = psVal [uiCurPos+1]; objLength++; } bLastCharWasSpace = FALSE; } psDestPtr[ uiDestPos++] = ucValue; break; } case WHITE_SPACE_CODE: // 110nnnnn { if( bLastCharWasSpace || bNoSpace) { break; } // Sets to true if we want to minimize spaces. bLastCharWasSpace = bMinSpaces; psDestPtr[ uiDestPos++] = ASCII_SPACE; break; } case CHAR_SET_CODE: // 10nnnnnn case UNK_EQ_1_CODE: case OEM_CODE: { bLastCharWasSpace = FALSE; psDestPtr[ uiDestPos++] = psVal [uiCurPos]; psDestPtr[ uiDestPos++] = psVal [uiCurPos+1]; objLength = 2; break; } case UNICODE_CODE: // Unconvertable UNICODE code case EXT_CHAR_CODE: // Full extended character { bLastCharWasSpace = FALSE; psDestPtr[ uiDestPos++] = psVal [uiCurPos]; psDestPtr[ uiDestPos++] = psVal [uiCurPos+1]; psDestPtr[ uiDestPos++] = psVal [uiCurPos+2]; objLength = 3; break; } case UNK_GT_255_CODE: { bLastCharWasSpace = FALSE; objLength = 1 + sizeof( FLMUINT) + FB2UW( &psVal [uiCurPos + 1]); break; } case UNK_LE_255_CODE: { bLastCharWasSpace = FALSE; objLength = 2 + (FLMUINT) (psVal [uiCurPos+1]); break; } default: { psDestPtr[ uiDestPos++] = psVal [uiCurPos]; bLastCharWasSpace = FALSE; break; } } } // On overflow - back out of the last character. if( uiDestPos >= MAX_KEY_SIZ - 1) { uiDestPos = uiOldDestPos; bLastCharWasSpace = FALSE; } // Handle the trailing space if present. // bLastCharWasSpace cannot be set to true if bNoSpace is true. if( bLastCharWasSpace && uiDestPos && !bInputTruncated) { uiDestPos--; } psDestPtr[ uiDestPos] = '\0'; *puuiDestLen = (FLMUINT) uiDestPos; return( rc); } /**************************************************************************** Desc: Convert a text string to a collated string. ****************************************************************************/ RCODE AsiaFlmTextToColStr( const FLMBYTE * Str, // Points to the internal TEXT string FLMUINT StrLen, // Length of the internal TEXT string FLMBYTE * ColStr, // Output collated string FLMUINT * ColStrLenRV, // Collated string length return value // Input value is MAX num of bytes in buffer FLMUINT UppercaseFlag, // Set if to convert to uppercase FLMUINT * puiCollationLen, // Returns the collation bytes length FLMUINT * puiCaseLen, // Returns length of case bytes FLMUINT uiCharLimit, // Max number of characters in this key piece FLMBOOL bFirstSubstring, // TRUE is this is the first substring key FLMBOOL * pbDataTruncated) { RCODE rc = FERR_OK; const FLMBYTE * pszStrEnd; FLMUINT Length; FLMUINT uiTargetColLen = *ColStrLenRV - 12; FLMBYTE SubColBuf[MAX_SUBCOL_BUF + 1]; FLMBYTE LowUpBuf[MAX_LOWUP_BUF + MAX_LOWUP_BUF + 2]; FLMUINT ColLen; FLMUINT SubColBitPos; FLMUINT LowUpBitPos; FLMUINT Flags; FLMUINT16 NextWpChar; FLMUINT16 UnicodeChar; FLMUINT16 ColValue; FLMBOOL bDataTruncated = FALSE; ColLen = 0; SubColBitPos = 0; LowUpBitPos = 0; Flags = 0; UnicodeChar = 0; ColValue = 0; // Don't allow any key component to exceed 256 bytes regardless of the // user-specified character or byte limit. The goal is to prevent any // single key piece from consuming too much of the key (which is // limited to 640 bytes) and thus "starving" other pieces, resulting in // a key overflow error. if (uiTargetColLen > 256) { uiTargetColLen = 256; } // Make sure SubColBuf and LowUpBuf are set to 0's f_memset( SubColBuf, 0, sizeof( SubColBuf)); f_memset( LowUpBuf, 0, sizeof( LowUpBuf)); pszStrEnd = &Str[StrLen]; NextWpChar = 0; while ((Str < pszStrEnd) || NextWpChar || UnicodeChar) { FLMUINT16 WpChar; // Current WP character FLMUINT ObjLength; FLMUINT16 SubColVal; // Sub-collated value (diacritic) FLMBYTE CaseFlags; // Get the next character from the TEXT String. NOTE: OEM // characters will be returned as character set ZERO, the character // will be greater than 127. WpChar = NextWpChar; for (NextWpChar = 0; (!WpChar || !NextWpChar) && !UnicodeChar && (Str < pszStrEnd); Str += ObjLength) { FLMBYTE ObjType; FLMBYTE CurByte; FLMUINT16 CurWpChar = 0; CurByte = *Str; ObjType = (FLMBYTE) (flmTextObjType( CurByte)); ObjLength = 1; switch (ObjType) { case ASCII_CHAR_CODE: { CurWpChar = (FLMUINT16) CurByte; break; } case CHAR_SET_CODE: { ObjLength = 2; CurWpChar = (FLMUINT16) (((FLMUINT16) (CurByte & (~CHAR_SET_MASK)) << 8) + (FLMUINT16) *(Str + 1)); break; } case WHITE_SPACE_CODE: { CurByte &= (~WHITE_SPACE_MASK); CurWpChar = ((CurByte == HARD_HYPHEN) || (CurByte == HARD_HYPHEN_EOL) || (CurByte == HARD_HYPHEN_EOP)) ? 0x2D // Minus sign - character set zero : 0x20; // Space -- character set zero break; } case UNK_GT_255_CODE: { ObjLength = 1 + sizeof(FLMUINT16) + FB2UW( Str + 1); break; } case UNK_LE_255_CODE: { ObjLength = 2 + (FLMUINT16) * (Str + 1); break; } case UNK_EQ_1_CODE: { ObjLength = 2; break; } case EXT_CHAR_CODE: { ObjLength = 3; CurWpChar = (FLMUINT16) (((FLMUINT16) *(Str + 1) << 8) + (FLMUINT16) *(Str + 2)); break; } case OEM_CODE: { ObjLength = 2; // OEM characters are always >= 128. // We use character set zero to process them. CurWpChar = (FLMUINT16) * (Str + 1); break; } case UNICODE_CODE: { ObjLength = 3; UnicodeChar = (FLMUINT16) (((FLMUINT16) *(Str + 1) << 8) + (FLMUINT16) *(Str + 2)); CurWpChar = 0; break; } default: { // Shouldn't ever get to this point continue; } } if (!WpChar) { WpChar = CurWpChar; } else { NextWpChar = CurWpChar; } } // If we didn't get a character, break out of the outer processing // loop. if (!WpChar && !UnicodeChar) { break; } if (WpChar) { if (flmAsiaGetCollation( WpChar, NextWpChar, ColValue, &ColValue, &SubColVal, &CaseFlags, (FLMUINT16) UppercaseFlag) == 2) { // Took the NextWpChar value NextWpChar = 0; } } else { // This handles all of the UNICODE characters that could not be // converted to WP characters - which will include most of the // Asian characters. CaseFlags = 0; if (UnicodeChar < 0x20) { ColValue = 0xFFFF; // Setting SubColVal to a high code will ensure that the code // that the UnicodeChar will be stored in its full 16 bits in // the sub-collation area. SubColVal = 0xFFFF; // NOTE: UnicodeChar SHOULD NOT be set to zero here. It will // be set to zero below. } else { ColValue = UnicodeChar; SubColVal = 0; UnicodeChar = 0; } } // Store the values in 2 bytes ColStr[ColLen++] = (FLMBYTE) (ColValue >> 8); ColStr[ColLen++] = (FLMBYTE) (ColValue & 0xFF); if (SubColVal) { Flags |= HAD_SUB_COLLATION; if (SubColVal <= 31) // 5 bit - store bits 10 { SET_BIT( SubColBuf, SubColBitPos); SubColBitPos += 1 + 1; // Stores a zero SETnBITS( 5, SubColBuf, SubColBitPos, SubColVal); SubColBitPos += 5; } else // 2 bytes - store bits 110 or 11110 { FLMUINT Temp; SET_BIT( SubColBuf, SubColBitPos); SubColBitPos++; SET_BIT( SubColBuf, SubColBitPos); SubColBitPos++; if (!WpChar && UnicodeChar) // Store as "11110" { SubColVal = UnicodeChar; UnicodeChar = 0; SET_BIT( SubColBuf, SubColBitPos); SubColBitPos++; SET_BIT( SubColBuf, SubColBitPos); SubColBitPos++; } SubColBitPos++; // Skip past the zero // Go to the next byte boundary to write the WP char SubColBitPos = (SubColBitPos + 7) & (~7); Temp = BYTES_IN_BITS( SubColBitPos); // Need to store HIGH-Low - PC format is Low-high! SubColBuf[Temp] = (FLMBYTE) (SubColVal >> 8); SubColBuf[Temp + 1] = (FLMBYTE) (SubColVal); SubColBitPos += 16; } } else { SubColBitPos++; } // Save case information - always 2 bits worth for asian if (CaseFlags & 0x02) { SET_BIT( LowUpBuf, LowUpBitPos); } LowUpBitPos++; if (CaseFlags & 0x01) { SET_BIT( LowUpBuf, LowUpBitPos); } LowUpBitPos++; // Check to see if ColLen is within 1 byte of max if ((ColLen >= uiCharLimit) || (ColLen + BYTES_IN_BITS( SubColBitPos) + BYTES_IN_BITS( LowUpBitPos) >= uiTargetColLen)) { // Still something left? if ((Str < pszStrEnd) || NextWpChar || UnicodeChar) { bDataTruncated = TRUE; } // Hit the max. number of characters break; } } if (puiCollationLen) { *puiCollationLen = ColLen; } // Add the first substring marker - also serves as making the string // non-null. if (bFirstSubstring) { ColStr[ColLen++] = 0; ColStr[ColLen++] = COLL_FIRST_SUBSTRING; } if (bDataTruncated) { ColStr[ColLen++] = 0; ColStr[ColLen++] = COLL_TRUNCATED; } if (!ColLen && !SubColBitPos) { if (puiCaseLen) { *puiCaseLen = 0; } goto Exit; } // Done putting the String into 3 sections - build the COLLATED KEY if (Flags & HAD_SUB_COLLATION) { ColStr[ColLen++] = 0; ColStr[ColLen++] = COLL_MARKER | SC_SUB_COL; // Move the Sub-collation (diacritics) into the collating String Length = (FLMUINT) (BYTES_IN_BITS( SubColBitPos)); f_memcpy( &ColStr[ColLen], SubColBuf, Length); ColLen += Length; } // Always represent the marker as 2 bytes and case bits in asia ColStr[ColLen++] = 0; ColStr[ColLen++] = COLL_MARKER | SC_MIXED; Length = (FLMUINT) (BYTES_IN_BITS( LowUpBitPos)); f_memcpy( &ColStr[ColLen], LowUpBuf, Length); if (puiCaseLen) { *puiCaseLen = (FLMUINT) (Length + 2); } ColLen += Length; Exit: if (pbDataTruncated) { *pbDataTruncated = bDataTruncated; } *ColStrLenRV = (FLMUINT) ColLen; return (rc); } /**************************************************************************** Desc: Convert a text string to a collated string. If FERR_CONV_DEST_OVERFLOW is returned the string is truncated as best as it can be. The caller must decide to return the error up or deal with the truncation. Return: RCODE = SUCCESS or FERR_CONV_DEST_OVERFLOW VISIT: If the string is EXACTLY the length of the truncation length then it should, but doesn't, set the truncation flag. The code didn't match the design intent. Fix next major version. ****************************************************************************/ RCODE FTextToColStr( const FLMBYTE * pucStr, // Points to the internal TEXT string FLMUINT uiStrLen, // Length of the internal TEXT string FLMBYTE * pucCollatedStr, // Returns collated string FLMUINT * puiCollatedStrLen, // Returns total collated string length // Input is maximum bytes in buffer FLMUINT uiUppercaseFlag, // Set if to convert to uppercase FLMUINT * puiCollationLen, // Returns the collation bytes length FLMUINT * puiCaseLen, // Returns length of case bytes FLMUINT uiLanguage, // Language FLMUINT uiCharLimit, // Max number of characters in this key piece FLMBOOL bFirstSubstring, // TRUE is this is the first substring key FLMBOOL * pbOriginalCharsLost, FLMBOOL * pbDataTruncated) { RCODE rc = FERR_OK; const FLMBYTE * pucStrEnd; // Points to the end of the string FLMUINT16 ui16Base; // Value of the base character FLMUINT16 ui16SubColVal; // Sub-collated value (diacritic) FLMUINT uiObjLength = 0; FLMUINT uiLength; // Temporary variable for length FLMUINT uiTargetColLen = *puiCollatedStrLen - 8; // 4=ovhd,4=worse char FLMUINT uiObjType; FLMBOOL bDataTruncated = FALSE; // Need to increase the buffer sizes to not overflow. // Characaters without COLL values will take up 3 bytes in // the ucSubColBuf[] and easily overflow the buffer. // Hard coded the values so as to minimize changes. FLMBYTE ucSubColBuf[ MAX_SUBCOL_BUF + 301]; // Holds sub-collated values(diac) FLMBYTE ucCaseBits[ MAX_LOWUP_BUF + 81]; // Holds case bits FLMUINT16 ui16WpChr; // Current WP character FLMUNICODE unichr = 0; // Current unconverted Unicode character FLMUINT16 ui16WpChr2; // 2nd character if any; default 0 for US lang FLMUINT uiColLen; // Return value of collated length FLMUINT uiSubColBitPos; // Sub-collation bit position FLMUINT uiCaseBitPos; // Case bit position FLMUINT uiFlags; // Clear all bit flags FLMBOOL bHebrewArabic = FALSE; // Set if language is hebrew, arabic, farsi FLMBOOL bTwoIntoOne; uiColLen = 0; uiSubColBitPos = 0; uiCaseBitPos = 0; uiFlags = 0; ui16WpChr2 = 0; // Don't allow any key component to exceed 256 bytes regardless of the // user-specified character or byte limit. The goal is to prevent // any single key piece from consuming too much of the key (which is // limited to 640 bytes) and thus "starving" other pieces, resulting // in a key overflow error. if( uiTargetColLen > 256) { uiTargetColLen = 256; } // Code below sets ucSubColBuf[] and ucCaseBits[] values to zero. if (uiLanguage != FLM_US_LANG) { if (uiLanguage == FLM_AR_LANG || // Arabic uiLanguage == FLM_FA_LANG || // Farsi - persian uiLanguage == FLM_HE_LANG || // Hebrew uiLanguage == FLM_UR_LANG) // Urdu { bHebrewArabic = TRUE; } } pucStrEnd = &pucStr [uiStrLen]; while (pucStr < pucStrEnd) { // Set the case bits and sub-collation bits to zero when // on the first bit of the byte. if (!(uiCaseBitPos & 0x07)) { ucCaseBits [uiCaseBitPos >> 3] = 0; } if (!(uiSubColBitPos & 0x07)) { ucSubColBuf [uiSubColBitPos >> 3] = 0; } // Get the next character from the TEXT string. for (ui16WpChr = ui16SubColVal = 0; // Default sub-collation value !ui16WpChr && pucStr < pucStrEnd; pucStr += uiObjLength) { FLMBYTE ucChar = *pucStr; uiObjType = flmTextObjType( ucChar); switch (uiObjType) { case ASCII_CHAR_CODE: // 0nnnnnnn { uiObjLength = 1; // Character set zero is assumed. ui16WpChr = (FLMUINT16)ucChar; continue; } case CHAR_SET_CODE: // 10nnnnnn { uiObjLength = 2; // Character set followed by character ui16WpChr = (((FLMUINT16)(ucChar & (~CHAR_SET_MASK)) << 8) + (FLMUINT16)*(pucStr + 1)); continue; } case WHITE_SPACE_CODE: // 110nnnnn { uiObjLength = 1; ucChar &= (~WHITE_SPACE_MASK); ui16WpChr = (ucChar == HARD_HYPHEN || ucChar == HARD_HYPHEN_EOL || ucChar == HARD_HYPHEN_EOP) ? (FLMUINT16)0x2D // Minus sign -- character set 0 : (FLMUINT16)0x20;// Space -- character set zero continue; } case UNK_GT_255_CODE: { uiObjLength = 3 + FB2UW( pucStr + 1); continue; } case UNK_LE_255_CODE: { uiObjLength = 2 + (FLMUINT16)*(pucStr + 1); continue; } case UNK_EQ_1_CODE: { uiObjLength = 2; continue; } case EXT_CHAR_CODE: { uiObjLength = 3; // Character set followed by character ui16WpChr = (((FLMUINT16)*(pucStr + 1) << 8) + (FLMUINT16)*(pucStr + 2)); continue; } case OEM_CODE: { // OEM characters are always >= 128 // Use character set zero to process them. uiObjLength = 2; ui16WpChr = (FLMUINT16)*(pucStr + 1); continue; } case UNICODE_CODE: // Unconvertable UNICODE code { uiObjLength = 3; // Unicode character followed by unicode character set unichr = (FLMUINT16)(((FLMUINT16)*(pucStr + 1) << 8) + (FLMUINT16)*(pucStr + 2)); ui16WpChr = UNK_UNICODE_CODE; continue; } default: { // Should not happen, but don't return an error flmAssert( 0); continue; } } } // If we didn't get a character, break out of while loop. if (!ui16WpChr) { break; } // f_wpCheckDoubleCollation modifies ui16WpChr if a digraph or a double // character sequence is found. If a double character is found, pucStr // is incremented and ui16WpChr2 is set to 1. If a digraph is found, // pucStr is not changed, but ui16WpChr contains the first character and // ui16WpChr2 contains the second character of the digraph. if (uiLanguage != FLM_US_LANG) { ui16WpChr2 = f_wpCheckDoubleCollation( &ui16WpChr, &bTwoIntoOne, &pucStr, uiLanguage); } // Save the case bit if (!uiUppercaseFlag) { // charIsUpper returns TRUE if upper case, 0 if lower case. if (!charIsUpper( ui16WpChr)) { uiFlags |= HAD_LOWER_CASE; } else { // Set if upper case. SET_BIT( ucCaseBits, uiCaseBitPos); } uiCaseBitPos++; } // Handle OEM characters, non-collating characters, // characters with subcollating values, double collating // values. // Get the collated value from the WP character-if not collating value if ((pucCollatedStr[ uiColLen++] = (FLMBYTE)(f_wpGetCollation( ui16WpChr, uiLanguage))) >= COLS11) { FLMUINT uiTemp; // Save OEM characters just like non-collating characters // If lower case, convert to upper case. if (!charIsUpper( ui16WpChr)) { ui16WpChr &= ~1; } // No collating value given for this WP char. // Save original WP char (2 bytes) in subcollating // buffer. // 1110 is a new code that will store an insert over // the character OR a non-convertable unicode character. // Store with the same alignment as "store_extended_char" // below. // 11110 is code for unmappable UNICODE value. // A value 0xFE will be the collation value. The sub-collation // value will be 0xFFFF followed by the UNICODE value. // Be sure to eat an extra case bit. // See specific Hebrew and Arabic comments in the // switch statement below. // Set the next byte that follows in the sub collation buffer. ucSubColBuf [(uiSubColBitPos + 8) >> 3] = 0; if (bHebrewArabic && (pucCollatedStr [uiColLen-1] == COLS0_ARABIC)) { // Store first bit of 1110, fall through & store remaining 3 bits SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos++; // Don't store collation value uiColLen--; } else if (unichr) { ui16WpChr = unichr; unichr = 0; // Store 11 out of 11110 SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos++; SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos++; if (!uiUppercaseFlag) { ucCaseBits [(uiCaseBitPos + 7) >> 3] = 0; // Set upper case bit. SET_BIT( ucCaseBits, uiCaseBitPos); uiCaseBitPos++; } } store_extended_char: // Set the next byte that follows in the sub collation buffer. ucSubColBuf [(uiSubColBitPos + 8) >> 3] = 0; ucSubColBuf [(uiSubColBitPos + 16) >> 3] = 0; uiFlags |= HAD_SUB_COLLATION; // Set 110 bits in sub-collation - continued from above. // No need to explicitly set the zero, but must increment // for it. SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos++; SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos += 2; // store_aligned_word: This label is not referenced. // Go to the next byte boundary to write the character. uiSubColBitPos = (uiSubColBitPos + 7) & (~7); uiTemp = BYTES_IN_BITS( uiSubColBitPos); // Need to big-endian - so it will sort correctly. ucSubColBuf [uiTemp] = (FLMBYTE)(ui16WpChr >> 8); ucSubColBuf [uiTemp + 1] = (FLMBYTE)(ui16WpChr); uiSubColBitPos += 16; ucSubColBuf [uiSubColBitPos >> 3] = 0; } else { // Had a collation value // Add the lower/uppercase bit if a mixed case output. // If not lower ASCII set - check diacritic value for sub-collation if (!(ui16WpChr & 0xFF00)) { // ASCII character set - set a single 0 bit - just need to // increment to do this. uiSubColBitPos++; } else { FLMBYTE ucTmpChar = (FLMBYTE)ui16WpChr; FLMBYTE ucCharSet = (FLMBYTE)(ui16WpChr >> 8); // Convert char to uppercase because case information // is stored above. This will help // ensure that the "ETA" doesn't sort before "eta" if (!charIsUpper(ui16WpChr)) { ui16WpChr &= ~1; } switch (ucCharSet) { case F_CHSMUL1: // Multinational 1 { // If we cannot break down a char into base and // diacritic we cannot combine the charaacter // later when converting back the key. In that case, // write the entire WP char in the sub-collation area. if (f_breakWPChar( ui16WpChr, &ui16Base, &ui16SubColVal)) { goto store_extended_char; } // Write the FLAIM diacritic sub-collation value. // Prefix is 2 bits "10". Remember to leave // "111" alone for the future. // NOTE: The "unlaut" character must sort after the "ring" // character. ui16SubColVal = ((ui16SubColVal & 0xFF) == F_UMLAUT && (uiLanguage == FLM_SU_LANG || uiLanguage == FLM_SV_LANG || uiLanguage == FLM_CZ_LANG || uiLanguage == FLM_SL_LANG)) ? (FLMUINT16)(flmDia60Tbl[ F_RING] + 1) : (FLMUINT16)(flmDia60Tbl[ ui16SubColVal & 0xFF]); store_sub_col: // Set the next byte that follows in the sub collation buffer. ucSubColBuf[ (uiSubColBitPos + 8) >> 3] = 0; uiFlags |= HAD_SUB_COLLATION; // Set the 10 bits - no need to explicitly set the zero, but // must increment for it. SET_BIT( ucSubColBuf, uiSubColBitPos); uiSubColBitPos += 2; // Set sub-collation bits. SETnBITS( 5, ucSubColBuf, uiSubColBitPos, ui16SubColVal); uiSubColBitPos += 5; break; } case F_CHSGREK: // Greek { if (ucTmpChar >= 52 || // Keep case bit for 52-69 else ignore ui16WpChr == 0x804 || // [ 8,4] BETA Medial | Terminal ui16WpChr == 0x826) // [ 8,38] SIGMA terminal { goto store_extended_char; } // No subcollation to worry about - set a zero bit by // incrementing the bit position. uiSubColBitPos++; break; } case F_CHSCYR: { if (ucTmpChar >= 144) { goto store_extended_char; } // No subcollation to worry about - set a zero bit by // incrementing the bit position. uiSubColBitPos++; // VISIT: Georgian covers 208-249 - no collation defined yet break; } case F_CHSHEB: // Hebrew { // Three sections in Hebrew: // 0..26 - main characters // 27..83 - accents that apear over previous character // 84..118- dagesh (ancient) hebrew with accents // Because the ancient is only used for sayings & scriptures // we will support a collation value and in the sub-collation // store the actual character because sub-collation is in // character order. if (ucTmpChar >= 84) // Save ancient - value 84 and above { goto store_extended_char; } // No subcollation to worry about - set a zero bit by // incrementing the bit position. uiSubColBitPos++; break; } case F_CHSARB1: // Arabic 1 { // Three sections in Arabic: // 00..37 - accents that display OVER a previous character // 38..46 - symbols // 47..57 - numbers // 58..163 - characters // 164 - hamzah accent // 165..180- common characters with accents // 181..193- ligatures - common character combinations // 194..195- extensions - throw away when sorting if (ucTmpChar <= 46) { goto store_extended_char; // save original character } if (pucCollatedStr[ uiColLen-1] == COLS10a+1) // Alef? { ui16SubColVal = (ucTmpChar >= 165) ? (FLMUINT16)(flmAlefSubColTbl[ ucTmpChar - 165 ]) : (FLMUINT16)7; // Alef subcol value goto store_sub_col; } if (ucTmpChar >= 181) // Ligatures - char combination { goto store_extended_char; // save original character } if (ucTmpChar == 64) // taa exception { ui16SubColVal = 8; goto store_sub_col; } // No subcollation to worry about - set a zero bit by // incrementing the bit position. uiSubColBitPos++; break; } case F_CHSARB2: // Arabic 2 { // There are some characters that share the same slot // Check the bit table if above character 64 if (ucTmpChar >= 64 && flmAr2BitTbl[(ucTmpChar - 64) >> 3] & (0x80 >> (ucTmpChar & 0x07))) { goto store_extended_char; // Will save original } // No subcollation to worry about - set a zero bit by // incrementing the bit position. uiSubColBitPos++; break; } default: { // Increment bit position to set a zero bit. uiSubColBitPos++; break; } } } // Now let's worry about double character sorting if (ui16WpChr2) { if (pbOriginalCharsLost) { *pbOriginalCharsLost = TRUE; } // Set the next byte that follows in the sub collation buffer. ucSubColBuf [(uiSubColBitPos + 7) >> 3] = 0; if (bTwoIntoOne) { // Sorts after character in ui16WpChr after call to // f_wpCheckDoubleCollation // Write the char 2 times so lower/upper bits are correct. // Could write infinite times because of collation rules. pucCollatedStr[ uiColLen] = ++pucCollatedStr[ uiColLen-1]; uiColLen++; // If original was upper case, set one more upper case bit if (!uiUppercaseFlag) { ucCaseBits[ (uiCaseBitPos + 7) >> 3] = 0; if (!charIsUpper( (FLMUINT16) *(pucStr - 1))) { uiFlags |= HAD_LOWER_CASE; } else { SET_BIT( ucCaseBits, uiCaseBitPos); } uiCaseBitPos++; } // Take into account the diacritical space uiSubColBitPos++; } else { // We have a digraph, get second collation value pucCollatedStr[ uiColLen++] = (FLMBYTE)(f_wpGetCollation( ui16WpChr2, uiLanguage)); // Normal case, assume no diacritics set uiSubColBitPos++; // If first was upper, set one more upper bit. if (!uiUppercaseFlag) { ucCaseBits [(uiCaseBitPos + 7) >> 3] = 0; if (charIsUpper( ui16WpChr)) { SET_BIT( ucCaseBits, uiCaseBitPos); } uiCaseBitPos++; // no need to reset the uiFlags } } } } // Check to see if uiColLen is at some overflow limit. if (uiColLen >= uiCharLimit || uiColLen + BYTES_IN_BITS( uiSubColBitPos) + BYTES_IN_BITS( uiCaseBitPos) >= uiTargetColLen) { // We hit the maximum number of characters. if (pucStr < pucStrEnd) { bDataTruncated = TRUE; } break; } } if (puiCollationLen) { *puiCollationLen = uiColLen; } // Add the first substring marker - also serves as making the string non-null. if (bFirstSubstring) { pucCollatedStr [uiColLen++] = COLL_FIRST_SUBSTRING; } if (bDataTruncated) { pucCollatedStr[ uiColLen++ ] = COLL_TRUNCATED; } if (!uiColLen && !uiSubColBitPos) { if (puiCaseLen) { *puiCaseLen = 0; } goto Exit; } // Store extra zero bit in the sub-collation area for Hebrew/Arabic if (bHebrewArabic) { uiSubColBitPos++; } // Done putting the string into 4 sections - build the COLLATED KEY // Don't set uiUppercaseFlag earlier than here because SC_LOWER may be zero uiUppercaseFlag = (uiLanguage == FLM_GR_LANG) ? SC_LOWER : SC_UPPER; // The default terminating characters is (COLL_MARKER|SC_UPPER) // Did we write anything to the subcollation area? if (uiFlags & HAD_SUB_COLLATION) { // Writes out a 0x7 pucCollatedStr [uiColLen++] = COLL_MARKER | SC_SUB_COL; // Move the sub-collation into the collating string uiLength = BYTES_IN_BITS( uiSubColBitPos); f_memcpy( &pucCollatedStr[uiColLen], ucSubColBuf, uiLength); uiColLen += uiLength; } // Move the upper/lower case stuff - force bits for Greek ONLY // This is such a small size that a memcpy is not worth it if (uiFlags & HAD_LOWER_CASE) { FLMUINT uiNumBytes = BYTES_IN_BITS( uiCaseBitPos); FLMBYTE * pucCasePtr = ucCaseBits; // Output the 0x5 pucCollatedStr [uiColLen++] = (FLMBYTE)(COLL_MARKER | SC_MIXED); if (puiCaseLen) { *puiCaseLen = uiNumBytes + 1; } if (uiUppercaseFlag == SC_LOWER) { // Negate case bits for languages (like GREEK) that sort // upper case before lower case. while (uiNumBytes--) { pucCollatedStr [uiColLen++] = ~(*pucCasePtr++); } } else { while (uiNumBytes--) { pucCollatedStr [uiColLen++] = *pucCasePtr++; } } } else { // All characters are either upper or lower case, as determined // by uiUppercaseFlag. pucCollatedStr [uiColLen++] = (FLMBYTE)(COLL_MARKER | uiUppercaseFlag); if( puiCaseLen) { *puiCaseLen = 1; } } Exit: if( pbDataTruncated) { *pbDataTruncated = bDataTruncated; } *puiCollatedStrLen = uiColLen; return( rc); } /**************************************************************************** Desc: Return the sub-collation value of a WPText character. Unconvered Unicode values always have a sub-collation value of 11110 + Unicode Value. ****************************************************************************/ FLMUINT16 flmTextGetSubCol( FLMUINT16 ui16WPValue, // WP Character value. FLMUINT16 ui16ColValue, // Collation Value (for arabic) FLMUINT uiLangId) // WP Language ID. { FLMUINT16 ui16SubColVal; FLMBYTE byCharVal; FLMBYTE byCharSet; FLMUINT16 ui16Base; // Easy case first. ui16SubColVal = 0; if( (ui16WPValue & 0xFF00 ) == 0) { goto Exit; } // From here down default ui16SubColVal is WP value. ui16SubColVal = ui16WPValue; byCharVal = (FLMBYTE) ui16WPValue; byCharSet = (FLMBYTE) (ui16WPValue >> 8); // Convert char to uppercase because case information // is stored above. This will help // insure that the "ETA" doesn't sort before "eta" // could use is lower code here for added performance. // This just happens to work with all WP character values if (!f_wpIsUpper( ui16WPValue)) { ui16WPValue &= ~1; } switch( byCharSet) { case F_CHSMUL1: { // If you cannot break down a char into base and // diacritic then you cannot combine the charaacter // later when converting back the key. So, write // the entire WP char in the sub-collation area. // We can ONLY SUPPORT MULTINATIONAL 1 for brkcar() if( f_breakWPChar( ui16WPValue, &ui16Base, &ui16SubColVal)) { // WordPerfect character cannot be broken down. // If we had a collation value other than 0xFF (COLS0), don't // return a sub-collation value. This will allow things like // upper and lower AE digraphs to compare properly. if (ui16ColValue != COLS0) { ui16SubColVal = 0; } goto Exit; } // Write the FLAIM diacritic sub-collation value. // Prefix is 2 bits "10". Remember to leave // "111" alone for the future. ui16SubColVal = ((ui16SubColVal & 0xFF) == F_UMLAUT && ((uiLangId == FLM_SU_LANG) || (uiLangId == FLM_SV_LANG) || (uiLangId == FLM_CZ_LANG) || (uiLangId == FLM_SL_LANG))) ? (FLMUINT16)(flmDia60Tbl[ F_RING] + 1) : (FLMUINT16)(flmDia60Tbl[ ui16SubColVal & 0xFF]); break; } case F_CHSGREK: { if( (byCharVal >= 52) || (ui16WPValue == 0x804) || (ui16WPValue == 0x826)) { ui16SubColVal = ui16WPValue; } break; } case F_CHSCYR: { if( byCharVal >= 144) { ui16SubColVal = ui16WPValue; } break; } case F_CHSHEB: { // Three sections in Hebrew: // 0..26 - main characters // 27..83 - accents that apear over previous character // 84..118- dagesh (ancient) hebrew with accents // // Because the ancient is only used for sayings & scriptures // we will support a collation value and in the sub-collation // store the actual character because sub-collation is in // character order. if( byCharVal >= 84) { ui16SubColVal = ui16WPValue; } break; } case F_CHSARB1: { // Three sections in Arabic: // 00..37 - accents that display OVER a previous character // 38..46 - symbols // 47..57 - numbers // 58..163 - characters // 164 - hamzah accent // 165..180- common characters with accents // 181..193- ligatures - common character combinations // 194..195- extensions - throw away when sorting if( byCharVal <= 46 ) { ui16SubColVal = ui16WPValue; } else { if( ui16ColValue == COLS10a + 1) { ui16SubColVal = (byCharVal >= 165) ? (FLMUINT16)(flmAlefSubColTbl[ byCharVal - 165 ]) : (FLMUINT16)7; // Alef subcol value } else { if( byCharVal >= 181) // Ligatures - char combination { ui16SubColVal = ui16WPValue; } else if( byCharVal == 64) // taa exception { ui16SubColVal = 8; } } } break; } case F_CHSARB2: { // There are some characters that share the same slot // Check the bit table if above character 64 if ((byCharVal >= 64) && (flmAr2BitTbl[(byCharVal-64)>> 3] & (0x80 >> (byCharVal&0x07)))) { ui16SubColVal = ui16WPValue; } break; } } Exit: return( ui16SubColVal); } /**************************************************************************** Desc: Get the original string from an asian collation string Ret: Length of the word string in bytes ****************************************************************************/ FLMUINT AsiaConvertColStr( FLMBYTE * CollatedStr, // Points to the Flaim collated string FLMUINT * CollatedStrLenRV, // Length of the Flaim collated string FLMBYTE * WordStr, // Output string to build - WP word string FLMBOOL * pbDataTruncated, // Set to TRUE if data was truncated FLMBOOL * pbFirstSubstring) // Set to TRUE if marker exists { FLMBYTE * pWordStr = WordStr; FLMUINT Length = *CollatedStrLenRV; // May optimize as a register FLMUINT CollStrPos = 0; // Position in CollatedStr[] FLMBOOL bHadExtended = FALSE; FLMUINT WordStrLen; FLMUINT16 ColChar; // 2 byte value for asian while (Length) { FLMBYTE CharVal; FLMBYTE CharSet; CharSet = CollatedStr[CollStrPos]; CharVal = CollatedStr[CollStrPos + 1]; ColChar = (FLMUINT16) ((CharSet << 8) + CharVal); if (ColChar <= MAX_COL_OPCODE) { break; } CollStrPos += 2; Length -= 2; if (CharSet == 0) // Normal Latin/Greek/Cyrillic value { ColChar = colToWPChr[CharVal - COLLS]; } else if (CharSet == 1) // katakana or hiragana character { if (CharVal > sizeof(ColToKanaTbl)) // Special cases below { if (CharVal == COLS_ASIAN_MARK_VAL) { // dakuten ColChar = 0x240a; } else if (CharVal == COLS_ASIAN_MARK_VAL + 1) { // handakuten ColChar = 0x240b; } else if (CharVal == COLS_ASIAN_MARK_VAL + 2) { // chuuten ColChar = 0x2405; } else { ColChar = 0xFFFF; // error } } else { ColChar = (FLMUINT16) (0x2600 + ColToKanaTbl[CharVal]); } } else if (CharSet != 0xFF || CharVal != 0xFF) // Asian characters { // Insert zeroes that will be treated as a signal for // uncoverted unicode characters later on. NOTE: Cannot use // 0xFFFF, because we need to be able to detect this case in // the sub-collation stuff, and we don't want to confuse it // with the 0xFFFF that may have been inserted in another // case. THIS IS A REALLY BAD HACK, BUT IT IS THE BEST WE CAN // DO FOR NOW! *pWordStr++ = 0; *pWordStr++ = 0; bHadExtended = TRUE; } // else does not have a collation value - found in sub-collation // part UW2FBA( ColChar, pWordStr); // Put the uncollation value back pWordStr += 2; } UW2FBA( 0, pWordStr); // NULL Terminate the string WordStrLen = (FLMUINT) (pWordStr - WordStr); // Parse through the sub-collation and case information. // Watch out for COMP CollStrPosT indexes-doesn't have case info after // Here are values for some of the codes: // [ 0x01] - end for fields case info follows - for COMP POST indexes // [ 0x02] - compound marker // [ 0x05] - case bits follow // [ 0x06] - case information is all uppercase // [ 0x07] - beginning of sub-collation information // [ 0x08] - first substring field that is made // [ 0x09] - truncation marker for text and binary // // Asian chars the case information should always be there and not // compressed out. This is because the case information could change // the actual width of the character from 0x26xx to charset 11. if (Length) { ColChar = (FLMUINT16) ((CollatedStr[CollStrPos] << 8) + CollatedStr[CollStrPos + 1]); // First substring is before truncated. if (ColChar == COLL_FIRST_SUBSTRING) { if (pbFirstSubstring) { *pbFirstSubstring = TRUE; // Don't need to initialize to FALSE. } Length -= 2; CollStrPos += 2; ColChar = (FLMUINT16) ((CollatedStr[CollStrPos] << 8) + CollatedStr[CollStrPos + 1]); } if (ColChar == COLL_TRUNCATED) { if (pbDataTruncated) { *pbDataTruncated = TRUE; // Don't need to initialize to FALSE. } Length -= 2; CollStrPos += 2; ColChar = (FLMUINT16) ((CollatedStr[CollStrPos] << 8) + CollatedStr[CollStrPos + 1]); } if (ColChar == (COLL_MARKER | SC_SUB_COL)) { FLMUINT TempLen; // Do another pass on the word string adding diacritics/voicings CollStrPos += 2; Length -= 2; TempLen = AsiaParseSubCol( WordStr, &WordStrLen, &CollatedStr[CollStrPos]); CollStrPos += TempLen; Length -= TempLen; } else { goto check_case; } } // Does the case info follow? - It may not because of post indexes if (Length) { ColChar = (FLMUINT16) ((CollatedStr[CollStrPos] << 8) + CollatedStr[CollStrPos + 1]); check_case: if (ColChar == (COLL_MARKER | SC_MIXED)) { CollStrPos += 2; CollStrPos += AsiaParseCase( WordStr, &WordStrLen, &CollatedStr[CollStrPos]); // Set bHadExtended to FALSE, because they will have been taken // care of in this pass. bHadExtended = FALSE; } } // Change embedded zeroes to 0xFFFFs if (bHadExtended) { FLMUINT uiCnt; FLMBYTE* pucTmp; for (uiCnt = 0, pucTmp = WordStr; uiCnt < WordStrLen; uiCnt += 2, pucTmp += 2) { if (FB2UW( pucTmp) == 0) { UW2FBA( 0xFFFF, pucTmp); } } } // Following marker is 2 bytes if post otherwise will be 1 byte ; // Should make a pass and count the extended characters *CollatedStrLenRV = CollStrPos; // value should be on 0x01 or 0x02 flag return (WordStrLen); // Return the length of the word string } /**************************************************************************** Desc: Combine the diacritic 5 and 16 bit values to an existing word string. Ret: Number of bytes parsed Notes: For each bit in the sub-collation section: 0 - no subcollation information 10 - take next 5 bits - will tell about diacritics or japanese vowel 110 - align to next byte and take word value as extended character ****************************************************************************/ FLMUINT AsiaParseSubCol( FLMBYTE * WordStr, // Existing word string to modify FLMUINT * puiWordStrLen, // Wordstring length in bytes FLMBYTE * SubColBuf) // Diacritic values in 5 bit sets { FLMUINT SubColBitPos = 0; FLMUINT NumWords = *puiWordStrLen >> 1; FLMUINT16 Diac; FLMUINT16 WpChar; // For each word in the word string ... while (NumWords--) { // Have to skip 0, because it is not accounted for in the // sub-collation bits. It was inserted when we encountered // unconverted unicode characters (Asian). Will be converted to // something else later on. SEE NOTE ABOVE. if (FB2UW( WordStr) == 0) { WordStr += 2; continue; } // This macro DOESN'T increment bitPos if (TEST1BIT( SubColBuf, SubColBitPos)) { // Bits 10 - take next 5 bits Bits 110 align and take next word // Bits 11110 align and take unicode value // SubColBitPos++; if (!TEST1BIT( SubColBuf, SubColBitPos)) { SubColBitPos++; Diac = (FLMUINT16) (GETnBITS( 5, SubColBuf, SubColBitPos)); SubColBitPos += 5; if ((WpChar = FB2UW( WordStr)) < 0x100) { if ((WpChar >= 'A') && (WpChar <= 'Z')) { // Convert to WP diacritic and combine characters f_combineWPChar( &WpChar, WpChar, (FLMUINT16) ml1_COLtoD[Diac]); // Even if cmbcar fails, WpChar is still set to a valid // value } else // Symbols from charset 0x24 { WpChar = (FLMUINT16) (0x2400 + flmCh24ColTbl[Diac - 1].ByteValue); } } else if (WpChar >= 0x2600) // Katakana { // Voicings - will allow to select original char // 000 - some 001 are changed to 000 to save space // 001 - set if large char (uppercase) // 010 - set if voiced // 100 - set if half voiced // // Should NOT match voicing or wouldn't be here! FLMBYTE CharVal = (FLMBYTE) (WpChar & 0xFF); // Try exceptions first so don't access out of bounds if (CharVal == 84) { WpChar = (FLMUINT16) (0x2600 + ((Diac == 1) ? (FLMUINT16) 10 : (FLMUINT16) 11)); } else if (CharVal == 85) { WpChar = (FLMUINT16) (0x2600 + ((Diac == 1) ? (FLMUINT16) 16 : (FLMUINT16) 17)); } // Try the next 2 slots, if not then value is 83,84 or 85 else if (flmKanaSubColTbl[CharVal + 1] == Diac) { WpChar++; } else if ((flmKanaSubColTbl[CharVal + 2] == Diac)) { WpChar += 2; } // last exception below else if (CharVal == 4) { WpChar = 0x2600 + 83; } // else leave alone! - invalid storage } UW2FBA( WpChar, WordStr); // Set if changed or not } else // "110" { FLMUINT Temp; SubColBitPos++; // Skip second '1' if (TEST1BIT( SubColBuf, SubColBitPos)) { // Unconvertable UNICODE character ; // The format will be 4 bytes, 0xFF, 0xFF, 2 byte Unicode shiftN( WordStr, (FLMUINT16) (NumWords + NumWords + 4), 2); WordStr += 2; // Skip the 0xFFFF for now SubColBitPos += 2; // Skip next "11" (*puiWordStrLen) += 2; } SubColBitPos++; // Skip the zero // Round up to next byte SubColBitPos = (SubColBitPos + 7) & (~7); Temp = BYTES_IN_BITS( SubColBitPos); WordStr[1] = SubColBuf[Temp]; // Character set WordStr[0] = SubColBuf[Temp + 1]; // Character SubColBitPos += 16; } } else { SubColBitPos++; // Be sure to increment this! } WordStr += 2; // Next WP character } return (BYTES_IN_BITS( SubColBitPos)); } /**************************************************************************** Desc: The case bits for asia are: Latin/Greek/Cyrillic 01 - case bit set if character is uppercase 10 - double wide character in CS 0x25xx, 0x26xx and 0x27xx Japanese 00 - double wide hiragana 0x255e..25b0 01 - double wide katakana 0x2600..2655 10 - single wide symbols from charset 11 that map to CS24?? 11 - single wide katakana from charset 11 ****************************************************************************/ FLMUINT AsiaParseCase( FLMBYTE * WordStr, // Existing word string to modify FLMUINT * WordStrLenRV, // Length of the WordString in bytes FLMBYTE * pCaseBits) // Lower/upper case bit string { FLMUINT WordStrLen = *WordStrLenRV; FLMUINT uiWordCnt; FLMUINT uiExtraBytes = 0; FLMUINT16 WpChar; FLMBYTE TempByte = 0; FLMBYTE MaskByte; // For each character in the word string ... for (uiWordCnt = WordStrLen >> 1, MaskByte = 0; uiWordCnt--;) { FLMBYTE CharSet; FLMBYTE CharVal; WpChar = FB2UW( WordStr); // Get the next character // Must skip any 0xFFFFs or zeroes that were inserted. if (WpChar == 0xFFFF || WpChar == 0) { // Put back 0xFFFF in case it was a zero. UW2FBA( 0xFFFF, WordStr); WordStr += 2; uiExtraBytes += 2; continue; } if (MaskByte == 0) // Time to get another byte { TempByte = *pCaseBits++; MaskByte = 0x80; } CharSet = (FLMBYTE) (WpChar >> 8); CharVal = (FLMBYTE) (WpChar & 0xFF); if (WpChar < 0x2400) // SINGLE WIDE - NORMAL CHARACTERS { if (TempByte & MaskByte) // convert to double wide? { // Latin/greek/cyrillic Convert to uppercase double wide char if (CharSet == 0) // Latin - uppercase { // May convert to 0x250F (Latin) or CS24 if (WpChar >= 'A' && WpChar <= 'Z') { // Convert to double wide WpChar = (FLMUINT16) (WpChar - 0x30 + 0x250F); } else { f_wpHanToZenkaku( WpChar, 0, &WpChar); } } else if (CharSet == 8) // Greek { if (CharVal > 38) { // Adjust for spaces in greek CharVal -= 2; } if (CharVal > 4) { CharVal -= 2; } WpChar = (FLMUINT16) ((CharVal >> 1) + 0x265E); } else if (CharSet == 10) // Cyrillic { WpChar = (FLMUINT16) ((CharVal >> 1) + 0x2700); } else { f_wpHanToZenkaku( WpChar, 0, &WpChar); } CharSet = (FLMBYTE) (WpChar >> 8); // Less code this way CharVal = (FLMBYTE) (WpChar & 0xFF); } MaskByte >>= 1; // Next bit if ((TempByte & MaskByte) == 0) // Change to lower case? { switch (CharSet) // Convert WpChar to lower case { case 0: { WpChar |= 0x20; // Bit zero only if lower case break; } case 1: { if (CharVal >= 26) { WpChar++; } break; } case 8: { if (CharVal <= 69) { // All lowercase after 69 WpChar++; } break; } case 10: { if (CharVal <= 199) { // No cases after 199 WpChar++; } break; } case 0x25: case 0x26: { // should be double wide latin or greek WpChar += 0x20; // Add offset to convert to lowercase break; } case 0x27: // double wide cyrillic only { WpChar += 0x30; // Add offset to convert to lowercase break; } } } } else // JAPANESE CHARACTERS { if (TempByte & MaskByte) // Original chars from // CharSet 11 { if (CharSet == 0x26) { FLMUINT16 NextChar = 0; WpChar = f_wpZenToHankaku( WpChar, &NextChar); if (NextChar) // Move everyone down { uiWordCnt++; shiftN( WordStr, uiWordCnt + uiWordCnt + 2, 2); UW2FBA( WpChar, WordStr); WordStr += 2; WpChar = NextChar; // Store this below *WordStrLenRV = *WordStrLenRV + 2; // Adjust length // Don't change WordStrLen - returns number of bits used } } else if (CharSet == 0x24) { WpChar = f_wpZenToHankaku( WpChar, (FLMUINT16*) 0); } MaskByte >>= 1; // Eat next bit } else { MaskByte >>= 1; // Next bit if ((TempByte & MaskByte) == 0) // Convert to hiragana? { // kanji will also fall through here if (CharSet == 0x26) { WpChar = (FLMUINT16) (0x255E + CharVal); // Convert to // hiragana } } } } UW2FBA( WpChar, WordStr); WordStr += 2; MaskByte >>= 1; } // Should be 2 bits for each character uiWordCnt = WordStrLen - uiExtraBytes; return (BYTES_IN_BITS( uiWordCnt)); } /**************************************************************************** Desc: Returns the collation value of the input WP character. If in charset 11 will convert the character to Zenkaku (double wide). In: ui16WpChar - Char to collate off of - could be in CS0..14 or x24..up ui16NextWpChar - next WP char for CS11 voicing marks ui16PrevColValue - previous collating value - for repeat/vowel repeat pui16ColValue - returns 2 byte collation value pui16SubColVal - 0, 6 or 16 bit value for the latin sub collation or the kana size & vowel voicing 001 - set if large (upper) character 010 - set if voiced 100 - set if half voiced pucCaseBits - returns 2 bits Latin/Greek/Cyrillic 01 - case bit set if character is uppercase 10 - double wide character in CS 0x25xx, 0x26xx and 0x27xx Japanese 00 - double wide hiragana 0x255e..25b0 01 - double wide katakana 0x2600..2655 10 - double wide symbols that map to charset 11 11 - single wide katakana from charset 11 Ret: 0 - no valid collation value high values set for pui16ColValue Sub-collation gets original WP character value 1 - valid collation value 2 - valid collation value and used the ui16NextWpChar Terms: HANKAKU - single wide characters in charsets 0..14 ZENKAKU - double wide characters in charsets 0x24..end of kanji KANJI - collation values are 0x2900 less than WPChar value ****************************************************************************/ FLMUINT16 flmAsiaGetCollation( FLMUINT16 ui16WpChar, // WP char to get collation values FLMUINT16 ui16NextWpChar, // Next WP char - for CS11 voicing marks FLMUINT16 ui16PrevColValue, // Previous collating value FLMUINT16 * pui16ColValue, // Returns collation value FLMUINT16 * pui16SubColVal, // Returns sub-collation value FLMBYTE * pucCaseBits, // Returns case bits value FLMUINT16 uiUppercaseFlag) // Set if to convert to uppercase { FLMUINT16 ui16ColValue; FLMUINT16 ui16SubColVal; FLMBYTE ucCaseBits = 0; FLMBYTE ucCharSet = ui16WpChar >> 8; FLMBYTE ucCharVal = ui16WpChar & 0xFF; FLMUINT16 ui16Hankaku; FLMUINT uiLoop; FLMUINT16 ui16ReturnValue = 1; ui16ColValue = ui16SubColVal = 0; // Kanji or above if (ucCharSet >= 0x2B) { // Puts 2 or above into high byte. ui16ColValue = ui16WpChar - 0x2900; // No subcollation or case bits need to be set goto Exit; } // Single wide character? (HANKAKU) if (ucCharSet < 11) { // Get the values from a non-asian character LATIN, GREEK or // CYRILLIC. The width bit may have been set on a jump to label from // below. Latin_Greek_Cyrillic: // YES: Pass FLM_US_LANG because this is what we want - Prevents double // character sorting. ui16ColValue = f_wpGetCollation( ui16WpChar, FLM_US_LANG); if (uiUppercaseFlag || f_wpIsUpper( ui16WpChar)) { // Uppercase - set case bit ucCaseBits |= SET_CASE_BIT; } // Character for which there is no collation value? if (ui16ColValue == COLS0) { ui16ReturnValue = 0; if (!f_wpIsUpper( ui16WpChar)) { // Convert to uppercase ui16WpChar--; } ui16ColValue = 0xFFFF; ui16SubColVal = ui16WpChar; } else if (ucCharSet) // Don't bother with ascii { if (!f_wpIsUpper( ui16WpChar)) { // Convert to uppercase ui16WpChar--; } if (ucCharSet == F_CHSMUL1) { FLMUINT16 ui16Base; FLMUINT16 ui16Diacritic; ui16SubColVal = !f_breakWPChar( ui16WpChar, &ui16Base, &ui16Diacritic) ? flmDia60Tbl[ui16Diacritic & 0xFF] : ui16WpChar; } else if (ucCharSet == F_CHSGREK) // GREEK { if (ui16WpChar >= 0x834 || // [8,52] or above ui16WpChar == 0x804 || // [8,4] BETA Medial | Terminal ui16WpChar == 0x826) { // [8,38] SIGMA terminal ui16SubColVal = ui16WpChar; } } else if (ucCharSet == F_CHSCYR) // CYRILLIC { if (ui16WpChar >= 0xA90) // [10, 144] or above { ui16SubColVal = ui16WpChar; // Dup collation values } } // else don't need a sub collation value } goto Exit; } // Single wide Japanese character? if (ucCharSet == 11) { FLMUINT16 ui16KanaChar; // Convert charset 11 to Zenkaku (double wide) CS24 or CS26 hex. // All characters in charset 11 will convert to CS24 or CS26. When // combining the collation and the sub-collation values. if (f_wpHanToZenkaku( ui16WpChar, ui16NextWpChar, &ui16KanaChar) == 2) { // Return 2 ui16ReturnValue++; } ucCaseBits |= SET_WIDTH_BIT; // Set so will allow to go back ui16WpChar = ui16KanaChar; // If in CS24 will fall through // to ZenKaku ucCharSet = ui16KanaChar >> 8; ucCharVal = ui16KanaChar & 0xFF; } if (ui16WpChar < 0x2400) { // In some other character set goto Latin_Greek_Cyrillic; } else if (ui16WpChar >= 0x255e && ui16WpChar <= 0x2655) { if (ui16WpChar >= 0x2600) { ucCaseBits |= SET_KATAKANA_BIT; } // HIRAGANA and KATAKANA Kana contains both hiragana and katakana. // The tables contain the same characters in same order if (ucCharSet == 0x25) { // Change value to be in character set 26 ucCharVal -= 0x5E; } ui16ColValue = 0x0100 + KanaColTbl[ucCharVal]; ui16SubColVal = flmKanaSubColTbl[ucCharVal]; goto Exit; } if ((ui16Hankaku = f_wpZenToHankaku( ui16WpChar, (FLMUINT16*) 0)) != 0) { if ((ui16Hankaku >> 8) != 11) { ui16WpChar = ui16Hankaku; ucCharSet = ui16WpChar >> 8; ucCharVal = ui16WpChar & 0xFF; ucCaseBits |= SET_WIDTH_BIT; goto Latin_Greek_Cyrillic; } } // 0x2400..0x24bc Japanese symbols that cannot be converted to // Hankaku. All 6 original symbol chars from 11 will also be here. // First try to find a collation value of the symbol. The sub-collation // value will be the position in the CS24 table + 1. for (uiLoop = 0; uiLoop < (sizeof(flmCh24ColTbl) / sizeof(BYTE_WORD_TBL)); uiLoop++) { if (ucCharVal == flmCh24ColTbl[uiLoop].ByteValue) { if ((ui16ColValue = flmCh24ColTbl[uiLoop].WordValue) < 0x100) { // Don't save for chuuten, dakuten, handakuten ui16SubColVal = (FLMUINT16) (uiLoop + 1); } break; } } if (!ui16ColValue) { // Now see if it's a repeat or repeat-vowel character if ((((ucCharVal >= 0x12) && (ucCharVal <= 0x15)) || (ucCharVal == 0x17) || (ucCharVal == 0x18)) && ((ui16PrevColValue >> 8) == 1)) { ui16ColValue = ui16PrevColValue; // Store original WP character ui16SubColVal = ui16WpChar; } else if ((ucCharVal == 0x1B) && // repeat vowel? (ui16PrevColValue >= 0x100) && (ui16PrevColValue < COLS_ASIAN_MARKS)) // Previous kana char? { ui16ColValue = 0x0100 + KanaColToVowel[ui16PrevColValue & 0xFF]; // Store original WP character ui16SubColVal = ui16WpChar; } else { ui16ReturnValue = 0; ui16ColValue = 0xFFFF; // No collation value ui16SubColVal = ui16WpChar; // Never have changed if gets here } } Exit: *pui16ColValue = ui16ColValue; *pui16SubColVal = ui16SubColVal; *pucCaseBits = ucCaseBits; return (ui16ReturnValue); } /***************************************************************************** Desc: Get the Flaim collating string and convert back to a WP word string Ret: Length of new WP word string *****************************************************************************/ FLMUINT FWWSGetColStr( FLMBYTE * fColStr, // Points to the Flaim collated string FLMUINT * fcStrLenRV, // Length of the Flaim collated string FLMBYTE * wordStr, // Output string to build - WP word string FLMUINT fWPLang, // FLAIM WP language number FLMBOOL * pbDataTruncated, // Set to TRUE if truncated FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring { FLMBYTE * wsPtr = wordStr; // Points to the word string data area FLMUINT length = *fcStrLenRV; // May optimize as a register FLMUINT pos = 0; // Position in fColStr[] FLMUINT bitPos; // Computed bit position FLMUINT colChar; // Not portable if a FLMBYTE value FLMUINT wdStrLen; FLMBOOL hebrewArabicFlag = 0; // Set if hebrew/arabic language // WARNING: The code is duplicated for performance reasons. The US code // below is much more optimized so any changes must be done twice. if (fWPLang != FLM_US_LANG) // Code for NON-US languages { if ((fWPLang == FLM_AR_LANG) || // Arabic (fWPLang == FLM_FA_LANG) || // Farsi - persian (fWPLang == FLM_HE_LANG) || // Hebrew (fWPLang == FLM_UR_LANG)) // Urdu { hebrewArabicFlag++; } while (length && (fColStr[pos] > MAX_COL_OPCODE)) { length--; colChar = (FLMUINT) fColStr[pos++]; switch (colChar) { case COLS9 + 4: // ch in spanish case COLS9 + 11: // ch in czech { // Put the WP char in the word string UW2FBA( (FLMUINT16) 'C', wsPtr); wsPtr += 2; colChar = (FLMUINT) 'H'; pos++; // move past second duplicate char break; } case COLS9 + 17: // ll in spanish { // Put the WP char in the word string UW2FBA( (FLMUINT16) 'L', wsPtr); wsPtr += 2; colChar = (FLMUINT) 'L'; pos++; // move past duplicate character break; } case COLS0: // Non collating character { // Actual character is in sub-collation area colChar = (FLMUINT) 0xFFFF; break; } default: { if (hebrewArabicFlag && (colChar >= COLS10h)) { colChar = (colChar < COLS10a) ? (FLMUINT) (0x900 + (colChar - (COLS10h))) // Hebrew : (FLMUINT) (HebArabColToWPChr[colChar - (COLS10a)]); // Arabic } else { colChar = (FLMUINT) colToWPChr[colChar - COLLS]; } break; } } UW2FBA( (FLMUINT16) colChar, wsPtr); wsPtr += 2; } } else { while (length && (fColStr[pos] > MAX_COL_OPCODE)) { length--; // Move in the WP value given uppercase collated value colChar = (FLMUINT) fColStr[pos++]; if (colChar == COLS0) { colChar = (FLMUINT) 0xFFFF; } else { colChar = (FLMUINT) colToWPChr[colChar - COLLS]; } UW2FBA( (FLMUINT16) colChar, wsPtr); wsPtr += 2; } } // NULL Terminate the string UW2FBA( (FLMUINT16) 0, wsPtr); wdStrLen = pos + pos; // Parse through the sub-collation and case information. // Watch out for COMP CollStrPosT indexes-doesn't have case info after // Here are values for some of the codes: // [ 0x01] - end for fields case info follows - for COMP POST indexes // [ 0x02] - compound marker // [ 0x05] - case bits follow // [ 0x06] - case information is all uppercase // [ 0x07] - beginning of sub-collation information // [ 0x08] - first substring field that is made // [ 0x09] - truncation marker for text and binary // // Asian chars the case information should always be there and not // compressed out. This is because the case information could change // the actual width of the character from 0x26xx to charset 11. if (length && fColStr[pos] == COLL_FIRST_SUBSTRING) { if (pbFirstSubstring) { *pbFirstSubstring = TRUE; // Don't need to initialize to FALSE. } length--; pos++; } if (length && fColStr[pos] == COLL_TRUNCATED) { if (pbDataTruncated) { *pbDataTruncated = TRUE; // Don't need to initialize to FALSE. } length--; pos++; } if (length && (fColStr[pos] == (COLL_MARKER | SC_SUB_COL))) { FLMUINT tempLen; // Do another pass on the word string adding the diacritics bitPos = FWWSCmbSubColBuf( wordStr, &wdStrLen, &fColStr[++pos], hebrewArabicFlag); // Move pos to next byte value tempLen = BYTES_IN_BITS( bitPos); pos += tempLen; length -= tempLen + 1; // The 1 includes the 0x07 byte } // Does the case info follow? if (length && (fColStr[pos] > COMPOUND_MARKER)) { // Take care of the lower and upper case conversion If mixed case // then convert using case bits if (fColStr[pos++] & SC_MIXED) // Increment pos here! { // Don't pre-increment pos on line below! pos += f_wpToMixed( wordStr, wdStrLen, &fColStr[pos], fWPLang); } // else 0x04 or 0x06 - all characters already in uppercase } *fcStrLenRV = pos; // pos should be on the 0x01 or 0x02 flag return (wdStrLen); // Return the length of the word string } /************************************************************************** Desc: Combine the diacritic 5 bit values to an existing word string Todo: May want to check f_combineWPChar() for CY return value ***************************************************************************/ FLMUINT FWWSCmbSubColBuf( FLMBYTE* wordStr, // Existing word string to modify FLMUINT* wdStrLenRV, // Wordstring length in bytes FLMBYTE* subColBuf, // Diacritic values in 5 bit sets FLMBOOL hebrewArabicFlag) // Set if language is Hebrew or Arabic { FLMUINT subColBitPos = 0; FLMUINT numWords = *wdStrLenRV >> 1; FLMUINT16 diac; FLMUINT16 wpchar; FLMUINT temp; // For each word in the word string ... while (numWords--) { // Label used for hebrew/arabic - additional subcollation can follow ; // This macro DOESN'T increment bitPos if (TEST1BIT( subColBuf, subColBitPos)) { // If "11110" - unmappable unicode char - 0xFFFF is before it // If "1110" then INDEX extended char is inserted // If "110" then extended char follows that replaces collation // If "10" then take next 5 bits which contain the diacritic // subcollation value. after_last_character: subColBitPos++; // Eat the first 1 bit if (!TEST1BIT( subColBuf, subColBitPos)) { subColBitPos++; // Eat the 0 bit diac = (FLMUINT16) (GETnBITS( 5, subColBuf, subColBitPos)); subColBitPos += 5; if ((wpchar = FB2UW( wordStr)) < 0x100) // If not extended base.. { // Convert to WP diacritic and combine characters f_combineWPChar( &wpchar, wpchar, (FLMUINT16) ml1_COLtoD[diac]); // Even if cmbcar fails, wpchar is still set to a valid // value UW2FBA( wpchar, wordStr); } else if ((wpchar & 0xFF00) == 0x0D00) // arabic? { wpchar = ArabSubColToWPChr[diac]; UW2FBA( wpchar, wordStr); } // else diacritic is extra info ; // cmbcar should not handle extended chars for this design } else // "110" or "1110" or "11110" { subColBitPos++; // Eat the 2nd '1' bit if (TEST1BIT( subColBuf, subColBitPos)) // Test the 3rd bit { // 1110 - shift wpchars down 1 word and insert value below subColBitPos++; // Eat the 3rd '1' bit *wdStrLenRV += 2; // Return 2 more bytes if (TEST1BIT( subColBuf, subColBitPos)) // Test 4th bit { // Unconvertable UNICODE character. // // The format will be 4 bytes, 0xFF, 0xFF, 2 byte Unicode shiftN( wordStr, numWords + numWords + 4, 2); subColBitPos++; // Eat the 4th '1' bit wordStr += 2; // Skip the 0xFFFF for now } else { // Move down 2 byte NULL and rest of the 2 byte characters. // The extended character does not have a 0xFF col value shiftN( wordStr, numWords + numWords + 2, 2); numWords++; } } subColBitPos++; // Skip past the zero bit subColBitPos = (subColBitPos + 7) & (~7); // roundup to next byte temp = BYTES_IN_BITS( subColBitPos); // compute position wordStr[1] = subColBuf[temp]; // Character set wordStr[0] = subColBuf[temp + 1]; // Character subColBitPos += 16; } } else { subColBitPos++; } wordStr += 2; // Next WP character } if (hebrewArabicFlag) { if (TEST1BIT( subColBuf, subColBitPos)) { // Hebrew/Arabic can have trailing accents that don't have a // matching collation value. Keep looping in this case. Note that // subColBitPos isn't incremented above. numWords = 0; // set so won't loop forever! goto after_last_character; // process trailing bit } subColBitPos++; // Eat the last '0' bit } return (subColBitPos); } /******************************************************************** Desc: Build a responce tree of NODEs for the key output. *********************************************************************/ RCODE flmIxKeyOutput( IXD * pIxd, FLMBYTE * pucFromKey, FLMUINT uiKeyLen, FlmRecord ** ppKeyRV, // Returns key FLMBOOL bFullFldPaths) // If true add full field paths { RCODE rc = FERR_OK; FlmRecord * pKey = NULL; void * pvField; FLMBYTE ucKeyBuf[ MAX_KEY_SIZ + 12]; FLMBYTE * pucToKey = &ucKeyBuf[ 0]; FLMBYTE * pucPostBuf = NULL; IFD * pIfd; FLMUINT uiLongValue; FLMUINT uiToKeyLen; FLMUINT uiLanguage = pIxd->uiLanguage; FLMUINT uiFromKeyLen; FLMUINT uiFromRemaining; FLMUINT uiPostLen; FLMUINT uiPostPos = 0; FLMUINT uiTempFromKeyLen; FLMUINT uiFldType; FLMUINT uiDataType; FLMBOOL bDataRightTruncated; FLMBOOL bFirstSubstring; FLMBOOL bSigSign; FLMBYTE ucTemp; FLMUINT uiContainer; FLMUINT uiMaxKeySize; // If the index is on all containers, see if this key has // a container component. If so, strip it off. if( (uiContainer = pIxd->uiContainerNum) == 0) { FLMUINT uiContainerPartLen = getIxContainerPartLen( pIxd); if (uiKeyLen <= uiContainerPartLen) { flmAssert( 0); rc = RC_SET( FERR_BTREE_ERROR); goto Exit; } uiContainer = getContainerFromKey( pucFromKey, uiKeyLen); // Subtract off the bytes for the container part. uiKeyLen -= uiContainerPartLen; uiMaxKeySize = MAX_KEY_SIZ - uiContainerPartLen; } else { uiMaxKeySize = MAX_KEY_SIZ; } flmAssert( uiLanguage != 0xFFFF); if (*ppKeyRV) { if( (*ppKeyRV)->isReadOnly() || (*ppKeyRV)->isCached()) { (*ppKeyRV)->Release(); *ppKeyRV = NULL; } else { (*ppKeyRV)->clear(); } } if( (pKey = *ppKeyRV) == NULL) { if( (pKey = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } *ppKeyRV = pKey; } pKey->setContainerID( uiContainer); uiFromKeyLen = uiFromRemaining = uiKeyLen; pIfd = pIxd->pFirstIfd; // If post index, get post low/up section. if( pIfd->uiFlags & IFD_POST ) { // Last byte has low/upper length uiPostLen = pucFromKey[ uiFromKeyLen - 1 ]; pucPostBuf = &pucFromKey[ uiFromKeyLen - uiPostLen - 1 ]; uiPostPos = 0; } if (RC_BAD( rc = pKey->insertLast( 0, FLM_KEY_TAG, FLM_CONTEXT_TYPE, NULL))) { goto Exit; } // Loop for each compound piece of key for( ;;) { FLMBOOL bIsAsianCompound; FLMUINT uiMarker; bDataRightTruncated = bFirstSubstring = FALSE; bIsAsianCompound = (FLMBOOL)(((uiLanguage >= FLM_FIRST_DBCS_LANG) && (uiLanguage <= FLM_LAST_DBCS_LANG) && (IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) && (!(pIfd->uiFlags & IFD_CONTEXT))) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); uiMarker = (FLMUINT)((bIsAsianCompound) ? (FLMUINT)((FLMUINT)(*pucFromKey << 8) + *(pucFromKey+1)) : (FLMUINT) *pucFromKey); uiFldType = (FLMUINT) IFD_GET_FIELD_TYPE( pIfd); uiDataType = IFD_GET_FIELD_TYPE( pIfd); // Hit a compound marker or end of key marker // Check includes COMPOUND_MARKER & END_COMPOUND_MARKER if( uiMarker <= NULL_KEY_MARKER) { // If the field is required or single field then generate an empty node. if( ((pIfd->uiFlags & IFD_OPTIONAL) == 0) || (uiFldType == FLM_TEXT_TYPE) || (uiFldType == FLM_BINARY_TYPE) || ((pIfd->uiFlags & IFD_LAST) && !pIfd->uiCompoundPos )) { if( RC_BAD( rc = flmBuildKeyPaths( pIfd, pIfd->uiFldNum, uiDataType, bFullFldPaths, pKey, &pvField))) goto Exit; } if( uiMarker == END_COMPOUND_MARKER) // Used for post keys break; uiFromKeyLen = 0; // This piece is zero - skip it - may be others } else { // If compound key or if only field used in index // output the key elements field number or else 'NA' if( pIfd->uiFlags & IFD_CONTEXT) { if( RC_BAD( rc = flmBuildKeyPaths( pIfd, f_bigEndianToUINT16( &pucFromKey [1]), uiDataType, bFullFldPaths, pKey, &pvField))) { goto Exit; } uiFromKeyLen = KY_CONTEXT_LEN; } else { if( RC_BAD( rc = flmBuildKeyPaths( pIfd, pIfd->uiFldNum, uiDataType, bFullFldPaths, pKey, &pvField))) { goto Exit; } // Grab only the Nth section of key if compound key // Null out key if uiToKeyLen gets 0 UD2FBA( 0, pucToKey); switch( uiDataType) { case FLM_TEXT_TYPE: uiTempFromKeyLen = uiFromKeyLen; uiToKeyLen = FColStrToText( pucFromKey, &uiTempFromKeyLen, pucToKey, uiLanguage, pucPostBuf, &uiPostPos, &bDataRightTruncated, &bFirstSubstring); uiFromKeyLen = uiTempFromKeyLen; break; case FLM_NUMBER_TYPE: { FLMUINT uiFirstColNibble; // Current collated nibble FLMUINT uiFirstNumNibble; // Current output nibble FLMBYTE * pucOutPtr; // Output pointer FLMBYTE * pucColPtr; FLMUINT uiBytesProcessed; // Start at byte after sign/magnitude byte pucColPtr = pucFromKey + 1; uiBytesProcessed = 1; uiFirstColNibble = 1; // Determine the sign of the number pucOutPtr = pucToKey; if( (bSigSign = (*pucFromKey & SIG_POS)) == 0) { *pucOutPtr = 0xB0; uiFirstNumNibble = 0; } else { uiFirstNumNibble = 1; } // Parse through the collated number outputting data // to the buffer as we go. for( ;;) { // Determine what we are pointing at if( (ucTemp = *pucColPtr) <= COMPOUND_MARKER) { break; } if( uiFirstColNibble++ & 1) { ucTemp >>= 4; } else { ucTemp &= 0x0F; pucColPtr++; uiBytesProcessed++; } // A hex F signifies the end of a collated number with an // odd number of nibbles if( ucTemp == 0x0F) { break; } // Convert collated number nibble to BCD nibble // and lay it in buffer ucTemp -= COLLATED_DIGIT_OFFSET; // Is number negative? if( !bSigSign) { // Negative values are ~ed ucTemp = (FLMBYTE)(10 -(ucTemp + 1)); } if( uiFirstNumNibble++ & 1) { *pucOutPtr = (FLMBYTE)(ucTemp << 4); } else { *pucOutPtr++ += ucTemp; } if( uiBytesProcessed == uiFromKeyLen) { break; } } // Append Terminator code to internal number *pucOutPtr++ |= (uiFirstNumNibble & 1) ? 0xFF : 0x0F; uiToKeyLen = (FLMUINT) (pucOutPtr - pucToKey); uiFromKeyLen = uiBytesProcessed; rc = FERR_OK; break; } case FLM_BINARY_TYPE: { FLMUINT uiMaxLength; FLMBYTE * pucSrc = pucFromKey; uiMaxLength = ((uiFromKeyLen >> 1) < uiMaxKeySize) ? (FLMUINT)(uiFromKeyLen >> 1) : (FLMUINT)uiMaxKeySize; uiToKeyLen = 0; while( (uiToKeyLen < uiMaxLength) && ((ucTemp = *pucSrc) >= COLLS)) { // Take two bytes from source to make one byte in dest pucToKey[ uiToKeyLen++] = (FLMBYTE)(((ucTemp - COLLS) << 4) + (*(pucSrc + 1) - COLLS)); pucSrc += 2; } if( (uiToKeyLen < (uiFromKeyLen >> 1)) && (*pucSrc >= COLLS)) { rc = RC_SET( FERR_CONV_DEST_OVERFLOW); } else { rc = FERR_OK; uiFromKeyLen = uiToKeyLen << 1; // FLAIM has a bug where the binary fields don't have // the COLL_TRUNCATED value on truncated values. // The good news is that we know the true length of // binary fields. if( *pucSrc == COLL_TRUNCATED) { uiFromKeyLen++; bDataRightTruncated = TRUE; } else if( uiToKeyLen >= pIfd->uiLimit) { bDataRightTruncated = TRUE; } } break; } case FLM_CONTEXT_TYPE: default: uiFromKeyLen = 5; uiLongValue = f_bigEndianToUINT32( pucFromKey + 1); UD2FBA( (FLMUINT32)uiLongValue, pucToKey); uiToKeyLen = 4; break; } if( RC_BAD( rc)) { goto Exit; } // Allocate and Copy Value into the node if( uiToKeyLen) { FLMBYTE * pucValue; if( RC_BAD(rc = pKey->allocStorageSpace( pvField, uiDataType, uiToKeyLen, 0, 0, 0, &pucValue, NULL))) { goto Exit; } f_memcpy( pucValue, pucToKey, uiToKeyLen); } // Set first sub-string and truncated flags. if( (pIfd->uiFlags & IFD_SUBSTRING) && !bFirstSubstring) { pKey->setLeftTruncated( pvField, TRUE); } if( bDataRightTruncated) { pKey->setRightTruncated( pvField, TRUE); } } } // Compute variables for next section of compound key // Add 1 for compound marker if still is stuff in key if( uiFromRemaining != uiFromKeyLen) { uiFromKeyLen += (FLMUINT)(bIsAsianCompound ? (FLMUINT)2 : (FLMUINT)1); } pucFromKey += uiFromKeyLen; if( (uiFromKeyLen = (uiFromRemaining -= uiFromKeyLen)) == 0) { break; } while( ((pIfd->uiFlags & IFD_LAST) == 0) && (pIfd->uiCompoundPos == (pIfd+1)->uiCompoundPos)) { pIfd++; } if( pIfd->uiFlags & IFD_LAST) { break; } pIfd++; } // Check if we have one field left. if( (pIfd->uiFlags & IFD_LAST) == 0) { while( (pIfd->uiFlags & IFD_LAST) == 0) { pIfd++; } if( (pIfd->uiFlags & IFD_OPTIONAL) == 0) { if( RC_BAD( rc = flmBuildKeyPaths( pIfd, pIfd->uiFldNum, uiDataType, bFullFldPaths, pKey, &pvField))) { goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: This module will read all references of an index key. The references will be output number defined as REFS_PER_NODE ****************************************************************************/ RCODE flmBuildKeyPaths( IFD * pIfd, FLMUINT uiFldNum, FLMUINT uiDataType, FLMBOOL bFullFldPaths, FlmRecord * pKey, void ** ppvField) { RCODE rc = FERR_OK; void * pvField; void * pvParentField; void * pvChildField; FLMUINT * pFieldPath; FLMUINT uiTempDataType; FLMUINT uiFieldPos; FLMUINT uiTargetFieldID; if( !bFullFldPaths) { rc = pKey->insertLast( 1, uiFldNum, uiDataType, &pvField); goto Exit; } pFieldPath = pIfd->pFieldPathPToC; pvParentField = pKey->root(); uiFieldPos = 0; // Loop finding field matches. pvField = pKey->find( pvParentField, pFieldPath[ uiFieldPos]); if( pvField) { pvParentField = pvField; uiFieldPos++; uiTargetFieldID = pFieldPath[ uiFieldPos]; // Loop finding matching children from this point on. for( pvChildField = pKey->firstChild( pvParentField); pvChildField; ) { if( pKey->getFieldID( pvChildField) == uiTargetFieldID) { // On the child field? if( pFieldPath[ uiFieldPos + 1] == 0) { pvField = pvChildField; // Set the data type in case the data length is zero. pKey->allocStorageSpace( pvField, uiDataType, 0, 0, 0, 0, NULL, NULL); break; } pvParentField = pvChildField; uiFieldPos++; uiTargetFieldID = pFieldPath[ uiFieldPos]; pvChildField = pKey->firstChild( pvParentField); } else { pvChildField = pKey->nextSibling( pvChildField); } } } // Insert the rest of the field path down to the value field (uiFieldPos==0). uiTempDataType = FLM_CONTEXT_TYPE; for( ; pFieldPath[ uiFieldPos]; uiFieldPos++) { // Add the real data type for the last field, otherwise set as context. if( pFieldPath[ uiFieldPos + 1] == 0) { uiTempDataType = uiDataType; } if( RC_BAD( rc = pKey->insert( pvParentField, INSERT_LAST_CHILD, pFieldPath[ uiFieldPos], uiTempDataType, &pvField))) { goto Exit; } pvParentField = pvField; } Exit: *ppvField = pvField; return( rc); } /**************************************************************************** Desc: Compare only the leading left and right characters according to the many flags that are passed in. This routine operates to save and set state for the calling routine. TODO: This routine does NOT support Asian, Hebrew, or Arabic language collations. In addition, f_wpCheckDoubleCollation() is not called for other non-US lanagues. There is still a lot of work to do! This is our default US compare and it is not very good for JP. Return: Signed value of compare. <0 if less than, 0 if equal, >0 if greater than. Asian Notes: The asian compare takes two characters and may use one or both. This makes the algorithm complex so we may have to build full tests to see what we broke. NDS Notes: The right side (search string) is already formatted according to the space/dash rules of the syntax. ****************************************************************************/ FLMINT flmTextCompareSingleChar( FLMBYTE ** ppLeftText, // [in] Points to current value. // [out] Points to next character if equals. FLMUINT * puiLeftLen, // [in] Bytes remaining in text string. // [out] Bytes remaining in text string. FLMUINT * puiLeftWpChar2,// Second left character - for double characters FLMBYTE ** ppRightText, // [in] Points to current value. // [out] Points to next character if equals. FLMUINT * puiRightLen, // [in] Bytes remaining in text string. // [out] Bytes remaining in text string. FLMUINT * puiRightWpChar2,// Second right character - for double characters. FLMINT * piSubColCompare,//[in] If NULL disregard the subcollation // values if collation values are equal. // [out] If equals is returned, value is // set ONLY if the signed value of comparing // the sub-collation values is not equal. // See lengthy unicode compare below. FLMINT * piCaseCompare, // [in] If NULL disregard the case bits // if collation values are equal. Japanese // values are an exception to this rule. // [out] If equals is returned, value is // set ONLY if the signed value of comparing // the case values is not equal. FLMBOOL * pbHitWildCard, // [in] If NULL then do not look for wild // cards in the right text string. // [out] If non-null, a wild card (*,?) will // be looked for on the RIGHT SIDE ONLY. // If '?' is found 0 will be returned and // pointers are advanced. If '*' is found, // this value will be set to TRUE and the // right side is advanced. If no wild // card is found the value will not be set. FLMINT iCompareType, // COMPARE_COLLATION, COMPARE_COL_AND_SUBCOL, COMPARE_VALUE FLMUINT16 * pui16ColVal, // Needed for asian collation compare. FLMUINT uiFlags, // FLM_* flags FLMUINT uiLangId) // FLAIM/WordPerfect Lanaguge id. { FLMBYTE * pLeftText = *ppLeftText; FLMBYTE * pRightText = *ppRightText; FLMINT iCompare = 0; FLMUINT uiRightFlags = uiFlags; FLMUINT16 ui16LeftWPChar; FLMUINT16 ui16LeftUniChar; FLMUINT16 ui16RightWPChar; FLMUINT16 ui16RightUniChar; FLMUINT uiLeftValueLen; FLMUINT uiRightValueLen; FLMUINT16 ui16LeftCol; FLMUINT16 ui16RightCol; FLMUINT uiLeftWpChar2 = *puiLeftWpChar2; FLMUINT uiRightWpChar2 = *puiRightWpChar2; FLMBOOL bLeftTwoIntoOne; FLMBOOL bRightTwoIntoOne; // Get the next character from the TEXT string. NOTE: OEM characters // will be returned as a UNICODE character. A unicode character here // is a value that cannot be converted to the WP set (no good collation value).. uiLeftValueLen = flmTextGetValue( pLeftText, *puiLeftLen, &uiLeftWpChar2, uiFlags, &ui16LeftWPChar, &ui16LeftUniChar); uiRightValueLen = flmTextGetValue( pRightText, *puiRightLen, &uiRightWpChar2, uiRightFlags, &ui16RightWPChar, &ui16RightUniChar); // At this point, the double character, if any, should have been consumed. flmAssert( !uiLeftWpChar2 && !uiRightWpChar2); // Check for the following escape characters: "\\" "*" and "\\" "\\" if( ui16RightWPChar == ASCII_BACKSLASH) { if( pRightText[ uiRightValueLen ] == ASCII_BACKSLASH) { uiRightValueLen++; } else if( pRightText[ uiRightValueLen ] == ASCII_WILDCARD) { ui16RightWPChar = ASCII_WILDCARD; uiRightValueLen++; } } // Checking for wild cards in the right string? (Always a WP character) else if( pbHitWildCard) { // The '*' wildcard means to match zero or many characters. // The sick case of "A*B" compared to "A**B" should be considered. if( ui16RightWPChar == ASCII_WILDCARD) { // Eat all duplicate wild cards. while( pRightText[ uiRightValueLen] == ASCII_WILDCARD) { uiRightValueLen++; } // Advance the right value. Keep left value alone. // Return equals (default). *pbHitWildCard = TRUE; // Don't advance the left value. uiLeftValueLen = 0; uiLeftWpChar2 = *puiLeftWpChar2; goto Exit; } } // First section is to compare just WP values. if( ui16LeftWPChar && ui16RightWPChar) { FLMUINT16 ui16LeftSubCol; FLMUINT16 ui16RightSubCol; if (iCompareType == COMPARE_VALUE) { // Check the obvious case of equal WP values. if( ui16LeftWPChar != ui16RightWPChar) { iCompare = -1; } goto Exit; } // JP compare code. if (uiLangId >= FLM_FIRST_DBCS_LANG && uiLangId <= FLM_LAST_DBCS_LANG) { FLMUINT uiNextLeftLen; FLMUINT uiNextRightLen; FLMUINT16 ui16NextLeftWPChar; FLMUINT16 ui16NextRightWPChar; FLMUINT16 ui16ColVal = pui16ColVal ? *pui16ColVal : 0; FLMBYTE ucLeftCaseValue; FLMBYTE ucRightCaseValue; // Should have already consumed double character, if any flmAssert( !uiLeftWpChar2 && !uiRightWpChar2); uiNextLeftLen = flmTextGetValue( pLeftText+uiLeftValueLen, *puiLeftLen, &uiLeftWpChar2, uiFlags, &ui16NextLeftWPChar, &ui16LeftUniChar); uiNextRightLen = flmTextGetValue( pRightText+uiRightValueLen, *puiRightLen, &uiRightWpChar2, uiFlags, &ui16NextRightWPChar, &ui16RightUniChar); // nextL/R WPChar may be zero. if (flmAsiaGetCollation( ui16LeftWPChar, ui16NextLeftWPChar, ui16ColVal, &ui16LeftCol, &ui16LeftSubCol, &ucLeftCaseValue, FALSE) == 2) { uiLeftValueLen += uiNextLeftLen; } if (flmAsiaGetCollation( ui16RightWPChar, ui16NextRightWPChar, ui16ColVal, &ui16RightCol, &ui16RightSubCol, &ucRightCaseValue, FALSE) == 2) { uiRightValueLen += uiNextRightLen; } // Compare all of the stuff now. if (ui16LeftCol == ui16RightCol) { if( (iCompareType == COMPARE_COL_AND_SUBCOL) || (piSubColCompare && (*piSubColCompare == 0))) { if( ui16LeftSubCol != ui16RightSubCol) { if( iCompareType == COMPARE_COL_AND_SUBCOL) { iCompare = -1; goto Exit; } // At this point piSubColCompare cannot be NULL. *piSubColCompare = (ui16LeftSubCol < ui16RightSubCol) ? -1 : 1; // Write over the case compare value if( piCaseCompare ) { *piCaseCompare = *piSubColCompare; } } } if (iCompareType != COMPARE_COL_AND_SUBCOL) { // Check case? if (piCaseCompare && (*piCaseCompare == 0)) { if( ucLeftCaseValue != ucRightCaseValue) { *piCaseCompare = ucLeftCaseValue < ucRightCaseValue ? -1 : 1; } } } } else { iCompare = (ui16LeftCol < ui16RightCol) ? -1 : 1; } goto Exit; } flmAssert( !uiLeftWpChar2 && !uiRightWpChar2); if (uiLangId != FLM_US_LANG) { const FLMBYTE * pucTmp; pucTmp = pLeftText + uiLeftValueLen; uiLeftWpChar2 = f_wpCheckDoubleCollation( &ui16LeftWPChar, &bLeftTwoIntoOne, &pucTmp, uiLangId); uiLeftValueLen = (FLMUINT)(pucTmp - pLeftText); pucTmp = pRightText + uiRightValueLen; uiRightWpChar2 = f_wpCheckDoubleCollation( &ui16RightWPChar, &bRightTwoIntoOne, &pucTmp, uiLangId); uiRightValueLen = (FLMUINT)(pucTmp - pRightText); // See if we got the same double character if (uiLeftWpChar2 == uiRightWpChar2 && ui16LeftWPChar == ui16RightWPChar) { uiLeftWpChar2 = 0; uiRightWpChar2 = 0; goto Exit; } } else if (ui16LeftWPChar == ui16RightWPChar) { // Same WP character goto Exit; } ui16LeftCol = f_wpGetCollation( ui16LeftWPChar, uiLangId); // Handle two characters collating as one. if (uiLeftWpChar2 && bLeftTwoIntoOne) { ui16LeftCol++; } ui16RightCol = f_wpGetCollation( ui16RightWPChar, uiLangId); // Handle two characters collating as one. if (uiRightWpChar2 && bRightTwoIntoOne) { ui16RightCol++; } if( ui16LeftCol == ui16RightCol) { // Should we bother to check subcollation? - don't bother with 7-bit if( ((iCompareType == COMPARE_COL_AND_SUBCOL) || (piSubColCompare && (*piSubColCompare == 0))) && ((ui16LeftWPChar | ui16RightWPChar) & 0xFF00)) { ui16LeftSubCol = flmTextGetSubCol( ui16LeftWPChar, ui16LeftCol, uiLangId); ui16RightSubCol= flmTextGetSubCol( ui16RightWPChar, ui16RightCol, uiLangId); if (!piCaseCompare) { // If the sub-collation value is the original // character, it means that the collation could not // distinguish the characters and sub-collation is being // used to do it. However, this creates a problem when the // characters are the same character except for case. In that // scenario, we incorrectly return a not-equal when we are // doing a case-insensitive comparison. So, at this point, // we need to use the sub-collation for the upper-case of the // character instead of the sub-collation for the character // itself. if (ui16LeftSubCol == ui16LeftWPChar) { ui16LeftSubCol = flmTextGetSubCol( f_wpUpper( ui16LeftWPChar), ui16LeftCol, uiLangId); } if (ui16RightSubCol == ui16RightWPChar) { ui16RightSubCol= flmTextGetSubCol( f_wpUpper( ui16RightWPChar), ui16RightCol, uiLangId); } } // YES - go for it... if( ui16LeftSubCol != ui16RightSubCol) { if( iCompareType == COMPARE_COL_AND_SUBCOL) { iCompare = (ui16LeftSubCol < ui16RightSubCol) ? -1 : 1; goto Exit; } // At this point piSubColCompare cannot be NULL. *piSubColCompare = (ui16LeftSubCol < ui16RightSubCol) ? -1 : 1; // Write over the case compare value if( piCaseCompare ) { *piCaseCompare = *piSubColCompare; } } } if( iCompareType == COMPARE_COL_AND_SUBCOL) { goto Exit; } if( piCaseCompare && (*piCaseCompare == 0)) { // f_wpIsUpper() only returns FALSE (lower) or TRUE (not-lower) FLMBOOL bLeftUpper = f_wpIsUpper( ui16LeftWPChar); FLMBOOL bRightUpper = f_wpIsUpper( ui16RightWPChar); if (bLeftUpper != bRightUpper) { *piCaseCompare = !bLeftUpper ? -1 : 1; } } } else { iCompare = (ui16LeftCol < ui16RightCol) ? -1 : 1; } goto Exit; } if( ui16LeftUniChar && ui16RightUniChar) { // Compare two (non-convertable) UNICODE values. // Check the obvious case of equal UNICODE values. if( ui16LeftUniChar == ui16RightUniChar) { goto Exit; } // Compare subcollation or compare value? if( iCompareType != COMPARE_COLLATION) { iCompare = -1; goto Exit; } // For non-asian - we store these values in the sub-collcation area. // We should return the differece in sub-collation values - but this // may not work for all compares. // // For asian compares, most values we have a collation value. // This is a BIG differece in comparing asian values. // // If we want sub-collation compare then set it, otherwise set main // iCompare value. if( piSubColCompare ) { if( *piSubColCompare == 0) { *piSubColCompare = ui16LeftUniChar < ui16RightUniChar ? -1 : 1; } } else { // Treat as the collation value - this is different than the index. iCompare = ui16LeftUniChar < ui16RightUniChar ? -1 : 1; } goto Exit; } // Compare subcollation or compare value? if( iCompareType != COMPARE_COLLATION) { iCompare = -1; goto Exit; } // Check for no left character. if( !ui16LeftWPChar && !ui16LeftUniChar) { // No left character. check if no right character. if( ui16RightWPChar || ui16RightUniChar) { iCompare = -1; } } // Check for no right character. else if( !ui16RightWPChar && !ui16RightUniChar) { iCompare = 1; } // What remains is one WP char and one Unicode char. // Remember the sub-collation comment above. Some WP char may not // have a collation value (COLS0) so in US sort these values may be // equal and have different sub-collation values. YECH!!!! // // The unicode value will always have collation value of COLS0 (0xFF) // and subcollation value of 11110 [unicodeValue] // The WP value could be anything & if collation value is COLS0 will // have a subcollation value os 1110 [WPValue] // // So, we have to check to see of the WP collation value is COLS0. // If not iCompare is used. If both represent high collation then // the WP value will always have a lower sub-collation value. // // The (not so obvious) code would be to code up... // iCompare = ui16LeftWPChar ? -1 : 1; // if we didn't care about sub-collation (and we may not care). // // This is easier to over code than have ?: operators for the two cases. else if( ui16LeftWPChar) { // Remember - unicode subcol is always COLS0. if( f_wpGetCollation( ui16LeftWPChar, uiLangId) == COLS0) { if( piSubColCompare && (*piSubColCompare == 0)) { *piSubColCompare = -1; } } else { iCompare = -1; } } else { // left=unicode, right=WP // Remember - unicode subcol is always COLS0 for non-asian. if( f_wpGetCollation( ui16RightWPChar, uiLangId) == COLS0) { if( piSubColCompare && (*piSubColCompare == 0)) { *piSubColCompare = 1; } } else { iCompare = 1; } } Exit: if( !iCompare) { // Position to the next values if equal *puiLeftLen -= uiLeftValueLen; *ppLeftText = pLeftText + uiLeftValueLen; *puiLeftWpChar2 = uiLeftWpChar2; *puiRightLen -= uiRightValueLen; *ppRightText = pRightText + uiRightValueLen; *puiRightWpChar2 = uiRightWpChar2; } return( iCompare); } /************************************************************************** Desc: Get the Flaim collating string and convert back to a text string Ret: Length of new wpStr Notes: Allocates the area for the word string buffer if will be over 256. ***************************************************************************/ FLMUINT FColStrToText( FLMBYTE * fColStr, // Points to the Flaim collated string FLMUINT * fcStrLenRV, // Length of the Flaim collated string FLMBYTE * textStr, // Output string to build - TEXT string FLMUINT fWPLang, // FLAIM WP language number FLMBYTE * postBuf, // Lower/upper POST buffer or NULL FLMUINT * postBytesRV, // Return next position to use in postBuf FLMBOOL * pbDataTruncated, // Sets to TRUE if data had been truncated FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring { #define LOCAL_CHARS 150 FLMBYTE wordStr[LOCAL_CHARS * 2 + LOCAL_CHARS / 5]; // Sample + 20% FLMBYTE * wsPtr = NULL; FLMBYTE * wsAllocatedWsPtr = NULL; FLMUINT wsLen; FLMUINT textLen; FLMBYTE * textPtr; if (*fcStrLenRV > LOCAL_CHARS) // If won't fit allocate 1280 { if (RC_BAD( f_alloc( MAX_KEY_SIZ * 2, &wsPtr))) { return (0); } wsAllocatedWsPtr = wsPtr; } else { wsPtr = wordStr; } if ((fWPLang >= FLM_FIRST_DBCS_LANG) && (fWPLang <= FLM_LAST_DBCS_LANG)) { wsLen = AsiaConvertColStr( fColStr, fcStrLenRV, wsPtr, pbDataTruncated, pbFirstSubstring); if (postBuf) { FLMUINT postBytes = *postBytesRV + 2; // Skip past marker // may change wsLen postBytes += AsiaParseCase( wsPtr, &wsLen, &postBuf[postBytes]); *postBytesRV = postBytes; } } else { wsLen = FWWSGetColStr( fColStr, fcStrLenRV, wsPtr, fWPLang, pbDataTruncated, pbFirstSubstring); // If a post buffer is sent - turn unflagged chars to lower case if (postBuf) { FLMUINT postBytes = *postBytesRV; // Check if mixed case chars follow and always increment // postBytes // if (postBuf[postBytes++] == (COLL_MARKER | SC_MIXED)) { postBytes += f_wpToMixed( wsPtr, wsLen, &postBuf[postBytes], fWPLang); } *postBytesRV = postBytes; } } // Copy word string to TEXT string area wsLen >>= 1; // Convert # of bytes to # of words textPtr = textStr; while (wsLen--) { register FLMBYTE ch; register FLMBYTE cSet; // Put the character in a local variable for speed ch = *wsPtr++; cSet = *wsPtr++; if ((!cSet) && (ch <= 127)) { // Character set zero only needs one byte if the character is <= // 127. Otherwise, it is handled like all other extended // characters below. // *textPtr++ = ch; } // If the character set is > 63 it takes three bytes to store, // otherwise only two bytes are needed. else if (cSet < 63) { *textPtr++ = (FLMBYTE) (CHAR_SET_CODE | cSet); *textPtr++ = ch; } else if (cSet == 0xFF && ch == 0xFF) { *textPtr++ = UNICODE_CODE; *textPtr++ = *(wsPtr + 1); // Character set *textPtr++ = *wsPtr; // Character wsPtr += 2; wsLen--; // Skip past 4 bytes for UNICODE } else { *textPtr++ = EXT_CHAR_CODE; *textPtr++ = cSet; *textPtr++ = ch; } } textLen = (textPtr - textStr); // Compute total length if (wsAllocatedWsPtr != NULL) { f_free( &wsAllocatedWsPtr); } return (textLen); } /**************************************************************************** Desc: Compare two entire strings. There is some debate how this routine should compare the sub-collation values when wild cards are used. THIS DOES NOT ALLOW WILD CARDS. Return: Signed value of compare. <0 if less than, 0 if equal, >0 if greater than The case of returning 1 may be in using wild cards which only need to return a does not match value. ****************************************************************************/ FLMINT flmTextCompare( FLMBYTE * pLeftBuf, FLMUINT uiLeftLen, FLMBYTE * pRightBuf, FLMUINT uiRightLen, FLMUINT uiFlags, FLMUINT uiLang) { FLMINT iCompare = 0; FLMINT iSubColCompare = 0; FLMINT * pSubColCompare; FLMINT iCaseCompare = 0; FLMINT * pCaseCompare; FLMUINT uiLeadingSpace; FLMUINT uiTrailingSpace; FLMUINT16 ui16ColVal = 0; FLMUINT16 ui16WPChar; FLMUINT16 ui16UniChar; FLMUINT uiLeftWpChar2 = 0; FLMUINT uiRightWpChar2 = 0; uiTrailingSpace = uiLeadingSpace = (uiFlags & FLM_COMP_COMPRESS_WHITESPACE) ? FLM_COMP_NO_WHITESPACE : 0; pCaseCompare = (uiFlags & FLM_COMP_CASE_INSENSITIVE) ? NULL : &iCaseCompare; pSubColCompare = &iSubColCompare; // Handle NULL buffers first. if (!pLeftBuf) { if (pRightBuf) { iCompare = -1; } goto Exit; } while ((uiLeftLen || uiLeftWpChar2) && (uiRightLen || uiRightWpChar2)) { if ((iCompare = flmTextCompareSingleChar( &pLeftBuf, &uiLeftLen, &uiLeftWpChar2, &pRightBuf, &uiRightLen, &uiRightWpChar2, pSubColCompare, pCaseCompare, NULL_WILD_CARD_CHECK, COMPARE_COLLATION, &ui16ColVal, uiFlags | uiLeadingSpace, uiLang)) != 0) { goto Exit; } uiLeadingSpace = 0; } // EQUAL - as far as the collation values are concerned and one // or both of the strings is at the end. if (uiLeftLen || uiLeftWpChar2) { uiLeftLen -= flmTextGetValue( pLeftBuf, uiLeftLen, &uiLeftWpChar2, uiFlags | uiTrailingSpace, &ui16WPChar, &ui16UniChar); if (uiLeftLen || ui16WPChar || ui16UniChar) { iCompare = 1; } } else if (uiRightLen || uiRightWpChar2) { uiRightLen -= flmTextGetValue( pRightBuf, uiRightLen, &uiRightWpChar2, uiFlags | uiTrailingSpace, &ui16WPChar, &ui16UniChar); if (uiRightLen || ui16WPChar || ui16UniChar) { iCompare = -1; } } if (iCompare == 0) { // All collation bytes equal - return subcollation/case difference. iCompare = (iSubColCompare != 0) ? iSubColCompare : iCaseCompare; } Exit: return iCompare; } /**************************************************************************** Desc: Match two entire strings. Return: FLM_TRUE or FLM_FALSE Notes: This code calls the collation routine because in the future there will be equal conditions with different unicode characters. DOCUMENTATION DEALING WITH WILD CARDS AND SPACE RULES. The space rules are not obvious when dealing with wild cards. This will outline the rules that are being applied so that we can do a regression test when this code changes. Rule #1: Return same result if leading or trailing wild card is added. The underscore is also the space character in these examples and the MIN_SPACES rule is being applied. Format: DataString Operator SearchString Example: if A == A A_ == A A == A_ A_ == A_ then A == A* A_ == A* A == A_* A_ == A_* and A == *A A_ == *A A == *A_ A_ == *A_ and A == *A* A_ == *A* A == *A_* A_ == *A_* where 'A' represent a string of any characters. Strictly put, the query Field == A_* can be broken down to Field == A || Field == A_* where the space after 'A' should not be treated as a trailing space. In addition we can apply the space before the string with the same results, but we are not going to handle the case of *_A correctly. This is because the query *_A should be expanded to Field == A || Field == *_A where the space before 'A' should not be treated as a leading space. When we need to find "_A" in a search string then we will expand the query to handle this. Rule #2: The spaces before a trailing truncation are NOT to be treated as trailing spaces if there are remaining bytes in the data string. Example: (A_B == A_*) but (AB != A_*) Rule #3: Space value(s) without anything other value are equal to no values. Example: (" " == "") Rule #4: Trim leading/trailing spaces before and after wild cards. SMI does this when formatting. _* and *_ same as * so A == _* and A = *_ but A != *_* Additional wildcard cases to test for: Wildcard cases to handle. (ABBBBC == A*BC) Hits the goto Compare_Again case three times. (ABBBBD != A*B) Stuff still remains in dataString (ABBBBC != A*BCD) Stuff still remains in searchString ****************************************************************************/ FLMUINT flmTextMatch( FLMBYTE * pLeftBuf, FLMUINT uiLeftLen, FLMBYTE * pRightBuf, FLMUINT uiRightLen, FLMUINT uiFlags, FLMBOOL bLeadingWildCard, FLMBOOL bTrailingWildCard, FLMUINT uiLang) { FLMINT iCompare = 0; FLMUINT uiLeadingSpace; FLMUINT uiTrailingSpace; FLMBOOL bHitWildCard; FLMBOOL bHasWildCardPos; FLMBOOL * pbHitWildCard; FLMUINT uiValueLen; FLMUINT16 ui16WPChar; FLMUINT16 ui16UniChar; FLMUINT16 ui16Tmp1; FLMUINT16 ui16Tmp2; FLMINT iCompareType; FLMUINT uiLeftWpChar2 = 0; FLMUINT uiRightWpChar2 = 0; FLMBYTE * pLWCPLeftBuf = NULL; FLMBYTE * pLWCPRightBuf = NULL; FLMUINT uiLWCPLeftLen = 0; FLMUINT uiLWCPRightLen = 0; FLMUINT uiLWCPLeftWpChar2 = 0; FLMUINT uiLWCPRightWpChar2 = 0; if( uiFlags & FLM_COMPARE_COLLATED_VALUES) { iCompareType = COMPARE_COLLATION; } else { iCompareType = (uiFlags & FLM_COMP_CASE_INSENSITIVE) ? COMPARE_COL_AND_SUBCOL : COMPARE_VALUE; } // Handle NULL buffers first - don't test for zero length values yet. if (!pLeftBuf) { if (pRightBuf) { iCompare = -1; } goto Exit; } bHitWildCard = bHasWildCardPos = FALSE; uiLeadingSpace = uiTrailingSpace = (uiFlags & FLM_COMP_COMPRESS_WHITESPACE) ? FLM_COMP_NO_WHITESPACE : 0; pbHitWildCard = (uiFlags & FLM_COMP_WILD) ? &bHitWildCard : NULL; if (bLeadingWildCard) { goto Leading_Wild_Card; } while (!iCompare && (uiLeftLen || uiLeftWpChar2) && (uiRightLen || uiRightWpChar2)) { iCompare = flmTextCompareSingleChar( &pLeftBuf, &uiLeftLen, &uiLeftWpChar2, &pRightBuf, &uiRightLen, &uiRightWpChar2, NULL_SUB_COL_CHECK, NULL_CASE_CHECK, pbHitWildCard, iCompareType, NULL, uiFlags | uiLeadingSpace, uiLang); uiLeadingSpace = 0; if (bHitWildCard) { Leading_Wild_Card: bHitWildCard = FALSE; bHasWildCardPos = FALSE; // Turn off last wildcard. // If right side is done, we are done. if (!uiRightLen && !uiRightWpChar2) { uiLeftLen = 0; uiLeftWpChar2 = 0; break; } // Save state on the RIGHT to handle the sick case of search key // "b*aH" being able to match "baaaaaaaaaH" (Lambda Case) // LWCP = LastWildCardPosition pLWCPRightBuf = pRightBuf; uiLWCPRightLen = uiRightLen; uiLWCPRightWpChar2 = uiRightWpChar2; // Find first matching character on the left side. Compare_Again: iCompare = -1; while (iCompare && (uiLeftLen || uiLeftWpChar2)) { iCompare = flmTextCompareSingleChar( &pLeftBuf, &uiLeftLen, &uiLeftWpChar2, &pRightBuf, &uiRightLen, &uiRightWpChar2, NULL_SUB_COL_CHECK, NULL_CASE_CHECK, NULL_WILD_CARD_CHECK, iCompareType, NULL, uiFlags | uiLeadingSpace, uiLang); uiLeadingSpace = 0; // Done with the right side? Return iCompare value. if (!uiRightLen && !uiRightWpChar2) { break; } // Values different and still have stuff on left? if (iCompare && (uiLeftLen || uiLeftWpChar2)) { // Advance the left if there is anything left uiValueLen = flmTextGetValue( pLeftBuf, uiLeftLen, &uiLeftWpChar2, uiFlags, &ui16Tmp1, &ui16Tmp2); pLeftBuf += uiValueLen; uiLeftLen -= uiValueLen; } } // Save state on the LEFT if (uiLeftLen || uiLeftWpChar2) { pLWCPLeftBuf = pLeftBuf; uiLWCPLeftLen = uiLeftLen; uiLWCPLeftWpChar2 = uiLeftWpChar2; bHasWildCardPos = TRUE; } // EQUAL - as far as the collation values are concerned. } } if (iCompare == 0) { // In here because LEFT and/or RIGHT are out of bytes. // Check for trailing spaces if MIN_SPACES. if (uiLeftLen || uiLeftWpChar2) { if (!bTrailingWildCard) { uiLeftLen -= flmTextGetValue( pLeftBuf, uiLeftLen, &uiLeftWpChar2, uiFlags | uiTrailingSpace, &ui16WPChar, &ui16UniChar); if (uiLeftLen || ui16WPChar || ui16UniChar) { iCompare = 1; } } } else if (uiRightLen || uiRightWpChar2) { uiRightLen -= flmTextGetValue( pRightBuf, uiRightLen, &uiRightWpChar2, uiFlags | uiTrailingSpace, &ui16WPChar, &ui16UniChar); // Equals if right just had a trailing wild card. (else case) if (uiRightLen || !pbHitWildCard || ui16WPChar != '*') { if (uiRightLen || ui16WPChar || ui16UniChar) { iCompare = -1; } } } } // Handle the embedded wild card case. if (iCompare != 0 && bHasWildCardPos) { // Restore wild card state. pLeftBuf = pLWCPLeftBuf; uiLeftLen = uiLWCPLeftLen; uiLeftWpChar2 = uiLWCPLeftWpChar2; pRightBuf = pLWCPRightBuf; uiRightLen = uiLWCPRightLen; uiRightWpChar2 = uiLWCPRightWpChar2; bHasWildCardPos = FALSE; goto Compare_Again; } Exit: return (!iCompare ? FLM_TRUE : FLM_FALSE); } libflaim-4.9.966/src/fslfile.cpp0000644000175000017500000002673510510774540020021 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for accessing/updating an LFILE structure. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fslfile.cpp 12285 2006-01-19 14:54:29 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmLFileToBuffer( LFILE * pLFile, FLMBYTE * pucBuf); /*************************************************************************** Desc: Searches a block for an empty LFILE slot. This is called whenever a new logical file is create so we re-use the slots. Supports VER11 and VER15 formats ****************************************************************************/ FINLINE FLMUINT flmLFileFindEmpty( FLMBYTE * pucBlk) { FLMUINT uiPos = BH_OVHD; FLMUINT uiEndPos = (FLMUINT) FB2UW( &pucBlk[ BH_ELM_END ]); while( (uiPos < uiEndPos ) && (pucBlk[ uiPos + LFH_TYPE_OFFSET ] != LF_INVALID )) { uiPos += LFH_SIZE; } return( (uiPos < uiEndPos) ? uiPos : 0); } /*************************************************************************** Desc: Initialize an existing LFILE. Right now the only LFILE data structure is a b-tree so the root block will be allocated and initialized. ****************************************************************************/ RCODE flmLFileInit( FDB * pDb, LFILE * pLFile) { RCODE rc = FERR_OK; SCACHE * pSCache; FLMBYTE * pucBlk; FLMBOOL bReleaseCache = FALSE; FLMUINT uiBlkAddress; FLMUINT uiBlkPos; if( RC_BAD( rc = flmLFileRead( pDb, pLFile))) { goto Exit; } if( pLFile->uiRootBlk != BT_END) { goto Exit; } if( RC_BAD( rc = ScaCreateBlock( pDb, pLFile, &pSCache))) { goto Exit; } bReleaseCache = TRUE; pucBlk = pSCache->pucBlk; uiBlkAddress = GET_BH_ADDR( pucBlk); // Have the logical file header point to the root block pLFile->uiRootBlk = uiBlkAddress; // Initialize some other fields in the block header & log it // before modifying. The only type supported at this time is // a b-tree structure pucBlk[ BH_TYPE ] = BHT_LEAF + BHT_ROOT_BLK; pucBlk[ BH_LEVEL] = 0; UD2FBA( BT_END, &pucBlk[ BH_PREV_BLK ]); UD2FBA( BT_END, &pucBlk[ BH_NEXT_BLK ]); UW2FBA( (FLMUINT16) pLFile->uiLfNum, &pucBlk[ BH_LOG_FILE_NUM ]); uiBlkPos = BH_OVHD; // Check for encrypted index if (pLFile->uiLfType == LF_INDEX) { IXD * pIxd; if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pLFile->uiLfNum, NULL, &pIxd, TRUE))) { goto Exit; } } // Add the next DRN element if( pLFile->uiLfType == LF_CONTAINER) { FLMBYTE * pElm = &pucBlk[ BH_OVHD ]; // Set the nextDRN value in the block *pElm = BBE_FIRST_FLAG | BBE_LAST_FLAG; pElm[ BBE_KL ] = 4; pElm[ BBE_RL ] = 4; UD2FBA( (FLMUINT32)DRN_LAST_MARKER, &pElm[ BBE_KEY ]); UD2FBA( (FLMUINT32)pLFile->uiNextDrn, &pElm[ BBE_KEY + 4 ]); uiBlkPos += DRN_LAST_MARKER_LEN; } // Write the Last element marker pucBlk[ uiBlkPos] = BBE_FIRST_FLAG | BBE_LAST_FLAG; pucBlk[ uiBlkPos + BBE_KL] = pucBlk[ uiBlkPos + BBE_RL] = 0; uiBlkPos += BBE_LEM_LEN; UW2FBA( (FLMUINT16)uiBlkPos, &pucBlk[ BH_ELM_END]); // Release the cache block, because we are done with it ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; // Update the logical save area and return if( RC_BAD( rc = flmLFileWrite( pDb, pLFile))) { goto Exit; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Retrieves the logical file header record from disk & updates LFILE. Called when it is discovered that the LFH for a particular logical file is out of date. *****************************************************************************/ RCODE flmLFileRead( FDB * pDb, LFILE * pLFile) { RCODE rc = FERR_OK; SCACHE * pSCache; FLMBOOL bReleaseCache = FALSE; // Read in the block containing the logical file header if (RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LFH_BLK, pLFile->uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; // Copy the LFH from the block to the LFD if( RC_BAD( rc = flmBufferToLFile( &pSCache->pucBlk[ pLFile->uiOffsetInBlk], pLFile, pLFile->uiBlkAddress, pLFile->uiOffsetInBlk))) { goto Exit; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE flmBufferToLFile( FLMBYTE * pucBuf, LFILE * pLFile, FLMUINT uiBlkAddress, FLMUINT uiOffsetInBlk) { RCODE rc = FERR_OK; pLFile->uiBlkAddress = uiBlkAddress; pLFile->uiOffsetInBlk= uiOffsetInBlk; if( (pLFile->uiLfType = (FLMUINT)pucBuf[ LFH_TYPE_OFFSET]) == LF_INVALID) { pLFile->uiLfType = LF_INVALID; goto Exit; } pLFile->uiLfNum = (FLMUINT)FB2UW( &pucBuf[ LFH_LF_NUMBER_OFFSET]); pLFile->uiRootBlk = (FLMUINT)FB2UD( &pucBuf[ LFH_ROOT_BLK_OFFSET]); pLFile->uiNextDrn = (FLMUINT)FB2UD( &pucBuf[ LFH_NEXT_DRN_OFFSET]); Exit: return( rc); } /*************************************************************************** Desc: Update the LFILE data on disk *****************************************************************************/ RCODE flmLFileWrite( FDB * pDb, LFILE * pLFile) { RCODE rc = FERR_OK; SCACHE * pSCache; FLMBOOL bReleaseCache = FALSE; // Read in the block containing the logical file header if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LFH_BLK, pLFile->uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; // Log the block before modifying it if( RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } // Now modify the block and set its status to dirty if( RC_BAD( rc = flmLFileToBuffer( pLFile, &pSCache->pucBlk[ pLFile->uiOffsetInBlk]))) { goto Exit; } Exit: if( bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Copy the data from the LFILE OUT into the disk block buffer. Supports VER11 and VER15 formats. *****************************************************************************/ FSTATIC RCODE flmLFileToBuffer( LFILE * pLFile, FLMBYTE * pucBuf) { RCODE rc = FERR_OK; // If deleted, fill with 0xFF, except for type - it is set below if( pLFile->uiLfType == LF_INVALID) { f_memset( pucBuf, 0xFF, LFH_SIZE ); pucBuf[ LFH_TYPE_OFFSET ] = LF_INVALID; goto Exit; } UW2FBA( (FLMUINT16) pLFile->uiLfNum, &pucBuf[ LFH_LF_NUMBER_OFFSET]); pucBuf[ LFH_TYPE_OFFSET] = (FLMBYTE) pLFile->uiLfType; UD2FBA( (FLMUINT32)pLFile->uiRootBlk, &pucBuf[ LFH_ROOT_BLK_OFFSET]); UD2FBA( (FLMUINT32)pLFile->uiNextDrn, &pucBuf[ LFH_NEXT_DRN_OFFSET]); // Set these for backwards compatibility. pucBuf[ LFH_STATUS_OFFSET] = 0; pucBuf[ LFH_MIN_FILL_OFFSET] = (FLMBYTE)(FFILE_MIN_FILL * 128 / 100); pucBuf[ LFH_MAX_FILL_OFFSET] = (FLMBYTE)(FFILE_MAX_FILL * 128 / 100); Exit: return( rc); } /*************************************************************************** Desc: Creates and initializes a LFILE structure on disk and in memory. *****************************************************************************/ RCODE flmLFileCreate( FDB * pDb, LFILE * pLFile, FLMUINT uiLfNum, FLMUINT uiLfType) { RCODE rc = FERR_OK; SCACHE * pNewSCache; SCACHE * pSCache = NULL; FLMBYTE * pucBlk; FLMUINT uiBlkAddress = 0; FLMUINT uiNextBlkAddress; FLMUINT uiEndPos = 0; FLMUINT uiPos = 0; FLMBOOL bReleaseCache2 = FALSE; FLMBOOL bReleaseCache = FALSE; // Find an available slot to create the LFH -- follow the linked list // of LFH blocks to find one. Supports uiFirstLFHBlkAddr to be BT_END // so this routine can create the first block. for( uiNextBlkAddress = pDb->pFile->FileHdr.uiFirstLFHBlkAddr, pucBlk = NULL; (uiNextBlkAddress != BT_END) && (uiNextBlkAddress != 0 ); ) { if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } uiBlkAddress = uiNextBlkAddress; if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_LFH_BLK, uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; pucBlk = pSCache->pucBlk; uiNextBlkAddress = FB2UD( &pucBlk[ BH_NEXT_BLK]); uiEndPos = (FLMUINT) FB2UW( &pucBlk[ BH_ELM_END]); if( (uiPos = flmLFileFindEmpty( pucBlk)) != 0) { break; } } // pucBlk will be defined unless the file header is corrupt if( !pucBlk) { rc = RC_SET( FERR_DATA_ERROR); goto Exit; } // If we did not find a deleted slot we can use, see if there // is room for a new logical file in the last block // in the chain. If not, allocate a new block. if( !uiPos) { uiEndPos = (FLMUINT)FB2UW( &pucBlk[ BH_ELM_END]); // Allocate a new block? if( uiEndPos + LFH_SIZE >= pDb->pFile->FileHdr.uiBlockSize) { if( RC_BAD( rc = ScaCreateBlock( pDb, NULL, &pNewSCache))) { goto Exit; } bReleaseCache2 = TRUE; pucBlk = pNewSCache->pucBlk; uiNextBlkAddress = GET_BH_ADDR( pucBlk); // Modify the new block's next pointer and other fields UD2FBA( (FLMUINT32) BT_END, &pucBlk[ BH_NEXT_BLK]); pucBlk[ BH_TYPE] = BHT_LFH_BLK; pucBlk[ BH_LEVEL] = 0; UD2FBA( uiBlkAddress, &pucBlk[ BH_PREV_BLK]); UW2FBA( BH_OVHD, &pucBlk[ BH_ELM_END]); UW2FBA( 0, &pucBlk[ BH_LOG_FILE_NUM]); if( RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; UD2FBA( uiNextBlkAddress, &pucBlk[ BH_NEXT_BLK]); // Set everything up so we are pointing to the new block ScaReleaseCache( pSCache, FALSE); pSCache = pNewSCache; bReleaseCache2 = FALSE; pucBlk = pSCache->pucBlk; uiEndPos = (FLMUINT) FB2UW( &pucBlk[ BH_ELM_END]); uiBlkAddress = uiNextBlkAddress; } // Modify the end of block pointer -- log block before modifying uiPos = uiEndPos; uiEndPos += LFH_SIZE; } // Call memset to ensure unused bytes are zero. // pucBlk, uiPos and uiEndPos should ALL be set. if( RC_BAD( rc = ScaLogPhysBlk( pDb, &pSCache))) { goto Exit; } pucBlk = pSCache->pucBlk; f_memset( &pucBlk[ uiPos], 0, LFH_SIZE); UW2FBA( (FLMUINT16)uiEndPos, &pucBlk[ BH_ELM_END]); // Done with block in this routine ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; // Set the variables in the LFILE structure to later save to disk pLFile->uiLfNum = uiLfNum; pLFile->uiLfType = uiLfType; pLFile->uiBlkAddress = uiBlkAddress; pLFile->uiOffsetInBlk = uiPos; pLFile->uiRootBlk = (FLMUINT)BT_END; pLFile->uiNextDrn = 1; pLFile->pIxd = NULL; if( RC_BAD( rc = flmLFileWrite( pDb, pLFile))) { goto Exit; } Exit: if( bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if( bReleaseCache2) { ScaReleaseCache( pNewSCache, FALSE); } return( rc); } libflaim-4.9.966/src/rcache.cpp0000644000175000017500000023355110510774540017616 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Record caching // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: rcache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: ****************************************************************************/ class F_RCacheRelocator : public IF_Relocator { public: F_RCacheRelocator() { } virtual ~F_RCacheRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: ****************************************************************************/ class F_RecRelocator : public IF_Relocator { public: F_RecRelocator() { } virtual ~F_RecRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: ****************************************************************************/ class F_RecBufferRelocator : public IF_Relocator { public: F_RecBufferRelocator() { } virtual ~F_RecBufferRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Extended record object for accessing private members of FlmRecord ****************************************************************************/ struct FlmRecordExt { static FINLINE void clearCached( FlmRecord * pRec) { pRec->clearCached(); } static FINLINE void setCached( FlmRecord * pRec) { pRec->setCached(); } static FINLINE void setReadOnly( FlmRecord * pRec) { pRec->setReadOnly(); } static FINLINE FLMINT Release( FlmRecord * pRec, FLMBOOL bMutexLocked) { return( pRec->Release( bMutexLocked)); } static FINLINE void setOldVersion( FlmRecord * pRec) { pRec->setOldVersion(); } static FINLINE void clearOldVersion( FlmRecord * pRec) { pRec->clearOldVersion(); } static FINLINE FLMUINT getFlags( FlmRecord * pRec) { return( pRec->m_uiFlags); } }; // Functions for calculating minimum and maximum record counts for a // given hash table size. #define FLM_RCA_MIN_REC_CNT(uiHashTblSz) \ ((uiHashTblSz) / 4) #define FLM_RCA_MAX_REC_CNT(uiHashTblSz) \ ((uiHashTblSz) * 4) // Hash function for hashing to records in record cache. #define FLM_RCA_HASH( uiDrn) \ (RCACHE **)(&(gv_FlmSysData.RCacheMgr.ppHashBuckets[(uiDrn) & \ (gv_FlmSysData.RCacheMgr.uiHashMask)])) // Local functions FSTATIC void flmRcaFreePurged( RCACHE * pRCache); FSTATIC void flmRcaFreeCache( RCACHE * pRCache, FLMBOOL bPutInPurgeList); FSTATIC FLMUINT flmRcaGetBestHashTblSize( FLMUINT uiCurrRecCount); FSTATIC RCODE flmRcaRehash( void); FSTATIC RCODE flmRcaSetMemLimit( FLMUINT uiMaxCacheBytes); FSTATIC void flmRcaNotify( F_NOTIFY_LIST_ITEM * pNotify, RCACHE * pUseRCache, RCODE NotifyRc); FSTATIC RCODE flmRcaAllocCacheStruct( RCACHE ** ppRCache); FSTATIC void flmRcaFreeCacheStruct( RCACHE ** ppRCache); FSTATIC void flmRcaSetRecord( RCACHE * pRCache, FlmRecord * pNewRecord); FSTATIC void flmRcaLinkIntoRCache( RCACHE * pNewerRCache, RCACHE * pOlderRCache, RCACHE * pRCache, FLMBOOL bLinkAsMRU); FSTATIC void flmRcaLinkToFFILE( RCACHE * pRCache, FFILE * pFile, FDB * pDb, FLMUINT uiLowTransId, FLMBOOL bMostCurrent); #ifdef FLM_DEBUG FSTATIC RCODE flmRcaCheck( FDB * pDb, FLMUINT uiContainer, FLMUINT uiDrn); #endif /**************************************************************************** Desc: This inline assumes that the global mutex is locked, because it potentially updates the cache usage statistics. ****************************************************************************/ FINLINE void flmRcaSetTransID( RCACHE * pRCache, FLMUINT uiNewTransID) { if (pRCache->uiHighTransId == 0xFFFFFFFF && uiNewTransID != 0xFFFFFFFF) { FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) ? pRCache->pRecord->getTotalMemory() : (FLMUINT)0) + sizeof( RCACHE); gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiSize; gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; if( pRCache->pRecord) { FlmRecordExt::setOldVersion( pRCache->pRecord); } } else if (pRCache->uiHighTransId != 0xFFFFFFFF && uiNewTransID == 0xFFFFFFFF) { FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) ? pRCache->pRecord->getTotalMemory() : (FLMUINT)0) + sizeof( RCACHE); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiSize); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiSize; gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; if( pRCache->pRecord) { FlmRecordExt::clearOldVersion( pRCache->pRecord); } } pRCache->uiHighTransId = uiNewTransID; } /**************************************************************************** Desc: This routine links a record into the global list as the MRU record. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToGlobalAsMRU( RCACHE * pRCache) { pRCache->pPrevInGlobal = NULL; if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pMRURecord) != NULL) { gv_FlmSysData.RCacheMgr.pMRURecord->pPrevInGlobal = pRCache; } else { gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; } gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; } /**************************************************************************** Desc: This routine links a record into the global list as the LRU record. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToGlobalAsLRU( RCACHE * pRCache) { pRCache->pNextInGlobal = NULL; if ((pRCache->pPrevInGlobal = gv_FlmSysData.RCacheMgr.pLRURecord) != NULL) { gv_FlmSysData.RCacheMgr.pLRURecord->pNextInGlobal = pRCache; } else { gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; } gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; } /**************************************************************************** Desc: Moves a record one step closer to the MRU slot in the global list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaStepUpInGlobalList( RCACHE * pRCache) { RCACHE * pPrevRCache; if( (pPrevRCache = pRCache->pPrevInGlobal) != NULL) { if( pPrevRCache->pPrevInGlobal) { pPrevRCache->pPrevInGlobal->pNextInGlobal = pRCache; } else { gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; } pRCache->pPrevInGlobal = pPrevRCache->pPrevInGlobal; pPrevRCache->pPrevInGlobal = pRCache; pPrevRCache->pNextInGlobal = pRCache->pNextInGlobal; if( pRCache->pNextInGlobal) { pRCache->pNextInGlobal->pPrevInGlobal = pPrevRCache; } else { gv_FlmSysData.RCacheMgr.pLRURecord = pPrevRCache; } pRCache->pNextInGlobal = pPrevRCache; } } /**************************************************************************** Desc: This routine unlinks a record from the global list This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaUnlinkFromGlobal( RCACHE * pRCache) { if (pRCache->pNextInGlobal) { pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; } else { gv_FlmSysData.RCacheMgr.pLRURecord = pRCache->pPrevInGlobal; } if (pRCache->pPrevInGlobal) { pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; } else { gv_FlmSysData.RCacheMgr.pMRURecord = pRCache->pNextInGlobal; } pRCache->pPrevInGlobal = pRCache->pNextInGlobal = NULL; } /**************************************************************************** Desc: This routine unlinks a record from the global purged list This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaUnlinkFromPurged( RCACHE * pRCache) { if (pRCache->pNextInGlobal) { pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; } if (pRCache->pPrevInGlobal) { pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; } else { gv_FlmSysData.RCacheMgr.pPurgeList = pRCache->pNextInGlobal; } pRCache->pPrevInGlobal = NULL; pRCache->pNextInGlobal = NULL; } /**************************************************************************** Desc: ****************************************************************************/ FINLINE void flmRcaLinkToHeapList( RCACHE * pRCache) { flmAssert( !pRCache->pPrevInHeapList); flmAssert( !pRCache->pNextInHeapList); flmAssert( !RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); flmAssert( FlmRecordExt::getFlags( pRCache->pRecord) & RCA_HEAP_BUFFER); if( (pRCache->pNextInHeapList = gv_FlmSysData.RCacheMgr.pHeapList) != NULL) { pRCache->pNextInHeapList->pPrevInHeapList = pRCache; } gv_FlmSysData.RCacheMgr.pHeapList = pRCache; RCA_SET_IN_HEAP_LIST( pRCache->uiFlags); } /**************************************************************************** Desc: ****************************************************************************/ FINLINE void flmRcaUnlinkFromHeapList( RCACHE * pRCache) { flmAssert( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); if( pRCache->pNextInHeapList) { pRCache->pNextInHeapList->pPrevInHeapList = pRCache->pPrevInHeapList; } if( pRCache->pPrevInHeapList) { pRCache->pPrevInHeapList->pNextInHeapList = pRCache->pNextInHeapList; } else { gv_FlmSysData.RCacheMgr.pHeapList = pRCache->pNextInHeapList; } pRCache->pPrevInHeapList = NULL; pRCache->pNextInHeapList = NULL; RCA_UNSET_IN_HEAP_LIST( pRCache->uiFlags); } /**************************************************************************** Desc: This routine links a record to an FFILE list at the head of the list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToFileAtHead( RCACHE * pRCache, FFILE * pFile) { pRCache->pPrevInFile = NULL; if ((pRCache->pNextInFile = pFile->pFirstRecord) != NULL) { pFile->pFirstRecord->pPrevInFile = pRCache; } else { pFile->pLastRecord = pRCache; } pFile->pFirstRecord = pRCache; pRCache->pFile = pFile; RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); } /**************************************************************************** Desc: This routine links a record to an FFILE list at the end of the list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToFileAtEnd( RCACHE * pRCache, FFILE * pFile) { pRCache->pNextInFile = NULL; if( (pRCache->pPrevInFile = pFile->pLastRecord) != NULL) { pFile->pLastRecord->pNextInFile = pRCache; } else { pFile->pFirstRecord = pRCache; } pFile->pLastRecord = pRCache; pRCache->pFile = pFile; RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); } /**************************************************************************** Desc: This routine unlinks a record from its FFILE list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaUnlinkFromFile( RCACHE * pRCache) { if( RCA_IS_LINKED_TO_FILE( pRCache->uiFlags)) { if( pRCache->pNextInFile) { pRCache->pNextInFile->pPrevInFile = pRCache->pPrevInFile; } else { pRCache->pFile->pLastRecord = pRCache->pPrevInFile; } if( pRCache->pPrevInFile) { pRCache->pPrevInFile->pNextInFile = pRCache->pNextInFile; } else { pRCache->pFile->pFirstRecord = pRCache->pNextInFile; } pRCache->pPrevInFile = pRCache->pNextInFile = NULL; RCA_UNSET_LINKED_TO_FILE( pRCache->uiFlags); } } /**************************************************************************** Desc: This routine links a record into its hash bucket. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToHashBucket( RCACHE * pRCache) { RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); flmAssert( pRCache->pNewerVersion == NULL); pRCache->pPrevInBucket = NULL; if( (pRCache->pNextInBucket = *ppHashBucket) != NULL) { pRCache->pNextInBucket->pPrevInBucket = pRCache; } *ppHashBucket = pRCache; } /**************************************************************************** Desc: This routine unlinks a record from its hash bucket. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaUnlinkFromHashBucket( RCACHE * pRCache) { flmAssert( pRCache->pNewerVersion == NULL); if (pRCache->pNextInBucket) { pRCache->pNextInBucket->pPrevInBucket = pRCache->pPrevInBucket; } if (pRCache->pPrevInBucket) { pRCache->pPrevInBucket->pNextInBucket = pRCache->pNextInBucket; } else { RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); *ppHashBucket = pRCache->pNextInBucket; } pRCache->pPrevInBucket = pRCache->pNextInBucket = NULL; } /**************************************************************************** Desc: This routine unlinks a record from its version list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaLinkToVerList( RCACHE * pRCache, RCACHE * pNewerVer, RCACHE * pOlderVer) { if( (pRCache->pNewerVersion = pNewerVer) != NULL) { pNewerVer->pOlderVersion = pRCache; } if ((pRCache->pOlderVersion = pOlderVer) != NULL) { pOlderVer->pNewerVersion = pRCache; } } /**************************************************************************** Desc: This routine unlinks a record from its version list. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FINLINE void flmRcaUnlinkFromVerList( RCACHE * pRCache) { if (pRCache->pNewerVersion) { pRCache->pNewerVersion->pOlderVersion = pRCache->pOlderVersion; } if (pRCache->pOlderVersion) { pRCache->pOlderVersion->pNewerVersion = pRCache->pNewerVersion; } pRCache->pNewerVersion = pRCache->pOlderVersion = NULL; } /**************************************************************************** Desc: This routine frees a purged from record cache. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FSTATIC void flmRcaFreePurged( RCACHE * pRCache) { FLMUINT uiTotalMemory; FLMUINT uiFreeMemory; // Release the record data object we are pointing to. if (pRCache->pRecord) { if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) { flmRcaUnlinkFromHeapList( pRCache); } uiTotalMemory = pRCache->pRecord->getTotalMemory(); uiFreeMemory = pRCache->pRecord->getFreeMemory(); flmAssert( uiTotalMemory >= uiFreeMemory); FlmRecordExt::clearCached( pRCache->pRecord); FlmRecordExt::Release( pRCache->pRecord, TRUE); pRCache->pRecord = NULL; } else { uiTotalMemory = 0; uiFreeMemory = 0; } // If this is an old version, decrement the old version counters. if (pRCache->uiHighTransId != 0xFFFFFFFF) { FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiTotalOldMemory); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; } // Unlink the RCACHE from the purged list. flmRcaUnlinkFromPurged( pRCache); // Free the RCACHE structure. RCA_UNSET_PURGED( pRCache->uiFlags); flmRcaFreeCacheStruct( &pRCache); } /**************************************************************************** Desc: This routine frees a record in the record cache. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FSTATIC void flmRcaFreeCache( RCACHE * pRCache, FLMBOOL bPutInPurgeList) { FLMUINT uiTotalMemory; FLMUINT uiFreeMemory; FLMBOOL bOldVersion; #ifdef FLM_DBG_LOG char szTmpBuf[ 80]; #endif // Release the record data object we are pointing to. if (pRCache->pRecord && !bPutInPurgeList) { if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) { flmRcaUnlinkFromHeapList( pRCache); } uiTotalMemory = pRCache->pRecord->getTotalMemory(); uiFreeMemory = pRCache->pRecord->getFreeMemory(); flmAssert( uiTotalMemory >= uiFreeMemory); FlmRecordExt::clearCached( pRCache->pRecord); FlmRecordExt::Release( pRCache->pRecord, TRUE); pRCache->pRecord = NULL; } else { uiTotalMemory = 0; uiFreeMemory = 0; } bOldVersion = (FLMBOOL)((pRCache->uiHighTransId != 0xFFFFFFFF) ? TRUE : FALSE); #ifdef FLM_DBG_LOG f_sprintf( szTmpBuf, "RCD:H%X", (unsigned)pRCache->uiHighTransId); flmDbgLogWrite( pRCache->pFile ? pRCache->pFile->uiFFileId : 0, pRCache->uiContainer, pRCache->uiDrn, pRCache->uiLowTransId, szTmpBuf); #endif // Unlink the RCACHE from its various lists. flmRcaUnlinkFromGlobal( pRCache); flmRcaUnlinkFromFile( pRCache); if (!pRCache->pNewerVersion) { RCACHE * pOlderVersion = pRCache->pOlderVersion; flmRcaUnlinkFromHashBucket( pRCache); // If there was an older version, it now needs to be // put into the hash bucket. if (pOlderVersion) { flmRcaUnlinkFromVerList( pRCache); flmRcaLinkToHashBucket( pOlderVersion); } } else { flmRcaUnlinkFromVerList( pRCache); } // Free the RCACHE structure if not putting in purge list. if (!bPutInPurgeList) { // If this was an older version, decrement the old version counters. if (bOldVersion) { FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiTotalOldMemory); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; } flmRcaFreeCacheStruct( &pRCache); } else { if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pPurgeList) != NULL) { pRCache->pNextInGlobal->pPrevInGlobal = pRCache; } gv_FlmSysData.RCacheMgr.pPurgeList = pRCache; RCA_SET_PURGED( pRCache->uiFlags); } } /**************************************************************************** Desc: This routine initializes record cache manager. ****************************************************************************/ RCODE flmRcaInit( FLMUINT uiMaxRecordCacheBytes) { RCODE rc = FERR_OK; F_RCacheRelocator * pRCacheRelocator = NULL; F_RecRelocator * pRecRelocator = NULL; F_RecBufferRelocator * pRecBufferRelocator = NULL; f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxRecordCacheBytes; gv_FlmSysData.RCacheMgr.hMutex = F_MUTEX_NULL; // Allocate the hash buckets. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( RCACHE *) * (FLMUINT)MIN_RCACHE_BUCKETS, &gv_FlmSysData.RCacheMgr.ppHashBuckets))) { goto Exit; } gv_FlmSysData.RCacheMgr.uiNumBuckets = MIN_RCACHE_BUCKETS; gv_FlmSysData.RCacheMgr.uiHashMask = gv_FlmSysData.RCacheMgr.uiNumBuckets - 1; gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated += (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets); // Allocate the mutex for controlling access to the // record cache. if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.RCacheMgr.hMutex))) { goto Exit; } // Set up the RCACHE struct allocator if( RC_BAD( rc = FlmAllocFixedAllocator( &gv_FlmSysData.RCacheMgr.pRCacheAlloc))) { goto Exit; } if( (pRCacheRelocator = f_new F_RCacheRelocator) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( FALSE, gv_FlmSysData.pSlabManager, pRCacheRelocator, sizeof( RCACHE), &gv_FlmSysData.RCacheMgr.Usage.SlabUsage, &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) { goto Exit; } // Set up the record object allocator if( RC_BAD( rc = FlmAllocFixedAllocator( &gv_FlmSysData.RCacheMgr.pRecAlloc))) { goto Exit; } if( (pRecRelocator = f_new F_RecRelocator) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( TRUE, gv_FlmSysData.pSlabManager, pRecRelocator, sizeof( FlmRecord), &gv_FlmSysData.RCacheMgr.Usage.SlabUsage, &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) { goto Exit; } // Set up the record buffer allocator if( RC_BAD( rc = FlmAllocBufferAllocator( &gv_FlmSysData.RCacheMgr.pRecBufAlloc))) { goto Exit; } if( (pRecBufferRelocator = f_new F_RecBufferRelocator) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( TRUE, gv_FlmSysData.pSlabManager, pRecBufferRelocator, &gv_FlmSysData.RCacheMgr.Usage.SlabUsage, &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) { goto Exit; } #ifdef FLM_DEBUG gv_FlmSysData.RCacheMgr.bDebug = TRUE; #endif Exit: if( pRCacheRelocator) { pRCacheRelocator->Release(); } if( pRecRelocator) { pRecRelocator->Release(); } if( pRecBufferRelocator) { pRecBufferRelocator->Release(); } if (RC_BAD( rc)) { flmRcaExit(); } return( rc); } /**************************************************************************** Desc: This routine determines what hash table size best fits the current record count. It finds the hash bucket size whose midpoint between the minimum and maximum range is closest to the record count. ****************************************************************************/ FSTATIC FLMUINT flmRcaGetBestHashTblSize( FLMUINT uiCurrRecCount) { FLMUINT uiHashTblSize; FLMUINT uiMaxRecsForHashTblSize; FLMUINT uiMinRecsForHashTblSize; FLMUINT uiClosestHashTblSize = 0; FLMUINT uiDistanceFromMidpoint; FLMUINT uiLowestDistanceFromMidpoint; FLMUINT uiHashTblRecsMidpoint; uiLowestDistanceFromMidpoint = 0xFFFFFFFF; for (uiHashTblSize = MIN_RCACHE_BUCKETS; uiHashTblSize <= MAX_RCACHE_BUCKETS; uiHashTblSize *= 2) { // Maximum desirable record count for a specific hash table size // we have arbitrarily chosen to be four times the number of buckets. // Minimum desirable record count we have arbitrarily chosen to be // the hash table size divided by four. uiMaxRecsForHashTblSize = FLM_RCA_MAX_REC_CNT( uiHashTblSize); uiMinRecsForHashTblSize = FLM_RCA_MIN_REC_CNT( uiHashTblSize); // Ignore any hash bucket sizes where the current record count // is not between the desired minimum and maximum. if (uiCurrRecCount >= uiMinRecsForHashTblSize && uiCurrRecCount <= uiMaxRecsForHashTblSize) { // Calculate the midpoint between the minimum and maximum // for this particular hash table size. uiHashTblRecsMidpoint = (uiMaxRecsForHashTblSize - uiMinRecsForHashTblSize) / 2; // See how far our current record count is from this midpoint. uiDistanceFromMidpoint = (FLMUINT)((uiHashTblRecsMidpoint > uiCurrRecCount) ? (uiHashTblRecsMidpoint - uiCurrRecCount) : (uiCurrRecCount - uiHashTblRecsMidpoint)); // If the distance from the midpoint is closer than our previous // lowest distance, save it. if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) { uiClosestHashTblSize = uiHashTblSize; uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; } } } // Take the number of buckets whose middle was closest to the // current record count; if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) { // If we did not fall between any of the minimums or maximums, // we are either below the lowest minimum, or higher than the // highest maximum. uiHashTblSize = (FLMUINT)((uiCurrRecCount < FLM_RCA_MIN_REC_CNT( MIN_RCACHE_BUCKETS)) ? (FLMUINT)MIN_RCACHE_BUCKETS : (FLMUINT)MAX_RCACHE_BUCKETS); } else { uiHashTblSize = uiClosestHashTblSize; } return( uiHashTblSize); } /**************************************************************************** Desc: This routine resizes the hash table for the record cache manager. NOTE: This routine assumes that the record cache mutex has been locked. ****************************************************************************/ FSTATIC RCODE flmRcaRehash( void) { RCODE rc = FERR_OK; FLMUINT uiNewHashTblSize; RCACHE ** ppOldHashTbl; FLMUINT uiOldHashTblSize; RCACHE ** ppBucket; FLMUINT uiLoop; RCACHE * pTmpRCache; RCACHE * pTmpNextRCache; uiNewHashTblSize = flmRcaGetBestHashTblSize( gv_FlmSysData.RCacheMgr.Usage.uiCount); // At this point we better have a different hash table size // or something is mucked up! flmAssert( uiNewHashTblSize != gv_FlmSysData.RCacheMgr.uiNumBuckets); // Save the old hash table and its size. ppOldHashTbl = gv_FlmSysData.RCacheMgr.ppHashBuckets; uiOldHashTblSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; // Allocate a new hash table. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( RCACHE *) * (FLMUINT)uiNewHashTblSize, &gv_FlmSysData.RCacheMgr.ppHashBuckets))) { gv_FlmSysData.RCacheMgr.ppHashBuckets = ppOldHashTbl; goto Exit; } gv_FlmSysData.RCacheMgr.uiNumBuckets = uiNewHashTblSize; gv_FlmSysData.RCacheMgr.uiHashMask = uiNewHashTblSize - 1; // Relink all of the records into the new // hash table. for (uiLoop = 0, ppBucket = ppOldHashTbl; uiLoop < uiOldHashTblSize; uiLoop++, ppBucket++) { pTmpRCache = *ppBucket; while (pTmpRCache) { pTmpNextRCache = pTmpRCache->pNextInBucket; flmRcaLinkToHashBucket( pTmpRCache); pTmpRCache = pTmpNextRCache; } } // Throw away the old hash table. f_free( &ppOldHashTbl); Exit: return( rc); } /**************************************************************************** Desc: This routine changes the cache size for the record cache manager. If necessary, it will resize the hash table. NOTE: This routine assumes that the record cache mutex has been locked. ****************************************************************************/ FSTATIC RCODE flmRcaSetMemLimit( FLMUINT uiMaxCacheBytes) { RCODE rc = FERR_OK; // If we are shrinking the maximum cache, clean up and // defragment cache first gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxCacheBytes; if (gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated > uiMaxCacheBytes) { flmRcaCleanupCache( ~((FLMUINT)0), TRUE); } // If the current record count is below the minimum records for the // number of buckets or is greater than the maximum records for the // number of buckets, we want to resize the hash table. if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || (gv_FlmSysData.RCacheMgr.Usage.uiCount < FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) { if (RC_BAD( rc = flmRcaRehash())) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: This routine configures the record cache manager. NOTE: This routine assumes that the record cache mutex has been locked. ****************************************************************************/ RCODE flmRcaConfig( FLMUINT uiType, void * Value1, void * Value2) { RCODE rc = FERR_OK; F_UNREFERENCED_PARM( Value2); switch (uiType) { case FLM_CACHE_LIMIT: rc = flmRcaSetMemLimit( (FLMUINT)Value1); break; case FLM_SCACHE_DEBUG: #ifdef FLM_DEBUG gv_FlmSysData.RCacheMgr.bDebug = (FLMBOOL)(Value1 ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); #endif break; default: rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine shuts down the record cache manager and frees all resources allocated by it. ****************************************************************************/ void flmRcaExit( void) { FLMUINT uiCount; if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) { f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); } // Free all of the record cache objects. uiCount = 0; while (gv_FlmSysData.RCacheMgr.pMRURecord) { if( (++uiCount & 0xFF) == 0) { f_yieldCPU(); } flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); } // Must free those in the purge list too. uiCount = 0; while (gv_FlmSysData.RCacheMgr.pPurgeList) { if( (++uiCount & 0xFF) == 0) { f_yieldCPU(); } flmRcaFreePurged( gv_FlmSysData.RCacheMgr.pPurgeList); } // The math better be consistent! flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount == 0); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount == 0); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes == 0); // Free the hash bucket array if (gv_FlmSysData.RCacheMgr.ppHashBuckets) { f_free( &gv_FlmSysData.RCacheMgr.ppHashBuckets); } // Free the mutex that controls access to record cache. // NOTE: This should be done last so that the mutex is not // unlocked until the very end. if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexDestroy( &gv_FlmSysData.RCacheMgr.hMutex); } // Free the allocators if( gv_FlmSysData.RCacheMgr.pRecBufAlloc) { gv_FlmSysData.RCacheMgr.pRecBufAlloc->Release(); gv_FlmSysData.RCacheMgr.pRecBufAlloc = NULL; } if( gv_FlmSysData.RCacheMgr.pRecAlloc) { gv_FlmSysData.RCacheMgr.pRecAlloc->Release(); gv_FlmSysData.RCacheMgr.pRecAlloc = NULL; } if( gv_FlmSysData.RCacheMgr.pRCacheAlloc) { gv_FlmSysData.RCacheMgr.pRCacheAlloc->Release(); gv_FlmSysData.RCacheMgr.pRCacheAlloc = NULL; } // Zero the entire structure out, just for good measure. f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); } /**************************************************************************** Desc: This routine notifies threads waiting for a pending read to complete. NOTE: This routine assumes that the record cache mutex is already locked. ****************************************************************************/ FSTATIC void flmRcaNotify( F_NOTIFY_LIST_ITEM * pNotify, RCACHE * pUseRCache, RCODE NotifyRc) { while (pNotify) { F_SEM hSem; *(pNotify->pRc) = NotifyRc; if (RC_OK( NotifyRc)) { RCA_INCR_USE_COUNT( pUseRCache->uiFlags); } hSem = pNotify->hSem; pNotify = pNotify->pNext; f_semSignal( hSem); } } /**************************************************************************** Desc: Allocate a new record cache structure. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FSTATIC RCODE flmRcaAllocCacheStruct( RCACHE ** ppRCache) { RCODE rc = FERR_OK; f_assertMutexLocked( gv_FlmSysData.RCacheMgr.hMutex); if( (*ppRCache = (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell( NULL, NULL)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memset( *ppRCache, 0, sizeof( RCACHE)); // Increment the total records cached gv_FlmSysData.RCacheMgr.Usage.uiCount++; // Set the high transaction ID to 0xFFFFFFFF so that this will NOT // be treated as one that had memory assigned to the old records. (*ppRCache)->uiHighTransId = 0xFFFFFFFF; Exit: return( rc); } /**************************************************************************** Desc: Free a record cache structure. This routine assumes that the record cache mutex has already been locked. ****************************************************************************/ FSTATIC void flmRcaFreeCacheStruct( RCACHE ** ppRCache) { flmAssert( !RCA_IS_IN_HEAP_LIST( (*ppRCache)->uiFlags)); f_assertMutexLocked( gv_FlmSysData.RCacheMgr.hMutex); gv_FlmSysData.RCacheMgr.pRCacheAlloc->freeCell( *ppRCache); *ppRCache = NULL; flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount > 0); gv_FlmSysData.RCacheMgr.Usage.uiCount--; } /**************************************************************************** Desc: Cleanup old records in cache that are no longer needed by any transaction. ****************************************************************************/ void flmRcaCleanupCache( FLMUINT uiMaxLockTime, FLMBOOL bMutexesLocked) { RCACHE * pTmpRCache; RCACHE * pPrevRCache; RCACHE * pNextRCache; FLMUINT uiRecordsExamined = 0; FLMUINT uiLastTimePaused = FLM_GET_TIMER(); FLMUINT uiCurrTime; FLMBOOL bUnlockMutexes = FALSE; if( !bMutexesLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bUnlockMutexes = TRUE; } // Try to free everything in the heap list pTmpRCache = gv_FlmSysData.RCacheMgr.pHeapList; while( pTmpRCache) { uiRecordsExamined++; // Save the pointer to the next entry in the list because // we may end up unlinking pTmpRCache below pNextRCache = pTmpRCache->pNextInHeapList; // Determine if the item can be freed flmAssert( RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags)); if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && !RCA_IS_READING_IN( pTmpRCache->uiFlags)) { flmRcaFreeCache( pTmpRCache, FALSE); } pTmpRCache = pNextRCache; } // Now, free any old versions that are no longer needed flmRcaReduceCache( TRUE); pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; // Stay in the loop until we have freed all old records, or // we have run through the entire list. for( ;;) { if( !pTmpRCache || !gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes) { break; } // After each 200 records examined, see if our maximum // time has elapsed for examining without a pause. if( uiRecordsExamined >= 200) { uiRecordsExamined = 0; uiCurrTime = FLM_GET_TIMER(); if( FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= uiMaxLockTime) { // IMPORTANT! Don't stop and pause on one that is // being read in. while( pTmpRCache && RCA_IS_READING_IN( pTmpRCache->uiFlags)) { pTmpRCache = pTmpRCache->pPrevInGlobal; } if( !pTmpRCache) { break; } if( bUnlockMutexes) { // Increment the use count so that this item will not // go away while we are paused. RCA_INCR_USE_COUNT( pTmpRCache->uiFlags); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); // Shortest possible pause - to allow other threads // to do work. f_yieldCPU(); // Relock the mutexes uiLastTimePaused = FLM_GET_TIMER(); f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); // Decrement use count that was added above. RCA_DECR_USE_COUNT( pTmpRCache->uiFlags); } // If the item was purged while we were paused, // finish the job and then start again at the // top of the list. if( RCA_IS_PURGED( pTmpRCache->uiFlags)) { if( !RCA_IS_IN_USE( pTmpRCache->uiFlags)) { flmRcaFreePurged( pTmpRCache); } pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; continue; } } } uiRecordsExamined++; // Save the pointer to the previous entry in the list because // we may end up unlinking pTmpRCache below, in which case we would // have lost the previous entry. pPrevRCache = pTmpRCache->pPrevInGlobal; // Block must not currently be in use, // Must not be the most current version of a block, // Cannot be dirty in any way, // Cannot be in the process of being read in from disk, // And must not be needed by a read transaction. if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && !RCA_IS_READING_IN( pTmpRCache->uiFlags) && (RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags) || (pTmpRCache->uiHighTransId != 0xFFFFFFFF && !flmNeededByReadTrans( pTmpRCache->pFile, pTmpRCache->uiLowTransId, pTmpRCache->uiHighTransId)))) { flmRcaFreeCache( pTmpRCache, FALSE); } pTmpRCache = pPrevRCache; } // Defragment memory gv_FlmSysData.RCacheMgr.pRCacheAlloc->defragmentMemory(); gv_FlmSysData.RCacheMgr.pRecAlloc->defragmentMemory(); gv_FlmSysData.RCacheMgr.pRecBufAlloc->defragmentMemory(); // Unlock the mutexes if( bUnlockMutexes) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); } } /**************************************************************************** Desc: This routine reduces record cache down to the limit expected. ****************************************************************************/ void flmRcaReduceCache( FLMBOOL bMutexAlreadyLocked) { RCACHE * pRCache; RCACHE * pPrevRCache; // Make sure the mutex is locked. if (!bMutexAlreadyLocked) { f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); } pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; // Free things until we get down below our memory limit. while( gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated > gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) { if( !pRCache) { break; } // If the total of block and record cache is below the global // cache maximum, there is no need to reduce record cache. if( (gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated) <= gv_FlmSysData.uiMaxCache) { break; } pPrevRCache = pRCache->pPrevInGlobal; if (!(RCA_IS_IN_USE( pRCache->uiFlags)) && !(RCA_IS_READING_IN( pRCache->uiFlags))) { flmRcaFreeCache( pRCache, FALSE); } pRCache = pPrevRCache; } if (!bMutexAlreadyLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } } /**************************************************************************** Desc: This routine finds a record in the record cache. If it cannot find the record, it will return the position where the record should be inserted. NOTE: This routine assumes that the record cache mutex has been locked. ****************************************************************************/ void flmRcaFindRec( FFILE * pFile, F_SEM hWaitSem, FLMUINT uiContainer, FLMUINT uiDrn, FLMUINT uiVersionNeeded, FLMBOOL bDontPoisonCache, FLMUINT * puiNumLooks, RCACHE ** ppRCache, RCACHE ** ppNewerRCache, RCACHE ** ppOlderRCache) { RCACHE * pRCache; FLMUINT uiNumLooks = 0; FLMBOOL bFound; RCACHE * pNewerRCache; RCACHE * pOlderRCache; // Search down the hash bucket for the matching item. Start_Find: // NOTE: Need to always calculate hash bucket because // the hash table may have been changed while we // were waiting to be notified below - mutex can // be unlocked, but it is guaranteed to be locked // here. pRCache = *(FLM_RCA_HASH( uiDrn)); bFound = FALSE; uiNumLooks = 1; while ((pRCache) && ((pRCache->uiDrn != uiDrn) || (pRCache->uiContainer != uiContainer) || (pRCache->pFile != pFile))) { if ((pRCache = pRCache->pNextInBucket) != NULL) { uiNumLooks++; } } // If we found the record, see if we have the right version. if (!pRCache) { pNewerRCache = pOlderRCache = NULL; } else { pNewerRCache = NULL; pOlderRCache = pRCache; for (;;) { // If this one is being read in, we need to wait on it. if (RCA_IS_READING_IN( pRCache->uiFlags)) { // We need to wait for this record to be read in // in case it coalesces with other versions, resulting // in a version that satisfies our request. gv_FlmSysData.RCacheMgr.uiIoWaits++; if( RC_BAD( f_notifyWait( gv_FlmSysData.RCacheMgr.hMutex, hWaitSem, NULL, &pRCache->pNotifyList))) { // Don't care what error the other thread had reading // the thing in from disk - we'll bail out and start // our find again. goto Start_Find; } // The thread doing the notify "uses" the record cache // on behalf of this thread to prevent the record // from being replaced after it unlocks the mutex. // At this point, since we have locked the mutex, // we need to release the record - because we // will put a "use" on it below. RCA_DECR_USE_COUNT( pRCache->uiFlags); if (RCA_IS_PURGED( pRCache->uiFlags)) { if (!RCA_IS_IN_USE( pRCache->uiFlags)) { flmRcaFreePurged( pRCache); } } // Start over with the find because the list // structure has changed. goto Start_Find; } // See if this record version is the one we need. if (uiVersionNeeded < pRCache->uiLowTransId) { pNewerRCache = pRCache; if ((pOlderRCache = pRCache = pRCache->pOlderVersion) == NULL) { break; } uiNumLooks++; } else if (uiVersionNeeded <= pRCache->uiHighTransId) { // Make this the MRU record. if (puiNumLooks) { if (bDontPoisonCache) { flmRcaStepUpInGlobalList( pRCache); } else if (pRCache->pPrevInGlobal) { flmRcaUnlinkFromGlobal( pRCache); flmRcaLinkToGlobalAsMRU( pRCache); } gv_FlmSysData.RCacheMgr.Usage.uiCacheHits++; gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; } bFound = TRUE; break; } else { pOlderRCache = pRCache; pNewerRCache = pRCache->pNewerVersion; // Set pRCache to NULL as an indicator that we did not // find the version we needed. pRCache = NULL; break; } } } *ppRCache = pRCache; *ppOlderRCache = pOlderRCache; *ppNewerRCache = pNewerRCache; if (puiNumLooks) { *puiNumLooks = uiNumLooks; } } /**************************************************************************** Desc: This routine replaces the FlmRecord that a record is pointing to with a new one. NOTE: This routine assumes that the record cache mutex is already locked. ****************************************************************************/ FSTATIC void flmRcaSetRecord( RCACHE * pRCache, FlmRecord * pNewRecord) { FLMUINT uiTotalMemory = 0; FLMUINT uiFreeMemory; FlmRecord * pOldRecord; // Release the cache's pointer to the old record data if ((pOldRecord = pRCache->pRecord) != NULL) { if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) { flmRcaUnlinkFromHeapList( pRCache); } uiTotalMemory = pOldRecord->getTotalMemory(); uiFreeMemory = pOldRecord->getFreeMemory(); flmAssert( uiTotalMemory >= uiFreeMemory); FlmRecordExt::clearCached( pOldRecord); FlmRecordExt::Release( pOldRecord, TRUE); pRCache->pRecord = NULL; } if (pRCache->uiHighTransId != 0xFFFFFFFF) { FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiTotalOldMemory); flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; } // Point to the new record data. flmAssert( pNewRecord->getID() == pRCache->uiDrn); flmAssert( pNewRecord->getContainerID() == pRCache->uiContainer); pRCache->pRecord = pNewRecord; flmAssert( !pNewRecord->isCached()); FlmRecordExt::setCached( pNewRecord); FlmRecordExt::setReadOnly( pNewRecord); pNewRecord->AddRef(); if( FlmRecordExt::getFlags( pNewRecord) & RCA_HEAP_BUFFER) { flmRcaLinkToHeapList( pRCache); } uiTotalMemory = pNewRecord->getTotalMemory(); if (pRCache->uiHighTransId != 0xFFFFFFFF) { gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += (uiTotalMemory + sizeof( RCACHE)); gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; } uiFreeMemory = pNewRecord->getFreeMemory(); flmAssert( uiTotalMemory >= uiFreeMemory); } /**************************************************************************** Desc: This routine links a new RCACHE structure into the global list and into the correct place in its hash bucket. This routine assumes that the record cache mutex is already locked. ****************************************************************************/ FSTATIC void flmRcaLinkIntoRCache( RCACHE * pNewerRCache, RCACHE * pOlderRCache, RCACHE * pRCache, FLMBOOL bLinkAsMRU) { if( bLinkAsMRU) { flmRcaLinkToGlobalAsMRU( pRCache); } else { flmRcaLinkToGlobalAsLRU( pRCache); } if (pNewerRCache) { flmRcaLinkToVerList( pRCache, pNewerRCache, pOlderRCache); } else { RCACHE * pNull = NULL; if (pOlderRCache) { flmRcaUnlinkFromHashBucket( pOlderRCache); } flmRcaLinkToHashBucket( pRCache); flmRcaLinkToVerList( pRCache, pNull, pOlderRCache); } } /**************************************************************************** Desc: This routine links a new record to its FFILE according to whether or not it is an update transaction or a read transaction. It coalesces out any unnecessary versions. This routine assumes that the record cache mutex is already locked. ****************************************************************************/ FSTATIC void flmRcaLinkToFFILE( RCACHE * pRCache, FFILE * pFile, FDB * pDb, FLMUINT uiLowTransId, FLMBOOL bMostCurrent) { RCACHE * pTmpRCache; #ifdef FLM_DBG_LOG char szTmpBuf[ 80]; #endif pRCache->uiLowTransId = uiLowTransId; // Before coalescing, link to FFILE. // The following test determines if the record is an // uncommitted version generated by the update transaction. // If so, we mark it as such, and link it at the head of the // FFILE list - so we can get rid of it quickly if we abort // the transaction. if (flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) { // If we are in an update transaction, there better not // be any newer versions in the list and the high // transaction ID returned better be 0xFFFFFFFF. flmAssert( pRCache->pNewerVersion == NULL); flmRcaSetTransID( pRCache, 0xFFFFFFFF); // If the low transaction ID is the same as the transaction, // we may have modified this record during the transaction. // Unfortunately, there is no sure way to tell, so we are // forced to assume it may have been modified. If the // transaction aborts, we will get rid if this version out // of cache. if (uiLowTransId == pDb->LogHdr.uiCurrTransID) { RCA_SET_UNCOMMITTED( pRCache->uiFlags); flmRcaLinkToFileAtHead( pRCache, pFile); } else { RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); flmRcaLinkToFileAtEnd( pRCache, pFile); } #ifdef FLM_DBG_LOG f_sprintf( szTmpBuf, "RCI:L%X,H%X", (unsigned)pRCache->uiLowTransId, (unsigned)pRCache->uiHighTransId); flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, pDb->LogHdr.uiCurrTransID, szTmpBuf); #endif } else { // Adjust the high transaction ID to be the same as // the transaction ID - we may have gotten a 0xFFFFFFF // back, but that is possible even if the record is // not the most current version. Besides that, it is // possible that in the mean time one or more update // transactions have come along and created one or // more newer versions of the record. FLMUINT uiHighTransId = (FLMUINT)((bMostCurrent) ? (FLMUINT)0xFFFFFFFF : pDb->LogHdr.uiCurrTransID); flmRcaSetTransID( pRCache, uiHighTransId); // For a read transaction, if there is a newer version, // it better have a higher "low transaction ID" #ifdef FLM_DBG_LOG f_sprintf( szTmpBuf, "RCA:L%X,H%X", (unsigned)pRCache->uiLowTransId, (unsigned)pRCache->uiHighTransId); flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, pDb->LogHdr.uiCurrTransID, szTmpBuf); #endif #ifdef FLM_DEBUG if (pRCache->pNewerVersion && !RCA_IS_READING_IN( pRCache->pNewerVersion->uiFlags)) { flmAssert( pRCache->uiHighTransId < pRCache->pNewerVersion->uiLowTransId); if( pRCache->uiHighTransId >= pRCache->pNewerVersion->uiLowTransId) { flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); } } #endif RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); flmRcaLinkToFileAtEnd( pRCache, pFile); } // Coalesce any versions that overlap - can only // coalesce older versions. For an updater, there // should not be any newer versions. For a reader, it // is impossible to know how high up it can coalesce. // The read operation that read the record may have // gotten back a 0xFFFFFFFF for its high transaction // ID - but after that point in time, it is possible // that one or more update transactions may have come // along and created one or more newer versions that // it would be incorrect to coalesce with. // In reality, a read transaction has to ignore the // 0xFFFFFFFF in the high transaction ID anyway // because there is no way to know if it is correct. // Coalesce older versions. for (;;) { if ((pTmpRCache = pRCache->pOlderVersion) == NULL) { break; } // Stop if we encounter one that is being read in. if (RCA_IS_READING_IN( pTmpRCache->uiFlags)) { break; } // If there is no overlap between these two, there is // nothing more to coalesce. if (pRCache->uiLowTransId > pTmpRCache->uiHighTransId) { break; } if (pRCache->uiHighTransId <= pTmpRCache->uiHighTransId) { // This assert represents the following case, // which should not be possible to hit: // pOlder->uiHighTransId > pRCache->uiHighTransId. // This cannot be, because if pOlder has a higher // transaction ID, we would have found it up above and // not tried to have read it in. flmAssert( 0); #ifdef FLM_DEBUG flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); #endif } else if (pRCache->uiLowTransId >= pTmpRCache->uiLowTransId) { pRCache->uiLowTransId = pTmpRCache->uiLowTransId; flmRcaFreeCache( pTmpRCache, (FLMBOOL)((RCA_IS_IN_USE( pTmpRCache->uiFlags) || RCA_IS_READING_IN( pTmpRCache->uiFlags)) ? TRUE : FALSE)); } else { // This assert represents the following case, // which should not be possible to hit: // pRCache->uiLowTransId < pOlder->uiLowTransId. // This cannot be, because pOlder has to have been read // in to memory by a transaction whose transaction ID is // less than or equal to our own. That being the case, // it would be impossible for our transaction to have // found a version of the record that is older than pOlder. flmAssert( 0); #ifdef FLM_DEBUG flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); #endif } } } /**************************************************************************** Desc: This routine retrieves a record from the record cache. ****************************************************************************/ RCODE flmRcaRetrieveRec( FDB * pDb, FLMBOOL * pbTransStarted, FLMUINT uiContainer, // Container record is in. FLMUINT uiDrn, // DRN of record. FLMBOOL bOkToGetFromDisk, // If not in cache, OK to get from disk? BTSK * pStack, // Use stack to retrieve, if NON-NULL. LFILE * pLFile, // LFILE to use, if retrieving with stack FlmRecord ** ppRecord) { RCODE rc = FERR_OK; FLMBOOL bRCacheMutexLocked = FALSE; FFILE * pFile = pDb->pFile; RCACHE * pRCache; RCACHE * pNewerRCache; RCACHE * pOlderRCache; FlmRecord * pRecord = NULL; FLMBOOL bGotFromDisk = FALSE; FlmRecord * pNewRecord = NULL; FlmRecord * pOldRecord = NULL; FLMUINT uiLowTransId; FLMBOOL bMostCurrent; FLMUINT uiCurrTransId; F_NOTIFY_LIST_ITEM * pNotify; FLMUINT uiNumLooks; FLMBOOL bInitializedFdb = FALSE; FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE ? TRUE : FALSE; if( RC_BAD( rc = flmCheckDatabaseState( pDb))) { goto Exit; } // Get the current transaction ID if( pDb->uiTransType != FLM_NO_TRANS) { uiCurrTransId = pDb->LogHdr.uiCurrTransID; } else { flmAssert( pbTransStarted != NULL); f_mutexLock( gv_FlmSysData.hShareMutex); // Get the last committed transaction ID. uiCurrTransId = (FLMUINT)FB2UD( &pFile->ucLastCommittedLogHdr[ LOG_CURR_TRANS_ID]); f_mutexUnlock( gv_FlmSysData.hShareMutex); } flmAssert( uiDrn != 0); // Lock the mutex f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bRCacheMutexLocked = TRUE; // Reset the FDB's inactive time pDb->uiInactiveTime = 0; // See if we should resize the hash table. if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || (gv_FlmSysData.RCacheMgr.Usage.uiCount < FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) { if (RC_BAD( rc = flmRcaRehash())) { goto Exit; } } Start_Find: flmRcaFindRec( pFile, pDb->hWaitSem, uiContainer, uiDrn, uiCurrTransId, bDontPoisonCache, &uiNumLooks, &pRCache, &pNewerRCache, &pOlderRCache); if (pRCache) { if( ppRecord) { goto Found_Record; } goto Exit; } // Did not find the record, fetch from disk, if OK to do so. if (!bOkToGetFromDisk || !ppRecord) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } // Code to handle case where we are not in a transaction. // If we are already in a transaction, we will do the // call to fdbInit below - AFTER allocating the RCACHE, etc. if( pbTransStarted && pDb->uiTransType == FLM_NO_TRANS) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); bRCacheMutexLocked = FALSE; if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, pbTransStarted))) { fdbExit( pDb); goto Exit; } bInitializedFdb = TRUE; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bRCacheMutexLocked = TRUE; uiCurrTransId = pDb->LogHdr.uiCurrTransID; goto Start_Find; } // Increment the number of faults only if we retrieve the record from disk. gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults++; gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; // Create a place holder for the object. if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) { goto Exit; } pRCache->uiDrn = uiDrn; pRCache->uiContainer = uiContainer; // Set the FFILE so that other threads looking for this record in // cache will find it and wait until the read has completed. If // the FFILE is not set, other threads will attempt their own read, // because they won't match a NULL FFILE. The result of not setting // the FFILE is that multiple copies of the same version of a particular // record could end up in cache. pRCache->pFile = pFile; flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, !bDontPoisonCache); RCA_SET_READING_IN( pRCache->uiFlags); RCA_INCR_USE_COUNT( pRCache->uiFlags); pRCache->pNotifyList = NULL; // Unlock mutex before reading in from disk. f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); bRCacheMutexLocked = FALSE; if( pbTransStarted && !bInitializedFdb) { if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, pbTransStarted))) { fdbExit( pDb); goto Notify_Waiters; } bInitializedFdb = TRUE; } // Read record from disk. #ifdef FLM_DBG_LOG flmDbgLogWrite( pFile->uiFFileId, uiContainer, uiDrn, pDb->LogHdr.uiCurrTransID, "RRD"); #endif if (pStack) { rc = FSReadElement( pDb, &pDb->TempPool, pLFile, uiDrn, pStack, TRUE, ppRecord, &uiLowTransId, &bMostCurrent); } else if ((pLFile) || (RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))) { rc = FSReadRecord( pDb, pLFile, uiDrn, ppRecord, &uiLowTransId, &bMostCurrent); } Notify_Waiters: // Relock mutex f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bRCacheMutexLocked = TRUE; // If read was successful, link the record to its place in // the FFILE list and coalesce any versions that overlap // this one. if (RC_OK( rc)) { flmRcaLinkToFFILE( pRCache, pDb->pFile, pDb, uiLowTransId, bMostCurrent); } RCA_UNSET_READING_IN( pRCache->uiFlags); // Notify any threads waiting for the read to complete. pNotify = pRCache->pNotifyList; pRCache->pNotifyList = NULL; if (pNotify) { flmRcaNotify( pNotify, (RCACHE *)((RC_BAD( rc)) ? NULL : pRCache), rc); } RCA_DECR_USE_COUNT( pRCache->uiFlags); // If we did not succeed, free the RCACHE structure. if (RC_BAD( rc)) { flmRcaFreeCache( pRCache, FALSE); goto Exit; } // If this item was purged while we were reading it in, // start over with the search. if (RCA_IS_PURGED( pRCache->uiFlags)) { if (!RCA_IS_IN_USE( pRCache->uiFlags)) { flmRcaFreePurged( pRCache); } // Start over with the find - this one has // been marked for purging. pNewRecord = NULL; pOldRecord = NULL; bGotFromDisk = FALSE; goto Start_Find; } // When reading from disk, no need to verify that we // are getting the app implementation - because we // always will. bGotFromDisk = TRUE; pRecord = *ppRecord; flmRcaSetRecord( pRCache, pRecord); Found_Record: if (!bGotFromDisk) { if( (pRecord = *ppRecord) != pRCache->pRecord) { if (*ppRecord) { FlmRecordExt::Release( *ppRecord, bRCacheMutexLocked); } pRecord = *ppRecord = pRCache->pRecord; pRecord->AddRef(); } } // Clean up cache, if necessary. if (gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated > gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) { flmRcaReduceCache( bRCacheMutexLocked); } Exit: if (pNewRecord) { FlmRecordExt::Release( pNewRecord, bRCacheMutexLocked); } if (bRCacheMutexLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } if (bInitializedFdb) { fdbExit(pDb); } return( rc); } /**************************************************************************** Desc: This routine inserts a record into the record cache. This is ONLY called by FlmRecordModify or FlmRecordAdd. In the case of a modify, this should replace any uncommitted version of the record that may have been put there by a prior call to FlmRecordModify. ****************************************************************************/ RCODE flmRcaInsertRec( FDB * pDb, LFILE * pLFile, FLMUINT uiDrn, FlmRecord * pRecord) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiContainer = pLFile->uiLfNum; FLMBOOL bMutexLocked = FALSE; RCACHE * pRCache; RCACHE * pNewerRCache; RCACHE * pOlderRCache; FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE ? TRUE : FALSE; if (pLFile->bMakeFieldIdTable && !pRecord->fieldIdTableEnabled()) { // NOTE: createFieldIdTable will call sortFieldIdTable(). if (RC_BAD( rc = pRecord->createFieldIdTable( TRUE))) { goto Exit; } } else { pRecord->sortFieldIdTable(); if (getFieldIdTableItemCount( pRecord->getFieldIdTbl()) != getFieldIdTableArraySize( pRecord->getFieldIdTbl())) { if (RC_BAD( rc = pRecord->truncateFieldIdTable())) { goto Exit; } } } flmAssert( uiDrn != 0); // Lock the mutex f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bMutexLocked = TRUE; if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || (gv_FlmSysData.RCacheMgr.Usage.uiCount < FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) { if (RC_BAD( rc = flmRcaRehash())) { goto Exit; } } // See if we can find the record in cache flmRcaFindRec( pFile, pDb->hWaitSem, uiContainer, uiDrn, pDb->LogHdr.uiCurrTransID, bDontPoisonCache, NULL, &pRCache, &pNewerRCache, &pOlderRCache); if (pRCache) { // If we found the last committed version, instead of replacing it, // we want to change its high transaction ID, and go create a new // record to put in cache. if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) { // pOlderRCache and pRCache should be the same at this point if we // found something. Furthermore, the high transaction ID on what // we found better be -1 - most current version. flmAssert( pOlderRCache == pRCache); flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); RCA_SET_LATEST_VER( pOlderRCache->uiFlags); flmRcaUnlinkFromFile( pOlderRCache); flmRcaLinkToFileAtHead( pOlderRCache, pFile); } else { // Found latest UNCOMMITTED VERSION - replace it. if (RC_BAD( rc = pRecord->compressMemory())) { goto Exit; } // Replace the old record data with the new record data. flmRcaSetRecord( pRCache, pRecord); // Make sure we set the "uncommitted" flag and move the record // to the head of the FFILE's list. if (!RCA_IS_UNCOMMITTED( pRCache->uiFlags)) { RCA_SET_UNCOMMITTED( pRCache->uiFlags); flmRcaUnlinkFromFile( pRCache); flmRcaLinkToFileAtHead( pRCache, pFile); } // Will not have already been put at MRU if bDonPoisonCache is TRUE. if (pRCache->pPrevInGlobal) { flmRcaUnlinkFromGlobal( pRCache); flmRcaLinkToGlobalAsMRU( pRCache); } goto Exit; } } // We are positioned to insert the new record. For an update, it // must always be the newest version. flmAssert( !pNewerRCache); if (RC_BAD( rc = pRecord->compressMemory())) { goto Exit; } // Allocate a new RCACHE structure. if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) { goto Exit; } // Set the DRN and container for the structure. pRCache->uiDrn = uiDrn; pRCache->uiContainer = uiContainer; pRCache->pFile = pFile; // Link into the global list and hash bucket. flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, TRUE); // Link to its FFILE and coalesce out duplicates. // NOTE: This routine automatically puts an uncommitted version // at the head of the FFILE's list and sets the uncommitted flag. flmRcaLinkToFFILE( pRCache, pFile, pDb, pDb->LogHdr.uiCurrTransID, TRUE); // Set the record data pointer for the RCACHE structure. This will also // release the old data pointer and set the read only flag and the mutex // for the record data. Also updates memory usage fields in RCacheMgr. flmRcaSetRecord( pRCache, pRecord); // Clean up cache, if necessary. flmRcaReduceCache( bMutexLocked); Exit: if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } return( rc); } /**************************************************************************** Desc: This routine is called by FlmRecordDelete to remove a record from cache. If there is an uncommitted version of the record, it should remove that version from cache. If the last committed version is in cache, it should set the high transaction ID on that version to be one less than the transaction ID of the update transaction that is doing the FlmRecordDelete call. ****************************************************************************/ RCODE flmRcaRemoveRec( FDB * pDb, FLMUINT uiContainer, FLMUINT uiDrn) { RCODE rc = FERR_OK; FLMBOOL bMutexLocked = FALSE; RCACHE * pRCache; RCACHE * pNewerRCache; RCACHE * pOlderRCache; FFILE * pFile = pDb->pFile; flmAssert( uiDrn != 0); // Lock the semaphore bMutexLocked = TRUE; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || (gv_FlmSysData.RCacheMgr.Usage.uiCount < FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) { if (RC_BAD( rc = flmRcaRehash())) { goto Exit; } } // See if we can find the record in cache flmRcaFindRec( pFile, pDb->hWaitSem, uiContainer, uiDrn, pDb->LogHdr.uiCurrTransID, FALSE, NULL, &pRCache, &pNewerRCache, &pOlderRCache); if (pRCache) { // FlmRecordDelete is calling this routine, so we determine if we found // the last committed version or a record that was added by this same // transaction. If we found the last committed version, set its high // transaction ID. Otherwise, remove the record from cache. if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) { // pOlderRCache and pRCache should be the same at this point if we // found something. Furthermore, the high transaction ID on what // we found better be -1 - most current version. flmAssert( pOlderRCache == pRCache); flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); RCA_SET_LATEST_VER( pOlderRCache->uiFlags); flmRcaUnlinkFromFile( pOlderRCache); flmRcaLinkToFileAtHead( pOlderRCache, pFile); } else { flmRcaFreeCache( pRCache, (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) ? TRUE : FALSE)); } } // Take the opportunity to clean up cache. flmRcaReduceCache( bMutexLocked); Exit: if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } return( rc); } /**************************************************************************** Desc: This routine is called when an FFILE structure is going to be removed from the shared memory area. At that point, we also need to get rid of all records that have been cached for that FFILE. ****************************************************************************/ void flmRcaFreeFileRecs( FFILE * pFile) { FLMUINT uiNumFreed = 0; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); while (pFile->pFirstRecord) { flmRcaFreeCache( pFile->pFirstRecord, RCA_IS_IN_USE( pFile->pFirstRecord->uiFlags) ? TRUE : FALSE); // Release the CPU every 100 records freed. if (uiNumFreed < 100) { uiNumFreed++; } else { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_yieldCPU(); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); uiNumFreed = 0; } } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } /**************************************************************************** Desc: This routine is called when an update transaction aborts. At that point, we need to get rid of any uncommitted versions of records in the record cache. ****************************************************************************/ void flmRcaAbortTrans( FDB * pDb) { FFILE * pFile = pDb->pFile; RCACHE * pRCache; RCACHE * pOlderVersion; FLMUINT uiOlderTransId = pDb->LogHdr.uiCurrTransID - 1; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); pRCache = pFile->pFirstRecord; while (pRCache) { if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) { if (RCA_IS_LATEST_VER( pRCache->uiFlags)) { flmRcaSetTransID( pRCache, 0xFFFFFFFF); RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); RCA_UNSET_LATEST_VER( pRCache->uiFlags); flmRcaUnlinkFromFile( pRCache); flmRcaLinkToFileAtEnd( pRCache, pFile); } else { // Save the older version - we may be changing its // high transaction ID back to 0xFFFFFFFF pOlderVersion = pRCache->pOlderVersion; // Free the uncommitted version. flmRcaFreeCache( pRCache, (FLMBOOL)((RCA_IS_IN_USE( pRCache->uiFlags) || RCA_IS_READING_IN( pRCache->uiFlags)) ? TRUE : FALSE)); // If the older version has a high transaction ID that // is exactly one less than our current transaction, // it is the most current version. Hence, we need to // change its high transaction ID back to 0xFFFFFFFF. if ((pOlderVersion) && (pOlderVersion->uiHighTransId == uiOlderTransId)) { flmRcaSetTransID( pOlderVersion, 0xFFFFFFFF); } } pRCache = pFile->pFirstRecord; } else { // We can stop when we hit a committed version, because // uncommitted versions are always linked in together at // the head of the list. break; } } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } /**************************************************************************** Desc: This routine is called when an update transaction commits. At that point, we need to unset the "uncommitted" flag on any records currently in record cache for the FFILE. ****************************************************************************/ void flmRcaCommitTrans( FDB * pDb) { RCACHE * pRCache; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); pRCache = pDb->pFile->pFirstRecord; while (pRCache) { if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) { RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); RCA_UNSET_LATEST_VER( pRCache->uiFlags); pRCache = pRCache->pNextInFile; } else { // We can stop when we hit a committed version, because // uncommitted versions are always linked in together at // the head of the list. break; } } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } /**************************************************************************** Desc: This routine is called when a container in the database is deleted. All records in record cache that are in that container must be removed from cache. ****************************************************************************/ void flmRcaRemoveContainerRecs( FDB * pDb, FLMUINT uiContainer) { FFILE * pFile = pDb->pFile; RCACHE * pRCache; RCACHE * pPrevRCache; FLMUINT uiTransId = pDb->LogHdr.uiCurrTransID; f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; // Stay in the loop until we have freed all old records, or // we have run through the entire list. while (pRCache) { // Save the pointer to the previous entry in the list because // we may end up unlinking pRCache below, in which case we would // have lost the previous entry. pPrevRCache = pRCache->pPrevInGlobal; // Only look at records in this container and this database. if ((pRCache->uiContainer == uiContainer) && (pRCache->pFile == pFile)) { // Only look at the most current versions. if (pRCache->uiHighTransId == 0xFFFFFFFF) { // Better not be a newer version. flmAssert( pRCache->pNewerVersion == NULL); if (pRCache->uiLowTransId < uiTransId) { // This version was not added or modified by this // transaction so it's high transaction ID should simply // be set to one less than the current transaction ID. flmRcaSetTransID( pRCache, uiTransId - 1); flmAssert( pRCache->uiHighTransId >= pRCache->uiLowTransId); RCA_SET_UNCOMMITTED( pRCache->uiFlags); RCA_SET_LATEST_VER( pRCache->uiFlags); flmRcaUnlinkFromFile( pRCache); flmRcaLinkToFileAtHead( pRCache, pFile); } else { // The record was added or modified in this // transaction. Simply remove it from cache. flmRcaFreeCache( pRCache, (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) ? TRUE : FALSE)); } } else { // If not most current version, the record's high transaction // ID better already be less than transaction ID. flmAssert( pRCache->uiHighTransId < uiTransId); } } pRCache = pPrevRCache; } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); } /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG FSTATIC RCODE flmRcaCheck( FDB * pDb, FLMUINT uiContainer, FLMUINT uiDrn) { LFILE * pLFile; FlmRecord * pRecord = NULL; FLMUINT uiLowTransId; FLMBOOL bMostCurrent; RCODE rc; if( RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) { rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, &uiLowTransId, &bMostCurrent); } if( pRecord) { pRecord->Release(); } return( rc); } #endif /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_RecRelocator::canRelocate( void * pvAlloc) { FlmRecord * pRec = (FlmRecord *)pvAlloc; if( pRec->getRefCount() == 1 && pRec->isCached()) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: ****************************************************************************/ void F_RecRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { FlmRecord * pOldRec = (FlmRecord *)pvOldAlloc; FlmRecord * pNewRec = (FlmRecord *)pvNewAlloc; RCACHE * pRCache; RCACHE * pVersion; flmAssert( pOldRec->getRefCount() == 1); flmAssert( pOldRec->isCached()); flmAssert( pvNewAlloc < pvOldAlloc); // Update the record pointer in the data buffer if( pNewRec->m_pucBuffer) { flmAssert( *((FlmRecord **)pOldRec->m_pucBuffer) == pOldRec); *((FlmRecord **)pNewRec->m_pucBuffer) = pNewRec; } if( pNewRec->m_pucFieldIdTable) { flmAssert( *((FlmRecord **)pOldRec->m_pucFieldIdTable) == pOldRec); *((FlmRecord **)pNewRec->m_pucFieldIdTable) = pNewRec; } // Find the record pRCache = *(FLM_RCA_HASH( pNewRec->m_uiRecordID)); while( pRCache) { if( pRCache->uiDrn == pNewRec->m_uiRecordID) { pVersion = pRCache; while( pVersion) { if( pVersion->pRecord == pOldRec) { pVersion->pRecord = pNewRec; goto Done; } pVersion = pVersion->pOlderVersion; } } pRCache = pRCache->pNextInBucket; } Done: flmAssert( pRCache); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_RecBufferRelocator::canRelocate( void * pvAlloc) { FlmRecord * pRec = *((FlmRecord **)pvAlloc); flmAssert( pRec->m_pucBuffer == pvAlloc || pRec->m_pucFieldIdTable == pvAlloc); if( pRec->getRefCount() == 1 && pRec->isCached()) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: ****************************************************************************/ void F_RecBufferRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); flmAssert( pRec->getRefCount() == 1); flmAssert( pRec->isCached()); flmAssert( pvNewAlloc < pvOldAlloc); // Update the buffer pointer in the record if (pRec->m_pucBuffer == pvOldAlloc) { pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; } else if (pRec->m_pucFieldIdTable == pvOldAlloc) { pRec->m_pucFieldIdTable = (FLMBYTE *)pvNewAlloc; } else { flmAssert( 0); } } /**************************************************************************** Desc: Notes: This routine assumes the rcache mutex is locked ****************************************************************************/ FLMBOOL F_RCacheRelocator::canRelocate( void * pvAlloc) { RCACHE * pRCache = (RCACHE *)pvAlloc; if( RCA_IS_IN_USE( pRCache->uiFlags) || RCA_IS_READING_IN( pRCache->uiFlags)) { return( FALSE); } return( TRUE); } /**************************************************************************** Desc: Fixes up all pointers needed to allow an RCACHE struct to be moved to a different location in memory Notes: This routine assumes the rcache mutex is locked ****************************************************************************/ void F_RCacheRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { RCACHE * pOldRCache = (RCACHE *)pvOldAlloc; RCACHE * pNewRCache = (RCACHE *)pvNewAlloc; RCACHE ** ppBucket; RCACHE_MGR * pRCacheMgr = &gv_FlmSysData.RCacheMgr; FFILE * pFile = pOldRCache->pFile; flmAssert( !RCA_IS_IN_USE( pOldRCache->uiFlags)); flmAssert( !RCA_IS_READING_IN( pOldRCache->uiFlags)); flmAssert( !pOldRCache->pNotifyList); if( pNewRCache->pPrevInFile) { pNewRCache->pPrevInFile->pNextInFile = pNewRCache; } if( pNewRCache->pNextInFile) { pNewRCache->pNextInFile->pPrevInFile = pNewRCache; } if( pNewRCache->pPrevInGlobal) { pNewRCache->pPrevInGlobal->pNextInGlobal = pNewRCache; } if( pNewRCache->pNextInGlobal) { pNewRCache->pNextInGlobal->pPrevInGlobal = pNewRCache; } if( pNewRCache->pPrevInBucket) { pNewRCache->pPrevInBucket->pNextInBucket = pNewRCache; } if( pNewRCache->pNextInBucket) { pNewRCache->pNextInBucket->pPrevInBucket = pNewRCache; } if( pNewRCache->pOlderVersion) { pNewRCache->pOlderVersion->pNewerVersion = pNewRCache; } if( pNewRCache->pNewerVersion) { pNewRCache->pNewerVersion->pOlderVersion = pNewRCache; } if( pNewRCache->pPrevInHeapList) { pNewRCache->pPrevInHeapList->pNextInHeapList = pNewRCache; } if( pNewRCache->pNextInHeapList) { pNewRCache->pNextInHeapList->pPrevInHeapList = pNewRCache; } ppBucket = FLM_RCA_HASH( pOldRCache->uiDrn); if( *ppBucket == pOldRCache) { *ppBucket = pNewRCache; } if( pRCacheMgr->pHeapList == pOldRCache) { pRCacheMgr->pHeapList = pNewRCache; } if( pRCacheMgr->pPurgeList == pOldRCache) { pRCacheMgr->pPurgeList = pNewRCache; } if( pRCacheMgr->pMRURecord == pOldRCache) { pRCacheMgr->pMRURecord = pNewRCache; } if( pRCacheMgr->pLRURecord == pOldRCache) { pRCacheMgr->pLRURecord = pNewRCache; } if( pFile) { if( pFile->pFirstRecord == pOldRCache) { pFile->pFirstRecord = pNewRCache; } if( pFile->pLastRecord == pOldRCache) { pFile->pLastRecord = pNewRCache; } } #ifdef FLM_DEBUG f_memset( pOldRCache, 0, sizeof( RCACHE)); #endif } libflaim-4.9.966/src/fqsrch.cpp0000644000175000017500000007467110510774540017665 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query search // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqsrch.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" // Call progress callback once every second #define STATUS_CB_INTERVAL 1 FSTATIC RCODE flmCurSetSubQuery( CURSOR * pCursor, SUBQUERY * pSubQuery); FSTATIC int DRNCompareFunc( void * pvData1, void * pvData2, void * pvUserValue); FSTATIC RCODE flmCurRecValidate( eFlmFuncs eFlmFuncId, CURSOR * pCursor, SUBQUERY * pSubQuery, FLMUINT * puiSkipCount, FLMUINT * puiCount, FLMBOOL * pbReturnRecOK); FSTATIC RCODE flmCurRetrieveRec( FDB * pDb, SUBQUERY * pSubQuery, FLMUINT uiContainer); FSTATIC RCODE flmCurSearchIndex( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount, FLMBOOL bGettingRecord); FSTATIC RCODE flmCurSearchPredicate( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount); FSTATIC RCODE flmCurSearchContainer( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount); FSTATIC RCODE flmCurEvalSingleRec( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount); /**************************************************************************** Desc: This routine will do all the setup needed to establish a sub-query as the current subquery for the query. ****************************************************************************/ FSTATIC RCODE flmCurSetSubQuery( CURSOR * pCursor, SUBQUERY * pSubQuery ) { RCODE rc = FERR_OK; pCursor->pCurrSubQuery = pSubQuery; // Do setup associated with a change of sub-queries. f_memset( &pSubQuery->SQStatus, 0, sizeof( FCURSOR_SUBQUERY_STATUS)); pSubQuery->SQStatus.hDb = (HFDB)pCursor->pDb; pSubQuery->SQStatus.uiContainerNum = pCursor->uiContainer; if (pSubQuery->OptInfo.eOptType == QOPT_USING_INDEX) { pSubQuery->SQStatus.uiIndexNum = pSubQuery->OptInfo.uiIxNum; // Make sure that all of the keys have been committed. if (RC_BAD( rc = KYFlushKeys( pCursor->pDb))) { goto Exit; } } else if (pSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ) { pSubQuery->bRecReturned = FALSE; } Exit: return( rc); } /**************************************************************************** Desc: DRN comparison callback function for dynamic result set. ****************************************************************************/ FSTATIC int DRNCompareFunc( void * pvData1, void * pvData2, void * // pvUserValue ) { if( *((FLMUINT *)pvData1) < *((FLMUINT *)pvData2)) { return -1; } else if( *((FLMUINT *)pvData1) > *((FLMUINT *)pvData2)) { return 1; } return 0; } /**************************************************************************** Desc: Validate a record that has passed the search criteria. This routine checks the record against the validation function and also checks it against the result set, if there is one. ****************************************************************************/ FSTATIC RCODE flmCurRecValidate( eFlmFuncs eFlmFuncId, CURSOR * pCursor, SUBQUERY * pSubQuery, FLMUINT * puiSkipCount, FLMUINT * puiCount, FLMBOOL * pbReturnRecOK) { RCODE rc = FERR_OK; FLMBOOL bSavedInvisTrans; // At this point, we have a record that has passed all the selection // criteria in the query. If we have a record validator callback, // call it. if (pCursor->fnRecValidator) { CB_ENTER( pCursor->pDb, &bSavedInvisTrans); *pbReturnRecOK = (pCursor->fnRecValidator)( eFlmFuncId, (HFDB)pCursor->pDb, pCursor->uiContainer, pSubQuery->pRec, NULL, pCursor->RecValData, &rc); CB_EXIT( pCursor->pDb, bSavedInvisTrans); if (!(*pbReturnRecOK)) { pSubQuery->SQStatus.uiNumRejectedByCallback++; rc = FERR_OK; goto Exit; } else if (RC_BAD( rc)) { goto Exit; } } // Record passed all criteria -- check for dups if necessary. if (pCursor->bEliminateDups) { // Setup the result set if( !pCursor->pDRNSet) { char szTmpDir[ F_PATH_MAX_SIZE]; szTmpDir[ 0] = 0; if ((pCursor->pDRNSet = f_new F_DynSearchSet) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( gv_FlmSysData.bTempDirSet && gv_FlmSysData.szTempDir[ 0]) { if( RC_BAD( rc = flmGetTmpDir( szTmpDir))) { goto Exit; } } if( !szTmpDir[ 0]) { if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pCursor->pDb->pFile->pszDbPath, szTmpDir, NULL))) { goto Exit; } } if (RC_BAD( rc = pCursor->pDRNSet->setup( szTmpDir, sizeof( FLMUINT)))) { goto Exit; } pCursor->pDRNSet->setCompareFunc( DRNCompareFunc, NULL); } if (RC_BAD( rc = pCursor->pDRNSet->addEntry( &pSubQuery->uiDrn))) { if (rc == NE_FLM_EXISTS) { *pbReturnRecOK = FALSE; rc = FERR_OK; pSubQuery->SQStatus.uiDupsEliminated++; } goto Exit; } } // If we get here, the record passed all possible criteria. // However, it will not be returned if we are skipping // or counting. pSubQuery->SQStatus.uiMatchedCnt++; // If this is a request to skip records, make sure that the // correct number have been skipped. if (puiSkipCount && (--(*puiSkipCount) > 0)) { // If we are skipping, we need to continue past this // record. *pbReturnRecOK = FALSE; goto Exit; } // If we are counting, we also want to continue past this // record. if (puiCount) { (*puiCount)++; *pbReturnRecOK = FALSE; goto Exit; } // If we get to here, we are going to keep the record and // pass it back out to the caller. *pbReturnRecOK = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Does the actual reading of a record from cache or disk. ****************************************************************************/ FSTATIC RCODE flmCurRetrieveRec( FDB * pDb, SUBQUERY * pSubQuery, FLMUINT uiContainer ) { RCODE rc = FERR_OK; if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, uiContainer, pSubQuery->uiDrn, TRUE, NULL, NULL, &pSubQuery->pRec))) { goto Exit; } pSubQuery->bRecIsAKey = FALSE; Exit: if (RC_BAD( rc) && pSubQuery->pRec) { pSubQuery->pRec->Release(); pSubQuery->pRec = NULL; pSubQuery->uiDrn = 0; } return( rc); } /**************************************************************************** Desc: Searches an index for matching record. ****************************************************************************/ FSTATIC RCODE flmCurSearchIndex( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount, FLMBOOL bGettingRecord ) { RCODE rc = FERR_OK; FDB * pDb = pCursor->pDb; FSIndexCursor * pFSIndexCursor = pSubQuery->pFSIndexCursor; FLMUINT uiCBTimer = 0; FLMUINT uiCurrCBTime; FLMBOOL bReturnRecOK; FLMBOOL bDoKeyMatch; FLMBOOL bDoRecMatch; FLMBOOL bNeedToGetFullRec; FLMBOOL bSaveDoRecMatch; FLMBOOL bSaveNeedToGetFullRec; FLMUINT uiCPUReleaseCnt = 0; FLMBOOL bSavedInvisTrans; FLMUINT uiStartTime = FLM_GET_TIMER(); FLMUINT uiCurrTime; FLMUINT uiTimeLimit = pCursor->uiTimeLimit; if (pCursor->fnStatus) { uiCBTimer = FLM_SECS_TO_TIMER_UNITS( STATUS_CB_INTERVAL); } // Set up initial search parameters. bSaveDoRecMatch = pSubQuery->OptInfo.bDoRecMatch; bDoKeyMatch = pSubQuery->OptInfo.bDoKeyMatch; bSaveNeedToGetFullRec = !bSaveDoRecMatch && bGettingRecord; pSubQuery->uiDrn = 0; // Loop to evaluate keys and records. for (;;) { bDoRecMatch = bSaveDoRecMatch; bNeedToGetFullRec = bSaveNeedToGetFullRec; // Release the CPU periodically to prevent CPU hog if (((++uiCPUReleaseCnt) & 0x1F) == 0) { f_yieldCPU(); } // See if we have timed out if (uiTimeLimit) { uiCurrTime = FLM_GET_TIMER(); // Use greater than to compare because if the timeout was one // second, we want to be sure and give it at least one // full second. We would rather give it an extra second // than shortchange it. if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > uiTimeLimit) { rc = RC_SET( FERR_TIMEOUT); goto Exit; } } // Do the progress callback if enough time has elapsed. pSubQuery->SQStatus.uiProcessedCnt++; if (pCursor->fnStatus) { uiCurrCBTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrCBTime, pCursor->uiLastCBTime) > uiCBTimer) { CB_ENTER( pDb, &bSavedInvisTrans); rc = (pCursor->fnStatus)( FLM_SUBQUERY_STATUS, (void *)&pSubQuery->SQStatus, (void *)FALSE, pCursor->StatusData); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } pCursor->uiLastCBTime = FLM_GET_TIMER(); } } // Get the next or previous key or reference if (bFirstRead) { // A value in uiCurrKeyMatch means we have not evaluated the // current key. pSubQuery->uiCurrKeyMatch = 0; rc = (RCODE)((bReadForward) ? pFSIndexCursor->firstKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn) : pFSIndexCursor->lastKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn)); if (RC_OK( rc)) { bFirstRead = FALSE; } pSubQuery->bFirstReference = TRUE; } else if (!pSubQuery->bFirstReference) { rc = (RCODE)((bReadForward) ? pFSIndexCursor->nextRef( pDb, &pSubQuery->uiDrn) : pFSIndexCursor->prevRef( pDb, &pSubQuery->uiDrn)); if ((bReadForward && rc == FERR_EOF_HIT) || (!bReadForward && rc == FERR_BOF_HIT)) { rc = FERR_OK; pSubQuery->bFirstReference = TRUE; goto Get_Key; } // If we don't have a current key, need to get it. // pSubQuery->pRec could be NULL if we returned the last // record that passed the criteria back to the caller. In // that case we just need to get it from the index cursor. if (!pSubQuery->pRec || !pSubQuery->bRecIsAKey) { if (RC_BAD( rc = pFSIndexCursor->currentKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn))) { goto Exit; } pSubQuery->bRecIsAKey = TRUE; // These should have been set by currentKey flmAssert( pSubQuery->pRec->getContainerID() == pCursor->uiContainer); flmAssert( pSubQuery->pRec->getID() == pSubQuery->uiDrn); } else { // Container should have already been set. flmAssert( pSubQuery->pRec->getContainerID() == pCursor->uiContainer); // Need to modify DRN to the newly retrieved DRN. pSubQuery->pRec->setID( pSubQuery->uiDrn); } } else { Get_Key: // A value in uiCurrKeyMatch means we have not evaluated the // current key. pSubQuery->uiCurrKeyMatch = 0; rc = (RCODE)((bReadForward) ? pFSIndexCursor->nextKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn) : pFSIndexCursor->prevKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn)); } if (RC_BAD( rc)) { goto Exit; } if (pSubQuery->bFirstReference) { pSubQuery->SQStatus.uiKeysTraversed++; pSubQuery->bRecIsAKey = TRUE; } pSubQuery->SQStatus.uiRefsTraversed++; // Make sure the container of the key matches the container // of the query. NOTE: flmCurEvalCriteria also does this, but // we need to do the test here in case it is not called for // the key below. In that case, we want to avoid retrieving // the record. Furthermore, even if bHaveDrnFlds is TRUE, // there is no need to check each DRN of this key, because // it is guaranteed that none of them will match, so in this // case we simply want to skip the key entirely. if (pSubQuery->pRec->getContainerID() != pCursor->uiContainer) { pSubQuery->uiCurrKeyMatch = FLM_FALSE; // If bFirstReference is TRUE at this point, this is the // first reference of this key, so we need to increment // the uiNumKeysRejected counter as well as the // uiNumRefsRejected counter. if (pSubQuery->bFirstReference) { pSubQuery->SQStatus.uiKeysRejected++; } pSubQuery->SQStatus.uiRefsRejected++; // Set bFirstReference to TRUE so it will skip to the next // key. pSubQuery->bFirstReference = TRUE; continue; } // See if we need to evaluate the key against the criteria. if (bDoKeyMatch && (pSubQuery->bFirstReference || pSubQuery->bHaveDrnFlds)) { if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pSubQuery->pRec, TRUE, &pSubQuery->uiCurrKeyMatch))) { if (rc == FERR_TRUNCATED_KEY) { rc = FERR_OK; pSubQuery->uiCurrKeyMatch = FLM_UNK; } else { goto Exit; } } flmAssert( pSubQuery->uiCurrKeyMatch != 0); if (pSubQuery->uiCurrKeyMatch == FLM_FALSE) { // If bFirstReference is TRUE at this point, this is the // first reference of this key, so we need to increment // the uiNumKeysRejected counter as well as the // uiNumRefsRejected counter. if (pSubQuery->bFirstReference) { pSubQuery->SQStatus.uiKeysRejected++; } pSubQuery->SQStatus.uiRefsRejected++; // If we must evaluate DRN fields, we need to go to the // next reference, so we set bFirstReference to FALSE. // Otherwise, we set it to TRUE so it will skip to the // next key. pSubQuery->bFirstReference = !pSubQuery->bHaveDrnFlds; // Continue loop to process next key or reference continue; } } // If we did a key match, see if it passed or was unknown. // If it passed, no need to do the record match. If it // was unknown, must do record match. if (pSubQuery->uiCurrKeyMatch == FLM_TRUE) { bDoRecMatch = FALSE; } else if (pSubQuery->uiCurrKeyMatch == FLM_UNK) { bDoRecMatch = TRUE; } else { // NOTE: If uiCurrKeyMatch is FLM_FALSE, we should never get // to this point - it will continue up above. Thus, at this // point, uiCurrKeyMatch should be zero, meaning that no // evaluation was done on the current key - which also means // that bDoKeyMatch better be FALSE at this point. // Even though bDoKeyMatch is FALSE, bDoRecMatch needs to be left // unchanged. If bDoRecMatch is FALSE, we know from the query // criteria that all keys we read through will automatically // match. If bDoRecMatch is TRUE, we have previously decided // that we MUST NOT do a key match, but force a record match // instead. This would be the case where the comparison involves // a substring that is not the first substring - you can't do a // key match in that case because it will come out FALSE. flmAssert( pSubQuery->uiCurrKeyMatch == 0 && !bDoKeyMatch); } // Set up to get next reference the next time around. pSubQuery->bFirstReference = FALSE; // Retrieve and evalute the data record if necessary. if (bDoRecMatch) { FLMUINT uiRecMatch; // Because we are fetching the full record here, no need to below. bNeedToGetFullRec = FALSE; // Retrieve the record if (RC_OK( rc = flmCurRetrieveRec( pDb, pSubQuery, pCursor->uiContainer))) { pSubQuery->SQStatus.uiRecsFetchedForEval++; } else { if (rc == FERR_NOT_FOUND) { pSubQuery->SQStatus.uiRecsNotFound++; rc = RC_SET( FERR_NO_REC_FOR_KEY); } goto Exit; } // Evaluate the record against the query criteria if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pSubQuery->pRec, FALSE, &uiRecMatch))) { goto Exit; } if (uiRecMatch != FLM_TRUE) { pSubQuery->SQStatus.uiRecsRejected++; // Continue loop to process next key or reference continue; } } // If we must return full records, and we have not yet fetched // the record, retrieve the record now. if (bNeedToGetFullRec) { if (RC_OK( rc = flmCurRetrieveRec( pDb, pSubQuery, pCursor->uiContainer))) { pSubQuery->SQStatus.uiRecsFetchedForView++; } else { if (rc == FERR_NOT_FOUND) { pSubQuery->SQStatus.uiRecsNotFound++; rc = RC_SET( FERR_NO_REC_FOR_KEY); } goto Exit; } } // Record has passed all of the sub-query criteria. See if // it passes our validation tests. if (RC_BAD( rc = flmCurRecValidate( eFlmFuncId, pCursor, pSubQuery, puiSkipCount, puiCount, &bReturnRecOK))) { goto Exit; } if (bReturnRecOK) { break; } } Exit: return( rc); } /**************************************************************************** Desc: Searches a user predicate for matching record. ****************************************************************************/ FSTATIC RCODE flmCurSearchPredicate( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount ) { RCODE rc = FERR_OK; FDB * pDb = pCursor->pDb; FlmUserPredicate * pPredicate = pSubQuery->pPredicate; FLMBOOL bSavedInvisTrans; FLMUINT uiCBTimer = 0; FLMUINT uiCurrCBTime; FLMBOOL bReturnRecOK; FLMUINT uiCPUReleaseCnt = 0; FLMUINT uiStartTime = FLM_GET_TIMER(); FLMUINT uiCurrTime; FLMUINT uiTimeLimit = pCursor->uiTimeLimit; FLMUINT uiRecMatch; if (pCursor->fnStatus) { uiCBTimer = FLM_SECS_TO_TIMER_UNITS( STATUS_CB_INTERVAL); } // Set up initial search parameters. pSubQuery->uiDrn = 0; // Loop to evaluate keys and records. for (;;) { // Release the CPU periodically to prevent CPU hog if (((++uiCPUReleaseCnt) & 0x1F) == 0) { f_yieldCPU(); } // See if we have timed out if (uiTimeLimit) { uiCurrTime = FLM_GET_TIMER(); // Use greater than to compare because if the timeout was one // second, we want to be sure and give it at least one // full second. We would rather give it an extra second // than shortchange it. if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > uiTimeLimit) { rc = RC_SET( FERR_TIMEOUT); goto Exit; } } // Do the progress callback if enough time has elapsed. pSubQuery->SQStatus.uiProcessedCnt++; if (pCursor->fnStatus) { uiCurrCBTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrCBTime, pCursor->uiLastCBTime) > uiCBTimer) { CB_ENTER( pDb, &bSavedInvisTrans); rc = (pCursor->fnStatus)( FLM_SUBQUERY_STATUS, (void *)&pSubQuery->SQStatus, (void *)FALSE, pCursor->StatusData); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } pCursor->uiLastCBTime = FLM_GET_TIMER(); } } // Can't go backwards with embedded predicates CB_ENTER( pDb, &bSavedInvisTrans); if (bFirstRead) { if (bReadForward) { rc = pPredicate->firstRecord( (HFDB)pDb, &pSubQuery->uiDrn, &pSubQuery->pRec); } else { rc = pPredicate->lastRecord( (HFDB)pDb, &pSubQuery->uiDrn, &pSubQuery->pRec); } if (RC_OK( rc)) { bFirstRead = FALSE; } } else { if (bReadForward) { rc = pPredicate->nextRecord( (HFDB)pDb, &pSubQuery->uiDrn, &pSubQuery->pRec); } else { rc = pPredicate->prevRecord( (HFDB)pDb, &pSubQuery->uiDrn, &pSubQuery->pRec); } } CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } pSubQuery->SQStatus.uiRecsFetchedForEval++; pSubQuery->bRecIsAKey = FALSE; // Evaluate the record against the query criteria if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pSubQuery->pRec, FALSE, &uiRecMatch))) { goto Exit; } if (uiRecMatch != FLM_TRUE) { pSubQuery->SQStatus.uiRecsRejected++; // Continue loop to process next key or reference continue; } // Record has passed all of the sub-query criteria. See if // it passes our validation tests. if (RC_BAD( rc = flmCurRecValidate( eFlmFuncId, pCursor, pSubQuery, puiSkipCount, puiCount, &bReturnRecOK))) { goto Exit; } if (bReturnRecOK) { break; } } Exit: return( rc); } /**************************************************************************** Desc: Searches a container for matching record. ****************************************************************************/ FSTATIC RCODE flmCurSearchContainer( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount ) { RCODE rc = FERR_OK; FDB * pDb = pCursor->pDb; FSDataCursor * pFSDataCursor = pSubQuery->pFSDataCursor; FLMUINT uiCBTimer = 0; FLMUINT uiCurrCBTime; FLMBOOL bReturnRecOK; FLMUINT uiCPUReleaseCnt = 0; FLMBOOL bSavedInvisTrans; FLMUINT uiStartTime = FLM_GET_TIMER(); FLMUINT uiCurrTime; FLMUINT uiTimeLimit = pCursor->uiTimeLimit; FLMUINT uiRecMatch; if (pCursor->fnStatus) { uiCBTimer = FLM_SECS_TO_TIMER_UNITS( STATUS_CB_INTERVAL); } // Set up initial search parameters. pSubQuery->uiDrn = 0; // Loop to evaluate keys and records. for (;;) { // Release the CPU periodically to prevent CPU hog if (((++uiCPUReleaseCnt) & 0x1F) == 0) { f_yieldCPU(); } // See if we have timed out if (uiTimeLimit) { uiCurrTime = FLM_GET_TIMER(); // Use greater than to compare because if the timeout was one // second, we want to be sure and give it at least one // full second. We would rather give it an extra second // than shortchange it. if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > uiTimeLimit) { rc = RC_SET( FERR_TIMEOUT); goto Exit; } } // Do the progress callback if enough time has elapsed. pSubQuery->SQStatus.uiProcessedCnt++; if (pCursor->fnStatus) { uiCurrCBTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrCBTime, pCursor->uiLastCBTime) > uiCBTimer) { CB_ENTER( pDb, &bSavedInvisTrans); rc = (pCursor->fnStatus)( FLM_SUBQUERY_STATUS, (void *)&pSubQuery->SQStatus, (void *)FALSE, pCursor->StatusData); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } pCursor->uiLastCBTime = FLM_GET_TIMER(); } } // Get the next or previous key or reference if (bFirstRead) { rc = (RCODE)((bReadForward) ? pFSDataCursor->firstRec( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn) : pFSDataCursor->lastRec( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn)); if (RC_OK( rc)) { bFirstRead = FALSE; } } else { rc = (RCODE)((bReadForward) ? pFSDataCursor->nextRec( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn) : pFSDataCursor->prevRec( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn)); } if (RC_BAD( rc)) { goto Exit; } pSubQuery->SQStatus.uiRecsFetchedForEval++; pSubQuery->bRecIsAKey = FALSE; // Evaluate the record against the query criteria if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pSubQuery->pRec, FALSE, &uiRecMatch))) { goto Exit; } if (uiRecMatch != FLM_TRUE) { pSubQuery->SQStatus.uiRecsRejected++; // Continue loop to process next record continue; } // Record has passed all of the sub-query criteria. See if // it passes our validation tests. if (RC_BAD( rc = flmCurRecValidate( eFlmFuncId, pCursor, pSubQuery, puiSkipCount, puiCount, &bReturnRecOK))) { goto Exit; } if (bReturnRecOK) { break; } } Exit: return( rc); } /**************************************************************************** Desc: Retrieves a single record and evaluates it. ****************************************************************************/ FSTATIC RCODE flmCurEvalSingleRec( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, SUBQUERY * pSubQuery, FLMUINT * puiCount, FLMUINT * puiSkipCount ) { RCODE rc = FERR_OK; FDB * pDb = pCursor->pDb; FLMBOOL bReturnRecOK; FLMUINT uiRecMatch; // Return EOF or BOF if we have already returned the // record and it is not a first() or last() call. if (!bFirstRead && pSubQuery->bRecReturned) { rc = (RCODE)((bReadForward) ? RC_SET( FERR_EOF_HIT) : RC_SET( FERR_BOF_HIT)); goto Exit; } // Set up initial search parameters. pSubQuery->uiDrn = pSubQuery->OptInfo.uiDrn; if (RC_BAD( rc = flmCurRetrieveRec( pDb, pSubQuery, pCursor->uiContainer))) { goto Exit; } pSubQuery->SQStatus.uiRecsFetchedForEval++; // Evaluate the record against the query criteria if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pSubQuery->pRec, FALSE, &uiRecMatch))) { goto Exit; } if (uiRecMatch != FLM_TRUE) { pSubQuery->SQStatus.uiRecsRejected++; rc = (RCODE)((bReadForward) ? (RCODE)FERR_EOF_HIT : (RCODE)FERR_BOF_HIT); goto Exit; } if (RC_BAD( rc = flmCurRecValidate( eFlmFuncId, pCursor, pSubQuery, puiSkipCount, puiCount, &bReturnRecOK))) { goto Exit; } if (!bReturnRecOK) { rc = (RCODE)((bReadForward) ? (RCODE)FERR_EOF_HIT : (RCODE)FERR_BOF_HIT); goto Exit; } // Found a record, set bRecReturned to TRUE so that the // next time we come in on a next() or prev() call, we will // return EOF or BOF. pSubQuery->bRecReturned = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Performs the search operation. ****************************************************************************/ RCODE flmCurSearch( eFlmFuncs eFlmFuncId, CURSOR * pCursor, FLMBOOL bFirstRead, FLMBOOL bReadForward, FLMUINT * puiCount, FLMUINT * puiSkipCount, FlmRecord ** ppUserRecord, FLMUINT * puiDrn ) { RCODE rc = FERR_OK; FDB * pDb; DB_STATS * pDbStats; RCODE TmpRc; SUBQUERY * pSubQuery = pCursor->pCurrSubQuery; FLMBOOL bSavedInvisTrans; pDb = pCursor->pDb; if( RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } if ((pDbStats = pDb->pDbStats) != NULL) { pDbStats->bHaveStats = TRUE; pDbStats->ui64NumCursorReads++; } // If this is a first or last call, we need to reset the // current sub-query. if (bFirstRead) { pSubQuery = pCursor->pSubQueryList; // If reading backwards, position to the last subquery // in the list. if (!bReadForward) { while (pSubQuery->pNext) { pSubQuery = pSubQuery->pNext; } } if (RC_BAD( rc = flmCurSetSubQuery( pCursor, pSubQuery))) { goto Exit; } } // If counting, initialize the count to zero. if (puiCount) { *puiCount = 0; } // Loop through sub-queries for (;;) { switch (pSubQuery->OptInfo.eOptType) { case QOPT_USING_INDEX: rc = flmCurSearchIndex( eFlmFuncId, pCursor, bFirstRead, bReadForward, pSubQuery, puiCount, puiSkipCount, (FLMBOOL)((ppUserRecord && !pCursor->bOkToReturnKeys) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); break; case QOPT_USING_PREDICATE: rc = flmCurSearchPredicate( eFlmFuncId, pCursor, bFirstRead, bReadForward, pSubQuery, puiCount, puiSkipCount); break; case QOPT_SINGLE_RECORD_READ: rc = flmCurEvalSingleRec( eFlmFuncId, pCursor, bFirstRead, bReadForward, pSubQuery, puiCount, puiSkipCount); break; case QOPT_PARTIAL_CONTAINER_SCAN: case QOPT_FULL_CONTAINER_SCAN: rc = flmCurSearchContainer( eFlmFuncId, pCursor, bFirstRead, bReadForward, pSubQuery, puiCount, puiSkipCount); break; default: // Should never happen flmAssert( 0); rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } // If rc is FERR_OK, it means we got something that passed. if (RC_OK( rc)) { // ppUserRecord will be NULL if this is a DRN only function or // the record count function. if (ppUserRecord) { flmAssert( pSubQuery->pRec != NULL); *ppUserRecord = pSubQuery->pRec; (*ppUserRecord)->AddRef(); // We must release the record here since we are giving it // back to the caller. pSubQuery->pRec->Release(); pSubQuery->pRec = NULL; } if (puiDrn) { *puiDrn = pSubQuery->uiDrn; } break; } // This is the place where we handle EOF and BOF. We attempt to go to // the next or previous sub-query, if there is one. If the error is not // EOF or BOF, we simply go to Exit. if (rc != (RCODE)((bReadForward) ? (RCODE)FERR_EOF_HIT : (RCODE)FERR_BOF_HIT)) { goto Exit; } // If there is a callback, call it to give the final status of // this sub-query. if (pCursor->fnStatus) { CB_ENTER( pDb, &bSavedInvisTrans); TmpRc = (pCursor->fnStatus)( FLM_SUBQUERY_STATUS, (void *)&pSubQuery->SQStatus, (void *)TRUE, pCursor->StatusData); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( TmpRc)) { rc = TmpRc; goto Exit; } pCursor->uiLastCBTime = FLM_GET_TIMER(); } // Reached BOF/EOF in the current subquery. Move on to the next one. pSubQuery = (SUBQUERY *)((bReadForward) ? pSubQuery->pNext : pSubQuery->pPrev); if (!pSubQuery) { // No more sub-queries. If we were counting and have a non-zero // count, we can return FERR_OK. if (puiCount && *puiCount) { rc = FERR_OK; } //else rc should already be EOF or BOF. // If we are at the real EOF or BOF, and doing a move // relative, we must decrement the skip counter by one // more. That way, if the skip count is set to one and // we hit EOF, the value returned to the user will be // one. if (puiSkipCount) { (*puiSkipCount)--; } goto Exit; } // Set up up to use the next or previous sub-query. if (RC_BAD( rc = flmCurSetSubQuery( pCursor, pSubQuery))) { goto Exit; } // For the next (or previous) sub-query, need to set the // bFirstRead flag to TRUE the first time in. bFirstRead = TRUE; } Exit: if (pDb) { flmExit( eFlmFuncId, pDb, rc); } return( rc); } libflaim-4.9.966/src/fdbcopy.cpp0000644000175000017500000006451010510774540020014 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Copy database. // Tabs: 3 // // Copyright (c) 1998-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbcopy.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" typedef struct Copied_Name { char szPath[ F_PATH_MAX_SIZE]; Copied_Name * pNext; } COPIED_NAME; FSTATIC RCODE flmCopyDb( FLMUINT uiDbVersion, const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, STATUS_HOOK fnStatusCallback, void * UserData); FSTATIC RCODE flmCopyFile( IF_FileSystem * pFileSystem, FLMUINT uiStartOffset, FLMUINT uiEndOffset, DB_COPY_INFO * pDbCopyInfo, COPIED_NAME ** ppCopiedListRV, FLMBYTE * pucInMemLogHdr, FLMBOOL bOkToTruncate, STATUS_HOOK fnStatusCallback, void * UserData); /******************************************************************************* Desc: Copies a database, including roll-forward log files. *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbCopy( const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; FLMBYTE * pucLastCommittedLogHdr; HFDB hDb = HFDB_NULL; FDB * pDb; FLMBOOL bDbLocked = FALSE; FLMUINT uiDbVersion; // Make sure the destination database is closed if (RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszDestDbName, (void *)pszDestDataDir))) { goto Exit; } gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Open the database so we can force a checkpoint. if (RC_BAD( rc = FlmDbOpen( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, 0, NULL, &hDb))) { goto Exit; } pDb = (FDB *)hDb; // Need to lock the database, because we want to do a checkpoint // and then the copy immediately after without letting other // threads have the opportunity to get in and update the // database. if (RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; // Force a checkpoint if (RC_BAD( rc = FlmDbCheckpoint( hDb, FLM_NO_TIMEOUT))) { goto Exit; } pucLastCommittedLogHdr = &pDb->pFile->ucLastCommittedLogHdr[ 0]; // Get the low and high RFL log file numbers from the log // header. uiDbVersion = pDb->pFile->FileHdr.uiVersionNum; // Once we get this far, we have exclusive access to the database // and we have forced a checkpoint. The database's contents are // guaranteed to be on disk at this point, and they will not // change. rc = flmCopyDb( uiDbVersion, pszSrcDbName, pszSrcDataDir, pszSrcRflDir, pszDestDbName, pszDestDataDir, pszDestRflDir, fnStatusCallback, UserData); Exit: // Unlock and close the database if (bDbLocked) { FlmDbUnlock( hDb); } if (hDb != HFDB_NULL) { (void)FlmDbClose( &hDb); (void)FlmConfig( FLM_CLOSE_FILE, (void *)pszSrcDbName, (void *)pszSrcDataDir); } return( rc); } /**************************************************************************** Desc: Copy a database's files, including roll-forward log files. *****************************************************************************/ FSTATIC RCODE flmCopyDb( FLMUINT uiDbVersion, const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; DB_COPY_INFO DbCopyInfo; F_SuperFileHdl * pSrcSFileHdl = NULL; F_SuperFileHdl * pDestSFileHdl = NULL; F_SuperFileClient * pSrcSFileClient = NULL; F_SuperFileClient * pDestSFileClient = NULL; FLMUINT uiFileNumber; FLMUINT uiHighFileNumber; FLMUINT uiHighLogFileNumber; FLMUINT64 ui64FileSize; FFILE * pFile = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pLockFileHdl = NULL; IF_FileHdl * pTmpFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; FLMBOOL bFileLocked = FALSE; FLMBOOL bWriteLocked = FALSE; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pFileLockObj = NULL; COPIED_NAME * pCopiedList = NULL; FLMBOOL bUsedFFile = FALSE; FLMBYTE * pucInMemLogHdr = NULL; eLockType currLockType; FLMUINT uiLockThreadId; char * pszActualSrcRflPath = NULL; char * pszSrcPrefix = NULL; char * pszActualDestRflPath = NULL; char * pszDestPrefix = NULL; FLMBOOL bCreatedDestRflDir = FALSE; F_SEM hWaitSem = F_SEM_NULL; f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo)); // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcDbName, pszDestDbName) == 0) { goto Exit; } // Allocate a semaphore if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } // Allocate memory for paths we don't want to push onto the stack. if (RC_BAD( rc = f_calloc( (F_PATH_MAX_SIZE + F_FILENAME_SIZE) * 2, &pszActualSrcRflPath))) { goto Exit; } pszSrcPrefix = &pszActualSrcRflPath[ F_PATH_MAX_SIZE]; pszActualDestRflPath = &pszSrcPrefix[ F_FILENAME_SIZE]; pszDestPrefix = &pszActualDestRflPath[ F_PATH_MAX_SIZE]; // Set up the super file object for the source database. // Must at least open the control file. if( (pSrcSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileClient->setup( pszSrcDbName, pszSrcDataDir, uiDbVersion))) { goto Exit; } if( (pSrcSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileHdl->setup( pSrcSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, 0))) { goto Exit; } // Lock the destination database, if not already locked. // This is so we can overwrite it without necessarily // deleting it. May unlock and re-lock the global mutex. f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; if (RC_BAD( rc = flmFindFile( pszDestDbName, pszDestDataDir, &pFile))) { goto Exit; } // If we didn't find an FFILE structure, get an // exclusive lock on the file. if (!pFile) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl))) { goto Exit; } } else { // The call to flmVerifyFileUse will wait if the file is in // the process of being opened by another thread. if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile))) { goto Exit; } // Increment the use count on the FFILE so it will not // disappear while we are copying the file. if (++pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } bUsedFFile = TRUE; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; pucInMemLogHdr = &pFile->ucLastCommittedLogHdr [0]; // Lock the destination file object and transaction // object, if not already locked. pFile->pFileLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if (currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pFileLockObj = pFile->pFileLockObj; pFileLockObj->AddRef(); if (RC_BAD( rc = pFileLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bFileLocked = TRUE; } // Lock the write object, if not already locked pFile->pWriteLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if( currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId()) { pWriteLockObj = pFile->pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread - wait // forever until the checkpoint thread gives it up. if( RC_BAD( rc = pWriteLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bWriteLocked = TRUE; } } // Set up the super file object for the destination database. if( (pDestSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileClient->setup( pszDestDbName, pszDestDataDir, uiDbVersion))) { goto Exit; } if( (pDestSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileHdl->setup( pDestSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, gv_FlmSysData.uiFileCreateFlags))) { goto Exit; } // See how many files we have and calculate the total size. uiHighFileNumber = 0; for (;;) { if( RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighFileNumber, &ui64FileSize)) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { // If the control file doesn't exist, we will return // path not found. if (!uiHighFileNumber) { goto Exit; } uiHighFileNumber--; rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighFileNumber++; } // See how many rollback log files we have, and calculate // their total size. uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); for (;;) { if ((RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH || !ui64FileSize) { if (uiHighLogFileNumber == FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { uiHighLogFileNumber = 0; } else { uiHighLogFileNumber--; } rc = FERR_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } uiHighLogFileNumber++; } // Get the sizes of the roll-forward log files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to copy one RFL file. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, pszActualSrcRflPath))) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszActualSrcRflPath, gv_FlmSysData.uiFileOpenFlags, &pTmpFileHdl))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } else { if (RC_BAD( rc = pTmpFileHdl->size( &ui64FileSize))) { goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; pTmpFileHdl->Release(); pTmpFileHdl = NULL; } } else { if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszSrcDbName, pszSrcRflDir, pszActualSrcRflPath, pszSrcPrefix))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, increment ui64BytesToCopy if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { DbCopyInfo.ui64BytesToCopy += pDirHdl->currentItemSize(); } } pDirHdl->Release(); pDirHdl = NULL; } // Close all file handles in the source and destination pSrcSFileHdl->releaseFiles(); pDestSFileHdl->releaseFiles(); // Copy the database files. for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if( RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if( RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } // For the control file, don't copy first 2K - it will be set up // to show maintenance in progress. Then the first 2K will be copied // later. if (!uiFileNumber) { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 2048, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, pucInMemLogHdr, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } // Copy the additional rollback log files, if any. for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion); uiFileNumber <= uiHighLogFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if (RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } // Copy the RFL files if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, 1, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, 1, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } else { // Create the destination RFL directory, if needed. The purpose of this // code is two-fold: 1) We want to keep track of the fact that we tried // to create the destination RFL directory so we can try to remove it // if the copy fails; 2) If the destination RFL directory path specifies // a directory with existing files, we want to remove them. if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDestDbName, pszDestRflDir, pszActualDestRflPath, pszDestPrefix))) { goto Exit; } if( RC_OK( gv_FlmSysData.pFileSystem->doesFileExist( pszActualDestRflPath))) { if( gv_FlmSysData.pFileSystem->isDir( pszActualDestRflPath)) { // Remove the existing directory and all files, etc. (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath, TRUE); } else { (void)gv_FlmSysData.pFileSystem->deleteFile( pszActualDestRflPath); } } // Try to create the destination RFL directory. This might fail if // another process was accessing the directory for some reason // (i.e., from a command prompt), when we tried to remove it above. // We really don't care if the call to CreateDir is sucessful, because // when we try to create the destination files (below), the FLAIM file // file system code will try to create any necessary directories. (void)gv_FlmSysData.pFileSystem->createDir( pszActualDestRflPath); bCreatedDestRflDir = TRUE; // Copy the RFL files. NOTE: We need to copy all of the RFL files // in the source RFL directory so that they will be available // when performing a database restore operation. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } // If the current file is an RFL file, copy it to the destination if( rflGetFileNum( uiDbVersion, pszSrcPrefix, pDirHdl->currentItemName(), &uiFileNumber)) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName, pszSrcRflDir, uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName, pszDestRflDir, uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 0xFFFFFFFF, &DbCopyInfo, &pCopiedList, NULL, TRUE, fnStatusCallback, UserData))) { goto Exit; } } } pDirHdl->Release(); pDirHdl = NULL; } // Do one final copy on the control file to copy just the first 2K if (RC_BAD( rc = pSrcSFileHdl->getFilePath( 0, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( 0, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = FALSE; if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem, 0, 2048, &DbCopyInfo, NULL, pucInMemLogHdr, FALSE, fnStatusCallback, UserData))) { goto Exit; } Exit: if (bUsedFFile) { if (!bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } if (!(--pFile->uiUseCount)) { flmLinkFileToNUList( pFile); } } if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if (bWriteLocked) { if( RC_BAD( rc = pWriteLockObj->unlock())) { goto Exit; } bWriteLocked = FALSE; } if (bFileLocked) { RCODE rc3; if (RC_BAD( rc3 = pFileLockObj->unlock())) { if (RC_OK( rc)) rc = rc3; } bFileLocked = FALSE; } if (pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if (pFileLockObj) { pFileLockObj->Release(); pFileLockObj = NULL; } if (pLockFileHdl) { pLockFileHdl->Release(); pLockFileHdl = NULL; } if( pTmpFileHdl) { pTmpFileHdl->Release(); } if( pDirHdl) { pDirHdl->Release(); } // Free all the names of files that were copied. // If the copy didn't finish, try to delete any files // that were copied. while (pCopiedList) { COPIED_NAME * pNext = pCopiedList->pNext; // If the overall copy failed, delete the copied file. if (RC_BAD( rc)) { (void)gv_FlmSysData.pFileSystem->deleteFile( pCopiedList->szPath); } f_free( &pCopiedList); pCopiedList = pNext; } if( RC_BAD( rc) && bCreatedDestRflDir) { (void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath); } if( pszActualSrcRflPath) { f_free( &pszActualSrcRflPath); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } if( pSrcSFileHdl) { pSrcSFileHdl->Release(); } if( pSrcSFileClient) { pSrcSFileClient->Release(); } if( pDestSFileHdl) { pDestSFileHdl->Release(); } if( pDestSFileClient) { pDestSFileClient->Release(); } return( rc); } /**************************************************************************** Desc: Copy a file that is one of the files of the database. *****************************************************************************/ FSTATIC RCODE flmCopyFile( IF_FileSystem * pFileSystem, FLMUINT uiStartOffset, FLMUINT uiEndOffset, DB_COPY_INFO * pDbCopyInfo, COPIED_NAME ** ppCopiedListRV, FLMBYTE * pucInMemLogHdr, FLMBOOL bOkToTruncate, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; FLMBYTE * pucBuffer = NULL; IF_FileHdl * pSrcFileHdl = NULL; IF_FileHdl * pDestFileHdl = NULL; FLMUINT uiBufferSize = 32768; FLMUINT uiBytesToRead; FLMUINT uiBytesRead; FLMUINT uiBytesWritten; FLMUINT uiOffset; FLMBYTE * pucLogHdr = NULL; FLMUINT uiNewChecksum; FLMBOOL bCreatedDestFile = FALSE; // Open the source file. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pDbCopyInfo->szSrcFileName, gv_FlmSysData.uiFileOpenFlags, &pSrcFileHdl))) { goto Exit; } // First attempt to open the destination file. If it does // not exist, attempt to create it. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileOpenFlags, &pDestFileHdl))) { if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile( pDbCopyInfo->szDestFileName, gv_FlmSysData.uiFileCreateFlags, &pDestFileHdl))) { goto Exit; } bCreatedDestFile = TRUE; } // Allocate a buffer for reading and writing if( RC_BAD( rc = f_allocAlignedBuffer( uiBufferSize, &pucBuffer))) { goto Exit; } // Allocate a buffer for the log header if( RC_BAD( rc = f_allocAlignedBuffer( 2048, &pucLogHdr))) { goto Exit; } // If uiStartOffset is 2048, it is the special case of // the control file, and we are not copying the first 2K. // However, we need to set up the first 2K so that if // someone reads the first 2K, it will return them a // maintenance in progress error. if (uiStartOffset == 2048) { // Read the first 2K of the source file. if (RC_BAD( rc = pSrcFileHdl->read( 0, 2048, pucBuffer, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { rc = FERR_OK; } else { goto Exit; } } // Zero out whatever part of the 2K we didn't get on the read. if (uiBytesRead < 2048) { f_memset( &pucBuffer[ uiBytesRead], 0, (int)(2048 - uiBytesRead)); } // Attempt to read the log header from the destination file. // It is OK if we can't read it, because if we created the // destination file, these bytes may not be present. if( bCreatedDestFile || RC_BAD( pDestFileHdl->read( 0, 2048, pucLogHdr, &uiBytesRead))) { f_memset( pucLogHdr, 0, sizeof( 2048)); } // Set the transaction ID to zero. MUST ALSO SET THE TRANS ACTIVE FLAG // TO FALSE - OTHERWISE READERS WILL ATTEMPT TO DECREMENT THE // TRANSACTION ID AND WILL END UP WITH 0xFFFFFFFF - very bad! // We must use zero, because it is the only transaction ID that will not // appear on ANY block. UD2FBA( 0, &pucLogHdr[ 16 + LOG_CURR_TRANS_ID]); // Recalculate the log header checksum so that readers will not get a // checksum error. uiNewChecksum = lgHdrCheckSum( &pucLogHdr[ 16], FALSE); UW2FBA( (FLMUINT16)uiNewChecksum, &pucLogHdr[ 16 + LOG_HDR_CHECKSUM]); f_memcpy( &pucBuffer[ 16], &pucLogHdr[ 16], LOG_HEADER_SIZE); // Write this "special" first 2K into the destination file. // The real first 2K from the source file will be copied in // at a later time. if (RC_BAD( rc = pDestFileHdl->write( 0, 2048, pucBuffer, &uiBytesWritten))) { goto Exit; } // Save the log header to the in-memory version of the log // header as well - if pucInMemLogHdr is NULL, it is pointing // to the pFile->ucLastCommittedLogHdr buffer. if (pucInMemLogHdr) { f_memcpy( pucInMemLogHdr, &pucLogHdr[ 16], LOG_HEADER_SIZE); } } // Read from source file until we hit EOF in the file or // we hit the end offset. uiOffset = uiStartOffset; for (;;) { uiBytesToRead = (FLMUINT)((uiEndOffset - uiOffset >= uiBufferSize) ? uiBufferSize : (FLMUINT)(uiEndOffset - uiOffset)); // Read data from source file. if (RC_BAD( rc = pSrcFileHdl->read( uiOffset, uiBytesToRead, pucBuffer, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { rc = FERR_OK; if (!uiBytesRead) { break; } } else { goto Exit; } } // Write data to destination file. if (RC_BAD( rc = pDestFileHdl->write( uiOffset, uiBytesRead, pucBuffer, &uiBytesWritten))) { goto Exit; } // See if we wrote out the buffer that has the log header // If so, we need to copy it back to the in-memory log // header. if ((pucInMemLogHdr) && (uiOffset <= 16) && (uiOffset + uiBytesWritten >= 16 + LOG_HEADER_SIZE)) { f_memcpy( pucInMemLogHdr, &pucBuffer[ 16 - uiOffset], LOG_HEADER_SIZE); } uiOffset += uiBytesWritten; // Do callback to report progress. if (fnStatusCallback) { pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten; if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_COPY_STATUS, (void *)pDbCopyInfo, (void *)0, UserData))) { goto Exit; } pDbCopyInfo->bNewSrcFile = FALSE; } // Quit once we reach the end offset or we read fewer bytes // than we asked for. if (uiOffset >= uiEndOffset || uiBytesRead < uiBytesToRead) { break; } } // If we overwrote the destination file, as opposed to creating // it, truncate it in case it was larger than the number of // bytes we actually copied. if (!bCreatedDestFile && bOkToTruncate) { if (RC_BAD( rc = pDestFileHdl->truncateFile( uiOffset))) { goto Exit; } } // If the copy succeeded, add the destination name to a list // of destination files. This is done so we can clean up // copied files if we fail somewhere in the overall database // copy. if (ppCopiedListRV) { COPIED_NAME * pCopyName; if( RC_BAD( rc = f_alloc( (FLMUINT)sizeof( COPIED_NAME), &pCopyName))) { goto Exit; } f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName); pCopyName->pNext = *ppCopiedListRV; *ppCopiedListRV = pCopyName; } Exit: if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } if( pucLogHdr) { f_freeAlignedBuffer( &pucLogHdr); } if( pSrcFileHdl) { pSrcFileHdl->Release(); } if( pDestFileHdl) { pDestFileHdl->flush(); pDestFileHdl->Release(); } // Attempt to delete the destination file if // we didn't successfully copy it. if( RC_BAD( rc)) { (void)pFileSystem->deleteFile( pDbCopyInfo->szDestFileName); } return( rc); } libflaim-4.9.966/src/flbackup.cpp0000644000175000017500000021246110510774540020155 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Backup and restore routines. // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flbackup.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" typedef struct { char szPath[ F_PATH_MAX_SIZE]; IF_MultiFileHdl * pMultiFileHdl; FLMUINT64 ui64Offset; void * pvAppData; RCODE rc; } BACKER_HOOK_STATE; #define FLM_BACKER_SIGNATURE_OFFSET 0 #define FLM_BACKER_SIGNATURE "!DB_BACKUP_FILE!" #define FLM_BACKER_SIGNATURE_SIZE 16 #define FLM_BACKER_VERSION_OFFSET 16 #define FLM_BACKER_VERSION_1_0_1 101 #define FLM_BACKER_VERSION FLM_BACKER_VERSION_1_0_1 #define FLM_BACKER_DB_BLOCK_SIZE_OFFSET 20 #define FLM_BACKER_BFMAX_OFFSET 24 #define FLM_BACKER_MTU_OFFSET 28 #define FLM_BACKER_TIME_OFFSET 32 #define FLM_BACKER_DB_NAME_OFFSET 36 #define FLM_BACKER_BACKUP_TYPE_OFFSET 40 #define FLM_BACKER_NEXT_INC_SERIAL_NUM 44 #define FLM_BACKER_DB_VERSION 60 // The backer MTU size must be a multiple of the largest // supported block size. Additionally, it must be at least // 2 * FLM_BACKER_MAX_DB_BLOCK_SIZE. DO NOT CHANGE THE MTU // SIZE UNLESS YOU ALSO BUMP THE BACKUP VERSION. #define FLM_BACKER_MTU_SIZE ((FLMUINT) 1024 * 512) #define FLM_BACKER_MAX_FILE_SIZE ((FLMUINT) 1024 * 1024 * 1024 * 2) // 2 Gigabytes #define FLM_BACKER_MIN_DB_BLOCK_SIZE ((FLMUINT) 2 * 1024) #define FLM_BACKER_MAX_DB_BLOCK_SIZE ((FLMUINT) 16 * 1024) // Backup block header #define FLM_BACKER_BLK_HDR_SIZE ((FLMUINT) 8) #define FLM_BACKER_BLK_ADDR_OFFSET 0 #define FLM_BACKER_BLK_SIZE_OFFSET 4 FSTATIC RCODE flmDefaultBackerWriteHook( void * pvBuffer, FLMUINT uiBytesToWrite, void * pvCallbackData); FSTATIC RCODE flmRestoreFile( F_Restore * pRestoreObj, const char * pszDbPath, const char * pszDataDir, const char * pszPassword, F_SuperFileHdl ** ppSFile, FLMBOOL bIncremental, FLMUINT * puiDbVersion, FLMUINT * puiNextIncSeqNum, FLMBOOL * pbRflPreserved, eRestoreActionType * peRestoreAction, FLMBOOL * pbOKToRetry, FLMBYTE ** ppucKeyToSave, FLMBYTE * pucKeyToUse, FLMUINT * puiKeyLen); /******************************************************************************* Desc: *******************************************************************************/ class F_BackerStream : public F_Object { public: F_BackerStream( void); ~F_BackerStream( void); RCODE setup( FLMUINT uiMTUSize, F_Restore * pRestoreObj); RCODE setup( FLMUINT uiMTUSize, BACKER_WRITE_HOOK fnWrite, void * pvCallbackData); RCODE startThreads( void); void shutdownThreads( void); RCODE read( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesRead = NULL); RCODE write( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesWritten = NULL); RCODE flush( void); FINLINE FLMUINT64 getByteCount( void) { // Returns the total number of bytes read or written. return( m_ui64ByteCount); } FINLINE FLMUINT getMTUSize( void) { return( m_uiMTUSize); } private: RCODE signalThread( void); RCODE _setup( void); static RCODE FLMAPI readThread( IF_Thread * pThread); static RCODE FLMAPI writeThread( IF_Thread * pThread); FLMBOOL m_bSetup; FLMBOOL m_bFirstRead; FLMUINT m_uiBufOffset; FLMUINT64 m_ui64ByteCount; F_Restore * m_pRestoreObj; F_SEM m_hDataSem; F_SEM m_hIdleSem; IF_Thread * m_pThread; RCODE m_rc; FLMBYTE * m_pucInBuf; FLMUINT * m_puiInOffset; FLMBYTE * m_pucOutBuf; FLMUINT * m_puiOutOffset; FLMBYTE * m_pucBufs[ 2]; FLMUINT m_uiOffsets[ 2]; FLMUINT m_uiMTUSize; FLMUINT m_uiPendingIO; BACKER_WRITE_HOOK m_fnWrite; void * m_pvCallbackData; }; /******************************************************************************* Desc: Prepares FLAIM to backup a database. Notes: Only one backup of a particular database can be active at any time *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbBackupBegin( HFDB hDb, FBackupType eBackupType, FLMBOOL bHotBackup, HFBACKUP * phBackup ) { FDB * pDb = (FDB *)hDb; FBak * pFBak = NULL; FLMBOOL bBackupFlagSet = FALSE; FLMUINT uiLastCPFileNum; FLMUINT uiLastTransFileNum; FLMUINT uiDbVersion; FLMUINT uiTmp; FLMBYTE * pLogHdr; RCODE rc = FERR_OK; FLMUINT uiTransType = bHotBackup ? FLM_READ_TRANS : FLM_UPDATE_TRANS; // Initialize the handle *phBackup = HFBACKUP_NULL; // Make sure we are not being called inside a transaction if( RC_BAD( rc = FlmDbGetTransType( hDb, &uiTmp))) { goto Exit; } if( uiTmp != FLM_NO_TRANS) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } // Make sure a valid backup type has been specified if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_VERSION, (FLMUINT *)&uiDbVersion))) { goto Exit; } if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3 && eBackupType != FLM_FULL_BACKUP) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } // See if a backup is currently running against the database. If so, // return an error. Otherwise, set the backup flag on the FFILE. if( !IsInCSMode( hDb)) { f_mutexLock( gv_FlmSysData.hShareMutex); if( pDb->pFile->bBackupActive) { f_mutexUnlock( gv_FlmSysData.hShareMutex); rc = RC_SET( FERR_BACKUP_ACTIVE); goto Exit; } else { bBackupFlagSet = TRUE; pDb->pFile->bBackupActive = TRUE; } f_mutexUnlock( gv_FlmSysData.hShareMutex); } else { if( RC_BAD( rc = fcsSetBackupActiveFlag( hDb, TRUE))) { goto Exit; } bBackupFlagSet = TRUE; } // Allocate the backup handle if( RC_BAD( rc = f_calloc( sizeof( FBak), &pFBak))) { goto Exit; } if( RC_BAD( rc = f_allocAlignedBuffer( 2048, &pFBak->pucDbHeader))) { goto Exit; } pFBak->hDb = hDb; pFBak->uiDbVersion = uiDbVersion; // Set the C/S mode flag pFBak->bCSMode = IsInCSMode( hDb); // Start a transaction if( RC_BAD( rc = FlmDbTransBegin( hDb, uiTransType | FLM_DONT_KILL_TRANS | FLM_DONT_POISON_CACHE, FLM_NO_TIMEOUT, pFBak->pucDbHeader))) { goto Exit; } pFBak->bTransStarted = TRUE; pFBak->uiTransType = uiTransType; pLogHdr = &pFBak->pucDbHeader[ DB_LOG_HEADER_START]; // Don't allow an incremental backup to be performed // if a full backup has not yet been done. if( eBackupType == FLM_INCREMENTAL_BACKUP && FB2UD( &pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]) == 0) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } pFBak->eBackupType = eBackupType; // Set the next incremental backup serial number. This is // done regardless of the backup type to prevent the wrong // set of incremental backup files from being applied // to a database. if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { if( !pFBak->bCSMode) { if( RC_BAD( rc = f_createSerialNumber( pFBak->ucNextIncSerialNum))) { goto Exit; } } else { fdbInitCS( pDb); rc = fcsCreateSerialNumber( pDb->pCSContext, pFBak->ucNextIncSerialNum); fdbExit( pDb); if( RC_BAD( rc)) { goto Exit; } } } // Get the incremental sequence number from the log header pFBak->uiIncSeqNum = FB2UD( &pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); // Get version 4.3+ values from the header if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { // Determine the transaction ID of the last backup pFBak->uiLastBackupTransId = FB2UD( &pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]); // Get the block change count pFBak->uiBlkChgSinceLastBackup = FB2UD( &pLogHdr[ LOG_BLK_CHG_SINCE_BACKUP]); } // Get the current transaction ID if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_TRANS_ID, (void *)&pFBak->uiTransId))) { goto Exit; } // Get the logical end of file pFBak->uiLogicalEOF = FB2UD( &pLogHdr[ LOG_LOGICAL_EOF]); // Get the first required RFL file needed by the restore. uiLastCPFileNum = FB2UD( &pLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]); uiLastTransFileNum = FB2UD( &pLogHdr[ LOG_RFL_FILE_NUM]); flmAssert( uiLastCPFileNum <= uiLastTransFileNum); pFBak->uiFirstReqRfl = uiLastCPFileNum < uiLastTransFileNum ? uiLastCPFileNum : uiLastTransFileNum; flmAssert( pFBak->uiFirstReqRfl); // Get the database block size if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_BLKSIZ, &pFBak->uiBlockSize))) { goto Exit; } // Get the database path if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_PATH, pFBak->ucDbPath))) { goto Exit; } *phBackup = pFBak; Exit: if( RC_BAD( rc)) { if( pFBak) { if( pFBak->bTransStarted) { (void)FlmDbTransAbort( hDb); } if( pFBak->pucDbHeader) { f_freeAlignedBuffer( &pFBak->pucDbHeader); } f_free( &pFBak); } if( bBackupFlagSet) { if( !IsInCSMode( hDb)) { f_mutexLock( gv_FlmSysData.hShareMutex); pDb->pFile->bBackupActive = FALSE; f_mutexUnlock( gv_FlmSysData.hShareMutex); } else { (void)fcsSetBackupActiveFlag( hDb, FALSE); } } } return( rc); } /**************************************************************************** Desc : Returns information about a backup ****************************************************************************/ FLMEXP RCODE FLMAPI FlmBackupGetConfig( HFBACKUP hBackup, eBackupGetConfigType eConfigType, void * pvValue1, void * // pvValue2 ) { RCODE rc = FERR_OK; FBak * pFBak = (FBak *)hBackup; switch( eConfigType) { case FBAK_GET_BACKUP_TRANS_ID: { *((FLMUINT *)pvValue1) = pFBak->uiTransId; break; } case FBAK_GET_LAST_BACKUP_TRANS_ID: { *((FLMUINT *)pvValue1) = pFBak->uiLastBackupTransId; break; } default: { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc : Streams the contents of a database to the write hook supplied by the application. Notes: This routine attempts to create a backup of a database without excluding any readers or updaters. However, if the backup runs too long in an environment where extensive updates are happening, an old view error could be returned. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbBackup( HFBACKUP hBackup, const char * pszBackupPath, const char * pszPassword, BACKER_WRITE_HOOK fnWrite, STATUS_HOOK fnStatus, void * pvAppData, FLMUINT * puiIncSeqNum) { FDB * pDb = NULL; FLMBOOL bDbInitialized = FALSE; FLMBOOL bFullBackup = TRUE; FLMINT iFileNum; FLMUINT uiBlkAddr; FLMUINT uiTime; SCACHE * pSCache = NULL; BACKER_HOOK_STATE hookState; FLMBYTE * pLogHdr; DB_BACKUP_INFO backupInfo; FLMUINT uiBlockFileOffset; FLMUINT uiActualBlkSize; FLMUINT uiCount; FLMUINT uiBlockCount; FLMUINT uiBlockCountLastCB = 0; FLMUINT uiBytesToPad; void * pvCallbackData; FBak * pFBak = (FBak *)hBackup; FLMUINT uiBlockSize = pFBak->uiBlockSize; F_BackerStream * pBackerStream = NULL; FLMBYTE * pucBlkBuf = NULL; FLMUINT uiBlkBufOffset; FLMUINT uiBlkBufSize; FLMUINT uiMaxCSBlocks; FLMUINT uiCPTransOffset; FLMUINT uiMaxFileSize; RCODE rc = FERR_OK; F_CCS * pDbKey; #ifndef FLM_USE_NICI F_UNREFERENCED_PARM( pszPassword); #endif pDb = (FDB *)(pFBak->hDb); if( puiIncSeqNum) { *puiIncSeqNum = 0; } f_memset( &hookState, 0, sizeof( BACKER_HOOK_STATE)); // Make sure a backup attempt has not been made with this // backup handle. if( pFBak->bCompletedBackup) { rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( pFBak->backupRc)) { rc = pFBak->backupRc; goto Exit; } // Look at the backup type if( pFBak->eBackupType == FLM_INCREMENTAL_BACKUP) { if( puiIncSeqNum) { *puiIncSeqNum = pFBak->uiIncSeqNum; } bFullBackup = FALSE; } // Set up the callback if( !fnWrite) { fnWrite = flmDefaultBackerWriteHook; } if( fnWrite == flmDefaultBackerWriteHook) { if( !pszBackupPath) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } f_strcpy( hookState.szPath, pszBackupPath); hookState.pvAppData = pvAppData; pvCallbackData = &hookState; } else { pvCallbackData = pvAppData; } // Allocate and initialize the backer stream object if( (pBackerStream = f_new F_BackerStream) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, fnWrite, pvCallbackData))) { goto Exit; } // Allocate a temporary buffer uiBlkBufSize = FLM_BACKER_MTU_SIZE; uiMaxCSBlocks = uiBlkBufSize / uiBlockSize; if( RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf))) { goto Exit; } // Setup the status callback info f_memset( &backupInfo, 0, sizeof( DB_BACKUP_INFO)); // Setup the backup file header uiBlkBufOffset = 0; f_memset( pucBlkBuf, 0, uiBlockSize); f_memcpy( &pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE); UD2FBA( FLM_BACKER_VERSION, &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]); UD2FBA( (FLMUINT32)uiBlockSize, &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); uiMaxFileSize = flmGetMaxFileSize( pFBak->uiDbVersion, &pFBak->pucDbHeader [DB_LOG_HEADER_START]); UD2FBA( (FLMUINT32)uiMaxFileSize, &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); UD2FBA( (FLMUINT32)FLM_BACKER_MTU_SIZE, &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]); f_timeGetSeconds( &uiTime); UD2FBA( (FLMUINT32)uiTime, &pucBlkBuf[ FLM_BACKER_TIME_OFFSET]); uiCount = f_strlen( (const char *)pFBak->ucDbPath); if( uiCount <= 3) { pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET] = pFBak->ucDbPath[ uiCount - 6]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 1] = pFBak->ucDbPath[ uiCount - 5]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 2] = pFBak->ucDbPath[ uiCount - 4]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 3] = '\0'; } UD2FBA( (FLMUINT32)pFBak->eBackupType, &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); // Set the next incremental serial number in the backup's // header so that it can be put into the database's log header // after the backup has been restored. f_memcpy( &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE); // Set the database version number UD2FBA( (FLMUINT32)pFBak->uiDbVersion, &pucBlkBuf[ FLM_BACKER_DB_VERSION]); uiBlkBufOffset += uiBlockSize; // Copy the database header into the backup's buffer f_memset( &pucBlkBuf[ uiBlkBufOffset], 0, uiBlockSize); f_memcpy( &pucBlkBuf[ uiBlkBufOffset], pFBak->pucDbHeader, F_TRANS_HEADER_SIZE); pLogHdr = &pucBlkBuf[ uiBlkBufOffset + DB_LOG_HEADER_START]; uiBlkBufOffset += uiBlockSize; // Fix up the log header if( !pLogHdr[ LOG_KEEP_RFL_FILES] || pFBak->uiDbVersion < FLM_FILE_FORMAT_VER_4_3) { pLogHdr[ LOG_KEEP_RFL_FILES] = 0; // Put zero in as the last transaction offset so that the current // RFL file will be created if it does not exist after the database // is restored. This has basically the same effect as setting the // offset to 512 if the RFL file has already been created. UD2FBA( (FLMUINT32)0, &pLogHdr[ LOG_RFL_LAST_TRANS_OFFSET]); uiCPTransOffset = 512; // Create new serial numbers for the RFL. We don't want anyone // to be able to branch into a "no-keep" RFL sequence. if( pFBak->uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { if (RC_BAD( rc = f_createSerialNumber( &pLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]))) { goto Exit; } if (RC_BAD( rc = f_createSerialNumber( &pLogHdr [LOG_RFL_NEXT_SERIAL_NUM]))) { goto Exit; } } } else { uiCPTransOffset = FB2UD( &pLogHdr[ LOG_RFL_LAST_TRANS_OFFSET]); if( !uiCPTransOffset) { uiCPTransOffset = 512; } } // Shroud the database key (stored in the log header) in the password // so we can restore this backup to a different server if ( pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60 && pszPassword && *pszPassword && FB2UW( &pLogHdr[ LOG_DATABASE_KEY_LEN]) > 0) { FLMBYTE * pucTmpBuf = NULL; FLMUINT32 ui32KeyLen = 0; pDbKey = pDb->pFile->pDbWrappingKey; if( RC_BAD( rc = pDbKey->getKeyToStore( &pucTmpBuf, &ui32KeyLen, pszPassword, NULL, FALSE))) { goto Exit; } // Verify that the field in the log header is long enough to // hold the key. if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN) { rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY); goto Exit; } // Verify that the field in the log header is long enough to // hold the key. if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN) { f_free( &pucTmpBuf); rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY); goto Exit; } UW2FBA( ui32KeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]); f_memcpy( &pLogHdr[ LOG_DATABASE_KEY], pucTmpBuf, ui32KeyLen); f_free( &pucTmpBuf); } // Set the CP offsets to the last trans offsets. This is done // because the backup could actually read dirty (committed) blocks // from the cache, resulting in a backup set that contains blocks // that are more recent than the ones currently on disk. f_memcpy( &pLogHdr[ LOG_RFL_LAST_CP_FILE_NUM], &pLogHdr[ LOG_RFL_FILE_NUM], 4); f_memcpy( &pLogHdr[ LOG_LAST_CP_TRANS_ID], &pLogHdr[ LOG_CURR_TRANS_ID], 4); UD2FBA( (FLMUINT32)uiCPTransOffset, &pLogHdr[ LOG_RFL_LAST_CP_OFFSET]); UD2FBA( (FLMUINT32) uiBlockSize, &pLogHdr[ LOG_ROLLBACK_EOF]); UD2FBA( 0, &pLogHdr[ LOG_PL_FIRST_CP_BLOCK_ADDR]); // Compute the log header checksum UW2FBA( (FLMUINT16)lgHdrCheckSum( pLogHdr, FALSE), &pLogHdr[ LOG_HDR_CHECKSUM]); // Output the header if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf))) { goto Exit; } // There is no way to quickly compute the actual number of bytes // that will be written to the backup. This is due, in part, to the // fact that the backup compresses unused space out of blocks before // storing them. backupInfo.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, pFBak->uiLogicalEOF); // Initialize the FDB pDb = (FDB *)(pFBak->hDb); if( !pFBak->bCSMode) { FLMBOOL bDummy; bDbInitialized = TRUE; if( RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bDummy))) { goto Exit; } flmAssert( !bDummy); } else { bDbInitialized = TRUE; fdbInitCS( pDb); } uiBlockFileOffset = 0; uiBlockCount = 0; iFileNum = 1; for( ;;) { if( uiBlockFileOffset >= uiMaxFileSize) { uiBlockFileOffset = 0; iFileNum++; } uiBlkAddr = FSBlkAddress( iFileNum, uiBlockFileOffset); if( !FSAddrIsBelow( uiBlkAddr, pFBak->uiLogicalEOF)) { break; } if( !pFBak->bCSMode) { // Get the block if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE, uiBlkAddr, NULL, &pSCache))) { goto Exit; } if( bFullBackup || FB2UD( &pSCache->pucBlk[ BH_TRANS_ID]) > pFBak->uiLastBackupTransId) { uiBlkBufOffset = 0; uiActualBlkSize = getEncryptSize( pSCache->pucBlk); if( uiActualBlkSize < BH_OVHD) { flmAssert( 0); rc = RC_SET( FERR_DATA_ERROR); goto Exit; } // Output the backup header for the block UD2FBA( (FLMUINT32)uiBlkAddr, &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); UD2FBA( (FLMUINT32)uiActualBlkSize, &pucBlkBuf[ FLM_BACKER_BLK_SIZE_OFFSET]); uiBlkBufOffset += FLM_BACKER_BLK_HDR_SIZE; // Copy the block into the block buffer and compute the checksum f_memcpy( &pucBlkBuf[ uiBlkBufOffset], pSCache->pucBlk, uiActualBlkSize); // If this is an encrypted block, we need to make sure it gets encrypted. if ( pucBlkBuf[ BH_ENCRYPTED + uiBlkBufOffset]) { if (RC_BAD( rc = ScaEncryptBlock( pSCache->pFile, &pucBlkBuf[uiBlkBufOffset], uiActualBlkSize, pSCache->pFile-> FileHdr.uiBlockSize))) { goto Exit; } } BlkCheckSum( &pucBlkBuf[ uiBlkBufOffset], CHECKSUM_SET, uiBlkAddr, uiBlockSize); uiBlkBufOffset += uiActualBlkSize; // Write the block to the backup stream if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf))) { goto Exit; } uiBlockCount++; } ScaReleaseCache( pSCache, FALSE); pSCache = NULL; uiBlockFileOffset += uiBlockSize; } else { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Call the status callback if( fnStatus && (uiBlockCount - uiBlockCountLastCB) > 100) { backupInfo.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, uiBlkAddr); if( RC_BAD( rc = fnStatus( FLM_DB_BACKUP_STATUS, (void *)&backupInfo, NULL, pvAppData))) { goto Exit; } uiBlockCountLastCB = uiBlockCount; } } // Output the end-of-backup marker f_memset( pucBlkBuf, 0xFF, sizeof( FLM_BACKER_BLK_HDR_SIZE)); if( RC_BAD( rc = pBackerStream->write( FLM_BACKER_BLK_HDR_SIZE, pucBlkBuf))) { goto Exit; } // Pad the backup so that FlmDbRestore will never read more // data from the input stream than the backup wrote to it. uiBytesToPad = (FLMUINT32)(pBackerStream->getMTUSize() - (FLMUINT)(pBackerStream->getByteCount() % (FLMUINT64)pBackerStream->getMTUSize())); if( uiBytesToPad < pBackerStream->getMTUSize()) { f_memset( pucBlkBuf, 0, uiBytesToPad); if( RC_BAD( rc = pBackerStream->write( uiBytesToPad, pucBlkBuf))) { goto Exit; } } // Because of the double buffering, we need to have one empty // buffer at the end of the file. f_memset( pucBlkBuf, 0, pBackerStream->getMTUSize()); if( RC_BAD( rc = pBackerStream->write( pBackerStream->getMTUSize(), pucBlkBuf))) { goto Exit; } if( RC_BAD( rc = pBackerStream->flush())) { goto Exit; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } if( bDbInitialized) { fdbExit( pDb); } if( pBackerStream) { pBackerStream->Release(); } // Call the status callback now that the background // thread has terminated. if( RC_OK( rc) && fnStatus) { backupInfo.ui64BytesDone = backupInfo.ui64BytesToDo; (void)fnStatus( FLM_DB_BACKUP_STATUS, (void *)&backupInfo, NULL, pvAppData); } if( hookState.pMultiFileHdl) { hookState.pMultiFileHdl->Release(); } if( pucBlkBuf) { f_free( &pucBlkBuf); } if( RC_OK( rc)) { pFBak->bCompletedBackup = TRUE; } pFBak->backupRc = rc; return( rc); } /**************************************************************************** Desc : Ends the backup, updating the log header if needed. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbBackupEnd( HFBACKUP * phBackup) { RCODE rc = FERR_OK; FBak * pFBak = (FBak *)*phBackup; FDB * pDb = (FDB *)pFBak->hDb; FLMBOOL bDbInitialized = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBYTE * pLogHdr = NULL; // End the transaction flmAssert( pFBak->uiTransType != FLM_NO_TRANS); if( RC_BAD( rc = FlmDbTransAbort( (HFDB)pDb))) { goto Exit; } pFBak->uiTransType = FLM_NO_TRANS; pFBak->bTransStarted = FALSE; // Update log header fields if( pFBak->bCompletedBackup && pFBak->uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { // Start an update transaction. if( !pFBak->bCSMode) { bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, 0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans))) { goto Exit; } } else { if( RC_BAD( rc = FlmDbTransBegin( (HFDB)pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT, pFBak->pucDbHeader))) { goto Exit; } pLogHdr = &pFBak->pucDbHeader[ DB_LOG_HEADER_START]; bStartedTrans = TRUE; } // Update the log header fields. if( !pFBak->bCSMode) { UD2FBA( (FLMUINT32)pFBak->uiTransId, &pDb->pFile->ucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID]); } else { UD2FBA( (FLMUINT32)pFBak->uiTransId, &pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]); } // Since there may have been transactions during the backup, // we need to take into account the number of blocks that have // changed during the backup when updating the LOG_BLK_CHG_SINCE_BACKUP // statistic. if( !pFBak->bCSMode) { flmDecrUint( &pDb->pFile->ucUncommittedLogHdr [LOG_BLK_CHG_SINCE_BACKUP], pFBak->uiBlkChgSinceLastBackup); } else { flmDecrUint( &pLogHdr [LOG_BLK_CHG_SINCE_BACKUP], pFBak->uiBlkChgSinceLastBackup); } // Bump the incremental backup sequence number if( pFBak->eBackupType == FLM_INCREMENTAL_BACKUP) { if( !pFBak->bCSMode) { flmIncrUint( &pDb->pFile->ucUncommittedLogHdr [LOG_INC_BACKUP_SEQ_NUM], 1); } else { flmIncrUint( &pLogHdr [LOG_INC_BACKUP_SEQ_NUM], 1); } } // Always change the incremental backup serial number. This is // needed so that if the user performs a full backup, runs some // transactions against the database, performs another full backup, // and then performs an incremental backup we will know that the // incremental backup cannot be restored against the first full // backup. if( !pFBak->bCSMode) { f_memcpy( &pDb->pFile->ucUncommittedLogHdr [LOG_INC_BACKUP_SERIAL_NUM], pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE); } else { f_memcpy( &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM], pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE); } // Commit the transaction and perform a checkpoint so that the // modified log header values will be written. if( !pFBak->bCSMode) { if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) { goto Exit; } bStartedTrans = FALSE; } else { if( RC_BAD( rc = fcsDbTransCommitEx( (HFDB)pDb, TRUE, pFBak->pucDbHeader))) { goto Exit; } bStartedTrans = FALSE; } } Exit: // Abort the active transaction (if any) if( bStartedTrans) { if( !pFBak->bCSMode) { flmAbortDbTrans( pDb); } else { FlmDbTransAbort( (HFDB)pDb); } } // Release the FDB if( bDbInitialized) { fdbExit( pDb); } // Free the backup handle if( pFBak->pucDbHeader) { f_freeAlignedBuffer( &pFBak->pucDbHeader); } f_free( &pFBak); // Clear the handle *phBackup = HFBACKUP_NULL; // Unset the backup flag if( !IsInCSMode( (HFDB)pDb)) { f_mutexLock( gv_FlmSysData.hShareMutex); pDb->pFile->bBackupActive = FALSE; f_mutexUnlock( gv_FlmSysData.hShareMutex); } else { (void)fcsSetBackupActiveFlag( (HFDB)pDb, FALSE); } return( rc); } /**************************************************************************** Desc: Restores a database and supporting files. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRestore( const char * pszDbPath, const char * pszDataDir, const char * pszBackupPath, const char * pszRflDir, const char * pszPassword, F_Restore * pRestoreObj) { RCODE rc = FERR_OK; HFDB hDb = HFDB_NULL; IF_FileHdl * pFileHdl = NULL; IF_FileHdl * pLockFileHdl = NULL; F_SuperFileHdl * pSFile = NULL; char szBasePath[ F_PATH_MAX_SIZE]; char szTmpPath[ F_PATH_MAX_SIZE]; FLMUINT uiDbVersion; FLMUINT uiNextIncNum; eRestoreActionType eRestoreAction; FLMBOOL bRflPreserved; FLMBOOL bMutexLocked = FALSE; FFILE * pFile = NULL; F_FSRestore * pFSRestoreObj = NULL; FLMBOOL bOKToRetry; FLMBYTE * pucDbKey = NULL; FLMUINT uiKeyLen = 0; char * pszTempPassword = NULL; // Set up the callback if( !pRestoreObj) { if( (pFSRestoreObj = f_new F_FSRestore) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pFSRestoreObj->setup( pszDbPath, pszBackupPath, pszRflDir))) { goto Exit; } pRestoreObj = pFSRestoreObj; } // Get the base path flmGetDbBasePath( szBasePath, pszDbPath, NULL); // Force the file to close if it is not used by another thread (void)FlmConfig( FLM_CLOSE_FILE, (void *)pszDbPath, (void *)pszDataDir); gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Lock the global mutex f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; // Free any unused structures that have been unused for the maximum // amount of time. May unlock and re-lock the global mutex. flmCheckNUStructs( 0); // Look up the file using flmFindFile to see if the file is already open. // May unlock and re-lock the global mutex.. if( RC_BAD( rc = flmFindFile( pszDbPath, pszDataDir, &pFile))) { goto Exit; } // If the file is open, we cannot perform a restore if( pFile) { rc = RC_SET( FERR_ACCESS_DENIED); pFile = NULL; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; goto Exit; } // Allocate the FFILE. This will prevent other threads from opening the // database while the restore is being performed. if( RC_BAD( rc = flmAllocFile( pszDbPath, pszDataDir, NULL, &pFile))) { goto Exit; } // Remove the FFILE from the NU list -- it was put in the NU list by // flmAllocFile. We don't want the FFILE to disappear while we are // doing the restore because it happens to age out of the NU list. flmAssert( !pFile->uiUseCount); flmUnlinkFileFromNUList( pFile); // Unlock the global mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Create a lock file. If this fails, it could indicate // that the destination database exists and is in use by another // process. f_sprintf( szTmpPath, "%s.lck", szBasePath); if( RC_BAD( rc = flmCreateLckFile( szTmpPath, &pLockFileHdl))) { goto Exit; } // Create the control file if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile( pszDbPath, FLM_IO_RDWR, &pFileHdl))) { goto Exit; } // Open the backup set if( RC_BAD( rc = pRestoreObj->openBackupSet())) { goto Exit; } // Make a copy of the password as flmRestoreFile may zero-out the original // after unshrouding the key. if ( pszPassword) { if ( RC_BAD( rc = f_alloc( f_strlen( pszPassword) + 1, &pszTempPassword))) { goto Exit; } f_strcpy( pszTempPassword, pszPassword); } // Restore the data in the backup set if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pszDbPath, pszDataDir, pszTempPassword, &pSFile, FALSE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, &eRestoreAction, NULL, &pucDbKey, NULL, &uiKeyLen))) { goto Exit; } // See if we should continue if( eRestoreAction == RESTORE_ACTION_STOP) { goto Exit; } // Close the backup set if( RC_BAD( rc = pRestoreObj->close())) { goto Exit; } // Apply any available incremental backups. uiNextIncNum will be 0 if // the database version does not support incremental backups. if( uiNextIncNum && uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { FLMUINT uiCurrentIncNum; for( ;;) { uiCurrentIncNum = uiNextIncNum; if( RC_BAD( rc = pRestoreObj->openIncFile( uiCurrentIncNum))) { if( rc == FERR_IO_PATH_NOT_FOUND) { rc = FERR_OK; break; } else { goto Exit; } } else { if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pszDbPath, pszDataDir, pszTempPassword, &pSFile, TRUE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, &eRestoreAction, &bOKToRetry, NULL, pucDbKey, &uiKeyLen))) { RCODE tmpRc; if( !bOKToRetry) { // Cannot retry the operation or continue ... the // database is in an unknown state. goto Exit; } if (RC_BAD( tmpRc = pRestoreObj->status( RESTORE_ERROR, 0, (void *)(FLMUINT)rc, NULL, NULL, &eRestoreAction))) { rc = tmpRc; goto Exit; } if( eRestoreAction == RESTORE_ACTION_RETRY || eRestoreAction == RESTORE_ACTION_CONTINUE) { // Abort the current file (if any) if( RC_BAD( rc = pRestoreObj->abortFile())) { goto Exit; } if( eRestoreAction == RESTORE_ACTION_CONTINUE) { // Break out and begin processing the RFL break; } // Otherwise, retry opening the incremental file uiNextIncNum = uiCurrentIncNum; continue; } goto Exit; } // See if we should continue if( eRestoreAction == RESTORE_ACTION_STOP) { goto Exit; } // Close the current file if( RC_BAD( rc = pRestoreObj->close())) { goto Exit; } } } } // Force everything out to disk if( RC_BAD( rc = pSFile->flush())) { goto Exit; } pSFile->Release(); pSFile = NULL; // Don't do anything with the RFL if the preserve flag // isn't set. if( !bRflPreserved) { if( pFSRestoreObj == pRestoreObj) { pFSRestoreObj->Release(); pFSRestoreObj = NULL; } pRestoreObj = NULL; } // Open the file and apply any available RFL files. The // lock file handle is passed to the flmOpenFile call so // that we don't have to give up our lock until the // restore is complete. Also, we don't want to resume // any indexing at this point. By not resuming the indexes, // we can perform a DB diff of two restored databases that // should be identical without having differences in the // tracker container due to background indexing. rc = flmOpenFile( pFile, pszDbPath, pszDataDir, pszRflDir, FO_DONT_RESUME_BACKGROUND_THREADS, TRUE, pRestoreObj, pLockFileHdl, NULL, (FDB **)&hDb); pLockFileHdl = NULL; pFile = NULL; if( RC_BAD( rc)) { goto Exit; } // Close the database (void)FlmDbClose( &hDb); (void)FlmConfig( FLM_CLOSE_FILE, (void *)pszDbPath, (void *)pszDataDir); Exit: if( pSFile) { // Need to release the super file handle before cleaning up the // FFILE because the super file still has a reference to the // FFILE's file ID list. pSFile->Release(); } if( pFile) { // We should only get to this point if rc is bad. flmAssert( RC_BAD( rc)); if( !bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } rc = flmNewFileFinish( pFile, rc); flmFreeFile( pFile); f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } if( hDb != HFDB_NULL) { FlmDbClose( &hDb); } if( pFileHdl) { pFileHdl->Release(); } if( pLockFileHdl) { pLockFileHdl->Release(); } if( pFSRestoreObj) { pFSRestoreObj->Release(); } if (pucDbKey) { f_free( &pucDbKey); } if ( pszTempPassword) { f_free( &pszTempPassword); } // If restore failed, remove all database files (excluding RFL files) if( RC_BAD( rc)) { (void)FlmDbRemove( pszDbPath, pszDataDir, NULL, FALSE); } return( rc); } /*************************************************************************** Desc : Restores a full or incremental backup ****************************************************************************/ FSTATIC RCODE flmRestoreFile( F_Restore * pRestoreObj, const char * pszDbPath, const char * pszDataDir, const char * pszPassword, F_SuperFileHdl ** ppSFile, FLMBOOL bIncremental, FLMUINT * puiDbVersion, FLMUINT * puiNextIncSeqNum, FLMBOOL * pbRflPreserved, eRestoreActionType * peRestoreAction, FLMBOOL * pbOKToRetry, FLMBYTE ** ppucKeyToSave, FLMBYTE * pucKeyToUse, FLMUINT * puiKeyLen) { RCODE rc = FERR_OK; FLMUINT uiBytesWritten; FLMUINT uiLogicalEOF; FLMUINT uiBlkAddr; FLMUINT uiBlockCount = 0; FLMUINT uiActualBlkSize; FLMUINT uiBlockSize; FLMUINT uiDbVersion; FLMUINT uiMaxFileSize; FLMUINT uiBackupMaxFileSize; FLMUINT uiPriorBlkFile = 0; FLMUINT uiSectorSize; FLMBYTE * pLogHdr; FLMBYTE ucIncSerialNum[ F_SERIAL_NUM_SIZE]; FLMBYTE ucNextIncSerialNum[ F_SERIAL_NUM_SIZE]; FLMUINT uiIncSeqNum; FLMBYTE * pucBlkBuf = NULL; FLMBYTE ucLowChecksumByte; FLMUINT uiBlkBufSize; FLMUINT uiPriorBlkAddr = 0; BYTE_PROGRESS byteProgress; FBackupType eBackupType; F_BackerStream * pBackerStream = NULL; F_CCS * pTmpCCS = NULL; F_SuperFileHdl * pSFile = NULL; F_SuperFileClient * pSFileClient = NULL; #ifndef FLM_USE_NICI F_UNREFERENCED_PARM( pszPassword); #endif // Initialize the "ok-to-retry" flag if( pbOKToRetry) { *pbOKToRetry = TRUE; } // Initialize the progress struct f_memset( &byteProgress, 0, sizeof( BYTE_PROGRESS)); // Set up the backer stream object if( (pBackerStream = f_new F_BackerStream) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, pRestoreObj))) { goto Exit; } // Get the sector size if( RC_BAD( rc = gv_FlmSysData.pFileSystem->getSectorSize( pszDbPath, &uiSectorSize))) { goto Exit; } // Allocate a temporary buffer. Try to align the buffer on a sector // boundary to avoid memcpy operatons in the file system. uiBlkBufSize = FLM_BACKER_MTU_SIZE; if( uiSectorSize) { uiBlkBufSize = (((uiBlkBufSize / uiSectorSize) + 1) * uiSectorSize); } if( RC_BAD( rc = f_allocAlignedBuffer( uiBlkBufSize, (void **)&pucBlkBuf))) { goto Exit; } // Read and verify the backup header if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf))) { goto Exit; } if( FB2UD( &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]) != FLM_BACKER_VERSION) { rc = RC_SET_AND_ASSERT( FERR_UNSUPPORTED_VERSION); goto Exit; } if( f_strncmp( (const char *)&pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE) != 0) { rc = RC_SET_AND_ASSERT( FERR_UNSUPPORTED_VERSION); goto Exit; } uiBlockSize = (FLMUINT)FB2UW( &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); if( uiBlockSize > FLM_BACKER_MAX_DB_BLOCK_SIZE) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } // Get the maximum file size from the backup header. uiBackupMaxFileSize = (FLMUINT)FB2UD( &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); // Make sure the MTU is correct if( FB2UD( &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]) != FLM_BACKER_MTU_SIZE) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } // Make sure the backup type is correct eBackupType = (FBackupType)FB2UD( &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); if( (eBackupType == FLM_INCREMENTAL_BACKUP && !bIncremental) || (eBackupType == FLM_FULL_BACKUP && bIncremental)) { // Do not allow an incremental backup to be restored directly. The // only way to restore an incremental backup is to provide the // incremental files when requested by FlmDbRestore. Also, we don't // want to allow the user to mistakenly hand us a full backup when // we are expecting an incremental backup. rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Grab the "next" incremental backup serial number f_memcpy( ucNextIncSerialNum, &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], F_SERIAL_NUM_SIZE); // Get the database version from the backup header uiDbVersion = FB2UD( &pucBlkBuf[ FLM_BACKER_DB_VERSION]); if( puiDbVersion) { *puiDbVersion = uiDbVersion; } // Seek to the database header block if( uiBlockSize > FLM_BACKER_MIN_DB_BLOCK_SIZE) { if( RC_BAD( rc = pBackerStream->read( uiBlockSize - FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf))) { goto Exit; } } // Read the database header block from the backup if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf))) { goto Exit; } // Sanity check - make sure the block size in the backup header // is the same as the size in the database header if( uiBlockSize != FB2UD( &pucBlkBuf[ FLAIM_HEADER_START + DB_BLOCK_SIZE])) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } // Get a pointer to the log header and verify the checksum pLogHdr = &pucBlkBuf[ DB_LOG_HEADER_START]; if( lgHdrCheckSum( pLogHdr, TRUE)) { rc = RC_SET( FERR_BLOCK_CHECKSUM); goto Exit; } // Compare the database version in the log header with // the one extracted from the backup header if( (FLMUINT)FB2UW( &pLogHdr[ LOG_FLAIM_VERSION]) != uiDbVersion) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pLogHdr); // Make sure the maximum block file size matches what was read from the // backup header. if( uiBackupMaxFileSize != uiMaxFileSize) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } // Allocate a super file object if( (pSFile = *ppSFile) != NULL) { pSFile->AddRef(); } else { flmAssert( !bIncremental); if( (pSFile = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( (pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSFileClient->setup( pszDbPath, pszDataDir, uiDbVersion))) { goto Exit; } if( RC_BAD( rc = pSFile->setup( pSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, gv_FlmSysData.uiFileCreateFlags))) { goto Exit; } *ppSFile = pSFile; (*ppSFile)->AddRef(); } // Unshroud the database key (stored in the log header) using the // password the user gave us. (Note: this only re-writes the data // in the log header. It's up to the database open call to actually // create the F_CCS object when it initializes the FFILE.) if( pszPassword && *pszPassword && FB2UD( &pLogHdr[ LOG_FLAIM_VERSION]) >= FLM_FILE_FORMAT_VER_4_60 && FB2UW( &pLogHdr[ LOG_DATABASE_KEY_LEN]) > 0 ) { FLMBYTE * pucTmpBuf = NULL; FLMUINT32 ui32KeyLen = 0; FLMUINT uiNewChecksum; if ( (pTmpCCS = f_new F_CCS) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if ( RC_BAD( rc = pTmpCCS->init( TRUE, FLM_NICI_AES))) { goto Exit; } ui32KeyLen = FB2UW( &pLogHdr [ LOG_DATABASE_KEY_LEN]); if( RC_BAD( rc = pTmpCCS->setKeyFromStore( &pLogHdr[ LOG_DATABASE_KEY], ui32KeyLen, pszPassword, NULL, FALSE))) { goto Exit; } if( RC_BAD( rc = pTmpCCS->getKeyToStore( &pucTmpBuf, &ui32KeyLen, NULL, NULL, FALSE))) { goto Exit; } // Verify that the field in the log header is long enough to // hold the key. if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN) { f_free( &pucTmpBuf); rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY); goto Exit; } UW2FBA( ui32KeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]); f_memcpy( &pLogHdr[LOG_DATABASE_KEY], pucTmpBuf, ui32KeyLen); uiNewChecksum = lgHdrCheckSum( pLogHdr, FALSE); UW2FBA( (FLMUINT16)uiNewChecksum, &pLogHdr[ LOG_HDR_CHECKSUM]); f_free( &pucTmpBuf); pTmpCCS->Release(); pTmpCCS = NULL; if( ppucKeyToSave) { // Need to allocate a buffer to save the key in if ( RC_BAD( rc = f_alloc( ui32KeyLen, ppucKeyToSave))) { goto Exit; } f_memcpy( *ppucKeyToSave, &pLogHdr[ LOG_DATABASE_KEY], ui32KeyLen); *puiKeyLen = ui32KeyLen; } } else if( pucKeyToUse) { UW2FBA( (FLMUINT16)*puiKeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]); f_memcpy( &pLogHdr[ LOG_DATABASE_KEY], pucKeyToUse, *puiKeyLen); } // Get the logical EOF from the log header uiLogicalEOF = FB2UD( &pLogHdr[ LOG_LOGICAL_EOF]); // Are RFL files being preserved? if( pbRflPreserved) { *pbRflPreserved = pLogHdr[ LOG_KEEP_RFL_FILES] ? TRUE : FALSE; } // Get the incremental backup sequence number uiIncSeqNum = FB2UD( &pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); *puiNextIncSeqNum = uiIncSeqNum; if( bIncremental) { (*puiNextIncSeqNum)++; } // Get information about the incremental backup if( bIncremental) { FLMBYTE ucTmpSerialNum[ F_SERIAL_NUM_SIZE]; FLMBYTE ucTmpSeqNum[ 4]; FLMUINT uiTmp; f_memcpy( ucIncSerialNum, &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM], F_SERIAL_NUM_SIZE); // Compare the incremental backup sequence number to the value in the // database's log header. if( RC_BAD( rc = pSFile->readBlock( FSBlkAddress( 0, DB_LOG_HEADER_START + LOG_INC_BACKUP_SEQ_NUM), 4, ucTmpSeqNum, &uiTmp))) { goto Exit; } if( FB2UD( &ucTmpSeqNum[ 0]) != uiIncSeqNum) { rc = RC_SET( FERR_INVALID_FILE_SEQUENCE); goto Exit; } // Compare the incremental backup serial number to the value in the // database's log header. if( RC_BAD( rc = pSFile->readBlock( FSBlkAddress( 0, DB_LOG_HEADER_START + LOG_INC_BACKUP_SERIAL_NUM), F_SERIAL_NUM_SIZE, ucTmpSerialNum, &uiTmp))) { goto Exit; } if( f_memcmp( ucIncSerialNum, ucTmpSerialNum, F_SERIAL_NUM_SIZE) != 0) { rc = RC_SET( FERR_SERIAL_NUM_MISMATCH); goto Exit; } // Increment the incremental backup sequence number UD2FBA( (FLMUINT32)(uiIncSeqNum + 1), &pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]); } // At the start of a backup, either incremental or full, // we generate a new incremental serial number. This is needed so // that if the user performs a full backup, runs some transactions // against the database, performs another full backup, and then // performs an incremental backup we will know that the incremental // backup cannot be restored against the first full backup. // Since the new serial number is not written to the database's log // header until after the backup completes, we need to put the // new serial number in the log header during the restore. In doing // so, the log header will contain the correct serial number for a // subsequent incremental backup that may have been made. if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) { f_memcpy( &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM], ucNextIncSerialNum, F_SERIAL_NUM_SIZE); } // Re-calculate the log header checksum UW2FBA( (FLMUINT16)lgHdrCheckSum( pLogHdr, FALSE), &pLogHdr[ LOG_HDR_CHECKSUM]); pLogHdr = NULL; // Set the "ok-to-retry" flag if( pbOKToRetry) { *pbOKToRetry = FALSE; } // Write the database header if( RC_BAD( rc = pSFile->writeBlock( FSBlkAddress( 0, 0), uiBlockSize, pucBlkBuf, &uiBytesWritten))) { goto Exit; } // The status callback will give a general idea of how much work // is left to do. We don't have any way to get the total size // of the stream to give a correct count, so a close estimate // will have to suffice. byteProgress.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, uiLogicalEOF); // Write the blocks in the backup file to the database for( ;;) { if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_BLK_HDR_SIZE, pucBlkBuf))) { goto Exit; } uiBlockCount++; uiBlkAddr = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); uiActualBlkSize = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_SIZE_OFFSET]); // Are we done? if( uiBlkAddr == 0xFFFFFFFF) { break; } if( !uiBlkAddr || !FSAddrIsBelow( uiBlkAddr, uiLogicalEOF) || (uiPriorBlkAddr && !FSAddrIsBelow( uiPriorBlkAddr, uiBlkAddr))) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } // Read and process the block if( uiActualBlkSize > uiBlockSize || uiActualBlkSize < BH_OVHD) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } if( RC_BAD( rc = pBackerStream->read( uiActualBlkSize, pucBlkBuf))) { goto Exit; } if( (GET_BH_ADDR( pucBlkBuf) & 0xFFFFFF00) != (uiBlkAddr & 0xFFFFFF00)) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } if( uiActualBlkSize < uiBlockSize) { f_memset( &pucBlkBuf[ uiActualBlkSize], 0, uiBlockSize - uiActualBlkSize); } // Verify the checksum ucLowChecksumByte = pucBlkBuf[ BH_CHECKSUM_LOW]; if( RC_BAD( rc = BlkCheckSum( pucBlkBuf, CHECKSUM_CHECK, uiBlkAddr, uiBlockSize))) { if( rc == FERR_BLOCK_CHECKSUM) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); } goto Exit; } pucBlkBuf[ BH_CHECKSUM_LOW] = ucLowChecksumByte; // Write the block to the database if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr, uiBlockSize, pucBlkBuf, &uiBytesWritten))) { if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { // Create a new block file if( FSGetFileNumber( uiBlkAddr) != (uiPriorBlkFile + 1)) { rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP); goto Exit; } if( RC_BAD( rc = pSFile->createFile( FSGetFileNumber( uiBlkAddr)))) { goto Exit; } if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr, uiBlockSize, pucBlkBuf, &uiBytesWritten))) { goto Exit; } } else { goto Exit; } } uiPriorBlkAddr = uiBlkAddr; uiPriorBlkFile = FSGetFileNumber( uiBlkAddr); if( (uiBlockCount & 0x7F) == 0x7F) { byteProgress.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, uiBlkAddr); if( RC_BAD( rc = pRestoreObj->status( RESTORE_PROGRESS, 0, (void *)&byteProgress, NULL, NULL, peRestoreAction))) { goto Exit; } if( *peRestoreAction == RESTORE_ACTION_STOP) { rc = RC_SET( FERR_USER_ABORT); goto Exit; } } } // Call the status callback one last time. byteProgress.ui64BytesDone = byteProgress.ui64BytesToDo; if( RC_BAD( rc = pRestoreObj->status( RESTORE_PROGRESS, 0, (void *)&byteProgress, NULL, NULL, peRestoreAction))) { goto Exit; } if( *peRestoreAction == RESTORE_ACTION_STOP) { // It is safe to jump to exit at this point goto Exit; } Exit: if( pucBlkBuf) { f_freeAlignedBuffer( (void **)&pucBlkBuf); } if( pBackerStream) { pBackerStream->Release(); } if( pTmpCCS) { pTmpCCS->Release(); } if( pSFile) { pSFile->Release(); } if( pSFileClient) { pSFileClient->Release(); } return( rc); } /**************************************************************************** Desc: Default hook for creating a backup file set ****************************************************************************/ FSTATIC RCODE flmDefaultBackerWriteHook( void * pvBuffer, FLMUINT uiBytesToWrite, void * pvCallbackData) { BACKER_HOOK_STATE * pState = (BACKER_HOOK_STATE *)pvCallbackData; FLMUINT uiBytesWritten; RCODE rc = pState->rc; if( RC_BAD( rc)) { goto Exit; } if( !pState->pMultiFileHdl) { // Remove any existing backup files if( RC_BAD( rc = FlmAllocMultiFileHdl( &pState->pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = pState->pMultiFileHdl->deleteMultiFile( pState->szPath)) && rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) { pState->pMultiFileHdl->Release(); pState->pMultiFileHdl = NULL; goto Exit; } if( RC_BAD( rc = pState->pMultiFileHdl->createFile( pState->szPath))) { pState->pMultiFileHdl->Release(); pState->pMultiFileHdl = NULL; goto Exit; } } rc = pState->pMultiFileHdl->write( pState->ui64Offset, uiBytesToWrite, pvBuffer, &uiBytesWritten); pState->ui64Offset += uiBytesWritten; Exit: if( RC_BAD( rc)) { pState->rc = rc; if( pState->pMultiFileHdl) { pState->pMultiFileHdl->Release(); pState->pMultiFileHdl = NULL; } } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ F_BackerStream::F_BackerStream( void) { m_bSetup = FALSE; m_bFirstRead = TRUE; m_ui64ByteCount = 0; m_uiBufOffset = 0; m_pRestoreObj = NULL; m_hDataSem = F_SEM_NULL; m_hIdleSem = F_SEM_NULL; m_pThread = NULL; m_rc = FERR_OK; m_pucInBuf = NULL; m_puiInOffset = NULL; m_pucOutBuf = NULL; m_puiOutOffset = NULL; m_pucBufs[ 0] = NULL; m_pucBufs[ 1] = NULL; m_uiOffsets[ 0] = 0; m_uiOffsets[ 1] = 0; m_uiMTUSize = 0; m_uiPendingIO = 0; m_fnWrite = NULL; m_pvCallbackData = NULL; } /**************************************************************************** Desc: ****************************************************************************/ F_BackerStream::~F_BackerStream( void) { shutdownThreads(); if( m_hDataSem != F_SEM_NULL) { f_semDestroy( &m_hDataSem); } if( m_hIdleSem != F_SEM_NULL) { f_semDestroy( &m_hIdleSem); } if( m_pucBufs[ 0]) { f_free( &m_pucBufs[ 0]); } if( m_pucBufs[ 1]) { f_free( &m_pucBufs[ 1]); } } /**************************************************************************** Desc: Start any background threads ****************************************************************************/ RCODE F_BackerStream::startThreads( void) { RCODE rc = FERR_OK; if( m_pThread) { rc = RC_SET( FERR_FAILURE); goto Exit; } // The semaphore handles better be null flmAssert( m_hDataSem == F_SEM_NULL); flmAssert( m_hIdleSem == F_SEM_NULL); // Create a semaphore to signal the background thread // that data is available if( RC_BAD( rc = f_semCreate( &m_hDataSem))) { goto Exit; } // Create a semaphore to signal when the background thread // is idle if( RC_BAD( rc = f_semCreate( &m_hIdleSem))) { goto Exit; } // Start the thread if( m_fnWrite) { if( RC_BAD( rc = f_threadCreate( &m_pThread, F_BackerStream::writeThread, "backup", 0, 0, (void *)this))) { goto Exit; } } else if( m_pRestoreObj) { if( RC_BAD( rc = f_threadCreate( &m_pThread, F_BackerStream::readThread, "restore", 0, 0, (void *)this))) { goto Exit; } } else { flmAssert( 0); rc = RC_SET( FERR_FAILURE); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Shut down any background threads ****************************************************************************/ void F_BackerStream::shutdownThreads( void) { if( m_pThread) { // Shut down the background read or write thread. m_pThread->setShutdownFlag(); f_semSignal( m_hDataSem); f_threadDestroy( &m_pThread); // Now that the thread has terminated, it is safe // to destroy the data and idle semaphores. f_semDestroy( &m_hDataSem); f_semDestroy( &m_hIdleSem); } } /**************************************************************************** Desc: Setup method to use the backer stream as an input stream ****************************************************************************/ RCODE F_BackerStream::setup( FLMUINT uiMTUSize, F_Restore * pRestoreObj) { RCODE rc = FERR_OK; flmAssert( pRestoreObj); flmAssert( !m_bSetup); m_pRestoreObj = pRestoreObj; m_uiMTUSize = uiMTUSize; if( RC_BAD( rc = _setup())) { goto Exit; } // Fire up the background threads if( RC_BAD( rc = startThreads())) { goto Exit; } m_bSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Setup method to use the backer stream as an output stream ****************************************************************************/ RCODE F_BackerStream::setup( FLMUINT uiMTUSize, BACKER_WRITE_HOOK fnWrite, void * pvCallbackData) { RCODE rc = FERR_OK; flmAssert( fnWrite); flmAssert( !m_bSetup); m_fnWrite = fnWrite; m_uiMTUSize = uiMTUSize; m_pvCallbackData = pvCallbackData; if( RC_BAD( rc = _setup())) { goto Exit; } // Fire up the background threads if( RC_BAD( rc = startThreads())) { goto Exit; } m_bSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Performs setup operations common to read and write streams ****************************************************************************/ RCODE F_BackerStream::_setup( void) { RCODE rc = FERR_OK; // Allocate a buffer for reading or writing blocks if( (m_uiMTUSize < (2 * FLM_BACKER_MAX_DB_BLOCK_SIZE)) || m_uiMTUSize % FLM_BACKER_MAX_DB_BLOCK_SIZE) { rc = RC_SET( FERR_INVALID_PARM); goto Exit; } // Allocate buffers for reading or writing if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 0]))) { goto Exit; } if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 1]))) { goto Exit; } m_pucInBuf = m_pucBufs[ 0]; m_puiInOffset = &m_uiOffsets[ 0]; m_pucOutBuf = m_pucBufs[ 1]; m_puiOutOffset = &m_uiOffsets[ 1]; Exit: return( rc); } /**************************************************************************** Desc: Reads data from the input stream ****************************************************************************/ RCODE F_BackerStream::read( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesRead) { FLMUINT uiBufSize; FLMBYTE * pucBuf; FLMUINT uiBytesRead = 0; FLMUINT uiBytesAvail; RCODE rc = FERR_OK; flmAssert( m_bSetup); flmAssert( m_pRestoreObj); flmAssert( uiLength); if( m_bFirstRead) { m_bFirstRead = FALSE; // Prime the pump. Call signalThread twice ... once to // get the first chunk of data and a second time to have // the background thread pre-fetch the next chunk. A backup // will always have at least two MTU data chunks, so we should // never get an IO_END_OF_FILE error. If we do, the restore // operation needs to abort (which will happen because the // error will be returned to the caller of this routine). if( RC_BAD( rc = signalThread()) || RC_BAD( rc = signalThread())) { goto Exit; } } while( uiLength) { uiBufSize = *m_puiOutOffset; pucBuf = m_pucOutBuf; uiBytesAvail = uiBufSize - m_uiBufOffset; flmAssert( uiBytesAvail); if( uiBytesAvail < uiLength) { f_memcpy( &pucData[ uiBytesRead], &pucBuf[ m_uiBufOffset], uiBytesAvail); m_uiBufOffset += uiBytesAvail; uiBytesRead += uiBytesAvail; uiLength -= uiBytesAvail; } else { f_memcpy( &pucData[ uiBytesRead], &pucBuf[ m_uiBufOffset], uiLength); m_uiBufOffset += uiLength; uiBytesRead += uiLength; uiLength = 0; } if( m_uiBufOffset == uiBufSize) { m_uiBufOffset = 0; if( RC_BAD( rc = signalThread())) { // Since we are reading MTU-sized units and the restore // code knows when to stop reading, we should never // get an IO_END_OF_FILE error back from a call to // signalThread(). If we do, we need to return the // error to the caller (FlmDbRestore). goto Exit; } } } Exit: if( puiBytesRead) { *puiBytesRead = uiBytesRead; } m_ui64ByteCount += (FLMUINT64)uiBytesRead; return( rc); } /**************************************************************************** Desc: Writes data to the output stream ****************************************************************************/ RCODE F_BackerStream::write( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesWritten) { FLMUINT uiMaxWriteSize; FLMUINT uiBytesWritten = 0; RCODE rc = FERR_OK; flmAssert( m_bSetup); flmAssert( m_fnWrite); flmAssert( uiLength); while( uiLength) { uiMaxWriteSize = m_uiMTUSize - *m_puiInOffset; flmAssert( uiMaxWriteSize); if( uiMaxWriteSize < uiLength) { f_memcpy( &m_pucInBuf[ *m_puiInOffset], &pucData[ uiBytesWritten], uiMaxWriteSize); (*m_puiInOffset) += uiMaxWriteSize; uiBytesWritten += uiMaxWriteSize; uiLength -= uiMaxWriteSize; } else { f_memcpy( &m_pucInBuf[ *m_puiInOffset], &pucData[ uiBytesWritten], uiLength); (*m_puiInOffset) += uiLength; uiBytesWritten += uiLength; uiLength = 0; } if( (*m_puiInOffset) == m_uiMTUSize) { if( RC_BAD( rc = signalThread())) { goto Exit; } } } Exit: if( puiBytesWritten) { *puiBytesWritten = uiBytesWritten; } m_ui64ByteCount += (FLMUINT64)uiBytesWritten; return( rc); } /**************************************************************************** Desc: Flushes any pending writes to the output stream ****************************************************************************/ RCODE F_BackerStream::flush( void) { RCODE rc = FERR_OK; flmAssert( m_bSetup); if( m_fnWrite && m_pThread) { if( *m_puiInOffset) { if( RC_BAD( rc = signalThread())) { goto Exit; } } // Wait for the background thread to become idle. When it // does, we know that all writes have completed. if( RC_BAD( rc = f_semWait( m_hIdleSem, F_WAITFOREVER))) { goto Exit; } // If the background thread set an error code, we need to return it. rc = m_rc; // At this point, we know the background thread is either waiting // for the data semaphore to be signaled or it has exited due to // an error. We need to re-signal the idle semaphore so that // other F_BackerStream calls (i.e., additional calls to // flush, etc.) will not block waiting for it to be signaled // since it won't be signaled by the background thread until // after the data semaphore has been signaled again. f_semSignal( m_hIdleSem); // Jump to exit if we have a bad rc if( RC_BAD( rc)) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Signals the read or write thread indicating that data is needed or that data is available. ****************************************************************************/ RCODE F_BackerStream::signalThread( void) { FLMBYTE * pucTmp; FLMUINT * puiTmp; RCODE rc = FERR_OK; flmAssert( m_bSetup); // Return an error if we don't have a thread. if( !m_pThread) { rc = RC_SET( FERR_FAILURE); goto Exit; } // Wait for the thread to become idle if( RC_BAD( rc = f_semWait( m_hIdleSem, F_WAITFOREVER))) { goto Exit; } if( RC_BAD( rc = m_rc)) { // If m_rc is bad, we know that the background thread has // exited and will not signal the idle semaphore again. // Thus, we will re-signal the idle semaphore so that if the // code using this class happens to call flush() or some // other method that waits on the idle semaphore, we // won't wait forever on something that will never happen. f_semSignal( m_hIdleSem); // Check the error code if( rc != FERR_IO_END_OF_FILE) { goto Exit; } } pucTmp = m_pucOutBuf; puiTmp = m_puiOutOffset; m_pucOutBuf = m_pucInBuf; m_puiOutOffset = m_puiInOffset; m_pucInBuf = pucTmp; m_puiInOffset = puiTmp; *(m_puiInOffset) = 0; // If m_rc is bad, the background thread has terminated. if( RC_OK( m_rc)) { // Signal the thread to read or write data // NOTE: The background thread will never be decrementing // m_uiPendingIO while we are incrementing it because it // will be blocked on m_hDataSem waiting for it to // be signaled. m_uiPendingIO++; f_semSignal( m_hDataSem); } Exit: return( rc); } /**************************************************************************** Desc: This thread reads data in the background ****************************************************************************/ RCODE FLMAPI F_BackerStream::readThread( IF_Thread * pThread) { RCODE rc = FERR_OK; F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); for( ;;) { f_semSignal( pBackerStream->m_hIdleSem); if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, F_WAITFOREVER))) { goto Exit; } if( !pBackerStream->m_uiPendingIO && pThread->getShutdownFlag()) { break; } if( RC_BAD( rc = pBackerStream->m_pRestoreObj->read( pBackerStream->m_uiMTUSize, pBackerStream->m_pucInBuf, pBackerStream->m_puiInOffset))) { goto Exit; } flmAssert( pBackerStream->m_uiPendingIO); pBackerStream->m_uiPendingIO--; } Exit: pBackerStream->m_rc = rc; pBackerStream->m_uiPendingIO = 0; f_semSignal( pBackerStream->m_hIdleSem); return( rc); } /**************************************************************************** Desc: This thread writes data in the background ****************************************************************************/ RCODE FLMAPI F_BackerStream::writeThread( IF_Thread * pThread) { RCODE rc = FERR_OK; F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); for( ;;) { f_semSignal( pBackerStream->m_hIdleSem); if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, F_WAITFOREVER))) { goto Exit; } if( *(pBackerStream->m_puiOutOffset)) { if( RC_BAD( rc = pBackerStream->m_fnWrite( pBackerStream->m_pucOutBuf, *(pBackerStream->m_puiOutOffset), pBackerStream->m_pvCallbackData))) { goto Exit; } // Reset *puiOutOffset so that we won't re-write // the same data again if we receive a shut-down // signal. *(pBackerStream->m_puiOutOffset) = 0; } // Need to put the thread shutdown check here // so that if the thread is signaled twice, // once to do final work and once to shut down, // we will actually do the work before we exit. if( pThread->getShutdownFlag()) { break; } } Exit: pBackerStream->m_rc = rc; pBackerStream->m_uiPendingIO = 0; f_semSignal( pBackerStream->m_hIdleSem); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ F_FSRestore::F_FSRestore() { m_pFileHdl = NULL; m_pMultiFileHdl = NULL; m_ui64Offset = 0; m_bSetupCalled = FALSE; m_szDbPath[ 0] = 0; m_uiDbVersion = 0; m_szBackupSetPath[ 0] = 0; m_szRflDir[ 0] = 0; m_bOpen = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ F_FSRestore::~F_FSRestore() { if( m_bOpen) { (void)close(); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::setup( const char * pucDbPath, const char * pucBackupSetPath, const char * pucRflDir) { flmAssert( !m_bSetupCalled); flmAssert( pucDbPath); flmAssert( pucBackupSetPath); f_strcpy( m_szDbPath, pucDbPath); f_strcpy( m_szBackupSetPath, pucBackupSetPath); if( pucRflDir) { f_strcpy( m_szRflDir, pucRflDir); } m_bSetupCalled = TRUE; return( FERR_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openBackupSet( void) { RCODE rc = FERR_OK; flmAssert( m_bSetupCalled); flmAssert( !m_pMultiFileHdl); if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->openFile( m_szBackupSetPath))) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } m_ui64Offset = 0; m_bOpen = TRUE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openRflFile( FLMUINT uiFileNum) { RCODE rc = FERR_OK; char szRflPath[ F_PATH_MAX_SIZE]; char szDbPrefix[ F_FILENAME_SIZE]; char szBaseName[ F_FILENAME_SIZE]; FLMBYTE * pBuf = NULL; FILE_HDR fileHdr; LOG_HDR logHdr; IF_FileHdl * pFileHdl = NULL; flmAssert( m_bSetupCalled); flmAssert( uiFileNum); flmAssert( !m_pFileHdl); // Read the database header to determine the version number if( !m_uiDbVersion) { if (RC_BAD( rc = f_alloc( 2048, &pBuf))) { goto Exit; } if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( m_szDbPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl))) { goto Exit; } if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pFileHdl, pBuf, &fileHdr, &logHdr, NULL))) { goto Exit; } pFileHdl->Release(); pFileHdl = NULL; m_uiDbVersion = fileHdr.uiVersionNum; } // Generate the log file name. if( RC_BAD( rc = rflGetDirAndPrefix( m_uiDbVersion, m_szDbPath, m_szRflDir, szRflPath, szDbPrefix))) { goto Exit; } rflGetBaseFileName( m_uiDbVersion, szDbPrefix, uiFileNum, szBaseName); gv_FlmSysData.pFileSystem->pathAppend( szRflPath, szBaseName); // Open the file. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( szRflPath, gv_FlmSysData.uiFileOpenFlags, &m_pFileHdl))) { goto Exit; } m_bOpen = TRUE; m_ui64Offset = 0; Exit: if( pBuf) { f_free( &pBuf); } if( pFileHdl) { pFileHdl->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openIncFile( FLMUINT uiFileNum) { RCODE rc = FERR_OK; char szIncPath[ F_PATH_MAX_SIZE]; char szIncFile[ F_FILENAME_SIZE]; flmAssert( m_bSetupCalled); flmAssert( !m_pMultiFileHdl); // Since this is a non-interactive restore, we will "guess" // that incremental backups are located in the same parent // directory as the main backup set. We will further assume // that the incremental backup sets have been named XXXXXXXX.INC, // where X is a hex digit. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( m_szBackupSetPath, szIncPath, NULL))) { goto Exit; } f_sprintf( szIncFile, "%08X.INC", (unsigned)uiFileNum); gv_FlmSysData.pFileSystem->pathAppend( szIncPath, szIncFile); if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->openFile( szIncPath))) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } m_ui64Offset = 0; m_bOpen = TRUE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::read( FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead) { RCODE rc = FERR_OK; FLMUINT uiBytesRead = 0; flmAssert( m_bSetupCalled); flmAssert( m_pFileHdl || m_pMultiFileHdl); if( m_pMultiFileHdl) { if( RC_BAD( rc = m_pMultiFileHdl->read( m_ui64Offset, uiLength, pvBuffer, &uiBytesRead))) { goto Exit; } } else { if( RC_BAD( rc = m_pFileHdl->read( (FLMUINT)m_ui64Offset, uiLength, pvBuffer, &uiBytesRead))) { goto Exit; } } f_assert( uiBytesRead <= uiLength); Exit: m_ui64Offset += uiBytesRead; if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::close( void) { flmAssert( m_bSetupCalled); if( m_pMultiFileHdl) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; } if( m_pFileHdl) { m_pFileHdl->Release(); m_pFileHdl = NULL; } m_bOpen = FALSE; m_ui64Offset = 0; return( FERR_OK); } libflaim-4.9.966/src/imonchk.cpp0000644000175000017500000012767310510774540020030 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Check a database via HTTP monitoring. // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonchk.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define CHECK_FORM_NAME "CheckForm" #define DATABASE_NAME_FIELD "databasename" #define DATA_DIR_FIELD "datadir" #define RFL_DIR_FIELD "rfldir" #define LOG_FILE_NAME_FIELD "logfilename" #define CHECK_INDEXES_FIELD "checkindexes" #define REPAIR_INDEXES_FIELD "repairindexes" #define DETAILED_STATS_FIELD "detailedstats" FSTATIC void format64Num( FLMUINT64 ui64Num, char * pszNum); FSTATIC RCODE copyStr( char ** ppszDestStr, const char * pszSrcStr); FSTATIC void copyNames( CHECK_STATUS * pDestCheckStatus, CHECK_STATUS * pSrcCheckStatus); FSTATIC void freeCheckStatus( CHECK_STATUS * pCheckStatus, FLMBOOL bFreeStruct); FSTATIC void imonLogField( IF_FileHdl * pLogFile, F_NameTable * pNameTable, FlmRecord * pRecord, void * pvField, FLMUINT uiStartCol, FLMUINT uiLevelOffset); FSTATIC void imonLogKeyError( IF_FileHdl * pLogFile, F_NameTable * pNameTable, CORRUPT_INFO * pCorrupt); FSTATIC void imonLogCorruptError( IF_FileHdl * pLogFile, F_NameTable * pNameTable, CORRUPT_INFO * pCorrupt); FSTATIC RCODE CheckStatusCB( eStatusType eStatus, void * pvParm1, void * pvParm2, void * pvAppData); FSTATIC RCODE FLMAPI imonDoCheck( IF_Thread * pThread); FSTATIC void imonLogStr( IF_FileHdl * pLogFile, FLMUINT uiIndent, const char * pszStr); /**************************************************************************** Desc: Prints the web page for checking a database. ****************************************************************************/ RCODE F_CheckDbPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; const char * pszErrType = NULL; RCODE runRc = FERR_OK; F_Session * pFlmSession = m_pFlmSession; HFDB hDb = HFDB_NULL; F_NameTable * pNameTable = NULL; char szTmp[ 32]; char * pszTmp; char * pszOperation = NULL; char * pszDbName = NULL; char * pszDataDir = NULL; char * pszRflDir = NULL; FLMBOOL bCheckingIndexes = FALSE; FLMBOOL bRepairingIndexes = FALSE; FLMBOOL bDetailedStatistics = FALSE; char * pszLogFileName = NULL; FLMBOOL bPerformCheck = FALSE; FLMBOOL bStopCheck = FALSE; FLMUINT uiCheckThreadId; CHECK_STATUS CheckStatus; char szDbKey[ F_SESSION_DB_KEY_LEN]; f_memset( &CheckStatus, 0, sizeof( CHECK_STATUS)); // Acquire a FLAIM session if (!pFlmSession) { rc = RC_SET( m_uiSessionRC); goto ReportErrorExit; } // Get the database handle, if any if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams, ppszParams, pFlmSession, &hDb, szDbKey))) { hDb = HFDB_NULL; } else { if( IsInCSMode( hDb)) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto ReportErrorExit; } if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable))) { goto ReportErrorExit; } } // Get the value of the Operation field, if present. getFormValueByName( "Operation", &pszOperation, 0, NULL); if (pszOperation) { if (f_stricmp( pszOperation, OPERATION_CHECK) == 0) { bPerformCheck = TRUE; } else if (f_stricmp( pszOperation, OPERATION_STOP) == 0) { bStopCheck = TRUE; } } // Get the database name, if any if (getFormValueByName( DATABASE_NAME_FIELD, &pszDbName, 0, NULL) == 0) { if (pszDbName && *pszDbName) { fcsDecodeHttpString( pszDbName); } } // Get the database directory, if any if (getFormValueByName( DATA_DIR_FIELD, &pszDataDir, 0, NULL) == 0) { if (pszDataDir && *pszDataDir) { fcsDecodeHttpString( pszDataDir); } } // Get the RFL directory, if any if (getFormValueByName( RFL_DIR_FIELD, &pszRflDir, 0, NULL) == 0) { if (pszRflDir && *pszRflDir) { fcsDecodeHttpString( pszRflDir); } } // Get the log file name, if any if (getFormValueByName( LOG_FILE_NAME_FIELD, &pszLogFileName, 0, NULL) == 0) { if (pszLogFileName && *pszLogFileName) { fcsDecodeHttpString( pszLogFileName); } } // Get the flag for whether or not to check indexes. szTmp [0] = 0; pszTmp = &szTmp [0]; if( RC_BAD( getFormValueByName( CHECK_INDEXES_FIELD, &pszTmp, sizeof( szTmp), NULL))) { if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, CHECK_INDEXES_FIELD, sizeof( szTmp), szTmp))) { szTmp [0] = 0; } } if (f_strcmp( szTmp, "yes") == 0) { bCheckingIndexes = TRUE; } // Get the flag for whether or not to repair indexes szTmp [0] = 0; pszTmp = &szTmp [0]; if (RC_BAD( getFormValueByName( REPAIR_INDEXES_FIELD, &pszTmp, sizeof( szTmp), NULL))) { if( RC_BAD( ExtractParameter( uiNumParams, ppszParams, REPAIR_INDEXES_FIELD, sizeof( szTmp), szTmp))) { szTmp [0] = 0; } } if (f_strcmp( szTmp, "yes") == 0) { bRepairingIndexes = TRUE; } // Get the flag for whether or not to collect detailed statistics. szTmp [0] = 0; pszTmp = &szTmp [0]; if (RC_BAD( getFormValueByName( DETAILED_STATS_FIELD, &pszTmp, sizeof( szTmp), NULL))) { if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, DETAILED_STATS_FIELD, sizeof( szTmp), szTmp))) { szTmp [0] = 0; } } if (f_strcmp( szTmp, "yes") == 0) { bDetailedStatistics = TRUE; } // See if we had a check running. Get the check thread ID // if any. szTmp [0] = '\0'; uiCheckThreadId = 0; if (RC_OK( ExtractParameter( uiNumParams, ppszParams, "Running", sizeof( szTmp), szTmp))) { if (szTmp [0]) { uiCheckThreadId = f_atoud( szTmp); CheckStatus.bCheckRunning = TRUE; } } if (bPerformCheck) { // Better not have both bCheckRunning and bPerformCheck set! flmAssert( !CheckStatus.bCheckRunning); if (RC_BAD( runRc = runCheck( pFlmSession, &hDb, szDbKey, pszDbName, pszDataDir, pszRflDir, pszLogFileName, bCheckingIndexes, bRepairingIndexes, bDetailedStatistics, &uiCheckThreadId))) { pszErrType = "RUNNING CHECK"; } else { CheckStatus.bCheckRunning = TRUE; } } // Stop the check, if requested, or get the check data. if (CheckStatus.bCheckRunning) { // getCheckStatus could change CheckStatus.bCheckRunning // to FALSE. getCheckStatus( uiCheckThreadId, bStopCheck, &CheckStatus); } // Output the web page. if (!CheckStatus.bCheckRunning && CheckStatus.bHaveCheckStatus) { // If we have check results, output a page for viewing/editing them. printDocStart( "Check Results"); } else if (!CheckStatus.bCheckRunning) { printDocStart( "Run Check"); if (pszErrType) { fnPrintf( m_pHRequest, "
ERROR %04X (%s) %s

\n", (unsigned)runRc, FlmErrorString( runRc), pszErrType); } } else { stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n" "\n"); printStyle(); // Output html that will cause a refresh to occur. fnPrintf( m_pHRequest, "" "Check Status\n", m_pszURLString, (unsigned)uiCheckThreadId, szDbKey); fnPrintf( m_pHRequest, "\n" "\n"); } // Output the form for entering the check parameters and // check status. outputCheckForm( hDb, szDbKey, &CheckStatus, pNameTable, uiCheckThreadId); // End the document printDocEnd(); Exit: fnEmit(); if (pszOperation) { f_free( &pszOperation); } if (pszDbName) { f_free( &pszDbName); } if (pszDataDir) { f_free( &pszDataDir); } if (pszRflDir) { f_free( &pszRflDir); } if (pszLogFileName) { f_free( &pszLogFileName); } freeCheckStatus( &CheckStatus, FALSE); return( FERR_OK); ReportErrorExit: printErrorPage( rc); goto Exit; } /**************************************************************************** Desc: Output a string parameter. ****************************************************************************/ void F_CheckDbPage::outputStrParam( CHECK_STATUS * pCheckStatus, FLMBOOL bHighlight, const char * pszParamName, const char * pszFieldName, FLMUINT uiMaxValueLen, const char * pszFieldValue) { printTableRowStart( bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT, 35); fnPrintf( m_pHRequest, "%s", pszParamName); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_LEFT, 65); if (pCheckStatus->bCheckRunning || !pszFieldName) { if (pszFieldValue) { printEncodedString( pszFieldValue, HTML_ENCODING); } else { fnPrintf( m_pHRequest, " "); } } else { fnPrintf( m_pHRequest, "bHaveCheckStatus && pszFieldValue && *pszFieldValue) { fnPrintf( m_pHRequest, " value=\"", pszFieldValue); printEncodedString( pszFieldValue, HTML_ENCODING); fnPrintf( m_pHRequest, "\">\n"); } else { fnPrintf( m_pHRequest, ">\n"); } } printTableDataEnd(); printTableRowEnd(); } /**************************************************************************** Desc: Output a flag parameter. ****************************************************************************/ void F_CheckDbPage::outputFlagParam( CHECK_STATUS * pCheckStatus, FLMBOOL bHighlight, const char * pszParamName, const char * pszFieldName, FLMBOOL bFieldValue) { printTableRowStart( bHighlight); if (pCheckStatus->bCheckRunning) { printTableDataStart( TRUE, JUSTIFY_LEFT, 35); fnPrintf( m_pHRequest, "%s", pszParamName); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_LEFT, 65); fnPrintf( m_pHRequest, "%s", (char *)(bFieldValue ? "yes" : "no")); printTableDataEnd(); } else { printTableDataStart( TRUE, JUSTIFY_LEFT, 35); fnPrintf( m_pHRequest, "bHaveCheckStatus && bFieldValue) { fnPrintf( m_pHRequest, " checked"); } fnPrintf( m_pHRequest, " value=\"yes\"> %s\n", pszParamName); printTableDataEnd(); printTableDataStart( TRUE, JUSTIFY_LEFT, 65); fnPrintf( m_pHRequest, " "); printTableDataEnd(); } printTableRowEnd(); } /**************************************************************************** Desc: format a 64 bit number with commas. ****************************************************************************/ FSTATIC void format64Num( FLMUINT64 ui64Num, char * pszNum ) { FLMUINT uiNums [15]; FLMUINT uiNumNums = 0; FLMBOOL bFirstNum; // Format the number with commas. do { uiNums [uiNumNums++] = (FLMUINT)(ui64Num % (FLMUINT64)1000); ui64Num /= 1000; } while (ui64Num); bFirstNum = TRUE; while (uiNumNums) { uiNumNums--; if (bFirstNum) { f_sprintf( pszNum, "%u", (unsigned)uiNums [uiNumNums]); bFirstNum = FALSE; } else { f_sprintf( pszNum, ",%03u", (unsigned)uiNums [uiNumNums]); } while (*pszNum) { pszNum++; } } } /**************************************************************************** Desc: Output a flag parameter. ****************************************************************************/ void F_CheckDbPage::outputNum64Param( FLMBOOL bHighlight, const char * pszParamName, FLMUINT64 ui64Num) { char szNum [60]; printTableRowStart( bHighlight); printTableDataStart( TRUE, JUSTIFY_LEFT, 35); fnPrintf( m_pHRequest, "%s", pszParamName); printTableDataEnd(); // Format the number with commas format64Num( ui64Num, szNum); // Output the number printTableDataStart( TRUE, JUSTIFY_LEFT, 65); fnPrintf( m_pHRequest, "%s", szNum); printTableDataEnd(); printTableRowEnd(); } /**************************************************************************** Desc: Output the form for the user to run a check. ****************************************************************************/ void F_CheckDbPage::outputCheckForm( HFDB hDb, const char * pszDbKey, CHECK_STATUS * pCheckStatus, F_NameTable * pNameTable, FLMUINT uiCheckThreadId) { FLMBOOL bHighlight = FALSE; char szTmp [128]; char * pszTmp; char * pszName; IF_FileHdl * pFileHdl = NULL; fnPrintf( m_pHRequest, "
bCheckRunning) { fnPrintf( m_pHRequest, "?Running=%u&dbhandle=%s\">\n", (unsigned)uiCheckThreadId, pszDbKey); } else { if (hDb != HFDB_NULL) { fnPrintf( m_pHRequest, "?dbhandle=%s\">\n", pszDbKey); } else { fnPrintf( m_pHRequest, "\">\n"); } } printStartCenter(); if (pCheckStatus->bCheckRunning) { printTableStart( "CHECK PROGRESS", 2, 75); } else if (pCheckStatus->bHaveCheckStatus) { printTableStart( "CHECK RESULTS", 2, 75); } else { printTableStart( "CHECK PARAMETERS", 2, 74); } // Column headers printTableRowStart(); printColumnHeading( "Parameter", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 35); printColumnHeading( "Value", JUSTIFY_LEFT, NULL, 1, 1, TRUE, 65); printTableRowEnd(); if (hDb == HFDB_NULL) { // Output the database name outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Database Name", DATABASE_NAME_FIELD, F_PATH_MAX_SIZE + 1, pCheckStatus->pszDbName); // Output the data directory outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Data Directory", DATA_DIR_FIELD, F_PATH_MAX_SIZE + 1, pCheckStatus->pszDataDir); // Output the rfl directory outputStrParam( pCheckStatus, bHighlight = !bHighlight, "RFL Directory", RFL_DIR_FIELD, F_PATH_MAX_SIZE + 1, pCheckStatus->pszRflDir); } else { FDB * pDb = (FDB *)hDb; // Output the database name outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Database Name", NULL, 0, pDb->pFile->pszDbPath); outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Data Directory", NULL, 0, pDb->pFile->pszDataDir); } // Output the log file name outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Log File Name", LOG_FILE_NAME_FIELD, F_PATH_MAX_SIZE + 1, pCheckStatus->pszLogFileName); // Output the checking indexes flag. outputFlagParam( pCheckStatus, bHighlight = !bHighlight, "Check Indexes", CHECK_INDEXES_FIELD, pCheckStatus->bCheckingIndexes); // Output the repairing indexes flag. outputFlagParam( pCheckStatus, bHighlight = !bHighlight, "Repair Indexes", REPAIR_INDEXES_FIELD, pCheckStatus->bRepairingIndexes); #if 0 // Output the collecting detailed stats flag. outputFlagParam( pCheckStatus, bHighlight = !bHighlight, "Detailed Statistics", DETAILED_STATS_FIELD, pCheckStatus->bDetailedStatistics); #endif if (pCheckStatus->bHaveCheckStatus) { // Output what we are currently doing switch (pCheckStatus->Progress.iCheckPhase) { case CHECK_LFH_BLOCKS: pszTmp = (char *)"LFH BLOCKS"; break; case CHECK_B_TREE: pszTmp = &szTmp [0]; if (pCheckStatus->Progress.uiLfType == LF_INDEX) { if (pCheckStatus->Progress.bUniqueIndex) { f_strcpy( pszTmp, "UNIQUE INDEX: "); } else { f_strcpy( pszTmp, "INDEX: "); } } else { f_strcpy( pszTmp, "CONTAINER: "); } pszName = &pszTmp [f_strlen( pszTmp)]; if (!pNameTable || !pNameTable->getFromTagNum( pCheckStatus->Progress.uiLfNumber, NULL, pszName, sizeof( szTmp) - (pszName - &szTmp [0]))) { f_sprintf( pszName, "#%u", (unsigned)pCheckStatus->Progress.uiLfNumber); } else { f_sprintf( &pszTmp [f_strlen( pszTmp)], " (%u)", (unsigned)pCheckStatus->Progress.uiLfNumber); } break; case CHECK_AVAIL_BLOCKS: pszTmp = (char *)"AVAIL BLOCKS"; break; case CHECK_RS_SORT: pszTmp = (char *)"SORTING INDEX KEYS"; break; default: pszTmp = &szTmp [0]; f_sprintf( pszTmp, "UNKNOWN: %u", (unsigned)pCheckStatus->Progress.iCheckPhase); break; } outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Doing", NULL, 0, pszTmp); // Output various statistics as we go outputNum64Param( bHighlight = !bHighlight, "Database Size", pCheckStatus->Progress.ui64DatabaseSize); if (pCheckStatus->Progress.iCheckPhase == CHECK_RS_SORT) { FLMUINT uiPercent = 0; if (pCheckStatus->Progress.ui64NumRSUnits > (FLMUINT64)0) { uiPercent = (FLMUINT)((pCheckStatus->Progress.ui64NumRSUnitsDone * (FLMUINT64)100) / pCheckStatus->Progress.ui64NumRSUnits); } outputNum64Param( bHighlight = !bHighlight, "Percent Sorted", (FLMUINT64)uiPercent); } else { outputNum64Param( bHighlight = !bHighlight, "Bytes Checked", pCheckStatus->Progress.ui64BytesExamined); } outputNum64Param( bHighlight = !bHighlight, "Total Index Keys", pCheckStatus->Progress.ui64NumKeys); outputNum64Param( bHighlight = !bHighlight, "Num. Keys Checked", pCheckStatus->Progress.ui64NumKeysExamined); outputNum64Param( bHighlight = !bHighlight, "Invalid Index Keys", pCheckStatus->Progress.ui64NumKeysNotFound); outputNum64Param( bHighlight = !bHighlight, "Missing Index Keys", pCheckStatus->Progress.ui64NumRecKeysNotFound); outputNum64Param( bHighlight = !bHighlight, "Non-unique Index Keys", pCheckStatus->Progress.ui64NumNonUniqueKeys); outputNum64Param( bHighlight = !bHighlight, "Key Conflicts", pCheckStatus->Progress.ui64NumConflicts); outputNum64Param( bHighlight = !bHighlight, "Total Corruptions", (FLMUINT64)pCheckStatus->uiCorruptCount); outputNum64Param( bHighlight = !bHighlight, "Problems Repaired", (FLMUINT64)pCheckStatus->Progress.uiNumProblemsFixed); outputNum64Param( bHighlight = !bHighlight, "Old View Count", (FLMUINT64)pCheckStatus->uiOldViewCount); // Output the return status if the check is finished. if (!pCheckStatus->bCheckRunning) { if (pCheckStatus->CheckRc == FERR_OK) { pszTmp = (char *)"Database OK"; } else if (pCheckStatus->CheckRc == FERR_USER_ABORT) { pszTmp = (char *)"User Halted"; } else { pszTmp = &szTmp [0]; f_sprintf( pszTmp, "Error %04X, (%s)", (unsigned)pCheckStatus->CheckRc, FlmErrorString( pCheckStatus->CheckRc)); } outputStrParam( pCheckStatus, bHighlight = !bHighlight, "Check Status", NULL, 0, pszTmp); } } // End the table printTableEnd(); printEndCenter( FALSE); fnPrintf( m_pHRequest, "
\n"); // Output the setOperation function printSetOperationScript(); printStartCenter(); if (!pCheckStatus->bCheckRunning) { // If we are not running a check, add a Perform Check button printOperationButton( CHECK_FORM_NAME, "Perform Check", OPERATION_CHECK); } else { // If we are running a check, output a stop button. printOperationButton( CHECK_FORM_NAME, "Stop Check", OPERATION_STOP); } printEndCenter( TRUE); // Close the form fnPrintf( m_pHRequest, "
\n"); // If the check is done, and we have a log file, output it. if (!pCheckStatus->bCheckRunning && pCheckStatus->bHaveCheckStatus && pCheckStatus->uiCorruptCount && pCheckStatus->pszLogFileName) { fnPrintf( m_pHRequest, "

------LOG FILE CONTENTS------\n");

		// Open the log file

		if (RC_OK( gv_FlmSysData.pFileSystem->openFile( 
			pCheckStatus->pszLogFileName, FLM_IO_RDWR | FLM_IO_SH_DENYNONE,
			&pFileHdl)))
		{
			RCODE		rc;
			FLMUINT	uiBytesRead;

			// Read and output until we run out of data

			for (;;)
			{
				if (RC_BAD( rc = pFileHdl->read( FLM_IO_CURRENT_POS,
						sizeof( szTmp) - 1, szTmp, &uiBytesRead)))
				{
					if (rc != FERR_IO_END_OF_FILE || !uiBytesRead)
					{
						break;
					}
				}
				if (uiBytesRead)
				{
					szTmp [uiBytesRead] = 0;
					fnPrintf( m_pHRequest, "%s", szTmp);
				}
				if (uiBytesRead < sizeof( szTmp) - 1)
				{
					break;
				}
			}
			
			pFileHdl->Release();
			pFileHdl = NULL;
		}
		
		fnPrintf( m_pHRequest, "\n------END OF LOG FILE------\n");
		fnPrintf( m_pHRequest, "
\n"); } if( pFileHdl) { pFileHdl->Release(); } } /**************************************************************************** Desc: Copy one string into another - allocating memory if needed. ****************************************************************************/ FSTATIC RCODE copyStr( char ** ppszDestStr, const char * pszSrcStr) { RCODE rc = FERR_OK; FLMUINT uiLen; if (pszSrcStr && *pszSrcStr) { uiLen = f_strlen( pszSrcStr) + 1; if (RC_BAD( rc = f_alloc( uiLen, ppszDestStr))) { goto Exit; } f_memcpy( *ppszDestStr, pszSrcStr, uiLen); } else { *ppszDestStr = NULL; } Exit: return( rc); } /**************************************************************************** Desc: Copy the database names, etc. from one check status to another. ****************************************************************************/ FSTATIC void copyNames( CHECK_STATUS * pDestCheckStatus, CHECK_STATUS * pSrcCheckStatus) { (void)copyStr( &pDestCheckStatus->pszDbName, pSrcCheckStatus->pszDbName); (void)copyStr( &pDestCheckStatus->pszDataDir, pSrcCheckStatus->pszDataDir); (void)copyStr( &pDestCheckStatus->pszRflDir, pSrcCheckStatus->pszRflDir); (void)copyStr( &pDestCheckStatus->pszLogFileName, pSrcCheckStatus->pszLogFileName); } /**************************************************************************** Desc: Free a CHECK_STATUS structure and all associated memory. ****************************************************************************/ FSTATIC void freeCheckStatus( CHECK_STATUS * pCheckStatus, FLMBOOL bFreeStruct ) { f_free( &pCheckStatus->pszDbName); f_free( &pCheckStatus->pszDataDir); f_free( &pCheckStatus->pszRflDir); f_free( &pCheckStatus->pszLogFileName); if (bFreeStruct) { if (pCheckStatus->hDb != HFDB_NULL) { FlmDbClose( &pCheckStatus->hDb); } if (pCheckStatus->pLogFile) { pCheckStatus->pLogFile->Release(); pCheckStatus->pLogFile = NULL; } if (pCheckStatus->pNameTable) { pCheckStatus->pNameTable->Release(); pCheckStatus->pNameTable = NULL; } f_free( &pCheckStatus); } } /**************************************************************************** Desc: Run a database check. ****************************************************************************/ RCODE F_CheckDbPage::runCheck( F_Session * pFlmSession, HFDB * phDb, char * pszDbKey, const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszLogFileName, FLMBOOL bCheckingIndexes, FLMBOOL bRepairingIndexes, FLMBOOL bDetailedStatistics, FLMUINT * puiCheckThreadId) { RCODE rc = FERR_OK; CHECK_STATUS * pCheckStatus = NULL; IF_Thread * pThread; HFDB hDb = HFDB_NULL; FDB * pDb; if (*phDb == HFDB_NULL) { // Open the database if (RC_BAD( rc = FlmDbOpen( pszDbName, pszDataDir, pszRflDir, 0, NULL, phDb))) { goto Exit; } else { // Insert the handle into the session if (RC_BAD( rc = pFlmSession->addDbHandle( *phDb, pszDbKey))) { FlmDbClose( phDb); goto Exit; } } } else { pDb = (FDB *)(*phDb); pszDbName = pDb->pFile->pszDbPath; if ((pszDataDir = pDb->pFile->pszDataDir) != NULL) { if (!(*pszDataDir)) { pszDataDir = NULL; } } pszRflDir = NULL; } // Open the database for the thread - so it doesn't have // to worry about the handle going away. The thread will close the // new handle when it exits. if (RC_BAD( rc = flmOpenFile( ((FDB *)(*phDb))->pFile, NULL, NULL, NULL, 0, TRUE, NULL, NULL, (((FDB *)(*phDb))->pFile)->pszDbPassword, &pDb))) { goto Exit; } hDb = (HFDB)pDb; // Create an object to track the check. if (RC_BAD( rc = f_calloc( sizeof( CHECK_STATUS), &pCheckStatus))) { goto Exit; } pCheckStatus->hDb = hDb; // Set hDb to HFDB_NULL so it won't be closed below. hDb = HFDB_NULL; // Copy database names. if (RC_BAD( rc = copyStr( &pCheckStatus->pszDbName, pszDbName))) { goto Exit; } if (RC_BAD( rc = copyStr( &pCheckStatus->pszDataDir, pszDataDir))) { goto Exit; } if (RC_BAD( rc = copyStr( &pCheckStatus->pszRflDir, pszRflDir))) { goto Exit; } if (RC_BAD( rc = copyStr( &pCheckStatus->pszLogFileName, pszLogFileName))) { goto Exit; } // Create the log file, if one was specified. if (pCheckStatus->pszLogFileName) { gv_FlmSysData.pFileSystem->deleteFile( pCheckStatus->pszLogFileName); if (RC_BAD( gv_FlmSysData.pFileSystem->createFile( pCheckStatus->pszLogFileName, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pCheckStatus->pLogFile))) { f_free( &pCheckStatus->pszLogFileName); } } // Get a name table for the database - if we can. if ((pCheckStatus->pNameTable = f_new F_NameTable) != NULL) { if (RC_BAD( pCheckStatus->pNameTable->setupFromDb( hDb))) { pCheckStatus->pNameTable->Release(); pCheckStatus->pNameTable = NULL; } } pCheckStatus->bCheckingIndexes = bCheckingIndexes; pCheckStatus->bRepairingIndexes = bRepairingIndexes; pCheckStatus->bDetailedStatistics = bDetailedStatistics; pCheckStatus->bCheckRunning = TRUE; pCheckStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER(); // If browser does not check status at least every 15 seconds, we will // assume it has gone away and the thread will terminate itself. pCheckStatus->uiCheckTimeout = FLM_SECS_TO_TIMER_UNITS( 15); // Start a thread to do the check. if (RC_BAD( rc = f_threadCreate( &pThread, imonDoCheck, "IMON DB CHECK", gv_uiDbThrdGrp, 1, (void *)pCheckStatus, (void *)hDb))) { goto Exit; } *puiCheckThreadId = pThread->getThreadId(); // Set pCheckStatus to NULL so it won't be freed below. The thread // will free it when it stops. pCheckStatus = NULL; Exit: if (pThread) { pThread->Release(); } if (pCheckStatus) { freeCheckStatus( pCheckStatus, TRUE); } if (hDb != HFDB_NULL) { FlmDbClose( &hDb); } return( rc); } /**************************************************************************** Desc: Output the current thread status to the web page. ****************************************************************************/ void F_CheckDbPage::getCheckStatus( FLMUINT uiCheckThreadId, FLMBOOL bStopCheck, CHECK_STATUS * pCheckStatus) { FLMUINT uiThreadId; IF_Thread * pThread = NULL; CHECK_STATUS * pThreadCheckStatus; FLMBOOL bMutexLocked = FALSE; // pCheckStatus->bHaveCheckStatus should be set to FALSE by the caller. flmAssert( !pCheckStatus->bHaveCheckStatus); // See if the thread is still running. f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; uiThreadId = 0; for (;;) { if (RC_BAD( gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_uiDbThrdGrp, &uiThreadId))) { pCheckStatus->bCheckRunning = FALSE; goto Exit; } if (uiThreadId == uiCheckThreadId) { // If the app ID is zero, the thread is on its way out or already // out. Can no longer get thread status. if (!pThread->getThreadAppId()) { pCheckStatus->bCheckRunning = FALSE; goto Exit; } // Found thread, get its check status data pThreadCheckStatus = (CHECK_STATUS *)pThread->getParm1(); pThreadCheckStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER(); // Tell the thread to stop the check before telling it // to stop. This is so we can get partial results. if (bStopCheck) { pThreadCheckStatus->bStopCheck = TRUE; // Go into a while loop, waiting for the thread // to finish its check. while (pThreadCheckStatus->bCheckRunning) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; f_sleep( 200); f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; // If the thread app ID goes to zero, it has been // told to shut down, and has either already gone // away or is in the process of doing so, in which // case pThreadCheckStatus has either already been // deleted, or will be - so it is not safe to access // it any more! if (!pThread->getThreadAppId()) { pCheckStatus->bCheckRunning = FALSE; goto Exit; } } } break; } pThread->Release(); pThread = NULL; } // Mutex better still be locked at this point. flmAssert( bMutexLocked); // Note that we test pThreadCheckStatus->bCheckRunning BEFORE // doing the memcpy. This is because puiDrnList is not guaranteed // to be set until bCheckRunning is FALSE. If bCheckRunning is TRUE, // we will NULL out whatever got copied into puiDrnList. if (!pThreadCheckStatus->bCheckRunning) { f_memcpy( pCheckStatus, pThreadCheckStatus, sizeof( CHECK_STATUS)); copyNames( pCheckStatus, pThreadCheckStatus); // Need to unlock the mutex so that the thread can stop. f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; pThread->stopThread(); } else { f_memcpy( pCheckStatus, pThreadCheckStatus, sizeof( CHECK_STATUS)); copyNames( pCheckStatus, pThreadCheckStatus); // Set bCheckRunning to TRUE. This takes care of a race // race condition of pThreadCheckStatus->bCheckRunning getting // set to FALSE by the check thread after we test it above. // we make the test on pThreadCheckStatus->bCheckRunning. We will // simply get that fact next time we get status. pCheckStatus->bCheckRunning = TRUE; } // NULL out certain members so we won't attempt to use them. They // may go away if the background thread has gone away. pCheckStatus->hDb = HFDB_NULL; pCheckStatus->pLogFile = NULL; pCheckStatus->pNameTable = NULL; pCheckStatus->bHaveCheckStatus = TRUE; Exit: if (bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if (pThread) { pThread->Release(); } } /******************************************************************** Desc: Log a string to the log file. *********************************************************************/ FSTATIC void imonLogStr( IF_FileHdl * pLogFile, FLMUINT uiIndent, const char * pszStr) { char szBuffer [100]; FLMUINT uiLoop; FLMUINT uiBytesWritten; if ((uiLoop = uiIndent) != 0) { f_memset( szBuffer, ' ', uiIndent); uiLoop = uiIndent; } if (pszStr) { while (*pszStr) { if (uiLoop == sizeof( szBuffer)) { pLogFile->write( FLM_IO_CURRENT_POS, uiLoop, szBuffer, &uiBytesWritten); uiLoop = 0; } szBuffer [uiLoop++] = *pszStr; pszStr++; } } if (uiLoop >= sizeof( szBuffer) - 2) { pLogFile->write( FLM_IO_CURRENT_POS, uiLoop, szBuffer, &uiBytesWritten); uiLoop = 0; } szBuffer [uiLoop++] = '\r'; szBuffer [uiLoop++] = '\n'; pLogFile->write( FLM_IO_CURRENT_POS, uiLoop, szBuffer, &uiBytesWritten); } /*************************************************************************** Desc: Log a field's data. *****************************************************************************/ FSTATIC void imonLogField( IF_FileHdl * pLogFile, F_NameTable * pNameTable, FlmRecord * pRecord, void * pvField, FLMUINT uiStartCol, FLMUINT uiLevelOffset) { char szTmpBuf [200]; char * pszTmp; FLMUINT uiFieldNum; FLMUINT uiLen; FLMUINT uiBinLen; FLMUINT uiTmpLen; FLMBYTE * pucTmp; FLMBYTE ucTmpBin [80]; FLMUINT uiNum; FLMUINT uiLevel = pRecord->getLevel( pvField) + uiLevelOffset; FLMUINT uiIndent = (uiLevel * 2) + uiStartCol; // Insert leading spaces to indent for level if (uiIndent) { f_memset(szTmpBuf, ' ', uiIndent); } // Output level and tag f_sprintf( &szTmpBuf [uiIndent], "%u ", (unsigned)uiLevel); pszTmp = &szTmpBuf [f_strlen( szTmpBuf)]; uiFieldNum = pRecord->getFieldID( pvField); if (!pNameTable || !pNameTable->getFromTagNum( uiFieldNum, NULL, pszTmp, sizeof( szTmpBuf) - (pszTmp - &szTmpBuf [0]))) { f_sprintf( pszTmp, "#%u", (unsigned)uiFieldNum); } // Output what will fit of the value on the rest of the line uiLen = f_strlen( szTmpBuf); szTmpBuf [uiLen++] = ' '; szTmpBuf [uiLen] = 0; if (!pRecord->getDataLength( pvField)) { goto Exit; } switch (pRecord->getDataType( pvField)) { case FLM_TEXT_TYPE: pszTmp = &szTmpBuf [uiLen]; uiLen = 80 - uiLen; pRecord->getNative( pvField, pszTmp, &uiLen); break; case FLM_NUMBER_TYPE: pRecord->getUINT( pvField, &uiNum); f_sprintf( &szTmpBuf [uiLen], "%u", (unsigned)uiNum); break; case FLM_BINARY_TYPE: pRecord->getBinaryLength( pvField, &uiBinLen); uiTmpLen = sizeof( ucTmpBin); pRecord->getBinary( pvField, ucTmpBin, &uiTmpLen); pucTmp = &ucTmpBin [0]; while (uiBinLen && uiLen < 77) { f_sprintf( &szTmpBuf [uiLen], "%02X ", (unsigned)*pucTmp); uiBinLen--; pucTmp++; uiLen += 3; } szTmpBuf [uiLen - 1] = 0; break; case FLM_CONTEXT_TYPE: pRecord->getUINT( pvField, &uiNum); f_sprintf( &szTmpBuf[ uiLen], "@%u@", (unsigned)uiNum); break; } Exit: imonLogStr( pLogFile, 0, szTmpBuf); } /******************************************************************** Desc: Log an index key corruption error. *********************************************************************/ FSTATIC void imonLogKeyError( IF_FileHdl * pLogFile, F_NameTable * pNameTable, CORRUPT_INFO * pCorrupt) { FLMUINT uiLogItem; FlmRecord * pRecord = NULL; void * pvField; REC_KEY * pTempKeyList = NULL; FLMUINT uiIndent; FLMUINT uiLevelOffset; char szNameBuf [128]; char szTmpBuf [128]; if (!pNameTable || !pNameTable->getFromTagNum( pCorrupt->uiErrLfNumber, NULL, szNameBuf, sizeof( szNameBuf))) { f_sprintf( (char *)szNameBuf, "#%u", (unsigned)pCorrupt->uiErrLfNumber); } imonLogStr( pLogFile, 0, NULL); imonLogStr( pLogFile, 0, NULL); f_sprintf( szTmpBuf, "ERROR IN INDEX: %s", szNameBuf); imonLogStr( pLogFile, 0, szTmpBuf); uiLogItem = 'R'; uiLevelOffset = 0; for (;;) { uiIndent = 2; if (uiLogItem == 'K') { if ((pRecord = pCorrupt->pErrIxKey) == NULL) { uiLogItem = 'L'; continue; } imonLogStr( pLogFile, 0, NULL); imonLogStr( pLogFile, 0, " PROBLEM KEY"); } else if (uiLogItem == 'R') { if ((pRecord = pCorrupt->pErrRecord) == NULL) { uiLogItem = 'K'; continue; } imonLogStr( pLogFile, 0, NULL); imonLogStr( pLogFile, 0, " RECORD"); } else if (uiLogItem == 'L') { if ((pTempKeyList = pCorrupt->pErrRecordKeyList) == NULL) { break; } pRecord = pTempKeyList->pKey; imonLogStr( pLogFile, 0, NULL); imonLogStr( pLogFile, 0, " RECORD KEYS"); imonLogStr( pLogFile, 0, " 0 Key"); uiLevelOffset = 1; } for (pvField = pRecord->root();;) { if (!pvField) { if (uiLogItem != 'L') { break; } if ((pTempKeyList = pTempKeyList->pNextKey) == NULL) { break; } pRecord = pTempKeyList->pKey; pvField = pRecord->root(); imonLogStr( pLogFile, 0, " 0 Key"); continue; } else { imonLogField( pLogFile, pNameTable, pRecord, pvField, uiIndent, uiLevelOffset); } pvField = pRecord->next( pvField); } if (uiLogItem == 'L') { break; } else if (uiLogItem == 'R') { uiLogItem = 'K'; } else { uiLogItem = 'L'; } } } /******************************************************************** Desc: Log corruptions to log file. *********************************************************************/ FSTATIC void imonLogCorruptError( IF_FileHdl * pLogFile, F_NameTable * pNameTable, CORRUPT_INFO * pCorrupt) { char szWhat [20]; char szTmpBuf [100]; switch (pCorrupt->eErrLocale) { case LOCALE_LFH_LIST: imonLogStr( pLogFile, 0, "ERROR IN LFH LINKED LIST:"); break; case LOCALE_AVAIL_LIST: imonLogStr( pLogFile, 0, "ERROR IN AVAIL LINKED LIST:"); break; case LOCALE_B_TREE: if (pCorrupt->eCorruption == FLM_OLD_VIEW) { imonLogStr( pLogFile, 0, "OLD VIEW"); } else { if (pCorrupt->uiErrFieldNum) { f_strcpy( szWhat, "FIELD"); } else if (pCorrupt->uiErrElmOffset) { f_strcpy( szWhat, "ELEMENT"); } else if (pCorrupt->uiErrBlkAddress) { f_strcpy( szWhat, "BLOCK"); } else { f_strcpy( szWhat, "LAST BLOCK"); } f_sprintf( szTmpBuf, "BAD %s", szWhat); imonLogStr( pLogFile, 0, szTmpBuf); } // Log the logical file number, name, and type f_sprintf( szTmpBuf, "Logical File Number: %u", (unsigned)pCorrupt->uiErrLfNumber); imonLogStr( pLogFile, 2, szTmpBuf); switch( pCorrupt->uiErrLfType) { case LF_CONTAINER: f_strcpy( szWhat, "Container"); break; case LF_INDEX: f_strcpy( szWhat, "Index"); break; default: f_sprintf( (char *)szWhat, "?%u", (unsigned)pCorrupt->uiErrLfType); break; } f_sprintf( szTmpBuf, "Logical File Type: %s", szWhat); imonLogStr( pLogFile, 2, szTmpBuf); // Log the level in the B-Tree, if known if (pCorrupt->uiErrBTreeLevel != 0xFF) { f_sprintf( szTmpBuf, "Level in B-Tree: %u", (unsigned)pCorrupt->uiErrBTreeLevel); imonLogStr( pLogFile, 2, szTmpBuf); } break; case LOCALE_IXD_TBL: f_sprintf( szTmpBuf, "ERROR IN IXD TABLE, Index Number: %u", (unsigned)pCorrupt->uiErrLfNumber); imonLogStr( pLogFile, 0, szTmpBuf); break; case LOCALE_INDEX: f_strcpy( szWhat, "Index"); imonLogKeyError( pLogFile, pNameTable, pCorrupt); break; default: pCorrupt->eErrLocale = LOCALE_NONE; break; } // Log the block address, if known if (pCorrupt->uiErrBlkAddress) { f_sprintf( szTmpBuf, "Block Address: 0x%08X (%u)", (unsigned)pCorrupt->uiErrBlkAddress, (unsigned)pCorrupt->uiErrBlkAddress); imonLogStr( pLogFile, 2, szTmpBuf); } // Log the parent block address, if known if (pCorrupt->uiErrParentBlkAddress) { if (pCorrupt->uiErrParentBlkAddress != 0xFFFFFFFF) { f_sprintf( szTmpBuf, "Parent Block Address: 0x%08X (%u)", (unsigned)pCorrupt->uiErrParentBlkAddress, (unsigned)pCorrupt->uiErrParentBlkAddress); } else { f_sprintf( szTmpBuf, "Parent Block Address: NONE, Root Block"); } imonLogStr( pLogFile, 2, szTmpBuf); } // Log the element offset, if known if (pCorrupt->uiErrElmOffset) { f_sprintf( szTmpBuf, "Element Offset: %u", (unsigned)pCorrupt->uiErrElmOffset); imonLogStr( pLogFile, 2, szTmpBuf); } // Log the record number, if known if (pCorrupt->uiErrDrn) { f_sprintf( szTmpBuf, "Record Number: %u", (unsigned)pCorrupt->uiErrDrn); imonLogStr( pLogFile, 2, szTmpBuf); } // Log the offset within the element record, if known if (pCorrupt->uiErrElmRecOffset != 0xFFFF) { f_sprintf( szTmpBuf, "Offset Within Element: %u", (unsigned)pCorrupt->uiErrElmRecOffset); imonLogStr( pLogFile, 2, szTmpBuf); } // Log the field number, if known if (pCorrupt->uiErrFieldNum) { f_sprintf( szTmpBuf, "Field Number: %u", (unsigned)pCorrupt->uiErrFieldNum); imonLogStr( pLogFile, 2, szTmpBuf); } f_strcpy( szTmpBuf, FlmVerifyErrToStr( pCorrupt->eCorruption)); f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], " (%d)", (int)pCorrupt->eCorruption); imonLogStr( pLogFile, 2, szTmpBuf); imonLogStr( pLogFile, 0, NULL); pLogFile->flush(); } /*************************************************************************** Desc: Check status callback. ***************************************************************************/ FSTATIC RCODE CheckStatusCB( eStatusType eStatus, void * pvParm1, void * pvParm2, void * pvAppData) { RCODE rc = FERR_OK; FLMUINT uiCurrTime; CORRUPT_INFO * pCorrupt; CHECK_STATUS * pCheckStatus = (CHECK_STATUS *)pvAppData; uiCurrTime = FLM_GET_TIMER(); if (pCheckStatus->bStopCheck) { rc = RC_SET( FERR_USER_ABORT); goto Exit; } else if (FLM_ELAPSED_TIME( uiCurrTime, pCheckStatus->uiLastTimeBrowserChecked) >= pCheckStatus->uiCheckTimeout) { rc = RC_SET( FERR_TIMEOUT); goto Exit; } // Handle each of the status types if (eStatus == FLM_PROBLEM_STATUS) { FLMBOOL * pbFixCorruptions = (FLMBOOL *)pvParm2; pCorrupt = (CORRUPT_INFO *)pvParm1; if (pCheckStatus->pLogFile && pCorrupt->eCorruption != FLM_OLD_VIEW) { imonLogCorruptError( pCheckStatus->pLogFile, pCheckStatus->pNameTable, pCorrupt); } if (pCorrupt->eCorruption == FLM_OLD_VIEW) { pCheckStatus->uiOldViewCount++; } else { pCheckStatus->uiCorruptCount++; } if (pbFixCorruptions) { *pbFixCorruptions = pCheckStatus->bRepairingIndexes; } } else if (eStatus == FLM_CHECK_STATUS) { // Capture the progress information. f_memcpy( &pCheckStatus->Progress, pvParm1, sizeof( DB_CHECK_PROGRESS)); // Update thread status if (FLM_ELAPSED_TIME( uiCurrTime, pCheckStatus->uiLastTimeSetStatus) >= pCheckStatus->uiUpdateStatusInterval) { if (pCheckStatus->Progress.iCheckPhase == CHECK_RS_SORT) { FLMUINT uiPercent = 0; if (pCheckStatus->Progress.ui64NumRSUnits > (FLMUINT64)0) { uiPercent = (FLMUINT)((pCheckStatus->Progress.ui64NumRSUnitsDone * (FLMUINT64)100) / pCheckStatus->Progress.ui64NumRSUnits); } pCheckStatus->pThread->setThreadStatus( "Sorting, %u percent done", (unsigned)uiPercent); } else { char szFileSize [60]; char szBytesDone [60]; format64Num( pCheckStatus->Progress.ui64DatabaseSize, szFileSize); format64Num( pCheckStatus->Progress.ui64BytesExamined, szBytesDone); pCheckStatus->pThread->setThreadStatus( "%s of %s bytes checked", szBytesDone, szFileSize); } pCheckStatus->uiLastTimeSetStatus = uiCurrTime; } } Exit: return( rc); } /**************************************************************************** Desc: Thread to perform a database check for a web page. ****************************************************************************/ FSTATIC RCODE FLMAPI imonDoCheck( IF_Thread * pThread) { RCODE rc; CHECK_STATUS * pCheckStatus = (CHECK_STATUS *)pThread->getParm1(); FLMUINT uiFlags; F_Pool pool; DB_CHECK_PROGRESS CheckProgress; FLMUINT uiCurrTime; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); pCheckStatus->pThread = pThread; pCheckStatus->uiUpdateStatusInterval = FLM_SECS_TO_TIMER_UNITS( 5); uiFlags = FLM_CHK_FIELDS; if (pCheckStatus->bCheckingIndexes) { uiFlags |= FLM_CHK_INDEX_REFERENCING; } pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); pool.poolInit( 512); rc = FlmDbCheck( pCheckStatus->hDb, NULL, NULL, NULL, uiFlags, &pool, &CheckProgress, CheckStatusCB, pCheckStatus); pool.poolFree(); // Close the database and log file before doing anything else. FlmDbClose( &pCheckStatus->hDb); if (pCheckStatus->pLogFile) { pCheckStatus->pLogFile->Release(); pCheckStatus->pLogFile = NULL; } pCheckStatus->CheckRc = rc; pCheckStatus->bCheckRunning = FALSE; if (RC_BAD( rc)) { if (rc == FERR_USER_ABORT) { // Callback forced us to quit. pThread->setThreadStatus( "User halted"); } else if (rc == FERR_TIMEOUT) { pThread->setThreadStatus( "Timed out"); goto Exit; } else { pThread->setThreadStatus( "Check Error %04X,", (unsigned)rc); } } // Wait for the user to tell us to quit. for (;;) { // See if we should shut down. if (pThread->getShutdownFlag()) { // Transaction will be aborted below pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); goto Exit; } // See if we timed out uiCurrTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrTime, pCheckStatus->uiLastTimeBrowserChecked) >= pCheckStatus->uiCheckTimeout) { goto Exit; } // Pause one second pThread->sleep( 1000); } Exit: // Set the thread's app ID to 0, so that it will not // be found now that the thread is terminating (we don't // want getCheckStatus() to find the thread). pThread->setThreadAppId( 0); // Free the check status. Must do inside mutex lock so // that it doesn't go away after getCheckStatus finds the // thread. f_mutexLock( gv_FlmSysData.hShareMutex); freeCheckStatus( pCheckStatus, TRUE); f_mutexUnlock( gv_FlmSysData.hShareMutex); return( FERR_OK); } libflaim-4.9.966/src/lock.cpp0000644000175000017500000002115210510774540017311 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Database locking and unlocking. // Tabs: 3 // // Copyright (c) 1991,1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: lock.cpp 12315 2006-01-19 15:16:37 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /**************************************************************************** Desc: Obtains a a lock on the database. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbLock( HFDB hDb, eLockType lockType, FLMINT iPriority, FLMUINT uiTimeout) { RCODE rc = FERR_OK; FLMBOOL bIgnore; FDB * pDb = (FDB *)hDb; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_LOCK))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, (FLMUINT)lockType))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_SIGNED_NUMBER, 0, iPriority))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, uiTimeout))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } // lockType better be exclusive or shared if ((lockType != FLM_LOCK_EXCLUSIVE) && (lockType != FLM_LOCK_SHARED)) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Nesting of locks is not allowed - this test also keeps this call from // being executed inside an update transaction that implicitly acquired // the lock. if (pDb->uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED | FDB_FILE_LOCK_IMPLICIT)) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Attempt to acquire the lock. if (RC_BAD( rc = pDb->pFile->pFileLockObj->lock( pDb->hWaitSem, (FLMBOOL)((lockType == FLM_LOCK_EXCLUSIVE) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE), uiTimeout, iPriority, pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL))) { goto Exit; } pDb->uiFlags |= FDB_HAS_FILE_LOCK; if (lockType == FLM_LOCK_SHARED) { pDb->uiFlags |= FDB_FILE_LOCK_SHARED; } Exit: flmExit( FLM_DB_LOCK, pDb, rc); return( rc); } /**************************************************************************** Desc: Releases a lock on the database ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbUnlock( HFDB hDb) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bIgnore; if (IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_DATABASE, FCS_OP_DATABASE_UNLOCK))) { goto Exit; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK | FDB_CLOSING_OK, 0, &bIgnore))) { goto Exit; } // If we don't have an explicit lock, can't do the unlock. It is // also illegal to do the unlock during an update transaction. if (!(pDb->uiFlags & FDB_HAS_FILE_LOCK) || (pDb->uiFlags & FDB_FILE_LOCK_IMPLICIT) || (pDb->uiTransType == FLM_UPDATE_TRANS)) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Unlock the file. if (RC_BAD( rc = pDb->pFile->pFileLockObj->unlock())) { goto Exit; } // Unset the flags that indicated the file was explicitly locked. pDb->uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED)); Exit: if( RC_OK( rc)) { rc = flmCheckDatabaseState( pDb); } flmExit( FLM_DB_UNLOCK, pDb, rc); return( rc); } /**************************************************************************** Desc : Returns information about the lock held by the specified database handle. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbGetLockType( HFDB hDb, eLockType * pLockType, FLMBOOL * pbImplicit) { RCODE rc = FERR_OK; FDB * pDb = NULL; FLMBOOL bIgnore; if( pLockType) { *pLockType = FLM_LOCK_NONE; } if( pbImplicit) { *pbImplicit = FALSE; } if (IsInCSMode( hDb)) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } pDb = (FDB *)hDb; if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, FDB_TRANS_GOING_OK, 0, &bIgnore))) { goto Exit; } if( pDb->uiFlags & FDB_HAS_FILE_LOCK) { if( pLockType) { if( pDb->uiFlags & FDB_FILE_LOCK_SHARED) { *pLockType = FLM_LOCK_SHARED; } else { *pLockType = FLM_LOCK_EXCLUSIVE; } } if( pbImplicit) { *pbImplicit = (pDb->uiFlags & FDB_FILE_LOCK_IMPLICIT) ? TRUE : FALSE; } } Exit: flmExit( FLM_DB_GET_LOCK_TYPE, pDb, rc); return( rc); } /**************************************************************************** Desc: This routine locks a database for exclusive access. ****************************************************************************/ RCODE dbLock( FDB * pDb, FLMUINT uiMaxLockWait) { RCODE rc = FERR_OK; FLMBOOL bGotFileLock = FALSE; FFILE * pFile = pDb->pFile; // There must NOT be a shared lock on the file. if (pDb->uiFlags & FDB_FILE_LOCK_SHARED) { rc = RC_SET( FERR_PERMISSION); goto Exit; } // Must acquire an exclusive file lock first, if it hasn't been // acquired. if (!(pDb->uiFlags & FDB_HAS_FILE_LOCK)) { if (RC_BAD( rc = pFile->pFileLockObj->lock( pDb->hWaitSem, TRUE, uiMaxLockWait, 0, pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL))) { goto Exit; } bGotFileLock = TRUE; pDb->uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } if (RC_BAD( rc = pFile->pWriteLockObj->lock( pDb->hWaitSem, TRUE, uiMaxLockWait, 0, pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL))) { goto Exit; } pDb->uiFlags |= FDB_HAS_WRITE_LOCK; Exit: if (rc == FERR_IO_FILE_LOCK_ERR) { if (bGotFileLock) { (void)pFile->pFileLockObj->unlock(); pDb->uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); } if (pDb->uiTransType != FLM_NO_TRANS) { // Unlink the DB from the transaction. (void)flmUnlinkDbFromTrans( pDb, FALSE); } } else if (RC_BAD( rc)) { if (bGotFileLock) { (void)pFile->pFileLockObj->unlock(); pDb->uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); } } return( rc); } /**************************************************************************** Desc: This routine unlocks a database that was previously locked using the dbLock routine. ****************************************************************************/ RCODE dbUnlock( FDB * pDb) { RCODE rc = FERR_OK; // If we have the write lock, unlock it first. flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK); pDb->pFile->pWriteLockObj->unlock(); pDb->uiFlags &= ~FDB_HAS_WRITE_LOCK; // Give up the file lock, if it was acquired implicitly. if (pDb->uiFlags & FDB_FILE_LOCK_IMPLICIT) { if (RC_OK( rc = pDb->pFile->pFileLockObj->unlock())) { pDb->uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT)); } } return( rc); } libflaim-4.9.966/src/fqopt.cpp0000644000175000017500000020775610510774540017532 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query optimization // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqopt.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FLMBOOL gv_DoValAndDictTypesMatch[ LAST_VALUE][ FLM_CONTEXT_TYPE + 1] = { // Dictionary Types (node types - FLM_XXXX_TYPE) // TEXT NUMBER BINARY CONTEXT //qTypes (NO_TYPE=0) /*BOOL=1*/ {FALSE, TRUE, FALSE, FALSE}, /*UINT32*/ {FALSE, TRUE, FALSE, TRUE}, /*INT32*/ {FALSE, TRUE, FALSE, TRUE}, /*REAL*/ {FALSE, FALSE, FALSE, FALSE}, /*REC_PTR*/ {FALSE, TRUE, FALSE, TRUE}, /*UINT64*/ {FALSE, TRUE, FALSE, TRUE}, /*INT64*/ {FALSE, TRUE, FALSE, TRUE}, /*NOTUSED*/ {FALSE, FALSE, FALSE, FALSE}, /*BINARY*/ {FALSE, FALSE, TRUE, FALSE}, /*STRING*/ {FALSE, FALSE, FALSE, FALSE}, /*UNICODE*/ {FALSE, FALSE, FALSE, FALSE}, /*TEXT*/ {TRUE, FALSE, FALSE, FALSE}, }; #define RANK_EQ 0 #define RANK_NE 1 #define RANK_MATCH 2 #define RANK_NOT_MATCH 3 #define RANK_MATCH_BEGIN 4 #define RANK_NOT_MATCH_BEGIN 5 #define RANK_MATCH_END 6 #define RANK_NOT_MATCH_END 7 #define RANK_CONTAINS 8 #define RANK_NOT_CONTAINS 9 #define RANK_COMPARE 10 #define RANK_EXISTS 11 #define RANK_NOT_EXISTS 12 #define RANK_OTHER 13 #define NUM_RANK_OPS 14 #define RANK_IFD_SUBSTRING 0 #define RANK_IFD_VALUE 1 #define RANK_IFD_CONTEXT 2 #define NUM_IFD_RANKS 3 FLMUINT gv_uiRanks [NUM_IFD_RANKS][ NUM_RANK_OPS] = { // EQ NE MTCH !MTCH MTCHB !MTCHB MTCHE !MTCHE CONT !CONT CMP EXIST !EXIST OTHER /*SS*/ { 3, 16, 4, 16, 6, 16, 7, 16, 8, 16, 10, 15, 16, 100}, /*VAL*/ { 1, 16, 2, 16, 5, 16, 14, 16, 13, 16, 9, 12, 16, 100}, /*CTX*/ { 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 11, 17, 100}, }; /* Desc: Checks the from (value) and until (dict) values in a field info structure to verify that they match the field type. */ FINLINE FLMBOOL DoValAndDictTypesMatch( QTYPES eValType, FLMUINT uiDictType) { if (uiDictType > FLM_CONTEXT_TYPE) { return( FALSE); } else { // subtract 1 from QTYPES - array doesn't have space for the NO_TYPE enum return gv_DoValAndDictTypesMatch[ ((int)eValType) - FIRST_VALUE][ uiDictType]; } } /* Desc: Clips a SUBQUERY from a list, and frees memory associated with it. */ FINLINE void flmClipSubQuery( CURSOR * pCursor, SUBQUERY * pSubQuery ) { if( pSubQuery == pCursor->pSubQueryList) { pCursor->pSubQueryList = pSubQuery->pNext; } if( pSubQuery->pPrev) { pSubQuery->pPrev->pNext = pSubQuery->pNext; } if( pSubQuery->pNext) { pSubQuery->pNext->pPrev = pSubQuery->pPrev; } flmSQFree( pSubQuery, TRUE); } FSTATIC RCODE flmAllocIndexInfo( F_Pool * pPool, QINDEX ** ppIndex, QINDEX ** ppIndexList, IXD * pIxd); FSTATIC RCODE flmSQGetDrnRanges( SUBQUERY * pSubQuery, QTYPES eOperator, FLMUINT uiVal); FSTATIC RCODE flmSQGenPredicateList( FDB * pDb, SUBQUERY * pSubQuery, F_Pool * pPool, QPREDICATE * * ppPredicateList, FLMUINT * puiTotalPredicates, FLMBOOL * pbHaveUserPredicates); FSTATIC FLMUINT flmCurCalcPredicateRank( QPREDICATE * pPredicate, IFD * pIfd); FSTATIC void flmCurLinkPredicate( QINDEX * pIndex, QFIELD_PREDICATE * pPredToLink, QFIELD_PREDICATE ** ppPredicateList); FSTATIC FLMBOOL flmIxFldPathSuitable( FLMUINT * puiIxFldPath, FLMUINT * puiQueryFldPath, FLMBOOL * pbMustVerifyQueryPath); FSTATIC RCODE flmSQGetSuitableIndexes( FDB * pDb, FLMUINT uiForceIndex, CURSOR * pCursor, SUBQUERY * pSubQuery, FLMUINT uiContainer, F_Pool * pPool, QPREDICATE * pPredicateList, FLMUINT uiTotalPredicates, FLMBOOL bHaveUserPredicates, QINDEX * * ppIndexList, FLMUINT * puiMaxIfds); FSTATIC RCODE flmSQEvaluateCurrIndexKey( FDB * pDb, SUBQUERY * pSubQuery, FSIndexCursor ** ppTmpFSIndexCursor, QINDEX * pIndex, QFIELD_PREDICATE ** ppFieldCurrPredicate, QPREDICATE ** ppPredicateList); FSTATIC RCODE flmCheckUserPredicateCosts( FDB * pDb, SUBQUERY * pSubQuery, FLMBOOL bOkToOptimizeWithPredicate); FSTATIC RCODE flmMergeSubQueries( CURSOR * pCursor, SUBQUERY * * ppFromSubQuery, SUBQUERY * pIntoSubQuery, FLMBOOL bFromSubQuerySubsumed); FSTATIC RCODE flmSQSetupFullContainerScan( CURSOR * pCursor, SUBQUERY * pSubQuery); FSTATIC RCODE flmSQChooseBestIndex( CURSOR * pCursor, FDB * pDb, FLMUINT uiForceIndex, FLMUINT bForceFirstToLastKey, SUBQUERY * pSubQuery, F_Pool * pTempPool, QPREDICATE * pPredicateList, FLMUINT uiTotalPredicates, FLMBOOL bHaveUserPredicates); /**************************************************************************** Desc: Keep track of DRN ranges for a subquery. ****************************************************************************/ FSTATIC RCODE flmSQGetDrnRanges( SUBQUERY * pSubQuery, QTYPES eOperator, FLMUINT uiVal ) { RCODE rc = FERR_OK; switch (eOperator) { case FLM_EQ_OP: if ((!uiVal) || (pSubQuery->uiLowDrn > uiVal) || (pSubQuery->uiHighDrn < uiVal) || (pSubQuery->uiNotEqualDrn == uiVal)) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } else { pSubQuery->uiLowDrn = pSubQuery->uiHighDrn = uiVal; } break; case FLM_GT_OP: if (pSubQuery->uiHighDrn <= uiVal || uiVal == 0xFFFFFFFF) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } if (pSubQuery->uiLowDrn < uiVal + 1) { pSubQuery->uiLowDrn = uiVal + 1; if ((pSubQuery->uiLowDrn == pSubQuery->uiHighDrn) && (pSubQuery->uiNotEqualDrn == pSubQuery->uiLowDrn)) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } } break; case FLM_GE_OP: if (pSubQuery->uiHighDrn < uiVal) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } if (pSubQuery->uiLowDrn < uiVal) { pSubQuery->uiLowDrn = uiVal; if ((pSubQuery->uiLowDrn == pSubQuery->uiHighDrn) && (pSubQuery->uiNotEqualDrn == pSubQuery->uiLowDrn)) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } } break; case FLM_LT_OP: if (pSubQuery->uiLowDrn >= uiVal || !uiVal) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } if (pSubQuery->uiHighDrn > uiVal - 1) { pSubQuery->uiHighDrn = uiVal - 1; if ((pSubQuery->uiLowDrn == pSubQuery->uiHighDrn) && (pSubQuery->uiNotEqualDrn == pSubQuery->uiLowDrn)) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } } break; case FLM_LE_OP: if (pSubQuery->uiLowDrn > uiVal) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } if (pSubQuery->uiHighDrn > uiVal) { pSubQuery->uiHighDrn = uiVal; if ((pSubQuery->uiLowDrn == pSubQuery->uiHighDrn) && (pSubQuery->uiNotEqualDrn == pSubQuery->uiLowDrn)) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } } break; case FLM_NE_OP: if (pSubQuery->uiLowDrn == uiVal && pSubQuery->uiHighDrn == uiVal) { rc = RC_SET( FERR_EMPTY_QUERY); goto Exit; } pSubQuery->uiNotEqualDrn = uiVal; break; default: // Other operators are not allowed for DRN queries. flmAssert( 0); rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Generate the predicate list for a sub-query. ****************************************************************************/ FSTATIC RCODE flmSQGenPredicateList( FDB * pDb, SUBQUERY * pSubQuery, F_Pool * pPool, QPREDICATE * * ppPredicateList, FLMUINT * puiTotalPredicates, FLMBOOL * pbHaveUserPredicates) { RCODE rc = FERR_OK; FQNODE * pQNode; QTYPES eOp; QPREDICATE * pPredicate; QPREDICATE * pLastPredicate; QTYPES eParentOp; FQNODE * pSibling; QTYPES eSibOp; FLMUINT uiFldId; FLMUINT uiDictType; *ppPredicateList = NULL; *puiTotalPredicates = 0; *pbHaveUserPredicates = FALSE; // Nothing to do in an empty tree. if ((pQNode = pSubQuery->pTree) == NULL) { goto Exit; } // Traverse through all of the nodes of the tree - non-recursively. pLastPredicate = NULL; for (;;) { eOp = GET_QNODE_TYPE( pQNode); if (IS_FIELD( eOp)) { // Create a predicate for this field. // Better be a parent node that is the operator for // this field. if( RC_BAD( rc = pPool->poolCalloc( sizeof( QPREDICATE), (void **)&pPredicate))) { goto Exit; } (*puiTotalPredicates)++; pPredicate->puiFldPath = pQNode->pQAtom->val.QueryFld.puiFldPath; if (pQNode->pQAtom->uiFlags & FLM_SINGLE_VALUED) { pPredicate->bFldSingleValued = TRUE; } uiFldId = *pPredicate->puiFldPath; // Make sure the field is in the dictionary if it is below the // reserved range. if (uiFldId <= FLM_RESERVED_TAG_NUMS) { if (RC_BAD( rc = fdictGetField( pDb->pDict, uiFldId, &uiDictType, NULL, NULL))) { rc = RC_SET( FERR_BAD_FIELD_NUM); pDb->Diag.uiInfoFlags |= FLM_DIAG_FIELD_NUM; pDb->Diag.uiFieldNum = uiFldId; goto Exit; } } // If there is no parent node, this is an existence operator. if (!pQNode->pParent) { flmAssert( uiFldId != FLM_RECID_FIELD); pPredicate->pPredNode = pQNode; pPredicate->eOperator = FLM_EXISTS_OP; if (pQNode->uiStatus & FLM_NOTTED) { pPredicate->bNotted = TRUE; } } else { // If parent is a logical operator, it is an existence test, so // we leave pVal to NULL. eParentOp = GET_QNODE_TYPE( pQNode->pParent); if (IS_LOG_OP( eParentOp)) { flmAssert( uiFldId != FLM_RECID_FIELD); pPredicate->pPredNode = pQNode; pPredicate->eOperator = FLM_EXISTS_OP; if (pQNode->uiStatus & FLM_NOTTED) { pPredicate->bNotted = TRUE; } } else { pPredicate->pPredNode = pQNode->pParent; pPredicate->eOperator = eParentOp; if (pQNode->pParent->uiStatus & FLM_NOTTED) { pPredicate->bNotted = TRUE; } // Better be a previous or next sibling if ((pSibling = pQNode->pNextSib) == NULL) { pSibling = pQNode->pPrevSib; } flmAssert( pSibling != NULL); eSibOp = GET_QNODE_TYPE( pSibling); // Better be a value or field if (IS_VAL( eSibOp)) { pPredicate->pVal = pSibling->pQAtom; if (uiFldId == FLM_RECID_FIELD) { pSubQuery->bHaveDrnFlds = TRUE; flmAssert( eSibOp == FLM_UINT32_VAL); if (RC_BAD( rc = flmSQGetDrnRanges( pSubQuery, pPredicate->eOperator, (FLMUINT)pPredicate->pVal->val.ui32Val))) { goto Exit; } } else if (uiFldId <= FLM_RESERVED_TAG_NUMS) { // Make sure that the value type we are comparing to // is compatible. if (!DoValAndDictTypesMatch( pPredicate->pVal->eType, uiDictType)) { flmAssert( 0); rc = RC_SET( FERR_CONV_BAD_DEST_TYPE); goto Exit; } } } else { // Can't generate a key with this because it is a field // to field comparison, or it is comparing to an // arithmetic expression. Must set operator to NO_TYPE to // indicate this. if (uiFldId == FLM_RECID_FIELD) { pSubQuery->bHaveDrnFlds = TRUE; } pPredicate->eOperator = NO_TYPE; } } } // Link in order the predicates are found in the tree. // The order doesn't matter from a pure evaluation standpoint, // but we do it this way so that the predicates are in somewhat // the same order that the user expressed them. if (pLastPredicate) { pLastPredicate->pNext = pPredicate; } else { *ppPredicateList = pPredicate; } pLastPredicate = pPredicate; // Go to parent and then to parent's sibling, unless the // predicate is an exists operator, in which case we need // to go to this node's sibling, if any. if (pPredicate->eOperator != FLM_EXISTS_OP) { pQNode = pQNode->pParent; } Goto_Sibling: // If no sibling, go back up the tree. while (pQNode && !pQNode->pNextSib) { pQNode = pQNode->pParent; } // If got to top of tree, we are done. if (!pQNode) { break; } // Goto the sibling node - has to be non-NULL at this point. pQNode = pQNode->pNextSib; flmAssert( pQNode != NULL); } else if (eOp == FLM_USER_PREDICATE) { *pbHaveUserPredicates = TRUE; goto Goto_Sibling; } else if (IS_VAL( eOp)) { // Go to sibling in case sibling is a field - don't want to miss // a field that is on the right-hand side of the predicate. goto Goto_Sibling; } else { // At this point, we know we are on a logical, // relational (comparison), or arithmetic // operator. There must always be a child // node at this point. We simply descend to it. pQNode = pQNode->pChild; flmAssert( pQNode != NULL); // Since things have been De-Morganized, we should not encounter // any NOT operators. flmAssert( eOp != FLM_NOT_OP); } } Exit: return( rc); } /**************************************************************************** Desc: Allocate a QINDEX structure for an index. ****************************************************************************/ FSTATIC RCODE flmAllocIndexInfo( F_Pool * pPool, QINDEX ** ppIndex, QINDEX ** ppIndexList, IXD * pIxd) { RCODE rc = FERR_OK; QINDEX * pIndex; if( RC_BAD( rc = pPool->poolCalloc( sizeof( QINDEX), (void **)&pIndex))) { goto Exit; } *ppIndex = pIndex; // The following items are initialized because of the calloc: // pIndex->bDoRecMatch = FALSE; // pIndex->bPredicatesRequireMatch = FALSE; // pIndex->uiNumPredicatesCovered = 0; pIndex->uiIndexNum = pIxd->uiIndexNum; pIndex->uiNumFields = pIxd->uiNumFlds; pIndex->pIxd = pIxd; // Allocate space for a list of predicate pointers // for each IFD. if( RC_BAD( rc = pPool->poolCalloc( sizeof( QFIELD_PREDICATE *) * pIndex->uiNumFields, (void **)&pIndex->ppFieldPredicateList))) { goto Exit; } // Link the index into the list of indexes. pIndex->pPrev = NULL; if ((pIndex->pNext = *ppIndexList) != NULL) { (*ppIndexList)->pPrev = pIndex; } *ppIndexList = pIndex; Exit: return( rc); } /**************************************************************************** Desc: Calculate a predicate's "rank" with respect to a particular IFD. ****************************************************************************/ FSTATIC FLMUINT flmCurCalcPredicateRank( QPREDICATE * pPredicate, IFD * pIfd ) { FLMUINT uiIfdType; FLMUINT uiOpType; if (pIfd->uiFlags & IFD_SUBSTRING) { uiIfdType = RANK_IFD_SUBSTRING; } else if (pIfd->uiFlags & IFD_CONTEXT) { uiIfdType = RANK_IFD_CONTEXT; } else { uiIfdType = RANK_IFD_VALUE; } switch (pPredicate->eOperator) { case FLM_EQ_OP: uiOpType = RANK_EQ; break; case FLM_MATCH_OP: uiOpType = (FLMUINT)((pPredicate->bNotted) ? RANK_NOT_MATCH : RANK_MATCH); break; case FLM_MATCH_BEGIN_OP: uiOpType = (FLMUINT)((pPredicate->bNotted) ? RANK_NOT_MATCH_BEGIN : RANK_MATCH_BEGIN); break; case FLM_MATCH_END_OP: uiOpType = (FLMUINT)((pPredicate->bNotted) ? RANK_NOT_MATCH_END : RANK_MATCH_END); break; case FLM_CONTAINS_OP: uiOpType = (FLMUINT)((pPredicate->bNotted) ? RANK_NOT_CONTAINS : RANK_CONTAINS); break; case FLM_NE_OP: uiOpType = RANK_NE; break; case FLM_LT_OP: case FLM_LE_OP: case FLM_GT_OP: case FLM_GE_OP: uiOpType = RANK_COMPARE; break; case FLM_EXISTS_OP: uiOpType = (FLMUINT)((pPredicate->bNotted) ? RANK_NOT_EXISTS : RANK_EXISTS); break; default: uiOpType = RANK_OTHER; break; } return( gv_uiRanks [uiIfdType][uiOpType]); } /**************************************************************************** Desc: Link a predicate into the predicate list for a particular index's IFD. The predicate is linked according to its ranking - lower rankings are better (1st, 2nd, 3rd, etc.) than higher rankings, so the order is from low to high rankings. ****************************************************************************/ FSTATIC void flmCurLinkPredicate( QINDEX * pIndex, QFIELD_PREDICATE * pPredToLink, QFIELD_PREDICATE ** ppPredicateList ) { QFIELD_PREDICATE * pPriorPred = NULL; QFIELD_PREDICATE * pAfterPred = *ppPredicateList; // Position this predicate according to how good it looks in // comparison to others in the list. while (pAfterPred && pAfterPred->uiRank < pPredToLink->uiRank) { pPriorPred = pAfterPred; pAfterPred = pAfterPred->pNext; } // Link between the after and before predicates. if (!pPriorPred) { *ppPredicateList = pPredToLink; } else { pPriorPred->pNext = pPredToLink; if (!pPredToLink->pPredicate->bFldSingleValued || !pPriorPred->pPredicate->bFldSingleValued) { pIndex->bMultiplePredsOnIfd = TRUE; } } if ((pPredToLink->pNext = pAfterPred) != NULL) { if (!pPredToLink->pPredicate->bFldSingleValued || !pAfterPred->pPredicate->bFldSingleValued) { pIndex->bMultiplePredsOnIfd = TRUE; } } } /**************************************************************************** Desc: Determine if an index field path is suitable for the query field path. It must either match or be less specific than the query field path. ****************************************************************************/ FSTATIC FLMBOOL flmIxFldPathSuitable( FLMUINT * puiIxFldPath, FLMUINT * puiQueryFldPath, FLMBOOL * pbMustVerifyQueryPath ) { FLMBOOL bIxPathHasWildcard = FALSE; FLMBOOL bSuitable = FALSE; while (*puiIxFldPath) { if (*puiIxFldPath == FLM_ANY_FIELD) { // Look at next field in IFD path to see if it matches // the current field. If it does, continue from there. if (*(puiIxFldPath + 1)) { bIxPathHasWildcard = TRUE; if (*puiQueryFldPath == *(puiIxFldPath + 1)) { // Skip wild card and field that matched. puiIxFldPath += 2; } if (*puiQueryFldPath) { // Go to next field in path being evaluated no matter // what. If it didn't match, we continue looking at // the wild card. If it did match, we go to the next // field in the path. puiQueryFldPath++; } else { // Index path not suitable - more specific than // query path. goto Exit; // Will return FALSE } } else { // Rest of path is an automatic match - had wildcard // at top of IFD path. break; } } else if (*puiQueryFldPath != *puiIxFldPath) { // If we did not go through all of the index's field path, // the index field path either doesn't match or is more // specific than the query's field path. goto Exit; // will return FALSE. } else { puiIxFldPath++; puiQueryFldPath++; } } bSuitable = TRUE; // If the query path is more specific, or the index path has // a wild card, the query path must be verified by fetching // the record. *pbMustVerifyQueryPath = (*puiQueryFldPath || bIxPathHasWildcard) ? TRUE : FALSE; Exit: return( bSuitable); } /**************************************************************************** Desc: Generate the list of suitable indexes for a sub-query. Rank each index to determine which of them we should do cost estimation on first. ****************************************************************************/ FSTATIC RCODE flmSQGetSuitableIndexes( FDB * pDb, FLMUINT uiForceIndex, CURSOR * pCursor, SUBQUERY * pSubQuery, FLMUINT uiContainer, F_Pool * pPool, QPREDICATE * pPredicateList, FLMUINT uiTotalPredicates, FLMBOOL bHaveUserPredicates, QINDEX * * ppIndexList, FLMUINT * puiMaxIfds) { RCODE rc = FERR_OK; QPREDICATE * pPredicate; IFD * pIfd; QINDEX * pIndexList; QINDEX * pIndex; QINDEX * pNextIndex; FLMUINT uiLoop; QFIELD_PREDICATE * pFieldPredicate; FLMUINT uiMaxIfds = 0; FLMBOOL bMustVerifyQueryPath; // Cycle through all of the predicates and traverse each field's // list of indexes. pIndexList = NULL; if (uiForceIndex) { IXD * pIxd; if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiForceIndex, NULL, &pIxd))) { goto Exit; } if (RC_BAD( rc = flmAllocIndexInfo( pPool, &pIndex, &pIndexList, pIxd))) { goto Exit; } if (pCursor->uiRecType) { pIndex->bDoRecMatch = TRUE; } if (uiMaxIfds < pIndex->uiNumFields) { uiMaxIfds = pIndex->uiNumFields; } } else { for (pPredicate = pPredicateList; pPredicate; pPredicate = pPredicate->pNext) { // No need to check for IFDs if this is the DRN field. // It will never have an IFD. if (*pPredicate->puiFldPath == FLM_RECID_FIELD) { continue; } // Get the field's IFD list. if (RC_BAD( rc = fdictGetField( pDb->pDict, *pPredicate->puiFldPath, NULL, &pIfd, NULL))) { goto Exit; } // Cycle through the IFDs where the field is required. for (; pIfd; pIfd = pIfd->pNextInChain) { // Stop at the first non-required field. if (!(pIfd->uiFlags & (IFD_REQUIRED_PIECE | IFD_REQUIRED_IN_SET))) { break; } // Check the following conditions of suitability: // 1) Index must be on the container we are searching. // 2) Index must be on-line // 3) If index is encrypted and we must not be in limited mode. // NOTE: A container number of zero means we are indexing // all containers. if ((pIfd->pIxd->uiContainerNum && pIfd->pIxd->uiContainerNum != uiContainer) || ((pIfd->pIxd->uiFlags & IXD_OFFLINE) != 0) || (pIfd->pIxd->uiEncId && pDb->pFile->bInLimitedMode)) { continue; } // Make sure the IFD's path is of less or equal specificity // to the field's path. We can skip the zeroeth element, // because we already know it matches. if (!flmIxFldPathSuitable( &pIfd->pFieldPathCToP [1], &pPredicate->puiFldPath [1], &bMustVerifyQueryPath)) { continue; } // Verify that the predicate does not return TRUE when // tested against a NULL record. If we have already // evaluated the predicate, no need to do it again, just // take the results from last time. if (!pPredicate->bEvaluatedNullRec) { if (IS_COMPARE_OP( pPredicate->eOperator)) { FQATOM Result; // f_memset( &Result, 0, sizeof( FQATOM)); // flmCurEvalCompareOp inits the following values: // eType, pNext, val.Bool; Result.uiFlags = Result.uiBufLen = 0; if (RC_BAD( rc = flmCurEvalCompareOp( pDb, pSubQuery, NULL, pPredicate->pPredNode, pPredicate->eOperator, FALSE, &Result))) { goto Exit; } if (Result.eType == FLM_BOOL_VAL && Result.val.uiBool == FLM_TRUE) { pPredicate->bReturnsTrueOnNullRec = TRUE; } } else if (pPredicate->eOperator == FLM_EXISTS_OP && pPredicate->bNotted) { pPredicate->bReturnsTrueOnNullRec = TRUE; } pPredicate->bEvaluatedNullRec = TRUE; } // Put this index in the list - if not already there. pIndex = pIndexList; while (pIndex && pIndex->uiIndexNum != pIfd->uiIndexNum) { pIndex = pIndex->pNext; } // If we did not find the index, allocate it and link into // the list. if (!pIndex) { if (RC_BAD( rc = flmAllocIndexInfo( pPool, &pIndex, &pIndexList, pIfd->pIxd))) { goto Exit; } if (uiMaxIfds < pIndex->uiNumFields) { uiMaxIfds = pIndex->uiNumFields; } } // If we are testing the record type, must do a record match. // Also if we are forcing a particular index and we did not // go through all of the index's field path, // the index field path either does not match the query's // field path or it is more specific that the query's // field path. In either case, we must force a record match. if (pCursor->uiRecType) { pIndex->bDoRecMatch = TRUE; } // If the field's query path is more specific than the index's query // path, we need to set bDoRecMatch to TRUE - because that can only // be verified by fetching the record. // If the IFD is on field's tag and the operator is not the exists // operator, we must also fetch the record to evaluate the predicate. if (bMustVerifyQueryPath || ((pIfd->uiFlags & IFD_CONTEXT) && pPredicate->eOperator != FLM_EXISTS_OP)) { pIndex->bDoRecMatch = TRUE; } // Add this predicate to the list of predicates for this IFD. if( RC_BAD( rc = pPool->poolAlloc( sizeof( QFIELD_PREDICATE), (void **)&pFieldPredicate))) { goto Exit; } pFieldPredicate->pIfd = pIfd; pFieldPredicate->pPredicate = pPredicate; pFieldPredicate->uiRank = flmCurCalcPredicateRank( pPredicate, pIfd); flmCurLinkPredicate( pIndex, pFieldPredicate, &pIndex->ppFieldPredicateList [pIfd->uiCompoundPos]); pIndex->uiNumPredicatesCovered++; } } } // Get all predicates that match each IFD of each index. Must eliminate // any indexes that do not have criteria on each required field. This pass // will also set the bDoRecMatch flag if necessary. pIndex = pIndexList; while (pIndex) { FLMBOOL bHavePredInRequiredSet; FLMBOOL bHaveRequiredSet; // Need to get the next index in case we remove pIndex from the list. pNextIndex = pIndex->pNext; // Loop through all of the IFDs for the index - make sure that every // required IFD has a predicate. bHavePredInRequiredSet = FALSE; bHaveRequiredSet = FALSE; for (uiLoop = 0, pIfd = pIndex->pIxd->pFirstIfd; uiLoop < pIndex->uiNumFields; uiLoop++, pIfd++) { // If we are forcing an index, we MUST process the IFD because it // will not have been done above - even if it is required. if (uiForceIndex) { goto Process_IFD; } // See if there is a predicate for this IFD. If non-NULL, we have // already collected them above - when we traversed the required IFDs. // We are now mainly traversing to collect the non-required IFDs. if (pIndex->ppFieldPredicateList [uiLoop]) { // At this point, we know that the IFD is required, otherwise // it would not have a predicate linked off of it. flmAssert( pIfd->uiFlags & (IFD_REQUIRED_IN_SET | IFD_REQUIRED_PIECE)); // Since this IFD is required, we must have at least one // predicate where the bReturnsTrueOnNullRec flag is NOT TRUE. // If it is TRUE for all of the predicates linked off of this // IFD, the index is not suitable. // NOTE: We could have skipped these predicates in the loop // above, but we actually need to link them into the list // because if there is at least one good predicate and one or // more bad predicates, we need to set the bMultiplePredsOnIfd // flag for the index. If we skipped over the bad predicates // without linking them off of the IFD, we would not know that // an IFD had multiple predicates. pFieldPredicate = pIndex->ppFieldPredicateList [uiLoop]; while (pFieldPredicate && pFieldPredicate->pPredicate->bReturnsTrueOnNullRec) { pFieldPredicate = pFieldPredicate->pNext; } if (!pFieldPredicate) { goto Remove_Index; } if (pIfd->uiFlags & IFD_REQUIRED_IN_SET) { // Only one of the fields in a required set has to // be in the query. Here we set the flag indicating that // we have a required set and we have a predicate on at // least one of the fields in the required set. At the // end of the looping through the IFDs we make our final // test of this condition. bHaveRequiredSet = TRUE; bHavePredInRequiredSet = TRUE; } } else { // If this IFD is required, but there is no predicate, we have // an unsuitable index. if (pIfd->uiFlags & IFD_REQUIRED_PIECE) { goto Remove_Index; } else if (pIfd->uiFlags & IFD_REQUIRED_IN_SET) { // Only one of the fields in a required set has to // be in the query. Although this field has no predicate, // there may be another that does. We can't really tell // until we come to the end of the IFDs for this index. // For now, we just set a flag to indicate that this index // has a required set of IFDs. bHaveRequiredSet = TRUE; } else { Process_IFD: // Field is optional or we are forcing the index and // have not yet checked for predicates on the IFDs. // See if there are any predicates in the query for this // IFD. for (pPredicate = pPredicateList; pPredicate; pPredicate = pPredicate->pNext) { if (*pPredicate->puiFldPath == FLM_RECID_FIELD) { pIndex->bPredicatesRequireMatch = TRUE; // Count this as a covered predicate - because we // can evaluate it on just the key if necessary. pIndex->uiNumPredicatesCovered++; continue; } // See if the predicate matches this IFD. if (!flmIxFldPathSuitable( pIfd->pFieldPathCToP, pPredicate->puiFldPath, &bMustVerifyQueryPath)) { continue; } // If the query field path is more specific, we need to // fetch the record to evaluate this predicate. // If the IFD is on field's tag, the operator better be // an exists operator. If not, we must fetch the // record to evaluate this predicate. if (bMustVerifyQueryPath || ((pIfd->uiFlags & IFD_CONTEXT) && pPredicate->eOperator != FLM_EXISTS_OP)) { pIndex->bDoRecMatch = TRUE; } // Add this predicate to the list of predicates for this IFD. if( RC_BAD( rc = pPool->poolAlloc( sizeof( QFIELD_PREDICATE), (void **)&pFieldPredicate))) { goto Exit; } pFieldPredicate->pIfd = pIfd; pFieldPredicate->pPredicate = pPredicate; pFieldPredicate->uiRank = flmCurCalcPredicateRank( pPredicate, pIfd); flmCurLinkPredicate( pIndex, pFieldPredicate, &pIndex->ppFieldPredicateList [uiLoop]); pIndex->uiNumPredicatesCovered++; } } } } // See if we had a required required set of fields in the IFD list. // If so, we better have had a predicate for at least one of the // IFDs in the set. If we didn't, the index is not suitable for // the query. // NOTE: This doesn't matter if we are forcing an index. if (bHaveRequiredSet && !bHavePredInRequiredSet && !uiForceIndex) { Remove_Index: // Remove the index from the list. if (pIndex->pNext) { pIndex->pNext->pPrev = pIndex->pPrev; } if (pIndex->pPrev) { pIndex->pPrev->pNext = pIndex->pNext; } else { pIndexList = pIndex->pNext; } // Set pIndex to NULL so we won't process below. pIndex = NULL; } // pIndex will be NULL if we removed it from the list above. if (pIndex) { // If we did not cover all of the predicates with this index // we need to fetch the records. Also, if there are user // predicates, we need to fetch the record for evaluation. if (pIndex->uiNumPredicatesCovered < uiTotalPredicates || bHaveUserPredicates) { pIndex->bDoRecMatch = TRUE; } // Set the index's ranking. Use the rank that was given // to the first IFD's first predicate. If there is no // first predicate, set the index's rank to be very // high so that it will be evaluated last. pIndex->uiRank = (pIndex->ppFieldPredicateList && pIndex->ppFieldPredicateList [0]) ? pIndex->ppFieldPredicateList [0]->uiRank : 0xFFFFFFFF; } pIndex = pNextIndex; } // Order the indexes according to their rank that was // assigned above. pIndex = pIndexList; while (pIndex && ((pNextIndex = pIndex->pNext) != NULL)) { if (pIndex->uiRank <= pNextIndex->uiRank) { pIndex = pNextIndex; } else { QINDEX * pPrevIndex = NULL; // Unlink pNextIndex from chain. pIndex->pNext = pNextIndex->pNext; // Insert pNextIndex into the list according to its rank. pIndex = pIndexList; for (;;) { if (pNextIndex->uiRank <= pIndex->uiRank) { // Insert pNextIndex between pPrevIndex and pIndex. if (pPrevIndex) { pPrevIndex->pNext = pNextIndex; } else { pIndexList = pNextIndex; } pNextIndex->pNext = pIndex; break; } else { pPrevIndex = pIndex; pIndex = pIndex->pNext; // Should be impossible for pIndex to go NULL here! // This is because we know that there is at least // one index that has a higher rank number than // pNextIndex has. flmAssert( pIndex != NULL); } } } } *ppIndexList = pIndexList; Exit: *puiMaxIfds = uiMaxIfds; return( rc); } /**************************************************************************** Desc: Generate a key for the current set of predicates being pointed to and evaluate their cost. ****************************************************************************/ FSTATIC RCODE flmSQEvaluateCurrIndexKey( FDB * pDb, SUBQUERY * pSubQuery, FSIndexCursor ** ppTmpFSIndexCursor, QINDEX * pIndex, QFIELD_PREDICATE ** ppFieldCurrPredicate, QPREDICATE ** ppPredicateList ) { RCODE rc = FERR_OK; FSIndexCursor * pTmpFSIndexCursor; FLMUINT uiLoop; QPREDICATE * pPredicate; FLMUINT uiLeafBlocksBetween; FLMUINT uiTotalKeys; FLMUINT uiTotalRefs; FLMBOOL bDoRecMatch; FLMBOOL bDoKeyMatch; FLMBOOL bTotalsEstimated; FLMUINT uiCost; for (uiLoop = 0; uiLoop < pIndex->uiNumFields; uiLoop++) { if ((pPredicate = (QPREDICATE *)((ppFieldCurrPredicate [uiLoop]) ? ppFieldCurrPredicate [uiLoop]->pPredicate : (QPREDICATE *)NULL)) == NULL) { ppPredicateList [uiLoop] = NULL; } else { switch (pPredicate->eOperator) { case FLM_EQ_OP: case FLM_MATCH_OP: case FLM_MATCH_BEGIN_OP: case FLM_MATCH_END_OP: case FLM_CONTAINS_OP: case FLM_NE_OP: case FLM_LT_OP: case FLM_LE_OP: case FLM_GT_OP: case FLM_GE_OP: if (pPredicate->pVal) { ppPredicateList [uiLoop] = pPredicate; } else { ppPredicateList [uiLoop] = NULL; pIndex->bPredicatesRequireMatch = TRUE; } break; case FLM_EXISTS_OP: ppPredicateList [uiLoop] = pPredicate; break; default: ppPredicateList [uiLoop] = NULL; pIndex->bPredicatesRequireMatch = TRUE; break; } } } // Use the temporary file system cursor to evaluate the cost. // Allocate a temporary file system cursor if we have not // already allocated one. if ((pTmpFSIndexCursor = *ppTmpFSIndexCursor) == NULL) { if ((pTmpFSIndexCursor = *ppTmpFSIndexCursor = f_new FSIndexCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } } else { pTmpFSIndexCursor->reset(); } bDoRecMatch = pIndex->bDoRecMatch; bDoKeyMatch = TRUE; if (RC_BAD( rc = pTmpFSIndexCursor->setupKeys( pDb, pIndex->pIxd, ppPredicateList, &bDoRecMatch, &bDoKeyMatch, &uiLeafBlocksBetween, &uiTotalKeys, &uiTotalRefs, &bTotalsEstimated))) { goto Exit; } // If we have multiple predicates on an IFD, we MUST NOT // do a key match, because any key read from the index // will NOT have multiple values, and hence would possibly // fail one of the predicates. Instead, we must do a // record match. if (pIndex->bMultiplePredsOnIfd) { bDoKeyMatch = FALSE; bDoRecMatch = TRUE; } // If we must do some kind of predicate matching, either // bDoKeyMatch or bDoRecMatch must be set to TRUE, // preferrably bDoKeyMatch. // If bDoKeyMatch was FALSE and the intent was that it be // forced to FALSE, bDoRecMatch would have been set to TRUE. // Thus, Since bDoRecMatch is FALSE, we can safely set // bDoKeyMatch to TRUE, regardless of what it was before, // because there was no intent that it be forced to FALSE. if (pIndex->bPredicatesRequireMatch) { if (!bDoRecMatch) { bDoKeyMatch = TRUE; } // else bDoRecMatch is TRUE, and that is sufficient. } uiCost = (FLMUINT)((bDoRecMatch) ? uiLeafBlocksBetween + uiTotalRefs : uiLeafBlocksBetween); // Could be that there are zero leaf blocks between and // bDoRecMatch is FALSE. But we do not want a cost of // zero. if (!uiCost) { uiCost = 1; } if (!pSubQuery->OptInfo.uiCost || uiCost < pSubQuery->OptInfo.uiCost) { // Exchange the temporary file system cursor and the // file system cursor inside the sub-query. Want to // keep the temporary one and reuse the one that was // inside the sub-query. if (pSubQuery->pFSIndexCursor) { pSubQuery->pFSIndexCursor->reset(); } *ppTmpFSIndexCursor = pSubQuery->pFSIndexCursor; pSubQuery->OptInfo.eOptType = QOPT_USING_INDEX; pSubQuery->OptInfo.uiIxNum = pIndex->uiIndexNum; pSubQuery->pFSIndexCursor = pTmpFSIndexCursor; pSubQuery->OptInfo.uiCost = uiCost; pSubQuery->OptInfo.uiDrnCost = uiTotalRefs; pSubQuery->OptInfo.bDoRecMatch = bDoRecMatch; pSubQuery->OptInfo.bDoKeyMatch = bDoKeyMatch; // Not really necessary to set these, but it is // cleaner. pSubQuery->OptInfo.uiDrn = 0; pSubQuery->pPredicate = NULL; // The following better already be set. flmAssert( pSubQuery->pFSDataCursor == NULL); } Exit: return( rc); } /**************************************************************************** Desc: Set up a sub-query to do a full container scan. ****************************************************************************/ FSTATIC RCODE flmSQSetupFullContainerScan( CURSOR * pCursor, SUBQUERY * pSubQuery ) { RCODE rc = FERR_OK; FLMBOOL bTotalsEstimated; FLMUINT uiLeafBlocksBetween; FLMUINT uiEstimatedDrns; // Set up a file system data cursor if ((pSubQuery->pFSDataCursor = f_new FSDataCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Set up the range to calculate the cost. if (RC_BAD( rc = pSubQuery->pFSDataCursor->setupRange( pCursor->pDb, pCursor->uiContainer, 1, 0xFFFFFFFF, &uiLeafBlocksBetween, &uiEstimatedDrns, &bTotalsEstimated))) { goto Exit; } // Set up everything to be a full container scan. pSubQuery->OptInfo.eOptType = QOPT_FULL_CONTAINER_SCAN; pSubQuery->OptInfo.bDoRecMatch = TRUE; pSubQuery->OptInfo.bDoKeyMatch = FALSE; // Must not ever have a cost of zero. If the container is // empty, we will get zero leaf blocks between, in which case // we want to return at least a cost of one. if ((pSubQuery->OptInfo.uiCost = uiLeafBlocksBetween) == 0) { pSubQuery->OptInfo.uiCost = 1; } pSubQuery->OptInfo.uiDrnCost = uiEstimatedDrns; // Not really necessary to set these things, but it is cleaner. pSubQuery->OptInfo.uiIxNum = 0; pSubQuery->OptInfo.uiDrn = 0; pSubQuery->pPredicate = NULL; // Kill the file system index cursor, if any. if (pSubQuery->pFSIndexCursor) { pSubQuery->pFSIndexCursor->Release(); pSubQuery->pFSIndexCursor = NULL; } Exit: return( rc); } /**************************************************************************** Desc: Chooses a best index to use for a sub-query. ****************************************************************************/ FSTATIC RCODE flmSQChooseBestIndex( CURSOR * pCursor, FDB * pDb, FLMUINT uiForceIndex, FLMUINT bForceFirstToLastKey, SUBQUERY * pSubQuery, F_Pool * pTempPool, QPREDICATE * pPredicateList, FLMUINT uiTotalPredicates, FLMBOOL bHaveUserPredicates) { RCODE rc = FERR_OK; QINDEX * pIndexList; QINDEX * pIndex; FLMUINT uiCurrIfd; FLMUINT uiMaxIfds; QFIELD_PREDICATE ** ppFieldCurrPredicate = NULL; QPREDICATE ** ppPredicateList = NULL; FSIndexCursor * pTmpFSIndexCursor = NULL; FSDataCursor * pTmpFSDataCursor = NULL; // If this is a DRN==x query, don't bother looking at indexes. if (!uiForceIndex && pSubQuery->uiLowDrn == pSubQuery->uiHighDrn) { // This is the lowest cost possible - the cost of reading // one record. pSubQuery->OptInfo.eOptType = QOPT_SINGLE_RECORD_READ; pSubQuery->OptInfo.uiCost = 1; pSubQuery->OptInfo.uiDrnCost = 1; pSubQuery->OptInfo.uiDrn = pSubQuery->uiLowDrn; pSubQuery->OptInfo.bDoRecMatch = TRUE; pSubQuery->OptInfo.bDoKeyMatch = FALSE; // Not really necessary to set these things, but makes // it cleaner. pSubQuery->OptInfo.uiIxNum =0; pSubQuery->pPredicate = NULL; // The following things should already be set correctly. flmAssert( pSubQuery->pFSIndexCursor == NULL && pSubQuery->pFSDataCursor == NULL); goto Exit; // Nothing more to do - should return SUCCESS. } // Generate the list of suitable indexes. if (RC_BAD( rc = flmSQGetSuitableIndexes( pDb, uiForceIndex, pCursor, pSubQuery, pCursor->uiContainer, pTempPool, pPredicateList, uiTotalPredicates, bHaveUserPredicates, &pIndexList, &uiMaxIfds))) { goto Exit; } // Allocate temporary predicate pointer arrays. if (uiMaxIfds) { // Allocate space for a second list of predicate pointers // for each IFD. This one is used to keep track of which // predicate we are on when we are generating keys. if( RC_BAD( rc = pTempPool->poolCalloc( sizeof( QFIELD_PREDICATE *) * uiMaxIfds, (void **)&ppFieldCurrPredicate))) { goto Exit; } // Allocate space for the array that will be passed into the // key generation routine. if( RC_BAD( rc = pTempPool->poolCalloc( sizeof( QPREDICATE *) * uiMaxIfds, (void **)&ppPredicateList))) { goto Exit; } } // Calculate the cost of each of the indexes. pSubQuery->OptInfo.eOptType = QOPT_NONE; pSubQuery->OptInfo.uiCost = 0; pSubQuery->OptInfo.uiDrnCost = 0; for (pIndex = pIndexList; pIndex; pIndex = pIndex->pNext) { // Generate all search keys, keep one with lowest cost. if (bForceFirstToLastKey) { // If we are forcing a first-to-last key, we set up an // array of NULL predicates. Forcing of a first-to-last // key is only done when we are forcing a particular index // and we were unable to stratify the query into a // disjunction of conjunct sub-queries. f_memset( ppFieldCurrPredicate, 0, sizeof( QFIELD_PREDICATE *) * pIndex->uiNumFields); } else { f_memcpy( ppFieldCurrPredicate, pIndex->ppFieldPredicateList, sizeof( QFIELD_PREDICATE *) * pIndex->uiNumFields); } uiCurrIfd = 0; for (;;) { if (RC_BAD( rc = flmSQEvaluateCurrIndexKey( pDb, pSubQuery, &pTmpFSIndexCursor, pIndex, ppFieldCurrPredicate, ppPredicateList))) { goto Exit; } // See if it is worth going on. If cost is lower than 8, it is not. // Also, if we are forcing a first to last key, we are done. if (pSubQuery->OptInfo.uiCost && pSubQuery->OptInfo.uiCost < 8) { goto Done_Evaluating_Indexes; } else if (bForceFirstToLastKey) { goto Next_Index; } // Go to next set of predicates for (;;) { // See if the current IFD has another predicate to process. if ((ppFieldCurrPredicate [uiCurrIfd]) && (ppFieldCurrPredicate [uiCurrIfd]->pNext)) { Next_Ifd_Predicate: ppFieldCurrPredicate [uiCurrIfd] = ppFieldCurrPredicate [uiCurrIfd]->pNext; // If this is not the last IFD in the index, change // uiCurrIfd so that we will be looking at the predicate // list for the next IFD in the index the next time we // come in to get another predicate. if (uiCurrIfd < pIndex->uiNumFields - 1) { uiCurrIfd++; } break; } else if (pIndex->uiNumFields == 1) { // Only one IFD in the index, and we have traversed all of // its predicates - go to next index. goto Next_Index; } else if (++uiCurrIfd == pIndex->uiNumFields) { // If we have gone through all of the IFDs, traverse back // up the list of IFDs until we hit one that has more // predicates to process. As we go back up the list, if an // IFD's predicate list has been completely processed, // reset its current predicate to start at the first of the // list again. uiCurrIfd--; for (;;) { // Reset this IFD's current predicate to the first of its // predicate list. ppFieldCurrPredicate [uiCurrIfd] = pIndex->ppFieldPredicateList [uiCurrIfd]; // See if prior IFD's predicate list was completely // processed. uiCurrIfd--; if ((ppFieldCurrPredicate [uiCurrIfd]) && (ppFieldCurrPredicate [uiCurrIfd]->pNext)) { goto Next_Ifd_Predicate; } // If we are at the first IFD in the index, we have // processed all combinations of predicates for the // index - time to go to the next index. if (!uiCurrIfd) { goto Next_Index; } } } } } Next_Index: ; } Done_Evaluating_Indexes: // If the sub-query has DRN fields, analyze it to see if it would be // better to use the DRN keys than any index at all. Also need to // do this if there were no suitable indexes. if (!uiForceIndex && (pSubQuery->bHaveDrnFlds || !pSubQuery->pFSIndexCursor)) { // NOTE: Will have already taken care of the case where // low drn == high drn - see above. // If it is first to last, set up a full container scan. if (pSubQuery->uiLowDrn == 1 && pSubQuery->uiHighDrn == 0xFFFFFFFF) { // Only use full container scan if there is no suitable index. if (!pSubQuery->pFSIndexCursor) { if (RC_BAD( rc = flmSQSetupFullContainerScan( pCursor, pSubQuery))) { goto Exit; } pSubQuery->pFSDataCursor->setContainer( pCursor->uiContainer); } } else { FLMUINT uiLeafBlocksBetween; FLMUINT uiEstimatedDrns; FLMBOOL bTotalsEstimated; // Set up a partial container scan. if ((pTmpFSDataCursor = f_new FSDataCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = pTmpFSDataCursor->setupRange( pDb, pCursor->uiContainer, pSubQuery->uiLowDrn, pSubQuery->uiHighDrn, &uiLeafBlocksBetween, &uiEstimatedDrns, &bTotalsEstimated))) { goto Exit; } // If this is lower cost than the index, use it and // free up the index cursor. if (uiLeafBlocksBetween < pSubQuery->OptInfo.uiCost || !pSubQuery->OptInfo.uiCost) { pSubQuery->OptInfo.eOptType = QOPT_PARTIAL_CONTAINER_SCAN; // Must not ever have a cost of zero. If the range is // empty, we will get zero leaf blocks between, in which case // we want to return at least a cost of one. if ((pSubQuery->OptInfo.uiCost = uiLeafBlocksBetween) == 0) { pSubQuery->OptInfo.uiCost = 1; } pSubQuery->OptInfo.uiDrnCost = uiEstimatedDrns; pSubQuery->OptInfo.bDoRecMatch = TRUE; pSubQuery->OptInfo.bDoKeyMatch = FALSE; pSubQuery->pFSDataCursor = pTmpFSDataCursor; // Must set pTmpFSDataCursor to NULL so that it will // not be freed below. pTmpFSDataCursor = NULL; // Not really necessary to set these, but makes things // cleaner. pSubQuery->OptInfo.uiIxNum = 0; pSubQuery->OptInfo.uiDrn = 0; pSubQuery->pPredicate = NULL; // Free up the file system index cursor, if any. if (pSubQuery->pFSIndexCursor) { pSubQuery->pFSIndexCursor->Release(); pSubQuery->pFSIndexCursor = NULL; } } } } Exit: if (pTmpFSIndexCursor) { pTmpFSIndexCursor->Release(); } if (pTmpFSDataCursor) { pTmpFSDataCursor->Release(); } return( rc); } /**************************************************************************** Desc: Gets scores for any embedded user predicate. ****************************************************************************/ FSTATIC RCODE flmCheckUserPredicateCosts( FDB * pDb, SUBQUERY * pSubQuery, FLMBOOL bOkToOptimizeWithPredicate ) { RCODE rc = FERR_OK; FQNODE * pQNode = pSubQuery->pTree; FLMUINT uiCost; FLMUINT uiDrnCost; FlmUserPredicate * pPredicate; FlmUserPredicate * pLowestCostPredicate = NULL; FLMUINT uiLowestCost = 0; FLMUINT uiLowestDrnCost = 0; FLMUINT uiSumTestRecordCost = 0; FLMUINT uiTestRecordCost; FLMUINT uiLowestTestRecordCost = 0; FLMUINT uiSumTestAllRecordCost = 0; FLMUINT uiTestAllRecordCost; FLMBOOL bPassesEmptyRec; while (pQNode) { // If we have a user predicate, get its score. if (GET_QNODE_TYPE( pQNode) == FLM_USER_PREDICATE) { FLMBOOL bSavedInvisTrans; pPredicate = pQNode->pQAtom->val.pPredicate; CB_ENTER( pDb, &bSavedInvisTrans); rc = pPredicate->searchCost( (HFDB)pDb, (FLMBOOL)((pQNode->uiStatus & FLM_NOTTED) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE), (FLMBOOL)((pQNode->uiStatus & FLM_FOR_EVERY) ? (FLMBOOL)FALSE : (FLMBOOL)TRUE), &uiCost, &uiDrnCost, &uiTestRecordCost, &bPassesEmptyRec); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } uiSumTestRecordCost += uiTestRecordCost; uiTestAllRecordCost = 0; if (pSubQuery->OptInfo.eOptType == QOPT_FULL_CONTAINER_SCAN) { CB_ENTER( pDb, &bSavedInvisTrans); rc = pPredicate->testAllRecordCost( (HFDB)pDb, &uiTestAllRecordCost); CB_EXIT( pDb, bSavedInvisTrans); if (RC_BAD( rc)) { goto Exit; } uiSumTestAllRecordCost += uiTestAllRecordCost; } // Cannot use a predicate that would pass an empty record - // because the predicate might not return all records that // would pass the criteria. if (!bPassesEmptyRec && (!pLowestCostPredicate || uiCost < uiLowestCost)) { pLowestCostPredicate = pPredicate; uiLowestCost = uiCost; uiLowestDrnCost = uiDrnCost; uiLowestTestRecordCost = uiTestRecordCost; } } if (pQNode->pChild) { pQNode = pQNode->pChild; } else { // Traverse back up the tree until we hit the top // or we hit a sibling that has not been processed. for (;;) { if (pQNode->pNextSib) { pQNode = pQNode->pNextSib; break; } else if ((pQNode = pQNode->pParent) == NULL) { break; } } } } // Adjust the current predicate with the additional test record // costs. // For a full container scan, it is less likely that we // will be calling the testRecord() method for every record, // because not as many will pass. In this case, the additional // cost is the result of the call we make the // additional cost is calculated by getting the cost from // the predicates of what it would be to test every record // the predicate is likely going to cover. // For a sub-query that is NOT a full container scan, the assumption // is that the records retrieved in the key (or DRN) ranges will mostly // pass the criteria - thus, we will likely call testRecord() // for each record fetched. So the additional cost is // the testRecordCost() times the number of records we // are probably going to have to fetch. if (pSubQuery->OptInfo.eOptType == QOPT_FULL_CONTAINER_SCAN) { pSubQuery->OptInfo.uiCost += uiSumTestAllRecordCost; } else { pSubQuery->OptInfo.uiCost += (uiSumTestRecordCost * pSubQuery->OptInfo.uiDrnCost); } if (pLowestCostPredicate) { // Lowest predicate cost is the lowest predicate's actual cost, // plus the test record cost of all of the other predicates - note // that we subtract out the test record cost of this // predicate. uiLowestCost += (uiSumTestRecordCost - uiLowestTestRecordCost) * uiLowestDrnCost; } // If the predicate with the lowest cost is lower than // the current sub-query estimated cost, use it to // optimize the query. if (bOkToOptimizeWithPredicate && pLowestCostPredicate && uiLowestCost < pSubQuery->OptInfo.uiCost) { pSubQuery->OptInfo.eOptType = QOPT_USING_PREDICATE; pSubQuery->pPredicate = pLowestCostPredicate; pSubQuery->OptInfo.uiCost = uiLowestCost; // Release index cursor if any. pSubQuery->OptInfo.uiIxNum = 0; if (pSubQuery->pFSIndexCursor) { pSubQuery->pFSIndexCursor->Release(); pSubQuery->pFSIndexCursor = NULL; } // Release data cursor if any pSubQuery->OptInfo.uiDrn = 0; if (pSubQuery->pFSDataCursor) { pSubQuery->pFSDataCursor->Release(); pSubQuery->pFSDataCursor = NULL; } pSubQuery->OptInfo.bDoRecMatch = TRUE; pSubQuery->OptInfo.bDoKeyMatch = FALSE; } Exit: return( rc); } /**************************************************************************** Desc: Merges two SUBQUERY structures. ****************************************************************************/ FSTATIC RCODE flmMergeSubQueries( CURSOR * pCursor, SUBQUERY * * ppFromSubQuery, SUBQUERY * pIntoSubQuery, FLMBOOL bFromSubQuerySubsumed ) { RCODE rc = FERR_OK; SUBQUERY * pFromSubQuery = *ppFromSubQuery; FSDataCursor * pTmpFSCursor = NULL; OPT_INFO TmpOptInfo; if( RC_BAD( rc = flmCurGraftNode( &pCursor->QueryPool, pFromSubQuery->pTree, FLM_OR_OP, &pIntoSubQuery->pTree))) { goto Exit; } pFromSubQuery->pTree = NULL; switch (pIntoSubQuery->OptInfo.eOptType) { case QOPT_USING_INDEX: // This kind of a merge should only occur if the // destination cursor and source cursor both had // the same index. flmAssert( pFromSubQuery->OptInfo.eOptType == QOPT_USING_INDEX && pFromSubQuery->pFSIndexCursor != NULL && pFromSubQuery->OptInfo.uiIxNum == pIntoSubQuery->OptInfo.uiIxNum); if (RC_BAD( rc = pIntoSubQuery->pFSIndexCursor->unionKeys( pFromSubQuery->pFSIndexCursor))) { goto Exit; } // Only change flags if pIntoSubQuery->bDoRecMatch is FALSE. // If pIntoSubQuery->bDoRecMatch is TRUE, we will not // change it or bDoKeyMatch. Remember, if // pIntoSubQuery->bDoKeyMatch is FALSE and // pIntoSubQuery->bDoRecMatch is TRUE, it means that // we MUST NOT do a key match - we are forcing a record match // INSTEAD of a key match. if (!pIntoSubQuery->OptInfo.bDoRecMatch) { if (pFromSubQuery->OptInfo.bDoRecMatch) { pIntoSubQuery->OptInfo.bDoRecMatch = TRUE; } if (pFromSubQuery->OptInfo.bDoKeyMatch) { pIntoSubQuery->OptInfo.bDoKeyMatch = TRUE; } } break; case QOPT_USING_PREDICATE: // Can only merge into a predicate sub-query if the // from sub-query is also a predicate. flmAssert( pFromSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE); break; case QOPT_SINGLE_RECORD_READ: if (pFromSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ) { // Can only merge into a single record read if the // from sub-query is also a single record read AND // it is the exact same DRN. flmAssert( pFromSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ && pFromSubQuery->OptInfo.uiDrn == pIntoSubQuery->OptInfo.uiDrn); } else if (pFromSubQuery->OptInfo.eOptType == QOPT_PARTIAL_CONTAINER_SCAN) { // Set up a file system data cursor if ((pIntoSubQuery->pFSDataCursor = f_new FSDataCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = pIntoSubQuery->pFSDataCursor->setupRange( pCursor->pDb, pCursor->uiContainer, pIntoSubQuery->OptInfo.uiDrn, pIntoSubQuery->OptInfo.uiDrn, NULL, NULL, NULL))) { goto Exit; } if (RC_BAD( rc = pIntoSubQuery->pFSDataCursor->unionRange( pFromSubQuery->pFSDataCursor))) { goto Exit; } pIntoSubQuery->OptInfo.eOptType = QOPT_PARTIAL_CONTAINER_SCAN; } else if (pFromSubQuery->OptInfo.eOptType == QOPT_FULL_CONTAINER_SCAN) { // Swap the types and data cursors of each sub-query. Could do // a merge, but we would have to set up a temporary data cursor // to do it. It is actually faster this way. // NOTE: We do the swapping simply so that the other fields in // a sub-query are consistent with the eOptType. Although we // are simply going to free up the pFromSubQuery down below, it // is important that the sub-query always be consistently set up // because it may be that the routine which frees a subquery // will assume that it is. Swap_Data_Opt_Info: pTmpFSCursor = pIntoSubQuery->pFSDataCursor; f_memcpy( &TmpOptInfo, &pIntoSubQuery->OptInfo, sizeof( OPT_INFO)); pIntoSubQuery->pFSDataCursor = pFromSubQuery->pFSDataCursor; f_memcpy( &pIntoSubQuery->OptInfo, &pFromSubQuery->OptInfo, sizeof( OPT_INFO)); pFromSubQuery->pFSDataCursor = pTmpFSCursor; f_memcpy( &pFromSubQuery->OptInfo, &TmpOptInfo, sizeof( OPT_INFO)); // Must set pTmpFSCursor back to NULL or it will be deleted // below. pTmpFSCursor = NULL; } else { flmAssert( 0); } break; case QOPT_PARTIAL_CONTAINER_SCAN: // The from sub-query better be another partial // container scan or a single record retrieve. if (pFromSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ) { // Set up a file system data cursor if ((pTmpFSCursor = f_new FSDataCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = pTmpFSCursor->setupRange( pCursor->pDb, pCursor->uiContainer, pFromSubQuery->OptInfo.uiDrn, pFromSubQuery->OptInfo.uiDrn, NULL, NULL, NULL))) { goto Exit; } if (RC_BAD( rc = pIntoSubQuery->pFSDataCursor->unionRange( pTmpFSCursor))) { goto Exit; } } else if (pFromSubQuery->OptInfo.eOptType == QOPT_PARTIAL_CONTAINER_SCAN) { flmAssert( pFromSubQuery->pFSDataCursor != NULL); if (RC_BAD( rc = pIntoSubQuery->pFSDataCursor->unionRange( pFromSubQuery->pFSDataCursor))) { goto Exit; } } else if (pFromSubQuery->OptInfo.eOptType == QOPT_FULL_CONTAINER_SCAN) { // Swap the types and data cursors of each sub-query. Could do // a merge, but a swap is going to be faster. goto Swap_Data_Opt_Info; } else { flmAssert( 0); } break; case QOPT_FULL_CONTAINER_SCAN: // Don't need to do anything with pFromSubQuery // simply discard it below. break; default: flmAssert( 0); break; } // Add the costs, unless the from query was subsumed by the into query. if (!bFromSubQuerySubsumed) { pIntoSubQuery->OptInfo.uiCost += pFromSubQuery->OptInfo.uiCost; pIntoSubQuery->OptInfo.uiDrnCost += pFromSubQuery->OptInfo.uiDrnCost; } // Set *ppFromSubQuery to the next sub-query in the list and // clip out pFromSubQuery. *ppFromSubQuery = pFromSubQuery->pNext; flmClipSubQuery( pCursor, pFromSubQuery); Exit: if (pTmpFSCursor) { pTmpFSCursor->Release(); } return( rc); } /**************************************************************************** Desc: Optimizes the passed-in query. ****************************************************************************/ RCODE flmCurOptimize( CURSOR * pCursor, FLMBOOL bStratified) { RCODE rc = FERR_OK; FDB * pDb = NULL; SUBQUERY * pSubQuery; SUBQUERY * pTmpSubQuery; SUBQUERY * pContainerScanSubQuery = NULL; FLMBOOL bChoosingIndex; DB_STATS * pDbStats; QPREDICATE * pPredicateList = NULL; FLMUINT uiTotalPredicates = 0; F_Pool * pTempPool; void * pvMark; qOptTypes eOptType; FLMBOOL bFromSubQuerySubsumed = FALSE; FLMBOOL bHaveUserPredicates = FALSE; // Set up the operation control structure. pDb = pCursor->pDb; if (RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } // Verify that we have a valid container. if(( pCursor->uiContainer != FLM_DATA_CONTAINER) && ( pCursor->uiContainer != FLM_DICT_CONTAINER)) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, pCursor->uiContainer, NULL))) { goto Exit; } } if ((pDbStats = pDb->pDbStats) != NULL) { pDbStats->bHaveStats = TRUE; pDbStats->ui64NumCursors++; } pSubQuery = pCursor->pSubQueryList; pTempPool = &pDb->TempPool; pvMark = pTempPool->poolMark(); bChoosingIndex = (FLMBOOL)((pCursor->uiIndexNum == FLM_SELECT_INDEX) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); // Process all of the sub-queries. for(;;) { pTempPool->poolReset( pvMark); // First create the predicate list for the sub-query - so // that it only has to be done once. This call also verifies // all fields in the subquery. Only do if we successfully // stratified the query. if (bStratified) { // pSubQuery->pFSIndexCursor = NULL; Should already be initialized. // pSubQuery->pFSDataCursor = NULL; Should already be initialized. pSubQuery->uiLowDrn = 1; pSubQuery->uiHighDrn = 0xFFFFFFFF; // pSubQuery->uiNotEqualDrn = 0; Should already be initialized. if (RC_BAD( rc = flmSQGenPredicateList( pDb, pSubQuery, pTempPool, &pPredicateList, &uiTotalPredicates, &bHaveUserPredicates))) { goto Do_Bad_Rc; } } // If one of the sub-queries has to do a container scan, there is no // point in optimizing any of the sub-queries on indexes. We will just // merge all sub-queries into a single one so that we only do the // container scan once. if (pContainerScanSubQuery) { // Need to check the predicates, even though it is a container // scan so that we will call the searchCost() method. if (RC_BAD( rc = flmCheckUserPredicateCosts( pDb, pSubQuery, FALSE))) { goto Do_Bad_Rc; } if( RC_BAD( rc = flmMergeSubQueries( pCursor, &pSubQuery, pContainerScanSubQuery, TRUE))) { goto Do_Bad_Rc; } if( pSubQuery) { continue; } else { break; } } // If no index number has been set, find a best index to satisfy the // query. NOTE: as the best index is chosen, a field group will be // built for it in the subquery. if (bChoosingIndex) { if (bStratified) { if( RC_BAD( rc = flmSQChooseBestIndex( pCursor, pDb, 0, FALSE, pSubQuery, pTempPool, pPredicateList, uiTotalPredicates, bHaveUserPredicates))) { goto Do_Bad_Rc; } // See if there is a better embedded user predicate if (RC_BAD( rc = flmCheckUserPredicateCosts( pDb, pSubQuery, TRUE))) { goto Do_Bad_Rc; } } else { if (RC_BAD( rc= flmSQSetupFullContainerScan( pCursor, pSubQuery))) { goto Do_Bad_Rc; } // Must make this call so that each user predicate // is traversed. Must do AFTER setting up the full // container scan because we want to make sure we // add in the cost of doing any user predicates. if (RC_BAD( rc = flmCheckUserPredicateCosts( pDb, pSubQuery, FALSE))) { goto Do_Bad_Rc; } } } else { // If no index was specified, set up sub-query to do a container // scan. Otherwise, call flmSQChooseBestIndex to set up keys // for the specified index. if (!pCursor->uiIndexNum) { if (RC_BAD( rc= flmSQSetupFullContainerScan( pCursor, pSubQuery))) { goto Do_Bad_Rc; } } else { if( RC_BAD( rc = flmSQChooseBestIndex( pCursor, pDb, pCursor->uiIndexNum, !bStratified, pSubQuery, pTempPool, pPredicateList, uiTotalPredicates, bHaveUserPredicates))) { goto Do_Bad_Rc; } } // Call flmCheckUserPredicateCosts so that searchCost gets // called for every user predicate. Do only AFTER the setting // up of the sub-query so that the cost of processing the // predicates gets added in to the cost for the sub-query. if (RC_BAD( rc = flmCheckUserPredicateCosts( pDb, pSubQuery, FALSE))) { goto Do_Bad_Rc; } } // See if this sub-query should be merged with another one. eOptType = pSubQuery->OptInfo.eOptType; pTmpSubQuery = pCursor->pSubQueryList; switch (eOptType) { // If an index has been chosen or set, see if we need to merge // this sub-query with another subquery that has the same index. case QOPT_USING_INDEX: while (pTmpSubQuery != pSubQuery) { if (pTmpSubQuery->OptInfo.eOptType == QOPT_USING_INDEX && pTmpSubQuery->OptInfo.uiIxNum == pSubQuery->OptInfo.uiIxNum) { bFromSubQuerySubsumed = FALSE; goto Merge_SubQueries; } pTmpSubQuery = pTmpSubQuery->pNext; } // Didn't merge with any other sub-query, need to // get the index's language. if (pSubQuery->OptInfo.uiIxNum == FLM_DICT_INDEX) { pSubQuery->uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; } else { IXD * pIxd; // Get the index language. if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pSubQuery->OptInfo.uiIxNum, NULL, &pIxd))) { goto Exit; } pSubQuery->uiLanguage = pIxd->uiLanguage; } break; // If we optimized to a user predicate, merge it with any // sub-query that has the same user predicate. case QOPT_USING_PREDICATE: while (pTmpSubQuery != pSubQuery) { if (pTmpSubQuery->OptInfo.eOptType == QOPT_USING_PREDICATE && pTmpSubQuery->pPredicate == pSubQuery->pPredicate) { bFromSubQuerySubsumed = TRUE; goto Merge_SubQueries; } pTmpSubQuery = pTmpSubQuery->pNext; } pSubQuery->uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; break; // Merge single record retrieve sub-query with other // sub-query doing a single record retrieve of the same // record or a partial container scan. case QOPT_SINGLE_RECORD_READ: while (pTmpSubQuery != pSubQuery) { if ((pTmpSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ && pTmpSubQuery->OptInfo.uiDrn == pSubQuery->OptInfo.uiDrn) || (pTmpSubQuery->OptInfo.eOptType == QOPT_PARTIAL_CONTAINER_SCAN)) { bFromSubQuerySubsumed = (pTmpSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ) ? TRUE : FALSE; goto Merge_SubQueries; } pTmpSubQuery = pTmpSubQuery->pNext; } pSubQuery->uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; break; // Merge partial container scan sub-queries with other sub-query // doing a single record retrieve or a partial container scan. case QOPT_PARTIAL_CONTAINER_SCAN: while (pTmpSubQuery != pSubQuery) { if (pTmpSubQuery->OptInfo.eOptType == QOPT_SINGLE_RECORD_READ || pTmpSubQuery->OptInfo.eOptType == QOPT_PARTIAL_CONTAINER_SCAN) { bFromSubQuerySubsumed = FALSE; goto Merge_SubQueries; } pTmpSubQuery = pTmpSubQuery->pNext; } pSubQuery->uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; break; // Merge full container scan sub-query with ALL sub-queries // that have been processed so far. All subsequent // sub-queries will also be merged with this sub-query // (see above). case QOPT_FULL_CONTAINER_SCAN: pContainerScanSubQuery = pSubQuery; while (pTmpSubQuery != pSubQuery) { if (RC_BAD( rc = flmMergeSubQueries( pCursor, &pTmpSubQuery, pSubQuery, TRUE))) { goto Do_Bad_Rc; } } pSubQuery->uiLanguage = pDb->pFile->FileHdr.uiDefaultLanguage; break; default: // Should never hit this case! flmAssert( 0); } // Go to the next sub-query. if (pTmpSubQuery != pSubQuery) { Merge_SubQueries: if (RC_BAD( rc = flmMergeSubQueries( pCursor, &pSubQuery, pTmpSubQuery, bFromSubQuerySubsumed))) { goto Do_Bad_Rc; } } else { pSubQuery = pSubQuery->pNext; } if (!pSubQuery) { break; } continue; Do_Bad_Rc: if (rc == FERR_EMPTY_QUERY) { if (pSubQuery->pNext) { rc = FERR_OK; pSubQuery = pSubQuery->pNext; flmClipSubQuery( pCursor, pSubQuery->pPrev); continue; } else if (pSubQuery->pPrev) { rc = FERR_OK; flmClipSubQuery( pCursor, pSubQuery); pSubQuery->pPrev->pNext = NULL; break; } } goto Exit; } // Set cursor up so it always has a current sub-query. // Cannot do this until the end, because pSubQueryList // might change due to merges, etc. pCursor->pCurrSubQuery = pCursor->pSubQueryList; Exit: if (pDb) { (void)fdbExit( pDb); } return( rc); } libflaim-4.9.966/src/flindex.cpp0000644000175000017500000011220710510774540020014 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Routines for managing indexes. // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flindex.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd( IF_Thread * pThread); FSTATIC void stopBackgroundIndexThread( FDB * pDb, FLMUINT uiIndexNum, FLMBOOL bWait, FLMBOOL * pbStopped); FSTATIC RCODE flmIndexStatusCS( FDB * pDb, FLMUINT uiIndexNum, FINDEX_STATUS * pIndexStatus); /**************************************************************************** Desc : Return the status of the index. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmIndexStatus( HFDB hDb, FLMUINT uiIndexNum, FINDEX_STATUS * pIndexStatus) { RCODE rc = FERR_OK; FLMBOOL bStartedAutoTrans = FALSE; FLMUINT uiLastDrnIndexed; FDB * pDb = (FDB *)hDb; F_BKGND_IX * pBackgroundIx; FLMBOOL bSuspended; FLMBOOL bMutexLocked = FALSE; flmAssert( pIndexStatus != NULL); if( IsInCSMode( hDb)) { fdbInitCS( pDb); rc = flmIndexStatusCS( pDb, uiIndexNum, pIndexStatus); goto Exit; } if( RC_BAD( rc = fdbInit( (FDB *)hDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, &bStartedAutoTrans))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; pBackgroundIx = flmBackgroundIndexGet( pDb->pFile, uiIndexNum, TRUE); if( pBackgroundIx) { f_memcpy( pIndexStatus, &pBackgroundIx->indexStatus, sizeof( FINDEX_STATUS)); f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; flmAssert( pIndexStatus->uiIndexNum == uiIndexNum); } else { IXD * pIxd; FLMBOOL bTrackerIxSuspended; if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum,NULL, &pIxd, TRUE))) { goto Exit; } bSuspended = (pIxd->uiFlags & IXD_SUSPENDED) ? TRUE : FALSE; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Get the index state from the tracker if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, NULL, &uiLastDrnIndexed, NULL, &bTrackerIxSuspended))) { if( rc == FERR_NOT_FOUND) { rc = RC_SET( FERR_BAD_IX); } goto Exit; } // Sanity check #ifdef FLM_DEBUG if( pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_51 && bSuspended != bTrackerIxSuspended) { flmAssert( 0); } #endif // Populate the index status structure. f_memset( pIndexStatus, 0, sizeof( FINDEX_STATUS)); pIndexStatus->uiIndexNum = uiIndexNum; pIndexStatus->uiLastRecordIdIndexed = uiLastDrnIndexed; pIndexStatus->bSuspended = bSuspended; } Exit: if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } if( bStartedAutoTrans) { rc = flmEndAutoTrans( pDb, rc); } flmExit( FLM_INDEX_STATUS, pDb, rc); return( rc); } /**************************************************************************** Desc : Return the number of the next index. Pass in zero to get the first index. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmIndexGetNext( HFDB hDb, FLMUINT * puiIndexNum) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; FLMBOOL bStartedAutoTrans = FALSE; IXD * pIxd; flmAssert( puiIndexNum != NULL); if( IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_INDEX, FCS_OP_INDEX_GET_NEXT))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, *puiIndexNum))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } *puiIndexNum = Wire.getIndexId(); goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if( RC_BAD( rc = fdbInit( (FDB *)hDb, FLM_READ_TRANS, FDB_TRANS_GOING_OK, 0, &bStartedAutoTrans))) { goto Exit; } (void) fdictGetNextIXD( pDb->pDict, *puiIndexNum, &pIxd); if( pIxd && pIxd->uiIndexNum < FLM_RESERVED_TAG_NUMS) { *puiIndexNum = pIxd->uiIndexNum; } else { rc = RC_SET( FERR_EOF_HIT); } Exit: if( bStartedAutoTrans) { rc = flmEndAutoTrans( pDb, rc); } flmExit( FLM_INDEX_GET_NEXT, pDb, rc); return( rc); } /**************************************************************************** Desc : Suspend the selected index from doing any key updates on records that are equal or higher than the next record ID value in the container that the index references. If the index is offline then the background process will be suspended. If the index is online then it will be suspended. If the index is already suspended FERR_OK will be returned. A suspended index is not persistant if the database goes down. Notes: An update transaction will be started if necessary. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmIndexSuspend( HFDB hDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; IXD * pIxd; FLMUINT uiHighestRecId; FLMUINT uiContainerNum; FLMBOOL bSuspended; FLMBOOL bStartedTrans = FALSE; LFILE * pLFile; if( IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_INDEX, FCS_OP_INDEX_SUSPEND))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans))) { goto Exit; } // See if the index is valid if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if( pIxd->uiFlags & IXD_UNIQUE) { // Can't suspend unique indexes rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if( pIxd->uiFlags & IXD_SUSPENDED) { // Index is already suspended. goto Exit; } // Get the current index info from the tracker if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, &uiContainerNum, &uiHighestRecId, NULL, &bSuspended))) { goto Exit; } flmAssert( !bSuspended); // Get information about the container(s) being indexed if( !(pIxd->uiFlags & IXD_OFFLINE)) { if ((uiContainerNum = pIxd->uiContainerNum) == 0) { // The index was on-line and up-to-date. For an index that // crosses all containers, we will suspend on the highest DRN of // the FLM_DATA_CONTAINER. uiContainerNum = FLM_DATA_CONTAINER; } if( RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile))) { goto Exit; } uiHighestRecId = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiHighestRecId))) { goto Exit; } // Decrement uiHighestRecId by 1 to correctly reflect the // last record that was indexed. flmAssert( uiHighestRecId != 0); uiHighestRecId--; } // There may be a background thread still assigned to the // index even though the index may be "on-line." This is because // the background thread may have just commited a transaction that // transitioned the index from off-line to on-line, but the thread // has not yet exited (even though it will not do any more work // to update the index). We want to wait for the thread to terminate // before our transaction is allowed to commit. This is so that if // we immediately call resume, it won't find the yet-to-terminate // thread still running in the background. if( !(pDb->uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = flmAddToStopList( pDb, uiIndexNum))) { goto Exit; } } flmAssert( uiContainerNum != 0xFFFFFFFF); if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIndexNum, uiContainerNum, uiHighestRecId, TRANS_ID_OFFLINE, TRUE))) { goto Exit; } // Create a new dictionary if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } // Get a pointer to the new IXD if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } } // Update the IXDs flags so that the current update // transaction will see the correct state of the index. // Old read transactions will continue to use a prior // version of the dictionary. pIxd->uiFlags |= (IXD_SUSPENDED | IXD_OFFLINE); // Log the suspend packet to the RFL if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSuspendOrResume( uiIndexNum, RFL_INDEX_SUSPEND_PACKET))) { goto Exit; } Exit: if( bStartedTrans) { rc = flmEndAutoTrans( pDb, rc); } flmExit( FLM_INDEX_SUSPEND, pDb, rc); return( rc); } /**************************************************************************** Desc : If the index was suspended, restart the background process that will get the index up to date so that it will eventually be online. Returns FERR_OK with no change if the index is already online. Notes: An update transaction will be started if necessary. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmIndexResume( HFDB hDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; FDB * pDb = (FDB *)hDb; IXD * pIxd; FLMUINT uiLastContainerIndexed; FLMUINT uiLastDrnIndexed; FLMUINT uiOnlineTransId; FLMBOOL bStartedTrans = FALSE; if( IsInCSMode( hDb)) { fdbInitCS( pDb); CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); if( !pCSContext->bConnectionGood) { rc = RC_SET( FERR_BAD_SERVER_CONNECTION); goto Transmission_Error; } if( RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_INDEX, FCS_OP_INDEX_RESUME))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum))) { goto Transmission_Error; } // Send the "auto-online" flag (only needed for // backwards compatibility) if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_BOOLEAN, 1))) { goto Transmission_Error; } // Send a priority of high (only needed for // backwards compatibility) if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_NUMBER1, 1))) { goto Transmission_Error; } if( RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if( RC_BAD( rc = Wire.getRCode())) { goto Exit; } goto Exit; Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS, FDB_TRANS_GOING_OK, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans))) { goto Exit; } // See if the index is valid if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if( pIxd->uiFlags & IXD_UNIQUE) { // Can't suspend or resume unique indexes flmAssert( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))); rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } if( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))) { // Index is already on-line goto Exit; } // If we're in limited mode and this is an encrypted index, // it can't be resumed if (pDb->pFile->bInLimitedMode && pIxd->uiEncId) { rc = RC_SET( FERR_ENCRYPTION_UNAVAILABLE); goto Exit; } if( !(pIxd->uiFlags & IXD_SUSPENDED)) { // Index is not suspended. It is offline (see test // above), but a thread should already be building the // index, or it better be in the start list. #ifdef FLM_DEBUG if (flmBackgroundIndexGet( pDb->pFile, uiIndexNum, FALSE) == NULL) { F_BKGND_IX * pBackgroundIx; for( pBackgroundIx = pDb->pIxStartList; pBackgroundIx; pBackgroundIx = pBackgroundIx->pNext) { if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) { break; } } flmAssert( pBackgroundIx); } #endif goto Exit; } // Better not have a background thread running, or it better be // in the stop list - because its state shows suspended. #ifdef FLM_DEBUG if (flmBackgroundIndexGet( pDb->pFile, uiIndexNum, FALSE) != NULL) { F_BKGND_IX * pBackgroundIx; for( pBackgroundIx = pDb->pIxStopList; pBackgroundIx; pBackgroundIx = pBackgroundIx->pNext) { if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) { break; } } flmAssert( pBackgroundIx); } #endif // Get the tracker info if( RC_BAD( rc = flmGetIxTrackerInfo( pDb, uiIndexNum, &uiLastContainerIndexed, &uiLastDrnIndexed, &uiOnlineTransId, NULL))) { goto Exit; } // Update the tracker info so that the index state will // be changed to "unsuspended." if( RC_BAD( rc = flmSetIxTrackerInfo( pDb, uiIndexNum, uiLastContainerIndexed, uiLastDrnIndexed, uiOnlineTransId, FALSE))) { goto Exit; } // Add an entry to the start list so that an indexing thread // will be started when this transaction commits. if( !(pDb->uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = flmAddToStartList( pDb, uiIndexNum))) { goto Exit; } } // Create a new dictionary. if( !(pDb->uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = fdictCloneDict( pDb))) { goto Exit; } // Get a pointer to the new IXD if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } } // Update the IXDs flags so that the current update // transaction will see the correct state of the index. // Old read transactions will continue to use a prior // version of the dictionary. pIxd->uiFlags &= ~IXD_SUSPENDED; pIxd->uiFlags |= IXD_OFFLINE; // Log the resume packet to the RFL if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSuspendOrResume( uiIndexNum, RFL_INDEX_RESUME_PACKET))) { goto Exit; } Exit: if( bStartedTrans) { rc = flmEndAutoTrans( pDb, rc); } flmExit( FLM_INDEX_RESUME, pDb, rc); return( rc); } /**************************************************************************** Desc: Add the index to the stop list of background threads. ****************************************************************************/ RCODE flmAddToStopList( FDB * pDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; F_BKGND_IX * pBackgroundIx; F_BKGND_IX * pNextBackgroundIx; // We'd better not be replaying the RFL flmAssert( !(pDb->uiFlags & FDB_REPLAYING_RFL)); // First look in the start list and remove any index matches. // This is need if you add an index and drop // it within the same transaction. for( pBackgroundIx = pDb->pIxStartList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) { if( pNextBackgroundIx) { pNextBackgroundIx->pPrev = pBackgroundIx->pPrev; } if( pBackgroundIx->pPrev) { pBackgroundIx->pPrev->pNext = pNextBackgroundIx; } else { pDb->pIxStartList = pNextBackgroundIx; } f_free( &pBackgroundIx); } } // See if we already have an entry in the stop list for the index. There // is no reason to have the index in the list more than once. for( pBackgroundIx = pDb->pIxStopList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) { goto Exit; // Should return FERR_OK } } // Allocate and add the thread structure to the pFile thread list. if( RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), &pBackgroundIx))) { goto Exit; } pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; pBackgroundIx->pPrev = NULL; if( (pBackgroundIx->pNext = pDb->pIxStopList) != NULL) { pDb->pIxStopList->pPrev = pBackgroundIx; } pDb->pIxStopList = pBackgroundIx; Exit: return( rc); } /**************************************************************************** Desc: Add the index to the start list of background threads. ****************************************************************************/ RCODE flmAddToStartList( FDB * pDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; F_BKGND_IX * pBackgroundIx; F_BKGND_IX * pNextBackgroundIx; // We'd better not be replaying the RFL flmAssert( !(pDb->uiFlags & FDB_REPLAYING_RFL)); // Look in the start list to make sure we don't already // have an entry for this index. We don't want to // start more than one thread per index. The background // indexing code is not structured to handle multiple build // threads on the same index. // NOTE: We don't want to remove any entries in the stop // list corresponding to this index. The reason for this // is the index may have been deleted, re-added, deleted, // modified, etc. several times during the transaction. // We want to make sure that an existing background indexing // thread is terminated and a new one is started. The stop // list is always processed first at transaction commit time. // Then new indexing threads (in the start list) are started. for( pBackgroundIx = pDb->pIxStartList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if( pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) { goto Exit; // Should return FERR_OK } } // Allocate and add the thread structure to the pDb thread list. if( RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), &pBackgroundIx))) { goto Exit; } pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; pBackgroundIx->pPrev = NULL; if( (pBackgroundIx->pNext = pDb->pIxStartList) != NULL) { pDb->pIxStartList->pPrev = pBackgroundIx; } pDb->pIxStartList = pBackgroundIx; Exit: return( rc); } /**************************************************************************** Desc: After Abort and before we unlock, stop and start all indexing. ****************************************************************************/ void flmIndexingAfterAbort( FDB * pDb) { F_BKGND_IX * pStartIx; F_BKGND_IX * pStopIx; F_BKGND_IX * pNextIx; pStopIx = pDb->pIxStopList; pDb->pIxStopList = NULL; for( ; pStopIx; pStopIx = pNextIx) { pNextIx = pStopIx->pNext; f_free( &pStopIx); } pStartIx = pDb->pIxStartList; pDb->pIxStartList = NULL; for( ; pStartIx; pStartIx = pNextIx) { pNextIx = pStartIx->pNext; f_free( &pStartIx); } } /**************************************************************************** Desc: Stops a background indexing thread Notes: This routine DOES NOT assume that the global mutex is locked. It will lock and unlock the mutex as needed. ****************************************************************************/ FSTATIC void stopBackgroundIndexThread( FDB * pDb, FLMUINT uiIndexNum, FLMBOOL bWait, FLMBOOL * pbStopped) { F_BKGND_IX * pBackgroundIx; FLMUINT uiThreadId; FLMBOOL bMutexLocked = FALSE; if( pbStopped) { *pbStopped = FALSE; } for( ;;) { // Lock the global mutex if( !bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } // Get the background index if( (pBackgroundIx = flmBackgroundIndexGet( pDb->pFile, uiIndexNum, TRUE, &uiThreadId)) == NULL) { if( pbStopped) { *pbStopped = TRUE; } goto Exit; } // Set the thread's shutdown flag first. gv_FlmSysData.pThreadMgr->setThreadShutdownFlag( uiThreadId); // Unlock the global mutex f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // The thread may be waiting to start a transaction. pDb->pFile->pFileLockObj->timeoutLockWaiter( uiThreadId); pDb->pFile->pWriteLockObj->timeoutLockWaiter( uiThreadId); if( !bWait) { break; } // Wait for the thread to terminate f_sleep( 50); } Exit: if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } } /**************************************************************************** Desc: After commit and before we unlock, stop and start all indexing. ****************************************************************************/ void flmIndexingAfterCommit( FDB * pDb) { F_BKGND_IX * pStartIx; F_BKGND_IX * pStopIx; F_BKGND_IX * pNextIx; FLMBOOL bThreadsActive; FLMBOOL bStopped; // Signal all background indexing threads in the stop list // to shutdown. Poll until all have terminated. for( ;;) { bThreadsActive = FALSE; for( pStopIx = pDb->pIxStopList; pStopIx; pStopIx = pStopIx->pNext) { stopBackgroundIndexThread( pDb, pStopIx->indexStatus.uiIndexNum, FALSE, &bStopped); if( !bStopped) { bThreadsActive = TRUE; } } if( !bThreadsActive) { break; } f_sleep( 50); } // Now that all of the threads have been stopped, discard the stop list pStopIx = pDb->pIxStopList; pDb->pIxStopList = NULL; for( ; pStopIx; pStopIx = pNextIx) { pNextIx = pStopIx->pNext; f_free( &pStopIx); } // Start threads listed in the index start list. pStartIx = pDb->pIxStartList; pDb->pIxStartList = NULL; for( ; pStartIx; pStartIx = pNextIx) { pNextIx = pStartIx->pNext; (void)flmStartIndexBuild( pDb, pStartIx->indexStatus.uiIndexNum); f_free( &pStartIx); } } /**************************************************************************** Desc: Thread that will build an index in the background. Caller will create a pDb to use. This pDb must be freed at the conclusion of the routine. ****************************************************************************/ RCODE flmStartIndexBuild( FDB * pDb, FLMUINT uiIndexNum) { RCODE rc = FERR_OK; FLMUINT uiGMT; IXD * pIxd; F_BKGND_IX * pBackgroundIx = NULL; char szThreadName[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; f_timeGetSeconds( &uiGMT ); if( flmBackgroundIndexGet( pDb->pFile, uiIndexNum, FALSE) != NULL) { // There is already a background thread running on this index. flmAssert( 0); rc = RC_SET( FERR_FAILURE); goto Exit; } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } // Allocate the background thread and index status strucutures. if( RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_BKGND_IX), &pBackgroundIx))) { goto Exit; } pBackgroundIx->pFile = pDb->pFile; pBackgroundIx->indexStatus.bSuspended = FALSE; pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; pBackgroundIx->indexStatus.uiStartTime = uiGMT; pBackgroundIx->indexStatus.uiLastRecordIdIndexed = pIxd->uiLastDrnIndexed; pBackgroundIx->indexStatus.uiKeysProcessed = 0; pBackgroundIx->indexStatus.uiRecordsProcessed = 0; pBackgroundIx->indexStatus.uiTransactions = 0; pBackgroundIx->uiIndexingAction = FTHREAD_ACTION_INDEX_OFFLINE; pBackgroundIx->pPrev = NULL; pBackgroundIx->pNext = NULL; // Generate the thread name if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pDb->pFile->pszDbPath, szThreadName, szBaseName))) { goto Exit; } f_sprintf( (char *)szThreadName, "BldIX %u (%s)", (unsigned)uiIndexNum, szBaseName); // Start the thread in the background indexing thread group. // The new thread will cleanup pBackgroundIx on termination. if( RC_BAD( rc = f_threadCreate( NULL, flmBackgroundIndexBuildThrd, szThreadName, gv_uiBackIxThrdGroup, uiIndexNum, (void *)pBackgroundIx, NULL, 24000))) { goto Exit; } Exit: if( RC_BAD( rc) && pBackgroundIx) { f_free( &pBackgroundIx); } return( rc); } /**************************************************************************** Desc: Thread that will build an index in the background. Caller will create a pDb to use. This pDb must be freed at the conclusion of the routine. ****************************************************************************/ FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd( IF_Thread * pThread) { RCODE rc = FERR_OK; IXD * pIxd; F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); FLMBOOL bStartedTrans; FLMBOOL bDbInitialized; FLMUINT uiContainerNum; FLMUINT uiFirstDrn; FLMUINT uiIndexNum; FDB * pDb = NULL; FLMBOOL bForcedShutdown = FALSE; FLMBOOL bHitEnd; FINDEX_STATUS savedIxStatus; FlmRecord * pReusableRec = NULL; char szMsg[ 128]; FLMINT iErrorLine = 0; FLMBOOL bLimitedMode = FALSE; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); if( (pReusableRec = f_new FlmRecord) != NULL) { if( RC_BAD( pReusableRec->preallocSpace( 512, 1024 * 64))) { pReusableRec->Release(); pReusableRec = NULL; } } Loop_Again: rc = FERR_OK; uiIndexNum = pBackgroundIx->indexStatus.uiIndexNum; flmAssert( pThread->getThreadAppId() == uiIndexNum); bDbInitialized = FALSE; bStartedTrans = FALSE; pDb = NULL; // We could loop forever on flmOpenFile errors, check if we should exit. if( pThread->getShutdownFlag()) { bForcedShutdown = TRUE; goto Exit; } if( RC_BAD( rc = flmOpenFile( pBackgroundIx->pFile, NULL, NULL, NULL, 0, TRUE, NULL, NULL, pBackgroundIx->pFile->pszDbPassword, &pDb))) { // If the file is being closed, this is not an error. if( pBackgroundIx->pFile->uiFlags & DBF_BEING_CLOSED) { bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } flmAssert( pDb->pSFileHdl); bDbInitialized = TRUE; if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bStartedTrans))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } flmAssert( !bStartedTrans); pDb->uiFlags |= FDB_BACKGROUND_INDEXING; for(;;) { // Set the thread's status pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); // See if we should shut down. if( pThread->getShutdownFlag()) { bForcedShutdown = TRUE; break; } // Obtain the file lock flmAssert( !(pDb->uiFlags & FDB_HAS_FILE_LOCK)); if( RC_BAD( rc = pDb->pFile->pFileLockObj->lock( pDb->hWaitSem, TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, pDb->pDbStats ? &pDb->pDbStats->LockStats : NULL))) { if( rc == FERR_IO_FILE_LOCK_ERR) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } // The lock needs to be marked as implicit so that flmCommitDbTrans // will unlock the file and allow the next update transaction to // begin before all writes are complete. pDb->uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); // If there are higher priority waiters in the lock queue, // or we are being told to shut down, we want to relinquish. if( pThread->getShutdownFlag() || pDb->pFile->pFileLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { if (RC_BAD( rc = pDb->pFile->pFileLockObj->unlock())) { iErrorLine = (FLMINT)__LINE__; goto Exit; } pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); continue; } // Start an update transaction if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT, FLM_DONT_POISON_CACHE))) { if( rc == FERR_IO_FILE_LOCK_ERR) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); bForcedShutdown = TRUE; rc = FERR_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } bStartedTrans = TRUE; if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd, TRUE))) { // Index may have been deleted by another transaction, or // there may have been some other error. iErrorLine = (FLMINT)__LINE__; goto Exit; } // If we're running in limited mode, then we can't mess with encrypted // indexes. On the other hand, since the index is marked as offline, // but not suspended, this thread has to exist, or else it will cause // all kinds of problems elsewhere. So, in such a case, we will simply // sleep in an inifinite loop until the shutdown flag is set. // (We consider this acceptable becase running in limited mode is not // the norm, and Flaim probably won't be up for very long in this mode.) if( pDb->pFile->bInLimitedMode && pIxd->uiEncId) { bLimitedMode = TRUE; goto Exit; } // Look up the tracker info to determine where we need to being indexing if (RC_BAD( rc = flmGetIxTrackerInfo( pDb, pBackgroundIx->indexStatus.uiIndexNum, &uiContainerNum, &uiFirstDrn, NULL, &pBackgroundIx->indexStatus.bSuspended))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } // If the index is suspended, this thread should have been // shut down. The suspending thread will keep the database // locked until we exit. So, if we have the database locked, // the index better not be suspended. flmAssert( !pBackgroundIx->indexStatus.bSuspended && !(pIxd->uiFlags & IXD_SUSPENDED)); if (pIxd->uiContainerNum) { uiContainerNum = pIxd->uiContainerNum; if( uiFirstDrn == DRN_LAST_MARKER) { goto Exit; } } else { if( uiFirstDrn == DRN_LAST_MARKER && uiContainerNum == 0xFFFFFFFF) { goto Exit; } else { // The container number from the tracker record // may not be a real container. // Determine what the next actual container number is. if (uiContainerNum != FLM_DATA_CONTAINER) { while( uiContainerNum < pDb->pDict->uiIttCnt) { ITT * pItt = &pDb->pDict->pIttTbl [uiContainerNum]; if (ITT_IS_CONTAINER( pItt)) { break; } else { uiContainerNum++; } } if (uiContainerNum >= pDb->pDict->uiIttCnt) { uiContainerNum = FLM_DATA_CONTAINER; } } } } // Setup the DRN range we want to index. uiFirstDrn++; flmAssert( pIxd->uiLastDrnIndexed == uiFirstDrn - 1); // Set the thread's status pThread->setThreadStatus( "Indexing %u:%u", (unsigned)uiContainerNum, (unsigned)uiFirstDrn); // Read and index up to the highest drn (or record higher than uiEndDrn) // or until time runs out. The 500 is millisecs to take for the transaction. f_memcpy( &savedIxStatus, &pBackgroundIx->indexStatus, sizeof( FINDEX_STATUS)); if( RC_BAD( rc = flmIndexSetOfRecords( pDb, uiIndexNum, uiContainerNum, uiFirstDrn, DRN_LAST_MARKER, NULL, NULL, NULL, NULL, &pBackgroundIx->indexStatus, &bHitEnd, pThread, pReusableRec))) { // Lock the mutex while copying the saved index status back to // the main index status so that someone requesting the index status // won't see the status while the memcpy is in progress. f_mutexLock( gv_FlmSysData.hShareMutex); f_memcpy( &pBackgroundIx->indexStatus, &savedIxStatus, sizeof( FINDEX_STATUS)); f_mutexUnlock( gv_FlmSysData.hShareMutex); iErrorLine = (FLMINT)__LINE__; goto Exit; } if( pBackgroundIx->indexStatus.uiRecordsProcessed - savedIxStatus.uiRecordsProcessed) { if( RC_BAD( rc = pDb->pFile->pRfl->logIndexSet( uiIndexNum, uiContainerNum, uiFirstDrn, pBackgroundIx->indexStatus.uiLastRecordIdIndexed))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } } // Commit the transaction (even if we didn't do any indexing work). if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { iErrorLine = (FLMINT)__LINE__; goto Exit; } bStartedTrans = FALSE; pBackgroundIx->indexStatus.uiTransactions++; if( bHitEnd) { // flmIndexSetOfRecords brought the index on-line if( gv_FlmSysData.UpdateEvents.pEventCBList) { flmDoEventCallback( F_EVENT_UPDATES, F_EVENT_INDEXING_COMPLETE, (void *)uiIndexNum, (void *)0); } // Log a message flmLogIndexingProgress( uiIndexNum, 0); break; } } Exit: pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); if( bStartedTrans) { (void)flmAbortDbTrans( pDb); bStartedTrans = FALSE; } if( pDb && pDb->uiFlags & FDB_HAS_FILE_LOCK) { (void)pDb->pFile->pFileLockObj->unlock(); pDb->uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } if( bDbInitialized) { fdbExit( pDb); bDbInitialized = FALSE; } if( pDb) { (void)FlmDbClose( (HFDB *) &pDb); } if( RC_BAD(rc) && !bForcedShutdown) { if (rc == FERR_MEM || rc == FERR_IO_DISK_FULL || rc == FERR_MUST_WAIT_CHECKPOINT) { // Log the error f_sprintf( (char *)szMsg, "Background indexing thread %u (index %u)", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine); // Sleep a half second and try again. pThread->sleep( 500); goto Loop_Again; } else { f_sprintf( (char *)szMsg, "Background indexing thread %u (index %u) -- unrecoverable error.", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, (char *)szMsg, __FILE__, iErrorLine); } } if( pReusableRec) { flmAssert( pReusableRec->getRefCount() == 1); pReusableRec->Release(); } if( bLimitedMode) { flmAssert( RC_OK( rc)); for (;;) { if( pThread->getShutdownFlag()) { break; } pThread->sleep( 1000); } } // Set the thread's app ID to 0, so that it will not // be found now that the thread is terminating (we don't // want flmBackgroundIndexGet to find the thread). pThread->setThreadAppId( 0); // Free the background index structure f_mutexLock( gv_FlmSysData.hShareMutex); f_free( &pBackgroundIx); pThread->setParm1( NULL); f_mutexUnlock( gv_FlmSysData.hShareMutex); return( rc); } /**************************************************************************** Desc: Looks for a background indexing thread that is running with a matching action and value. Note: The shared semaphore must be locked on the outside while calling this routine and accessing anything within the F_BKGND_IX structure. ****************************************************************************/ F_BKGND_IX * flmBackgroundIndexGet( FFILE * pFile, FLMUINT uiIndexNum, FLMBOOL bMutexLocked, FLMUINT * puiThreadId) { RCODE rc = FERR_OK; IF_Thread * pThread; FLMUINT uiThreadId; F_BKGND_IX * pBackgroundIx = NULL; if( !bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); } uiThreadId = 0; for( ;;) { if( RC_BAD( rc = gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_uiBackIxThrdGroup, &uiThreadId))) { if( rc == FERR_NOT_FOUND) { rc = FERR_OK; break; } else { flmAssert( 0); } } if( pThread->getThreadAppId()) { F_BKGND_IX * pTmpIx = NULL; pTmpIx = (F_BKGND_IX *)pThread->getParm1(); if( pTmpIx->indexStatus.uiIndexNum == uiIndexNum && pTmpIx->pFile == pFile) { flmAssert( pThread->getThreadAppId() == uiIndexNum); pBackgroundIx = pTmpIx; pThread->Release(); if( puiThreadId) { *puiThreadId = uiThreadId; } break; } } pThread->Release(); } if( !bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); } return( pBackgroundIx); } /**************************************************************************** Desc : Return the status of the index (via C/S protocol) ****************************************************************************/ FSTATIC RCODE flmIndexStatusCS( FDB * pDb, FLMUINT uiIndexNum, FINDEX_STATUS * pIndexStatus) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = pDb->pCSContext; FCL_WIRE Wire( pCSContext, pDb); void * pvMark = pCSContext->pool.poolMark(); // Set the temporary pool Wire.setPool( &pCSContext->pool); // Send a request to do the update if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_INDEX, FCS_OP_INDEX_GET_STATUS))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_INDEX_ID, uiIndexNum))) { goto Transmission_Error; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (RC_BAD( rc = Wire.getRCode())) { goto Exit; } if( RC_BAD( rc = fcsExtractIndexStatus( Wire.getHTD(), pIndexStatus))) { goto Exit; } Exit: pCSContext->pool.poolReset( pvMark); return( rc); Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } libflaim-4.9.966/src/frebuild.cpp0000644000175000017500000021020610510774540020155 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Rebuild corrupted database. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flblddb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" typedef struct Container_Info { FLMUINT uiHighetstDrnFound; FLMUINT uiHighestNextDrnFound; FLMUINT uiNumNextDrnsFound; FLMBOOL bCountEstimated; } CONTAINER_INFO, * CONTAINER_INFO_p; typedef struct RECOV_DICT_REC { FlmRecord * pRec; FLMUINT uiBlkAddress; FLMUINT uiElmOffset; FLMBOOL bAdded; FLMBOOL bGotFromDataRec; RECOV_DICT_REC * pNext; } RECOV_DICT_REC; typedef struct RECOV_DICT_INFO { RECOV_DICT_REC * pRecovRecs; F_Pool pool; } RECOV_DICT_INFO; FSTATIC RCODE bldAdjustNextDrn( FDB * pDb, LFILE * pLFile, CONTAINER_INFO * pContInfo); FSTATIC RCODE bldRecovData( FDB * pDb, REBUILD_STATE * pRebuildState, CONTAINER_INFO * pContainerInfo, FLMBOOL bRecovDictRecs, FLMBOOL * pbStartedTransRV); FSTATIC RCODE bldCheckBlock( STATE_INFO * pStateInfo, HDR_INFO * pHdrInfo, FLMUINT uiBlkAddress, FLMUINT uiPrevBlkAddress, eCorruptionType * peCorruptionCode); FSTATIC RCODE bldExtractRecs( FDB * pDb, REBUILD_STATE * pRebuildState, LFILE * pLFile, CONTAINER_INFO * pContInfo, FLMUINT uiBlkAddress, LF_HDR * pLogicalFile, FLMBOOL bRecovDictRecs, RECOV_DICT_INFO ** ppRecovDictInfoRV); FSTATIC RCODE bldGetNextElm( REBUILD_STATE * pRebuildState, STATE_INFO * pStateInfo, FLMBOOL * pbGotNextElmRV, FLMBOOL * pbGotNewBlockRV); FSTATIC RCODE bldGetOneRec( FDB * pDb, REBUILD_STATE * pRebuildState, STATE_INFO * pStateInfo, FLMBOOL bRecovDictRecs, FLMBOOL * pbGotNewBlockRV, FLMBOOL * pbGotRecord); FSTATIC RCODE bldSaveRecovDictRec( FDB * pDb, RECOV_DICT_INFO ** ppRecovDictInfoRV, FlmRecord * pRecord, FLMUINT uiDrn, FLMBOOL bGotFromDataRec, FLMUINT uiBlkAddress, FLMUINT uiElmOffset); FSTATIC void bldFreeRecovDictInfo( RECOV_DICT_INFO * pRecovDictInfo); FSTATIC RCODE bldDoDict( FDB * pDb, REBUILD_STATE * pRebuildState, RECOV_DICT_INFO * pDictToDo, FLMBOOL * pbStartedTransRV); FSTATIC RCODE bldDetermineBlkSize( const char * pszSourceDbPath, const char * pszSourceDataDir, FLMUINT uiDbVersion, FLMUINT uiMaxFileSize, FLMUINT * puiBlkSizeRV, STATUS_HOOK fnStatusFunc, REBUILD_INFO * pCallbackData, void * AppArg); /*************************************************************************** Desc: This routine adds all of the recovered dictionary records to their appropriate dictionaries. ****************************************************************************/ FINLINE RCODE bldAddRecovDictRecs( FDB * pDb, REBUILD_STATE * pRebuildState, RECOV_DICT_INFO ** ppDictListRV, FLMBOOL * pbStartedTransRV) { RECOV_DICT_INFO * pDict; if( (pDict = *ppDictListRV) != NULL) { *ppDictListRV = NULL; return bldDoDict( pDb, pRebuildState, pDict, pbStartedTransRV); } return FERR_OK; } /*************************************************************************** Desc: Setup corrupt info structure prior to calling status callback ****************************************************************************/ FINLINE RCODE bldReportReason( REBUILD_STATE * pRebuildState, eCorruptionType eCorruption, FLMUINT uiErrBlkAddress, FLMUINT uiErrElmOffset, FLMUINT uiErrDrn, FLMUINT uiErrElmRecOffset, FLMUINT uiErrFieldNum) { RCODE rc = FERR_OK; if( pRebuildState->fnStatusFunc) { pRebuildState->CorruptInfo.eCorruption = eCorruption; pRebuildState->CorruptInfo.uiErrBlkAddress = uiErrBlkAddress; pRebuildState->CorruptInfo.uiErrElmOffset = uiErrElmOffset; pRebuildState->CorruptInfo.uiErrDrn = uiErrDrn; pRebuildState->CorruptInfo.uiErrElmRecOffset = uiErrElmRecOffset; pRebuildState->CorruptInfo.uiErrFieldNum = uiErrFieldNum; rc = (*pRebuildState->fnStatusFunc)( FLM_PROBLEM_STATUS, (void *)&pRebuildState->CorruptInfo, (void *)0, pRebuildState->AppArg); pRebuildState->CorruptInfo.eCorruption = FLM_NO_CORRUPTION; } return( rc); } /*************************************************************************** Desc: This routine determines whether or not a container should be done. ****************************************************************************/ FINLINE FLMBOOL bldDoContainer( FLMUINT uiContainerNum, FLMBOOL bDoDictContainers) { if (!bDoDictContainers) { switch (uiContainerNum) { case FLM_DICT_CONTAINER: return( FALSE); case FLM_DATA_CONTAINER: return( TRUE); default: if (uiContainerNum < FLM_RESERVED_TAG_NUMS) { return( TRUE); } else { return( FALSE); } } } return( TRUE); } /*************************************************************************** Desc: Reads through a database, extracts data records from all containers and puts them into the database specified by hDb. It is assumed that the new database has the same containers as the old database. ****************************************************************************/ RCODE flmDbRebuildFile( REBUILD_STATE * pRebuildState, // Rebuild state information. FLMBOOL bBadHeader // Was file's header or log header // information bad? ) { RCODE rc = FERR_OK; FLMUINT uiCurrLf; FLMBOOL bFdbInitialized = FALSE; FLMBOOL bStartedTrans = FALSE; LFILE * pLFile; FLMUINT uiTemp; FLMUINT uiContainerNum; CONTAINER_INFO * pContainerInfo = NULL; CONTAINER_INFO * pContInfo; FDB * pDb = (FDB *)pRebuildState->hDb; pRebuildState->CorruptInfo.eErrLocale = LOCALE_B_TREE; pRebuildState->CorruptInfo.uiErrLfType = LF_CONTAINER; bFdbInitialized = TRUE; if (RC_BAD( fdbInit( pDb, FLM_UPDATE_TRANS, FDB_DONT_RESET_DIAG, FLM_AUTO_TRANS | FLM_NO_TIMEOUT, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = f_allocAlignedBuffer( pRebuildState->pHdrInfo->FileHdr.uiBlockSize, &pRebuildState->pBlk))) { goto Exit; } // Do a first pass to recover any dictionary items that may not have // been added from the dictionary file that was passed into the rebuild // function. if( RC_BAD( rc = bldRecovData( pDb, pRebuildState, pContainerInfo, TRUE, &bStartedTrans))) { goto Exit; } // Reset records recovered to zero after dictionary pass. pRebuildState->CallbackData.uiRecsRecov = 0; // Allocate an array of structures to keep information on each // container. if( RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( CONTAINER_INFO) * pDb->pDict->uiLFileCnt), &pContainerInfo))) { goto Exit; } if( RC_BAD( rc = bldRecovData( pDb, pRebuildState, pContainerInfo, FALSE, &bStartedTrans))) { goto Exit; } // Adjust the next DRN for all containers so that they are at least as high // as the next DRN in the containers we were rebuilding from. for( uiCurrLf = 0, pContInfo = pContainerInfo, pLFile = (LFILE *)pDb->pDict->pLFileTbl; uiCurrLf < pDb->pDict->uiLFileCnt; uiCurrLf++, pLFile++, pContInfo++) { if (pLFile->uiLfType == LF_CONTAINER) { uiContainerNum = pLFile->uiLfNum; if (bldDoContainer( uiContainerNum, FALSE)) { if (RC_BAD( rc = bldAdjustNextDrn( pDb, pLFile, pContInfo))) { goto Exit; } } } } // Preserve other things in the log header that we ought // to try and preserve. if( !bBadHeader) { FLMBYTE * pucLogHdr = &pDb->pFile->ucUncommittedLogHdr [0]; // Set the commit count one less than the old database's // This is because it will be incremented if the transaction // successfully commits - which will make it exactly right. uiTemp = (FLMUINT)FB2UD( &pRebuildState->pLogHdr [LOG_COMMIT_COUNT]) - 1; if ((FLMUINT)FB2UD( &pucLogHdr [LOG_COMMIT_COUNT]) < uiTemp) { UD2FBA( (FLMUINT32)uiTemp, &pucLogHdr [LOG_COMMIT_COUNT]); } } // Signal the we are finished the rebuild if (pRebuildState->fnStatusFunc) { pRebuildState->CallbackData.iDoingFlag = REBUILD_FINISHED; pRebuildState->CallbackData.bStartFlag = TRUE; if (RC_BAD( rc = (*pRebuildState->fnStatusFunc)( FLM_REBUILD_STATUS, (void *)&pRebuildState->CallbackData, (void *)0, pRebuildState->AppArg))) { goto Exit; } pRebuildState->CallbackData.bStartFlag = FALSE; } Exit: if (pContainerInfo) { f_free( &pContainerInfo); } if (pRebuildState->pBlk) { f_freeAlignedBuffer( &pRebuildState->pBlk); } if (bStartedTrans) { if (rc == FERR_OK) { rc = flmCommitDbTrans( pDb, 0, TRUE); } else { (void)flmAbortDbTrans( pDb); } } if (bFdbInitialized) { fdbExit( pDb); } return( rc); } /*************************************************************************** Desc: This routine adjusts the next DRN for a container so that it is at least as high as the DRN in the file we are rebuilding from. ****************************************************************************/ FSTATIC RCODE bldAdjustNextDrn( FDB * pDb, LFILE * pLFile, CONTAINER_INFO * pContInfo) { RCODE rc; FLMUINT uiNextDrn; BTSK StackBuf [BH_MAX_LEVELS]; FLMBOOL bUsedStack = FALSE; // First see what the next DRN is currently set to uiNextDrn = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &uiNextDrn))) { goto Exit; } // Adjust the next DRN to be at least as high as the highest DRN // in the old databaseor the highest next DRN found int the // old database. if( uiNextDrn < pContInfo->uiHighetstDrnFound) { uiNextDrn = pContInfo->uiHighetstDrnFound + 1; } if( uiNextDrn < pContInfo->uiHighestNextDrnFound) { uiNextDrn = pContInfo->uiHighestNextDrnFound; } // Add either 100 or 1000 to next record number - just in case // things weren't as accurate as they should have been in the // old database. We want to make sure the next record // number is high enough to avoid accidentally reusing any // records which may have been used in the old database. uiNextDrn += ((pContInfo->uiNumNextDrnsFound != 1) ? 1000 : 100); // If there is no root block, the next DRN is stored inside the // logical file header. Otherwise, it is stored in the rightmost // element of the B-Tree - the one with a DRN of DRN_LAST_MARKER. if( pLFile->uiRootBlk == BT_END) { // LFILE is up to date from previously calling FSGetNxtDrn pLFile->uiNextDrn = uiNextDrn; if (RC_BAD( rc = flmLFileWrite( pDb, pLFile))) { goto Exit; } } else { BTSK * pStack = StackBuf; FLMBYTE KeyBuf [DIN_KEY_SIZ + 4]; FLMBYTE DrnMarker [DIN_KEY_SIZ]; FLMBYTE * pNextDrnBuf; // Set up the stack FSInitStackCache( &StackBuf [0], BH_MAX_LEVELS); bUsedStack = TRUE; pStack->pKeyBuf = KeyBuf; // Find the element whose DRN is DRN_LAST_MARKER f_UINT32ToBigEndian( (FLMUINT32)DRN_LAST_MARKER, DrnMarker); if( RC_BAD( rc = FSBtSearch( pDb, pLFile, &pStack, DrnMarker, DIN_KEY_SIZ, 0))) { goto Exit; } // Log the block before modifying it if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } pNextDrnBuf = CURRENT_ELM( pStack); pNextDrnBuf += BBE_GETR_KL( pNextDrnBuf ) + BBE_KEY; // Update with the next DRN value and dirty the block UD2FBA( (FLMUINT32)uiNextDrn, pNextDrnBuf ); } Exit: if( bUsedStack) { FSReleaseStackCache( StackBuf, BH_MAX_LEVELS, FALSE); } return( rc); } /*************************************************************************** Desc: This routine recovers all records in the database for all containers. *****************************************************************************/ FSTATIC RCODE bldRecovData( FDB * pDb, REBUILD_STATE * pRebuildState, CONTAINER_INFO * pContainerInfo, FLMBOOL bRecovDictRecs, FLMBOOL * pbStartedTransRV) { RCODE rc = FERR_OK; FLMUINT uiBlkAddress; FLMUINT uiBytesRead; F_SuperFileHdl * pSFileHdl = pRebuildState->pSFileHdl; HDR_INFO * pHdrInfo = pRebuildState->pHdrInfo; LF_HDR LogicalFile; LF_STATS LfStats; FLMBYTE * pucBlk = pRebuildState->pBlk; STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; REBUILD_INFO * pCallbackData = &pRebuildState->CallbackData; void * AppArg = pRebuildState->AppArg; FLMUINT uiBlockSize = pHdrInfo->FileHdr.uiBlockSize; FLMUINT uiCurrContainerNum = 0; LFILE * pCurrLFile = NULL; CONTAINER_INFO * pCurrContInfo = NULL; FLMUINT uiBlkContainerNum; LFILE * pBlkLFile; RECOV_DICT_INFO * pRecovDictInfo = NULL; FLMUINT uiFileNumber = 0; FLMUINT uiOffset = 0; FLMUINT uiMaxFileSize = pRebuildState->uiMaxFileSize; FLMUINT uiDbVersion = pRebuildState->pHdrInfo->FileHdr.uiVersionNum; // Read through all blocks in the file -- looking for leaf blocks // of containers. Read until we get an error or run out of file. LogicalFile.pLfStats = &LfStats; fnStatusFunc = pRebuildState->fnStatusFunc; pCallbackData->iDoingFlag = (FLMINT)((bRecovDictRecs) ? (FLMINT)REBUILD_RECOVER_DICT : (FLMINT)REBUILD_RECOVER_DATA); pCallbackData->bStartFlag = TRUE; pCallbackData->ui64BytesExamined = 0; for (;;) { if (uiOffset >= uiMaxFileSize || !uiFileNumber) { uiOffset = 0; uiFileNumber++; if( uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion)) { break; } } // Read the block into memory. if( RC_BAD( rc = pSFileHdl->readBlock( FSBlkAddress( uiFileNumber, uiOffset), uiBlockSize, pucBlk, &uiBytesRead))) { if( rc == FERR_IO_PATH_NOT_FOUND) { rc = FERR_OK; break; } if( rc == FERR_IO_END_OF_FILE) { if( !uiBytesRead) { // Set uiOffset so we will go to the next file. uiOffset = uiMaxFileSize; continue; } else { rc = FERR_OK; } } else { goto Exit; } } if( fnStatusFunc) { pCallbackData->ui64BytesExamined += (FLMUINT64)uiBlockSize; if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, (void *)pCallbackData, (void *)0, AppArg))) { goto Exit; } pCallbackData->bStartFlag = FALSE; } f_yieldCPU(); uiBlkAddress = (FLMUINT)GET_BH_ADDR( pucBlk); if ((FSGetFileOffset( uiBlkAddress) == uiOffset) && (BH_GET_TYPE( pucBlk) == BHT_LEAF) && (pucBlk [BH_LEVEL] == 0) && ((uiBlkContainerNum = (FLMUINT)FB2UW( &pucBlk [BH_LOG_FILE_NUM])) != 0) && (bldDoContainer( uiBlkContainerNum, bRecovDictRecs))) { if (uiBlkContainerNum != uiCurrContainerNum) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiBlkContainerNum, &pBlkLFile))) { if (rc != FERR_BAD_CONTAINER) goto Exit; rc = FERR_OK; goto Do_Next_Block; } else { uiCurrContainerNum = uiBlkContainerNum; f_memset( &LogicalFile, 0, sizeof( LF_HDR)); f_memset( &LfStats, 0, sizeof( LF_STATS)); LogicalFile.pLfStats = &LfStats; LogicalFile.pLFile = pCurrLFile = pBlkLFile; if (bRecovDictRecs) { pCurrContInfo = NULL; } else { pCurrContInfo = &pContainerInfo [pCurrLFile - ((LFILE *)pDb->pDict->pLFileTbl)]; } pRebuildState->CorruptInfo.uiErrLfNumber = uiBlkContainerNum; } } // Estimate the number of records in the block if we did't have // a count of records in the container. The loop ignores the // possibility that the block may be corrupted -- we are trying // to estimate what might have been in the block. It loops through // the elements in the block, looking for those which are marked // as FIRST elements. When it encounters one of these, it will // increment the counter. if (pCurrContInfo && !pCurrContInfo->bCountEstimated) { FLMUINT uiBlkOffset; FLMUINT uiEndOfBlock; FLMBYTE * pucElm; FLMUINT uiElmLen; FLMUINT uiElmKeyLen; FLMUINT uiElmPKCLen; FLMUINT uiNxtBlkAddr; FLMBOOL bIncremented; uiEndOfBlock = (FLMUINT)FB2UW( &pucBlk [BH_ELM_END]); uiNxtBlkAddr = (FLMUINT)FB2UD( &pucBlk [BH_NEXT_BLK]); // If uiEndOfBlock is too big, adjust it down so we can // estimate. if (uiEndOfBlock > uiBlockSize) { uiEndOfBlock = uiBlockSize; } uiBlkOffset = BH_OVHD; bIncremented = FALSE; while (uiBlkOffset < uiEndOfBlock) { pucElm = &pucBlk [uiBlkOffset]; uiElmLen = (FLMUINT)(BBE_LEN( pucElm)); uiElmKeyLen = (FLMUINT)(BBE_GET_KL( pucElm)); uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( pucElm)); // If it is a FIRST element, and it is NOT the LEM // element, increment the count. if ((BBE_IS_FIRST( pucElm)) && ((uiElmLen != BBE_LEM_LEN) || (uiElmKeyLen > 0) || (uiElmPKCLen > 0) || (uiBlkOffset + uiElmLen != uiEndOfBlock) || (uiNxtBlkAddr != BT_END))) { pCallbackData->uiTotRecs++; bIncremented = TRUE; } uiBlkOffset += uiElmLen; } // Decrement the estimated count by one if it is a last // block - one of the elements in a last block should // always be a DRN_LAST_MARKER element. if ((bIncremented) && (uiNxtBlkAddr == BT_END)) { pCallbackData->uiTotRecs--; } } // See if we can now extract any records from the block uiBlkAddress = FSBlkAddress( uiFileNumber, uiOffset); if( RC_BAD( rc = bldExtractRecs( pDb, pRebuildState, pCurrLFile, pCurrContInfo, uiBlkAddress, &LogicalFile, bRecovDictRecs, &pRecovDictInfo))) { goto Exit; } } Do_Next_Block: uiOffset += uiBlockSize; } // If we are recovering dictionary records, we need to now add them // into the appropriate dictionaries. if( bRecovDictRecs) { if( RC_BAD( rc = bldAddRecovDictRecs( pDb, pRebuildState, &pRecovDictInfo, pbStartedTransRV))) { goto Exit; } } Exit: bldFreeRecovDictInfo( pRecovDictInfo); return( rc); } /*************************************************************************** Desc: This routine checks a few things in the block header and then attempts to decrypt the block so we can read through its elements. Ret: 0 if block is OK, error code otherwise *****************************************************************************/ FSTATIC RCODE bldCheckBlock( STATE_INFO * pStateInfo, HDR_INFO * pHdrInfo, FLMUINT uiBlkAddress, FLMUINT uiPrevBlkAddress, eCorruptionType * peCorruptionCode) { RCODE rc = FERR_OK; // Determine where the end of block is -- make sure it is a legal value pStateInfo->uiBlkAddress = uiBlkAddress; // Must force the block address to be correct so this check will not // fail. We already have previously verified that the offset matches, and // we know that we got it from the right block file. However, the low // byte of the block header will not be correct because until we do a // block checksum calculation, it will hold the low checksum byte. // We don't do a block checksum calculation during rebuild, so at this // point, it still holds the low checksum byte. SET_BH_ADDR( pStateInfo->pBlk, (FLMUINT32)uiBlkAddress); if ((*peCorruptionCode = flmVerifyBlockHeader( pStateInfo, NULL, pHdrInfo->FileHdr.uiBlockSize, 0, uiPrevBlkAddress, FALSE, TRUE)) != FLM_NO_CORRUPTION) { goto Exit; } pStateInfo->uiElmOffset = BH_OVHD; // Decrypt the block if necessary to check the elements. // If encryption is not enabled for the database, or the block // is already decrypted, do nothing. Exit: return( rc); } /*************************************************************************** Desc: This routine traverses all elements within a block, extracting whatever records it can from the block. *****************************************************************************/ FSTATIC RCODE bldExtractRecs( FDB * pDb, REBUILD_STATE * pRebuildState, LFILE * pLFile, CONTAINER_INFO * pContInfo, FLMUINT uiBlkAddress, LF_HDR * pLogicalFile, FLMBOOL bRecovDictRecs, RECOV_DICT_INFO ** ppRecovDictInfoRV) { RCODE rc = FERR_OK; eCorruptionType eCorruptionCode; FLMBOOL bGotNewBlock; FLMUINT uiSaveElmOffset; STATE_INFO * pStateInfo = pRebuildState->pStateInfo; FLMBOOL bStateInitialized = TRUE; STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; void * AppArg = pRebuildState->AppArg; FLMBOOL bGotRecord; // Setup the STATE variable for processing through the block flmInitReadState( pStateInfo, &bStateInitialized, pRebuildState->pHdrInfo->FileHdr.uiVersionNum, pDb, pLogicalFile, 0xFF, BHT_LEAF, pRebuildState->pKeyBuffer); pStateInfo->pBlk = pRebuildState->pBlk; if( (RC_BAD( rc = bldCheckBlock( pStateInfo, pRebuildState->pHdrInfo, uiBlkAddress, 0, &eCorruptionCode))) || (eCorruptionCode != FLM_NO_CORRUPTION)) { if( eCorruptionCode != FLM_NO_CORRUPTION) { (void)bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, 0, 0, 0xFFFF, 0); } goto Exit; } // Go through each element in the block, extracting whatever data // we can. The loop quits if it finds an inconsistency in the block. bGotNewBlock = FALSE; while ((pStateInfo->uiElmOffset < pStateInfo->uiEndOfBlock) && (!bGotNewBlock)) { bGotRecord = FALSE; if (pRebuildState->pRecord) { pRebuildState->pRecord->clear(); } uiSaveElmOffset = pStateInfo->uiElmOffset; // Get the element and check it if( (eCorruptionCode = flmVerifyElement( pStateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) { if( RC_BAD( rc = bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0))) { goto Exit; } } // Skip continuation elements -- at this point, it should only // be continuation elements which are at the first of the block. // This is because the bldGetOneRec routine will traverse through // continuation elements for a record. else if ((BBE_IS_FIRST( pStateInfo->pElm)) && (pStateInfo->uiCurKeyLen)) { if (pStateInfo->uiElmDrn == DRN_LAST_MARKER) { if (pStateInfo->uiElmRecLen == 4) { FLMUINT uiNxtDrn = (FLMUINT)FB2UD( pStateInfo->pElmRec); if (pContInfo) { pContInfo->uiNumNextDrnsFound++; if (uiNxtDrn > pContInfo->uiHighestNextDrnFound) { pContInfo->uiHighestNextDrnFound = uiNxtDrn; } } } } // If the element is the FIRST element in a record, // see if we can extract a record from it. else if( RC_BAD( rc = bldGetOneRec( pDb, pRebuildState, pStateInfo, bRecovDictRecs, &bGotNewBlock, &bGotRecord))) { // If we didn't have enough memory to retrieve the record, just // skip it. if( rc == FERR_MEM) { bGotRecord = FALSE; if (pRebuildState->pRecord) { pRebuildState->pRecord->clear(); } rc = FERR_OK; } else { goto Exit; } } } // If we didn't get a data record, there was some inconsistency // we encountered, so we continue to the next element in the block. if( bGotRecord) { f_yieldCPU(); if( pContInfo) { if( pStateInfo->uiElmDrn > pContInfo->uiHighetstDrnFound) { pContInfo->uiHighetstDrnFound = pStateInfo->uiElmDrn; } } // Add the record to the database if( bRecovDictRecs) { FlmRecord * pDictRec; FLMUINT uiDictDrn = 0; FLMBOOL bGotFromDataRec; CHK_RECORD ChkRec; f_memset( &ChkRec, 0, sizeof( ChkRec)); // If this is not a dictionary container record, need to do the // callback to see if there is dictionary information in this // record. if( pLFile->uiLfNum == FLM_DICT_CONTAINER) { pDictRec = pRebuildState->pRecord; uiDictDrn = pStateInfo->uiElmDrn; bGotFromDataRec = FALSE; } else { if( !fnStatusFunc) { pDictRec = NULL; } else { ChkRec.pRecord = pRebuildState->pRecord; ChkRec.uiContainer = pLFile->uiLfNum; ChkRec.uiDrn = pStateInfo->uiElmDrn; if( RC_BAD( rc = (*fnStatusFunc)( FLM_CHECK_RECORD_STATUS, (void *)&ChkRec, (void *)0, AppArg))) { if( ChkRec.pDictRecSet) { ChkRec.pDictRecSet->Release(); ChkRec.pDictRecSet = NULL; } goto Exit; } if( ChkRec.pDictRecSet) { if( (pDictRec = ChkRec.pDictRecSet->first()) != NULL) { uiDictDrn = pDictRec->getID(); } } else { pDictRec = NULL; } bGotFromDataRec = TRUE; } } if( !pDictRec) { rc = FERR_OK; } else { for (;;) { rc = bldSaveRecovDictRec( pDb, ppRecovDictInfoRV, pDictRec, uiDictDrn, bGotFromDataRec, uiBlkAddress, uiSaveElmOffset); if( RC_BAD( rc) || !bGotFromDataRec) { break; } // If bGotFromDataRec is TRUE, there may be more than // one that was returned. if( (pDictRec = ChkRec.pDictRecSet->next()) == NULL) { break; } uiDictDrn = pDictRec->getID(); } } if( ChkRec.pDictRecSet) { ChkRec.pDictRecSet->Release(); ChkRec.pDictRecSet = NULL; } } else if( pLFile->uiLfNum == FLM_TRACKER_CONTAINER) { rc = FSRecUpdate( pDb, pLFile, pRebuildState->pRecord, pStateInfo->uiElmDrn, REC_UPD_ADD); } else { rc = flmAddRecord( pDb, pLFile, &pStateInfo->uiElmDrn, pRebuildState->pRecord, TRUE, FALSE, FALSE, FALSE, NULL); if( RC_OK(rc) && fnStatusFunc ) { CHK_RECORD ChkRec; f_memset( &ChkRec, 0, sizeof( ChkRec)); ChkRec.pRecord = pRebuildState->pRecord; ChkRec.uiContainer = pLFile->uiLfNum; ChkRec.uiDrn = pStateInfo->uiElmDrn; rc = (*fnStatusFunc)( FLM_EXAMINE_RECORD_STATUS, (void *)&ChkRec, (void *)0, AppArg); if (ChkRec.pDictRecSet) { ChkRec.pDictRecSet->Release(); ChkRec.pDictRecSet = NULL; } } if( pRebuildState->pRecord->isReadOnly()) { pRebuildState->pRecord->Release(); pRebuildState->pRecord = NULL; } } if( RC_BAD( rc)) { if( (rc == FERR_EXISTS) || (rc == FERR_NOT_UNIQUE)) { eCorruptionCode = (rc == FERR_EXISTS) ? FLM_REBUILD_REC_EXISTS : FLM_REBUILD_KEY_NOT_UNIQUE; if (RC_BAD( rc = bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, uiSaveElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0))) { goto Exit; } } else { goto Exit; } } else { // Make sure the tempory memory is freed. // Eats up the memory during a rebuild. pDb->TempPool.poolReset(); if (!bRecovDictRecs) { pRebuildState->CallbackData.uiRecsRecov++; } } } pStateInfo->uiElmOffset += pStateInfo->uiElmLen; } Exit: return( rc); } /*************************************************************************** Desc: This routine gets the next element for a record. If necessary, it will try to go to the next block. Ret: TRUE if we got the next element, FALSE otherwise. *****************************************************************************/ FSTATIC RCODE bldGetNextElm( REBUILD_STATE * pRebuildState, STATE_INFO * pStateInfo, FLMBOOL * pbGotNextElmRV, FLMBOOL * pbGotNewBlockRV) { RCODE rc = FERR_OK; FLMUINT uiSaveDrn; FLMUINT uiBytesRead; FLMUINT uiBlkAddress; FLMBYTE * pBlk = pStateInfo->pBlk; HDR_INFO * pHdrInfo = pRebuildState->pHdrInfo; eCorruptionType eCorruptionCode; FLMUINT uiSaveBlkAddress = pStateInfo->uiBlkAddress; *pbGotNextElmRV = FALSE; uiSaveDrn = pStateInfo->uiElmDrn; // See if we need to go to the next block to get the element pStateInfo->uiElmOffset += pStateInfo->uiElmLen; if (pStateInfo->uiElmOffset >= pStateInfo->uiEndOfBlock) { // Get the next block *pbGotNewBlockRV = TRUE; uiBlkAddress = (FLMUINT)FB2UD( &pBlk [BH_NEXT_BLK]); rc = pRebuildState->pSFileHdl->readBlock( uiBlkAddress, pHdrInfo->FileHdr.uiBlockSize, pBlk, &uiBytesRead); if( uiBytesRead < pHdrInfo->FileHdr.uiBlockSize) { rc = RC_SET( FERR_IO_END_OF_FILE); } if( RC_BAD( rc)) { RCODE TempRc; if( rc == FERR_IO_END_OF_FILE || rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } if( RC_BAD( TempRc = bldReportReason( pRebuildState, FLM_BAD_BLK_HDR_NEXT, uiSaveBlkAddress, 0, 0, 0xFFFF, 0))) { if( RC_OK( rc)) { rc = TempRc; } } goto Exit; } // Make sure it is the right type of block else if( (RC_BAD( rc = bldCheckBlock( pStateInfo, pRebuildState->pHdrInfo, uiBlkAddress, uiSaveBlkAddress, &eCorruptionCode))) || (eCorruptionCode != FLM_NO_CORRUPTION)) { if (eCorruptionCode != FLM_NO_CORRUPTION) { (void)bldReportReason( pRebuildState, eCorruptionCode, uiBlkAddress, 0, 0, 0xFFFF, 0); } goto Exit; } } // Verify other things about the element if( (eCorruptionCode = flmVerifyElement( pStateInfo, FLM_CHK_FIELDS)) != FLM_NO_CORRUPTION) { rc = bldReportReason( pRebuildState, eCorruptionCode, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0); goto Exit; } // This had better not be a LEM element if( pStateInfo->uiCurKeyLen == 0) { rc = bldReportReason( pRebuildState, FLM_BAD_LEM, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0); goto Exit; } // This element had better not be the first element if( BBE_IS_FIRST( pStateInfo->pElm)) { rc = bldReportReason( pRebuildState, FLM_BAD_FIRST_ELM_FLAG, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0); goto Exit; } // Must stay on the same DRN while processing the record if (pStateInfo->uiElmDrn != uiSaveDrn) { rc = bldReportReason( pRebuildState, FLM_BAD_CONT_ELM_KEY, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, 0xFFFF, 0); goto Exit; } *pbGotNextElmRV = TRUE; Exit: return( rc); } /*************************************************************************** Desc: This routine retrieves one record from a block -- at the current element. It will follow continuation elements if necessary. The record is returned in pRebuildState->pRecord. A NULL is returned if some inconsistency was encountered. *****************************************************************************/ FSTATIC RCODE bldGetOneRec( FDB * pDb, REBUILD_STATE * pRebuildState, STATE_INFO * pStateInfo, FLMBOOL bRecovDictRecs, FLMBOOL * pbGotNewBlockRV, FLMBOOL * pbGotRecord) { RCODE rc = FERR_OK; FLMBYTE * pValue; FLMBYTE * pData; FLMBYTE * pTempValue; eCorruptionType eCorruptionCode; FlmRecord * pRecord = NULL; FLMBOOL bAllocatedRecord = FALSE; FLMBOOL bFieldDone; FLMUINT uiSaveElmRecOffset; FLMBOOL bGotNextElm; FLMBOOL bSkipField = FALSE; FLMBOOL bSkippedField = FALSE; FLMUINT uiSkipToLevel = 0; // Setup things to get the record if( pRebuildState->pRecord) { pRebuildState->pRecord->clear(); pRecord = pRebuildState->pRecord; } pValue = NULL; pTempValue = NULL; *pbGotRecord = FALSE; // Follow elements until we have traversed all continuation elements // or until we discover some inconsistency. for (;;) { uiSaveElmRecOffset = pStateInfo->uiElmRecOffset; if ((eCorruptionCode = flmVerifyElmFOP( pStateInfo)) != FLM_NO_CORRUPTION) { if ((bRecovDictRecs) && (eCorruptionCode == FLM_BAD_ELM_FLD_NUM)) { bSkipField = TRUE; bSkippedField = TRUE; uiSkipToLevel = pStateInfo->uiFieldLevel; bFieldDone = (pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) ? TRUE : FALSE; } else { rc = bldReportReason( pRebuildState, eCorruptionCode, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, uiSaveElmRecOffset, pStateInfo->uiFieldNum); goto Exit; } } // See if we are starting a new field if( (pStateInfo->uiFOPType == FLM_FOP_STANDARD) || (pStateInfo->uiFOPType == FLM_FOP_OPEN) || (pStateInfo->uiFOPType == FLM_FOP_TAGGED) || (pStateInfo->uiFOPType == FLM_FOP_NO_VALUE) || (pStateInfo->uiFOPType == FLM_FOP_ENCRYPTED)) { // If we skipped a previous field, see if this field is a child // or grandchild, etc. of the field that was skipped. If so, we skip // this field as well. We stop skipping when we come to a field // that is a sibling or aunt/uncle to the field that was skipped. if( (bSkipField) && (pStateInfo->uiFieldLevel <= uiSkipToLevel)) { bSkipField = FALSE; } // See if field should be skipped. if( !bSkipField) { FLMUINT uiState; FLMUINT uiDictFieldType; if( RC_BAD( fdictGetField( pDb->pDict, pStateInfo->uiFieldNum, &uiDictFieldType, NULL, &uiState))) { bSkipField = TRUE; bSkippedField = TRUE; uiSkipToLevel = pStateInfo->uiFieldLevel; } } // If we aren't skipping the field, allocate space for it if( !bSkipField) { void * pvField; if( !pRecord) { if( (pRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } bAllocatedRecord = TRUE; } // Create a new field in the record. if( RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel, pStateInfo->uiFieldNum, pStateInfo->uiFieldType, &pvField))) { goto Exit; } pStateInfo->pvField = pvField; // Allocate space for the field's value and set the field's type. if( !pStateInfo->uiFieldLen) { pValue = NULL; } else { if (!pStateInfo->uiEncId) { if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, pStateInfo->uiFieldType, pStateInfo->uiFieldLen, 0, 0, 0, &pValue, NULL))) { goto Exit; } } else { if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, pStateInfo->uiFieldType, pStateInfo->uiFieldLen, pStateInfo->uiEncFieldLen, pStateInfo->uiEncId, FLD_HAVE_ENCRYPTED_DATA, &pData, &pValue))) { goto Exit; } } } pTempValue = pValue; } bFieldDone = ((!pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) || (pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen)) ? TRUE : FALSE; } else if( (pStateInfo->uiFOPType == FLM_FOP_JUMP_LEVEL) || (pStateInfo->uiFOPType == FLM_FOP_NEXT_DRN)) { bFieldDone = FALSE; } else { bFieldDone = ((!pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) || (pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen)) ? TRUE : FALSE; } // See if we got some data with this FOP if( pValue && pStateInfo->uiFOPDataLen && !bSkipField && pStateInfo->uiFOPType != FLM_FOP_REC_INFO) { f_memcpy( pTempValue, pStateInfo->pFOPData, pStateInfo->uiFOPDataLen); pTempValue += pStateInfo->uiFOPDataLen; } // See if we are done with this field if( bFieldDone) { // Verify the value if( pStateInfo->uiFieldLen) { if( bSkipField || pStateInfo->uiFieldType == 0xFF) { eCorruptionCode = FLM_NO_CORRUPTION; } else { if (!pStateInfo->uiEncId) { eCorruptionCode = flmVerifyField( pStateInfo, pValue, pStateInfo->uiFieldLen, pStateInfo->uiFieldType); } // Encrypted fields are never supposed to be found in the dictionary, so // if we do not have an ITT table, we will not be able to decrypt this field. // Therefore, we should be in our first pass. This field / record will // not be used to recover the dictionary. else if (pDb->pFile->pDictList->pIttTbl) { if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, pStateInfo->pvField, pStateInfo->uiEncId, &pDb->TempPool))) { goto Exit; } eCorruptionCode = flmVerifyField( pStateInfo, pData, pStateInfo->uiFieldLen, pStateInfo->uiFieldType); } } if( eCorruptionCode != FLM_NO_CORRUPTION) { rc = bldReportReason( pRebuildState, eCorruptionCode, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, uiSaveElmRecOffset, pStateInfo->uiFieldNum); goto Exit; } } // Set pValue to NULL for the next field pValue = pTempValue = NULL; } // See if we are at the end of this element if( pStateInfo->uiElmRecOffset == pStateInfo->uiElmRecLen) { // If the last element flag is set, we are done with this // record and can return - unless we have a half processed field. if( BBE_IS_LAST( pStateInfo->pElm)) { if (!bFieldDone) { rc = bldReportReason( pRebuildState, FLM_BAD_LAST_ELM_FLAG, pStateInfo->uiBlkAddress, pStateInfo->uiElmOffset, pStateInfo->uiElmDrn, uiSaveElmRecOffset, pStateInfo->uiFieldNum); } else { pRebuildState->pRecord = pRecord; *pbGotRecord = TRUE; pRecord = NULL; bAllocatedRecord = FALSE; } goto Exit; } else { // Attempt to get the next element if( (RC_BAD( rc = bldGetNextElm( pRebuildState, pStateInfo, &bGotNextElm, pbGotNewBlockRV))) || !bGotNextElm) { // Need to set bSkippedField to TRUE so that cleanup will // occur at the bottom of this routine. bSkippedField = TRUE; goto Exit; } } } } Exit: if( bSkippedField || RC_BAD( rc)) { if (pRebuildState->pRecord) { pRebuildState->pRecord->Release(); pRebuildState->pRecord = NULL; } *pbGotRecord = FALSE; } if( pRecord && bAllocatedRecord) { pRecord->Release(); } return( rc); } /*************************************************************************** Desc: This routine recovers any dictionary records from the corrupted file that it can recover, attempting to find any that may not have been in the dictionary file that was passed into the rebuild routines. *****************************************************************************/ FSTATIC RCODE bldSaveRecovDictRec( FDB * pDb, // FDB for newly created database RECOV_DICT_INFO ** ppRecovDictInfoRV, // Recover info FlmRecord * pRecord, // Dictionary Record FLMUINT uiDrn, // Dict. record DRN FLMBOOL bGotFromDataRec, // Was this dictionary record // extracted from a data record? FLMUINT uiBlkAddress, // Block the record was // recovered from FLMUINT uiElmOffset // Offset in block the // record was recovered // from ) { RCODE rc = FERR_OK; RECOV_DICT_INFO * pRecovDictInfo; RECOV_DICT_REC * pRecovDictRec; RECOV_DICT_REC * pNewDictRec = NULL; RECOV_DICT_REC * pPrevDictRec; FLMUINT uiRecType = pRecord->getFieldID( pRecord->root()); FlmRecord * pDummyRec; LFILE * pDictLFile; // Ignore any record that is not a dictionary record or // an unregistered (comment) record. if( uiRecType < FLM_RESERVED_TAG_NUMS) { goto Exit; } // Determine if the record already exists in the dictionary. If it // does, simply ignore this record - the record that was loaded from // the dictionary file takes precedence. if( RC_OK( rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, &pDictLFile))) { pDummyRec = NULL; rc = FSReadRecord( pDb, pDictLFile, uiDrn, &pDummyRec, NULL, NULL); if( pDummyRec) { pDummyRec->Release(); } } if( rc != FERR_NOT_FOUND) { goto Exit; } rc = FERR_OK; // If the dictionary was not found in the list, create an entry in the // list. if( (pRecovDictInfo = *ppRecovDictInfoRV) == NULL) { if( RC_BAD( rc = f_calloc( (FLMUINT)sizeof( RECOV_DICT_INFO), &pRecovDictInfo))) { goto Exit; } pRecovDictInfo->pool.poolInit( 512); *ppRecovDictInfoRV = pRecovDictInfo; } // Determine if the record is already in our list of records. If so, // simply ignore it, or remove the old one. The old one is removed if // it is "less desirable to recover". Desirability of record types is // as follows: // // 1. Field Definitions && Template definitions take top priority // 2. Container Definitions // 3. Area Definitions // 4. Reserve Definitions // 5. Index Definitions // 6. Other pPrevDictRec = NULL; pRecovDictRec = pRecovDictInfo->pRecovRecs; while( pRecovDictRec) { if( pRecovDictRec->pRec->getID() != uiDrn) { pPrevDictRec = pRecovDictRec; pRecovDictRec = pRecovDictRec->pNext; } else if( bGotFromDataRec && !pRecovDictRec->bGotFromDataRec) { // Throw away this record and keep the one that was previously // recovered from the dictionary container. Records recovered // from the dictionary container are preferred to ones that // were extracted from a data record. goto Exit; } else if( !bGotFromDataRec && pRecovDictRec->bGotFromDataRec) { // Throw away prior dictionary record that was recovered, because // we got it from a data record. This newer one was actually found // in the dictionary, so we will keep it in preference to the // earlier one that we found in a data record. goto Remove_Rec; } else { switch (pRecovDictRec->pRec->getFieldID( pRecovDictRec->pRec->root())) { case FLM_FIELD_TAG: goto Exit; case FLM_CONTAINER_TAG: if( uiRecType == FLM_FIELD_TAG) { Remove_Rec: if (pPrevDictRec) { pPrevDictRec->pNext = pRecovDictRec->pNext; } else { pRecovDictInfo->pRecovRecs = pRecovDictRec->pNext; } // Might as well use the old structure for the new record, // so we don't have to allocate it below. pNewDictRec = pRecovDictRec; if( pRecovDictRec->pRec) { pRecovDictRec->pRec->Release(); pRecovDictRec->pRec = NULL; } f_memset( pNewDictRec, 0, sizeof( RECOV_DICT_REC)); } else { goto Exit; } break; case FLM_AREA_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG) { goto Remove_Rec; } else { goto Exit; } case FLM_RESERVED_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG) { goto Remove_Rec; } else { goto Exit; } case FLM_INDEX_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG || uiRecType == FLM_RESERVED_TAG) { goto Remove_Rec; } else { goto Exit; } default: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG || uiRecType == FLM_RESERVED_TAG || uiRecType == FLM_INDEX_TAG) { goto Remove_Rec; } else { goto Exit; } } break; } } // Link the record into the list of records that need to be // recovered. We don't add it right away, because we want to be sure // and do them in a certain order, as follows: // // 1. Field definitions // 2. Container definitions // 3. Area definitions // 4. Template definitions // 5. Index definitions // 6. Reserve definitions // 7. Other if( !pNewDictRec) { // All elements of pNewDictRec are initialized below. if( RC_BAD( rc = pRecovDictInfo->pool.poolAlloc( sizeof( RECOV_DICT_REC), (void **)&pNewDictRec))) { goto Exit; } pNewDictRec->pNext = NULL; pNewDictRec->bAdded = FALSE; } if( (pNewDictRec->pRec = pRecord->copy()) == NULL) { rc = RC_SET( FERR_MEM); } pNewDictRec->bGotFromDataRec = bGotFromDataRec; pNewDictRec->pRec->setID( uiDrn); pNewDictRec->uiBlkAddress = uiBlkAddress; pNewDictRec->uiElmOffset = uiElmOffset; pPrevDictRec = NULL; pRecovDictRec = pRecovDictInfo->pRecovRecs; while( pRecovDictRec) { switch( pRecovDictRec->pRec->getFieldID( pRecovDictRec->pRec->root())) { case FLM_FIELD_TAG: break; case FLM_CONTAINER_TAG: if( uiRecType == FLM_FIELD_TAG) { goto Insert_Rec; } break; case FLM_AREA_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG) { goto Insert_Rec; } break; case FLM_INDEX_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG) { goto Insert_Rec; } break; case FLM_RESERVED_TAG: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG || uiRecType == FLM_INDEX_TAG) { goto Insert_Rec; } break; default: if( uiRecType == FLM_FIELD_TAG || uiRecType == FLM_CONTAINER_TAG || uiRecType == FLM_AREA_TAG || uiRecType == FLM_INDEX_TAG || uiRecType == FLM_RESERVED_TAG) { goto Insert_Rec; } break; } pPrevDictRec = pRecovDictRec; pRecovDictRec = pRecovDictRec->pNext; } Insert_Rec: pNewDictRec->pNext = pRecovDictRec; if( pPrevDictRec) { pPrevDictRec->pNext = pNewDictRec; } else { pRecovDictInfo->pRecovRecs = pNewDictRec; } Exit: return( rc); } /*************************************************************************** Desc: This routine frees all of the recovery dictionary information. *****************************************************************************/ FSTATIC void bldFreeRecovDictInfo( RECOV_DICT_INFO * pRecovDictInfo) { if( pRecovDictInfo) { RECOV_DICT_REC * pDictRec; pDictRec = pRecovDictInfo->pRecovRecs; while (pDictRec) { if (pDictRec->pRec) { pDictRec->pRec->Release(); pDictRec->pRec = NULL; } pDictRec = pDictRec->pNext; } pRecovDictInfo->pool.poolFree(); f_free( &pRecovDictInfo); } } /*************************************************************************** Desc: This routine adds all of the recovered dictionary records for a specific dictionary. It makes sure to do parent dictionaries first. *****************************************************************************/ FSTATIC RCODE bldDoDict( FDB * pDb, REBUILD_STATE * pRebuildState, RECOV_DICT_INFO * pDictToDo, FLMBOOL * pbStartedTransRV) { RCODE rc = FERR_OK; RECOV_DICT_REC * pDictRec; RECOV_DICT_REC * pFirstDictRecInTrans = NULL; LFILE * pDictLFile; STATUS_HOOK fnStatusFunc = pRebuildState->fnStatusFunc; REBUILD_INFO * pCallbackData = &pRebuildState->CallbackData; FLMBOOL bHaveLFile; FLMBOOL bAddedAtLeastOne; FLMBOOL bFailedAtLeastOne; FlmRecord * pSaveRec; FLMUINT uiRecordsPerTrans; FLMUINT uiRecordInTrans; FLMUINT uiDictRecId; // Commit the current update transaction if( *pbStartedTransRV) { *pbStartedTransRV = FALSE; if( RC_BAD( rc = flmCommitDbTrans( pDb, 0, FALSE))) { goto Exit; } } // Add all of the records for pDictToDo - one per transaction. bHaveLFile = FALSE; uiRecordsPerTrans = 100; Do_Dict_Recs: pDictRec = pDictToDo->pRecovRecs; bAddedAtLeastOne = FALSE; bFailedAtLeastOne = FALSE; uiRecordInTrans = 0; while (pDictRec) { if (!pDictRec->bAdded) { if( !uiRecordInTrans) { // Start an update transaction if( RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { goto Exit; } *pbStartedTransRV = TRUE; pFirstDictRecInTrans = pDictRec; } uiRecordInTrans++; // Don't optimize and keep pDictLFile as a local variable! if( RC_BAD( rc = fdictGetContainer( pDb->pDict, FLM_DICT_CONTAINER, &pDictLFile))) { rc = FERR_OK; goto Exit; } bHaveLFile = TRUE; // Call the status callback to give the application a chance // to change the definition record if( fnStatusFunc) { if( RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_ADD_DICT_REC_STATUS, (void *)pDictRec->pRec, (void *)0, pRebuildState->AppArg))) { goto Exit; } } // Add the dictionary record. // NOTE: We are deliberately ignoring return codes here - so we // can attempt to process as many records as possible. uiDictRecId = pDictRec->pRec->getID(); if (RC_BAD( flmLFileDictUpdate( pDb, &pDictLFile, &uiDictRecId, pDictRec->pRec, NULL, FALSE, FALSE, NULL, TRUE))) { *pbStartedTransRV = FALSE; (void)flmAbortDbTrans( pDb); bFailedAtLeastOne = TRUE; uiRecordInTrans = 0; } else if( (uiRecordInTrans >= uiRecordsPerTrans) || !pDictRec->pNext) { *pbStartedTransRV = FALSE; if (RC_BAD( flmCommitDbTrans( pDb, 0, FALSE))) { bFailedAtLeastOne = TRUE; } else { // Set bAdded to TRUE on all dict recs in transaction. while( pFirstDictRecInTrans != pDictRec) { pFirstDictRecInTrans->bAdded = TRUE; pFirstDictRecInTrans = pFirstDictRecInTrans->pNext; } pDictRec->bAdded = bAddedAtLeastOne = TRUE; pRebuildState->CallbackData.uiRecsRecov += uiRecordInTrans; if( fnStatusFunc) { if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, (void *)pCallbackData, (void *)0, pRebuildState->AppArg))) { goto Exit; } } f_yieldCPU(); } uiRecordInTrans = 0; } } pDictRec = pDictRec->pNext; } // Retry the add loop until either they all FAIL or they all succeed. // This is done to handle record template dependencies - or other // dependencies for that matter - that may not have been in the proper // order in the list. if( (bAddedAtLeastOne || uiRecordsPerTrans > 1) && bFailedAtLeastOne) { // We MUST do single record transactions from this point on // so the commit above will be called if the last pDictRec // was added on any prior pass. uiRecordsPerTrans = 1; goto Do_Dict_Recs; } // Log the records that failed pDictRec = pDictToDo->pRecovRecs; pSaveRec = pRebuildState->pRecord; while (pDictRec) { if (!pDictRec->bAdded) { RCODE TempRc; pRebuildState->pRecord = pDictRec->pRec; if (RC_BAD( TempRc = bldReportReason( pRebuildState, FLM_DICT_REC_ADD_ERR, pDictRec->uiBlkAddress, pDictRec->uiElmOffset, pDictRec->pRec->getID(), 0xFFFF, 0))) { rc = TempRc; } } pDictRec = pDictRec->pNext; } pRebuildState->pRecord = pSaveRec; Exit: bldFreeRecovDictInfo( pDictToDo); if ((RC_OK( rc)) && (!(*pbStartedTransRV))) { if (RC_OK( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { *pbStartedTransRV = TRUE; } } return( rc); } /**************************************************************************** Desc: Extract create options from file header and log header pieces. ****************************************************************************/ void flmGetCreateOpts( FILE_HDR * pFileHdr, FLMBYTE * pucLogHdr, CREATE_OPTS * pCreateOpts) { f_memset( pCreateOpts, 0, sizeof( CREATE_OPTS)); if (pFileHdr) { pCreateOpts->uiBlockSize = pFileHdr->uiBlockSize; pCreateOpts->uiVersionNum = pFileHdr->uiVersionNum; pCreateOpts->uiDefaultLanguage = pFileHdr->uiDefaultLanguage; pCreateOpts->uiAppMajorVer = pFileHdr->uiAppMajorVer; pCreateOpts->uiAppMinorVer = pFileHdr->uiAppMinorVer; } else { pCreateOpts->uiBlockSize = DEFAULT_BLKSIZ; pCreateOpts->uiVersionNum = FLM_CUR_FILE_FORMAT_VER_NUM; pCreateOpts->uiDefaultLanguage = DEFAULT_LANG; // uiAppMajorVer and uiAppMinorVer are already zero. } if (pucLogHdr) { pCreateOpts->uiMinRflFileSize = (FLMUINT)FB2UD( &pucLogHdr [LOG_RFL_MIN_FILE_SIZE]); pCreateOpts->uiMaxRflFileSize = (FLMUINT)FB2UD( &pucLogHdr [LOG_RFL_MAX_FILE_SIZE]); pCreateOpts->bKeepRflFiles = (FLMBOOL)((pucLogHdr [LOG_KEEP_RFL_FILES]) ? TRUE : FALSE); pCreateOpts->bLogAbortedTransToRfl = (FLMBOOL)((pucLogHdr [LOG_KEEP_ABORTED_TRANS_IN_RFL]) ? TRUE : FALSE); } else { pCreateOpts->uiMinRflFileSize = DEFAULT_MIN_RFL_FILE_SIZE; pCreateOpts->uiMaxRflFileSize = DEFAULT_MAX_RFL_FILE_SIZE; pCreateOpts->bKeepRflFiles = DEFAULT_KEEP_RFL_FILES_FLAG; pCreateOpts->bLogAbortedTransToRfl = DEFAULT_LOG_ABORTED_TRANS_FLAG; } } /**************************************************************************** Desc: Rebuilds a damaged database. Notes: This routine performs the following actions: 1) A temporary database is created; 2) A copy of the source database is saved; 3) The source database is scanned. Data records from all containers are extracted and stored in the temporary database. 4) When the rebuild is complete, the temporary database file is copied over the source database file. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, const char * pszDestDbPath, const char * pszDestDataDir, const char * pszDestRflDir, const char * pszDictPath, CREATE_OPTS * pCreateOpts, FLMUINT * puiTotRecsRV, FLMUINT * puiRecsRecovRV, STATUS_HOOK fnStatusFunc, void * pvStatusData) { RCODE rc = FERR_OK; FDB * pDb = NULL; FFILE * pFile; F_SuperFileHdl * pSFileHdl = NULL; F_SuperFileClient * pSFileClient = NULL; FLMBOOL bFileLocked = FALSE; FLMBOOL bWriteLocked = FALSE; REBUILD_STATE * pRebuildState = NULL; HDR_INFO * pHdrInfo; CREATE_OPTS * pDefaultCreateOpts = NULL; FLMUINT uiTransID; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pFileLockObj = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pCFileHdl = NULL; IF_FileHdl * pLockFileHdl = NULL; eLockType currLockType; FLMUINT uiLockThreadId; FLMUINT uiFileNumber; FLMUINT uiDbVersion = 0; FLMBOOL bUsedFFile = FALSE; FLMBOOL bBadHeader = FALSE; F_SEM hWaitSem = F_SEM_NULL; // Allocate a semaphore if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; // See if there is an FFILE structure for this file // May unlock and re-lock the global mutex. if( RC_BAD( rc = flmFindFile( pszSourceDbPath, pszSourceDataDir, &pFile))) { goto Exit; } // If we didn't find an FFILE structure, get an // exclusive lock on the file. if( !pFile) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if( RC_BAD( rc = flmCreateLckFile( pszSourceDbPath, &pLockFileHdl))) { goto Exit; } } else { if( RC_BAD( rc = flmCheckFFileState( pFile))) { goto Exit; } // The call to flmVerifyFileUse will wait if the file is in // the process of being opened by another thread. if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile))) { goto Exit; } // Increment the use count on the FFILE so it will not // disappear while we are copying the file. if (++pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } bUsedFFile = TRUE; f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; // See if the thread already has a file lock. If so, there // is no need to obtain another. However, we also want to // make sure there is no write lock. If there is, // we cannot do the rebuild right now. pFile->pFileLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if (currLockType == FLM_LOCK_EXCLUSIVE && uiLockThreadId == f_threadId()) { // See if there is already a transaction going. pFile->pWriteLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL); if (currLockType == FLM_LOCK_EXCLUSIVE && uiLockThreadId == f_threadId()) { rc = RC_SET( FERR_TRANS_ACTIVE); goto Exit; } } else { pFileLockObj = pFile->pFileLockObj; pFileLockObj->AddRef(); if (RC_BAD( rc = pFileLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bFileLocked = TRUE; } // Lock the write object to eliminate contention with // the checkpoint thread. pWriteLockObj = pFile->pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread. // Wait forever for the checkpoint thread to give // up the lock. if( RC_BAD( rc = pWriteLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bWriteLocked = TRUE; } if( RC_BAD( rc = f_calloc( sizeof( REBUILD_STATE), &pRebuildState))) { goto Exit; } if( RC_BAD( rc = f_calloc( sizeof( HDR_INFO), &pRebuildState->pHdrInfo))) { goto Exit; } if( RC_BAD( rc = f_calloc( sizeof( STATE_INFO), &pRebuildState->pStateInfo))) { goto Exit; } if( RC_BAD( rc = f_calloc( sizeof( CREATE_OPTS), &pDefaultCreateOpts))) { goto Exit; } if( RC_BAD( rc = f_calloc( LOG_HEADER_SIZE, &pRebuildState->pLogHdr))) { goto Exit; } if( RC_BAD( rc = f_calloc( MAX_KEY_SIZ, &pRebuildState->pKeyBuffer))) { goto Exit; } pRebuildState->AppArg = pvStatusData; pRebuildState->fnStatusFunc = fnStatusFunc; pHdrInfo = pRebuildState->pHdrInfo; // Open the database file for reading header information if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszSourceDbPath, gv_FlmSysData.uiFileOpenFlags, &pCFileHdl))) { goto Exit; } rc = flmGetHdrInfo( pCFileHdl, &pHdrInfo->FileHdr, &pHdrInfo->LogHdr, pRebuildState->pLogHdr); pCFileHdl->Release(); pCFileHdl = NULL; if( RC_OK( rc)) { if (!pCreateOpts) { flmGetCreateOpts( &pHdrInfo->FileHdr, pRebuildState->pLogHdr, pDefaultCreateOpts); pCreateOpts = pDefaultCreateOpts; } rc = FERR_OK; uiDbVersion = pHdrInfo->FileHdr.uiVersionNum; pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pRebuildState->pLogHdr); } else if( rc == FERR_BLOCK_CHECKSUM || rc == FERR_INCOMPLETE_LOG || rc == FERR_DATA_ERROR || (rc == FERR_UNSUPPORTED_VERSION && pHdrInfo->FileHdr.uiVersionNum == 0)) { if( rc == FERR_BLOCK_CHECKSUM || rc == FERR_DATA_ERROR) { bBadHeader = TRUE; } rc = FERR_OK; if (!pCreateOpts) { flmGetCreateOpts( &pHdrInfo->FileHdr, pRebuildState->pLogHdr, pDefaultCreateOpts); pCreateOpts = pDefaultCreateOpts; } uiDbVersion = pHdrInfo->FileHdr.uiVersionNum; pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pRebuildState->pLogHdr); } else if( rc == FERR_UNSUPPORTED_VERSION || rc == FERR_NEWER_FLAIM) { goto Exit; } else if( rc == FERR_NOT_FLAIM || !VALID_BLOCK_SIZE( pHdrInfo->FileHdr.uiBlockSize)) { FLMUINT uiSaveBlockSize; FLMUINT uiCalcBlockSize = 0; FLMBYTE ucFileHdrBuf[ FLM_FILE_HEADER_SIZE]; uiDbVersion = (FLMUINT)((rc != FERR_NOT_FLAIM) ? pHdrInfo->FileHdr.uiVersionNum : FLM_CUR_FILE_FORMAT_VER_NUM); pRebuildState->uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pRebuildState->pLogHdr); if (!pCreateOpts) { if (rc != FERR_NOT_FLAIM) { flmGetCreateOpts( &pHdrInfo->FileHdr, pRebuildState->pLogHdr, pDefaultCreateOpts); } else { flmGetCreateOpts( NULL, NULL, pDefaultCreateOpts); } // Set block size to zero, so we will always take the calculated // block size below. pDefaultCreateOpts->uiBlockSize = 0; pCreateOpts = pDefaultCreateOpts; } // Try to determine the correct block size if (RC_BAD( rc = bldDetermineBlkSize( pszSourceDbPath, pszSourceDataDir, uiDbVersion, pRebuildState->uiMaxFileSize, &uiCalcBlockSize, fnStatusFunc, &pRebuildState->CallbackData, pRebuildState->AppArg))) { goto Exit; } uiSaveBlockSize = pCreateOpts->uiBlockSize; pCreateOpts->uiBlockSize = uiCalcBlockSize; // Initialize pHdrInfo->FileHdr to useable values. flmInitFileHdrInfo( pCreateOpts, &pHdrInfo->FileHdr, ucFileHdrBuf); // Only use the passed-in block size (uiSaveBlockSize) if it // was non-zero. if (uiSaveBlockSize) { pCreateOpts->uiBlockSize = uiSaveBlockSize; } } else { goto Exit; } // Delete the destination database in case it already exists. if( RC_BAD( rc = FlmDbRemove( pszDestDbPath, pszDestDataDir, pszDestRflDir, TRUE))) { if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } // If no block size has been specified or determined yet, use what we // read from the file header. if( !pCreateOpts->uiBlockSize) { pCreateOpts->uiBlockSize = pHdrInfo->FileHdr.uiBlockSize; } // Open the corrupted database if( (pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( (pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSFileClient->setup( pszSourceDbPath, pszSourceDataDir, pHdrInfo->FileHdr.uiVersionNum))) { goto Exit; } if( RC_BAD( rc = pSFileHdl->setup( pSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, 0))) { goto Exit; } pRebuildState->pSFileHdl = pSFileHdl; // Calculate the file size. pRebuildState->CallbackData.ui64DatabaseSize = 0; for (uiFileNumber = 1;;uiFileNumber++) { FLMUINT64 ui64TmpSize; if (RC_BAD( pSFileHdl->getFileSize( uiFileNumber, &ui64TmpSize))) { break; } pRebuildState->CallbackData.ui64DatabaseSize += ui64TmpSize; } // When creating the new file, set the transaction ID to one greater than it // is in the corrupt file. However, don't let it get greater than about // 2 billion - want to leave room for 2 billion transactions in case they // were corrupted somehow in our old file. uiTransID = ((FLMUINT)FB2UD( &pRebuildState->pLogHdr[ LOG_CURR_TRANS_ID]) + 1) & 0x7FFFFFFF; if (RC_BAD( rc = flmCreateNewFile( pszDestDbPath, pszDestDataDir, pszDestRflDir, pszDictPath, NULL, pCreateOpts, uiTransID, (FDB * *)&pRebuildState->hDb, pRebuildState))) { goto Exit; } pDb = (FDB *)pRebuildState->hDb; // Rebuild the database if (RC_BAD( rc = flmDbRebuildFile( pRebuildState, bBadHeader))) { goto Exit; } Exit: // Close the temporary database, if it is still open if (pDb) { FFILE * pTmpFile; FFILE * pTmpFile1; // Get the FFILE pointer for the temporary file before closing it. pTmpFile = pDb->pFile; (void)FlmDbClose( (HFDB *)&pDb); // Force temporary FFILE structure to be cleaned up, if it // isn't already gone. The following code searches for the // temporary file in the not-used list. If it finds it, // it will unlink it. if (!bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } pTmpFile1 = gv_FlmSysData.pLrnuFile; while (pTmpFile1) { if (pTmpFile1 == pTmpFile) { flmFreeFile( pTmpFile); break; } pTmpFile1 = pTmpFile1->pNextNUFile; } } if (bUsedFFile) { if (!bMutexLocked) { f_mutexLock( gv_FlmSysData.hShareMutex); bMutexLocked = TRUE; } if (!(--pFile->uiUseCount)) { flmLinkFileToNUList( pFile); } } if( bMutexLocked) { f_mutexUnlock( gv_FlmSysData.hShareMutex); bMutexLocked = FALSE; } if( bWriteLocked) { pWriteLockObj->unlock(); bWriteLocked = FALSE; } if( bFileLocked) { pFileLockObj->unlock(); bFileLocked = FALSE; } if( pSFileHdl) { pSFileHdl->Release(); } if( pCFileHdl) { pCFileHdl->Release(); } if( pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if( pFileLockObj) { pFileLockObj->Release(); pFileLockObj = NULL; } if( pLockFileHdl) { pLockFileHdl->Release(); pLockFileHdl = NULL; } if( pDefaultCreateOpts) { f_free( &pDefaultCreateOpts); } if( pRebuildState) { if( puiTotRecsRV) { *puiTotRecsRV = pRebuildState->CallbackData.uiTotRecs; } if( puiRecsRecovRV) { *puiRecsRecovRV = pRebuildState->CallbackData.uiRecsRecov; } if( pRebuildState->pStateInfo && pRebuildState->pStateInfo->pRecord) { pRebuildState->pStateInfo->pRecord->Release(); } if( pRebuildState->pRecord) { pRebuildState->pRecord->Release(); pRebuildState->pRecord = NULL; } if( pRebuildState->pHdrInfo) { f_free( &pRebuildState->pHdrInfo); } if( pRebuildState->pStateInfo) { f_free( &pRebuildState->pStateInfo); } if( pRebuildState->pLogHdr) { f_free( &pRebuildState->pLogHdr); } if( pRebuildState->pKeyBuffer) { f_free( &pRebuildState->pKeyBuffer); } f_free( &pRebuildState); } else { if( puiTotRecsRV) { *puiTotRecsRV = 0; } if( puiRecsRecovRV) { *puiRecsRecovRV = 0; } } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } if( pSFileClient) { pSFileClient->Release(); } return( rc); } /*************************************************************************** Desc: This routine reads through a database and makes a best guess as to the true block size of the database. *****************************************************************************/ FSTATIC RCODE bldDetermineBlkSize( const char * pszSourceDbPath, const char * pszSourceDataDir, FLMUINT uiDbVersion, FLMUINT uiMaxFileSize, FLMUINT * puiBlkSizeRV, STATUS_HOOK fnStatusFunc, REBUILD_INFO * pCallbackData, void * AppArg) { RCODE rc = FERR_OK; FLMBYTE ucBlkHeader [BH_OVHD]; FLMUINT uiBytesRead; FLMUINT uiBlkAddress; FLMUINT uiFileNumber = 0; FLMUINT uiOffset = 0; FLMUINT uiCount4K = 0; FLMUINT uiCount8K = 0; FLMUINT64 ui64BytesDone = 0; F_SuperFileHdl * pSFileHdl = NULL; F_SuperFileClient * pSFileClient = NULL; // Open the corrupted database if( (pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( (pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if( RC_BAD( rc = pSFileClient->setup( pszSourceDbPath, pszSourceDataDir, uiDbVersion))) { goto Exit; } if( RC_BAD( rc = pSFileHdl->setup( pSFileClient, gv_FlmSysData.pFileHdlCache, gv_FlmSysData.uiFileOpenFlags, 0))) { goto Exit; } // Start from byte offset 0 in the first file pCallbackData->iDoingFlag = REBUILD_GET_BLK_SIZ; pCallbackData->bStartFlag = TRUE; for (;;) { if (uiOffset >= uiMaxFileSize || !uiFileNumber) { uiOffset = 0; uiFileNumber++; } if( (RC_OK( rc = pSFileHdl->readBlock( FSBlkAddress( uiFileNumber, uiOffset), BH_OVHD, ucBlkHeader, &uiBytesRead))) || rc == FERR_IO_END_OF_FILE) { if (RC_OK( rc)) { ui64BytesDone += (FLMUINT64)MIN_BLOCK_SIZE; } else { ui64BytesDone += (FLMUINT64)uiBytesRead; } uiBlkAddress = GET_BH_ADDR( ucBlkHeader); if ((uiBytesRead == BH_OVHD) && (FSGetFileOffset( uiBlkAddress) == uiOffset)) { if (uiOffset % 4096 == 0) { uiCount4K++; } if (uiOffset % 8192 == 0) { uiCount8K++; } } if (rc != FERR_OK || uiBytesRead < BH_OVHD) { // Even if the file is not full size, set offset to // the maximum file offset so we will attempt to go // to the next file at the top of this loop. If that // fails, we will assume we truly are at EOF. uiOffset = uiMaxFileSize; } else { uiOffset += MIN_BLOCK_SIZE; } // Call the callback function to report copy progress if (fnStatusFunc != NULL) { pCallbackData->ui64BytesExamined = ui64BytesDone; if (RC_BAD( rc = (*fnStatusFunc)( FLM_REBUILD_STATUS, (void *)pCallbackData, (void *)0, AppArg))) { goto Exit; } pCallbackData->bStartFlag = FALSE; } f_yieldCPU(); } else { if( rc == FERR_IO_PATH_NOT_FOUND) { rc = RC_SET( FERR_IO_END_OF_FILE); break; } goto Exit; } } if (rc == FERR_IO_END_OF_FILE) { rc = FERR_OK; } // If our count of 4K blocks is greater than 66% of the number // of 4K blocks that would fit in the database, we will use // a 4K block size. Otherwise, we will use an 8K block size. if (uiCount4K > (FLMUINT)(((ui64BytesDone / (FLMUINT64)4096) * (FLMUINT64)66) / (FLMUINT64)100)) { *puiBlkSizeRV = 4096; } else { *puiBlkSizeRV = 8192; } Exit: if( pSFileHdl) { pSFileHdl->Release(); } if( pSFileClient) { pSFileClient->Release(); } return( rc); } libflaim-4.9.966/src/fqdecl.cpp0000644000175000017500000003351310510774540017623 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Various cursor/query functions // Tabs: 3 // // Copyright (c) 1994-2001,2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqdecl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmSendCursorFrom( FCL_WIRE * pWire, CURSOR * pCursor); FSTATIC RCODE flmSendCursorWhere( FCL_WIRE * pWire, CURSOR * pCursor); /**************************************************************************** Desc: Send the FROM information for the cursor to the client. ****************************************************************************/ FSTATIC RCODE flmSendCursorFrom( FCL_WIRE * pWire, CURSOR * pCursor) { RCODE rc = FERR_OK; NODE * pRootNode; NODE * pChildNode = NULL; NODE * pTmp; F_Pool * pPool = pWire->getPool(); void * pvMark = pPool->poolMark(); FLMUINT uiTmp; CS_CONTEXT * pCSContext = pWire->getContext(); if ((pRootNode = GedNodeMake( pPool, FCS_ITERATOR_FROM, &rc)) == NULL) { goto Exit; } uiTmp = 0; if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_CANDIDATE_SET, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } pChildNode = (NODE *)((!pChildNode) ? GedChild( pRootNode) : GedSibNext( pChildNode)); // Add all record sources. if ((pTmp = GedNodeMake( pPool, FCS_ITERATOR_RECORD_SOURCE, &rc)) == NULL) { goto Exit; } GedChildGraft( pChildNode, pTmp, GED_LAST); // Insert container number. if (pCursor->uiContainer != FLM_DATA_CONTAINER) { if (RC_BAD( rc = gedAddField( pPool, pTmp, FCS_ITERATOR_CONTAINER_ID, (void *)&pCursor->uiContainer, 0, FLM_NUMBER_TYPE))) { goto Exit; } } // Add record type. if (pCursor->uiRecType) { if (RC_BAD( rc = gedAddField( pPool, pChildNode, FCS_ITERATOR_RECORD_TYPE, (void *)&pCursor->uiRecType, 0, FLM_NUMBER_TYPE))) { goto Exit; } } // Add bOkToReturnKeys flag if( pCSContext->uiServerFlaimVer >= FLM_FILE_FORMAT_VER_4_3) { uiTmp = (FLMUINT)(pCursor->bOkToReturnKeys ? 1 : 0); if (RC_BAD( rc = gedAddField( pPool, pChildNode, FCS_ITERATOR_OK_TO_RETURN_KEYS, (void *)&uiTmp, 0, FLM_NUMBER_TYPE))) { goto Exit; } } // Add index number if (pCursor->uiIndexNum) { if (RC_BAD( rc = gedAddField( pPool, pChildNode, FCS_ITERATOR_FLAIM_INDEX, (void *)&pCursor->uiIndexNum, 0, FLM_NUMBER_TYPE))) { goto Exit; } } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_MODE, (void *)&pCursor->QTInfo.uiFlags, 0, FLM_NUMBER_TYPE))) { goto Exit; } if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_ITERATOR_FROM, pRootNode))) { pCSContext->bConnectionGood = FALSE; goto Exit; } Exit: pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Send selection criteria for the cursor to the server. ****************************************************************************/ FSTATIC RCODE flmSendCursorWhere( FCL_WIRE * pWire, CURSOR * pCursor ) { RCODE rc = FERR_OK; NODE * pRootNode; NODE * pFldNode; FQNODE * pQNode; FLMUINT uiOperator; FLMUINT uiLastFlags = 0; QTYPES eOp; F_Pool * pPool = pWire->getPool(); void * pvMark = pPool->poolMark(); CS_CONTEXT * pCSContext = pWire->getContext(); if ((pRootNode = GedNodeMake( pPool, FCS_ITERATOR_WHERE, &rc)) == NULL) { goto Exit; } if ((pQNode = pCursor->QTInfo.pTopNode) == NULL) { if ((pQNode = pCursor->QTInfo.pCurAtomNode) == NULL) { goto Exit; } } // Do an in-order traversal of the tree. for (;;) { eOp = GET_QNODE_TYPE( pQNode); // Skip the node if it has children and is not // a unary operator. It will be output after its first child has // been output. if( pQNode->pChild && (eOp != FLM_NOT_OP && eOp != FLM_NEG_OP)) { // Insert a left paren. if( RC_BAD( rc = fcsTranslateQFlmToQCSOp( FLM_LPAREN_OP, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } pQNode = pQNode->pChild; continue; } // Output the node's mode flags if (pQNode->pQAtom && pQNode->pQAtom->uiFlags != uiLastFlags) { if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_MODE, (void *)&pQNode->pQAtom->uiFlags, 0, FLM_NUMBER_TYPE))) { goto Exit; } uiLastFlags = pQNode->pQAtom->uiFlags; } // Output the node if( eOp == FLM_NOT_OP || eOp == FLM_NEG_OP) { // Unary operator if (RC_BAD( rc = fcsTranslateQFlmToQCSOp( eOp, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } // Insert a left paren. if (RC_BAD( rc = fcsTranslateQFlmToQCSOp( FLM_LPAREN_OP, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } pQNode = pQNode->pChild; continue; } else { // Output whatever is in the node at this point. if (IS_OP( eOp)) { if( RC_BAD( rc = fcsTranslateQFlmToQCSOp( eOp, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } } else if (IS_VAL( eOp)) { switch (eOp) { case FLM_UINT32_VAL: if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_NUMBER_VALUE, (void *)&pQNode->pQAtom->val.ui32Val, 4, FLM_NUMBER_TYPE))) { goto Exit; } break; case FLM_UINT64_VAL: if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_NUMBER_VALUE, (void *)&pQNode->pQAtom->val.ui64Val, 8, FLM_NUMBER_TYPE))) { goto Exit; } break; case FLM_INT32_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_NUMBER_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutINT( pPool, pFldNode, (FLMINT)pQNode->pQAtom->val.i32Val))) { goto Exit; } break; case FLM_INT64_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_NUMBER_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutINT64( pPool, pFldNode, pQNode->pQAtom->val.i64Val))) { goto Exit; } break; case FLM_REC_PTR_VAL: if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_REC_PTR_VALUE, (void *)&pQNode->pQAtom->val.ui32Val, 4, FLM_NUMBER_TYPE))) { goto Exit; } break; case FLM_STRING_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_NATIVE_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutNATIVE( pPool, pFldNode, (const char *)pQNode->pQAtom->val.pucBuf))) { goto Exit; } break; case FLM_BINARY_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_BINARY_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutBINARY( pPool, pFldNode, pQNode->pQAtom->val.pucBuf, pQNode->pQAtom->uiBufLen))) { goto Exit; } break; case FLM_TEXT_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_FLM_TEXT_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutBINARY( pPool, pFldNode, pQNode->pQAtom->val.pucBuf, pQNode->pQAtom->uiBufLen))) { goto Exit; } break; case FLM_UNICODE_VAL: if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_UNICODE_VALUE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); if (RC_BAD( rc = GedPutUNICODE( pPool, pFldNode, (FLMUNICODE *)pQNode->pQAtom->val.pucBuf))) { goto Exit; } break; default: flmAssert( 0); rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } } else { FLMUINT * puiPath = pQNode->pQAtom->val.QueryFld.puiFldPath; FLMUINT uiPathLen = 0; while (*puiPath) { uiPathLen++; puiPath++; } if (uiPathLen == 1) { if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_ATTRIBUTE, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); puiPath--; if (RC_BAD( rc = GedPutUINT( pPool, pFldNode, *puiPath))) { goto Exit; } } else { if ((pFldNode = GedNodeMake( pPool, FCS_ITERATOR_ATTRIBUTE_PATH, &rc)) == NULL) { goto Exit; } GedChildGraft( pRootNode, pFldNode, GED_LAST); while (uiPathLen) { uiPathLen--; puiPath--; if (RC_BAD( rc = gedAddField( pPool, pFldNode, FCS_ITERATOR_ATTRIBUTE, (void *)puiPath, 0, FLM_NUMBER_TYPE))) { goto Exit; } } } } // See if the node has a sibling we should traverse down. Test_Sib: if (pQNode->pNextSib) { QTYPES eParentOp = GET_QNODE_TYPE( (pQNode->pParent)); // If we have a sibling, the parent MUST be a binary operator. // Output the operator if( RC_BAD( rc = fcsTranslateQFlmToQCSOp( eParentOp, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } pQNode = pQNode->pNextSib; continue; } if ((pQNode = pQNode->pParent) == NULL) { break; } // Insert a right paren. if (RC_BAD( rc = fcsTranslateQFlmToQCSOp( FLM_RPAREN_OP, &uiOperator))) { goto Exit; } if (RC_BAD( rc = gedAddField( pPool, pRootNode, FCS_ITERATOR_OPERATOR, (void *)&uiOperator, 0, FLM_NUMBER_TYPE))) { goto Exit; } goto Test_Sib; } } if (RC_BAD( rc = pWire->sendHTD( WIRE_VALUE_ITERATOR_WHERE, pRootNode))) { pCSContext->bConnectionGood = FALSE; goto Exit; } Exit: pPool->poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Initialize a query over the client/server line. ****************************************************************************/ RCODE flmInitCurCS( CURSOR * pCursor ) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = pCursor->pCSContext; FCL_WIRE Wire( pCSContext); if (pCursor->uiCursorId != FCS_INVALID_ID) { goto Exit; // Returns SUCCESS; } // Send a request to create an iterator for this cursor. if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_ITERATOR, FCS_OP_ITERATOR_INIT))) { goto Exit; } if (RC_BAD( rc = flmSendCursorFrom( &Wire, pCursor))) { goto Exit; } if (RC_BAD( rc = flmSendCursorWhere( &Wire, pCursor))) { goto Exit; } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (RC_BAD( rc = Wire.getRCode())) { goto Exit; } pCursor->uiCursorId = Wire.getIteratorId(); Exit: return( rc); Transmission_Error: pCursor->pCSContext->bConnectionGood = FALSE; goto Exit; } /**************************************************************************** Desc: Validates the selection criteria of a cursor. Notes: It is not necessary to explicitly validate the selection criteria through a call to this routine. FLAIM will automatically attempt validation on the first call to any of the cursor routines which make use of the criteria. Although explicit validation is unnecessary, it can be convenient to identify an error in the selection criteria before calling cursor routines which will make use of it. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorValidate( HFCURSOR hCursor) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (pCursor->pCSContext) { rc = flmInitCurCS( pCursor); goto Exit2; } // Validate the query by optimizing it. if( !pCursor->bOptimized) { rc = flmCurPrep( pCursor); } Exit: Exit2: return( pCursor->rc = rc); } libflaim-4.9.966/src/fqparse.cpp0000644000175000017500000010456210510774540020031 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query parsing // Tabs: 3 // // Copyright (c) 1998-2000,2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqparse.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE tokenAllocSpace( char ** ppszTokenStart, char ** ppszToken, FLMUINT * puiTokenBufSize, FLMUINT uiNewTokenBufSize); FSTATIC RCODE tokenGet( const char ** ppszString, char ** ppszToken, FLMBOOL * pbQuoted, QTYPES * peType, FLMUINT * puiTokenBufSize); FSTATIC FLMBOOL tokenIsNum( const char * pszToken, FLMUINT64 ui64Max, FLMUINT64 * pui64Num); FSTATIC FLMBOOL tokenIsOperator( const char * pszToken, QTYPES * peOperator); FSTATIC FLMBOOL tokenIsField( const char * pszToken, F_NameTable * pNameTable, FLMUINT * puiFieldPath, QTYPES * peValueType, FLMBOOL bAllowNamesOnly, FLMBOOL bMustBeField); FSTATIC RCODE allocValueSpace( void ** ppvVal, FLMUINT * puiValBufSize, FLMUINT uiNewSize); FSTATIC RCODE tokenGetBinary( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize); FSTATIC RCODE tokenGetValue( QTYPES eValueType, const char * pszToken, FLMBOOL bQuoted, void ** ppvVal, QTYPES * peValType, FLMUINT * puiValLen, FLMUINT * puiValBufSize); /**************************************************************************** Desc: Allocate space for the token. ****************************************************************************/ FSTATIC RCODE tokenAllocSpace( char ** ppszTokenStart, char ** ppszToken, FLMUINT * puiTokenBufSize, FLMUINT uiNewTokenBufSize) { RCODE rc = FERR_OK; char * pszTmp; if (RC_BAD( rc = f_alloc( uiNewTokenBufSize, &pszTmp))) { goto Exit; } // Copy the current token into the new buffer. f_memcpy( pszTmp, *ppszTokenStart, *puiTokenBufSize); // Free the old buffer, if it was allocated f_free( ppszTokenStart); *ppszTokenStart = pszTmp; *ppszToken = *ppszTokenStart + *puiTokenBufSize; *puiTokenBufSize = uiNewTokenBufSize; Exit: return( rc); } /**************************************************************************** Desc: Determine if a character is a delimiter character. ****************************************************************************/ FINLINE FLMBOOL tokenIsDelimiter( FLMBYTE ucChar) { if (ucChar <= ' ') { return( TRUE); } switch (ucChar) { case '!': case '+': case '-': case '*': case '%': case '/': case '(': case ')': case '=': case '>': case '<': case '&': case '|': case '\'': case '"': return( TRUE); default: return( FALSE); } } /**************************************************************************** Desc: Get the next token from a query string. ****************************************************************************/ FSTATIC RCODE tokenGet( const char ** ppszString, char ** ppszToken, FLMBOOL * pbQuoted, QTYPES * peType, FLMUINT * puiTokenBufSize) { RCODE rc = FERR_OK; const char * pszStr = *ppszString; char * pszToken = *ppszToken; char * pszTokenStart = pszToken; FLMBYTE ucQuote; FLMUINT uiLen; // Skip leading white space *peType = NO_TYPE; *pbQuoted = FALSE; while (*pszStr && *pszStr <= ' ') { pszStr++; } switch (*pszStr) { case 0: break; case '!': case '+': case '-': case '*': case '%': case '/': case '(': case ')': { *pszToken++ = *pszStr++; break; } case '=': case '>': case '<': { *pszToken++ = *pszStr++; if (*pszStr == '=') { *pszToken++ = *pszStr++; } break; } case '&': { *pszToken++ = *pszStr++; if (*pszStr == '&') { *pszToken++ = *pszStr++; } break; } case '|': { *pszToken++ = *pszStr++; if (*pszStr == '|') { *pszToken++ = *pszStr++; } break; } case '\'': case '"': { *pbQuoted = TRUE; ucQuote = *pszStr++; uiLen = 0; while (*pszStr && *pszStr != ucQuote) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (*pszTmp && *pszTmp != ucQuote) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } // If we ended on the quote, skip it. if (*pszStr == ucQuote) { pszStr++; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } break; } default: { uiLen = 0; while (!tokenIsDelimiter( *pszStr)) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (!tokenIsDelimiter( *pszTmp)) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } break; } } // Always null terminate the token *pszToken = 0; // Test to see if we have any function keywords we want to parse out here. if( !(*pbQuoted)) { if (f_stricmp( pszTokenStart, "field") == 0 || f_stricmp( pszTokenStart, "path") == 0) { *peType = FLM_FLD_PATH; } else if (f_stricmp( pszTokenStart, "unicode") == 0 || f_stricmp( pszTokenStart, "text") == 0) { *peType = FLM_UNICODE_VAL; } else if (f_stricmp( pszTokenStart, "unsigned") == 0) { *peType = FLM_UINT32_VAL; } else if (f_stricmp( pszTokenStart, "unsigned64") == 0) { *peType = FLM_UINT64_VAL; } else if (f_stricmp( pszTokenStart, "boolean") == 0) { *peType = FLM_BOOL_VAL; } else if (f_stricmp( pszTokenStart, "signed") == 0) { *peType = FLM_INT32_VAL; } else if (f_stricmp( pszTokenStart, "signed64") == 0) { *peType = FLM_INT64_VAL; } else if (f_stricmp( pszTokenStart, "context") == 0) { *peType = FLM_REC_PTR_VAL; } else if (f_stricmp( pszTokenStart, "binary") == 0) { *peType = FLM_BINARY_VAL; } if (*peType != NO_TYPE) { FLMBYTE ucEndChar; // Skip white space - should hit a left paren. while (*pszStr && *pszStr <= ' ') { pszStr++; } // Better have a left paren here, or it is a syntax // error. if (*pszStr != '(') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip past left paren pszStr++; // Skip white space again while (*pszStr && *pszStr <= ' ') { pszStr++; } // Better not be at end of string or have an empty value if (!(*pszStr) || *pszStr == ')') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Token can be quoted inside the parens. However, it doesn't // change the type to string. The type specified takes precedence. if (*pszStr == '"' || *pszStr == '\'') { ucEndChar = *pszStr; pszStr++; } else { ucEndChar = ')'; } uiLen = 0; pszToken = pszTokenStart; while (*pszStr && *pszStr != ucEndChar) { *pszToken++ = *pszStr++; uiLen++; // If we don't have room for a null terminating byte, // better reallocate. Allocate enough so that we can // get the entire token. if (uiLen == *puiTokenBufSize) { const char * pszTmp = pszStr; FLMUINT uiExtraCharsNeeded = 1; // For NULL // See how many more characters we need. while (*pszStr && *pszStr != ucEndChar) { uiExtraCharsNeeded++; pszTmp++; } if (RC_BAD( rc = tokenAllocSpace( &pszTokenStart, &pszToken, puiTokenBufSize, *puiTokenBufSize + uiExtraCharsNeeded))) { goto Exit; } } } *pszToken = 0; // If the string is not quoted, trim off trailing white space if (ucEndChar == ')') { while (pszToken > pszTokenStart) { pszToken--; if (*pszToken <= ' ') { *pszToken = 0; } else { break; } } } // If we did not hit the end character, we have a syntax error. if (*pszStr != ucEndChar) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip past end character pszStr++; // If the end character is not a right paren, skip any white space // between it and the right paren. if (ucEndChar != ')') { while (*pszStr && *pszStr <= ' ') { pszStr++; } // If we did not hit a right paren, it is a syntax error. if (*pszStr != ')') { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Skip the right paren pszStr++; } } } Exit: // Return string should be positioned past the last character of the // returned token. *ppszString = pszStr; *ppszToken = pszTokenStart; return( rc); } /**************************************************************************** Desc: Determine if a token is an operator. ****************************************************************************/ FSTATIC FLMBOOL tokenIsOperator( const char * pszToken, QTYPES * peOperator) { FLMBOOL bIsOperator = TRUE; if (f_stricmp( pszToken, "(") == 0) { *peOperator = FLM_LPAREN_OP; } else if (f_stricmp( pszToken, ")") == 0) { *peOperator = FLM_RPAREN_OP; } else if (f_stricmp( pszToken, "&&") == 0 || f_stricmp( pszToken, "AND") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "||") == 0 || f_stricmp( pszToken, "OR") == 0) { *peOperator = FLM_OR_OP; } else if (f_stricmp( pszToken, "!") == 0) { *peOperator = FLM_NOT_OP; } else if (f_stricmp( pszToken, "==") == 0 || f_stricmp( pszToken, "EQ") == 0 || f_stricmp( pszToken, "=") == 0) { *peOperator = FLM_EQ_OP; } else if (f_stricmp( pszToken, "!=") == 0 || f_stricmp( pszToken, "NE") == 0) { *peOperator = FLM_NE_OP; } else if (f_stricmp( pszToken, "<=") == 0 || f_stricmp( pszToken, "LE") == 0) { *peOperator = FLM_LE_OP; } else if (f_stricmp( pszToken, "<") == 0 || f_stricmp( pszToken, "LT") == 0) { *peOperator = FLM_LT_OP; } else if (f_stricmp( pszToken, ">=") == 0 || f_stricmp( pszToken, "GE") == 0) { *peOperator = FLM_GE_OP; } else if (f_stricmp( pszToken, ">") == 0 || f_stricmp( pszToken, "GT") == 0) { *peOperator = FLM_GT_OP; } else if (f_stricmp( pszToken, "MATCH") == 0 || f_stricmp( pszToken, "MATCHES") == 0) { *peOperator = FLM_MATCH_OP; } else if (f_stricmp( pszToken, "MATCHBEGIN") == 0 || f_stricmp( pszToken, "MATCH_BEGIN") == 0) { *peOperator = FLM_MATCH_BEGIN_OP; } else if (f_stricmp( pszToken, "MATCHEND") == 0 || f_stricmp( pszToken, "MATCH_END") == 0) { *peOperator = FLM_MATCH_END_OP; } else if (f_stricmp( pszToken, "CONTAINS") == 0 || f_stricmp( pszToken, "CONTAIN") == 0) { *peOperator = FLM_CONTAINS_OP; } else if (f_stricmp( pszToken, "&") == 0 || f_stricmp( pszToken, "BITAND") == 0) { *peOperator = FLM_BITAND_OP; } else if (f_stricmp( pszToken, "|") == 0 || f_stricmp( pszToken, "BITOR") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "^") == 0 || f_stricmp( pszToken, "BITXOR") == 0) { *peOperator = FLM_AND_OP; } else if (f_stricmp( pszToken, "*") == 0) { *peOperator = FLM_MULT_OP; } else if (f_stricmp( pszToken, "/") == 0) { *peOperator = FLM_DIV_OP; } else if (f_stricmp( pszToken, "%") == 0 || f_stricmp( pszToken, "MOD") == 0) { *peOperator = FLM_MOD_OP; } else if (f_stricmp( pszToken, "+") == 0) { *peOperator = FLM_PLUS_OP; } else if (f_stricmp( pszToken, "-") == 0) { *peOperator = FLM_MINUS_OP; } else { bIsOperator = FALSE; } return( bIsOperator); } /**************************************************************************** Desc: Map a field type to a query value type. ****************************************************************************/ FINLINE QTYPES mapFieldTypeToValType( FLMUINT uiFieldType) { switch (uiFieldType) { case FLM_TEXT_TYPE: return( FLM_UNICODE_VAL); case FLM_NUMBER_TYPE: return( FLM_UINT64_VAL); case FLM_CONTEXT_TYPE: return( FLM_REC_PTR_VAL); case FLM_BINARY_TYPE: return( FLM_BINARY_VAL); } // Should never reach here - but some compilers don't like it when // all of the cases are not covered. flmAssert( 0); return( NO_TYPE); } /**************************************************************************** Desc: Determine if a token is a field. ****************************************************************************/ FSTATIC FLMBOOL tokenIsField( const char * pszToken, F_NameTable * pNameTable, FLMUINT * puiFieldPath, QTYPES * peValueType, FLMBOOL bAllowNamesOnly, FLMBOOL bMustBeField) { FLMBOOL bIsField = TRUE; FLMUINT uiFieldCount = 0; FLMUINT uiFieldNum; FLMUINT uiDictType; FLMBOOL bIsTagName; FLMBOOL bIsNum; FLMBOOL bIsDrnNum; FLMBOOL bIsTagNum; FLMUINT64 ui64Num; FLMUINT uiNum; FLMUINT64 ui64TagNum; FLMUINT uiFieldType; char szNameBuf[ 128]; char * pszNameEnd; // Parse out each part of the name. while( *pszToken) { // Path components are separated by periods or spaces. They may be names or // tag numbers. pszNameEnd = &szNameBuf[ 0]; while( *pszToken && *pszToken != '.') { *pszNameEnd++ = *pszToken; pszToken++; } *pszNameEnd = 0; // If we detect a period, the bAllowNamesOnly restriction is lifted. if( *pszToken == '.') { bAllowNamesOnly = FALSE; } if( pNameTable) { bIsTagName = pNameTable->getFromTagTypeAndName( NULL, szNameBuf, FLM_FIELD_TAG, &uiFieldNum, &uiFieldType); } else { bIsTagName = FALSE; } bIsNum = tokenIsNum( szNameBuf, (FLMUINT64)(FLM_MAX_UINT16), &ui64Num); uiNum = (FLMUINT)ui64Num; bIsDrnNum = FALSE; if( f_stricmp( szNameBuf, "DRN") == 0) { bIsDrnNum = TRUE; uiNum = FLM_RECID_FIELD; } bIsTagNum = (f_strnicmp( szNameBuf, "TAG_", 4) == 0 && tokenIsNum( &szNameBuf[ 4], (FLMUINT64)65535, &ui64TagNum)) ? TRUE : FALSE; // See if the token is a field name. // // NOTE: If it is a number and also comes out as a legitimate tag name, // we have a bit of an ambiguity. In that case, we will ignore the tag // name if the bMustBeField flag is TRUE, because that means it was // specified inside a field() construct: e.g., field( 60). In these // cases, we would take the literal number, even though the string "60" // also happens to map to a tag name. if( !bIsTagName || (bIsNum && bMustBeField)) { if( bAllowNamesOnly) { bIsField = FALSE; goto Exit; } // Not a field name, see if it is a number in the proper // range for field numbers. if( bIsNum || bIsDrnNum) { uiFieldNum = uiNum; } else if( bIsTagNum) { uiFieldNum = (FLMUINT)ui64TagNum; } else { bIsField = FALSE; goto Exit; } if( !uiFieldNum || uiFieldNum > 0xFFFF) { bIsField = FALSE; goto Exit; } if( uiFieldNum >= FLM_UNREGISTERED_TAGS) { // This is only valid if the field name began with "TAG_" // Otherwise, we will treat it as a value, not a field. if( !bIsTagNum) { bIsField = FALSE; goto Exit; } *peValueType = NO_TYPE; } else if( uiFieldNum == FLM_RECID_FIELD) { *peValueType = FLM_REC_PTR_VAL; } else if( pNameTable && pNameTable->getFromTagNum( uiFieldNum, NULL, NULL, 0, &uiDictType, &uiFieldType) && uiDictType == FLM_FIELD_TAG) { *peValueType = mapFieldTypeToValType( uiFieldType); } else if( bMustBeField) { // Couldn't find in name table, or name table was NULL, // so we don't know its type, but we know that this is // inside a field() construct - so we have to take it as is. *peValueType = NO_TYPE; } else { bIsField = FALSE; goto Exit; } } else { *peValueType = mapFieldTypeToValType( uiFieldType); // If the name happens to be a number and we are only allowing // names, don't use this one. if( bIsNum && bAllowNamesOnly) { bIsField = FALSE; goto Exit; } } puiFieldPath [uiFieldCount++] = uiFieldNum; // If *pszToken is non-null (a period), skip it. if( *pszToken) { pszToken++; } } // A field count of zero means we didn't get anything that // looked like a legitimate field. if( !uiFieldCount) { bIsField = FALSE; goto Exit; } // Null terminate the field path. puiFieldPath[ uiFieldCount] = 0; Exit: return( bIsField); } /**************************************************************************** Desc: Allocate buffer space for a query value. ****************************************************************************/ FSTATIC RCODE allocValueSpace( void ** ppvVal, FLMUINT * puiValBufSize, FLMUINT uiNewSize) { RCODE rc = FERR_OK; void * pvVal; if( RC_BAD( rc = f_alloc( uiNewSize, &pvVal))) { goto Exit; } f_free( ppvVal); *ppvVal = pvVal; *puiValBufSize = uiNewSize; Exit: return( rc); } /**************************************************************************** Desc: Get a unicode value from a token. ****************************************************************************/ RCODE tokenGetUnicode( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMUINT uiLen = f_strlen( pszToken) + 1; FLMUNICODE * puzTmp; // Just make sure we have enough to cover the maximum number // of unicode characters that could be created. if( *puiValBufSize < uiLen * sizeof( FLMUNICODE)) { if( RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, uiLen * sizeof( FLMUNICODE)))) { goto Exit; } } // Convert the ASCII to unicode. puzTmp = (FLMUNICODE *)(*ppvVal); while (*pszToken) { if (*pszToken != '~' || *(pszToken + 1) != '[') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } else { const char * pszSave = pszToken; FLMUNICODE * puzSave = puzTmp; // Skip the ~[ pszToken += 2; // Unicode characters may be specified as numbers // separated by commas and/or spaces. The list // ends when we hit a ']' character or a non-numeric // value. while (*pszToken && *pszToken != ']') { char szNumBuf[ 32]; char * pszNumEnd; FLMBOOL bIsNum; FLMUINT64 ui64Num; // Skip white space and commas while (*pszToken && (*pszToken <= ' ' || *pszToken == ',')) { pszToken++; } // Number should start here. pszNumEnd = &szNumBuf[ 0]; while( *pszToken > ' ' && *pszToken != ',' && *pszToken != ']') { *pszNumEnd++ = *pszToken; pszToken++; } *pszNumEnd = 0; // If we ended on a non-null character, see if it is a number. // If not, we will quit processing. if( !(*pszToken)) { pszToken = pszSave; break; } bIsNum = tokenIsNum( szNumBuf, (FLMUINT64)(FLM_MAX_UINT16), &ui64Num); if( bIsNum && ui64Num && ui64Num <= 0xFFFE) { *puzTmp++ = (FLMUNICODE)ui64Num; } else { // Resetting pszToken to pszSave so that we will know that // we did not successfully process everything in the ~[]. pszToken = pszSave; break; } } // If we hit a ']', we successfully processed whatever was between // the ~[], and we can skip the ']'. If we did not hit the ']' // we were not able to interpret everything between the ~[] as unicode // characters, so we should just go back and redo everything between // the ~[] as ascii - including the ~[]. if (*pszToken == ']') { pszToken++; } else { // UNICODE-ize the rest of the string, including the ~[ and // up to a trailing ']', if any. pszToken = pszSave; puzTmp = puzSave; while (*pszToken && *pszToken != ']') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } // Get trailing ']', if any if (*pszToken == ']') { *puzTmp++ = (FLMUNICODE)(*pszToken); pszToken++; } } } } // Null terminate *puzTmp = 0; Exit: *puiValLen = 0; return( rc); } /**************************************************************************** Desc: Get a binary value from a token. ****************************************************************************/ FSTATIC RCODE tokenGetBinary( const char * pszToken, void ** ppvVal, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMUINT uiLen = f_strlen( pszToken); FLMBYTE * pucTmp; FLMUINT uiBinaryChars; FLMUINT uiNibble; FLMBOOL bFirstNibble; // Just make sure we have enough to cover the maximum number // of binary characters that could be created. if (*puiValBufSize < uiLen / 2 + 1) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, uiLen / 2 + 1))) { goto Exit; } } // Convert the ASCII to unicode. pucTmp = (FLMBYTE *)(*ppvVal); uiBinaryChars = 0; bFirstNibble = TRUE; while (*pszToken) { if (*pszToken >= '0' && *pszToken <= '9') { uiNibble = (FLMUINT)(*pszToken - '0'); } else if (*pszToken >= 'A' && *pszToken <= 'F') { uiNibble = (FLMUINT)(*pszToken - 'A' + 10); } else if (*pszToken >= 'a' && *pszToken <= 'f') { uiNibble = (FLMUINT)(*pszToken - 'a' + 10); } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } pszToken++; if (bFirstNibble) { uiBinaryChars++; *pucTmp = (FLMBYTE)(uiNibble << 4); bFirstNibble = FALSE; } else { (*pucTmp) |= (FLMBYTE)(uiNibble); bFirstNibble = TRUE; pucTmp++; } } // If we ended on an odd number of characters, shift our last character // down by four bits. if (!bFirstNibble) { (*pucTmp) >>= 4; } *puiValLen = uiBinaryChars; Exit: return( rc); } /**************************************************************************** Desc: Parse a value and return it. ****************************************************************************/ FSTATIC RCODE tokenGetValue( QTYPES eValueType, const char * pszToken, FLMBOOL bQuoted, void ** ppvVal, QTYPES * peValType, FLMUINT * puiValLen, FLMUINT * puiValBufSize) { RCODE rc = FERR_OK; FLMINT32 i32Num; FLMUINT32 ui32Num; FLMUINT64 ui64Num; FLMINT64 i64Num; if (bQuoted || eValueType == FLM_UNICODE_VAL) { *peValType = FLM_UNICODE_VAL; if (RC_BAD( rc = tokenGetUnicode( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } else if (eValueType == FLM_UINT32_VAL || eValueType == FLM_UINT64_VAL || eValueType == FLM_INT32_VAL || eValueType == FLM_INT64_VAL) { FLMUINT64 ui64Max; FLMBOOL bNeg; if (*pszToken == '-') { bNeg = TRUE; pszToken++; } else { bNeg = FALSE; } if (eValueType == FLM_UINT32_VAL) { if (bNeg) { ui64Max = (FLMUINT64)(FLM_MAX_INT32) + 1; } else { ui64Max = (FLMUINT64)(FLM_MAX_UINT32); } } else if (eValueType == FLM_INT32_VAL) { if (bNeg) { ui64Max = (FLMUINT64)(FLM_MAX_INT32) + 1; } else { ui64Max = (FLMUINT64)(FLM_MAX_INT32); } } else if (eValueType == FLM_UINT64_VAL) { if (bNeg) { ui64Max = (FLMUINT64)(FLM_MAX_INT64) + 1; } else { ui64Max = FLM_MAX_UINT64; } } else { if (bNeg) { ui64Max = (FLMUINT64)(FLM_MAX_INT64) + 1; } else { ui64Max = (FLMUINT64)(FLM_MAX_INT64); } } if (tokenIsNum( pszToken, ui64Max, &ui64Num)) { if (*puiValBufSize < sizeof( FLMUINT64)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMUINT64)))) { goto Exit; } } if (bNeg || eValueType == FLM_INT32_VAL || eValueType == FLM_INT64_VAL) { if (eValueType == FLM_INT32_VAL || eValueType == FLM_UINT32_VAL) { if (bNeg) { if (ui64Num == ui64Max) { // If the number is negative, the maximum will have been // set up to be the minimum negative 32 bit integer. i32Num = FLM_MIN_INT32; } else { i32Num = -((FLMINT32)ui64Num); } } else { i32Num = (FLMINT32)ui64Num; } *peValType = FLM_INT32_VAL; *((FLMINT32 *)(*ppvVal)) = i32Num; *puiValLen = sizeof( FLMINT32); } else { if (bNeg) { if (ui64Num == ui64Max) { // If the number is negative, the maximum will have been // set up to be the minimum negative 64 bit integer. i64Num = FLM_MIN_INT64; } else { i64Num = -((FLMINT64)ui64Num); } } else { i64Num = (FLMINT64)ui64Num; } *peValType = FLM_INT64_VAL; *((FLMINT64 *)(*ppvVal)) = i64Num; *puiValLen = sizeof( FLMINT64); } } else { if (eValueType == FLM_UINT32_VAL) { ui32Num = (FLMUINT32)ui64Num; *peValType = FLM_UINT32_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } else { *peValType = FLM_UINT64_VAL; *((FLMUINT64 *)(*ppvVal)) = ui64Num; *puiValLen = sizeof( FLMUINT64); } } } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_REC_PTR_VAL) { if (*puiValBufSize < sizeof( FLMUINT32)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMUINT32)))) { goto Exit; } } if (tokenIsNum( pszToken, (FLMUINT64)(FLM_MAX_UINT32), &ui64Num)) { ui32Num = (FLMUINT32)ui64Num; *peValType = FLM_REC_PTR_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_BOOL_VAL) { if (*puiValBufSize < sizeof( FLMBOOL)) { if (RC_BAD( rc = allocValueSpace( ppvVal, puiValBufSize, sizeof( FLMBOOL)))) { goto Exit; } } if (tokenIsNum( pszToken, FLM_MAX_UINT64, &ui64Num)) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = ui64Num ? TRUE : FALSE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "false") == 0) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = FALSE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "true") == 0) { *peValType = FLM_BOOL_VAL; *((FLMBOOL *)(*ppvVal)) = TRUE; *puiValLen = sizeof( FLMBOOL); } else if (f_stricmp( pszToken, "unknown") == 0) { *peValType = FLM_BOOL_VAL; if (*ppvVal) { f_free( ppvVal); } *puiValBufSize = 0; *puiValLen = 0; } else { rc = RC_SET( FERR_BAD_FIELD_TYPE); goto Exit; } } else if (eValueType == FLM_BINARY_VAL) { *peValType = FLM_BINARY_VAL; if (RC_BAD( rc = tokenGetBinary( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } else { // Field type is unknown. Try converting to // a number. If that doesn't work, simply // use text. if (tokenIsNum( pszToken, FLM_MAX_UINT64, &ui64Num)) { if (ui64Num <= (FLMUINT64)(FLM_MAX_UINT32)) { ui32Num = (FLMUINT32)ui64Num; *peValType = FLM_UINT32_VAL; *((FLMUINT32 *)(*ppvVal)) = ui32Num; *puiValLen = sizeof( FLMUINT32); } else { *peValType = FLM_UINT64_VAL; *((FLMUINT64 *)(*ppvVal)) = ui64Num; *puiValLen = sizeof( FLMUINT64); } } else { *peValType = FLM_UNICODE_VAL; if (RC_BAD( rc = tokenGetUnicode( pszToken, ppvVal, puiValLen, puiValBufSize))) { goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: Parse a query criteria string and populate an HFCURSOR from it. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmParseQuery( HFCURSOR hCursor, F_NameTable * pNameTable, const char * pszQueryCriteria) { RCODE rc = FERR_OK; const char * pszTmp = pszQueryCriteria; QTYPES eTokenType; QTYPES ePriorTokenType; void * pvVal = NULL; FLMUINT uiValBufSize; FLMUINT uiValLen; QTYPES eLastFldType = NO_TYPE; FLMUINT uiFieldPath [20]; char * pszToken = NULL; FLMUINT uiTokenBufSize; FLMBOOL bQuoted; FLMUINT64 ui64Num; // Allocate space for tokens and values. These will be // reallocated as needed. uiValBufSize = 512; if (RC_BAD( rc = f_alloc( uiValBufSize, &pvVal))) { goto Exit; } uiTokenBufSize = 512; if (RC_BAD( rc = f_alloc( uiTokenBufSize, &pszToken))) { goto Exit; } ePriorTokenType = NO_TYPE; for (;;) { // Get the next token. if( RC_BAD( rc = tokenGet( &pszTmp, &pszToken, &bQuoted, &eTokenType, &uiTokenBufSize))) { goto Exit; } // If the token is empty, we are done if (!(*pszToken)) { break; } // If eTokenType is not explicit, attempt to figure it out if (eTokenType == NO_TYPE) { if (bQuoted) { eLastFldType = FLM_UNICODE_VAL; Get_Value: if (RC_BAD( rc = tokenGetValue( eLastFldType, pszToken, bQuoted, &pvVal, &eTokenType, &uiValLen, &uiValBufSize))) { goto Exit; } if (RC_BAD( rc = FlmCursorAddValue( hCursor, eTokenType, pvVal, uiValLen))) { goto Exit; } eLastFldType = NO_TYPE; } else if (tokenIsOperator( pszToken, &eTokenType)) { // If this token is a minus operator, and the prior token was // a comparison operator, arithmetic operator, or // left paren, change this token to a negative sign. if (eTokenType == FLM_MINUS_OP && (ePriorTokenType == FLM_LPAREN_OP || IS_COMPARE_OP( ePriorTokenType) || IS_ARITH_OP( ePriorTokenType))) { eTokenType = FLM_NEG_OP; } if (RC_BAD( rc = FlmCursorAddOp( hCursor, eTokenType, TRUE))) { goto Exit; } // If the operator is not a comparison operator // and not an arithmetic operator and not a negative // operator, then the last field type is now unknown. if (!IS_COMPARE_OP( eTokenType) && !IS_ARITH_OP( eTokenType) && eTokenType != FLM_NEG_OP) { eLastFldType = NO_TYPE; } } else if (tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, TRUE, FALSE)) { Add_Field: eTokenType = FLM_FLD_PATH; if (RC_BAD( rc = FlmCursorAddFieldPath( hCursor, uiFieldPath, 0))) { goto Exit; } } else if (IS_COMPARE_OP( ePriorTokenType) || IS_ARITH_OP( ePriorTokenType) || ePriorTokenType == FLM_NEG_OP) { if (ePriorTokenType == FLM_NEG_OP) { eLastFldType = FLM_INT32_VAL; } goto Get_Value; } else if (tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, FALSE, FALSE)) { goto Add_Field; } else if (tokenIsNum( pszToken, FLM_MAX_UINT64, &ui64Num)) { goto Get_Value; } else { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } else { switch (eTokenType) { case FLM_FLD_PATH: { if (!tokenIsField( pszToken, pNameTable, uiFieldPath, &eLastFldType, FALSE, TRUE)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } goto Add_Field; } case FLM_UNICODE_VAL: case FLM_INT32_VAL: case FLM_UINT32_VAL: case FLM_UINT64_VAL: case FLM_INT64_VAL: case FLM_REC_PTR_VAL: case FLM_BINARY_VAL: case FLM_BOOL_VAL: { eLastFldType = eTokenType; goto Get_Value; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } } ePriorTokenType = eTokenType; } Exit: if( pszToken) { f_free( &pszToken); } if( pvVal) { f_free( &pvVal); } return( rc); } /**************************************************************************** Desc: Determine if a token is a number. ****************************************************************************/ FSTATIC FLMBOOL tokenIsNum( const char * pszToken, FLMUINT64 ui64Max, FLMUINT64 * pui64Num) { FLMBOOL bIsNum = TRUE; FLMUINT64 ui64Num; FLMBOOL bAllowHex = FALSE; if (*pszToken == 0) { bIsNum = FALSE; goto Exit; } if (*pszToken == '0' && (*(pszToken + 1) == 'x' || *(pszToken + 1) == 'X')) { pszToken += 2; bAllowHex = TRUE; } ui64Num = 0; while (*pszToken) { if (*pszToken >= '0' && *pszToken <= '9') { if (!bAllowHex) { if (ui64Num > ui64Max / 10) { // Number would overflow. bIsNum = FALSE; goto Exit; } else { ui64Num *= 10; } } else { if (ui64Num > ui64Max >> 4) { // Number would overflow. bIsNum = FALSE; goto Exit; } ui64Num <<= 4; } if (ui64Num > ui64Max - (FLMUINT64)(*pszToken - '0')) { // Number would overflow. bIsNum = FALSE; goto Exit; } ui64Num += (FLMUINT64)(*pszToken - '0'); } else if (bAllowHex) { if (ui64Num > ui64Max >> 4) { // Number would overflow. bIsNum = FALSE; goto Exit; } if (*pszToken >= 'A' && *pszToken <= 'F') { ui64Num <<= 4; if (ui64Num > ui64Max - (FLMUINT64)(*pszToken - 'A' + 10)) { // Number would overflow. bIsNum = FALSE; goto Exit; } ui64Num += (FLMUINT)(*pszToken - 'A') + 10; } else if (*pszToken >= 'a' && *pszToken <= 'f') { ui64Num <<= 4; if (ui64Num > ui64Max - (FLMUINT64)(*pszToken - 'a' + 10)) { // Number would overflow. bIsNum = FALSE; goto Exit; } ui64Num += (FLMUINT)(*pszToken - 'a') + 10; } else { bIsNum = FALSE; goto Exit; } } else { bIsNum = FALSE; goto Exit; } pszToken++; } *pui64Num = ui64Num; Exit: return( bIsNum); } libflaim-4.9.966/src/fqread.cpp0000644000175000017500000002744610510774540017637 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query record retrieval // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqread.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmCurCSPerformRead( CURSOR * pCursor, eFlmFuncs eFlmFuncId, FlmRecord ** ppRecordRV, FLMUINT * puiDrnRV, FLMUINT * puiCountRV); FSTATIC RCODE flmCurGetDRNRec( CURSOR * pCursor, FLMUINT uiDRN, FlmRecord ** ppRecord); /**************************************************************************** Desc: Gets the requested record, DRN, or count over the CS line. ****************************************************************************/ FSTATIC RCODE flmCurCSPerformRead( CURSOR * pCursor, eFlmFuncs eFlmFuncId, FlmRecord ** ppRecordRV, FLMUINT * puiDrnRV, FLMUINT * puiCountRV) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = pCursor->pCSContext; FCL_WIRE Wire( pCSContext); void * pvMark = pCSContext->pool.poolMark(); FLMUINT uiCSOp = 0; // If there is no VALID id for the cursor, get one. if (pCursor->uiCursorId == FCS_INVALID_ID) { if (RC_BAD( rc = flmInitCurCS( pCursor))) { goto Exit; } } Wire.setFDB( pCursor->pDb); // Set the temporary pool Wire.setPool( &pCSContext->pool); // Set the record object so that it can be re-used, // if possible if (ppRecordRV) { Wire.setRecord( *ppRecordRV); if (*ppRecordRV) { (*ppRecordRV)->Release(); *ppRecordRV = NULL; } } // Map Function ID to CS Op switch (eFlmFuncId) { case FLM_CURSOR_REC_COUNT: uiCSOp = FCS_OP_ITERATOR_COUNT; break; case FLM_CURSOR_FIRST: uiCSOp = FCS_OP_ITERATOR_FIRST; break; case FLM_CURSOR_LAST: uiCSOp = FCS_OP_ITERATOR_LAST; break; case FLM_CURSOR_NEXT: uiCSOp = FCS_OP_ITERATOR_NEXT; break; case FLM_CURSOR_PREV: uiCSOp = FCS_OP_ITERATOR_PREV; break; case FLM_CURSOR_FIRST_DRN: uiCSOp = FCS_OP_ITERATOR_FIRST; break; case FLM_CURSOR_LAST_DRN: uiCSOp = FCS_OP_ITERATOR_LAST; break; case FLM_CURSOR_NEXT_DRN: uiCSOp = FCS_OP_ITERATOR_NEXT; break; case FLM_CURSOR_PREV_DRN: uiCSOp = FCS_OP_ITERATOR_PREV; break; default: flmAssert( 0); // Unsupported flaim function hit. } // Send a request to perform the read. if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_ITERATOR, uiCSOp))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_ITERATOR_ID, pCursor->uiCursorId))) { goto Transmission_Error; } if (puiDrnRV && !ppRecordRV) { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_FLAGS, FCS_ITERATOR_DRN_FLAG))) { goto Transmission_Error; } } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } if (puiCountRV) { *puiCountRV = (FLMUINT)Wire.getCount(); } if (ppRecordRV) { if ((*ppRecordRV = Wire.getRecord()) != NULL) { (*ppRecordRV)->AddRef(); } } if (puiDrnRV) { if (ppRecordRV && *ppRecordRV) { *puiDrnRV = (*ppRecordRV)->getID(); } else { *puiDrnRV = Wire.getDrn(); } } rc = Wire.getRCode(); Exit: pCSContext->pool.poolReset( pvMark); return( rc); Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } /**************************************************************************** Desc: Gets the requested record. ****************************************************************************/ FLMEXP RCODE FLMAPI flmCurPerformRead( eFlmFuncs eFlmFuncId, HFCURSOR hCursor, FLMBOOL bReadForward, FLMBOOL bFirstRead, FLMUINT * puiSkipCount, FlmRecord ** ppRecord, FLMUINT * puiDrn ) { RCODE rc = FERR_OK; FLMUINT uiDrn = 0; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } // Make sure the record is clear. if (ppRecord && *ppRecord) { (*ppRecord)->Release(); *ppRecord = NULL; } if (pCursor->bEliminateDups) { if( pCursor->pDRNSet && (bFirstRead || !pCursor->bOptimized)) { pCursor->pDRNSet->Release(); pCursor->pDRNSet = NULL; } } if (!bFirstRead) { if (pCursor->ReadRc == FERR_EOF_HIT) { if (bReadForward) { rc = pCursor->ReadRc; goto Save_RecId; } else { bFirstRead = TRUE; } } else if (pCursor->ReadRc == FERR_BOF_HIT) { if (!bReadForward) { rc = pCursor->ReadRc; goto Save_RecId; } else { bFirstRead = TRUE; } } // No read has been performed yet - or the last // read returned an error besides eof or bof. else if (!pCursor->uiLastRecID) { bFirstRead = TRUE; } } pCursor->ReadRc = FERR_OK; if (pCursor->pCSContext) { rc = flmCurCSPerformRead( pCursor, eFlmFuncId, ppRecord, &uiDrn, NULL); } else { // Optimize the query if necessary. if (!pCursor->bOptimized) { bFirstRead = TRUE; if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // If this is an empty query, return EOF or BOF. if (pCursor->bEmpty) { pCursor->rc = rc = (RCODE)((bReadForward) ? RC_SET( FERR_EOF_HIT) : RC_SET( FERR_BOF_HIT)); } else { pCursor->rc = rc = flmCurSearch( eFlmFuncId, pCursor, bFirstRead, bReadForward, NULL, puiSkipCount, ppRecord, &uiDrn); } } if (RC_BAD( rc)) { if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { pCursor->ReadRc = rc; } uiDrn = 0; } Save_RecId: // Set a flag indicating that this cursor has been repositioned. pCursor->bUsePrcntPos = FALSE; pCursor->uiLastRecID = uiDrn; Exit: if (puiDrn) { *puiDrn = uiDrn; } return( rc); } /**************************************************************************** Desc: Gets the requested record given a DRN. ****************************************************************************/ FSTATIC RCODE flmCurGetDRNRec( CURSOR * pCursor, FLMUINT uiDRN, FlmRecord ** ppRecord) { RCODE rc = FERR_OK; FDB * pDb = NULL; LFILE * pLFile; if (pCursor->pCSContext) { HFDB hDb = (HFDB)pCursor->pDb; FLMUINT uiContainer = pCursor->uiContainer; FCL_WIRE Wire( pCursor->pCSContext); Wire.setFDB( (FDB *)hDb); for (;;) { rc = FlmRecordRetrieve( hDb, uiContainer, uiDRN, FO_EXACT, ppRecord, NULL); if (rc != FERR_OLD_VIEW) { break; } if (RC_BAD( rc = Wire.doTransOp( FCS_OP_TRANSACTION_RESET, FLM_READ_TRANS, 0, 0))) { break; } } goto Exit; } pDb = pCursor->pDb; if (RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN, FALSE, NULL, NULL, ppRecord); if (rc == FERR_NOT_FOUND) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, pCursor->uiContainer, &pLFile))) { goto Exit; } if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN, ppRecord, NULL, NULL))) { goto Exit; } } Exit: if (pDb) { fdbExit( pDb); } return( rc); } /**************************************************************************** Desc: Retrieves the record currently pointed to by a cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorCurrent( HFCURSOR hCursor, FlmRecord ** ppRecord) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } *ppRecord = NULL; if (pCursor->uiLastRecID == 0) { if (RC_OK( rc = pCursor->ReadRc)) { rc = RC_SET( FERR_BOF_HIT); } } else if (RC_OK( pCursor->rc)) { if (RC_BAD( rc = flmCurGetDRNRec( pCursor, pCursor->uiLastRecID, ppRecord))) { goto Exit; } } else { rc = pCursor->rc; } Exit: return( rc); } /**************************************************************************** Desc: Retrieves the DRN of the current record in a set defined by a cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorCurrentDRN( HFCURSOR hCursor, FLMUINT * puiDrn) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } *puiDrn = 0; if (!pCursor->uiLastRecID) { if (RC_OK( rc = pCursor->ReadRc)) { rc = RC_SET( FERR_BOF_HIT); } } else if (RC_OK( pCursor->rc)) { *puiDrn = pCursor->uiLastRecID; rc = FERR_OK; } else { rc = pCursor->rc; } Exit: return( rc); } /**************************************************************************** Desc: Positions the cursor to a next or previous item at an offset relative to the current item and retrieves that item from the database. Note: Requests that position beyond the end of the result set will cause an EOF_HIT error to be returned. Likewise, requests that position before the beginning of the result set will cause a BOF_HIT error to be returned. Passing a relative position of 0 is invalid and will cause ILLEGAL_OP to be returned. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorMoveRelative( HFCURSOR hCursor, FLMINT * piPosition, FlmRecord ** ppRecord) { RCODE rc = FERR_OK; FLMINT iPosition; FLMUINT uiTmpPos; if ((iPosition = *piPosition) == 0) { flmAssert( 0); rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } uiTmpPos = (FLMUINT)((iPosition < 0) ? (FLMUINT)(-iPosition) : (FLMUINT)iPosition); rc = flmCurPerformRead( FLM_CURSOR_MOVE_RELATIVE, hCursor, (FLMBOOL)((iPosition > 0) ? TRUE : FALSE), FALSE, &uiTmpPos, ppRecord, NULL); *piPosition = (FLMINT)((iPosition < 0) ? (FLMINT)(iPosition + uiTmpPos) : (FLMINT)(iPosition - uiTmpPos)); Exit: return( rc); } /**************************************************************************** Desc: Returns the number of records in a set defined by a cursor. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorRecCount( HFCURSOR hCursor, FLMUINT * puiCount) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; RCODE TmpRc; FDB * pDb = NULL; FLMBOOL bSavedPosition = FALSE; if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } *puiCount = 0; if (pCursor->pCSContext) { rc = flmCurCSPerformRead( pCursor, FLM_CURSOR_REC_COUNT, NULL, NULL, puiCount); goto Exit2; } pDb = pCursor->pDb; if( RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // Save current position so we can restore it after doing the count. bSavedPosition = TRUE; rc = flmCurSearch( FLM_CURSOR_REC_COUNT, pCursor, TRUE, TRUE, puiCount, NULL, NULL, NULL); if (rc == FERR_EOF_HIT) { rc = FERR_OK; } Exit: // Restore saved cursor settings if necessary. if (bSavedPosition) { if (RC_BAD( TmpRc = flmCurRestorePosition( pCursor))) { if (RC_OK( rc)) { rc = TmpRc; } } } flmExit( FLM_CURSOR_REC_COUNT, pDb, rc); pCursor->rc = rc; Exit2: return( rc); } libflaim-4.9.966/src/fqkeys.cpp0000644000175000017500000021713010510774540017666 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query positioning keys // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqkeys.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define DOMAIN_TO_DRN(uiDomain) \ (FLMUINT)(((uiDomain) + 1) * 256 + 1) #define DRN_TO_DOMAIN(uiDrn) \ (FLMUINT)(((uiDrn) - 1) / 256 - 1) FSTATIC FLMINT flmPosKeyCompare( POS_KEY * pKey1, POS_KEY * pKey2); FSTATIC RCODE flmLoadPosKeys( CURSOR * pCursor, POS_KEY * pKeys, FLMUINT uiNumKeys, FLMBOOL bLeafLevel); FSTATIC RCODE flmKeyIsMatch( CURSOR * pCursor, IXD * pIxd, FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiDrn, POS_KEY * * ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize, FLMUINT uiKeyArrayGrowSize); FSTATIC RCODE flmExamineBlock( CURSOR * pCursor, IXD * pIxd, FLMBYTE * pucBlk, FSIndexCursor * pFSIndexCursor, FLMUINT ** ppuiChildBlockAddresses, FLMUINT * puiNumChildBlocks, FLMUINT * puiBlkAddressArrayAllocSize, POS_KEY * * ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize, FLMBOOL * pbHighKeyInRange); FSTATIC RCODE flmGetLastKey( FDB * pDb, CURSOR * pCursor, IXD * pIxd, LFILE * pLFile, FLMUINT uiBlockAddress, POS_KEY ** ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize); FSTATIC RCODE flmCurGetPosKeys( FDB * pDb, CURSOR * pCursor); FSTATIC FLMBOOL flmFindWildcard( FLMBYTE * pValue, FLMUINT * puiCharPos); FSTATIC RCODE flmAddKeyPiece( FLMUINT uiMaxKeySize, IFD * pIfd, FLMBOOL bDoMatchBegin, FLMBYTE * pFromKey, FLMUINT * puiFromKeyPos, FLMBOOL bFromAtFirst, FLMBYTE * pUntilKey, FLMUINT * puiUntilKeyPos, FLMBOOL bUntilAtEnd, FLMBYTE * pBuf, FLMUINT uiBufLen, FLMBOOL * pbDataTruncated, FLMBOOL * pbDoneBuilding); FSTATIC RCODE flmAddTextPiece( FLMUINT uiMaxKeySize, IFD * pIfd, FLMBOOL bCaseInsensitive, FLMBOOL bDoMatchBegin, FLMBOOL bDoFirstSubstring, FLMBOOL bTrailingWildcard, FLMBYTE * pFromKey, FLMUINT * puiFromKeyPos, FLMBOOL bFromAtFirst, FLMBYTE * pUntilKey, FLMUINT * puiUntilKeyPos, FLMBOOL bUntilAtEnd, FLMBYTE * pBuf, FLMUINT uiBufLen, FLMBOOL * pbDataTruncated, FLMBOOL * pbDoneBuilding, FLMBOOL * pbOriginalCharsLost); FSTATIC FLMBOOL flmSelectBestSubstr( FLMBYTE ** ppValue, FLMUINT * puiValueLen, FLMUINT uiIfdFlags, FLMBOOL * pbTrailingWildcard); FSTATIC FLMUINT flmCountCharacters( FLMBYTE * pValue, FLMUINT uiValueLen, FLMUINT uiMaxToCount, FLMUINT uiIfdFlags); /**************************************************************************** Desc: Compares the contents of the key buffers for two cursor positioning keys, returning one of the following values: <0 Indicates that the first key is less than the second. 0 Indicates that the two keys are equal. >0 Indicates that the first key is greater then the second. ****************************************************************************/ FSTATIC FLMINT flmPosKeyCompare( POS_KEY * pKey1, POS_KEY * pKey2 ) { FLMINT iCmp; if (pKey1->uiKeyLen > pKey2->uiKeyLen) { if ((iCmp = f_memcmp( pKey1->pucKey, pKey2->pucKey, pKey2->uiKeyLen)) == 0) { iCmp = 1; } } else if( pKey1->uiKeyLen < pKey2->uiKeyLen) { if ((iCmp = f_memcmp( pKey1->pucKey, pKey2->pucKey, pKey1->uiKeyLen)) == 0) { iCmp = -1; } } else { if ((iCmp = f_memcmp( pKey1->pucKey, pKey2->pucKey, pKey2->uiKeyLen)) == 0) { // Compare DRNs if everything else is the same. NOTE: DRNs are in // reverse order in the positioning key array. if (pKey1->uiDrn && pKey2->uiDrn) { if (pKey1->uiDrn > pKey2->uiDrn) { iCmp = -1; } else if (pKey1->uiDrn < pKey2->uiDrn) { iCmp = 1; } } } } return iCmp; } /**************************************************************************** Desc: Loads a set of positioning keys into a subquery's array, allocating it if necessary. ****************************************************************************/ FSTATIC RCODE flmLoadPosKeys( CURSOR * pCursor, POS_KEY * pKeys, FLMUINT uiNumKeys, FLMBOOL bLeafLevel ) { RCODE rc = FERR_OK; FLMUINT uiKeyCnt; FLMUINT uiRFactor; FLMUINT uiTotCnt; // If the B-tree was empty, the key array will be left NULL. if (!pKeys || !uiNumKeys) { goto Exit; } // Allocate the array of positioning keys in the subquery. uiKeyCnt = (uiNumKeys > FLM_MAX_POS_KEYS + 1) ? FLM_MAX_POS_KEYS + 1 : uiNumKeys; if (RC_BAD( rc = f_calloc( uiKeyCnt * sizeof( POS_KEY), &pCursor->pPosKeyArray))) { goto Exit; } pCursor->uiNumPosKeys = uiKeyCnt; pCursor->bLeafLevel = bLeafLevel; // If there are less keys than the number of slots in the positioning // key array, each key must be put into multiple slots. Calculate how // many slots correspond to each key (uiSlots), and then set the keys // into their corresponding slots. NOTE: it will often be the case that // the number of keys does not divide evenly into the number of slots in // the array. In these cases thare will be a remainder, uiRFactor. If // uiRFactor = n, the first n keys will be set into (uiSlots + 1) slots. if (uiNumKeys <= FLM_MAX_POS_KEYS + 1) { for (uiTotCnt = 0; uiTotCnt < uiKeyCnt; uiTotCnt++) { f_memcpy( &pCursor->pPosKeyArray[ uiTotCnt], &pKeys[ uiTotCnt], sizeof( POS_KEY)); // NOTE: we're keeping this memory for the positioning key to which // it is being copied. pKeys [uiTotCnt].pucKey = NULL; } } // If there are more keys than the number of slots in the positioning // key array, a certain number of keys must be skipped for each key that // is set in the array. Calculate how many keys must be skipped for each // slot (uiIntervalSize), and then iterate through the passed-in set of // keys, setting the appropriate ones into their corresponding slots. // NOTE: it will often be the case that the number of slots in the array // does not divide evenly into the number of keys. In these cases there // will be a remainder (uiRFactor). Where uiRFactor = n, // (uiIntervalSize + 1) keys will be skipped before each of the first n // slots in the array are filled. else { FLMUINT uiLoopCnt; FLMUINT uiIntervalSize = (uiNumKeys - 2) / (FLM_MAX_POS_KEYS - 1) - 1; uiRFactor = (uiNumKeys - 2) % (FLM_MAX_POS_KEYS - 1); f_memcpy( &pCursor->pPosKeyArray[ 0], &pKeys[ 0], sizeof( POS_KEY)); f_memcpy( &pCursor->pPosKeyArray[ 1], &pKeys[ 1], sizeof( POS_KEY)); // NOTE: we're keeping this memory for the positioning key to which // it is being copied. pKeys [0].pucKey = NULL; pKeys [1].pucKey = NULL; uiKeyCnt = 2; for( uiTotCnt = 2; uiTotCnt < FLM_MAX_POS_KEYS; uiTotCnt++) { for( uiLoopCnt = 0; uiLoopCnt < uiIntervalSize; uiLoopCnt++) { f_free( &pKeys[ uiKeyCnt].pucKey); uiKeyCnt++; } if( uiRFactor) { f_free( &pKeys[ uiKeyCnt].pucKey); uiKeyCnt++; uiRFactor--; } f_memcpy( &pCursor->pPosKeyArray[ uiTotCnt], &pKeys[ uiKeyCnt], sizeof( POS_KEY)); // NOTE: we're keeping this memory for the positioning key to which // it is being copied. pKeys [uiKeyCnt].pucKey = NULL; uiKeyCnt++; } // Make sure the last key in the positioning key array is the last // key in the result set, then free the memory used for the pKey array. f_memcpy( &pCursor->pPosKeyArray[ FLM_MAX_POS_KEYS], &pKeys[ uiNumKeys - 1], sizeof( POS_KEY)); pKeys [uiNumKeys - 1].pucKey = NULL; while (uiKeyCnt < uiNumKeys - 1) { f_free( &pKeys[ uiKeyCnt].pucKey); uiKeyCnt++; } } Exit: return( rc); } /**************************************************************************** Desc: Evaluates an index key against selection criteria, and adds it to the passed-in key array. ****************************************************************************/ FSTATIC RCODE flmKeyIsMatch( CURSOR * pCursor, IXD * pIxd, FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiDrn, POS_KEY * * ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize, FLMUINT uiKeyArrayGrowSize ) { RCODE rc = FERR_OK; SUBQUERY * pSubQuery = pCursor->pSubQueryList; FlmRecord * pKey = NULL; FLMBOOL bHaveMatch = FALSE; FLMUINT uiResult; POS_KEY * pPosKey; // If pSubQuery->bDoKeyMatch is FALSE, the selection criteria for this // query are satisfied by a contiguous set of index keys. Therefore, // there is no need to evaluate keys against the selection criteria. // We have already established that the passed-in key falls within // the range of keys that contains the result set of the query. // NOTE: bDoRecMatch cannot ever be set, otherwise, positioning is not // allowed. bHaveMatch = !pSubQuery->OptInfo.bDoKeyMatch; if (!bHaveMatch) { // Get the key in the form of a FlmRecord object. if (RC_BAD( rc = flmIxKeyOutput( pIxd, pucKey, uiKeyLen, &pKey, TRUE))) { goto Exit; } pKey->setID( uiDrn); // Evaluate the key against the subquery - there will only // be one at this point. if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pKey, TRUE, &uiResult))) { if (rc == FERR_TRUNCATED_KEY) { rc = FERR_OK; } else { goto Exit; } } bHaveMatch = (uiResult == FLM_TRUE) ? TRUE : FALSE; } if (bHaveMatch) { if (*puiNumKeys == *puiKeyArrayAllocSize) { if (RC_BAD( rc = f_recalloc( (*puiKeyArrayAllocSize + uiKeyArrayGrowSize) * sizeof( POS_KEY), ppKeys))) { goto Exit; } (*puiKeyArrayAllocSize) += uiKeyArrayGrowSize; } pPosKey = &((*ppKeys)[*puiNumKeys]); if (RC_BAD( rc = f_calloc( uiKeyLen, &pPosKey->pucKey))) { goto Exit; } f_memcpy( pPosKey->pucKey, pucKey, uiKeyLen); pPosKey->uiKeyLen = uiKeyLen; pPosKey->uiDrn = uiDrn; (*puiNumKeys)++; } Exit: if (pKey) { pKey->Release(); } return( rc); } /**************************************************************************** Desc: Examines an index B-tree block to find the keys in it that could be used to position within a cursor's result set. Visit:This code NEEDS to use the b-tree routines and NOT use the low level format codes to go to the next element or key. Other problems include doing the same work for each element even though you are at the same level of the b-tree. ****************************************************************************/ FSTATIC RCODE flmExamineBlock( CURSOR * pCursor, IXD * pIxd, FLMBYTE * pucBlk, FSIndexCursor * pFSIndexCursor, FLMUINT ** ppuiChildBlockAddresses, FLMUINT * puiNumChildBlocks, FLMUINT * puiBlkAddressArrayAllocSize, POS_KEY * * ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize, FLMBOOL * pbHighKeyInRange ) { RCODE rc = FERR_OK; FLMBYTE ucFromKey [MAX_KEY_SIZ]; FLMUINT uiFromKeyLen; FLMBYTE ucUntilKey [MAX_KEY_SIZ]; FLMUINT uiUntilKeyLen; FLMUINT uiUntilDrn = 0; FLMBOOL bRangeOverlaps; FLMBOOL bUntilKeyInSet; FLMBOOL bUntilKeyPastEndOfKeys; FLMUINT uiDomain; DIN_STATE dinState; FLMBOOL bFirstRef; FLMUINT uiEndOfBlock = FB2UW( &pucBlk [BH_BLK_END]); FLMUINT uiCurrElmOffset = BH_OVHD; FLMUINT uiBlkType = (FLMUINT)BH_GET_TYPE( pucBlk); FLMUINT uiElmLength; FLMBYTE * pucElement; FLMBYTE * pucElm; FLMBYTE * pucElmKey; FLMBYTE * pucElmRecord; FLMBYTE * pucChildBlkAddr; FLMUINT uiChildBlkAddr; FLMUINT uiElmRecLen; FLMUINT uiElmKeyLen; FLMUINT uiElmPKCLen; FLMUINT uiElmOvhd; // This loop moves across a database block from the leftmost element to the // rightmost. Each contiguous pair of elements is viewed as a "key range", // where the first key in the pair is the start key and the second is the // end key. In the loop, each key range is checked to see if it overlaps // with any part of the query's result set. If it does, two things happen: // first, the down pointer from the end key is added to a passed-in list; // second, the end key is checked to see if it satisfies the query's // selection criteria. If it does, it is added to a passed-in list of // positioning keys. // NOTE: until key is given a key length of 0 so that in the first iteration, // the key range will be from FO_FIRST to the leftmost key in the block. if( uiBlkType == BHT_LEAF) { uiElmOvhd = BBE_KEY; } else if( uiBlkType == BHT_NON_LEAF_DATA) { uiElmOvhd = BNE_DATA_OVHD; } else if( uiBlkType == BHT_NON_LEAF) { uiElmOvhd = BNE_KEY_START; } else { uiElmOvhd = BNE_KEY_COUNTS_START; } uiUntilKeyLen = 0; bUntilKeyPastEndOfKeys = FALSE; bFirstRef = TRUE; while (uiCurrElmOffset < uiEndOfBlock) { // Move the until key into the start key buffer. if (uiUntilKeyLen) { f_memcpy( ucFromKey, ucUntilKey, uiUntilKeyLen); } uiFromKeyLen = uiUntilKeyLen; pucElement = &pucBlk [uiCurrElmOffset]; pucElm = pucElement; uiDomain = FSGetDomain( &pucElm, uiElmOvhd); if (uiBlkType == BHT_LEAF) { uiElmLength = (FLMUINT)(BBE_LEN( pucElement)); pucElmKey = &pucElement [BBE_KEY]; pucElmRecord = BBE_REC_PTR( pucElement); uiElmRecLen = BBE_GET_RL( pucElement); if (bFirstRef) { RESET_DINSTATE( dinState); uiUntilDrn = SENNextVal( &pucElm); bFirstRef = FALSE; } else { FLMUINT uiRefSize = uiElmRecLen - (FLMUINT)(pucElm - pucElmRecord); if (dinState.uiOffset < uiRefSize) { // Not at end, read current value. DINNextVal( pucElm, &dinState); } if (dinState.uiOffset >= uiRefSize) { uiCurrElmOffset += uiElmLength; bFirstRef = TRUE; // No need to go any further if we have run // off the end of the list of keys for the query. if (bUntilKeyPastEndOfKeys) { break; } else { continue; } } else { DIN_STATE savedState; // Don't move the dinState, stay // put and get the next DIN value savedState.uiOffset = dinState.uiOffset; savedState.uiOnes = dinState.uiOnes; uiUntilDrn -= DINNextVal( pucElm, &savedState); } } } else if (uiBlkType == BHT_NON_LEAF_DATA) { uiElmLength = uiElmOvhd; pucElmKey = pucElement; uiUntilDrn = DOMAIN_TO_DRN( uiDomain); } else { uiElmLength = BBE_GET_KL( pucElement ) + uiElmOvhd + (BNE_IS_DOMAIN(pucElement) ? BNE_DOMAIN_LEN : 0); pucElmKey = &pucElement [uiElmOvhd]; uiUntilDrn = DOMAIN_TO_DRN( uiDomain); } // See if we are on the last element. If it is a leaf block, // it does NOT represent a key. If it is a non-leaf block, // it represents the highest possible key, but there is no // data to extract fields from. if ((uiBlkType == BHT_LEAF) && (uiElmLength == uiElmOvhd)) { goto Exit; // Should return FERR_OK } if ((uiBlkType != BHT_LEAF) && (uiElmLength == uiElmOvhd)) { uiElmKeyLen = uiElmPKCLen = uiUntilKeyLen = 0; } else { // Get the element key length and previous key count (PKC). uiElmKeyLen = (FLMUINT)(BBE_GET_KL( pucElement)); uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( pucElement)); // Now copy the current partial key into the EndKey key buffer. f_memcpy( &ucUntilKey [uiElmPKCLen], pucElmKey, uiElmKeyLen); uiUntilKeyLen = uiElmKeyLen + uiElmPKCLen; } // Test for Overlap of from key (exclusive) to until key (inclusive) // with search keys. bRangeOverlaps = pFSIndexCursor->compareKeyRange( ucFromKey, uiFromKeyLen, (FLMBOOL)((uiFromKeyLen) ? TRUE : FALSE), ucUntilKey, uiUntilKeyLen, FALSE, &bUntilKeyInSet, &bUntilKeyPastEndOfKeys); // Does this range overlap a range of keys? if (bRangeOverlaps) { // If we are not at the leaf level, get and save child block address. if (uiBlkType != BHT_LEAF) { // THIS CODE SHOULD BE USING A STACK!!!! if (uiElmOvhd == BNE_DATA_OVHD) { pucChildBlkAddr = &pucElement[ BNE_DATA_CHILD_BLOCK]; } else { pucChildBlkAddr = &pucElement [BNE_CHILD_BLOCK]; } uiChildBlkAddr = FB2UD( pucChildBlkAddr ); // Save uiChildBlkAddr to array of child block addresses. if (*puiNumChildBlocks == *puiBlkAddressArrayAllocSize) { if (RC_BAD( rc = f_recalloc( (*puiBlkAddressArrayAllocSize + FLM_ADDR_GROW_SIZE) * sizeof( FLMUINT), ppuiChildBlockAddresses))) { goto Exit; } (*puiBlkAddressArrayAllocSize) += FLM_ADDR_GROW_SIZE; } (*ppuiChildBlockAddresses)[ *puiNumChildBlocks] = uiChildBlkAddr; (*puiNumChildBlocks)++; } // If the last element in the block has just been processed, the key // will have a length of 0. If it is somewhere within the range of // keys that contains the query's result set, return TRUE in // pbHighKeyInRange. At a higher level, if only one more key is // needed to fill the array of positioning keys, the B-Tree will // then be traversed to the leaf level to retrieve and test the // rightmost key. if (!uiUntilKeyLen && bUntilKeyInSet) { *pbHighKeyInRange = TRUE; } // If the key falls into one of the key ranges that contain the // query's result set, see if it satisfies the selection criteria. // If so, increment the counter for the positioning key array and // put the key into the array. else if (bUntilKeyInSet) { if (RC_BAD( rc = flmKeyIsMatch( pCursor, pIxd, ucUntilKey, uiUntilKeyLen, uiUntilDrn, ppKeys, puiNumKeys, puiKeyArrayAllocSize, FLM_KEYS_GROW_SIZE))) { goto Exit; } } } // If this is not the first reference, stay inside the element and // get the next reference. if (!bFirstRef) { continue; } uiCurrElmOffset += uiElmLength; // No need to go any further if we have run off the end of the list // of keys for the query. if (bUntilKeyPastEndOfKeys) { break; } } Exit: return( rc); } /**************************************************************************** Desc: Finds the rightmost key in the leaf level of a B-tree, and evaluates it against the selection criteria of the given subquery. Visit:This routine must be rewritten to get rid of the low level BTREE definitions. The next() btree calls should have been used. ****************************************************************************/ FSTATIC RCODE flmGetLastKey( FDB * pDb, CURSOR * pCursor, IXD * pIxd, LFILE * pLFile, FLMUINT uiBlockAddress, POS_KEY * * ppKeys, FLMUINT * puiNumKeys, FLMUINT * puiKeyArrayAllocSize ) { RCODE rc = FERR_OK; FLMBYTE ucEndKey [MAX_KEY_SIZ]; FLMUINT uiEndKeyLen = 0; FLMUINT uiEndDrn = 0; BTSK stack; FLMBYTE ucKeyBuf [MAX_KEY_SIZ]; BTSK * pStack = &stack; FLMUINT uiEndOfBlock; FLMUINT uiCurrElmOffset; FLMUINT uiBlkType; FLMUINT uiElmLength; FLMBYTE * pucBlk; FLMBYTE * pucElement = NULL; FLMBYTE * pucElm; FLMBYTE * pucElmKey; FLMBYTE * pucElmRecord; FLMUINT uiElmRecLen; FLMBYTE * pucBlockAddress; FLMUINT uiElmKeyLen; FLMUINT uiElmPKCLen; FLMBOOL bHaveLastKey = FALSE; FLMUINT uiElmOvhd = 0; DIN_STATE dinState; FLMUINT uiRefSize; FSInitStackCache( pStack, 1); pStack->pKeyBuf = &ucKeyBuf [0]; // uiBlockAddress contains the address of the rightmost B-Tree block at // some unspecified level of the B-Tree (usually not the leaf level). // This loop works down the right side of the B-Tree from the passed-in // block address until it reaches the rightmost block at the leaf level. // The rightmost key is then found in that block. for( ;;) { if (RC_BAD(rc = FSGetBlock( pDb, pLFile, uiBlockAddress, pStack))) { goto Exit; } pucBlk = pStack->pBlk; uiBlkType = (FLMUINT)(BH_GET_TYPE( pucBlk)); uiEndOfBlock = (FLMUINT)pStack->uiBlkEnd; uiCurrElmOffset = BH_OVHD; // This loop works across a B-Tree block from the leftmost key to the // rightmost key. At non-leaf levels of the B-Tree, the child block // address associated with the rightmost key is then used to progress // further down the right side of the B-Tree. while (uiCurrElmOffset < uiEndOfBlock) { pucElement = &pucBlk [uiCurrElmOffset]; if (uiBlkType == BHT_LEAF) { uiElmOvhd = BBE_KEY; uiElmLength = (FLMUINT)(BBE_LEN( pucElement)); pucElmKey = &pucElement [BBE_KEY]; // See if we are on the last element. If it is a leaf block, // it does NOT represent a key; the previous element that was // processed contained the last key, which means we're finished. if (uiElmLength == uiElmOvhd) { bHaveLastKey = TRUE; break; } // Get the last DRN in the element - in case this element is // the last one before the end. pucElmRecord = BBE_REC_PTR( pucElement); uiElmRecLen = BBE_GET_RL( pucElement); pucElm = pucElement; (void)FSGetDomain( &pucElm, uiElmOvhd); RESET_DINSTATE( dinState); uiEndDrn = SENNextVal( &pucElm); uiRefSize = uiElmRecLen - (FLMUINT)(pucElm - pucElmRecord); for (;;) { if (dinState.uiOffset < uiRefSize) { // Not at end, read current value. DINNextVal( pucElm, &dinState); } if (dinState.uiOffset >= uiRefSize) { break; } else { DIN_STATE savedState; // Don't move the dinState, stay // put and get the next DIN value savedState.uiOffset = dinState.uiOffset; savedState.uiOnes = dinState.uiOnes; uiEndDrn -= DINNextVal( pucElm, &savedState); } } } else if( uiBlkType == BHT_NON_LEAF_DATA) { uiElmOvhd = uiElmLength = BNE_DATA_OVHD; pucElmKey = pucElement; } else { uiElmOvhd = pStack->uiElmOvhd; uiElmLength = BBE_GET_KL( pucElement ) + uiElmOvhd + (BNE_IS_DOMAIN(pucElement) ? BNE_DOMAIN_LEN : 0); pucElmKey = &pucElement [uiElmOvhd]; } if ((uiBlkType != BHT_LEAF) && (uiElmLength == uiElmOvhd)) { uiElmKeyLen = uiElmPKCLen = uiEndKeyLen = 0; } else if (uiBlkType == BHT_NON_LEAF_DATA) { uiElmLength = BNE_DATA_OVHD; f_memcpy( ucEndKey, pucElmKey, DIN_KEY_SIZ); } else { /* Get the element key length and previous key count (PKC). */ uiElmKeyLen = (FLMUINT)(BBE_GET_KL( pucElement)); uiElmPKCLen = (FLMUINT)(BBE_GET_PKC( pucElement)); f_memcpy( &ucEndKey [uiElmPKCLen], pucElmKey, uiElmKeyLen); uiEndKeyLen = (FLMUINT)(uiElmKeyLen + uiElmPKCLen); } uiCurrElmOffset += uiElmLength; } if (!bHaveLastKey) { // Get and save child block address. pucBlockAddress = (FLMBYTE *)((uiElmOvhd == BNE_DATA_OVHD) ? &pucElement [BNE_DATA_CHILD_BLOCK] : &pucElement [BNE_CHILD_BLOCK]); uiBlockAddress = FB2UD( pucBlockAddress ); } else { // We have reached the leaf level of the B-Tree, and we have the // rightmost key. See if it satisfies the selection criteria for // the query. If so, put it into the passed-in array of positioning // keys. Then break out of the loop; we're finished. if (RC_BAD( rc = flmKeyIsMatch( pCursor, pIxd, ucEndKey, uiEndKeyLen, uiEndDrn, ppKeys, puiNumKeys, puiKeyArrayAllocSize, 1))) { goto Exit; } break; } } Exit: FSReleaseBlock( pStack, FALSE); return( rc); } /**************************************************************************** Desc: Frees the allocations associated with a subquery's array. ****************************************************************************/ void flmCurFreePosKeys( CURSOR * pCursor ) { FLMUINT uiLoopCnt; if (pCursor->pPosKeyArray) { for (uiLoopCnt = 0; uiLoopCnt < pCursor->uiNumPosKeys; uiLoopCnt++) { f_free( &pCursor->pPosKeyArray[ uiLoopCnt].pucKey); } f_free( &pCursor->pPosKeyArray); pCursor->uiNumPosKeys = 0; } pCursor->uiLastPrcntPos = 0; pCursor->uiLastPrcntOffs = 0; pCursor->bUsePrcntPos = FALSE; } /**************************************************************************** Desc: Gets a set of positioning keys for a particular subquery. ****************************************************************************/ FSTATIC RCODE flmCurGetPosKeys( FDB * pDb, CURSOR * pCursor ) { RCODE rc = FERR_OK; BTSK stack [BH_MAX_LEVELS]; FLMBYTE ucKeyBuf [MAX_KEY_SIZ]; BTSK * pStack = stack; LFILE * pLFile; LFILE TmpLFile; IXD * pIxd; SUBQUERY * pSubQuery; FLMUINT * puiChildBlockAddresses = NULL; FLMUINT * puiTmpBlocks = NULL; FLMUINT uiNumChildBlocks = 0; FLMUINT uiNumTmpBlks; FLMUINT uiBlkAddressArrayAllocSize = 0; POS_KEY * pKeys = NULL; FLMUINT uiNumKeys = 0; FLMUINT uiKeyArrayAllocSize = 0; FLMBOOL bHighKeyInRange = FALSE; FSInitStackCache( &stack[ 0], BH_MAX_LEVELS); // Check to verify that it is possible to set up an array of positioning keys // for this query. The following conditions must be met: // 1) The query must use one and only one index // 2) The criteria must be solvable using only the index keys // 3) The selection criteria cannot include DRNs. if (((pSubQuery = pCursor->pSubQueryList) == NULL) || pSubQuery->pNext || pSubQuery->OptInfo.eOptType != QOPT_USING_INDEX || pSubQuery->OptInfo.bDoRecMatch || pSubQuery->bHaveDrnFlds) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } // Free the existing key array, if there is one if (pCursor->pPosKeyArray) { flmCurFreePosKeys( pCursor); } // Get the necessary LFILE and IXD information from the subquery index. if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pSubQuery->OptInfo.uiIxNum, &pLFile, &pIxd))) { goto Exit; } // Set up a B-tree stack structure and get the root block in the index // B-tree. pStack->pKeyBuf = &ucKeyBuf [0]; // If no root block returned from FSGetRootBlock, the array will be // returned empty, with rc set to success. if (RC_BAD( rc = FSGetRootBlock( pDb, &pLFile, &TmpLFile, pStack))) { if (rc == FERR_NO_ROOT_BLOCK) { flmAssert( pLFile->uiRootBlk == BT_END); rc = FERR_OK; } goto Exit; } uiNumTmpBlks = 1; // Extract the array of positioning keys by working down the B-tree // from the root block. This loop will terminate when all levels of // the B-Tree have been processed, or when enough keys have been // found to populate the array. // NOTE: pSubQuery->pPosKeyPool has been initialized at a higher level. for(;;) { FLMUINT uiBlkCnt = 0; // Work across the present level of the B-Tree from right to left. for(;;) { // This function moves across a database block from the leftmost // element to the rightmost, checking each key to see if it is // found in the query's result set. If it is, it is added to a // list of possible positioning keys, and its pointers to child // blocks in the B-Tree are also kept. In the event that not // enough keys are found at a given level in the B-Tree, the list // of child block pointers is used to work through the next level // of the B-Tree. if (RC_BAD( rc = flmExamineBlock( pCursor, pIxd, pStack->pBlk, pSubQuery->pFSIndexCursor, &puiChildBlockAddresses, &uiNumChildBlocks, &uiBlkAddressArrayAllocSize, &pKeys, &uiNumKeys, &uiKeyArrayAllocSize, &bHighKeyInRange))) { goto Exit; } uiBlkCnt++; // uiNumTmpBlks has the number of blocks to be processed at the // current level of the B-Tree. When those have been processed, // break out of this loop and go to the next level of the B-Tree. if (uiBlkCnt == uiNumTmpBlks) { break; } if (RC_BAD( rc = FSGetBlock( pDb, pLFile, puiTmpBlocks[ uiBlkCnt], pStack ))) { goto Exit; } } // If we're not on the leaf level, and we have at least // FLM_MIN_POS_KEYS - 1 keys, we need to go out and evaluate // the last key at the leaf level. if (uiNumKeys >= FLM_MIN_POS_KEYS - 1 && bHighKeyInRange && uiNumChildBlocks) { if (RC_BAD( rc = flmGetLastKey( pDb, pCursor, pIxd, pLFile, puiChildBlockAddresses [uiNumChildBlocks - 1], &pKeys, &uiNumKeys, &uiKeyArrayAllocSize))) { goto Exit; } } // If we have enough keys, or if we have reached the last level of the // B-tree, load up the subquery key array and quit. if ((uiNumKeys >= FLM_MIN_POS_KEYS) || !uiNumChildBlocks) { rc = flmLoadPosKeys( pCursor, pKeys, uiNumKeys, (FLMBOOL)((uiNumChildBlocks == 0) ? TRUE : FALSE)); goto Exit; } // If not enough keys, go to the next level of the B-tree and traverse // it to find keys. This should be done down to the last level. else { FLMUINT uiKeyCnt; f_free( &puiTmpBlocks); puiTmpBlocks = puiChildBlockAddresses; uiNumTmpBlks = uiNumChildBlocks; puiChildBlockAddresses = NULL; uiNumChildBlocks = uiBlkAddressArrayAllocSize = 0; for (uiKeyCnt = 0; uiKeyCnt < uiNumKeys; uiKeyCnt++) { f_free( &pKeys[ uiKeyCnt].pucKey); } f_free( &pKeys); pKeys = NULL; uiNumKeys = 0; uiKeyArrayAllocSize = 0; if (RC_BAD( rc = FSGetBlock( pDb, pLFile, puiTmpBlocks[ 0], pStack ))) { goto Exit; } bHighKeyInRange = FALSE; } } Exit: if ( pKeys) { if (RC_BAD( rc)) { for ( FLMUINT uiKeyCnt = 0; uiKeyCnt < uiNumKeys; uiKeyCnt++) { f_free( &pKeys[ uiKeyCnt].pucKey); } } f_free( &pKeys); } f_free( &puiChildBlockAddresses); f_free( &puiTmpBlocks); FSReleaseStackCache( stack, BH_MAX_LEVELS, FALSE); return( rc); } /**************************************************************************** Desc: Gets a set of positioning keys for a particular subquery. ****************************************************************************/ RCODE flmCurSetupPosKeyArray( CURSOR * pCursor ) { RCODE rc = FERR_OK; FDB * pDb = NULL; // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // Set up the pDb pDb = pCursor->pDb; if (RC_BAD(rc = flmCurDbInit( pCursor))) { goto Exit; } // Set up array of positioning keys. if (RC_BAD( rc = flmCurGetPosKeys( pDb, pCursor))) { goto Exit; } Exit: if (pDb) { flmExit( FLM_CURSOR_CONFIG, pDb, rc); } return( rc); } /**************************************************************************** Desc: Gets the approximate percentage position of a passed-in key within a cursor's result set. ****************************************************************************/ RCODE flmCurGetPercentPos( CURSOR * pCursor, FLMUINT * puiPrcntPos ) { RCODE rc = FERR_OK; FDB * pDb = NULL; IXD * pIxd; POS_KEY * pPosKeyArray; POS_KEY CompKey; FLMUINT uiLowOffset; FLMUINT uiMidOffset; FLMUINT uiHighOffset; FLMUINT uiIntervalSize; FLMUINT uiRFactor; FLMINT iCmp; FLMUINT uiContainer; // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } pDb = pCursor->pDb; if (RC_BAD(rc = flmCurDbInit( pCursor))) { goto Exit; } // If no array of positioning keys exists in the subquery, set one up. if (!pCursor->uiNumPosKeys) { if (RC_BAD( rc = flmCurGetPosKeys( pDb, pCursor))) { goto Exit; } // If no positioning keys exist, either the index or the result set // is empty. Return NOT_FOUND. if (!pCursor->uiNumPosKeys) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } } // If the number of positioning keys is 1, the position is 0%. if (pCursor->uiNumPosKeys == 1) { *puiPrcntPos = 0; goto Exit; } pPosKeyArray = pCursor->pPosKeyArray; if (pCursor->uiNumPosKeys == 2) { uiIntervalSize = FLM_MAX_POS_KEYS; uiRFactor = 0; } else { uiIntervalSize = FLM_MAX_POS_KEYS / (pCursor->uiNumPosKeys - 1); uiRFactor = FLM_MAX_POS_KEYS % (pCursor->uiNumPosKeys - 1); } // DEFECT 84741 -- only want to return a position of 1 for the second key // if the positioning key array is full. // Get an IXD, then convert the passed-in key from GEDCOM format to a // buffer containing the key in the FLAIM internal format. if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pCursor->pSubQueryList->OptInfo.uiIxNum, NULL, &pIxd))) { goto Exit; } if (pCursor->ReadRc == FERR_BOF_HIT) { *puiPrcntPos = 0; goto Exit; } if (pCursor->ReadRc == FERR_EOF_HIT) { *puiPrcntPos = FLM_MAX_POS_KEYS; rc = RC_SET( FERR_EOF_HIT); goto Exit; } if (RC_BAD( rc = pCursor->pSubQueryList->pFSIndexCursor->currentKeyBuf( pDb, &pDb->TempPool, &CompKey.pucKey, &CompKey.uiKeyLen, &CompKey.uiDrn, &uiContainer))) { if (rc == FERR_EOF_HIT || rc == FERR_BOF_HIT || rc == FERR_NOT_FOUND) { rc = FERR_OK; *puiPrcntPos = 0; } goto Exit; } flmAssert( uiContainer == pCursor->uiContainer); // If a set position call has been performed, and no reposisioning has // been done since, check the passed-in key to see if it matches the // key returned from the set position call. If so, return the percent // passed in on the set position call. This is to create some symmetry // where the user calls set position, then takes the resulting key and // passes it back into a get position call. if (pCursor->bUsePrcntPos && pCursor->uiLastPrcntPos <= FLM_MAX_POS_KEYS) { if (flmPosKeyCompare( &pPosKeyArray[ pCursor->uiLastPrcntOffs], &CompKey) == 0) { *puiPrcntPos = pCursor->uiLastPrcntPos; goto Exit; } pCursor->bUsePrcntPos = FALSE; } // Do a binary search in the array of positioning keys for the passed-in // key. NOTE: the point of this search is to find the closest key <= to // the passed- in key. The range of values returned is // 0 to FLM_MAX_POS_KEYS (currently defined to be 1000), where 0 and // FLM_MAX_POS_KEYS represent the first and last keys in the query's // result set, respectively. Numbers between these two endpoints represent // intervals between two keys that are adjacent in the array, but which // may have any number of intervening keys in the index. uiLowOffset = 0; uiHighOffset = pCursor->uiNumPosKeys - 1; for(;;) { if (uiLowOffset == uiHighOffset) { uiMidOffset = uiLowOffset; // Defect #84741 (fix after failing regression test - // zeroeth object was always returning position 1). // Must do final comparison to determine which side of // the positioning key our key falls on. Remember, // the positioning key represents all keys that are // LESS THAN OR EQUAL to it. Thus, if this key is // greater than it, we should use the next positioning // key. if ((flmPosKeyCompare( &pPosKeyArray[ uiMidOffset], &CompKey) < 0) && (uiMidOffset < pCursor->uiNumPosKeys - 1)) { uiMidOffset++; } break; } uiMidOffset = (FLMUINT)((uiHighOffset + uiLowOffset) / 2); iCmp = flmPosKeyCompare( &pPosKeyArray[ uiMidOffset], &CompKey); if( iCmp < 0) { uiLowOffset = uiMidOffset + 1; } else if( iCmp > 0) { if( uiMidOffset == uiLowOffset) { break; } else { uiHighOffset = uiMidOffset - 1; } } else { break; } } // DEFECT 84741 -- the first object should only return a position of 1 // if there are FLM_MAX_POS_KEYS positioning keys in the array. if (uiMidOffset == 0 || (uiMidOffset == 1 && pCursor->uiNumPosKeys == FLM_MAX_POS_KEYS + 1)) { *puiPrcntPos = uiMidOffset; } else if (uiMidOffset == pCursor->uiNumPosKeys - 1) { *puiPrcntPos = FLM_MAX_POS_KEYS; } else if (uiMidOffset <= uiRFactor) { *puiPrcntPos = uiMidOffset * (uiIntervalSize + 1); } else if (uiRFactor) { *puiPrcntPos = uiRFactor * (uiIntervalSize + 1) + (uiMidOffset - uiRFactor) * uiIntervalSize; } else { *puiPrcntPos = uiMidOffset * uiIntervalSize; } Exit: if (pDb) { flmExit( FLM_CURSOR_GET_CONFIG, pDb, rc); } return( rc); } /**************************************************************************** Desc: Sets a query's position to a percentage represented by one of an array of positioning keys. ****************************************************************************/ RCODE flmCurSetPercentPos( CURSOR * pCursor, FLMUINT uiPrcntPos ) { RCODE rc = FERR_OK; FDB * pDb = NULL; FLMUINT uiPrcntOffs; FLMUINT uiIntervalSize; FLMUINT uiRFactor; SUBQUERY * pSubQuery = NULL; POS_KEY * pPosKey; // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // Check the value for the percentage position. Should be between // 0 and FLM_MAX_POS_KEYS. flmAssert( uiPrcntPos <= FLM_MAX_POS_KEYS); // Initialize some variables pCursor->uiLastRecID = 0; pDb = pCursor->pDb; if (RC_BAD(rc = flmCurDbInit( pCursor))) { goto Exit; } // If no array of positioning keys exists in the subquery, set one up. if (!pCursor->uiNumPosKeys) { if (RC_BAD( rc = flmCurGetPosKeys( pDb, pCursor))) { goto Exit; } // If no positioning keys exist, either the index or the result set // is empty. Return BOF or EOF. if (!pCursor->uiNumPosKeys) { rc = RC_SET( FERR_EOF_HIT); goto Exit; } } pSubQuery = pCursor->pSubQueryList; Retry: // Calculate the percent position using the following rules: // 1) If the number of positioning keys is 1, the position is 0%. // 2) If the number of positioning keys is 2, the position is either 0% or // FLM_MAX_POS_KEYS. // 3) If there are more than 2 positioning keys, calculate the interval into // which the percentage position falls. if (pCursor->uiNumPosKeys == 1) { uiPrcntOffs = 0; } else { if (pCursor->uiNumPosKeys == 2) { uiIntervalSize = FLM_MAX_POS_KEYS; uiRFactor = 0; } else { uiIntervalSize = FLM_MAX_POS_KEYS / (pCursor->uiNumPosKeys - 1); uiRFactor = FLM_MAX_POS_KEYS % (pCursor->uiNumPosKeys - 1); } // Convert passed-in number to an array offset. if (uiPrcntPos) { if (uiPrcntPos == 0 || pCursor->uiNumPosKeys == FLM_MAX_POS_KEYS + 1) { uiPrcntOffs = uiPrcntPos; } else if( uiPrcntPos == FLM_MAX_POS_KEYS) { uiPrcntOffs = pCursor->uiNumPosKeys - 1; } else if( uiPrcntPos <= uiRFactor * (uiIntervalSize + 1)) { uiPrcntOffs = uiPrcntPos / (uiIntervalSize + 1); } else { uiPrcntOffs = uiRFactor + (uiPrcntPos - (uiIntervalSize + 1) * uiRFactor) / uiIntervalSize; } } else { uiPrcntOffs = 0; } } pPosKey = &pCursor->pPosKeyArray [uiPrcntOffs]; // If the keys were generated from the leaf level, we can // position directly to them. If not, we must call the // positionToDomain routine. if (pCursor->bLeafLevel) { rc = pSubQuery->pFSIndexCursor->positionTo( pDb, pPosKey->pucKey, pPosKey->uiKeyLen, pPosKey->uiDrn); } else { rc = pSubQuery->pFSIndexCursor->positionToDomain( pDb, pPosKey->pucKey, pPosKey->uiKeyLen, DRN_TO_DOMAIN( pPosKey->uiDrn)); } if (RC_BAD( rc)) { RCODE saveRc; if (rc != FERR_BOF_HIT && rc != FERR_EOF_HIT && rc != FERR_NOT_FOUND) { goto Exit; } // If the positioning key was not found, the database has undergone // significant change since the array of positioning keys was generated. // Try to regenerate the array and reposition. saveRc = rc; if (RC_BAD( rc = flmCurGetPosKeys( pDb, pCursor))) { goto Exit; } if (pCursor->pPosKeyArray [0].pucKey == NULL) { rc = saveRc; goto Exit; } goto Retry; } // Retrieve the current key and DRN from the index cursor. if (RC_BAD( rc = pSubQuery->pFSIndexCursor->currentKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn))) { goto Exit; } pSubQuery->bFirstReference = FALSE; pSubQuery->uiCurrKeyMatch = FLM_TRUE; // These should have already been set by the call to currentKey. flmAssert( pSubQuery->pRec->getContainerID() == pCursor->uiContainer); flmAssert( pSubQuery->pRec->getID() == pSubQuery->uiDrn); pSubQuery->bRecIsAKey = TRUE; // If we got this far, the positioning operation was a success. Set // the query return code to success so it doesn't mess up subsequent // read operations. pCursor->uiLastRecID = pSubQuery->uiDrn; pCursor->rc = FERR_OK; pCursor->uiLastPrcntPos = uiPrcntPos; pCursor->uiLastPrcntOffs = uiPrcntOffs; pCursor->bUsePrcntPos = TRUE; Exit: if (pDb) { flmExit( FLM_CURSOR_CONFIG, pDb, rc); } return( rc); } /**************************************************************************** Desc: Build the from and until keys given a field list with operators and values and an index. Note: The knowledge of query definitions is limited in these routines. ****************************************************************************/ RCODE flmBuildFromAndUntilKeys( IXD * pIxd, QPREDICATE ** ppQPredicate, FLMBYTE * pFromKey, FLMUINT * puiFromKeyLen, FLMBYTE * pUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbDoRecMatch, FLMBOOL * pbDoKeyMatch, FLMBOOL * pbExclusiveUntilKey) { RCODE rc = FERR_OK; QPREDICATE * pCurPred; IFD * pIfd = pIxd->pFirstIfd; FLMUINT uiLanguage = pIxd->uiLanguage; FLMUINT uiIfdCnt = pIxd->uiNumFlds; FLMUINT uiFromKeyPos = 0; FLMUINT uiUntilKeyPos = 0; FLMBOOL bFromAtFirst; FLMBOOL bUntilAtEnd; FLMBOOL bDataTruncated; FLMBOOL bDoneBuilding; FLMBOOL bMustNotDoKeyMatch = FALSE; FLMBOOL bDoKeyMatch = FALSE; FLMBOOL bOriginalCharsLost; FLMBOOL bDBCSLanguage; FLMBYTE ucNumberBuf [ F_MAX_NUM64_BUF + 1]; FLMUINT uiTempLen; FLMUINT uiMaxKeySize; bDataTruncated = FALSE; bDoneBuilding = FALSE; *puiFromKeyLen = 0; *puiUntilKeyLen = 0; uiFromKeyPos = 0; uiUntilKeyPos = 0; *pbExclusiveUntilKey = TRUE; bDBCSLanguage = (uiLanguage >= FLM_FIRST_DBCS_LANG) && (uiLanguage <= FLM_LAST_DBCS_LANG) ? TRUE : FALSE; uiMaxKeySize = (pIxd->uiContainerNum) ? MAX_KEY_SIZ : MAX_KEY_SIZ - getIxContainerPartLen( pIxd); for (; !bDoneBuilding && uiIfdCnt--; ppQPredicate++, pIfd++) { // Add the compound marker if not the first piece. if (pIfd->uiCompoundPos) { IFD * pPrevIfd = (pIfd - 1); // Add the compound markers for this key piece. if (bDBCSLanguage && (IFD_GET_FIELD_TYPE( pPrevIfd) == FLM_TEXT_TYPE) && (!((pPrevIfd)->uiFlags & IFD_CONTEXT))) { pFromKey[uiFromKeyPos++] = 0; pUntilKey[uiUntilKeyPos++] = 0; } pFromKey[uiFromKeyPos++] = COMPOUND_MARKER; pUntilKey[uiUntilKeyPos++] = COMPOUND_MARKER; } bFromAtFirst = bUntilAtEnd = FALSE; pCurPred = *ppQPredicate; if (!pCurPred) { // There is not a predicate that matches this compound key piece. // // Done processing, yet may need to look for a predicate that // will force a doKeyMatch or a doRecMatch. if (RC_BAD( rc = flmAddKeyPiece( uiMaxKeySize, pIfd, FALSE, pFromKey, &uiFromKeyPos, TRUE, pUntilKey, &uiUntilKeyPos, TRUE, NULL, 0, &bDataTruncated, &bDoneBuilding))) { goto Exit; } continue; } // Handle special cases for indexing context and/or exists // predicate. else if (pIfd->uiFlags & IFD_CONTEXT) { // Indexed only the TAG. Simple to set the tag as the key. if (RC_BAD( rc = flmAddKeyPiece( uiMaxKeySize, pIfd, FALSE, pFromKey, &uiFromKeyPos, FALSE, pUntilKey, &uiUntilKeyPos, FALSE, NULL, 0, &bDataTruncated, &bDoneBuilding))) { goto Exit; } // If we don't have an exists predicate we need to read the // record. if (pCurPred->eOperator != FLM_EXISTS_OP) { bMustNotDoKeyMatch = TRUE; } continue; } else { FLMBOOL bMatchedBadOperator = FALSE; switch (pCurPred->eOperator) { case FLM_EXISTS_OP: case FLM_NE_OP: { bMatchedBadOperator = TRUE; bUntilAtEnd = TRUE; bFromAtFirst = TRUE; break; } default: { if (pCurPred->bNotted) { bMatchedBadOperator = TRUE; bUntilAtEnd = TRUE; bFromAtFirst = TRUE; } break; } } if (bMatchedBadOperator) { // Does exist is a FIRST to LAST for this piece. if (RC_BAD( rc = flmAddKeyPiece( uiMaxKeySize, pIfd, FALSE, pFromKey, &uiFromKeyPos, bFromAtFirst, pUntilKey, &uiUntilKeyPos, bUntilAtEnd, NULL, 0, &bDataTruncated, &bDoneBuilding))) { goto Exit; } continue; } } switch (IFD_GET_FIELD_TYPE( pIfd)) { // Build TEXT type piece case FLM_TEXT_TYPE: { FLMBOOL bCaseInsensitive; FLMBOOL bDoFirstSubstring; FLMBOOL bDoMatchBegin; FLMBOOL bDoSubstringSearch; FLMBOOL bTrailingWildcard; FLMBYTE * pValue = (FLMBYTE *) pCurPred->pVal->val.pucBuf; FLMUINT uiValueLen = pCurPred->pVal->uiBufLen; bCaseInsensitive = (FLMBOOL)((pCurPred->pVal->uiFlags & FLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE); bDoFirstSubstring = (FLMBOOL)((pIfd->uiFlags & IFD_SUBSTRING) ? TRUE : FALSE); bDoMatchBegin = FALSE; bDoSubstringSearch = FALSE; bTrailingWildcard = FALSE; switch (pCurPred->eOperator) { // The difference between MATCH and EQ_OP is that EQ does not // support wildcards inbedded in the search key. case FLM_MATCH_OP: case FLM_MATCH_BEGIN_OP: { if (pCurPred->eOperator == FLM_MATCH_BEGIN_OP) { bDoKeyMatch = bDoMatchBegin = TRUE; } if (pCurPred->pVal->uiFlags & FLM_COMP_WILD) { if (!bDoFirstSubstring) { FLMBOOL bFoundWildcard = flmFindWildcard( pValue, &uiValueLen); bDoKeyMatch = TRUE; if (pCurPred->eOperator == FLM_MATCH_OP) { bTrailingWildcard = bDoMatchBegin = bFoundWildcard; } else { bTrailingWildcard = bDoMatchBegin = TRUE; } } else { // If this is a substring index look for a better // 'contains' string to search for. We don't like // "A*BCDEFG" searches. bTrailingWildcard = (pCurPred->eOperator == FLM_MATCH_BEGIN_OP) ? TRUE : FALSE; if (flmSelectBestSubstr( &pValue, &uiValueLen, pIfd->uiFlags, &bTrailingWildcard)) { bDoMatchBegin = bTrailingWildcard; bMustNotDoKeyMatch = TRUE; bDoFirstSubstring = FALSE; } else if (bTrailingWildcard) { bDoKeyMatch = bDoMatchBegin = TRUE; } } } break; } case FLM_CONTAINS_OP: case FLM_MATCH_END_OP: { // Normal text index this piece goes from first to last. if (!bDoFirstSubstring) { bFromAtFirst = TRUE; bUntilAtEnd = TRUE; } else { bDoFirstSubstring = TRUE; bDoSubstringSearch = TRUE; // SPACE/Hyphen rules on SUBSTRING index. If the search // string starts with " _asdf" then we must do a record // match so "Z asdf" matches and "Zasdf" doesn't. We // won't touch key match even though it MAY return // FLM_TRUE when in fact the key may or may not match. // // VISIT: MatchBegin and Contains could also optimize // the trailing space by adding the space ONLY to the // UNTIL key. if (uiValueLen && ((*pValue == ASCII_SPACE && (pIfd->uiFlags & IFD_MIN_SPACES)) || (*pValue == ASCII_UNDERSCORE && (pIfd->uiFlags & IFD_NO_UNDERSCORE)))) { *pbDoRecMatch = TRUE; } // Take the flags from the pVal and NOT from the // predicate. if (pCurPred->pVal->uiFlags & FLM_COMP_WILD) { // Select the best substring. The case of // "A*BCD*E*FGHIJKLMNOP" will look for "FGHIJKLMNOP". // and TURN OFF doKeyMatch and SET doRecMatch. bTrailingWildcard = (pCurPred->eOperator == FLM_CONTAINS_OP) ? TRUE : FALSE; if (flmSelectBestSubstr( &pValue, &uiValueLen, pIfd->uiFlags, &bTrailingWildcard)) { bDoMatchBegin = bTrailingWildcard; bMustNotDoKeyMatch = TRUE; bDoFirstSubstring = FALSE; } if (bTrailingWildcard) { bDoKeyMatch = bDoMatchBegin = TRUE; } } if (bDoFirstSubstring) { // Setting bDoMatchBegin creates a UNTIL key with // trailing 0xFF values. if (pCurPred->eOperator == FLM_CONTAINS_OP) { bDoKeyMatch = TRUE; bDoMatchBegin = TRUE; } } // Special case: Single character contains/MEnd in a // substr ix. if (!bDBCSLanguage && flmCountCharacters( pValue, uiValueLen, 2, pIfd->uiFlags) < 2) { bDoKeyMatch = bFromAtFirst = bUntilAtEnd = TRUE; } } break; } // No wild card support for the operators below. case FLM_EQ_OP: { break; } case FLM_GE_OP: case FLM_GT_OP: { bUntilAtEnd = TRUE; break; } case FLM_LE_OP: { bFromAtFirst = TRUE; break; } case FLM_LT_OP: { bFromAtFirst = TRUE; *pbExclusiveUntilKey = TRUE; break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } // If index is case insensitive, but search is case sensitive // we must NOT do a key match - we would fail things we should // not be failing. if ((pIfd->uiFlags & IFD_UPPER) && !bCaseInsensitive) { bMustNotDoKeyMatch = TRUE; } if (RC_BAD( rc = flmAddTextPiece( uiMaxKeySize, pIfd, bCaseInsensitive, bDoMatchBegin, bDoFirstSubstring, bTrailingWildcard, pFromKey, &uiFromKeyPos, bFromAtFirst, pUntilKey, &uiUntilKeyPos, bUntilAtEnd, pValue, uiValueLen, &bDataTruncated, &bDoneBuilding, &bOriginalCharsLost))) { goto Exit; } if (bOriginalCharsLost) { bMustNotDoKeyMatch = TRUE; } break; } // Build NUMBER or CONTEXT type piece VISIT: Add a true number type // so we don't have to build a NODE. case FLM_NUMBER_TYPE: case FLM_CONTEXT_TYPE: { switch (pCurPred->pVal->eType) { case FLM_INT32_VAL: { FLMINT iValue = (FLMINT)pCurPred->pVal->val.i32Val; if (pCurPred->eOperator == FLM_GT_OP) { iValue++; } if (IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) { uiTempLen = sizeof( ucNumberBuf); (void)FlmINT2Storage( iValue, &uiTempLen, ucNumberBuf); } else { UD2FBA( (FLMUINT32)iValue, ucNumberBuf); uiTempLen = 4; } break; } case FLM_INT64_VAL: { FLMINT64 i64Value = pCurPred->pVal->val.i64Val; if (pCurPred->eOperator == FLM_GT_OP) { i64Value++; } if (IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) { uiTempLen = sizeof( ucNumberBuf); (void)FlmINT64ToStorage( i64Value, &uiTempLen, ucNumberBuf); } else { UD2FBA( (FLMUINT32)i64Value, ucNumberBuf); uiTempLen = 4; } break; } case FLM_UINT32_VAL: case FLM_REC_PTR_VAL: { FLMUINT uiValue = (FLMUINT)pCurPred->pVal->val.ui32Val; if (pCurPred->eOperator == FLM_GT_OP) { uiValue++; } if (IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) { uiTempLen = sizeof( ucNumberBuf); (void)FlmUINT2Storage( uiValue, &uiTempLen, ucNumberBuf); } else { UD2FBA( (FLMUINT32)uiValue, ucNumberBuf); uiTempLen = 4; } break; } case FLM_UINT64_VAL: { FLMUINT64 ui64Value = pCurPred->pVal->val.ui64Val; if (pCurPred->eOperator == FLM_GT_OP) { ui64Value++; } if (IFD_GET_FIELD_TYPE( pIfd) == FLM_NUMBER_TYPE) { uiTempLen = sizeof( ucNumberBuf); (void)FlmUINT64ToStorage( ui64Value, &uiTempLen, ucNumberBuf); } else { UD2FBA( (FLMUINT32)ui64Value, ucNumberBuf); uiTempLen = 4; } break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } switch (pCurPred->eOperator) { case FLM_EQ_OP: { break; } case FLM_GE_OP: case FLM_GT_OP: { bUntilAtEnd = TRUE; break; } case FLM_LE_OP: { bFromAtFirst = TRUE; break; } case FLM_LT_OP: { bFromAtFirst = TRUE; *pbExclusiveUntilKey = TRUE; break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } if (RC_BAD( rc = flmAddKeyPiece( uiMaxKeySize, pIfd, FALSE, pFromKey, &uiFromKeyPos, bFromAtFirst, pUntilKey, &uiUntilKeyPos, bUntilAtEnd, ucNumberBuf, uiTempLen, &bDataTruncated, &bDoneBuilding))) { goto Exit; } break; } case FLM_BINARY_TYPE: { FLMBOOL bMatchBegin = FALSE; switch (pCurPred->eOperator) { case FLM_MATCH_BEGIN_OP: { bMatchBegin = TRUE; break; } case FLM_EQ_OP: { break; } case FLM_GE_OP: { bUntilAtEnd = TRUE; break; } case FLM_GT_OP: { bUntilAtEnd = TRUE; bDoKeyMatch = TRUE; break; } case FLM_LE_OP: { bFromAtFirst = TRUE; break; } case FLM_LT_OP: { bFromAtFirst = TRUE; *pbExclusiveUntilKey = TRUE; break; } default: { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } } if (RC_BAD( rc = flmAddKeyPiece( uiMaxKeySize, pIfd, bMatchBegin, pFromKey, &uiFromKeyPos, bFromAtFirst, pUntilKey, &uiUntilKeyPos, bUntilAtEnd, pCurPred->pVal->val.pucBuf, pCurPred->pVal->uiBufLen, &bDataTruncated, &bDoneBuilding))) { goto Exit; } break; } default: { flmAssert( 0); break; } } if (bDataTruncated) { bMustNotDoKeyMatch = TRUE; } } // Really rare case where FROM/UNTIL keys are exactly the same. if (!bDoneBuilding && (uiIfdCnt + 1 == 0) && uiUntilKeyPos < uiMaxKeySize - 2) { // Always make the until key exclusive. pbExclusiveUntilKey = FALSE; pUntilKey[uiUntilKeyPos++] = 0xFF; pUntilKey[uiUntilKeyPos++] = 0xFF; } Exit: if (bMustNotDoKeyMatch) { *pbDoKeyMatch = FALSE; *pbDoRecMatch = TRUE; } else if (bDoKeyMatch || !pIxd->uiContainerNum) { *pbDoKeyMatch = TRUE; } // Special case for building FIRST/LAST keys. if (!uiFromKeyPos) { *pFromKey = '\0'; uiFromKeyPos = 1; } if (!uiUntilKeyPos) { f_memset( pUntilKey, 0xFF, uiMaxKeySize - 2); uiUntilKeyPos = uiMaxKeySize - 2; } *puiFromKeyLen = uiFromKeyPos; *puiUntilKeyLen = uiUntilKeyPos; return (rc); } /**************************************************************************** Desc: Truncate the length of the text buffer on the first wild card. ****************************************************************************/ FSTATIC FLMBOOL flmFindWildcard( FLMBYTE * pVal, FLMUINT * puiCharPos) { FLMBOOL bHaveChar = FALSE; FLMBYTE * pSaveVal = pVal; FLMUINT uiObjLength; FLMUINT uiLen = *puiCharPos; for (; *pVal; pVal += uiObjLength, uiLen = (uiObjLength < uiLen) ? uiLen - uiObjLength : 0) { switch ((FLMUINT) (flmTextObjType( *pVal))) { case ASCII_CHAR_CODE: // 0nnnnnnn { if (*pVal == ASCII_WILDCARD) { bHaveChar = TRUE; goto Exit; } uiObjLength = 1; // Check for '*' or '\\' after an escape character. if (*pVal == ASCII_BACKSLASH && (*(pVal + 1) == ASCII_WILDCARD || *(pVal + 1) == ASCII_BACKSLASH)) { uiObjLength++; } break; } case WHITE_SPACE_CODE: // 110nnnnn { uiObjLength = 1; break; } case CHAR_SET_CODE: // 10nnnnnn case UNK_EQ_1_CODE: case OEM_CODE: { uiObjLength = 2; break; } case UNICODE_CODE: // Unconvertable UNICODE code case EXT_CHAR_CODE: { uiObjLength = 3; break; } case UNK_GT_255_CODE: { uiObjLength = 1 + sizeof(FLMUINT16) + FB2UW( pVal + 1); break; } case UNK_LE_255_CODE: { uiObjLength = 2 + (FLMUINT16) * (pVal + 1); break; } default: { // should NEVER happen: bug if does flmAssert( 0); uiObjLength = 1; break; } } } Exit: *puiCharPos = (FLMUINT) (pVal - pSaveVal); return (bHaveChar); } /**************************************************************************** Desc: Add a key piece to the from and until key. Text fields are not handled in this routine because of their complexity. Note: The goal of this code is to build a the collated compound piece for the 'from' and 'until' key only once instead of twice. ****************************************************************************/ FSTATIC RCODE flmAddKeyPiece( FLMUINT uiMaxKeySize, IFD * pIfd, FLMBOOL bDoMatchBegin, FLMBYTE * pFromKey, FLMUINT * puiFromKeyPos, FLMBOOL bFromAtFirst, FLMBYTE * pUntilKey, FLMUINT * puiUntilKeyPos, FLMBOOL bUntilAtEnd, FLMBYTE * pBuf, FLMUINT uiBufLen, FLMBOOL * pbDataTruncated, FLMBOOL * pbDoneBuilding) { RCODE rc = FERR_OK; FLMUINT uiFromKeyPos = *puiFromKeyPos; FLMUINT uiUntilKeyPos = *puiUntilKeyPos; FLMBYTE * pDestKey; FLMUINT uiDestKeyLen; if (pIfd->uiCompoundPos == 0 && bFromAtFirst && bUntilAtEnd) { // Special case for the first piece - FIRST to LAST - zero length // keys. so that the caller can get the number of references for the // entire index. VISIT: May want to set the from key to have 1 byte // and set high values for the until key. This way the caller never // checks this special case. *pbDoneBuilding = TRUE; goto Exit; } // Handle the CONTEXT exception here - this is not done in kyCollate. if (pIfd->uiFlags & IFD_CONTEXT) { pFromKey[uiFromKeyPos] = KY_CONTEXT_PREFIX; f_UINT16ToBigEndian( (FLMUINT16) pIfd->uiFldNum, &pFromKey[uiFromKeyPos + 1]); uiFromKeyPos += KY_CONTEXT_LEN; if (uiUntilKeyPos + KY_CONTEXT_LEN < uiMaxKeySize) { pUntilKey[uiUntilKeyPos] = KY_CONTEXT_PREFIX; f_UINT16ToBigEndian( (FLMUINT16) pIfd->uiFldNum, &pUntilKey[uiUntilKeyPos + 1]); uiUntilKeyPos += KY_CONTEXT_LEN; } goto Exit; } if (bFromAtFirst) { if (bUntilAtEnd) { // Not the first piece and need to go from first to last. *pbDoneBuilding = TRUE; if (uiUntilKeyPos < uiMaxKeySize - 2) { if (uiUntilKeyPos > 0) { // Instead of filling the key with 0xFF, increment the // marker. pUntilKey[uiUntilKeyPos - 1]++; } else { f_memset( pUntilKey, 0xFF, uiMaxKeySize - 2); uiUntilKeyPos = uiMaxKeySize - 2; } } goto Exit; } if (uiUntilKeyPos >= uiMaxKeySize - 2) { goto Exit; } // Have a LAST key but no FROM key. pDestKey = pUntilKey + uiUntilKeyPos; uiDestKeyLen = uiMaxKeySize - uiUntilKeyPos; } else { pDestKey = pFromKey + uiFromKeyPos; uiDestKeyLen = uiMaxKeySize - uiFromKeyPos; } rc = KYCollateValue( pDestKey, &uiDestKeyLen, (FLMBYTE*) pBuf, uiBufLen, pIfd->uiFlags, pIfd->uiLimit, NULL, NULL, 0, TRUE, FALSE, FALSE, pbDataTruncated); if (rc == FERR_CONV_DEST_OVERFLOW) { rc = FERR_OK; } else if (RC_BAD( rc)) { goto Exit; } // If we just built the FROM key, we may want to copy to the UNTIL key. if (pDestKey == pFromKey + uiFromKeyPos) { uiFromKeyPos += uiDestKeyLen; // Unless the UNTIL key is full, the length is at or less than FROM // key. if (!bUntilAtEnd) { if (uiUntilKeyPos + uiDestKeyLen <= uiMaxKeySize) { f_memcpy( &pUntilKey[uiUntilKeyPos], pDestKey, uiDestKeyLen); uiUntilKeyPos += uiDestKeyLen; } if (bDoMatchBegin) { flmAssert( IFD_GET_FIELD_TYPE( pIfd) == FLM_BINARY_TYPE); if (uiUntilKeyPos < MAX_KEY_SIZ - 2) { // Optimization - only need to set a single byte to 0xFF. // We can do this because this routine does not deal with // text key pieces and binary, number and context will // never have 0xFF bytes. pUntilKey[uiUntilKeyPos++] = 0xFF; } // We don't need to set *pbDoneBuilding = TRUE, because we // may be able to continue building the from key } } else { if (uiUntilKeyPos > 0) { // Instead of filling the key with 0xFF, increment the marker. pUntilKey[uiUntilKeyPos - 1]++; } else { // Optimization - only need to set a single byte to 0xFF. We // can do this because this routine does not deal with text // key pieces and binary, number and context will never have // 0xFF bytes. flmAssert( IFD_GET_FIELD_TYPE( pIfd) != FLM_TEXT_TYPE); *pUntilKey = 0xFF; uiUntilKeyPos++; } } } else { uiUntilKeyPos += uiDestKeyLen; } Exit: // Set the FROM and UNTIL key length return values. *puiFromKeyPos = uiFromKeyPos; *puiUntilKeyPos = uiUntilKeyPos; return (rc); } /**************************************************************************** Desc: Add a text piece to the from and until key. ****************************************************************************/ FSTATIC RCODE flmAddTextPiece( FLMUINT uiMaxKeySize, IFD * pIfd, FLMBOOL bCaseInsensitive, FLMBOOL bDoMatchBegin, FLMBOOL bDoFirstSubstring, FLMBOOL bTrailingWildcard, FLMBYTE * pFromKey, FLMUINT * puiFromKeyPos, FLMBOOL bFromAtFirst, FLMBYTE * pUntilKey, FLMUINT * puiUntilKeyPos, FLMBOOL bUntilAtEnd, FLMBYTE * pBuf, FLMUINT uiBufLen, FLMBOOL * pbDataTruncated, FLMBOOL * pbDoneBuilding, FLMBOOL * pbOriginalCharsLost) { RCODE rc = FERR_OK; FLMUINT uiFromKeyPos = *puiFromKeyPos; FLMUINT uiUntilKeyPos = *puiUntilKeyPos; FLMUINT uiLanguage = pIfd->pIxd->uiLanguage; FLMBYTE * pDestKey; FLMUINT uiDestKeyLen; FLMUINT uiCollationLen = 0; FLMUINT uiCaseLen; FLMBOOL bIsDBCS; bIsDBCS = (uiLanguage >= FLM_FIRST_DBCS_LANG && uiLanguage <= FLM_LAST_DBCS_LANG) ? TRUE : FALSE; *pbOriginalCharsLost = FALSE; if (pIfd->uiCompoundPos == 0 && bFromAtFirst && bUntilAtEnd) { // Special case for the first piece - FIRST to LAST - zero length // keys. so that the caller can get the number of references for the // entire index. VISIT: May want to set the from key to have 1 byte // and set high values for the until key. This way the caller never // checks this special case. *pbDoneBuilding = TRUE; goto Exit; } if (bFromAtFirst) { if (bUntilAtEnd) { // Not the first piece and need to go from first to last. *pbDoneBuilding = TRUE; if (uiUntilKeyPos < uiMaxKeySize - 2) { // Instead of filling the key with 0xFF, increment the marker. pUntilKey[uiUntilKeyPos - 1]++; } goto Exit; } if (uiUntilKeyPos >= uiMaxKeySize - 2) { goto Exit; } // Have a LAST key but no FROM key. pDestKey = pUntilKey + uiUntilKeyPos; uiDestKeyLen = uiMaxKeySize - uiUntilKeyPos; } else // Handle below if UNTIL key is LAST. { pDestKey = pFromKey + uiFromKeyPos; uiDestKeyLen = uiMaxKeySize - uiFromKeyPos; } // Add IFD_ESC_CHAR to the ifd flags because the search string must // have BACKSLASHES and '*' escaped. rc = KYCollateValue( pDestKey, &uiDestKeyLen, (FLMBYTE*) pBuf, uiBufLen, pIfd->uiFlags | IFD_ESC_CHAR, pIfd->uiLimit, &uiCollationLen, &uiCaseLen, uiLanguage, TRUE, bDoFirstSubstring, bTrailingWildcard, pbDataTruncated, pbOriginalCharsLost); if (rc == FERR_CONV_DEST_OVERFLOW) { rc = FERR_OK; } else if (RC_BAD( rc)) { goto Exit; } if (pIfd->uiFlags & IFD_POST) { uiDestKeyLen -= uiCaseLen; } else { // Special case: The index is NOT an upper index and the search is // case-insensitive. The FROM key must have lower case values and // the UNTIL must be the upper case values. This will be true for // Asian indexes also. if (uiDestKeyLen && (bIsDBCS || (!(pIfd->uiFlags & IFD_UPPER) && bCaseInsensitive))) { // Subtract off all but the case marker. Remember that for DBCS // (Asian) the case marker is two bytes. uiDestKeyLen -= (uiCaseLen - ((FLMUINT) (bIsDBCS ? 2 : 1))); // NOTE: SC_LOWER is only used in GREEK indexes, which is why we // use it here instead of SC_MIXED. pDestKey[uiDestKeyLen - 1] = (FLMBYTE) ((uiLanguage != (FLMUINT) FLM_GR_LANG) ? COLL_MARKER | SC_MIXED : COLL_MARKER | SC_LOWER); // Once the FROM key has been approximated, we are done building. *pbDoneBuilding = TRUE; } } // Copy or move pieces of the FROM key into the UNTIL key. if (pDestKey == pFromKey + uiFromKeyPos) { if (uiUntilKeyPos < uiMaxKeySize - 2) { if (!bUntilAtEnd) { if (bDoMatchBegin) { if (uiCollationLen) { f_memcpy( &pUntilKey[uiUntilKeyPos], pDestKey, uiCollationLen); uiUntilKeyPos += uiCollationLen; } // Fill the rest of the key with high values. f_memset( &pUntilKey[uiUntilKeyPos], 0xFF, (uiMaxKeySize - 2) - uiUntilKeyPos); uiUntilKeyPos = uiMaxKeySize - 2; // Don't need to set the done building flag to TRUE. } else if (uiDestKeyLen) { if (!bDoFirstSubstring) { f_memcpy( &pUntilKey[uiUntilKeyPos], pDestKey, uiDestKeyLen); uiUntilKeyPos += uiDestKeyLen; } else { // Do two copies so that the first substring byte is // gone. f_memcpy( &pUntilKey[uiUntilKeyPos], pDestKey, uiCollationLen); uiUntilKeyPos += uiCollationLen; if (bIsDBCS) { uiCollationLen++; } uiCollationLen++; f_memcpy( &pUntilKey[uiUntilKeyPos], pDestKey + uiCollationLen, uiDestKeyLen - uiCollationLen); uiUntilKeyPos += (uiDestKeyLen - uiCollationLen); } // Special case again : raw case in index and search // comparison. Case has already been completely removed if // it is a post index, so no need to change the marker // byte. if (!(pIfd->uiFlags & IFD_POST) && (bIsDBCS || (!(pIfd->uiFlags & IFD_UPPER) && bCaseInsensitive))) { // Add 1 to make sure the until key is higher than the // upper value. pUntilKey[uiUntilKeyPos - 1] = (COLL_MARKER | SC_UPPER) + 1; } } } else { if (uiUntilKeyPos > 0) { // Instead of filling the key with 0xFF, increment the // marker. pUntilKey[uiUntilKeyPos - 1]++; } else { // Keys can have 0xFF values in them, so it is not // sufficient to set only uiDestKeyLen bytes to 0xFF. We // must set the entire key. f_memset( pUntilKey, 0xFF, uiMaxKeySize - 2); uiUntilKeyPos = uiMaxKeySize - 2; } } } uiFromKeyPos += uiDestKeyLen; } else { // We just built the UNTIL key. The FROM key doesn't need to be // built. uiUntilKeyPos += uiDestKeyLen; } Exit: // Set the FROM and UNTIL keys *puiFromKeyPos = uiFromKeyPos; *puiUntilKeyPos = uiUntilKeyPos; return (rc); } /**************************************************************************** Desc: Select the best substring for a CONTAINS or MATCH_END search. Look below for the algorithm. ****************************************************************************/ FSTATIC FLMBOOL flmSelectBestSubstr( FLMBYTE ** ppValue, FLMUINT * puiValueLen, FLMUINT uiIfdFlags, FLMBOOL * pbTrailingWildcard) { FLMBYTE * pValue = *ppValue; FLMBYTE * pCurValue; FLMBYTE * pBest; FLMBOOL bBestTerminatesWithWildCard = *pbTrailingWildcard; FLMUINT uiCurLen; FLMUINT uiBestNumChars; FLMUINT uiBestValueLen; FLMUINT uiWildcardPos = 0; FLMUINT uiTargetNumChars; FLMUINT uiNumChars; FLMBOOL bNotUsingFirstOfString = FALSE; #define GOOD_ENOUGH_CHARS 16 // There may not be any wildcards at all. Find the first one. if (flmFindWildcard( pValue, &uiWildcardPos)) { bBestTerminatesWithWildCard = TRUE; pBest = pValue; pCurValue = pValue + uiWildcardPos + 1; uiCurLen = *puiValueLen - (uiWildcardPos + 1); uiBestValueLen = uiWildcardPos; uiBestNumChars = flmCountCharacters( pValue, uiWildcardPos, GOOD_ENOUGH_CHARS, uiIfdFlags); uiTargetNumChars = uiBestNumChars + uiBestNumChars; while (uiBestNumChars < GOOD_ENOUGH_CHARS && *pCurValue) { if (flmFindWildcard( pCurValue, &uiWildcardPos)) { uiNumChars = flmCountCharacters( pCurValue, uiWildcardPos, GOOD_ENOUGH_CHARS, uiIfdFlags); if (uiNumChars >= uiTargetNumChars) { pBest = pCurValue; uiBestValueLen = uiWildcardPos; uiBestNumChars = uiNumChars; uiTargetNumChars = uiNumChars + uiNumChars; } else { uiTargetNumChars += 2; } pCurValue = pCurValue + uiWildcardPos + 1; uiCurLen -= uiWildcardPos + 1; } else { // Check the last section that may or may not have trailing *. uiNumChars = flmCountCharacters( pCurValue, uiCurLen, GOOD_ENOUGH_CHARS, uiIfdFlags); if (uiNumChars >= uiTargetNumChars) { pBest = pCurValue; uiBestValueLen = uiCurLen; bBestTerminatesWithWildCard = *pbTrailingWildcard; } break; } } if (pBest != *ppValue) { bNotUsingFirstOfString = TRUE; } *ppValue = pBest; *puiValueLen = uiBestValueLen; *pbTrailingWildcard = bBestTerminatesWithWildCard; } return (bNotUsingFirstOfString); } /**************************************************************************** Desc: Returns true if this text will generate a single characater key. ****************************************************************************/ FSTATIC FLMUINT flmCountCharacters( FLMBYTE * pValue, FLMUINT uiValueLen, FLMUINT uiMaxToCount, FLMUINT uiIfdFlags) { FLMUINT uiNumChars = 0; FLMUINT uiObjLength; for (uiObjLength = 0; uiNumChars <= uiMaxToCount && uiValueLen; pValue += uiObjLength, uiValueLen = (uiValueLen >= uiObjLength) ? uiValueLen - uiObjLength : 0) { switch ((FLMUINT) (flmTextObjType( *pValue))) { case ASCII_CHAR_CODE: // 0nnnnnnn { uiObjLength = 1; if (*pValue == ASCII_SPACE) { // Ignore if using space rules. VISIT: Need to address ending // spaces before a wildcard. if (uiIfdFlags & (IFD_MIN_SPACES | IFD_NO_SPACE)) { break; } uiNumChars++; } else if (*pValue == ASCII_UNDERSCORE) { // Ignore if using the underscore space rules. VISIT: Need to // address ending spaces before a wildcard. if (uiIfdFlags & IFD_NO_UNDERSCORE) { break; } uiNumChars++; } else if (*pValue == ASCII_DASH) { if (uiIfdFlags & IFD_NO_DASH) { break; } uiNumChars++; } else if (*pValue == ASCII_BACKSLASH && (*(pValue + 1) == ASCII_WILDCARD || *(pValue + 1) == ASCII_BACKSLASH)) { uiObjLength++; uiNumChars++; } else { uiNumChars++; } break; } case WHITE_SPACE_CODE: // 110nnnnn { uiObjLength = 1; uiNumChars++; break; } case CHAR_SET_CODE: // 10nnnnnn case UNK_EQ_1_CODE: case OEM_CODE: { uiObjLength = 2; uiNumChars++; break; } case UNICODE_CODE: // Unconvertable UNICODE code case EXT_CHAR_CODE: { uiObjLength = 3; uiNumChars++; break; } case UNK_GT_255_CODE: { uiObjLength = 1 + sizeof(FLMUINT16) + FB2UW( pValue + 1); break; } case UNK_LE_255_CODE: { uiObjLength = 2 + (FLMUINT16) * (pValue + 1); break; } default: { // should NEVER happen: bug if does flmAssert( 0); uiObjLength = 1; break; } } } return (uiNumChars); } libflaim-4.9.966/src/fsv.h0000644000175000017500000002450410510774540016630 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Client/server definitions. // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsv.h 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #ifndef FSV_H #define FSV_H #include "fpackon.h" // IMPORTANT NOTE: No other include files should follow this one except // for fpackoff.h #define FSV_MAX_TCP_HANDLERS 64 #define FSV_LOG_BUFFER_SIZE 256 // Server defaults #define FSV_DEFAULT_CACHE_SIZE 1024000 #define FSV_DEFAULT_MAX_CONNECTIONS 256 #define FSV_DEFAULT_CONOTBL_TABLE_SIZE 32 #define FSV_DEFAULT_PATH_TABLE_SIZE 32 // Server object types #define FSV_OBJECT_SESSION 0x0001 #define FSV_OBJECT_DATABASE 0x0002 #define FSV_OBJECT_ITERATOR 0x0003 #define FSV_OBJECT_TRANSACTION 0x0004 #define FSV_OBJECT_BLOB 0x0005 #define FSV_OBJECT_PATH 0x0006 #define FSV_OBJECT_ROPS 0x0007 #define FSV_OBJECT_POOL 0x0008 typedef struct FSV_RECORD_ID { FLMUINT uiDatabaseId; FLMUINT uiStore; FLMUINT uiContainer; FLMUINT uiDrn; } FSV_RECORD_ID; typedef void (* FSV_LOG_FUNC)( const char * pszMsg, RCODE rc, FLMUINT uiSeverity, void * pvUserData); #define FSV_LOG_DEBUG 1 #define FSV_LOG_EVENT 2 #define FSV_LOG_ERROR 3 #define FSV_LOG_NOTHING 4 class FSV_SCTX; class FSV_SESN; typedef FSV_SCTX * FSV_SCTX_p; /**************************************************************************** Desc: ****************************************************************************/ class FSV_SCTX : public F_Object { public: FSV_SCTX(); virtual ~FSV_SCTX(); RCODE Setup( FLMUINT uiMaxSessions, const char * pszServerBasePath, FSV_LOG_FUNC pLogFunc); RCODE OpenSession( FLMUINT uiVersion, FLMUINT uiFlags, FLMUINT * puiIdRV, FSV_SESN ** ppSessionRV); RCODE CloseSession( FLMUINT uiId); RCODE GetSession( FLMUINT uiId, FSV_SESN ** ppSession); RCODE SetBasePath( const char * pszServerBasePath); RCODE GetBasePath( char * pszServerBasePath); RCODE SetTempDir( const char * pszTempDir); RCODE BuildFilePath( const FLMUNICODE * puzUrlString, char * pszFilePathRV); void Lock( void); void Unlock( void); void LogMessage( FSV_SESN * pSession, const char * pszMsg, RCODE rc, FLMUINT uiMsgSeverity); private: FLMUINT m_uiSessionToken; FLMUINT m_uiMaxSessions; FLMUINT m_uiCacheSize; char m_szServerBasePath[ F_PATH_MAX_SIZE]; FSV_SESN ** m_paSessions; F_MUTEX m_hMutex; FSV_LOG_FUNC m_pLogFunc; FLMBOOL m_bSetupCalled; char m_pucLogBuf[ FSV_LOG_BUFFER_SIZE]; friend class FSV_SESN; }; class FSV_SESN; typedef FSV_SESN * FSV_SESN_p; /**************************************************************************** Desc: ****************************************************************************/ class FSV_SESN : public F_Object { public: FSV_SESN(); virtual ~FSV_SESN(); RCODE Setup( FSV_SCTX * pServerContext, FLMUINT uiVersion, FLMUINT uiFlags); RCODE OpenDatabase( FLMUNICODE * puzDbPath, FLMUNICODE * puzDataDir, FLMUNICODE * puzRflPath, FLMUINT uiOpenFlags); RCODE CreateDatabase( FLMUNICODE * puzDbPath, FLMUNICODE * puzDataDir, FLMUNICODE * puzRflPath, FLMUNICODE * puzDictPath, FLMUNICODE * puzDictBuf, CREATE_OPTS * pCreateOpts); RCODE CloseDatabase( void); FINLINE HFDB GetDatabase( void) { return( m_hDb); } RCODE InitializeIterator( FLMUINT * puiIteratorIdRV, HFDB hDb, FLMUINT uiContainer, HFCURSOR * phIteratorRV); RCODE FreeIterator( FLMUINT uiIteratorId); RCODE GetIterator( FLMUINT uiIteratorId, HFCURSOR * phIteratorRV); RCODE GetBIStream( FCS_BIOS ** ppBIStream); RCODE GetBOStream( FCS_BIOS ** ppBOStream); FINLINE void setId( FLMUINT uiId) { m_uiSessionId = uiId; } FINLINE FLMUINT getId( void) { return( m_uiSessionId); } FINLINE void setCookie( FLMUINT uiCookie) { m_uiCookie = uiCookie; } FINLINE FLMUINT getCookie( void) { return( m_uiCookie); } FINLINE FLMUINT getFlags( void) { return( m_uiFlags); } FINLINE F_Pool * getWireScratchPool( void) { return &m_wireScratchPool; } FINLINE FLMUINT getClientVersion( void) { return( m_uiClientProtocolVersion); } private: FSV_SCTX * m_pServerContext; HFDB m_hDb; FLMBYTE m_pucLogBuf[ FSV_LOG_BUFFER_SIZE]; FLMUINT m_uiSessionId; FLMUINT m_uiCookie; FLMUINT m_uiFlags; FLMBOOL m_bSetupCalled; FLMUINT m_uiClientProtocolVersion; FCS_BIOS * m_pBIStream; FCS_BIOS * m_pBOStream; #define MAX_SESN_ITERATORS 10 HFCURSOR m_IteratorList[ MAX_SESN_ITERATORS]; F_Pool m_wireScratchPool; }; /**************************************************************************** Server Wire Class ****************************************************************************/ class FSV_WIRE; typedef FSV_WIRE * FSV_WIRE_p; class FSV_WIRE : public FCS_WIRE { private: FLMUINT m_uiOpSeqNum; FLMUINT m_uiClientVersion; FLMUINT m_uiAutoTrans; FLMUINT m_uiType; FLMUINT m_uiAreaId; FLMUINT m_uiMaxLockWait; FLMUNICODE * m_puzDictPath; FLMUNICODE * m_puzDictBuf; FLMUNICODE * m_puzFileName; FLMBYTE * m_pucPassword; FLMUINT * m_pDrnList; NODE * m_pIteratorSelect; NODE * m_pIteratorFrom; NODE * m_pIteratorWhere; NODE * m_pIteratorConfig; FSV_SESN * m_pSession; HFCURSOR m_hIterator; public: FINLINE FSV_WIRE( FCS_DIS * pDIStream, FCS_DOS * pDOStream) : FCS_WIRE( pDIStream, pDOStream) { reset(); } FINLINE ~FSV_WIRE() { } void reset( void); RCODE read( void); FINLINE FLMUINT getOpSeqNum( void) { return( m_uiOpSeqNum); } FINLINE FLMUINT getClientVersion( void) { return( m_uiClientVersion); } FINLINE FLMUINT getAutoTrans( void) { return( m_uiAutoTrans); } FINLINE FLMUINT getFlags( void) { return( m_uiFlags); } FINLINE FLMUINT getType( void) { return( m_uiType); } FINLINE FLMUINT getAreaId( void) { return( m_uiAreaId); } FINLINE FLMUINT getMaxLockWait( void) { return( m_uiMaxLockWait); } FINLINE FLMUNICODE * getDictPath( void) { return( m_puzDictPath); } FINLINE FLMUNICODE * getDictBuffer( void) { return( m_puzDictBuf); } FINLINE FLMUNICODE * getFileName( void) { return( m_puzFileName); } FINLINE FLMBYTE * getPassword( void) { return( m_pucPassword); } FINLINE FLMUINT * getDrnList( void) { return( m_pDrnList); } FINLINE NODE * getIteratorSelect( void) { return( m_pIteratorSelect); } FINLINE NODE * getIteratorFrom( void) { return( m_pIteratorFrom); } FINLINE NODE * getIteratorWhere( void) { return( m_pIteratorWhere); } FINLINE NODE * getIteratorConfig( void) { return( m_pIteratorSelect); } FINLINE FSV_SESN * getSession( void) { return( m_pSession); } FINLINE HFCURSOR getIteratorHandle( void) { return( m_hIterator); } void setSession( FSV_SESN * pSession); FINLINE void setIteratorId( FLMUINT uiId) { m_uiIteratorId = uiId; } FINLINE void setIteratorHandle( HFCURSOR hIterator) { m_hIterator = hIterator; } }; /**************************************************************************** Server BLOB Class ****************************************************************************/ class FSV_BLOB; typedef FSV_BLOB * FSV_BLOB_p; /**************************************************************************** Desc: ****************************************************************************/ class FSV_BLOB : public F_Object { private: HFBLOB m_hBlob; public: FINLINE FSV_BLOB( void) { m_hBlob = HFBLOB_NULL; } virtual FINLINE ~FSV_BLOB() { } FINLINE HFBLOB * getFlmBlob( void) { return( &m_hBlob); } }; RCODE fsvInitGlobalContext( FLMUINT uiMaxSessions, const char * pszServerBasePath, FSV_LOG_FUNC pLogFunc); void fsvFreeGlobalContext( void); RCODE fsvGetGlobalContext( FSV_SCTX ** ppGlobalContext); RCODE fsvSetBasePath( FLMBYTE * pszServerBasePath); RCODE fsvSetTempDir( FLMBYTE * pszTempDir); RCODE fsvProcessRequest( FCS_DIS * pDataIStream, FCS_DOS * pDataOStream, F_Pool * pScratchPool, FLMUINT * puiSessionIdRV); RCODE fsvOpClassDiag( FSV_WIRE_p pWire); RCODE fsvOpClassFile( FSV_WIRE_p pWire); RCODE fsvOpClassAdmin( FSV_WIRE_p pWire); RCODE fsvOpClassGlobal( FSV_WIRE_p pWire); RCODE fsvOpClassSession( FSV_WIRE_p pWire); RCODE fsvOpClassDatabase( FSV_WIRE_p pWire); RCODE fsvOpClassTransaction( FSV_WIRE_p pWire); RCODE fsvOpClassMaintenance( FSV_WIRE * pWire); RCODE fsvOpClassRecord( FSV_WIRE_p pWire); RCODE fsvOpClassIterator( FSV_WIRE_p pWire); RCODE fsvOpClassRops( FSV_WIRE_p pWire); RCODE fsvOpClassBlob( FSV_WIRE_p pWire); RCODE fsvOpClassIndex( FSV_WIRE * pWire); RCODE fsvOpClassMisc( FSV_WIRE * pWire); RCODE fsvDbTransCommitEx( HFDB hDb, FSV_WIRE * pWire); #ifdef FSV_LOGGING void fsvLogHandlerMessage( FSV_SESN * pSession, FLMBYTE * pucMsg, RCODE rc, FLMUINT uiMsgSeverity); #endif RCODE fsvStartTcpListener( FLMUINT uiPort); void fsvShutdownTcpListener( void); RCODE fsvPostStreamedRequest( FSV_SESN * pSession, FLMBYTE * pucPacket, FLMUINT uiPacketSize, FLMBOOL bLastPacket, FCS_BIOS * pSessionResponse); RCODE fsvGetStreamedResponse( FSV_SESN * pSession, FLMBYTE * pucPacketBuffer, FLMUINT uiMaxPacketSize, FLMUINT * puiPacketSize, FLMBOOL * pbLastPacket); RCODE fsvStreamLoopback( FCS_BIOS * pStream, FLMUINT uiEvent, void * UserData); #include "fpackoff.h" #endif libflaim-4.9.966/src/fsconvrt.cpp0000644000175000017500000002354010510774540020230 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Database format conversion routines. // Tabs: 3 // // Copyright (c) 1999-2001,2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsconvrt.cpp 12320 2006-01-19 15:53:51 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE FSConvertNonLeafTree( FDB * pDb, LFILE * pLFile, BTSK * pOldStack, BTSK * pOldStackBase, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData, DB_UPGRADE_INFO * pDbConvertInfo); FSTATIC void FSBuildNonLeafDataElement( BTSK * pStack, FLMBYTE * pElement, FLMUINT * puiElmLen, FLMUINT uiNewElmOvhd, FLMBYTE ** ppKey); /*************************************************************************** Desc: File system conversions from one version to another. *****************************************************************************/ RCODE FSVersionConversion40( FDB * pDb, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; LFILE * pLFileTbl; FLMUINT uiPos, uiTblSize; FLMUINT uiCurrentVersion; BTSK * pStack; BTSK stackBuf[ BH_MAX_LEVELS ]; FLMBYTE pKeyBuf[ DIN_KEY_SIZ + 4 ]; FLMBYTE pDrnKey[ DIN_KEY_SIZ]; DB_UPGRADE_INFO DbConvertInfo; f_memset( &DbConvertInfo, 0, sizeof( DB_UPGRADE_INFO)); // Supported conversions... uiCurrentVersion = pFile->FileHdr.uiVersionNum; // Loop through all of the data blocks in the lfile. pLFileTbl = pDb->pDict->pLFileTbl; uiTblSize = pDb->pDict->uiLFileCnt; FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); for( uiPos = 0; uiPos < uiTblSize; uiPos++) { LFILE * pLFile; pLFile = &pLFileTbl [uiPos]; if( pLFile->uiLfType == LF_CONTAINER) { if( fnStatusCallback) { DbConvertInfo.uiLastDrn = 0; if( RC_BAD( rc = FSGetNextDrn( pDb, pLFile, FALSE, &DbConvertInfo.uiLastDrn))) goto Exit; } // Set up the stack FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; f_UINT32ToBigEndian( 0, pDrnKey); if( RC_BAD(rc = FSBtSearch( pDb, pLFile, &pStack, pDrnKey, DIN_KEY_SIZ,0))) goto Exit; // If pStack isn't at stackBuf[] we have a b-tree of 2 or more levels. // VISIT: the change may not be necessary - may be new version. // Look at the root block type. if( pStack != stackBuf) { DbConvertInfo.uiContainer = pLFile->uiLfNum; if( RC_BAD( rc = FSConvertNonLeafTree( pDb, pLFile, pStack - 1, stackBuf, uiNewVersion, fnStatusCallback, UserData, &DbConvertInfo))) goto Exit; } FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); } } // Loop through all of the data b-trees and look for non-leaf blocks. Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return( rc); } /*************************************************************************** Desc: Convert the non-leaf data blocks from one version to another version. pOldStack points to the level 1 non-leaf data blocks. Read all of the elements and build a new non-leaf b-tree and make this the new tree while freeing up the old non-leaf blocks. *****************************************************************************/ FSTATIC RCODE FSConvertNonLeafTree( FDB * pDb, LFILE * pLFile, BTSK * pOldStack, BTSK * pOldStackBase, FLMUINT uiNewVersion, STATUS_HOOK fnStatusCallback, void * UserData, DB_UPGRADE_INFO * pDbConvertInfo) { RCODE rc = FERR_OK; BTSK stackBuf[ BH_MAX_LEVELS ]; BTSK * pStack; FLMBYTE pKeyBuf[ DIN_KEY_SIZ + 4 ]; FLMBYTE pDrnKey[ DIN_KEY_SIZ]; FLMBYTE pElement[ DIN_KEY_SIZ + 16]; // Enough bytes for either format SCACHE * pTempSCache; FLMUINT uiBlkAddr; FSInitStackCache( &stackBuf [0], BH_MAX_LEVELS); f_UINT32ToBigEndian( 0, pDrnKey); /* Free the b-tree blocks that are above this lowest non-leaf level. This will reuse the blocks instead of allocating from the avail list or end of file. */ pOldStack->uiFlags &= ~FULL_STACK; for( ; pOldStackBase != pOldStack; pOldStackBase++ ) { uiBlkAddr = pOldStackBase->uiBlkAddr; // Release block so that caller doesn't release an avail block. FSReleaseBlock( pOldStackBase, FALSE); while( uiBlkAddr != BT_END) { if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; uiBlkAddr = FB2UD( &pTempSCache->pucBlk[ BH_NEXT_BLK ]); if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; } } // Allocate a new root block in the new NON-LEAF format. pStack = stackBuf; pStack->pKeyBuf = pKeyBuf; pLFile->uiRootBlk = BT_END; if( RC_BAD( flmLFileWrite( pDb, pLFile))) { goto Exit; } if( RC_BAD( rc = flmLFileInit( pDb, pLFile))) { goto Exit; } if( RC_BAD(rc = FSGetBlock( pDb, pLFile, pLFile->uiRootBlk, pStack))) { goto Exit; } if( RC_BAD( rc = FSLogPhysBlk( pDb, pStack))) { goto Exit; } // Remove both leading elements in the block and setup as a non-leaf block. UW2FBA( BH_OVHD, &pStack->pBlk[ BH_BLK_END]); if( uiNewVersion >= FLM_FILE_FORMAT_VER_4_0) { pStack->pBlk[ BH_TYPE ] = BHT_NON_LEAF_DATA + BHT_ROOT_BLK; } else { pStack->pBlk[ BH_TYPE ] = BHT_NON_LEAF + BHT_ROOT_BLK; } pStack->pBlk[ BH_LEVEL ] = 1; pStack->uiKeyBufSize = DIN_KEY_SIZ; FSBlkToStack( pStack); pStack->uiFlags = FULL_STACK; // Build the key for the first element in the old stack. if( RC_BAD( rc = FSBtScanTo( pOldStack, NULL, 0, 0))) { goto Exit; } // Start inserting from pOldStack to pStack until pOldStack is done. for(;;) { FLMUINT uiElmLen; FLMBYTE * pKey; FSBuildNonLeafDataElement( pOldStack, pElement, &uiElmLen, pStack->uiElmOvhd, &pKey); if( uiNewVersion <= FLM_FILE_FORMAT_VER_3_02) { flmAssert( pStack->uiCurElm == pStack->uiBlkEnd); if( pStack != stackBuf) { flmAssert( stackBuf[0].uiCurElm + 6 == stackBuf[0].uiBlkEnd); } pStack->uiCurElm = pStack->uiBlkEnd; // Call scanto to place the last element's key in keybuf. if( RC_BAD( rc = FSBtScanTo( pStack, NULL, 0, 0))) goto Exit; if( pStack->uiBlkEnd > BH_OVHD) { if( uiElmLen > BNE_KEY_START) { pStack->uiPrevElmPKC = FSElmComparePKC( pKeyBuf, DIN_KEY_SIZ, pKey, DIN_KEY_SIZ); } } } if( RC_BAD( rc = FSBtInsert( pDb, pLFile, &pStack, pElement, uiElmLen))) goto Exit; // Go to next element so we always insert at the end. (void) FSBtNextElm( pDb, pLFile, pStack ); uiBlkAddr = pOldStack->uiBlkAddr; if( RC_BAD( rc = FSBtNextElm( pDb, pLFile, pOldStack ))) { // All done? Free the last block in the old b-tree. if( rc == FERR_BT_END_OF_DATA) { if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; // No need to release pTempSCache. FSBlockFree does it. if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; rc = FERR_OK; break; } goto Exit; } // Did next element position to a new block? if( uiBlkAddr != pOldStack->uiBlkAddr) { // Do callback to report progress. if (fnStatusCallback) { pDbConvertInfo->uiDrn = f_bigEndianToUINT32( pOldStack->pKeyBuf); if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_UPGRADE_STATUS, (void *) pDbConvertInfo, (void *)0, UserData))) { goto Exit; } } // Free the previous block back into the avail list. if( RC_BAD( rc = ScaGetBlock( pDb, pLFile, BHT_LEAF, uiBlkAddr, NULL, &pTempSCache))) goto Exit; if( RC_BAD( FSBlockFree( pDb, pTempSCache))) goto Exit; // No need to release pTempSCache. FSBlockFree does it. } } Exit: FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE); return( rc); } /*************************************************************************** Desc: Build a non-leaf data element from the current stack position. *****************************************************************************/ FSTATIC void FSBuildNonLeafDataElement( BTSK * pStack, FLMBYTE * pElement, FLMUINT * puiElmLen, FLMUINT uiNewElmOvhd, FLMBYTE ** ppKey) { FLMUINT uiElmLen; // Grab the key from the key buffer, if( uiNewElmOvhd == BNE_DATA_OVHD) // New fixed length 4.0 format. { FLMBYTE * pOldElm = CURRENT_ELM( pStack); uiElmLen = BNE_DATA_OVHD; *ppKey = pElement; // Check for last element marker. if( pOldElm[ BBE_PKC ] == 0 && pOldElm[ BBE_KL] == 0) { f_UINT32ToBigEndian( (FLMUINT32)DRN_LAST_MARKER, pElement); } else { f_memcpy( pElement, pStack->pKeyBuf, DIN_KEY_SIZ); } } else // Old variable length 3.x format. { pElement [ BBE_PKC ] = 0; // Set PKC, DOMAIN to zero if( FB2UD( pStack->pKeyBuf) == DRN_LAST_MARKER) { pElement [ BBE_KL ] = 0; uiElmLen = BNE_KEY_START; } else { pElement [ BBE_PKC ] = 0; // Set PKC, DOMAIN to zero pElement [ BBE_KL ] = DIN_KEY_SIZ; // sizeof( FLMUINT32) or 4 uiElmLen = BNE_KEY_START + DIN_KEY_SIZ; f_memcpy( pElement + BNE_KEY_START, pStack->pKeyBuf, DIN_KEY_SIZ); } *ppKey = pElement + BNE_KEY_START; } // Set the child block address. FSSetChildBlkAddr( pElement, FSChildBlkAddr( pStack), uiNewElmOvhd); *puiElmLen = uiElmLen; return; } libflaim-4.9.966/src/fgedcom.cpp0000644000175000017500000017773010510774540020003 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: GEDCOM routines // Tabs: 3 // // Copyright (c) 1990-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id$ //------------------------------------------------------------------------- #include "flaimsys.h" extern FLMBYTE arr[]; #define BINARY_GED_HEADER_LEN 8 static FLMBYTE FlmBinaryGedHeader[BINARY_GED_HEADER_LEN] = { 0xFF, 'F', 'L', 'M', 'D', 'I', 'C', 'T' }; static FLMBYTE FlmBinaryRecHeader[BINARY_GED_HEADER_LEN] = { 0xFF, 'F', 'L', 'M', 'R', 'E', 'C', 'S' }; #define NODE_DRN_POS 0 #define NODE_CONTAINER_POS ( NODE_DRN_POS + sizeof(FLMUINT)) #define NODE_DB_POS ( NODE_CONTAINER_POS + sizeof(FLMUINT)) #define f_isdigit(c) \ ((c) < 60 ? ((((FLMBYTE) (arr[(c) >> 3])) << ((c) & 0x07)) & 0x80) : 0) FSTATIC RCODE expWrite( EXP_IMP_INFO * pExpImpInfo, const FLMBYTE * pData, FLMUINT uiDataLen); FSTATIC RCODE impRead( EXP_IMP_INFO * pExpImpInfo, FLMBYTE * pData, FLMUINT uiDataLen, FLMUINT * puiBytesReadRV); FSTATIC RCODE tagValLenType( F_Pool * pPool, GED_STREAM * x, NODE ** ppNode, F_NameTable * pNameTable); /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetBINARY( NODE * pNode, void * buffer, FLMUINT * bufLenRV) { RCODE rc = FERR_OK; FLMBYTE * ptr; FLMUINT valLength; FLMUINT outputData; FLMUINT uiNodeType; // Check for a null node if (!pNode) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } // If the node is not a BINARY node, return an error uiNodeType = GedValType( pNode); if (uiNodeType != FLM_BINARY_TYPE) { rc = RC_SET( FERR_CONV_ILLEGAL); goto Exit; } ptr = (FLMBYTE *) GedValPtr( pNode); valLength = GedValLen( pNode); // At this point we know the node is a BINARY node outputData = ((buffer != NULL) && (*bufLenRV)); if ((outputData) && (valLength)) { if (valLength > *bufLenRV) { rc = RC_SET( FERR_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( buffer, ptr, valLength); } *bufLenRV = valLength; Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutBINARY( F_Pool * pPool, NODE * pNode, const void * pvData, FLMUINT uiDataLen, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMBYTE * outPtr; // Check for a null node being passed in if (!pNode) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } // If data pointer is NULL or length is zero, call GedAllocSpace with // a length of zero to set the node length to zero and node type to // FLM_BINARY_TYPE. if (pvData == NULL || !uiDataLen) { (void) GedAllocSpace( pPool, pNode, FLM_BINARY_TYPE, 0, uiEncId, uiEncSize); goto Exit; } // Allocate space in the node for the binary data if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_BINARY_TYPE, uiDataLen, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Set the node type and copy the data into the node f_memcpy( outPtr, pvData, uiDataLen); if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedToTree( F_Pool * pPool, IF_FileHdl * pFileHdl, char ** pBuf, FLMUINT uiBufSize, NODE ** ppRoot, F_NameTable * pNameTable) { RCODE rc = FERR_OK; GED_STREAM gedStream; FLMUINT level; FLMUINT levelBase = 0; FLMUINT levelPrior = 0; FLMBYTE nextChar; NODE * nd = NULL; NODE * ndPrior = NULL; FLMUINT64 ui64StartPos; gedStream.pFileHdl = pFileHdl; gedStream.pThis = gedStream.pBuf = *pBuf; gedStream.uiBufSize = uiBufSize; if (pFileHdl) { // Find 1st starting file position if (RC_OK( pFileHdl->seek( 0, FLM_IO_SEEK_CUR, &gedStream.ui64FilePos))) { gedStream.pLast = gedStream.pBuf; gedReadChar( &gedStream, gedStream.ui64FilePos); } else { return (RC_SET( FERR_FILE_ER)); } } else { gedStream.errorIO = 0; gedStream.ui64FilePos = 0; gedStream.pLast = gedStream.pBuf + (uiBufSize - 1); gedStream.thisC = f_toascii( *gedStream.pBuf); } for (;;) { gedSkipBlankLines( &gedStream); ui64StartPos = gedStream.ui64FilePos; if (f_isdigit( gedStream.thisC)) { level = 0; do { level = gedStream.thisC - ASCII_ZERO + (level * 10); nextChar = (FLMBYTE) (gedNextChar( &gedStream)); } while (f_isdigit( nextChar)); if (!f_iswhitespace( gedStream.thisC)) { rc = RC_SET( FERR_BAD_FIELD_LEVEL); break; } if (level > GED_MAXLVLNUM) { rc = RC_SET( FERR_GED_MAXLVLNUM); break; } if (ndPrior) { if (levelBase >= level) { goto successful; } else if ((levelPrior < level) && ((levelPrior + 1) != level)) { rc = RC_SET( FERR_GED_SKIP_LEVEL); break; } } else { levelBase = level; } levelPrior = level; if( RC_OK( rc = tagValLenType( pPool, &gedStream, &nd, pNameTable))) { if (ndPrior) { ndPrior->next = nd; } else { *ppRoot = nd; } nd->prior = ndPrior; GedNodeLevelSet( nd, level - levelBase); ndPrior = nd; continue; } } else if (gedStream.thisC == '\0' || gedStream.thisC == ASCII_CTRLZ) { if (gedStream.errorIO) { rc = RC_SET( FERR_FILE_ER); } else if (ndPrior) { successful: ndPrior->next = NULL; if( !pFileHdl) { *pBuf = gedStream.pThis + (FLMINT32) (ui64StartPos - gedStream.ui64FilePos); } gedStream.ui64FilePos = ui64StartPos; rc = FERR_OK; } else { rc = RC_SET( FERR_END); } } else { rc = RC_SET( FERR_BAD_FIELD_LEVEL); } break; } if (RC_BAD( rc)) { *ppRoot = NULL; if (pFileHdl == NULL) { *pBuf = gedStream.pThis; } } if (pFileHdl) { pFileHdl->seek( gedStream.ui64FilePos, FLM_IO_SEEK_SET, &gedStream.ui64FilePos); } return (rc); } /***************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE tagValLenType( F_Pool * pPool, GED_STREAM * pGedStream, NODE ** newNode, F_NameTable * pNameTable) { FLMUINT64 ui64StartPos; RCODE rc = FERR_OK; NODE * nd; FLMUINT drn = 0; FLMUINT uiTagNum; char tagBuf[ GED_MAXTAGLEN + 1]; gedSkipWhiteSpaces( pGedStream); ui64StartPos = pGedStream->ui64FilePos; if (pGedStream->thisC == ASCII_AT) { int badDRN; for (badDRN = 0, gedNextChar( pGedStream); pGedStream->thisC != ASCII_AT; gedNextChar( pGedStream)) { FLMUINT priorDrn = drn; if (!badDRN) { if (f_isdigit( pGedStream->thisC)) { drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO; badDRN = priorDrn != (drn / 10); } else { badDRN = 1; } } } if (badDRN) { drn = 0; } gedNextChar( pGedStream); if (f_iswhitespace( pGedStream->thisC)) { gedSkipWhiteSpaces( pGedStream); } else { rc = RC_SET( FERR_GED_BAD_RECID); goto Exit; } } // Determine the Tag Number and Build the NODE ui64StartPos = pGedStream->ui64FilePos; if (!gedCopyTag( pGedStream, tagBuf)) { return (RC_SET( FERR_INVALID_TAG)); } if (!pNameTable->getFromTagTypeAndName( NULL, tagBuf, FLM_FIELD_TAG, &uiTagNum)) { // See if tag is the reserved tag with the number following if (tagBuf[0] == f_toascii( 'T') && tagBuf[1] == f_toascii( 'A') && tagBuf[2] == f_toascii( 'G') && tagBuf[3] == f_toascii( '_')) { uiTagNum = f_atoi( &tagBuf[4]); } else { return (RC_SET( FERR_NOT_FOUND)); } } if ((*newNode = nd = GedNodeCreate( pPool, uiTagNum, drn, &rc)) == NULL) { goto Exit; } gedSkipWhiteSpaces( pGedStream); ui64StartPos = pGedStream->ui64FilePos; if (pGedStream->thisC == ASCII_AT) { for (drn = 0; gedNextChar( pGedStream) != ASCII_AT;) { FLMUINT priorDrn = drn; if (f_isdigit( pGedStream->thisC)) { drn = (drn * 10) + pGedStream->thisC - ASCII_ZERO; if (priorDrn == (drn / 10)) { continue; } } rc = RC_SET( FERR_GED_BAD_VALUE); goto Exit; } gedNextChar( pGedStream); GedPutRecPtr( pPool, nd, drn); if (gedCopyValue( pGedStream, NULL)) { rc = RC_SET( FERR_GED_BAD_VALUE); goto Exit; } } else { FLMINT valLength; FLMUINT64 ui64TempPos = pGedStream->ui64FilePos; if ((valLength = gedCopyValue( pGedStream, NULL)) > 0) { char * vp = (char *) GedAllocSpace( pPool, nd, FLM_TEXT_TYPE, valLength); if (vp) { gedReadChar( pGedStream, ui64TempPos); gedCopyValue( pGedStream, vp); } else { rc = RC_SET( FERR_MEM); goto Exit; } } } ui64StartPos = pGedStream->ui64FilePos; Exit: gedReadChar( pGedStream, ui64StartPos); return (rc); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedNodeCopy( F_Pool * pPool, NODE * pNode, NODE * pChildList, NODE * pSibList) { NODE * newNd; FLMUINT bias; FLMBYTE * vp; RCODE rc; HFDB hDb; FLMUINT uiContainer; FLMUINT uiRecId; // If the node has source information, we need to copy it if (RC_OK( GedGetRecSource( pNode, &hDb, &uiContainer, &uiRecId))) { // The passed in node contains record source information, so create // a GEDCOM record source node if (RC_BAD( gedCreateSourceNode( pPool, GedTagNum( pNode), hDb, uiContainer, uiRecId, &newNd))) { return (NULL); } } else { // Create a normal (non-source) GEDCOM node if ((newNd = GedNodeMake( pPool, GedTagNum( pNode), &rc)) == NULL) { return (NULL); } } newNd->prior = NULL; newNd->next = pChildList; GedNodeLevelSet( newNd, 0); if ((vp = (FLMBYTE *) GedAllocSpace( pPool, newNd, GedValType( pNode), GedValLen( pNode), pNode->ui32EncId, GedEncLen( pNode))) != NULL) { f_memcpy( vp, GedValPtr( pNode), GedValLen( pNode)); if (pNode->ui32EncFlags & FLD_HAVE_ENCRYPTED_DATA) { f_memcpy( GedEncPtr( newNd), GedEncPtr( pNode), GedEncLen( pNode)); } newNd->ui32EncFlags = pNode->ui32EncFlags; } else { return (NULL); } if (pChildList) { pChildList->prior = newNd; for (bias = GedNodeLevel( pChildList) - 1; pChildList->next; GedNodeLevelSub( pChildList, bias), pChildList = pChildList->next); GedNodeLevelSub( pChildList, bias); pChildList->next = pSibList; } else { pChildList = newNd; } if (pSibList) { pSibList->prior = pChildList; pChildList->next = pSibList; for (bias = GedNodeLevel( pSibList); pSibList->next; GedNodeLevelSub( pSibList, bias), pSibList = pSibList->next); GedNodeLevelSub( pSibList, bias); } return (newNd); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedCopy( F_Pool * pPool, FLMUINT cnt, NODE * tree) { NODE * oldNd; NODE * newNd; NODE * newRoot; FLMUINT baseLevel; if (tree) { newRoot = newNd = GedNodeCopy( pPool, tree, NULL, NULL); if (newRoot) { for (baseLevel = GedNodeLevel( tree); (tree = tree->next) != NULL && (GedNodeLevel( tree) > baseLevel || (GedNodeLevel( tree) == baseLevel && --cnt));) { oldNd = newNd; if ((newNd = GedNodeCopy( pPool, tree, NULL, NULL)) != NULL) { oldNd->next = newNd; newNd->prior = oldNd; GedNodeLevelSet( newNd, GedNodeLevel( tree) - baseLevel); } else { return (NULL); } } } return (newRoot); } return (NULL); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedClip( FLMUINT treeCnt, NODE * self) { NODE * next; if (self) { FLMUINT oldLevel = GedNodeLevel( self); GedNodeLevelSet( self, 0); for (next = self->next; next && (GedNodeLevel( next) > oldLevel || (GedNodeLevel( next) == oldLevel && --treeCnt)); GedNodeLevelSub( next, oldLevel), next = next->next) { ; } if (self->prior) { self->prior->next = next; } if (next) { next->prior->next = NULL; next->prior = self->prior; } self->prior = NULL; } return (self); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedFind( FLMUINT treeCnt, NODE * nd, FLMUINT tnum, FLMINT nth) { if (nd) { FLMUINT strtLvl = GedNodeLevel( nd); do { if ((tnum == GedTagNum( nd)) && (--nth < 1)) { return (nd); } } while( (nd = nd->next) != NULL && (GedNodeLevel( nd) > strtLvl || (--treeCnt && GedNodeLevel( nd) == strtLvl))); } return (NULL); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedPathFind( FLMUINT treeCnt, NODE * nd, FLMUINT * puiPathArray, FLMINT nth) { NODE * pNode = nd; NODE * savenode; FLMUINT * path; if (nd && puiPathArray) { FLMUINT uiLevel = GedNodeLevel( nd); for (;;) { path = puiPathArray + (GedNodeLevel( pNode) - uiLevel); savenode = pNode; if (*path == GedTagNum( pNode)) { if (*(path + 1) == 0 && (--nth < 1)) { return (pNode); } if ((pNode = GedChild( pNode)) != NULL) { continue; } pNode = savenode; } do { pNode = pNode->next; } while (pNode != NULL && GedNodeLevel( pNode) > GedNodeLevel( savenode)); // find next sibling/uncle/end if (!pNode || GedNodeLevel( pNode) < uiLevel || (GedNodeLevel( pNode) == uiLevel && !(--treeCnt))) { break; } } } return (NULL); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedChildGraft( NODE * parent, NODE * child, FLMINT nth) { NODE * lastChildNode; if (parent && child) { FLMINT level = GedNodeLevel( parent) + 1; if (GedChild( parent)) { GedSibGraft( GedChild( parent), child, (FLMINT) (nth == GED_FIRST ? GED_FIRST : nth - 1)); } else { for (lastChildNode = child; lastChildNode->next; GedNodeLevelAdd( lastChildNode, level), lastChildNode = lastChildNode->next); child->prior = parent; GedNodeLevelAdd( lastChildNode, level); lastChildNode->next = parent->next; if (parent->next) { parent->next->prior = lastChildNode; } parent->next = child; } } return (parent); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedSibGraft( NODE * self, NODE * sib, FLMINT nth) { NODE * lastSibNode; NODE * returnNode; FLMINT deltaLevel; FLMUINT level; FLMUINT linkAt = TRUE; if (!sib) { return (self); } if (!self) { return (sib); } for( level = GedNodeLevel( self), deltaLevel = (FLMINT) (level - GedNodeLevel( sib)), lastSibNode = sib; lastSibNode->next; GedNodeLevelAdd( lastSibNode, deltaLevel), lastSibNode = lastSibNode->next); GedNodeLevelAdd( lastSibNode, deltaLevel); if (nth != GED_LAST) { nth++; } if (nth <= 0) { returnNode = sib; while (nth) { if (self->prior) { self = self->prior; if (GedNodeLevel( self) > level) { continue; } else if (GedNodeLevel( self) == level) { nth++; continue; } self = self->next; } break; } } else { returnNode = self; while (nth) { if (self->next) { self = self->next; if (GedNodeLevel( self) > level) { continue; } else if (GedNodeLevel( self) == level) { nth--; continue; } self = self->prior; } linkAt = FALSE; break; } } if (linkAt) { // Link the sib tree AT the current self location - link before self sib->prior = self->prior; lastSibNode->next = self; if (self->prior) { self->prior->next = sib; } self->prior = lastSibNode; } else { // Link the sib tree AFTER the current self location sib->prior = self; lastSibNode->next = self->next; if (self->next) { self->next->prior = lastSibNode; } self->next = sib; } return (returnNode); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedNodeCreate( F_Pool * pPool, FLMUINT tagNum, FLMUINT id, RCODE * rc) { NODE * nd; *rc = FERR_OK; if( RC_BAD( *rc = pPool->poolAlloc( (sizeof(NODE) + (id ? sizeof(id) : 0)), (void **)&nd))) { goto Exit; } f_memset( nd, '\0', sizeof(NODE)); GedValTypeSet( nd, FLM_CONTEXT_TYPE); GedTagNumSet( nd, tagNum); if (id) { FLMBYTE * ptr; GedValTypeSetFlag( nd, HAS_REC_ID); ptr = ((FLMBYTE *) nd) + sizeof(NODE); *((FLMUINT *) (ptr + NODE_DRN_POS)) = id; } Exit: return (nd); } /***************************************************************************** Desc: *****************************************************************************/ void * GedAllocSpace( F_Pool * pPool, NODE * pNode, FLMUINT valType, FLMUINT size, FLMUINT uiEncId, FLMUINT uiEncSize) { FLMBYTE * rPtr; FLMUINT uiAllocSize = size; if (valType == FLM_TEXT_TYPE) { uiAllocSize++; } if (uiAllocSize <= sizeof(void *)) { // If the size is less than sizeof (void *), we use the space right // inside value pointer itself. rPtr = (FLMBYTE *) &pNode->value; } else if (size <= GedValLen( pNode)) { // If there is already allocated space, just re-use it rPtr = (FLMBYTE *) GedValPtr( pNode); } else { if( RC_BAD( pPool->poolAlloc( uiAllocSize, (void **)&rPtr))) { pNode->ui32Length = 0; pNode->value = NULL; return (NULL); } pNode->value = rPtr; } if (valType == FLM_TEXT_TYPE) { rPtr[size] = '\0'; } // Now set the size and the data type pNode->ui32Length = (FLMUINT32) size; GedSetType( pNode, valType); // If passed-in enc id is zero, use the node's enc id. if (!uiEncId) { flmAssert( !uiEncSize); if (size) { uiEncId = pNode->ui32EncId; uiEncSize = size + (16 - (size % 16)); } } else { // We only should have an encryption ID if size is non-zero. If // size is non-zero, encryption size must also be non-zero. flmAssert( size); flmAssert( uiEncSize); } if (uiEncId) { if (uiEncSize > GedEncLen( pNode)) { if( RC_BAD( pPool->poolAlloc( uiEncSize, (void **)&pNode->pucEncValue))) { pNode->ui32EncLength = 0; pNode->pucEncValue = NULL; return (NULL); } } pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA | FLD_HAVE_ENCRYPTED_DATA; pNode->ui32EncId = (FLMUINT32) uiEncId; pNode->ui32EncLength = (FLMUINT32) uiEncSize; } return (rPtr); } /***************************************************************************** Desc: *****************************************************************************/ void * GedValPtr( NODE * nd) { return( nd && nd->ui32Length ? GedValType( nd) == FLM_TEXT_TYPE ? nd->ui32Length < sizeof(void *) ? (void *) &nd->value : (void *) nd->value : nd->ui32Length > sizeof(void *) ? (void *) nd->value : (void *) &nd->value : NULL); } /***************************************************************************** Desc: *****************************************************************************/ void * GedEncPtr( NODE * nd) { return( nd && nd->ui32EncLength ? (void *) nd->pucEncValue : (void *) NULL); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutRecId( F_Pool * pPool, NODE ** ppNd, FLMUINT uiId) { NODE * pNewNd; NODE * pOldNd = *ppNd; FLMBYTE * ptr; if( RC_BAD( pPool->poolAlloc( sizeof(NODE) + sizeof(uiId), (void **)&pNewNd))) { *ppNd = NULL; return (RC_SET( FERR_MEM)); } // Copy the contents of the existing node pNewNd->prior = pOldNd->prior; pNewNd->next = pOldNd->next; pNewNd->value = pOldNd->value; pNewNd->ui32Length = pOldNd->ui32Length; pNewNd->ui32EncId = pOldNd->ui32EncId; pNewNd->ui32EncLength = pOldNd->ui32EncLength; pNewNd->ui32EncFlags = pOldNd->ui32EncFlags; pNewNd->pucEncValue = pOldNd->pucEncValue; GedTagNumSet( pNewNd, GedTagNum( pOldNd)); GedNodeLevelSet( pNewNd, GedNodeLevel( pOldNd)); GedNodeTypeSet( pNewNd, (GedNodeType( pOldNd) | HAS_REC_ID)); // Link in new node to parent and children/siblings if (pNewNd->prior) { pNewNd->prior->next = pNewNd; } if (pNewNd->next) { pNewNd->next->prior = pNewNd; } // Set the Ids value ptr = (FLMBYTE *) GedIdPtr( pNewNd); *((FLMUINT *) (ptr + NODE_DRN_POS)) = uiId; *ppNd = pNewNd; return (FERR_OK); } /***************************************************************************** Desc: *****************************************************************************/ void gedSetRecSource( NODE * pNode, HFDB hDb, FLMUINT uiContainer, FLMUINT uiDrn) { FLMBYTE * pucPtr; pucPtr = ((FLMBYTE *) pNode) + sizeof(NODE); if (uiDrn) { GedValTypeSetFlag( pNode, HAS_REC_ID); *((FLMUINT *) (pucPtr + NODE_DRN_POS)) = uiDrn; } if (uiContainer) { GedValTypeSetFlag( pNode, HAS_REC_SOURCE); *((FLMUINT *) (pucPtr + NODE_CONTAINER_POS)) = uiContainer; } if (hDb) { GedValTypeSetFlag( pNode, HAS_REC_SOURCE); *((HFDB *) (pucPtr + NODE_DB_POS)) = hDb; } } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetRecSource( NODE * pNode, HFDB * phDb, FLMUINT * puiContainer, FLMUINT * puiRecId) { RCODE rc = FERR_OK; FLMBYTE * ptr = ((FLMBYTE *) pNode) + sizeof(NODE); if (GedNodeType( pNode) & HAS_REC_SOURCE) { if (phDb) { *phDb = *((HFDB *) (ptr + NODE_DB_POS)); } if (puiContainer) { *puiContainer = *((FLMUINT *) (ptr + NODE_CONTAINER_POS)); } if (puiRecId) { *puiRecId = *((FLMUINT *) (ptr + NODE_DRN_POS)); } } else if (GedNodeType( pNode) & HAS_REC_ID) { if (phDb) { *phDb = NULL; } if (puiContainer) { *puiContainer = 0; } if (puiRecId) { *puiRecId = *((FLMUINT *) (ptr + NODE_DRN_POS)); } } else { // The record contains no record source, because the user may ignore // the return code lets make sure everything is set to null. if (phDb) { *phDb = NULL; } if (puiContainer) { *puiContainer = 0; } if (puiRecId) { *puiRecId = 0; } rc = RC_SET( FERR_NOT_FOUND); goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutRecPtr( F_Pool * pPool, NODE * nd, FLMUINT drn, FLMUINT uiEncId, FLMUINT uiEncSize) { void * ptr; RCODE rc = FERR_OK; // Check for a null node being passed in if (nd == NULL) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } if ((ptr = GedAllocSpace( pPool, nd, FLM_CONTEXT_TYPE, sizeof(FLMUINT32), uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } UD2FBA( (FLMUINT32) drn, (FLMBYTE *)ptr); if (nd->ui32EncId) { nd->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetRecPtr( NODE * nd, FLMUINT * drnRV) { RCODE rc = FERR_OK; *drnRV = (FLMUINT) 0xFFFFFFFF; if (nd == NULL) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } if (nd->ui32EncId) { if (!(nd->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (GedValType( nd) != FLM_CONTEXT_TYPE) { rc = RC_SET( FERR_CONV_ILLEGAL); goto Exit; } if (GedValLen( nd) == sizeof(FLMUINT32)) { *drnRV = (FLMUINT) (FB2UD( (FLMBYTE *) GedValPtr( nd))); } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedWalk( FLMUINT treeCnt, NODE * pNode, GEDWALK_FUNC_p func, void * arg) { RCODE rc; if (pNode) { FLMUINT baseLevel = GedNodeLevel( pNode); do { rc = (*func)((GedNodeLevel( pNode) - baseLevel), pNode, arg); } while( RC_OK( rc) && (pNode = pNode->next) != NULL && (GedNodeLevel( pNode) > baseLevel || (GedNodeLevel( pNode) == baseLevel && --treeCnt))); } else { rc = FERR_OK; } return (rc); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedSibNext( NODE * pNode) { FLMUINT lev; if (pNode) { lev = GedNodeLevel( pNode); while( ((pNode = pNode->next) != NULL) && (GedNodeLevel( pNode) > lev)); } return ((pNode && (GedNodeLevel( pNode) == lev)) ? pNode : NULL); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedParent( NODE * pNode) { if (pNode) { FLMUINT lev = GedNodeLevel( pNode); while( ((pNode = pNode->prior) != NULL) && (GedNodeLevel( pNode) >= lev)); } return (pNode); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedChild( NODE * pNode) { return( pNode && pNode->next && (GedNodeLevel( pNode->next) > GedNodeLevel( pNode)) ? pNode->next : NULL); } /***************************************************************************** Desc: *****************************************************************************/ NODE * GedSibPrev( NODE * pNode) { FLMUINT lev; if (pNode) { lev = GedNodeLevel( pNode); while( ((pNode = pNode->prior) != NULL) && (GedNodeLevel( pNode) > lev)); } return ((pNode && (GedNodeLevel( pNode) == lev)) ? pNode : NULL); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutNATIVE( F_Pool * pPool, NODE * pNode, const char * nativeString, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMUINT allocLength; FLMBYTE * outPtr; // Check for a null node being passed in if (!pNode) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } // If the string is NULL or empty, call GedAllocSpace with a length // of zero to set the node length to zero and node type to // FLM_TEXT_TYPE. if ((!nativeString) || (!(*nativeString))) { (void) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, 0, uiEncId, uiEncSize); goto Exit; } // Determine the size of the buffer needed to store the string if (RC_BAD( rc = FlmNative2Storage( nativeString, 0, &allocLength, NULL))) { goto Exit; } if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, allocLength, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Convert the string if (RC_BAD( rc = FlmNative2Storage( nativeString, 0, &allocLength, outPtr))) { goto Exit; } // Encrypted fields - only have decrypetd data at this point. if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetNATIVE( NODE * pNode, char * pszBuffer, FLMUINT * bufLenRV) { RCODE rc = FERR_OK; FLMBYTE * ptr; FLMUINT valLength; FLMUINT uiNodeType; if (!pNode) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } // If the node is not a TEXT or a NUMBER node, return an error for now uiNodeType = (FLMBYTE) GedValType( pNode); if ((uiNodeType == FLM_BINARY_TYPE) || (uiNodeType == FLM_CONTEXT_TYPE)) { rc = RC_SET( FERR_CONV_ILLEGAL); goto Exit; } ptr = (FLMBYTE *) GedValPtr( pNode); valLength = GedValLen( pNode); rc = FlmStorage2Native( uiNodeType, valLength, (const FLMBYTE *) ptr, bufLenRV, pszBuffer); Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutUINT( F_Pool * pPool, NODE * pNode, FLMUINT uiNum, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMBYTE * pucPtr; FLMBYTE ucStorageBuf[F_MAX_NUM_BUF + 1]; FLMUINT uiStorageLen; if (pNode == NULL) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } uiStorageLen = sizeof( ucStorageBuf); if( RC_BAD( rc = FlmUINT2Storage( uiNum, &uiStorageLen, ucStorageBuf))) { goto Exit; } // Allocate the needed space. if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, uiStorageLen, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memcpy( pucPtr, ucStorageBuf, uiStorageLen); if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutUINT64( F_Pool * pPool, NODE * pNode, FLMUINT64 ui64Num, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMBYTE * pucPtr; FLMBYTE ucStorageBuf[F_MAX_NUM64_BUF + 1]; FLMUINT uiStorageLen; if (pNode == NULL) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } uiStorageLen = sizeof( ucStorageBuf); if( RC_BAD( rc = FlmUINT64ToStorage( ui64Num, &uiStorageLen, ucStorageBuf))) { goto Exit; } // Allocate the needed space. if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, uiStorageLen, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memcpy( pucPtr, ucStorageBuf, uiStorageLen); if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutINT( F_Pool * pPool, NODE * pNode, FLMINT iNum, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMBYTE * pucPtr; FLMBYTE ucStorageBuf[F_MAX_NUM_BUF + 1]; FLMUINT uiStorageLen; if (!pNode) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } uiStorageLen = sizeof( ucStorageBuf); if( RC_BAD( rc = FlmINT2Storage( iNum, &uiStorageLen, ucStorageBuf))) { goto Exit; } // Determine number of bytes required for BCD number & allocate space if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, uiStorageLen, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memcpy( pucPtr, ucStorageBuf, uiStorageLen); if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutINT64( F_Pool * pPool, NODE * pNode, FLMINT64 i64Num, FLMUINT uiEncId, FLMUINT uiEncSize) { RCODE rc = FERR_OK; FLMBYTE * pucPtr; FLMBYTE ucStorageBuf[F_MAX_NUM64_BUF + 1]; FLMUINT uiStorageLen; if (!pNode) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } uiStorageLen = sizeof( ucStorageBuf); if( RC_BAD( rc = FlmINT64ToStorage( i64Num, &uiStorageLen, ucStorageBuf))) { goto Exit; } // Determine number of bytes required for BCD number & allocate space if ((pucPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_NUMBER_TYPE, uiStorageLen, uiEncId, uiEncSize)) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } f_memcpy( pucPtr, ucStorageBuf, uiStorageLen); if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetINT( NODE * pNode, FLMINT * piNum) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2INT( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), piNum))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetINT64( NODE * pNode, FLMINT64 * pi64Num) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2INT64( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), pi64Num))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetINT32( NODE * pNode, FLMINT32 * pi32Num) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2INT32( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *)GedValPtr( pNode), pi32Num))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetINT16( NODE * pNode, FLMINT16 * pi16Num) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), &uiNum, &bNegFlag))) { if (bNegFlag) { // We will have checked to make sure we are not less than // -(FLM_MAX_INT + 1), but this is smaller than // than -(FLM_MAX_INT16 + 1), // so we need to check to make sure we are not less than // -(FLM_MAX_INT32 + 1) if (uiNum > (FLMUINT)(FLM_MAX_INT16) + 1) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); goto Exit; } *pi16Num = -((FLMINT16)uiNum); } // If the value is positive, we will have checked to make sure the // number did not overflow FLM_MAX_UINT, but not FLM_MAX_INT16. else if (uiNum > (FLMUINT)(FLM_MAX_INT16)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); goto Exit; } else { *pi16Num = (FLMINT16)uiNum; } } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetUINT( NODE * pNode, FLMUINT * puiNum) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2UINT( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), puiNum))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetUINT8( NODE * pNode, FLMUINT8 * pui8Num) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), &uiNum, &bNegFlag))) { if (bNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else if (uiNum > (FLMUINT)(FLM_MAX_UINT8)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { *pui8Num = (FLMUINT8)uiNum; } } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetUINT64( NODE * pNode, FLMUINT64 * pui64Num) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2UINT64( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), pui64Num))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetUINT32( NODE * pNode, FLMUINT32 * pui32Num) { RCODE rc = FERR_OK; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_BAD( rc = FlmStorage2UINT32( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), pui32Num))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedGetUINT16( NODE * pNode, FLMUINT16 * pui16Num) { RCODE rc = FERR_OK; FLMUINT uiNum; FLMBOOL bNegFlag; if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } if (RC_OK( rc = flmBcd2Num( GedValType( pNode), GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), &uiNum, &bNegFlag))) { if (bNegFlag) { rc = RC_SET( FERR_CONV_NUM_UNDERFLOW); } else if (uiNum > (FLMUINT)(FLM_MAX_UINT16)) { rc = RC_SET( FERR_CONV_NUM_OVERFLOW); } else { *pui16Num = (FLMUINT16)uiNum; } } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedNumToText( const FLMBYTE * num, FLMBYTE * buffer, FLMUINT * bufLenRV) { FLMBYTE * outPtr; FLMBYTE c; FLMBYTE c1 = 0; FLMUINT bytesOutput; FLMUINT outputData; FLMUINT maxOutLen; FLMBYTE done; FLMBYTE firstNibble; FLMBYTE lastChar; const FLMBYTE * pExp = NULL; FLMBYTE parseExponent = 0; FLMBYTE firstNibbleAtExp = 0; FLMBYTE firstDigit = 0; maxOutLen = *bufLenRV; outputData = ((buffer != NULL) && (maxOutLen)); bytesOutput = 0; outPtr = buffer; // Parse through the string outputting data to the buffer // as we go. done = (num == NULL); // Sets to TRUE if NULL else FALSE firstNibble = 1; lastChar = 0xFF; while (!done) { continue_loop: if (firstNibble) // Rather not do a ? : here because { // of the num++ in the : portion c = (FLMBYTE) (*num >> 4); } else { c = (FLMBYTE) (*num++ &0x0F); } firstNibble = !firstNibble; if (c <= 9) // Check common case before switch { if (parseExponent) { // Exponent number? firstDigit++; } c1 = (FLMBYTE) (ASCII_ZERO + c); // Normal decimal value } else { switch (c) { case 0x0A: c1 = ASCII_DOT; break; case 0x0B: c1 = ASCII_DASH; break; case 0x0C: // Ignore for now - imaginary // numbers not implemented c1 = 0; // Set c1 to zero if no output break; case 0x0D: c1 = ASCII_SLASH; break; case 0x0E: // For real numbers the exponent appears first ; // This was done to make it easier for building keys if (!parseExponent) { parseExponent++; // 1=need to output 1st digit pExp = num; // Set state to reparse exponent if (firstNibble) { pExp--; } firstNibbleAtExp = (FLMBYTE) (firstNibble ^ 1); // Parse to the end of the exponent area - most 5 nibbles for (;;) { if (firstNibble) { if ((*num >> 4) == 0x0F) { break; } } else { if ((*num++ &0x0F) == 0x0F) { break; } } firstNibble = !firstNibble; } firstNibble = !firstNibble; // Don't forget this! goto continue_loop; // 'continue' is vauge - use ///* a goto } else { c1 = ASCII_UPPER_E; parseExponent = 0; // Clear flag } break; case 0x0F: c1 = 0; // Set c1 to zero if no output if (!parseExponent) { // Done if no exponent or done /w exp done = TRUE; } break; } } // If we got a character, put into output buffer (or just count) if (c1) { // If the last character was an exponent and the current ; // character is not a minus sign, insert a plus (+) if ((lastChar == ASCII_UPPER_E) && (c1 != ASCII_MINUS)) { if (outputData) { if (bytesOutput < maxOutLen) { *outPtr++ = ASCII_PLUS; } else { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } } bytesOutput++; } if (outputData) { if (bytesOutput < maxOutLen) { *outPtr++ = c1; } else { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } } bytesOutput++; // If exponent (real) number output decimal place if (firstDigit == 1) { firstDigit++; // Set to != 1 if (outputData) { if (bytesOutput < maxOutLen) { *outPtr++ = ASCII_DOT; } else { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } } bytesOutput++; } lastChar = c1; } else if (parseExponent) // Hit last trailing 'F' in num { num = pExp; // Restore state firstNibble = firstNibbleAtExp; // Go again parsing the exponent } } *bufLenRV = bytesOutput; return (FERR_OK); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedTextToNum( FLMBYTE * textStr, // Pointer to buffer containing TEXT FLMUINT textLen, // Length of text (in bytes) FLMBYTE * buffer, // Pointer to buffer where number data is to be // returned FLMUINT * bufLenRV) // Return length -- on input contains buffer size { FLMBYTE * outPtr; FLMBYTE c; FLMUINT bytesProcessed; FLMUINT bytesOutput; FLMUINT outputData; FLMUINT maxOutLen; FLMUINT objType; FLMUINT objLength; FLMBOOL firstNibble; FLMBOOL have1Num; FLMBOOL insideNum; FLMBOOL haveSign; maxOutLen = *bufLenRV; outputData = ((buffer != NULL) && (maxOutLen)); bytesProcessed = bytesOutput = 0; outPtr = buffer; // Parse through the string outputting data to the buffer // as we go. haveSign = have1Num = insideNum = 0; firstNibble = 1; if (textStr == NULL) { textLen = 0; } for (; bytesProcessed < textLen; textStr += objLength, bytesProcessed += objLength) { // Determine what we are pointing at c = *textStr; objType = (FLMBYTE) flmTextObjType( c); if (objType == ASCII_CHAR_CODE) { objLength = 1; if ((c == ASCII_SPACE) || (c == ASCII_TAB) || (c == ASCII_NEWLINE) || (c == ASCII_CR)) { if (insideNum) { have1Num = 1; } break; } // Code below was a break - now skips leading zeros if ((c == ASCII_ZERO) && (!insideNum)) { // Ignore leading zeroes continue; } if ((c >= ASCII_ZERO) && (c <= ASCII_NINE)) { if (!insideNum) { insideNum = 1; haveSign = 1; } c -= ASCII_ZERO; } // Handle sign characters ('+', '-') else if (((c == ASCII_PLUS) || (c == ASCII_MINUS)) && (!haveSign) && (!insideNum)) { haveSign = 1; if (c == ASCII_MINUS) { c = 0x0B; } } else { return (RC_SET( FERR_CONV_BAD_DIGIT)); } if (outputData) { if ((firstNibble) && (bytesOutput == maxOutLen)) { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } if (firstNibble) { c <<= 4; *outPtr = c; } else { *outPtr = (FLMBYTE) (*outPtr + c); outPtr++; } } if (firstNibble) { bytesOutput++; } firstNibble = !firstNibble; } else { switch (objType) { case WHITE_SPACE_CODE: objLength = 1; break; // Skip the unkown codes for now case UNK_GT_255_CODE: objLength = (1 + sizeof(FLMUINT16) + FB2UW( textStr + 1)); break; case UNK_LE_255_CODE: objLength = (2 + (FLMUINT16) * (textStr + 1)); break; case UNK_EQ_1_CODE: objLength = 2; break; case CHAR_SET_CODE: case EXT_CHAR_CODE: case OEM_CODE: case UNICODE_CODE: // Should not hit default. default: return (RC_SET( FERR_CONV_BAD_DIGIT)); } } } // Interpret empty number or all zeroes as single zero if ((!insideNum) && (!have1Num)) { if (outputData) { if ((firstNibble) && (bytesOutput == maxOutLen)) { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } if (firstNibble) { *outPtr = 0x00; } else { outPtr++; } } if (firstNibble) { bytesOutput++; } firstNibble = !firstNibble; } // Add Terminator code to the end of the number if (outputData) { if ((firstNibble) && (bytesOutput == maxOutLen)) { return (RC_SET( FERR_CONV_DEST_OVERFLOW)); } if (firstNibble) { *outPtr = 0xFF; } else { *outPtr = (FLMBYTE) (*outPtr + 0x0F); } } if (firstNibble) { bytesOutput++; } *bufLenRV = bytesOutput; return (FERR_OK); } /***************************************************************************** Desc: *****************************************************************************/ RCODE GedPutUNICODE( F_Pool * pPool, NODE * pNode, const FLMUNICODE * puzString, FLMUINT uiEncId, FLMUINT uiEncSize) { FLMUINT allocLength = 0; FLMBYTE * outPtr; RCODE rc = FERR_OK; // Check for a null node being passed in if (pNode == NULL) { rc = RC_SET( FERR_CONV_NULL_DEST); goto Exit; } // If the string is NULL or empty, call GedAllocSpace with a length of // zero to set the node length to zero and node type to FLM_TEXT_TYPE. if ((puzString == NULL) || (*puzString == 0)) { GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, 0, uiEncId, uiEncSize); return (FERR_OK); } // Two passes are needed on the data. The first pass is to determine // the storage length The second pass is to store the string into // FLAIMs internal text format allocLength = FlmGetUnicodeStorageLength( puzString); if ((outPtr = (FLMBYTE *) GedAllocSpace( pPool, pNode, FLM_TEXT_TYPE, allocLength, uiEncId, uiEncSize)) == NULL) { return (RC_SET( FERR_MEM)); } if (RC_BAD( rc = FlmUnicode2Storage( puzString, &allocLength, outPtr))) { goto Exit; } if (pNode->ui32EncId) { pNode->ui32EncFlags = FLD_HAVE_DECRYPTED_DATA; } Exit: return (rc); } /***************************************************************************** Desc: Get Unicode data from a GEDCOM node. *****************************************************************************/ RCODE GedGetUNICODE( NODE * pNode, FLMUNICODE * uniBuf, FLMUINT * bufLenRV) { RCODE rc = FERR_OK; FLMUINT uiNodeType; // Check for a null node if( !pNode) { rc = RC_SET( FERR_CONV_NULL_SRC); goto Exit; } if (pNode->ui32EncId) { if (!(pNode->ui32EncFlags & FLD_HAVE_DECRYPTED_DATA)) { rc = RC_SET( FERR_FLD_NOT_DECRYPTED); goto Exit; } } // If the node is not a TEXT or a NUMBER node, return an error for now. uiNodeType = GedValType( pNode); if (uiNodeType == FLM_BINARY_TYPE || uiNodeType == FLM_CONTEXT_TYPE) { rc = RC_SET( FERR_CONV_ILLEGAL); goto Exit; } if( RC_BAD( rc = FlmStorage2Unicode( uiNodeType, GedValLen( pNode), (const FLMBYTE *) GedValPtr( pNode), bufLenRV, uniBuf))) { goto Exit; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE gedCreateSourceNode( F_Pool * pPool, FLMUINT uiFieldNum, HFDB hDb, FLMUINT uiContainer, FLMUINT uiRecId, NODE ** ppNode) { NODE * nd; RCODE rc = FERR_OK; if( RC_BAD( rc = pPool->poolCalloc( sizeof(NODE) + sizeof(FLMUINT) + sizeof(FLMUINT) + sizeof(HFDB), (void **)&nd))) { goto Exit; } GedValTypeSet( nd, FLM_CONTEXT_TYPE); GedTagNumSet( nd, uiFieldNum); gedSetRecSource( nd, hDb, uiContainer, uiRecId); *ppNode = nd; Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE expImpInit( IF_FileHdl * pFileHdl, FLMUINT uiFlag, EXP_IMP_INFO * pExpImpInfoRV) { RCODE rc = FERR_OK; f_memset( pExpImpInfoRV, 0, sizeof(EXP_IMP_INFO)); pExpImpInfoRV->pFileHdl = pFileHdl; pExpImpInfoRV->bDictRecords = (uiFlag == EXPIMP_IMPORT_EXPORT_GEDCOM) ? FALSE : TRUE; // Allocate a buffer for reading or writing. pExpImpInfoRV->uiBufSize = (uiFlag == EXPIMP_IMPORT_EXPORT_GEDCOM) ? (FLMUINT) 2048 : (FLMUINT) 32768; for (;;) { if (RC_BAD( rc = f_alloc( pExpImpInfoRV->uiBufSize, &pExpImpInfoRV->pBuf))) { pExpImpInfoRV->uiBufSize -= 512; if (pExpImpInfoRV->uiBufSize < 1024) { pExpImpInfoRV->uiBufSize = 0; goto Exit; } } else { break; } } // If writing, output the header data. If reading, seek past it. if (uiFlag == EXPIMP_EXPORT_DICTIONARY) { // Write out the header data. rc = expWrite( pExpImpInfoRV, FlmBinaryGedHeader, BINARY_GED_HEADER_LEN); } else if (uiFlag == EXPIMP_IMPORT_DICTIONARY) { rc = pFileHdl->seek( (FLMUINT) BINARY_GED_HEADER_LEN, FLM_IO_SEEK_SET, &pExpImpInfoRV->ui64FilePos); } else { rc = expWrite( pExpImpInfoRV, FlmBinaryRecHeader, BINARY_GED_HEADER_LEN); } Exit: if (RC_BAD( rc)) { expImpFree( pExpImpInfoRV); } return (rc); } /***************************************************************************** Desc: *****************************************************************************/ void expImpFree( EXP_IMP_INFO * pExpImpInfo) { if (pExpImpInfo->pBuf) { f_free( &pExpImpInfo->pBuf); } f_memset( pExpImpInfo, 0, sizeof(EXP_IMP_INFO)); } /***************************************************************************** Desc: *****************************************************************************/ RCODE expFlush( EXP_IMP_INFO * pExpImpInfo) { RCODE rc = FERR_OK; FLMUINT uiBytesWritten; if ((pExpImpInfo->uiBufUsed) && (pExpImpInfo->bBufDirty)) { if (RC_BAD( rc = pExpImpInfo->pFileHdl->write( pExpImpInfo->ui64FilePos, pExpImpInfo->uiBufUsed, pExpImpInfo->pBuf, &uiBytesWritten))) { goto Exit; } if (uiBytesWritten < pExpImpInfo->uiBufUsed) { rc = RC_SET( FERR_IO_DISK_FULL); goto Exit; } pExpImpInfo->ui64FilePos += uiBytesWritten; pExpImpInfo->uiCurrBuffOffset = pExpImpInfo->uiBufUsed = 0; pExpImpInfo->bBufDirty = FALSE; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE expImpSeek( EXP_IMP_INFO * pExpImpInfo, FLMUINT uiSeekPos) { RCODE rc = FERR_OK; if ((uiSeekPos >= pExpImpInfo->ui64FilePos) && (uiSeekPos < pExpImpInfo->ui64FilePos + (FLMUINT) pExpImpInfo->uiBufUsed)) { pExpImpInfo->uiCurrBuffOffset = (FLMUINT) (uiSeekPos - pExpImpInfo->ui64FilePos); } else { if (pExpImpInfo->bBufDirty) { if (RC_BAD( rc = expFlush( pExpImpInfo))) { goto Exit; } } pExpImpInfo->ui64FilePos = uiSeekPos; pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE expWrite( EXP_IMP_INFO * pExpImpInfo, const FLMBYTE * pData, FLMUINT uiDataLen) { RCODE rc = FERR_OK; FLMUINT uiCopyLen; while (uiDataLen) { if ((uiCopyLen = (pExpImpInfo->uiBufSize - pExpImpInfo->uiCurrBuffOffset)) > uiDataLen) { uiCopyLen = uiDataLen; } f_memcpy( &pExpImpInfo->pBuf[pExpImpInfo->uiCurrBuffOffset], pData, uiCopyLen); pExpImpInfo->bBufDirty = TRUE; uiDataLen -= uiCopyLen; pData += uiCopyLen; pExpImpInfo->uiCurrBuffOffset += uiCopyLen; if (pExpImpInfo->uiCurrBuffOffset > pExpImpInfo->uiBufUsed) { pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset; } if (pExpImpInfo->uiCurrBuffOffset == pExpImpInfo->uiBufSize) { if (RC_BAD( rc = expFlush( pExpImpInfo))) { goto Exit; } } } Exit: return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE expWriteRec( EXP_IMP_INFO * pExpImpInfo, FlmRecord * pRecord, FLMUINT uiDrn) { RCODE rc = FERR_OK; FLMBYTE TBuf[ 24]; FLMUINT uiLen; FLMUINT uiTagNum; FLMUINT uiInitLevel; FLMBOOL bOutputtingRecInfo; FLMBOOL bRootNode; FLMUINT uiTmpLen; FlmRecord * pRec = NULL; FlmRecord * pRecInfoRec = NULL; void * pvField; if (pExpImpInfo->bDictRecords) { // Create a record for the RECINFO information if ((pRecInfoRec = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } if (RC_BAD( rc = pRecInfoRec->insertLast( 0, FLM_RECINFO_TAG, FLM_NUMBER_TYPE, &pvField))) { goto Exit; } // Add the record's DRN to the RECINFO information. if (RC_BAD( rc = flmAddField( pRecInfoRec, FLM_DRN_TAG, (void *) &uiDrn, 4, FLM_NUMBER_TYPE))) { goto Exit; } bOutputtingRecInfo = TRUE; // Output both the REC_INFO GEDCOM tree and the record's GEDCOM // tree. bRootNode = FALSE; pRec = pRecInfoRec; } else { // Output only the GEDCOM tree. bOutputtingRecInfo = FALSE; bRootNode = TRUE; pRec = pRecord; } for (;;) { // Output each node in the record. pvField = pRec->root(); uiInitLevel = pRec->getLevel( pvField); do { uiTagNum = pRec->getFieldID( pvField); uiLen = pRec->getDataLength( pvField); UW2FBA( (FLMUINT16) uiTagNum, TBuf); UW2FBA( (FLMUINT16) uiLen, &TBuf[2]); TBuf[4] = (FLMBYTE) (pRec->getLevel( pvField) - uiInitLevel); TBuf[5] = (FLMBYTE) (pRec->getDataType( pvField)); // Add on the record source information for the root node. uiTmpLen = 6; if (bRootNode) { UW2FBA( (FLMUINT16) pRec->getContainerID(), &TBuf[14]); UD2FBA( (FLMUINT32)pRec->getID(), &TBuf[16]); uiTmpLen = 20; bRootNode = FALSE; } if (RC_BAD( rc = expWrite( pExpImpInfo, TBuf, uiTmpLen))) { goto Exit; } if (uiLen) { const FLMBYTE * pvData = pRec->getDataPtr( pvField); if (RC_BAD( rc = expWrite( pExpImpInfo, pvData, uiLen))) { goto Exit; } } pvField = pRec->next( pvField); } while (pvField && (pRec->getLevel( pvField) > uiInitLevel)); // Output a zero tag number to indicate end of GEDCOM record. UW2FBA( 0, TBuf); if (RC_BAD( rc = expWrite( pExpImpInfo, TBuf, 2))) { goto Exit; } // Set things up to output the record after the REC_INFO. if (!bOutputtingRecInfo) { break; } bOutputtingRecInfo = FALSE; bRootNode = TRUE; pRec = pRecord; } Exit: if (pRecInfoRec) { pRecInfoRec->Release(); } return (rc); } /***************************************************************************** Desc: *****************************************************************************/ FSTATIC RCODE impRead( EXP_IMP_INFO * pExpImpInfo, // Export/Import information. FLMBYTE * pData, // Buffer where data is to be read into. FLMUINT uiDataLen, // Length of data to be read in. FLMUINT * puiBytesReadRV) // Returns amount of data read in. { RCODE rc = FERR_OK; FLMUINT uiCopyLen; FLMUINT uiBytesRead = 0; while (uiDataLen) { // See if we need to read some more data into the import buffer. if (pExpImpInfo->uiCurrBuffOffset == pExpImpInfo->uiBufUsed) { // If we have a dirty buffer, flush it out first. if (pExpImpInfo->bBufDirty) { if (RC_BAD( rc = expFlush( pExpImpInfo))) { goto Exit; } } else { pExpImpInfo->ui64FilePos += (FLMUINT) pExpImpInfo->uiBufUsed; pExpImpInfo->uiBufUsed = pExpImpInfo->uiCurrBuffOffset = 0; } if (RC_BAD( rc = pExpImpInfo->pFileHdl->read( pExpImpInfo->ui64FilePos, pExpImpInfo->uiBufSize, pExpImpInfo->pBuf, &pExpImpInfo->uiBufUsed))) { if ((rc == FERR_IO_END_OF_FILE) && (pExpImpInfo->uiBufUsed)) { rc = FERR_OK; } else { goto Exit; } } } // Copy from the import buffer to the data buffer. if (( uiCopyLen = ( pExpImpInfo->uiBufUsed - pExpImpInfo->uiCurrBuffOffset ) ) > uiDataLen) { uiCopyLen = uiDataLen; } f_memcpy( pData, &pExpImpInfo->pBuf[pExpImpInfo->uiCurrBuffOffset], uiCopyLen); uiDataLen -= uiCopyLen; uiBytesRead += uiCopyLen; pData += uiCopyLen; pExpImpInfo->uiCurrBuffOffset += uiCopyLen; } Exit: *puiBytesReadRV = uiBytesRead; return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE impReadRec( EXP_IMP_INFO * pExpImpInfo, // Export/Import information. FlmRecord ** ppRecordRV) // Returns record that was read in. { RCODE rc = FERR_OK; FLMBYTE TBuf[24]; FLMUINT uiLen; FLMUINT uiTagNum; FLMUINT uiRecInfoDrn = 0; FLMUINT uiDictID; FLMBOOL bHaveRecInfo = FALSE; FLMBOOL bHaveDictID = FALSE; FLMUINT uiLevel; FLMUINT uiType; FLMBOOL bGettingRecInfo; FLMUINT uiBytesRead; FLMUINT uiTmpLen; FlmRecord * pRecord = NULL; void * pvField; bGettingRecInfo = (pExpImpInfo->bDictRecords) ? TRUE : FALSE; // Read each node in the REC_INFO (if dictionary) and then the record. for (;;) { if (RC_BAD( rc = impRead( pExpImpInfo, TBuf, 2, &uiBytesRead))) { if ((rc == FERR_IO_END_OF_FILE) && (uiBytesRead == 0) && ((!bGettingRecInfo) || (!bHaveRecInfo))) { rc = RC_SET( FERR_END); } goto Exit; } // A tag number of zero means we are at the end of the record. uiTagNum = FB2UW( TBuf); if (!uiTagNum) { if (bGettingRecInfo) { bGettingRecInfo = FALSE; continue; } else { break; } } uiTmpLen = ((!bGettingRecInfo) && (!pRecord)) ? 18 : 4; if (RC_BAD( rc = impRead( pExpImpInfo, TBuf, uiTmpLen, &uiBytesRead))) { goto Exit; } uiLen = FB2UW( TBuf); uiLevel = TBuf[2]; uiType = TBuf[3]; if (!pRecord) { if ((pRecord = f_new FlmRecord) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } pRecord->setContainerID( FB2UW( &TBuf[12])); pRecord->setID( FB2UD( &TBuf[14])); } if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum, uiType, &pvField))) { goto Exit; } if (uiLen) { FLMBYTE * pValue; if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiType, uiLen, 0, 0, 0, &pValue, NULL))) { goto Exit; } if (RC_BAD( rc = impRead( pExpImpInfo, pValue, uiLen, &uiBytesRead))) { goto Exit; } } // Link the node into the tree. if (bGettingRecInfo) { switch (uiTagNum) { case FLM_RECINFO_TAG: { bHaveRecInfo = TRUE; break; } case FLM_DRN_TAG: { if (RC_BAD( rc = pRecord->getUINT( pvField, &uiRecInfoDrn))) { goto Exit; } break; } case FLM_DICT_SEQ_TAG: { if (RC_BAD( rc = pRecord->getUINT( pvField, &uiDictID))) { goto Exit; } bHaveDictID = TRUE; break; } } } } Exit: if (RC_OK( rc)) { *ppRecordRV = pRecord; } else { if (pRecord) { pRecord->Release(); } *ppRecordRV = NULL; } return (rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE impFileIsExpImp( IF_FileHdl * pFileHdl, FLMBOOL * pbFileIsBinaryRV) { RCODE rc = FERR_OK; FLMUINT64 ui64CurrPos; FLMBYTE byHeader[ BINARY_GED_HEADER_LEN]; FLMUINT uiBytesRead; *pbFileIsBinaryRV = FALSE; // Save current position so we can return to it. if (RC_BAD( rc = pFileHdl->seek( 0, FLM_IO_SEEK_CUR, &ui64CurrPos))) { goto Exit; } // Read the file's header information. if (RC_BAD( rc = pFileHdl->read( (FLMUINT) 0, BINARY_GED_HEADER_LEN, byHeader, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { uiBytesRead = 0; rc = FERR_OK; } else { goto Exit; } } if ((uiBytesRead == BINARY_GED_HEADER_LEN) && ((f_memcmp( byHeader, FlmBinaryGedHeader, BINARY_GED_HEADER_LEN) == 0) || (f_memcmp( byHeader, FlmBinaryRecHeader, BINARY_GED_HEADER_LEN) == 0))) { *pbFileIsBinaryRV = TRUE; } // Reset the file position to where it was before. rc = pFileHdl->seek( ui64CurrPos, FLM_IO_SEEK_SET); Exit: return (rc); } /**************************************************************************** Desc: This routine adds a field to a GEDCOM tree ****************************************************************************/ RCODE gedAddField( F_Pool * pPool, NODE * pRecord, FLMUINT uiTagNum, const void * pvData, FLMUINT uiDataLen, FLMUINT uiDataType) { RCODE rc = FERR_OK; NODE * pChildNode; FLMUINT uiNum; if ((pChildNode = GedNodeMake( pPool, uiTagNum, &rc)) == NULL) { goto Exit; } switch( uiDataType) { case FLM_TEXT_TYPE: { rc = GedPutNATIVE( pPool, pChildNode, (const char *)pvData); break; } case FLM_NUMBER_TYPE: { switch (uiDataLen) { case 0: uiNum = (FLMUINT)(*((FLMUINT *)(pvData))); rc = GedPutUINT( pPool, pChildNode, uiNum); break; case 1: uiNum = (FLMUINT)(*((FLMBYTE *)(pvData))); rc = GedPutUINT( pPool, pChildNode, uiNum); break; case 2: uiNum = (FLMUINT)(*((FLMUINT16 *)(pvData))); rc = GedPutUINT( pPool, pChildNode, uiNum); break; case 4: uiNum = (FLMUINT)(*((FLMUINT32 *)(pvData))); rc = GedPutUINT( pPool, pChildNode, uiNum); break; case 8: rc = GedPutUINT64( pPool, pChildNode, *((FLMUINT64 *)(pvData))); break; default: flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } break; } case FLM_BINARY_TYPE: { rc = GedPutBINARY( pPool, pChildNode, pvData, uiDataLen); break; } } if (RC_BAD( rc)) { goto Exit; } GedChildGraft( pRecord, pChildNode, GED_LAST); Exit: return( rc); } libflaim-4.9.966/src/fdbremov.cpp0000644000175000017500000002076010510774540020171 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Delete a database. // Tabs: 3 // // Copyright (c) 2001-2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbremov.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" /******************************************************************************* Desc: Removes a database, including roll-forward log files, if requested. *******************************************************************************/ FLMEXP RCODE FLMAPI FlmDbRemove( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, FLMBOOL bRemoveRflFiles) { RCODE rc = FERR_OK; IF_FileHdl * pFileHdl = NULL; FLMBYTE * pucBuffer = NULL; FLMUINT uiFileNumber; FILE_HDR FileHdr; FLMUINT uiVersionNum; char * pszTmpName = NULL; char * pszRflDirName; char * pszDataName; char * pszBaseName; char szPrefix[ F_FILENAME_SIZE]; char * pszExt; char * pszDataExt; IF_DirHdl * pDirHdl = NULL; // Cannot handle empty database name. if( !pszDbName || !(*pszDbName)) { rc = RC_SET( FERR_IO_INVALID_PATH); goto Exit; } // Allocate memory, so as to not consume stack. if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 3 + F_FILENAME_SIZE, &pszTmpName))) { goto Exit; } pszRflDirName = pszTmpName + F_PATH_MAX_SIZE; pszDataName = pszRflDirName + F_PATH_MAX_SIZE; pszBaseName = pszDataName + F_PATH_MAX_SIZE; // First make sure we have closed this database and gotten rid of // it from our internal memory tables - in case it had been open. if (RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE, (void *)pszDbName, (void *)pszDataDir))) { goto Exit; } gv_FlmSysData.pFileHdlCache->closeUnusedFiles(); // Open the file so we can get the log header. if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile( pszDbName, gv_FlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } // Allocate a buffer for reading the header stuff. if( RC_BAD( rc = f_allocAlignedBuffer( 2048, &pucBuffer))) { goto Exit; } // Read the header to get the low and high RFL log // file numbers. if (RC_BAD( flmReadAndVerifyHdrInfo( NULL, pFileHdl, pucBuffer, &FileHdr, NULL, NULL))) { uiVersionNum = FLM_CUR_FILE_FORMAT_VER_NUM; } else { uiVersionNum = FileHdr.uiVersionNum; } // Close the file. pFileHdl->Release(); pFileHdl = NULL; if (pszDataDir && *pszDataDir) { if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( pszDbName, pszDataName, pszBaseName))) { goto Exit; } f_strcpy( pszDataName, pszDataDir); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->pathAppend( pszDataName, pszBaseName))) { goto Exit; } } else { f_strcpy( pszDataName, pszDbName); } f_strcpy( pszTmpName, pszDbName); // Start deleting files, beginning with the main DB file. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszDbName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } // Find where the extension of the database name is pszExt = pszTmpName + f_strlen( pszTmpName) - 1; pszDataExt = pszDataName + f_strlen( pszDataName) - 1; while (pszExt != pszTmpName && *pszExt != '.') { pszExt--; // Both the db name and data name have the same // base name, so we can decrement pszDataExt // at the same time we decrement pszExt. pszDataExt--; } if (*pszExt != '.') { pszExt = pszTmpName + f_strlen( pszTmpName); pszDataExt = pszDataName + f_strlen( pszDataName); } // Delete the .lck file, if any f_strcpy( pszExt, ".lck"); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } // Delete block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiVersionNum, uiFileNumber, pszDataExt); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszDataName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; break; } else { goto Exit; } } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( uiVersionNum)) { break; } uiFileNumber++; } // Delete rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiVersionNum); for (;;) { F_SuperFileClient::bldSuperFileExtension( uiVersionNum, uiFileNumber, pszExt); if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; break; } else { goto Exit; } } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiVersionNum)) { break; } uiFileNumber++; } if (bRemoveRflFiles) { // Delete roll-forward log files. if (uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { // For pre-4.3 versions, only need to delete one RFL file. if (RC_BAD( rc = rflGetFileName( uiVersionNum, pszDbName, pszRflDir, 1, pszTmpName))) { goto Exit; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } } else { FLMBOOL bCanDeleteDir; // For 4.3 and greater, need to scan the RFL directory for // RFL files. if (RC_BAD( rc = rflGetDirAndPrefix( uiVersionNum, pszDbName, pszRflDir, pszRflDirName, szPrefix))) { goto Exit; } // See if the directory exists. If not, we are done. if (!gv_FlmSysData.pFileSystem->isDir( pszRflDirName)) { goto Exit; // Should return FERR_OK } // Open the directory and scan for RFL files. // NOTE: DO NOT just call RemoveDir. There may be other // things in the directory that we do not want to delete. // Look specifically for files that match our expected // name format for RFL files. if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir( pszRflDirName, "*", &pDirHdl))) { goto Exit; } // Assume that we can delete the directory. This will only // be set to FALSE if we can't delete all of the files in // the directory - i.e., some don't look like RFL log files. bCanDeleteDir = TRUE; for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == FERR_IO_NO_MORE_FILES) { rc = FERR_OK; break; } else { goto Exit; } } pDirHdl->currentItemPath( pszTmpName); if (pDirHdl->currentItemIsDir()) { bCanDeleteDir = FALSE; } else if (!rflGetFileNum( uiVersionNum, szPrefix, pszTmpName, &uiFileNumber)) { bCanDeleteDir = FALSE; } else { if( RC_BAD( rc = gv_FlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } else { goto Exit; } } } } // Attempt to delete the directory - if allowed. if (bCanDeleteDir) { // Need to release the directory handle so the // directory will be closed when we try to delete it // below. if (pDirHdl) { pDirHdl->Release(); pDirHdl = NULL; } if (RC_BAD( rc = gv_FlmSysData.pFileSystem->removeDir( pszRflDirName))) { if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) { rc = FERR_OK; } goto Exit; } } } } Exit: if( pszTmpName) { f_free( &pszTmpName); } if( pFileHdl) { pFileHdl->Release(); } if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } if( pDirHdl) { pDirHdl->Release(); } return( rc); } libflaim-4.9.966/src/imonrche.cpp0000644000175000017500000013600710510774540020173 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Class for displaying an RCACHE structure in HTML on a web page. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: imonrche.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" #define GENERIC_SIZE 1024 FSTATIC void flmBuildRCacheLink( char * pszString, RCACHE * pRCache, char * pszURLString); /**************************************************************************** Desc: procedure to display the contents of the RCache Manager ****************************************************************************/ RCODE F_RCacheMgrPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMBOOL bUsage; FLMBOOL bRefresh; FLMBYTE * pszTemp = NULL; if( RC_BAD( rc = f_alloc( 150, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Determine if we are being requested to refresh this page or not and if // we are being asked to show the Usage Statistics. bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); bUsage = DetectParameter( uiNumParams, ppszParams, "Usage"); // Invoke the public function to display the Usage structure. if (bUsage) { RCACHE_MGR LocalRCacheMgr; RCACHE_MGR * pRCacheMgr; f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); f_memcpy( &LocalRCacheMgr, (char *)&gv_FlmSysData.RCacheMgr, sizeof(LocalRCacheMgr)); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); pRCacheMgr = &LocalRCacheMgr; rc = writeUsage( &pRCacheMgr->Usage, bRefresh, "/RCacheMgr?Usage", "RCache Manager Usage Statistics"); goto Exit; } stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); if (bRefresh) { // Send back the page with a refresh command in the header fnPrintf( m_pHRequest, "" "" "gv_FlmSysData.RCacheMgr\n", m_pszURLString); // Add the function to generate a popup framed window only if this is not // showing the Usage statistics. printStyle(); popupFrame(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString); } else { // Add the function to generate a popup framed window if not // displaying the Usage. fnPrintf( m_pHRequest, "gv_FlmSysData.RCacheMgr\n"); printStyle(); popupFrame(); fnPrintf( m_pHRequest, "\n"); // Send back a page without the refresh command fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString); } // Begin the table printTableStart( "RCache Manager", 4, 100); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString); fnPrintf( m_pHRequest, "%s\n", pszTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); write_data(); fnPrintf( m_pHRequest, "\n"); Exit: fnEmit(); if (pszTemp) { f_free( &pszTemp); } return( rc); } /**************************************************************************** Desc: private procedure to generate the HTML page ****************************************************************************/ void F_RCacheMgrPage::write_data( void) { RCODE rc = FERR_OK; char szTemp[100]; RCACHE_MGR LocalRCacheMgr; RCACHE_MGR * pRCacheMgr; FLMBOOL bFlaimLocked = FALSE; RCACHE * pPurgeList = NULL; RCACHE * pMRURecord = NULL; RCACHE * pLRURecord = NULL; char szAddress[20]; char szOffset[8]; FLMBOOL bHighlight = FALSE; // First, get a local copy of the RCacheMgr structure so we don't interfere // with the operation of the database. f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bFlaimLocked = TRUE; f_memcpy(&LocalRCacheMgr, &gv_FlmSysData.RCacheMgr, sizeof(LocalRCacheMgr)); pRCacheMgr = &LocalRCacheMgr; // Make copies of needed records so we can use them later. if (pRCacheMgr->pPurgeList) { if (RC_BAD( rc = f_alloc( sizeof(RCACHE), &pPurgeList))) { goto Exit; } f_memcpy( pPurgeList, pRCacheMgr->pPurgeList, sizeof(RCACHE)); } if (pRCacheMgr->pMRURecord) { if (RC_BAD( rc = f_alloc( sizeof(RCACHE), &pMRURecord))) { goto Exit; } f_memcpy( pMRURecord, pRCacheMgr->pMRURecord, sizeof(RCACHE)); } if (pRCacheMgr->pLRURecord) { if (RC_BAD( rc = f_alloc( sizeof(RCACHE), &pLRURecord))) { goto Exit; } f_memcpy( pLRURecord, pRCacheMgr->pLRURecord, sizeof(RCACHE)); } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); bFlaimLocked = FALSE; // pPurgeList if (pRCacheMgr->pPurgeList) { printAddress( pPurgeList->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pPurgeList->uiContainer, (unsigned long)pPurgeList->uiDrn, szAddress, (unsigned long)pPurgeList->uiLowTransId); } printHTMLLink( "pPurgeList", "RCACHE *", (void *)pRCacheMgr, (void *)&pRCacheMgr->pPurgeList, (void *)pRCacheMgr->pPurgeList, (char*)szTemp, (bHighlight = ~bHighlight)); // pMRURecord if (pRCacheMgr->pMRURecord) { printAddress( pMRURecord->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pMRURecord->uiContainer, (unsigned long)pMRURecord->uiDrn, szAddress, (unsigned long)pMRURecord->uiLowTransId); } printHTMLLink( "pMRURecord", "RCACHE *", (void *)pRCacheMgr, (void *)&pRCacheMgr->pMRURecord, (void *)pRCacheMgr->pMRURecord, (char*)szTemp, (bHighlight = ~bHighlight)); // pLRURecord if (pRCacheMgr->pLRURecord) { printAddress( pRCacheMgr->pLRURecord->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%ld?File=%s?Version=%ld", m_pszURLString, (unsigned long)pLRURecord->uiContainer, (unsigned long)pLRURecord->uiDrn, szAddress, (unsigned long)pLRURecord->uiLowTransId); } printHTMLLink( "pLRURecord", "RCACHE *", (void *)pRCacheMgr, (void *)&pRCacheMgr->pLRURecord, (void *)pRCacheMgr->pLRURecord, (char*)szTemp, (bHighlight = ~bHighlight)); // Usage printTableRowStart( (bHighlight = ~bHighlight)); f_sprintf( (char *)szTemp, "%s/RCacheMgr?Usage", m_pszURLString); printOffset( (void *)pRCacheMgr, (void *)&pRCacheMgr->Usage, szOffset); fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset fnPrintf( m_pHRequest, TD_a_p_s, szTemp, "Usage"); // Link & Name fnPrintf( m_pHRequest, TD_s, "FLM_CACHE_USAGE"); // Type fnPrintf( m_pHRequest, TD_a_p_x, szTemp, (FLMUINT)&pRCacheMgr->Usage); // Link & Value printTableRowEnd(); // ppHashBuckets if (pRCacheMgr->ppHashBuckets) { f_sprintf( (char *)szTemp, "%s/RCHashBucket?Start=0", m_pszURLString); } printHTMLLink( "ppHashBuckets", "RCACHE **", (void *)pRCacheMgr, (void *)&pRCacheMgr->ppHashBuckets, (void *)pRCacheMgr->ppHashBuckets, (char*)szTemp, (bHighlight = ~bHighlight)); // uiNumBuckets printHTMLUint( "uiNumBuckets", "FLMUINT", (void *)pRCacheMgr, (void *)&pRCacheMgr->uiNumBuckets, pRCacheMgr->uiNumBuckets, (bHighlight = ~bHighlight)); // uiHashMask printHTMLUint( "uiHashMask", "FLMUINT", (void *)pRCacheMgr, (void *)&pRCacheMgr->uiHashMask, pRCacheMgr->uiHashMask, (bHighlight = ~bHighlight)); // uiPendingReads printHTMLUint( "uiPendingReads", "FLMUINT", (void *)pRCacheMgr, (void *)&pRCacheMgr->uiPendingReads, pRCacheMgr->uiPendingReads, (bHighlight = ~bHighlight)); // uiIoWaits printHTMLUint( "uiIoWaits", "FLMUINT", (void *)pRCacheMgr, (void *)&pRCacheMgr->uiIoWaits, pRCacheMgr->uiIoWaits, (bHighlight = ~bHighlight)); // hMutex printAddress( &pRCacheMgr->hMutex, szAddress); printHTMLString( "hMutex", "F_MUTEX", (void *)pRCacheMgr, (void *)&pRCacheMgr->hMutex, (char*)szAddress, (bHighlight = ~bHighlight)); #ifdef FLM_DEBUG // bDebug printHTMLString( "bDebug", "F_MUTEX", (void *)pRCacheMgr, (void *)&pRCacheMgr->bDebug, (char *)(pRCacheMgr->bDebug ? "Yes" : "No"), (bHighlight = ~bHighlight)); #endif Exit: printTableEnd(); if (bFlaimLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); bFlaimLocked = FALSE; } if (pPurgeList) { f_free( &pPurgeList); } if (pMRURecord) { f_free( &pMRURecord); } if (pLRURecord) { f_free( &pLRURecord); } } /**************************************************************************** Desc: Implements the RCache display function. *****************************************************************************/ RCODE F_RCachePage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; char szContainer[GENERIC_SIZE]; FLMUINT uiContainer; char szDrn[GENERIC_SIZE]; FLMUINT uiDrn; char szVersion[GENERIC_SIZE]; FLMUINT uiVersion; char szTemp[GENERIC_SIZE]; RCACHE * pRCache = NULL; RCACHE * pOlderRCache; RCACHE * pNewerRCache; char szFile[GENERIC_SIZE]; FFILE * pFile; char szFrom[GENERIC_SIZE]; char szBucket[GENERIC_SIZE]; FLMUINT uiBucket; FLMBOOL bRCLocked = FALSE; FLMBOOL bpFileInc = FALSE; FLMBYTE * pszTemp = NULL; if( RC_BAD( rc = f_alloc( 150, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Check to see if this page is being invoked from the RCHashBucket // Page. If it is, then all we will need is the Bucket and we should // be able to find the RCache block. if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "From", sizeof( szFrom), szFrom))) { if (rc != FERR_NOT_FOUND) { goto Exit; } else { szFrom[0] = '\0'; } } if (f_strcmp( szFrom, "RCHashBucket")==0) { if (RC_BAD(rc = ExtractParameter(uiNumParams, ppszParams, "Bucket", sizeof( szBucket), szBucket))) { goto Exit; } uiBucket = f_atoud( szBucket); // We need to lock the RCache Manager before messing with the RCache records f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bRCLocked = TRUE; if ((pRCache = gv_FlmSysData.RCacheMgr.ppHashBuckets[uiBucket]) == NULL) { goto Exit; } uiContainer = pRCache->uiContainer; uiDrn = pRCache->uiDrn; uiVersion = pRCache->uiLowTransId; pFile = pRCache->pFile; pRCache = NULL; // We will retrieve this again later. f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); bRCLocked = FALSE; } else { // Container tag if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Container", sizeof( szContainer), szContainer))) { goto Exit; } uiContainer = f_atoud( szContainer); // DRN tag if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "DRN", sizeof( szDrn), szDrn))) { goto Exit; } uiDrn = f_atoud( szDrn); // File tag if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "File", sizeof( szFile), szFile))) { goto Exit; } pFile = (FFILE *)f_atoud( szFile); // Version tag if (RC_BAD(rc = ExtractParameter( uiNumParams, ppszParams, "Version", sizeof( szVersion), szVersion))) { goto Exit; } uiVersion = f_atoud( szVersion); } stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); // Determine if we are being requested to refresh this page or not. if (DetectParameter( uiNumParams, ppszParams, "Refresh")) { // Send back the page with a refresh command in the header f_sprintf((char *)szTemp, "%s/RCache?Refresh?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); fnPrintf( m_pHRequest, "" "" "RCache\n", szTemp); printStyle(); fnPrintf( m_pHRequest, "\n"); f_sprintf((char *)szTemp, "%s/RCache?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Stop Auto-refresh", szTemp); } else { fnPrintf( m_pHRequest, "RCache\n"); printStyle(); fnPrintf( m_pHRequest, "\n"); f_sprintf((char *)szTemp, "%s/RCache?Refresh?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); fnPrintf( m_pHRequest, "\n"); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", szTemp); } // Prepare the Refresh link. f_sprintf((char *)szTemp, "%s/RCache?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); // Need to lock the record Cache mutex first... f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); bRCLocked = TRUE; flmRcaFindRec( pFile, F_SEM_NULL, uiContainer, uiDrn, uiVersion, TRUE, 0, &pRCache, &pNewerRCache, &pOlderRCache); // We want to hold the RCache and pFile in memory while we render this page. if (pRCache) { RCA_INCR_USE_COUNT( pRCache->uiFlags); if (++pRCache->pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pFile); } bpFileInc = TRUE; } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); bRCLocked = FALSE; if (pRCache) { // Begin the table printTableStart( "RCache", 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", szTemp); fnPrintf( m_pHRequest, "%s\n", pszTemp); printColumnHeadingClose(); printTableRowEnd(); // Write out the table headings printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); write_data(pRCache); } else { // Return an error page fnPrintf( m_pHRequest, "

Unable to find the RCache structure that you requested." " This is probably because the state of the cache changed " "between the time that you displayed the previous page and the time " "that you clicked on the link that brought you here.\n" "

Click on your browser's \"Back\" button, then click \"Reload\" " "and then try the link again.

\n"); } fnPrintf( m_pHRequest, "\n"); fnEmit(); // Now decrement the use count on the pFile and on the RCache if (pRCache) { if (bpFileInc) { if (--pRCache->pFile->uiUseCount == 0) { flmLinkFileToNUList( pRCache->pFile); } bpFileInc = FALSE; } RCA_DECR_USE_COUNT( pRCache->uiFlags); } Exit: if ( bRCLocked) { f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); } if (pszTemp) { f_free( &pszTemp); } return( rc); } /**************************************************************************** Desc: This is procedure for generating HTML content for an RCACHE structure ****************************************************************************/ void F_RCachePage::write_data( RCACHE * pRCache) { char szTemp[GENERIC_SIZE]; char szAddress[20]; FLMBOOL bHighlight = FALSE; F_UNREFERENCED_PARM( fnPrintf); if (!pRCache) { flmAssert(0); return; } else { // pRecord if (pRCache->pRecord) { printAddress( pRCache->pFile, szAddress); f_sprintf((char *)szTemp, "%s/Record?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->uiContainer, (unsigned long)pRCache->uiDrn, szAddress, (unsigned long)pRCache->uiLowTransId); } printHTMLLink( "pRecord", "FlmRecord *", (void *)pRCache, (void *)&pRCache->pRecord, (void *)pRCache->pRecord, (char*)szTemp, (bHighlight = ~bHighlight)); // pFile if (pRCache->pFile) { printAddress( pRCache->pFile, szAddress); f_sprintf((char *)szTemp, "%s/FFile?From=RCache?Bucket=%lu?Address=%s", m_pszURLString, (unsigned long)pRCache->pFile->uiBucket, szAddress); } printHTMLLink( "pFile", "FFILE *", (void *)pRCache, (void *)&pRCache->pFile, (void *)pRCache->pFile, (char*)szTemp, (bHighlight = ~bHighlight)); // uiContainer printHTMLUint( "uiContainer", "FLMUINT", (void *)pRCache, (void *)&pRCache->uiContainer, pRCache->uiContainer, (bHighlight = ~bHighlight)); // uiDrn printHTMLUint( "uiDrn", "FLMUINT", (void *)pRCache, (void *)&pRCache->uiDrn, pRCache->uiDrn, (bHighlight = ~bHighlight)); // uiLowTransId printHTMLUint( "uiLowTransId", "FLMUINT", (void *)pRCache, (void *)&pRCache->uiLowTransId, pRCache->uiLowTransId, (bHighlight = ~bHighlight)); // uiHighTransId printHTMLUint( "uiHighTransId", "FLMUINT", (void *)pRCache, (void *)&pRCache->uiHighTransId, pRCache->uiHighTransId, (bHighlight = ~bHighlight)); // pNextInBucket if (pRCache->pNextInBucket) { printAddress( pRCache->pNextInBucket->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pNextInBucket->uiContainer, (unsigned long)pRCache->pNextInBucket->uiDrn, szAddress, (unsigned long)pRCache->pNextInBucket->uiLowTransId); } printHTMLLink( "pNextInBucket", "RCACHE *", (void *)pRCache, (void *)&pRCache->pNextInBucket, (void *)pRCache->pNextInBucket, (char*)szTemp, (bHighlight = ~bHighlight)); // pPrevInBucket if (pRCache->pPrevInBucket) { printAddress( pRCache->pPrevInBucket->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pPrevInBucket->uiContainer, (unsigned long)pRCache->pPrevInBucket->uiDrn, szAddress, (unsigned long)pRCache->pPrevInBucket->uiLowTransId); } printHTMLLink( "pPrevInBucket", "RCACHE *", (void *)pRCache, (void *)&pRCache->pPrevInBucket, (void *)pRCache->pPrevInBucket, (char*)szTemp, (bHighlight = ~bHighlight)); // pNextInFile if (pRCache->pNextInFile) { printAddress( pRCache->pNextInFile->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pNextInFile->uiContainer, (unsigned long)pRCache->pNextInFile->uiDrn, szAddress, (unsigned long)pRCache->pNextInFile->uiLowTransId); } printHTMLLink( "pNextInFile", "RCACHE *", (void *)pRCache, (void *)&pRCache->pNextInFile, (void *)pRCache->pNextInFile, (char*)szTemp, (bHighlight = ~bHighlight)); // pPrevInFile if (pRCache->pPrevInFile) { printAddress( pRCache->pPrevInFile->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pPrevInFile->uiContainer, (unsigned long)pRCache->pPrevInFile->uiDrn, szAddress, (unsigned long)pRCache->pPrevInFile->uiLowTransId); } printHTMLLink( "pPrevInFile", "RCACHE *", (void *)pRCache, (void *)&pRCache->pPrevInFile, (void *)pRCache->pPrevInFile, (char*)szTemp, (bHighlight = ~bHighlight)); // pNextInGlobal if (pRCache->pNextInGlobal) { printAddress( pRCache->pNextInGlobal->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pNextInGlobal->uiContainer, (unsigned long)pRCache->pNextInGlobal->uiDrn, szAddress, (unsigned long)pRCache->pNextInGlobal->uiLowTransId); } printHTMLLink( "pNextInGlobal", "RCACHE *", (void *)pRCache, (void *)&pRCache->pNextInGlobal, (void *)pRCache->pNextInGlobal, (char*)szTemp, (bHighlight = ~bHighlight)); // pPrevInGlobal if (pRCache->pPrevInGlobal) { printAddress( pRCache->pPrevInGlobal->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pPrevInGlobal->uiContainer, (unsigned long)pRCache->pPrevInGlobal->uiDrn, szAddress, (unsigned long)pRCache->pPrevInGlobal->uiLowTransId); } printHTMLLink( "pPrevInGlobal", "RCACHE *", (void *)pRCache, (void *)&pRCache->pPrevInGlobal, (void *)pRCache->pPrevInGlobal, (char*)szTemp, (bHighlight = ~bHighlight)); // pOlderVersion if (pRCache->pOlderVersion) { printAddress( pRCache->pOlderVersion->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pOlderVersion->uiContainer, (unsigned long)pRCache->pOlderVersion->uiDrn, szAddress, (unsigned long)pRCache->pOlderVersion->uiLowTransId); } printHTMLLink( "pOlderVersion", "RCACHE *", (void *)pRCache, (void *)&pRCache->pOlderVersion, (void *)pRCache->pOlderVersion, (char*)szTemp, (bHighlight = ~bHighlight)); // pNewerVersion if (pRCache->pNewerVersion) { printAddress( pRCache->pNewerVersion->pFile, szAddress); f_sprintf((char *)szTemp, "%s/RCache?Container=%lu?DRN=%lu?File=%s?Version=%lu", m_pszURLString, (unsigned long)pRCache->pNewerVersion->uiContainer, (unsigned long)pRCache->pNewerVersion->uiDrn, szAddress, (unsigned long)pRCache->pNewerVersion->uiLowTransId); } printHTMLLink( "pNewerVersion", "RCACHE *", (void *)pRCache, (void *)&pRCache->pNewerVersion, (void *)pRCache->pNewerVersion, (char*)szTemp, (bHighlight = ~bHighlight)); // pNotifyList if (pRCache->pNotifyList) { printAddress( pRCache->pNotifyList, szAddress); f_sprintf((char *)szTemp, "%s/FNOTIFY?From=RCache?Address=%s", m_pszURLString, szAddress); } printHTMLLink( "pNotifyList", "FNOTIFY *", (void *)pRCache, (void *)&pRCache->pNotifyList, (void *)pRCache->pNotifyList, (char*)szTemp, (bHighlight = ~bHighlight)); // uiFlags printHTMLUint( "uiFlags", "FLMUINT", (void *)pRCache, (void *)&pRCache->uiFlags, pRCache->uiFlags, (bHighlight = ~bHighlight)); printTableEnd(); } } /**************************************************************************** Desc: Implements the Record display function. *****************************************************************************/ RCODE F_RecordPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; char szContainer[GENERIC_SIZE]; FLMUINT uiContainer; char szDrn[GENERIC_SIZE]; FLMUINT uiDrn; char szVersion[GENERIC_SIZE]; FLMUINT uiVersion; char szTemp[GENERIC_SIZE]; RCACHE * pRCache = NULL; RCACHE * pOlderRCache; RCACHE * pNewerRCache; char szFile[GENERIC_SIZE]; FFILE * pFile; FlmRecord * pRecord = NULL; FLMBOOL bpFileInc = FALSE; FLMBYTE * pszTemp = NULL; if( RC_BAD( rc = f_alloc( 150, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Container tag if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Container", sizeof( szContainer), szContainer))) { goto Exit; } uiContainer = f_atoud( szContainer); // DRN tag if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "DRN", sizeof( szDrn), szDrn))) { goto Exit; } uiDrn = f_atoud( szDrn); // File tag if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "File", sizeof( szFile), szFile))) { goto Exit; } pFile = (FFILE *)f_atoud( szFile); // Version tag if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Version", sizeof( szVersion), szVersion))) { goto Exit; } uiVersion = f_atoud( szVersion); stdHdr(); fnPrintf( m_pHRequest, HTML_DOCTYPE); fnPrintf( m_pHRequest, "\n"); // Determine if we are being requested to refresh this page or not. if (DetectParameter( uiNumParams, ppszParams, "Refresh")) { // Send back the page with a refresh command in the header f_sprintf((char *)szTemp, "%s/Record?Refresh?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); fnPrintf( m_pHRequest, "" "Database iMonitor - gv_FlmSysData\n", szTemp); printRecordStyle(); printStyle(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); f_sprintf((char *)szTemp, "%s/Record?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); f_sprintf( (char *)pszTemp, "Stop Auto-refresh", szTemp); } else { fnPrintf( m_pHRequest, "Database iMonitor - gv_FlmSysData\n"); printRecordStyle(); printStyle(); fnPrintf( m_pHRequest, "\n"); fnPrintf( m_pHRequest, "\n"); f_sprintf((char *)szTemp, "%s/Record?Refresh?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", szTemp); } // Prepare the refresh link. f_sprintf((char *)szTemp, "%s/Record?Container=%s?DRN=%s?File=%s?Version=%s", m_pszURLString, szContainer, szDrn, szFile, szVersion); // Need to lock the record Cache mutex first... f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); flmRcaFindRec( pFile, F_SEM_NULL, uiContainer, uiDrn, uiVersion, TRUE, 0, &pRCache, &pNewerRCache, &pOlderRCache); if (pRCache) { // Keep the record in memory until we finish rendering this page RCA_INCR_USE_COUNT( pRCache->uiFlags); if (++pRCache->pFile->uiUseCount == 1) { flmUnlinkFileFromNUList( pRCache->pFile); } bpFileInc = TRUE; pRecord = pRCache->pRecord; } f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); printTableStart( "DB Record", 1, 100); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 1, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, ", szTemp); fnPrintf( m_pHRequest, "%s\n", pszTemp); printColumnHeadingClose(); printTableRowEnd(); printTableEnd(); write_links( pRCache); write_data( pRecord, pRCache); fnPrintf( m_pHRequest, "\n"); fnEmit(); Exit: if (pRCache) { f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); // Decrement the use count on the record if (bpFileInc) { if (--pRCache->pFile->uiUseCount == 0) { flmLinkFileToNUList( pRCache->pFile); } } RCA_DECR_USE_COUNT( pRCache->uiFlags); f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); } if (pszTemp) { f_free( &pszTemp); } return( rc); } /**************************************************************************** Desc: This is a procedure for generating HTML content to display links to next and previous records from the Record page, rather than having to backup to the RCache page first. ****************************************************************************/ void F_RecordPage::write_links( RCACHE * pRCache) { FLMUINT uiContainer; FLMUINT uiDrn; FLMUINT uiVersion; void * pFile; RCACHE * pTmpRCache; char szAddress[20]; // Do nothing if there is no RCache to report on. if (!pRCache) { return; } // Begin the table printTableStart( "DB Record - Links", 8, 100); printTableRowStart(); // pNextInBucket if (pRCache->pNextInBucket) { pTmpRCache = pRCache->pNextInBucket; // Extract the container etc. uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pNextInBucket\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pNextInBucket\n"); } // pPrevInBucket if (pRCache->pPrevInBucket) { pTmpRCache = pRCache->pPrevInBucket; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pPrevInBucket\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pPrevInBucket\n"); } // pNextInFile if (pRCache->pNextInFile) { pTmpRCache = pRCache->pNextInFile; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pNextInFile\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pNextInFile\n"); } // pPrevInFile if (pRCache->pPrevInFile) { pTmpRCache = pRCache->pPrevInFile; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pPrevInFile\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pPrevInFile\n"); } // pNextInGlobal if (pRCache->pNextInGlobal) { pTmpRCache = pRCache->pNextInGlobal; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pNextInGlobal\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pNextInGlobal\n"); } // pPrevInGlobal if (pRCache->pPrevInGlobal) { pTmpRCache = pRCache->pPrevInGlobal; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pPrevInGlobal\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pPrevInGlobal\n"); } // pOlderVersion if (pRCache->pOlderVersion) { pTmpRCache = pRCache->pOlderVersion; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pOlderVersion\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pOlderVersion\n"); } // pNewerVersion if (pRCache->pNewerVersion) { pTmpRCache = pRCache->pNewerVersion; uiContainer = pTmpRCache->uiContainer; uiDrn = pTmpRCache->uiDrn; pFile = (void *)pTmpRCache->pFile; uiVersion = pTmpRCache->uiLowTransId; printAddress( pFile, szAddress); fnPrintf( m_pHRequest, "" "" "pNewerVersion\n", m_pszURLString, (unsigned long)uiContainer, (unsigned long)uiDrn, szAddress, (unsigned long)uiVersion); } else { fnPrintf( m_pHRequest, "pNewerVersion\n"); } printTableRowEnd(); printTableEnd(); } /**************************************************************************** Desc: This is a procedure for generating HTML content to display the results of querying the various methods in a FlmRecord ****************************************************************************/ void F_RecordPage::write_data( FlmRecord * pRecord, RCACHE * pRCache) { FLMBOOL bHighlight = FALSE; // Do we have a valid reference to an record? if (!pRecord) { // Return an error page fnPrintf( m_pHRequest, "

Unable to find the Record that you requested." " This is probably because the state of the cache changed " "between the time that you displayed the previous page and the time " "that you clicked on the link that brought you here.\n" "

Click on your browser's \"Back\" button, then click \"Reload\" " "and then try the link again.\n"); } else { // Begin the table printTableStart( "DB Record - Methods", 2, 100); // Write out the table headings printTableRowStart(); printColumnHeading( "Method Name"); printColumnHeading( "Value"); printTableRowEnd(); // getID() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "getID"); fnPrintf( m_pHRequest, TD_ui, pRecord->getID()); printTableRowEnd(); // getContainerID() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "getContainerID"); fnPrintf( m_pHRequest, TD_ui, pRecord->getContainerID()); printTableRowEnd(); // isReadOnly() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "isReadOnly"); fnPrintf( m_pHRequest, TD_s, pRecord->isReadOnly() ? "Yes" : "No"); printTableRowEnd(); // getTotalMemory() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "getTotalMemory"); fnPrintf( m_pHRequest, TD_ui, pRecord->getTotalMemory()); printTableRowEnd(); // getFreeMemory() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "getFreeMemory"); fnPrintf( m_pHRequest, TD_ui, pRecord->getFreeMemory()); printTableRowEnd(); // getRefCount() printTableRowStart( bHighlight = ~bHighlight); fnPrintf( m_pHRequest, TD_s, "getRefCount"); fnPrintf( m_pHRequest, TD_ui, pRecord->getRefCount()); printTableRowEnd(); // End the table printTableEnd(); // Begin a new table to display the fields printTableStart( "DB Record - Fields", 4); // Write out the new table headings printTableRowStart(); printColumnHeading( "Byte Offset (hex)"); printColumnHeading( "Field Name"); printColumnHeading( "Field Type"); printColumnHeading( "Value"); printTableRowEnd(); // End the table printTableEnd(); // At this point, we will extract the various fields and display each // one according to the structure of the record. printRecordFields( pRecord, pRCache); } } /**************************************************************************** Desc: This is a procedure for generating HTML content to display the fields of a FlmRecord. ****************************************************************************/ void F_RecordPage::printRecordFields( FlmRecord * pRecord, RCACHE * pRCache) { RCODE rc = FERR_OK; F_NameTable * pNameTable = NULL; FLMUINT uiContext = 0; // Return if we have nothing to display. if( !pRecord) { goto Exit; } if (!m_pFlmSession) { fnPrintf( m_pHRequest, "

Cannot display record data. No session object available. " "Return Code = 0x%04X (%s)
\n", m_uiSessionRC, FlmErrorString( m_uiSessionRC)); goto Exit; } if (RC_BAD( rc = m_pFlmSession->getNameTable( pRCache->pFile, &pNameTable))) { fnPrintf( m_pHRequest, "
Cannot display record data. Could not get a Name Table." "Return Code = 0x%04X (%s)
\n", m_uiSessionRC, FlmErrorString( m_uiSessionRC)); goto Exit; } // We will call this when the code is ready... printRecord( NULL, pRecord, pNameTable, &uiContext, TRUE); Exit: return; } /**************************************************************************** Desc: Prints the web page for the RCHashBucket ****************************************************************************/ RCODE F_RCHashBucketPage::display( FLMUINT uiNumParams, const char ** ppszParams) { RCODE rc = FERR_OK; FLMBOOL bRefresh; FLMBOOL bHighlight = TRUE; FLMUINT uiLoop; FLMUINT uiHashTableSize; FLMUINT uiUsedEntries = 0; char szStart[10]; char szRefresh[] = "&Refresh"; FLMUINT uiStart; FLMUINT uiNewStart; FLMBYTE * pszTemp; FLMBOOL bNextUsed; FLMUINT uiNextStart; // We display 20 hash table entries at a time, some of which might need // to be hyperlinked. #define NUM_ENTRIES 20 FLMBYTE * pszHTLinks[NUM_ENTRIES]; // Check for the refresh parameter bRefresh = DetectParameter( uiNumParams, ppszParams, "Refresh"); if (!bRefresh) { szRefresh[0]='\0'; // Effectively turns szRefresh into a null string } // Get the starting entry number... if (RC_BAD( rc = ExtractParameter( uiNumParams, ppszParams, "Start", sizeof( szStart), szStart))) { flmAssert( 0); goto Exit; } uiStart = f_atoud( szStart); // Allocate space for the hyperlink text for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { if( RC_BAD( rc = f_alloc( 250, &pszHTLinks[ uiLoop]))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } pszHTLinks[uiLoop][0] = '\0'; } if( RC_BAD( rc = f_alloc( 250, &pszTemp))) { printErrorPage( rc, TRUE, (char *)"Failed to allocate temporary buffer"); goto Exit; } // Find out if we want to go to the next or previous used entry. bNextUsed = DetectParameter( uiNumParams, ppszParams, "NextUsed"); // Lock the database f_mutexLock( gv_FlmSysData.hShareMutex); f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); // Get the number of entries in the hash table uiHashTableSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; // May need to modify starting number if it's out of range... if ((uiStart + NUM_ENTRIES) >= uiHashTableSize) { uiStart = uiHashTableSize - NUM_ENTRIES; } if (bNextUsed) { uiNextStart = uiStart + NUM_ENTRIES; if ((uiNextStart + NUM_ENTRIES) >= uiHashTableSize) { uiNextStart = uiHashTableSize - NUM_ENTRIES; } } // We need to find out if there are any used entries after our uiNextStart // index into the hash table. To do this, we will need to scan the table // first, just to find out if there are any used entries... if (bNextUsed) { for (uiLoop = 0; uiLoop < uiHashTableSize; uiLoop++) { if (gv_FlmSysData.RCacheMgr.ppHashBuckets[ uiLoop]) { if ( uiLoop >= uiNextStart) { uiStart = uiLoop - (uiLoop % NUM_ENTRIES); break; // No need to go further } } } } // Loop through the entire table counting the number of entries in use // If the entry is one of the one's we're going to display, store the // appropriate text in pszHTLinks for (uiLoop = 0; uiLoop < uiHashTableSize; uiLoop++) { if (gv_FlmSysData.RCacheMgr.ppHashBuckets[ uiLoop]) { uiUsedEntries++; } if ( (uiLoop >= uiStart) && (uiLoop < (uiStart + NUM_ENTRIES)) ) { // This is one of the entries that we will display if (gv_FlmSysData.RCacheMgr.ppHashBuckets[ uiLoop]) { flmBuildRCacheLink( (char *)pszHTLinks[uiLoop - uiStart], gv_FlmSysData.RCacheMgr.ppHashBuckets[ uiLoop], m_pszURLString); } } } // Unlock the database f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); f_mutexUnlock( gv_FlmSysData.hShareMutex); // Begin rendering the page... stdHdr(); printStyle(); fnPrintf( m_pHRequest, HTML_DOCTYPE "\n"); // Determine if we are being requested to refresh this page or not. if (bRefresh) { fnPrintf( m_pHRequest, "" "" "Database iMonitor - RCache Hash Bucket\n", m_pszURLString, uiStart, szRefresh); } else { fnPrintf( m_pHRequest, "\n"); } // If we are not to refresh this page, then don't include the // refresh meta command if (!bRefresh) { f_sprintf( (char *)pszTemp, "Start Auto-refresh (5 sec.)", m_pszURLString, uiStart); } else { f_sprintf( (char *)pszTemp, "Stop Auto-refresh", m_pszURLString, uiStart); } // Print out a formal header and the refresh option. printTableStart("RCache Hash Bucket", 4); printTableRowStart(); printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); fnPrintf( m_pHRequest, "Refresh, %s\n", m_pszURLString, uiStart, szRefresh, pszTemp); printColumnHeadingClose(); printTableRowEnd(); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, "Table Size: %lu \n", uiHashTableSize); printTableRowEnd(); printTableRowStart( (bHighlight = !bHighlight)); fnPrintf( m_pHRequest, "Entries Used: %lu (%lu%%) \n", uiUsedEntries, ((uiUsedEntries * 100) / uiHashTableSize) ); printTableRowEnd(); // The rest of the table is going to be a single row with two columns: // one for the list of hash buckets and the other for everything else printTableRowStart( FALSE); fnPrintf( m_pHRequest, " \n"); // Print out the hash buckets for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { if (pszHTLinks[uiLoop][0] != '\0') { fnPrintf( m_pHRequest, "%lu
\n", pszHTLinks[uiLoop], szRefresh, uiStart+uiLoop); } else { fnPrintf( m_pHRequest, "%lu
\n", uiStart+uiLoop); } } fnPrintf( m_pHRequest, "\n\n\n"); // Print out the other stuff... uiNewStart = (uiStart > 1000)?(uiStart - 1000):0; fnPrintf( m_pHRequest, "Previous 1000
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart > 100)?(uiStart - 100):0; fnPrintf( m_pHRequest, "Previous 100
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart > 10)?(uiStart - 10):0; fnPrintf( m_pHRequest, "Previous 10
\n", m_pszURLString, uiNewStart, szRefresh); fnPrintf( m_pHRequest, "
\n"); uiNewStart = (uiStart + 10); if (uiNewStart >= (uiHashTableSize - NUM_ENTRIES)) { uiNewStart = (uiHashTableSize - NUM_ENTRIES); } fnPrintf( m_pHRequest, "Next 10
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart + 100); if (uiNewStart >= (uiHashTableSize - NUM_ENTRIES)) { uiNewStart = (uiHashTableSize - NUM_ENTRIES); } fnPrintf( m_pHRequest, "Next 100
\n", m_pszURLString, uiNewStart, szRefresh); uiNewStart = (uiStart + 1000); if (uiNewStart >= (uiHashTableSize - NUM_ENTRIES)) { uiNewStart = (uiHashTableSize - NUM_ENTRIES); } fnPrintf( m_pHRequest, "Next 1000
\n" "Next Used Bucket
\n" "
\n" "
Jump to specific bucket:
\n" "
\n", m_pszURLString, uiNewStart, szRefresh, m_pszURLString, uiStart, szRefresh, m_pszURLString); printButton( "Jump", BT_Submit); fnPrintf( m_pHRequest, "
\n"); // We use a hidden field to pass the refresh parameter back the the server if (bRefresh) { fnPrintf( m_pHRequest, "\n"); } fnPrintf( m_pHRequest, "
\n\n"); printTableRowEnd(); printTableEnd(); printDocEnd(); fnEmit(); Exit: // Free the space for the hyperlink text for (uiLoop = 0; uiLoop < NUM_ENTRIES; uiLoop++) { f_free( &pszHTLinks[uiLoop]); } f_free( &pszTemp); return( rc); } /**************************************************************************** Desc: Determines the values of the parameters needed to reference a specific RCache structure. Must be called from within a mutex ****************************************************************************/ FSTATIC void flmBuildRCacheLink( char * pszString, RCACHE * pRCache, char * pszURLString) { char szAddress[20]; if (pRCache == NULL) { pszString[0] = 0; } else { printAddress( pRCache->pFile, szAddress); f_sprintf((char *)pszString, "%s/RCache?Container=%lu&DRN=%lu&File=%s&Version=%lu", pszURLString, (unsigned long)pRCache->uiContainer, (unsigned long)pRCache->uiDrn, szAddress, (unsigned long)pRCache->uiLowTransId); } return; } libflaim-4.9.966/src/recover.cpp0000644000175000017500000003370410510774540020034 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Recover database after a failure. // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: recover.cpp 12315 2006-01-19 15:16:37 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmReadLog( FDB * pDb, FLMUINT uiLogEOF, FLMUINT * puiCurrAddrRV, FLMBYTE * pBuf, FLMBOOL * pbIsBeforeImageBlkRV); FSTATIC RCODE flmProcessBeforeImage( FDB * pDb, FLMUINT uiLogEOF, FLMUINT * puiCurrAddrRV, FLMBYTE * pBuf, FLMBOOL bDoingRecovery, FLMUINT uiMaxTransID); /**************************************************************************** Desc: This routine reads the next before-image block from the file. ****************************************************************************/ FSTATIC RCODE flmReadLog( FDB * pDb, FLMUINT uiLogEOF, // Address of end of rollback log FLMUINT * puiCurrAddrRV, // This is the current address we are // reading in the log file. It // will be updated after reading the // data FLMBYTE * pBlk, // This is the buffer that is to hold // the data that is read from the // log file FLMBOOL * pbIsBeforeImageBlkRV // Is block a before-image block? ) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiFilePos; FLMUINT uiBlkSize = pFile->FileHdr.uiBlockSize; FLMUINT uiBytesRead; F_TMSTAMP StartTime; DB_STATS * pDbStats = pDb->pDbStats; uiFilePos = *puiCurrAddrRV; // Verify that we are not going to read beyond the log EOF if (!FSAddrIsAtOrBelow( uiFilePos + uiBlkSize, uiLogEOF)) { rc = RC_SET( FERR_INCOMPLETE_LOG); goto Exit; } // Position to the appropriate place and read the data if (pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->LogBlockReads.ui64Count++; pDbStats->LogBlockReads.ui64TotalBytes += uiBlkSize; f_timeGetTimeStamp( &StartTime); } if( RC_BAD( rc = pDb->pSFileHdl->readBlock( uiFilePos, uiBlkSize, pBlk, &uiBytesRead))) { if (rc == FERR_IO_END_OF_FILE) { rc = RC_SET( FERR_INCOMPLETE_LOG); } if (pDbStats) { pDbStats->uiReadErrors++; } goto Exit; } if (pDbStats) { flmAddElapTime( &StartTime, &pDbStats->LogBlockReads.ui64ElapMilli); } if (uiBytesRead != uiBlkSize) { if (pDbStats) { pDbStats->uiLogBlockChkErrs++; } rc = RC_SET( FERR_DATA_ERROR); goto Exit; } // Verify the checksum on the block if( RC_BAD( rc = BlkCheckSum( pBlk, CHECKSUM_CHECK, BT_END, uiBlkSize))) { if (pDbStats) { pDbStats->uiLogBlockChkErrs++; } goto Exit; } *pbIsBeforeImageBlkRV = (FLMBOOL)((BH_IS_BI( pBlk)) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); BH_UNSET_BI( pBlk); // Adjust the current address for the next read uiFilePos += uiBlkSize; if (FSGetFileOffset( uiFilePos) >= pFile->uiMaxFileSize) { FLMUINT uiFileNumber = FSGetFileNumber( uiFilePos); if (!uiFileNumber) { uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( pFile->FileHdr.uiVersionNum); } else { uiFileNumber++; } if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER( pFile->FileHdr.uiVersionNum)) { rc = RC_SET( FERR_DB_FULL); goto Exit; } uiFilePos = FSBlkAddress( uiFileNumber, 0 ); } *puiCurrAddrRV = uiFilePos; Exit: return( rc); } /**************************************************************************** Desc: This routine reads and processes a before-image block record in the log file. The reapply flag indicates whether the block should be written back to the database file. ****************************************************************************/ FSTATIC RCODE flmProcessBeforeImage( FDB * pDb, FLMUINT uiLogEOF, // Address of the end of the rollback // log FLMUINT * puiCurrAddrRV, // This is the current offset we are // reading in the log file. // It will be updated after reading the // data FLMBYTE * pBlk, // This is a pointer to a buffer that // will be used to hold the block that // is read FLMBOOL bDoingRecovery, // Are we doing a recovery as opposed to // rolling back a transaction? FLMUINT uiMaxTransID) // Maximum transaction ID to recover to when // bDoingRecovery is TRUE. This parameter // is ignored when bDoingRecover is FALSE. { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiBlkAddress; FLMUINT uiBlkLength; FLMUINT uiBytesWritten; FLMBOOL bIsBeforeImageBlk = FALSE; F_TMSTAMP StartTime; DB_STATS * pDbStats = pDb->pDbStats; // Read the block from the log if (RC_BAD( rc = flmReadLog( pDb, uiLogEOF, puiCurrAddrRV, pBlk, &bIsBeforeImageBlk))) goto Exit; // Determine if we want to restore the block. // If we are doing a recovery, restore the block only if // its checkpoint is <= uiMaxTransID. If we are // rolling back a transaction, restore the block only if // it is marked as a before-image block. // For the recovery process, multiple versions // of the same block may be restored if there are // multiple versions in the log. However, because // the versions will be in ascending order in the // file, ultimately, the one with the highest // checkpoint that does not exceed uiMaxTransID // will be restored - which is precisely the one // we want to be restored for a recovery. // For a transaction rollback, it is impossible for us // to see more than one version of a block that is // marked as the before-image version, because we // started from a point in the log where the last // update transaction logged its first block. All // blocks after that point that have the BI bits // set should be restored. Any that do not have // the BI bit set should NOT be restored. if (bDoingRecovery) { if ((FLMUINT)FB2UD( &pBlk [BH_TRANS_ID]) > uiMaxTransID) { goto Exit; } } else if (!bIsBeforeImageBlk) { goto Exit; } // Determine the block address before setting the checksum uiBlkAddress = (FLMUINT)GET_BH_ADDR( pBlk); uiBlkLength = pFile->FileHdr.uiBlockSize; // Set the block checksum AFTER encrypting BlkCheckSum( pBlk, CHECKSUM_SET, uiBlkAddress, pFile->FileHdr.uiBlockSize); if (pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->LogBlockRestores.ui64Count++; pDbStats->LogBlockRestores.ui64TotalBytes += uiBlkLength; f_timeGetTimeStamp( &StartTime); } pDb->pSFileHdl->setMaxAutoExtendSize( pFile->uiMaxFileSize); pDb->pSFileHdl->setExtendSize( pFile->uiFileExtendSize); rc = pDb->pSFileHdl->writeBlock( uiBlkAddress, uiBlkLength, pBlk, &uiBytesWritten); #ifdef FLM_DBG_LOG flmDbgLogWrite( pFile->uiFFileId, uiBlkAddress, 0, FB2UD( &pBlk [BH_TRANS_ID]), "ROLLBACK"); #endif if (pDbStats) { flmAddElapTime( &StartTime, &pDbStats->LogBlockRestores.ui64ElapMilli); if (RC_BAD( rc)) { pDbStats->uiWriteErrors++; } } Exit: return( rc); } /*************************************************************************** Desc: Writes the log header to disk. The checksum is calculated before writing the log header to disk. *****************************************************************************/ RCODE flmWriteLogHdr( DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FFILE * pFile, FLMBYTE * pucLogHdr, FLMBYTE * pucCPLogHdr, FLMBOOL bIsCheckpoint) { RCODE rc = FERR_OK; FLMUINT uiBytesToWrite; FLMUINT uiNewCheckSum; FLMBYTE * pucTmpLogHdr; F_TMSTAMP StartTime; // Force any recent writes to disk before modifying the log file // header. This routine is generally called after having added // things to the log file. It is critical that any previous writes // be flushed before the header is updated because the header will // generally have been modified to point to the new things that were // added. if (RC_BAD( rc = pSFileHdl->flush())) { goto Exit; } pucTmpLogHdr = &pFile->pucLogHdrIOBuf[ 16]; uiBytesToWrite = LOG_HEADER_SIZE + 16; // Very Important Note: FlmDbConfig relies on the fact that we will // write out the prefix area of the database header. Do not remove // this code. flmSetFilePrefix( pFile->pucLogHdrIOBuf, pFile->FileHdr.uiAppMajorVer, pFile->FileHdr.uiAppMinorVer); // Only copy the part of the header that is relevant for this // database version. if( pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { f_memcpy( pucTmpLogHdr, pucLogHdr, LOG_HEADER_SIZE_VER40); } else { f_memcpy( pucTmpLogHdr, pucLogHdr, LOG_HEADER_SIZE); } // If we are not doing a checkpoint, we don't really want // to write out certain items, so we restore them from // the save info. buffer, which is the buffer that contains // the log header data as it was at the time of the // checkpoint. if (!bIsCheckpoint && pucCPLogHdr) { f_memcpy( &pucTmpLogHdr [LOG_RFL_LAST_CP_FILE_NUM], &pucCPLogHdr [LOG_RFL_LAST_CP_FILE_NUM], 4); f_memcpy( &pucTmpLogHdr [LOG_RFL_LAST_CP_OFFSET], &pucCPLogHdr [LOG_RFL_LAST_CP_OFFSET], 4); f_memcpy( &pucTmpLogHdr [LOG_CURR_TRANS_ID], &pucCPLogHdr [LOG_CURR_TRANS_ID], 4); f_memcpy( &pucTmpLogHdr [LOG_COMMIT_COUNT], &pucCPLogHdr [LOG_COMMIT_COUNT], 4); f_memcpy( &pucTmpLogHdr [LOG_PF_FIRST_BACKCHAIN], &pucCPLogHdr [LOG_PF_FIRST_BACKCHAIN], 4); f_memcpy( &pucTmpLogHdr [LOG_PF_AVAIL_BLKS], &pucCPLogHdr [LOG_PF_AVAIL_BLKS], 4); f_memcpy( &pucTmpLogHdr [LOG_LOGICAL_EOF], &pucCPLogHdr [LOG_LOGICAL_EOF], 4); pucTmpLogHdr [LOG_PF_FIRST_BC_CNT] = pucCPLogHdr [LOG_PF_FIRST_BC_CNT]; f_memcpy( &pucTmpLogHdr [LOG_PF_NUM_AVAIL_BLKS], &pucCPLogHdr [LOG_PF_NUM_AVAIL_BLKS], 4); if( pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) { f_memcpy( &pucTmpLogHdr [LOG_BLK_CHG_SINCE_BACKUP], &pucCPLogHdr [LOG_BLK_CHG_SINCE_BACKUP], 4); } if( pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) { f_memcpy( &pucTmpLogHdr [LOG_LAST_RFL_COMMIT_ID], &pucCPLogHdr [LOG_LAST_RFL_COMMIT_ID], 4); } } // If this is not a 4.3 database, make sure the old values // in the log header slots are preserved. if( pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) { // Compatibility for parts that were unused. UD2FBA( 0, &pucTmpLogHdr [20]); UD2FBA( 0, &pucTmpLogHdr [48]); UD2FBA( 0, &pucTmpLogHdr [52]); UD2FBA( 0, &pucTmpLogHdr [84]); // Compatibility for trans active and maint in progress. pucTmpLogHdr [76] = 0; pucTmpLogHdr [79] = 0; } uiNewCheckSum = lgHdrCheckSum( pucTmpLogHdr, FALSE); UW2FBA( (FLMUINT16)uiNewCheckSum, &pucTmpLogHdr [LOG_HDR_CHECKSUM]); // Now update the log header record on disk if( pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->LogHdrWrites.ui64Count++; pDbStats->LogHdrWrites.ui64TotalBytes += uiBytesToWrite; f_timeGetTimeStamp( &StartTime); } if( RC_BAD( rc = pSFileHdl->writeBlock( 0, (FLMUINT)f_roundUp( uiBytesToWrite, 512), pFile->pucLogHdrIOBuf, NULL))) { if (pDbStats) { pDbStats->uiWriteErrors++; } goto Exit; } if( pDbStats) { flmAddElapTime( &StartTime, &pDbStats->LogHdrWrites.ui64ElapMilli); } if( RC_BAD( rc = pSFileHdl->flush())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine recovers the database to a physically consistent state. Ret: FERR_OK - Indicates the database has been recovered. other - other FLAIM error codes ****************************************************************************/ RCODE flmPhysRollback( FDB * pDb, FLMUINT uiLogEOF, FLMUINT uiFirstLogBlkAddr, // Address of first log block FLMBOOL bDoingRecovery, // Doing recovery? If so, we will // ignore blocks whose transaction // ID is higher than uiMaxTransID. // Also, we will not check the BI // bits in the logged blocks, because // we are not rolling back a // transaction. FLMUINT uiMaxTransID // Ignored when bDoingRecovery is // FALSE ) { RCODE rc = FERR_OK; FFILE * pFile = pDb->pFile; FLMUINT uiCurrAddr; FLMBYTE * pucBlk = NULL; // If the log is empty, no need to do anything. // A uiFirstLogBlkAddr of zero indicates that there // is nothing in the log to rollback. This will be true // if we are rolling back a transaction, and the transaction // has not logged anything or if we are doing a recovery and // nothing was logged since the last checkpoint. if (uiLogEOF == pFile->FileHdr.uiBlockSize || !uiFirstLogBlkAddr) { goto Exit; } // Allocate a buffer to be used for reading. if( RC_BAD( rc = f_allocAlignedBuffer( pFile->FileHdr.uiBlockSize, (void **)&pucBlk))) { goto Exit; } // Start from beginning of log and read to EOF restoring before-image // blocks along the way. uiCurrAddr = uiFirstLogBlkAddr; while (FSAddrIsBelow( uiCurrAddr, uiLogEOF)) { if (RC_BAD( rc = flmProcessBeforeImage( pDb, uiLogEOF, &uiCurrAddr, pucBlk, bDoingRecovery, uiMaxTransID))) { goto Exit; } } // Force the writes to the file. if (RC_BAD( rc = pDb->pSFileHdl->flush())) { goto Exit; } Exit: // Free the memory handle, if one was allocated. if (pucBlk) { f_freeAlignedBuffer( (void **)&pucBlk); } return( rc); } libflaim-4.9.966/src/fqget.cpp0000644000175000017500000004711310510774540017474 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: Query record retrieval // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqget.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------- #include "flaimsys.h" FSTATIC RCODE flmCurCSTestRec( CURSOR * pCursor, FLMUINT uiDrn, FlmRecord * pTestRec, FLMBOOL * pbIsMatchRV); /**************************************************************************** Desc: Makes a SET_DEL from a record. ****************************************************************************/ RCODE flmCurMakeKeyFromRec( FDB * pDb, IXD * pIxd, F_Pool * pPool, FlmRecord * pRec, FLMBYTE ** ppucKeyBuffer, FLMUINT * puiKeyLen) { REC_KEY * pKey = NULL; RCODE rc = FERR_OK; // Set up CDL table and other KREF stuff in FDB. if (RC_BAD( rc = KrefCntrlCheck( pDb))) { goto Exit; } // Parse the keys from the record, and verify that the record contains // only one key. rc = flmGetRecKeys( pDb, pIxd, pRec, pRec->getContainerID(), TRUE, pPool, &pKey); KYAbortCurrentRecord( pDb); if (RC_BAD( rc)) { goto Exit; } else if (!pKey) { rc = RC_SET( FERR_NOT_FOUND); goto Exit; } if (pKey->pNextKey) { rc = RC_SET( FERR_ILLEGAL_OP); goto Exit; } // Now allocate a key buffer to hold the key. if (!(*ppucKeyBuffer)) { if( RC_BAD( rc = pPool->poolCalloc( MAX_KEY_SIZ, (void **)ppucKeyBuffer))) { goto Exit; } } // We pass in pRec->getContainerID(), because every key we generate // came from pRec, and we want to use its container number. if (RC_BAD( rc = KYTreeToKey( pDb, pIxd, pKey->pKey, pRec->getContainerID(), *ppucKeyBuffer, puiKeyLen, 0))) { goto Exit; } Exit: while (pKey) { pKey->pKey->Release(); pKey->pKey = NULL; pKey = pKey->pNextKey; } return( rc); } /**************************************************************************** Desc: Sets a query's position from a passed-in DRN. ****************************************************************************/ RCODE flmCurSetPosFromDRN( CURSOR * pCursor, FLMUINT uiDRN ) { RCODE rc = FERR_OK; FlmRecord * pRec = NULL; F_Pool * pTempPool; FDB * pDb = NULL; IXD * pIxd; LFILE * pLFile; SUBQUERY * pSubQuery; FLMBOOL bPositioned; FLMBYTE * pucKeyBuffer = NULL; FLMUINT uiKeyLen; pDb = pCursor->pDb; if (RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } pTempPool = &pDb->TempPool; // Read the record associated with the DRN, and construct an index key rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN, FALSE, NULL, NULL, &pRec); if (rc == FERR_NOT_FOUND) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, pCursor->uiContainer, &pLFile))) { goto Exit; } if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN, &pRec, NULL, NULL))) { goto Exit; } } else if (RC_BAD( rc)) { goto Exit; } // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // Go through the subquery list to find the sub-query into which the // DRN falls. bPositioned = FALSE; for( pSubQuery = pCursor->pSubQueryList; pSubQuery && !bPositioned; pSubQuery = pSubQuery->pNext) { FLMUINT uiResult; // See if the record satisifies this sub-query's criteria. // If not, we cannot position into this sub-query. if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pRec, FALSE, &uiResult))) { goto Exit; } // Found a sub-query where this DRN passes. Set this sub-query to // be the current sub-query on certain conditions. if (uiResult == FLM_TRUE) { switch (pSubQuery->OptInfo.eOptType) { case QOPT_USING_INDEX: if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, pSubQuery->OptInfo.uiIxNum, NULL, &pIxd))) { goto Exit; } // If we cannot create a key for this index, go to the next // subquery. if (RC_BAD( rc = flmCurMakeKeyFromRec( pDb, pIxd, pTempPool, pRec, &pucKeyBuffer, &uiKeyLen))) { if (rc == FERR_NOT_FOUND || rc == FERR_ILLEGAL_OP) { rc = FERR_OK; break; } goto Exit; } // Position to the key and drn - If we can't just go to the // next subquery. if (RC_BAD( rc = pSubQuery->pFSIndexCursor->positionTo( pDb, pucKeyBuffer, uiKeyLen, uiDRN))) { if (rc == FERR_NOT_FOUND || rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { rc = FERR_OK; break; } goto Exit; } // Retrieve the current key and DRN from the index cursor. rc = pSubQuery->pFSIndexCursor->currentKey( pDb, &pSubQuery->pRec, &pSubQuery->uiDrn); if (RC_BAD( rc)) { goto Exit; } bPositioned = TRUE; pSubQuery->uiCurrKeyMatch = FLM_UNK; pSubQuery->bFirstReference = FALSE; // These should have been set by the call to currentKey. flmAssert( pSubQuery->pRec->getContainerID() == pCursor->uiContainer); flmAssert( pSubQuery->pRec->getID() == pSubQuery->uiDrn); pSubQuery->bRecIsAKey = TRUE; break; case QOPT_USING_PREDICATE: // Can't position in a predicate - go to next sub-query. break; case QOPT_SINGLE_RECORD_READ: bPositioned = TRUE; pSubQuery->uiDrn = uiDRN; break; case QOPT_PARTIAL_CONTAINER_SCAN: case QOPT_FULL_CONTAINER_SCAN: rc = pSubQuery->pFSDataCursor->positionTo( pDb, uiDRN); if (RC_BAD( rc)) { if (rc == FERR_NOT_FOUND || rc == FERR_EOF_HIT || rc == FERR_BOF_HIT) { rc = FERR_OK; break; } goto Exit; } bPositioned = TRUE; break; default: flmAssert( 0); break; } if (bPositioned) { pCursor->uiLastRecID = uiDRN; pCursor->pCurrSubQuery = pSubQuery; break; } } } if (!bPositioned) { rc = RC_SET( FERR_NOT_FOUND); } Exit: if (pRec) { pRec->Release(); } flmExit( FLM_CURSOR_CONFIG, pDb, rc); return( pCursor->rc = rc); } /**************************************************************************** Desc: Given a cursor and two DRNs, this API does the following: ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorCompareDRNs( HFCURSOR hCursor, FLMUINT uiDRN1, FLMUINT uiDRN2, FLMUINT uiTimeLimit, FLMINT * piCmpResult, FLMBOOL * pbTimedOut, FLMUINT * puiCount) { RCODE rc = FERR_OK; CURSOR * pCursor = (CURSOR *)hCursor; FDB * pDb = NULL; F_Pool * pTempPool; FLMBYTE * pucKey1; FLMUINT uiKey1Len; FLMBYTE * pucKey2; FLMUINT uiKey2Len; FlmRecord * pRec1 = NULL; FlmRecord * pRec2 = NULL; IXD * pIxd; FSIndexCursor * pTmpFSIndexCursor = NULL; FSIndexCursor * pSaveFSIndexCursor = NULL; FLMUINT uiSaveTimeLimit = 0; LFILE * pLFile; FLMUINT uiIndexNum; FLMINT iCmp; // Verify that return params are non-NULL. flmAssert( piCmpResult != NULL); flmAssert( pbTimedOut != NULL); flmAssert( puiCount != NULL); flmAssert( pCursor != NULL); if (!pCursor) { flmAssert( 0); rc = RC_SET( FERR_INVALID_PARM); goto Exit; } if (pCursor->pCSContext) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } // If the two DRNs are equal, there is no need to read the records to compare // their keys. Furthermore, if they are in the cursor's result set, the // return count will be 2, otherwise it will be 0. if (uiDRN1 == uiDRN2) { *piCmpResult = 0; if (uiTimeLimit) { FLMBOOL bInRSet; if (RC_BAD( rc = FlmCursorTestDRN( hCursor, uiDRN1, &bInRSet))) { goto Exit; } if (bInRSet) { *puiCount = 2; } else { *puiCount = 0; } *pbTimedOut = FALSE; } else { *pbTimedOut = TRUE; rc = FERR_OK; } goto Exit; } *puiCount = 0; *pbTimedOut = (FLMBOOL)((!uiTimeLimit) ? TRUE : FALSE); // Read the records associated with the two DRNs, and construct index keys // from them to determine the directional relationship between them. pDb = pCursor->pDb; if (RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } pTempPool = &pDb->TempPool; // Get the records corresponding to the two DRNs. rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN1, FALSE, NULL, NULL, &pRec1); if (rc == FERR_NOT_FOUND) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, pCursor->uiContainer, &pLFile))) { goto Exit; } if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN1, &pRec1, NULL, NULL))) { goto Exit; } } else if (RC_BAD( rc)) { goto Exit; } // Retrieve the 2nd record. rc = flmRcaRetrieveRec( pDb, NULL, pCursor->uiContainer, uiDRN2, FALSE, NULL, NULL, &pRec2); if (rc == FERR_NOT_FOUND) { if (RC_BAD( rc = fdictGetContainer( pDb->pDict, pCursor->uiContainer, &pLFile))) { goto Exit; } if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDRN2, &pRec2, NULL, NULL))) { goto Exit; } } else if (RC_BAD( rc)) { goto Exit; } // At this point, both DRNs have been found. Now generate keys from each // of the records and perform the following actions: // 1) Verify that only one key exists in each record. If there is more // than one key, no clear comparison can be made. // 2) Compare the two keys and set *piCmpResult as follows: // <0 First Key < Second Key // 0 First Key = Second Key // >0 First Key > Second Key // If necessary, optimize the query here. NOTE: this should be done only if // FLAIM is to choose an index for the query. if (!pCursor->bOptimized && pCursor->uiIndexNum == FLM_SELECT_INDEX) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } if (pCursor->bOptimized) { // If there are multiple subqueries, that means there are // multiple indexes. Set uiIndexNum to zero in this // case so that we will return FERR_NOT_IMPLEMENTED // below. if (!pCursor->pSubQueryList || pCursor->pSubQueryList->pNext || pCursor->pSubQueryList->OptInfo.eOptType != QOPT_USING_INDEX) { uiIndexNum = 0; } else { uiIndexNum = pCursor->pSubQueryList->OptInfo.uiIxNum; } } else { uiIndexNum = pCursor->uiIndexNum; } // Index number of zero means that the user either did not set an // index, or no index was selected, or multiple indexes were // selected - all of which disqualify this query from being able // to compare to DRNs. if (!uiIndexNum) { rc = RC_SET( FERR_NOT_IMPLEMENTED); goto Exit; } if( RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode, uiIndexNum, NULL, &pIxd))) { goto Exit; } pucKey1 = NULL; if (RC_BAD( rc = flmCurMakeKeyFromRec( pDb, pIxd, pTempPool, pRec1, &pucKey1, &uiKey1Len))) { goto Exit; } pucKey2 = NULL; if (RC_BAD( rc = flmCurMakeKeyFromRec( pDb, pIxd, pTempPool, pRec2, &pucKey2, &uiKey2Len))) { goto Exit; } if (uiKey1Len > uiKey2Len) { if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey2Len)) == 0) { iCmp = 1; } else { iCmp = ( (iCmp > 0) ? 1 : -1); } } else if (uiKey1Len < uiKey2Len) { if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey1Len)) == 0) { iCmp = -1; } else { iCmp = ( (iCmp > 0) ? 1 : -1); } } else { if ((iCmp = f_memcmp( pucKey1, pucKey2, uiKey2Len)) != 0) { iCmp = ( (iCmp > 0) ? 1 : -1); } else { // Keys are equal, compare DRNs. iCmp = (FLMINT)((uiDRN1 > uiDRN2) ? (FLMINT)-1 : (FLMINT)((uiDRN1 < uiDRN2) ? (FLMINT)1 : (FLMINT)0)); } } *piCmpResult = iCmp; // If time limit is zero, don't need to do the count. if (!uiTimeLimit) { goto Exit; } // If keys are equal, simply return a count of two. if (!iCmp) { *puiCount = 2; goto Exit; } // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } // Create a new temporary index cursor. if ((pTmpFSIndexCursor = f_new FSIndexCursor) == NULL) { rc = RC_SET( FERR_MEM); goto Exit; } // Count the entries between the two keys/references. if (iCmp > 0) { if (RC_BAD( rc = pTmpFSIndexCursor->setupKeys( pDb, pIxd, pucKey1, uiKey1Len, uiDRN1, pucKey2, uiKey2Len, uiDRN2, FALSE))) { goto Exit; } } else { if (RC_BAD( rc = pTmpFSIndexCursor->setupKeys( pDb, pIxd, pucKey1, uiKey1Len, uiDRN1, pucKey2, uiKey2Len, uiDRN2, FALSE))) { goto Exit; } } // Intersect the temporary index cursor's key range with the // cursor's key range(es). pSaveFSIndexCursor = pCursor->pSubQueryList->pFSIndexCursor; uiSaveTimeLimit = pCursor->uiTimeLimit; if (RC_BAD( rc = pTmpFSIndexCursor->intersectKeys( pDb, pSaveFSIndexCursor))) { goto Exit; } // Set the query's index cursor to the temporary index cursor and count // the entries between them. if (uiTimeLimit == FLM_NO_LIMIT) { pCursor->uiTimeLimit = 0; } else { pCursor->uiTimeLimit = FLM_SECS_TO_TIMER_UNITS( uiTimeLimit); } // Perform the count operation. pCursor->pSubQueryList->pFSIndexCursor = pTmpFSIndexCursor; if (RC_BAD( rc = flmCurSearch( FLM_CURSOR_REC_COUNT, pCursor, TRUE, TRUE, puiCount, NULL, NULL, NULL))) { if (rc == FERR_EOF_HIT) { rc = FERR_OK; } else if (rc == FERR_TIMEOUT) { rc = FERR_OK; *pbTimedOut = TRUE; } goto Exit; } Exit: if (pRec1) { pRec1->Release(); } if (pRec2) { pRec2->Release(); } if (pTmpFSIndexCursor) { pTmpFSIndexCursor->Release(); pTmpFSIndexCursor = NULL; } // Restore saved index cursor if necessary. if (pSaveFSIndexCursor) { pCursor->pSubQueryList->pFSIndexCursor = pSaveFSIndexCursor; pCursor->uiTimeLimit = uiSaveTimeLimit; } flmExit( FLM_CURSOR_COMPARE_DRNS, pDb, rc); return( pCursor->rc = rc); } /**************************************************************************** Desc: Does FlmCursorTestRec and FlmCursorTestDRN over the client/server line. ****************************************************************************/ FSTATIC RCODE flmCurCSTestRec( CURSOR * pCursor, FLMUINT uiDrn, FlmRecord * pTestRec, FLMBOOL * pbIsMatchRV ) { RCODE rc = FERR_OK; CS_CONTEXT * pCSContext = pCursor->pCSContext; FCL_WIRE Wire( pCSContext); // If there is no VALID id for the cursor, get one. if (pCursor->uiCursorId == FCS_INVALID_ID) { if (RC_BAD( rc = flmInitCurCS( pCursor))) { goto Exit; } } // Send a request to test the record or DRN. if (RC_BAD( rc = Wire.sendOp( FCS_OPCLASS_ITERATOR, FCS_OP_ITERATOR_TEST_REC))) { goto Exit; } if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_ITERATOR_ID, pCursor->uiCursorId))) { goto Transmission_Error; } if (pTestRec) { if (RC_BAD( rc = Wire.sendRecord( WIRE_VALUE_RECORD, pTestRec))) { goto Transmission_Error; } } else { if (RC_BAD( rc = Wire.sendNumber( WIRE_VALUE_DRN, uiDrn))) { goto Transmission_Error; } } if (RC_BAD( rc = Wire.sendTerminate())) { goto Transmission_Error; } // Read the response. if (RC_BAD( rc = Wire.read())) { goto Transmission_Error; } *pbIsMatchRV = Wire.getBoolean(); rc = Wire.getRCode(); Exit: return( rc); Transmission_Error: pCSContext->bConnectionGood = FALSE; goto Exit; } /**************************************************************************** Desc: Checks a record that has been retrieved from the database to see if satisfies the cursor selection criteria. IMPORTANT NOTE: pRec's containerID better be set to the container it came from, because flmCurEvalCriteria verifies that the record's container number matches the cursor's container number. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorTestRec( HFCURSOR hCursor, FlmRecord * pRec, FLMBOOL * pbIsMatch) { RCODE rc = FERR_OK; FDB * pDb = NULL; CURSOR * pCursor = (CURSOR *)hCursor; SUBQUERY * pSubQuery; flmAssert( pCursor != NULL); *pbIsMatch = FALSE; if (pCursor->pCSContext) { rc = flmCurCSTestRec( pCursor, 0, pRec, pbIsMatch); goto Exit2; } // Make sure that we don't have partially finished // query criteria. if (pCursor->QTInfo.uiNestLvl || ((pCursor->QTInfo.uiExpecting & FLM_Q_OPERAND) && pCursor->QTInfo.pTopNode)) { rc = RC_SET( FERR_CURSOR_SYNTAX); goto Exit; } // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } flmAssert( pCursor->pDb != NULL); pDb = pCursor->pDb; if (RC_BAD( rc = flmCurDbInit( pCursor))) { goto Exit; } // Evaluate the record against all sub-queries until // we find a TRUE. // IMPORTANT NOTE: pRec's containerID better be set to the container it // came from, because flmCurEvalCriteria verifies that the record's // container number matches the cursor's container number. flmAssert( pRec->getContainerID() != 0); pSubQuery = pCursor->pSubQueryList; while (pSubQuery) { FLMUINT uiResult; if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pRec, FALSE, &uiResult))) { goto Exit; } if (uiResult == FLM_TRUE) { *pbIsMatch = TRUE; break; } pSubQuery = pSubQuery->pNext; } Exit: if (pDb) { fdbExit( pDb); } Exit2: return( rc); } /**************************************************************************** Desc: Retrieves the record identified by the passed-in DRN and checks it to see if it satisfies the cursor selection criteria. Notes: This function is designed for use with cursors having only one associated source. Multiple sources are not supported. ****************************************************************************/ FLMEXP RCODE FLMAPI FlmCursorTestDRN( HFCURSOR hCursor, FLMUINT uiDrn, FLMBOOL * pbIsMatch) { RCODE rc = FERR_OK; FDB * pDb = NULL; CURSOR * pCursor = (CURSOR *)hCursor; SUBQUERY * pSubQuery; FlmRecord * pRec = NULL; flmAssert( pCursor != NULL); *pbIsMatch = FALSE; if (pCursor->pCSContext) { rc = flmCurCSTestRec( pCursor, uiDrn, NULL, pbIsMatch); goto Exit2; } flmAssert( pCursor->pDb != NULL); if (RC_OK( rc = FlmRecordRetrieve( (HFDB)pCursor->pDb, pCursor->uiContainer, uiDrn, FO_EXACT, &pRec, NULL))) { // Optimize the subqueries as necessary if (!pCursor->bOptimized) { if (RC_BAD( rc = flmCurPrep( pCursor))) { goto Exit; } } pDb = pCursor->pDb; if (RC_BAD(rc = flmCurDbInit( pCursor))) { goto Exit; } // Evaluate the record against all sub-queries until // we find a TRUE. pSubQuery = pCursor->pSubQueryList; while (pSubQuery) { FLMUINT uiResult; if (RC_BAD( rc = flmCurEvalCriteria( pCursor, pSubQuery, pRec, FALSE, &uiResult))) { goto Exit; } if (uiResult == FLM_TRUE) { *pbIsMatch = TRUE; break; } pSubQuery = pSubQuery->pNext; } } Exit: if (pDb) { fdbExit( pDb); } if (pRec) { pRec->Release(); } Exit2: return( rc); } libflaim-4.9.966/src/flmimon.h0000644000175000017500000013765310510774540017505 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------- // Desc: HTML callback function for displaying monitoring web pages - definitions. // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flmimon.h 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ //------------------------------------------------------------------------- #ifndef FLMIMON_H #define FLMIMON_H #include "fpackon.h" // IMPORTANT NOTE: No other include files should follow this one except // for fpackoff.h int flmHttpCallback( HRequest * pHRequest, void * pvUserData); // HTML definitions #define HTML_DOCTYPE "\n" #define TABLE "
\n" #define TABLE_END "
\n" #define TR "\n" #define TR_END "\n" #define TD_4x "0x%0.4X\n" #define TD_s "%s\n" #define TD_a_s_s "%s\n" #define TD_a_s_x "0x%.8X\n" #define TD_a_p_s "%s\n" #define TD_a_p_x "0x%.8X\n" #define TD_ui "%u\n" #define TD_i "%d\n" #define TD_lu "%lu\n" #define TD_ld "%ld\n" #define TD_8x "0x%0.8X\n" #define TD "\n" #define HEAD "\n" #define HEAD_END "\n" #define HEADING "%s\n" // Colors #define FLM_IMON_COLOR_PUTTY_1 ("#dfddd5") #define FLM_IMON_COLOR_PUTTY_2 ("#efeee9") // RCache Link name definitions #define MANAGER "RCacheMgr" #define AVAILGROUPS "pAvailGroups" #define USEDGROUPS "pUsedGroups" #define NEXT "pNext" #define PREV "pPrev" #define PREVINBUCKET "pPrevInBucket" #define NEXTINBUCKET "pNextInBucket" #define PREVINFILE "pPrevInFile" #define NEXTINFILE "pNextInFile" #define PREVINGLOBAL "pPrevInGlobal" #define NEXTINGLOBAL "pNextInGlobal" #define NEWERVERSION "pNewerVersion" #define OLDERVERSION "pOlderVersion" // Operations for the printOperationButton #define OPERATION_QUERY "doQuery" #define OPERATION_DELETE "doDelete" #define OPERATION_STOP "doStop" #define OPERATION_ABORT "doAbort" #define OPERATION_CHECK "doCheck" #define OPERATION_INDEX_LIST "doIndexList" // Enum. types typedef enum { URL_PATH_ENCODING = 1, URL_QUERY_ENCODING, HTML_ENCODING } FStringEncodeType; typedef enum { JUSTIFY_LEFT = 1, JUSTIFY_CENTER, JUSTIFY_RIGHT } JustificationType; enum ButtonTypes { BT_Submit, BT_Reset, BT_Button }; /**************************************************************************** Desc: The F_WebPage class, from which all of the various web page classes will be defined. *****************************************************************************/ class F_WebPage : public F_Object { public: F_WebPage() { m_pszFormData = NULL; m_pszURLString = gv_FlmSysData.HttpConfigParms.pszURLString; fnPrintf = gv_FlmSysData.HttpConfigParms.fnPrintf; m_uiSessionRC = FERR_NOT_IMPLEMENTED; // Indicates not setup yet. } virtual ~F_WebPage() { if( m_pszFormData) { f_free( &m_pszFormData); } // If the session has not been released yet, then release it. if (m_pFlmSession) { flmAssert( 0); releaseSession(); } } FINLINE const char * fnReqPath( void) { return( gv_FlmSysData.HttpConfigParms.fnReqPath( m_pHRequest)); } FINLINE const char * fnReqQuery( void) { return( gv_FlmSysData.HttpConfigParms.fnReqQuery( m_pHRequest)); } FINLINE const char * fnReqHdrValue( const char * pszName) { return( gv_FlmSysData.HttpConfigParms.fnReqHdrValue( m_pHRequest, pszName)); } FINLINE int fnSetHdrValue( const char * pszName, const char * pszValue) { return( gv_FlmSysData.HttpConfigParms.fnSetHdrValue( m_pHRequest, pszName, pszValue)); } FINLINE int fnEmit( void) { return( gv_FlmSysData.HttpConfigParms.fnEmit( m_pHRequest)); } FINLINE void fnSetNoCache( const char * pszHeader) { gv_FlmSysData.HttpConfigParms.fnSetNoCache( m_pHRequest, pszHeader); } FINLINE int fnSendHeader( int iStatus) { return( gv_FlmSysData.HttpConfigParms.fnSendHeader( m_pHRequest, iStatus)); } FINLINE int fnSetIOMode( int bRaw, int bOutput) { return( gv_FlmSysData.HttpConfigParms.fnSetIOMode( m_pHRequest, bRaw, bOutput)); } FINLINE int fnSendBuffer( const void * pvBuf, FLMSIZET bufsz) { return( gv_FlmSysData.HttpConfigParms.fnSendBuffer( m_pHRequest, pvBuf, bufsz)); } FINLINE void * fnAcquireSession( void) { return( gv_FlmSysData.HttpConfigParms.fnAcquireSession( m_pHRequest)); } FINLINE void fnReleaseSession( void * pvHSession) { gv_FlmSysData.HttpConfigParms.fnReleaseSession( pvHSession); } FINLINE void * fnAcquireUser( void * pvHSession) { return( gv_FlmSysData.HttpConfigParms.fnAcquireUser( pvHSession, m_pHRequest)); } FINLINE void fnReleaseUser( void * pvHUser) { gv_FlmSysData.HttpConfigParms.fnReleaseUser( pvHUser); } FINLINE int fnSetSessionValue( void * pvHSession, const char * pcTag, const void * pvData, FLMSIZET uiSize) { return( gv_FlmSysData.HttpConfigParms.fnSetSessionValue( pvHSession, pcTag, pvData, uiSize)); } FINLINE int fnGetSessionValue( void * pvHSession, const char * pcTag, void * pvData, FLMSIZET * puiSize) { return( gv_FlmSysData.HttpConfigParms.fnGetSessionValue( pvHSession, pcTag, pvData, puiSize)); } FINLINE int fnGetGblValue( const char * pcTag, void * pvData, FLMSIZET * puiSize) { return( gv_FlmSysData.HttpConfigParms.fnGetGblValue( pcTag, pvData, puiSize)); } FINLINE int fnSetGblValue( const char * pcTag, const void * pvData, FLMSIZET uiSize) { return( gv_FlmSysData.HttpConfigParms.fnSetGblValue( pcTag, pvData, uiSize)); } FINLINE int fnRecvBuffer( void * pvBuf, FLMSIZET * puiBufSize) { return( gv_FlmSysData.HttpConfigParms.fnRecvBuffer( m_pHRequest, pvBuf, puiBufSize)); } void setMembers( HRequest * pHRequest) { m_pHRequest = pHRequest; // Get the session object for this page. m_uiSessionRC = acquireSession(); } virtual RCODE display( FLMUINT uiNumParams, const char ** ppszParams) = 0; RCODE ExtractParameter( FLMUINT uiNumParams, const char ** ppszParams, const char * pszParamName, FLMUINT uiParamLen, char * pszParam); FLMBOOL DetectParameter( FLMUINT uiNumParams, const char ** ppszParams, const char * pszParamName); RCODE getDatabaseHandleParam( FLMUINT uiNumParams, const char ** ppszParams, F_Session * pFlmSession, HFDB * phDb, char * pszKey = NULL); void FormatTime( FLMUINT uiTimerUnits, char * pszFormattedTime); void popupFrame( void); RCODE writeUsage( FLM_CACHE_USAGE * pUsage, FLMBOOL bRefresh, const char * pszURL, const char * pszTitle); FINLINE void stdHdr( void) { fnSetHdrValue( "Content-Type", "text/html"); fnSetNoCache( NULL); fnSendHeader( HTS_OK); } void printHTMLLink( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, void * pvValue, const char * pszLink, FLMBOOL bHighlight = FALSE); void printHTMLString( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, const char * pszValue, FLMBOOL bHighlight = FALSE); void printHTMLUint( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, FLMUINT uiValue, FLMBOOL bHighlight = FALSE); void printHTMLInt( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, FLMINT iValue, FLMBOOL bHighlight = FALSE); void printHTMLUlong( const char * pszName, const char * pszType, void * pvBase, void * pvAddress, unsigned long luValue, FLMBOOL bHighlight = FALSE); void printStyle( void); void printColumnHeading( const char * pszHeading, JustificationType eJustification = JUSTIFY_LEFT, const char * pszBackground = NULL, FLMUINT uiColSpan = 1, FLMUINT uiRowSpan = 1, FLMBOOL bClose = TRUE, FLMUINT uiWidth = 0); void printColumnHeadingClose( void); void printEncodedString( const char * pszString, FStringEncodeType eEncodeType = HTML_ENCODING, FLMBOOL bMapSlashes = TRUE); void printDocStart( const char * pszTitle, FLMBOOL bPrintTitle = TRUE, FLMBOOL bStdHeader = TRUE, const char * pszBackground = NULL); void printDocEnd( void); void printMenuReload( void); void printTableStart( const char * pszTitle, FLMUINT uiColumns, FLMUINT uiWidthFactor = 100); void printTableEnd( void); void printTableRowStart( FLMBOOL bHighlight = FALSE); void printTableRowEnd( void); void printTableDataStart( FLMBOOL bNoWrap = TRUE, JustificationType eJustification = JUSTIFY_LEFT, FLMUINT uiWidth = 0); void printTableDataEnd( void); void printTableDataEmpty( void); void printErrorPage( RCODE rc, FLMBOOL bStdHeader = TRUE, const char * pszWhat = "Unable to process request ... "); void printErrorPage( const char * pszErrMsg, const char * pszErrStr2 = NULL, FLMBOOL bStdHeader = TRUE); RCODE getFormValueByName( const char * pszValueTag, char ** ppszBuf, FLMUINT uiBufLen, FLMUINT * puiDataLen); void printDate( FLMUINT uiGMTTime, char * pszBuffer = NULL); void printYesNo( FLMBOOL bYes); void printCommaNum( FLMUINT64 ui64Num, JustificationType eJustify = JUSTIFY_RIGHT, FLMBOOL bChangedValue = FALSE); void printCommaNumText( FLMUINT64 ui64Num); void printStartInputForm( const char * pszFormName, const char * pszPage, FLMUINT uiFormValue); void printEndInputForm( void); void printButton( const char * pszContents, ButtonTypes eBType, const char * pszName = NULL, const char * pszValue = NULL, const char * pszExtra = NULL, FLMBOOL bDisabled = FALSE, FLMBYTE ucAccessKey ='\0', FLMUINT uiTabIndex = 0); // pszExtra is used for text that doesn't appear too often - event handler // scripts are a good example. The text of pszExtra appears just before // the closing > of the